From d47701056526266e34acfb27d2e26d448db9a4d2 Mon Sep 17 00:00:00 2001 From: Robin Macharg Date: Mon, 10 Feb 2020 13:39:24 +0000 Subject: [PATCH] feat: Add apiKey as a mutable property of BugsnagEvent (#458) * refactor: rename BugsnagCrashReport to BugsnagEvent * feat: add per-BugsnagEvent mutable apiKey --- Bugsnag.podspec.json | 2 +- CHANGELOG.md | 5 + Source/BugsnagConfiguration.h | 1 + Source/BugsnagConfiguration.m | 30 +++-- Source/BugsnagEvent.h | 7 + Source/BugsnagEvent.m | 26 ++++ Tests/BugsnagEventTests.m | 182 +++++++++++++++++--------- Tests/BugsnagTestConstants.h | 2 + iOS/Bugsnag.xcodeproj/project.pbxproj | 5 +- 9 files changed, 180 insertions(+), 80 deletions(-) diff --git a/Bugsnag.podspec.json b/Bugsnag.podspec.json index 6c75c5b93..b8a4e534a 100644 --- a/Bugsnag.podspec.json +++ b/Bugsnag.podspec.json @@ -26,6 +26,6 @@ "requires_arc": true, "public_header_files": [ "Source/BSG_KSCrashReportWriter.h", - "Source/Bugsnag{,MetaData,Configuration,Breadcrumb,Event,Plugin}.h" + "Source/Bugsnag{,Metadata,Configuration,Breadcrumb,Event,Plugin}.h" ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index 8136018bd..c2f1b6680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,11 @@ Bugsnag Notifiers on other platforms. * Add a breadcrumb when Bugsnag first starts with the message "Bugsnag loaded" [#445](https://github.com/bugsnag/bugsnag-cocoa/pull/445) +* Add a per-Event `apiKey` property. This defaults to the global + `BugsnagConfiguration` value but can be overridden in event passed to the + `Bugsnag.notify()` callback. + [#458](https://github.com/bugsnag/bugsnag-cocoa/pull/458) + ## Bug fixes * Fix possible report corruption when using `notify()` from multiple threads diff --git a/Source/BugsnagConfiguration.h b/Source/BugsnagConfiguration.h index 683d80e2a..e7b3467f6 100644 --- a/Source/BugsnagConfiguration.h +++ b/Source/BugsnagConfiguration.h @@ -84,6 +84,7 @@ typedef NSDictionary *_Nullable (^BugsnagBeforeNotifyHook)( NSArray *_Nonnull rawEventReports, NSDictionary *_Nonnull report); @interface BugsnagConfiguration : NSObject + /** * The API key of a Bugsnag project */ diff --git a/Source/BugsnagConfiguration.m b/Source/BugsnagConfiguration.m index 64969b744..53a798825 100644 --- a/Source/BugsnagConfiguration.m +++ b/Source/BugsnagConfiguration.m @@ -57,6 +57,21 @@ @interface BugsnagConfiguration () @implementation BugsnagConfiguration +// MARK: - Class Methods + +/** + * Determine the apiKey-validity of a passed-in string: + * Exactly 32 hexadecimal digits. + */ ++ (BOOL)isValidApiKey:(NSString *)apiKey { + NSCharacterSet *chars = [[NSCharacterSet + characterSetWithCharactersInString:@"0123456789ABCDEF"] invertedSet]; + BOOL isHex = (NSNotFound == [[apiKey uppercaseString] rangeOfCharacterFromSet:chars].location); + return isHex && [apiKey length] == BSGApiKeyLength; +} + +// MARK: - Instance Methods + /** * Should not be called, but if it _is_ then fail meaningfully rather than silently */ @@ -70,7 +85,7 @@ - (instancetype)init { -(instancetype)initWithApiKey:(NSString *)apiKey error:(NSError * _Nullable __autoreleasing * _Nullable)error { - if (! [self isValidApiKey:apiKey]) { + if (! [BugsnagConfiguration isValidApiKey:apiKey]) { *error = [NSError errorWithDomain:BSGConfigurationErrorDomain code:BSGConfigurationErrorInvalidApiKey userInfo:nil]; @@ -279,7 +294,7 @@ - (NSString *)apiKey { } - (void)setApiKey:(NSString *)apiKey { - if ([self isValidApiKey:apiKey]) { + if ([BugsnagConfiguration isValidApiKey:apiKey]) { [self willChangeValueForKey:NSStringFromSelector(@selector(apiKey))]; _apiKey = apiKey; [self didChangeValueForKey:NSStringFromSelector(@selector(apiKey))]; @@ -319,17 +334,6 @@ - (BOOL)isValidUrl:(NSURL *)url { return url != nil && url.scheme != nil && url.host != nil; } -/** - * Determine the apiKey-validity of a passed-in string: - * Exactly 32 hexadecimal digits. - */ -- (BOOL)isValidApiKey:(NSString *)apiKey { - NSCharacterSet *chars = [[NSCharacterSet - characterSetWithCharactersInString:@"0123456789ABCDEF"] invertedSet]; - BOOL isHex = (NSNotFound == [[apiKey uppercaseString] rangeOfCharacterFromSet:chars].location); - return isHex && [apiKey length] == BSGApiKeyLength; -} - - (NSUInteger)maxBreadcrumbs { return self.breadcrumbs.capacity; } diff --git a/Source/BugsnagEvent.h b/Source/BugsnagEvent.h index 00ce400dc..0e5cdd879 100644 --- a/Source/BugsnagEvent.h +++ b/Source/BugsnagEvent.h @@ -177,6 +177,13 @@ __deprecated_msg("Use toJson: instead."); */ @property(readonly, nonnull) BugsnagHandledState *handledState; +/** + * A per-event override for the apiKey. + * - Reads default to the BugsnagConfiguration apiKey value unless explicitly set. + * - Writes are not persisted to BugsnagConfiguration. + */ +@property(readwrite, copy, nonnull) NSString *apiKey; + /** * Property overrides */ diff --git a/Source/BugsnagEvent.m b/Source/BugsnagEvent.m index 4b16a2df2..cb3777ca6 100644 --- a/Source/BugsnagEvent.m +++ b/Source/BugsnagEvent.m @@ -191,6 +191,10 @@ + (instancetype)errorDataFromThreads:(NSArray *)threads; - (instancetype)initWithClass:(NSString *_Nonnull)errorClass message:(NSString *_Nonnull)errorMessage NS_DESIGNATED_INITIALIZER; @end +@interface BugsnagConfiguration (BugsnagEvent) ++ (BOOL)isValidApiKey:(NSString *_Nullable)apiKey; +@end + @interface BugsnagEvent () /** @@ -368,6 +372,28 @@ - (void)addMetadata:(NSDictionary *_Nonnull)tabData self.metadata = allMetadata; } +@synthesize apiKey = _apiKey; + +- (NSString *)apiKey { + if (! _apiKey) { + _apiKey = Bugsnag.configuration.apiKey; + } + return _apiKey; +} + + +- (void)setApiKey:(NSString *)apiKey { + if ([BugsnagConfiguration isValidApiKey:apiKey]) { + _apiKey = apiKey; + } + + // A malformed apiKey should not cause an error: the fallback global value + // in BugsnagConfiguration will do to get the event reported. + else { + bsg_log_warn(@"Attempted to set an invalid Event API key."); + } +} + - (void)addAttribute:(NSString *)attributeName withValue:(id)value toTabWithName:(NSString *)tabName { diff --git a/Tests/BugsnagEventTests.m b/Tests/BugsnagEventTests.m index de9178844..8dc64441c 100644 --- a/Tests/BugsnagEventTests.m +++ b/Tests/BugsnagEventTests.m @@ -26,14 +26,14 @@ - (void)testNotifyReleaseStagesSendsFromConfig { config.releaseStage = @"foo"; BugsnagHandledState *state = [BugsnagHandledState handledStateWithSeverityReason:HandledException]; - BugsnagEvent *report = + BugsnagEvent *event = [[BugsnagEvent alloc] initWithErrorName:@"Bad error" errorMessage:@"it was so bad" configuration:config metadata:@{} handledState:state session:nil]; - XCTAssertTrue([report shouldBeSent]); + XCTAssertTrue([event shouldBeSent]); } - (void)testNotifyReleaseStagesSkipsSendFromConfig { @@ -43,14 +43,14 @@ - (void)testNotifyReleaseStagesSkipsSendFromConfig { BugsnagHandledState *state = [BugsnagHandledState handledStateWithSeverityReason:HandledException]; - BugsnagEvent *report = + BugsnagEvent *event = [[BugsnagEvent alloc] initWithErrorName:@"Bad error" errorMessage:@"it was so bad" configuration:config metadata:@{} handledState:state session:nil]; - XCTAssertFalse([report shouldBeSent]); + XCTAssertFalse([event shouldBeSent]); } - (void)testSessionJson { @@ -66,14 +66,14 @@ - (void)testSessionJson { bugsnagSession.handledCount = 2; bugsnagSession.unhandledCount = 1; - BugsnagEvent *report = + BugsnagEvent *event = [[BugsnagEvent alloc] initWithErrorName:@"Bad error" errorMessage:@"it was so bad" configuration:config metadata:@{} handledState:state session:bugsnagSession]; - NSDictionary *json = [report toJson]; + NSDictionary *json = [event toJson]; XCTAssertNotNil(json); NSDictionary *session = json[@"session"]; @@ -89,35 +89,35 @@ - (void)testSessionJson { } - (void)testDefaultErrorMessageNilForEmptyThreads { - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:@{ + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{ @"threads" : @[] }]; - NSDictionary *payload = [report toJson]; + NSDictionary *payload = [event toJson]; XCTAssertEqualObjects(@"Exception", payload[@"exceptions"][0][@"errorClass"]); XCTAssertEqualObjects(@"", payload[@"exceptions"][0][@"message"]); - XCTAssertEqualObjects(report.errorClass, + XCTAssertEqualObjects(event.errorClass, payload[@"exceptions"][0][@"errorClass"]); - XCTAssertEqualObjects(report.errorMessage, + XCTAssertEqualObjects(event.errorMessage, payload[@"exceptions"][0][@"message"]); } - (void)testEnhancedErrorMessageNilForEmptyNotableAddresses { - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:@{ + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{ @"threads" : @[ @{@"crashed" : @YES, @"notable_addresses" : @{}} ] }]; - NSDictionary *payload = [report toJson]; + NSDictionary *payload = [event toJson]; XCTAssertEqualObjects(@"Exception", payload[@"exceptions"][0][@"errorClass"]); XCTAssertEqualObjects(@"", payload[@"exceptions"][0][@"message"]); - XCTAssertEqualObjects(report.errorClass, + XCTAssertEqualObjects(event.errorClass, payload[@"exceptions"][0][@"errorClass"]); - XCTAssertEqualObjects(report.errorMessage, + XCTAssertEqualObjects(event.errorMessage, payload[@"exceptions"][0][@"message"]); } - (void)testEnhancedErrorMessageForFatalErrorWithoutAdditionalMessage { - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:@{ + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{ @"crash" : @{ @"threads" : @[ @{ @"crashed" : @YES, @@ -131,18 +131,18 @@ - (void)testEnhancedErrorMessageForFatalErrorWithoutAdditionalMessage { } ] } }]; - NSDictionary *payload = [report toJson]; + NSDictionary *payload = [event toJson]; XCTAssertEqualObjects(@"fatal error", payload[@"exceptions"][0][@"errorClass"]); XCTAssertEqualObjects(@"", payload[@"exceptions"][0][@"message"]); - XCTAssertEqualObjects(report.errorClass, + XCTAssertEqualObjects(event.errorClass, payload[@"exceptions"][0][@"errorClass"]); - XCTAssertEqualObjects(report.errorMessage, + XCTAssertEqualObjects(event.errorMessage, payload[@"exceptions"][0][@"message"]); } - (void)testEnhancedErrorMessageForAssertionWithoutAdditionalMessage { - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:@{ + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{ @"crash" : @{ @"threads" : @[ @{ @"crashed" : @YES, @@ -156,13 +156,13 @@ - (void)testEnhancedErrorMessageForAssertionWithoutAdditionalMessage { } ] } }]; - NSDictionary *payload = [report toJson]; + NSDictionary *payload = [event toJson]; XCTAssertEqualObjects(@"assertion failed", payload[@"exceptions"][0][@"errorClass"]); XCTAssertEqualObjects(@"", payload[@"exceptions"][0][@"message"]); - XCTAssertEqualObjects(report.errorClass, + XCTAssertEqualObjects(event.errorClass, payload[@"exceptions"][0][@"errorClass"]); - XCTAssertEqualObjects(report.errorMessage, + XCTAssertEqualObjects(event.errorMessage, payload[@"exceptions"][0][@"message"]); } @@ -171,7 +171,7 @@ - (void)testEnhancedErrorMessageForAssertionError { @"assertion failed", @"Assertion failed", @"fatal error", @"Fatal error" ]) { - BugsnagEvent *report = + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{ @"crash" : @{ @"threads" : @[ @{ @@ -191,20 +191,20 @@ - (void)testEnhancedErrorMessageForAssertionError { } ] } }]; - NSDictionary *payload = [report toJson]; + NSDictionary *payload = [event toJson]; XCTAssertEqualObjects(assertionName, payload[@"exceptions"][0][@"errorClass"]); XCTAssertEqualObjects(@"Something went wrong", payload[@"exceptions"][0][@"message"]); - XCTAssertEqualObjects(report.errorClass, + XCTAssertEqualObjects(event.errorClass, payload[@"exceptions"][0][@"errorClass"]); - XCTAssertEqualObjects(report.errorMessage, + XCTAssertEqualObjects(event.errorMessage, payload[@"exceptions"][0][@"message"]); } } - (void)testEnhancedErrorMessageIgnoresFilePaths { - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:@{ + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{ @"crash" : @{ @"threads" : @[ @{ @"crashed" : @YES, @@ -223,18 +223,18 @@ - (void)testEnhancedErrorMessageIgnoresFilePaths { } ] } }]; - NSDictionary *payload = [report toJson]; + NSDictionary *payload = [event toJson]; XCTAssertEqualObjects(@"fatal error", payload[@"exceptions"][0][@"errorClass"]); XCTAssertEqualObjects(@"", payload[@"exceptions"][0][@"message"]); - XCTAssertEqualObjects(report.errorClass, + XCTAssertEqualObjects(event.errorClass, payload[@"exceptions"][0][@"errorClass"]); - XCTAssertEqualObjects(report.errorMessage, + XCTAssertEqualObjects(event.errorMessage, payload[@"exceptions"][0][@"message"]); } - (void)testEnhancedErrorMessageIgnoresNonStrings { - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:@{ + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{ @"crash" : @{ @"threads" : @[ @{ @"crashed" : @YES, @@ -253,18 +253,18 @@ - (void)testEnhancedErrorMessageIgnoresNonStrings { } ] } }]; - NSDictionary *payload = [report toJson]; + NSDictionary *payload = [event toJson]; XCTAssertEqualObjects(@"fatal error", payload[@"exceptions"][0][@"errorClass"]); XCTAssertEqualObjects(@"", payload[@"exceptions"][0][@"message"]); - XCTAssertEqualObjects(report.errorClass, + XCTAssertEqualObjects(event.errorClass, payload[@"exceptions"][0][@"errorClass"]); - XCTAssertEqualObjects(report.errorMessage, + XCTAssertEqualObjects(event.errorMessage, payload[@"exceptions"][0][@"message"]); } - (void)testEnhancedErrorMessageConcatenatesMultipleMessages { - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:@{ + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{ @"crash" : @{ @"threads" : @[ @{ @"crashed" : @YES, @@ -288,19 +288,19 @@ - (void)testEnhancedErrorMessageConcatenatesMultipleMessages { } ] } }]; - NSDictionary *payload = [report toJson]; + NSDictionary *payload = [event toJson]; XCTAssertEqualObjects(@"Fatal error", payload[@"exceptions"][0][@"errorClass"]); XCTAssertEqualObjects(@"A message from beyond | Wo0o0o", payload[@"exceptions"][0][@"message"]); - XCTAssertEqualObjects(report.errorClass, + XCTAssertEqualObjects(event.errorClass, payload[@"exceptions"][0][@"errorClass"]); - XCTAssertEqualObjects(report.errorMessage, + XCTAssertEqualObjects(event.errorMessage, payload[@"exceptions"][0][@"message"]); } - (void)testEnhancedErrorMessageIgnoresUnknownAssertionTypes { - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:@{ + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{ @"crash" : @{ @"threads" : @[ @{ @"crashed" : @YES, @@ -319,49 +319,49 @@ - (void)testEnhancedErrorMessageIgnoresUnknownAssertionTypes { } ] } }]; - NSDictionary *payload = [report toJson]; + NSDictionary *payload = [event toJson]; XCTAssertEqualObjects(@"Exception", payload[@"exceptions"][0][@"errorClass"]); XCTAssertEqualObjects(@"", payload[@"exceptions"][0][@"message"]); - XCTAssertEqualObjects(report.errorClass, + XCTAssertEqualObjects(event.errorClass, payload[@"exceptions"][0][@"errorClass"]); - XCTAssertEqualObjects(report.errorMessage, + XCTAssertEqualObjects(event.errorMessage, payload[@"exceptions"][0][@"message"]); } - (void)testEmptyReport { - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:@{}]; - XCTAssertNil(report); + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{}]; + XCTAssertNil(event); } - (void)testUnhandledReportDepth { // unhandled reports should calculate their own depth NSDictionary *dict = @{@"user.depth": @2}; - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:dict]; - XCTAssertEqual(report.depth, 0); + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:dict]; + XCTAssertEqual(event.depth, 0); } - (void)testHandledReportDepth { // handled reports should use the serialised depth BugsnagHandledState *state = [BugsnagHandledState handledStateWithSeverityReason:HandledException]; NSDictionary *dict = @{@"user.depth": @2, @"user.handledState": [state toJson]}; - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:dict]; - XCTAssertEqual(report.depth, 2); + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:dict]; + XCTAssertEqual(event.depth, 2); } - (void)testUnhandledReportSeverity { // unhandled reports should calculate their own severity NSDictionary *dict = @{@"user.state.crash.severity": @"info"}; - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:dict]; - XCTAssertEqual(report.severity, BSGSeverityError); + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:dict]; + XCTAssertEqual(event.severity, BSGSeverityError); } - (void)testHandledReportSeverity { // handled reports should use the serialised depth BugsnagHandledState *state = [BugsnagHandledState handledStateWithSeverityReason:HandledException]; NSDictionary *dict = @{@"user.state.crash.severity": @"info", @"user.handledState": [state toJson]}; - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:dict]; - XCTAssertEqual(report.severity, BSGSeverityWarning); + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:dict]; + XCTAssertEqual(event.severity, BSGSeverityWarning); } - (void)testHandledReportMetaData { @@ -370,10 +370,10 @@ - (void)testHandledReportMetaData { [metadata addAttribute:@"Foo" withValue:@"Bar" toTabWithName:@"Custom"]; NSDictionary *dict = @{@"user.handledState": [state toJson], @"user.metaData": [metadata toDictionary]}; - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:dict]; - XCTAssertNotNil(report.metadata); - XCTAssertEqual(report.metadata.count, 1); - XCTAssertEqualObjects(report.metadata[@"Custom"][@"Foo"], @"Bar"); + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:dict]; + XCTAssertNotNil(event.metadata); + XCTAssertEqual(event.metadata.count, 1); + XCTAssertEqualObjects(event.metadata[@"Custom"][@"Foo"], @"Bar"); } - (void)testUnhandledReportMetaData { @@ -381,10 +381,10 @@ - (void)testUnhandledReportMetaData { [metadata addAttribute:@"Foo" withValue:@"Bar" toTabWithName:@"Custom"]; NSDictionary *dict = @{@"user.metaData": [metadata toDictionary]}; - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:dict]; - XCTAssertNotNil(report.metadata); - XCTAssertEqual(report.metadata.count, 1); - XCTAssertEqualObjects(report.metadata[@"Custom"][@"Foo"], @"Bar"); + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:dict]; + XCTAssertNotNil(event.metadata); + XCTAssertEqual(event.metadata.count, 1); + XCTAssertEqualObjects(event.metadata[@"Custom"][@"Foo"], @"Bar"); } - (void)testAppVersionOverride { @@ -403,13 +403,67 @@ - (void)testAppVersionOverride { } - (void)testReportAddAttr { - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:@{@"user.metaData": @{@"user": @{@"id": @"user id"}}}]; - [report addAttribute:@"foo" withValue:@"bar" toTabWithName:@"user"]; + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{@"user.metaData": @{@"user": @{@"id": @"user id"}}}]; + [event addAttribute:@"foo" withValue:@"bar" toTabWithName:@"user"]; } - (void)testReportAddMetadata { - BugsnagEvent *report = [[BugsnagEvent alloc] initWithKSReport:@{@"user.metaData": @{@"user": @{@"id": @"user id"}}}]; - [report addMetadata:@{@"foo": @"bar"} toTabWithName:@"user"]; + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{@"user.metaData": @{@"user": @{@"id": @"user id"}}}]; + [event addMetadata:@{@"foo": @"bar"} toTabWithName:@"user"]; +} + + +/** + * Test that BugsnagEvent has an apiKey value and supports non-persistent + * per-event changes to apiKey. + */ +- (void)testApiKey { + NSError *error; + BugsnagConfiguration *config = [[BugsnagConfiguration alloc] initWithApiKey:DUMMY_APIKEY_32CHAR_1 error:&error]; + [Bugsnag startBugsnagWithConfiguration:config]; + + NSException *ex = [[NSException alloc] initWithName:@"myName" reason:@"myReason1" userInfo:nil]; + + // Check that the event is passed the apiKey + [Bugsnag notify:ex block:^(BugsnagEvent * _Nonnull event) { + XCTAssertEqual(event.apiKey, DUMMY_APIKEY_32CHAR_1); + }]; + + // Check that we can change it + [Bugsnag notify:ex block:^(BugsnagEvent * _Nonnull event) { + XCTAssertEqual(event.apiKey, DUMMY_APIKEY_32CHAR_1); + event.apiKey = DUMMY_APIKEY_32CHAR_2; + XCTAssertEqual(event.apiKey, DUMMY_APIKEY_32CHAR_2); + XCTAssertEqual(Bugsnag.configuration.apiKey, DUMMY_APIKEY_32CHAR_1); + }]; + + // Check that the global configuration is unaffected + [Bugsnag notify:ex block:^(BugsnagEvent * _Nonnull event) { + XCTAssertEqual(event.apiKey, DUMMY_APIKEY_32CHAR_1); + event.apiKey = DUMMY_APIKEY_32CHAR_1; + XCTAssertEqual(event.apiKey, DUMMY_APIKEY_32CHAR_1); + XCTAssertEqual(Bugsnag.configuration.apiKey, DUMMY_APIKEY_32CHAR_1); + event.apiKey = DUMMY_APIKEY_32CHAR_3; + XCTAssertEqual(event.apiKey, DUMMY_APIKEY_32CHAR_3); + }]; + + // Check that previous local and global values are not persisted erroneously + Bugsnag.configuration.apiKey = DUMMY_APIKEY_32CHAR_4; + [Bugsnag notify:ex block:^(BugsnagEvent * _Nonnull event) { + XCTAssertEqual(event.apiKey, DUMMY_APIKEY_32CHAR_4); + event.apiKey = DUMMY_APIKEY_32CHAR_1; + XCTAssertEqual(event.apiKey, DUMMY_APIKEY_32CHAR_1); + XCTAssertEqual(Bugsnag.configuration.apiKey, DUMMY_APIKEY_32CHAR_4); + event.apiKey = DUMMY_APIKEY_32CHAR_2; + XCTAssertEqual(event.apiKey, DUMMY_APIKEY_32CHAR_2); + }]; + + // Check that validation is performed and that invalid API keys can't be set + Bugsnag.configuration.apiKey = DUMMY_APIKEY_32CHAR_1; + [Bugsnag notify:ex block:^(BugsnagEvent * _Nonnull event) { + event.apiKey = DUMMY_APIKEY_16CHAR; + XCTAssertEqual(event.apiKey, DUMMY_APIKEY_32CHAR_1); + }]; } @end diff --git a/Tests/BugsnagTestConstants.h b/Tests/BugsnagTestConstants.h index 4b63cadb2..599cbda01 100644 --- a/Tests/BugsnagTestConstants.h +++ b/Tests/BugsnagTestConstants.h @@ -17,6 +17,8 @@ // One string to rule(r) them all: 12345678901234567890123456789012345678901234567890 static NSString * _Nonnull const DUMMY_APIKEY_32CHAR_1 = @"0192837465afbecd0192837465afbecd"; // the correct length static NSString * _Nonnull const DUMMY_APIKEY_32CHAR_2 = @"aabbccddeeff00112233445566778899"; +static NSString * _Nonnull const DUMMY_APIKEY_32CHAR_3 = @"99887766554433221100ffeeddccbbaa"; +static NSString * _Nonnull const DUMMY_APIKEY_32CHAR_4 = @"98765432109876543210abcdefabcdef"; static NSString * _Nonnull const DUMMY_APIKEY_16CHAR = @"0192837465afbecd"; // too short static NSString * _Nonnull const DUMMY_APIKEY_48CHAR = @"0192837465afbecd0192837465afbecd0192837465afbecd"; // too long diff --git a/iOS/Bugsnag.xcodeproj/project.pbxproj b/iOS/Bugsnag.xcodeproj/project.pbxproj index 65bfbd65b..94b567ae7 100644 --- a/iOS/Bugsnag.xcodeproj/project.pbxproj +++ b/iOS/Bugsnag.xcodeproj/project.pbxproj @@ -7,14 +7,13 @@ objects = { /* Begin PBXBuildFile section */ - 4B47970A22A9AE1F00FF9C2E /* BugsnagEventFromKSCrashReportTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B47970922A9AE1F00FF9C2E /* BugsnagEventFromKSCrashReportTest.m */; }; 000DF29423DB4B4900A883CE /* TestConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 000DF29323DB4B4900A883CE /* TestConstants.m */; }; 000E6E9E23D8690F009D8194 /* BugsnagSwiftConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000E6E9D23D8690F009D8194 /* BugsnagSwiftConfigurationTests.swift */; }; 000E6EA323D8AC8C009D8194 /* UPGRADING.md in Resources */ = {isa = PBXBuildFile; fileRef = 000E6E9F23D8AC8C009D8194 /* UPGRADING.md */; }; 000E6EA423D8AC8C009D8194 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 000E6EA023D8AC8C009D8194 /* README.md */; }; 000E6EA523D8AC8C009D8194 /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 000E6EA123D8AC8C009D8194 /* VERSION */; }; 000E6EA623D8AC8C009D8194 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = 000E6EA223D8AC8C009D8194 /* CHANGELOG.md */; }; - 4B47970A22A9AE1F00FF9C2E /* BugsnagCrashReportFromKSCrashReportTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B47970922A9AE1F00FF9C2E /* BugsnagCrashReportFromKSCrashReportTest.m */; }; + 4B47970A22A9AE1F00FF9C2E /* BugsnagEventFromKSCrashReportTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B47970922A9AE1F00FF9C2E /* BugsnagEventFromKSCrashReportTest.m */; }; 4B775FCF22CBDEB4004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B775FCE22CBDEB4004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */; }; 4BE6C42622CAD61A0056305D /* BugsnagCollectionsBSGDictMergeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BE6C42522CAD61A0056305D /* BugsnagCollectionsBSGDictMergeTest.m */; }; 8A2C8F231C6BBD2300846019 /* Bugsnag.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A2C8F181C6BBD2300846019 /* Bugsnag.framework */; }; @@ -395,6 +394,7 @@ E7397EEA1F83CFC20034242A /* BugsnagConfiguration.h in CopyFiles */, E7397EEB1F83CFC20034242A /* BugsnagEvent.h in CopyFiles */, E7397EEC1F83CFC20034242A /* BugsnagMetadata.h in CopyFiles */, + E7397EEC1F83CFC20034242A /* BugsnagMetadata.h in CopyFiles */, E7397EED1F83CFC20034242A /* BugsnagNotifier.h in CopyFiles */, E7397EEE1F83CFC20034242A /* BugsnagSink.h in CopyFiles */, E7397EEF1F83CFC20034242A /* BugsnagHandledState.h in CopyFiles */, @@ -1319,6 +1319,7 @@ E7397E2C1F83BC2A0034242A /* BugsnagConfiguration.m in Sources */, E7397E2D1F83BC2A0034242A /* BugsnagEvent.m in Sources */, E7397E2E1F83BC2A0034242A /* BugsnagMetadata.m in Sources */, + E7397E2E1F83BC2A0034242A /* BugsnagMetadata.m in Sources */, E7397E2F1F83BC2A0034242A /* BugsnagNotifier.m in Sources */, E7397E301F83BC2A0034242A /* BugsnagSink.m in Sources */, E7397E311F83BC2A0034242A /* BugsnagHandledState.m in Sources */,