diff --git a/ios/ably_pubsub_device_flutter/Package.swift b/ios/ably_pubsub_device_flutter/Package.swift index 32c7d1c9f..4fe25d2de 100644 --- a/ios/ably_pubsub_device_flutter/Package.swift +++ b/ios/ably_pubsub_device_flutter/Package.swift @@ -70,5 +70,14 @@ let package = Package( .product(name: "FlutterFramework", package: "FlutterFramework"), ] ), + .testTarget( + name: "AblyFlutterCodecTests", + dependencies: [ + "ably_pubsub_device_flutter", + "AblyFlutterCommon", + .product(name: "FlutterFramework", package: "FlutterFramework"), + .product(name: "AblyPubSubDevice", package: "ably-pubsub-cocoa"), + ] + ), ] ) diff --git a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.m b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.m index c53feed0a..578f1b5ab 100644 --- a/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.m +++ b/ios/ably_pubsub_device_flutter/Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.m @@ -129,7 +129,8 @@ use an explicitly received null from Dart (manifesting this side as a nil id) READ_BOOL(clientOptions, autoConnect, dictionary, TxClientOptions_autoConnect); READ_VALUE(clientOptions, useBinaryProtocol, dictionary, TxClientOptions_useBinaryProtocol); READ_VALUE(clientOptions, queueMessages, dictionary, TxClientOptions_queueMessages); - READ_VALUE(clientOptions, echoMessages, dictionary, TxClientOptions_echoMessages); + // Unbox NSNumber so an explicit false is not treated as a non-null pointer. + READ_BOOL(clientOptions, echoMessages, dictionary, TxClientOptions_echoMessages); READ_VALUE(clientOptions, recover, dictionary, TxClientOptions_recover); READ_VALUE(clientOptions, idempotentRestPublishing, dictionary, TxClientOptions_idempotentRestPublishing); READ_VALUE(clientOptions, fallbackHosts, dictionary, TxClientOptions_fallbackHosts); diff --git a/ios/ably_pubsub_device_flutter/Tests/AblyFlutterCodecTests/AblyFlutterReaderTests.m b/ios/ably_pubsub_device_flutter/Tests/AblyFlutterCodecTests/AblyFlutterReaderTests.m new file mode 100644 index 000000000..58739f606 --- /dev/null +++ b/ios/ably_pubsub_device_flutter/Tests/AblyFlutterCodecTests/AblyFlutterReaderTests.m @@ -0,0 +1,53 @@ +#import +#import +@import AblyPubSubDevice; +@import AblyFlutterCommon; + +// Exercise the production decoder without making its header part of the public API. +#import "../../Sources/ably_pubsub_device_flutter/codec/AblyFlutterReader.h" + +@interface AblyFlutterReaderTests : XCTestCase +@end + +@implementation AblyFlutterReaderTests + +// Match Dart's ClientOptions wire format using Flutter's real standard codec. +- (ARTClientOptions*)decodeClientOptions:(NSDictionary*)options { + NSMutableData *data = [NSMutableData data]; + FlutterStandardWriter *writer = [[FlutterStandardWriter alloc] initWithData:data]; + [writer writeByte:CodecTypeClientOptions]; + [writer writeValue:options]; + + AblyFlutterReader *reader = [[AblyFlutterReader alloc] initWithData:data]; + AblyFlutterClientOptions *decoded = [reader readValue]; + XCTAssertTrue([decoded isKindOfClass:[AblyFlutterClientOptions class]]); + XCTAssertNotNil(decoded.clientOptions); + return decoded.clientOptions; +} + +// A boxed false from Flutter must disable echoes on the native client. +- (void)testEchoMessagesFalseIsPreserved { + ARTClientOptions *options = [self decodeClientOptions:@{TxClientOptions_echoMessages: @NO}]; + XCTAssertFalse(options.echoMessages, + @"An explicit false must disable echoes instead of treating NSNumber as a truthy pointer"); +} + +// Explicit true must continue to enable echoes. +- (void)testEchoMessagesTrueIsPreserved { + ARTClientOptions *options = [self decodeClientOptions:@{TxClientOptions_echoMessages: @YES}]; + XCTAssertTrue(options.echoMessages); +} + +// An omitted option must preserve the native SDK's documented default. +- (void)testMissingEchoMessagesKeepsDefault { + ARTClientOptions *options = [self decodeClientOptions:@{}]; + XCTAssertTrue(options.echoMessages); +} + +// Null means the caller did not override the native default. +- (void)testNullEchoMessagesKeepsDefault { + ARTClientOptions *options = [self decodeClientOptions:@{TxClientOptions_echoMessages: [NSNull null]}]; + XCTAssertTrue(options.echoMessages); +} + +@end