FIRFADApiServiceTests.m 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. //
  2. // Copyright 2020 Google LLC
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. #import <Foundation/Foundation.h>
  16. #import <OCMock/OCMock.h>
  17. #import <XCTest/XCTest.h>
  18. #import "FirebaseAppDistribution/Sources/FIRFADApiService.h"
  19. #import "FirebaseAppDistribution/Sources/FIRFADLogger.h"
  20. #import "FirebaseCore/Extension/FirebaseCoreInternal.h"
  21. #import "FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h"
  22. NSString *const kFakeErrorDomain = @"test.failure.domain";
  23. @interface FIRFADApiServiceTests : XCTestCase
  24. @end
  25. @interface FIRFADApiService (PrivateUnitTesting)
  26. + (NSString *)tryParseGoogleAPIErrorFromResponse:(NSData *)data;
  27. @end
  28. @implementation FIRFADApiServiceTests {
  29. id _mockFIRAppClass;
  30. id _mockURLSession;
  31. id _mockFIRInstallations;
  32. id _mockInstallationToken;
  33. NSString *_mockAuthToken;
  34. NSString *_mockInstallationId;
  35. NSString *_mockAPINotEnabledMessage;
  36. NSDictionary *_mockReleases;
  37. NSDictionary *_mockAPINotEnabledResponse;
  38. }
  39. - (void)setUp {
  40. [super setUp];
  41. _mockFIRAppClass = OCMClassMock([FIRApp class]);
  42. _mockURLSession = OCMClassMock([NSURLSession class]);
  43. _mockFIRInstallations = OCMClassMock([FIRInstallations class]);
  44. _mockInstallationToken = OCMClassMock([FIRInstallationsAuthTokenResult class]);
  45. _mockAuthToken = @"this-is-an-auth-token";
  46. OCMStub([_mockFIRAppClass defaultApp]).andReturn(_mockFIRAppClass);
  47. OCMStub([_mockURLSession sharedSession]).andReturn(_mockURLSession);
  48. OCMStub([_mockFIRInstallations installations]).andReturn(_mockFIRInstallations);
  49. OCMStub([_mockInstallationToken authToken]).andReturn(_mockAuthToken);
  50. _mockInstallationId = @"this-id-is-fake-ccccc";
  51. _mockReleases = @{
  52. @"releases" : @[
  53. @{
  54. @"displayVersion" : @"1.0.0",
  55. @"buildVersion" : @"111",
  56. @"releaseNotes" : @"This is a release",
  57. @"downloadURL" : @"http://faketyfakefake.download"
  58. },
  59. @{
  60. @"latest" : @YES,
  61. @"displayVersion" : @"1.0.1",
  62. @"buildVersion" : @"112",
  63. @"releaseNotes" : @"This is a release too",
  64. @"downloadURL" : @"http://faketyfakefake.download"
  65. }
  66. ]
  67. };
  68. _mockAPINotEnabledMessage =
  69. @"This is a long message about what's happening. This is a fake message from the Firebase "
  70. @"App Testers API in project 123456789. This should be logged.";
  71. _mockAPINotEnabledResponse = @{
  72. @"error" : @{
  73. @"code" : @403,
  74. @"message" : _mockAPINotEnabledMessage,
  75. @"status" : @"PERMISSION_DENIED",
  76. @"details" : @[ @{
  77. @"type" : @"type.fakeapis.com/appdistro.api.Help",
  78. @"links" : @[ @{
  79. @"description" : @"this is a short statement about enabling the api",
  80. @"url" : @"this should be a link"
  81. } ],
  82. } ],
  83. }
  84. };
  85. }
  86. - (void)tearDown {
  87. [super tearDown];
  88. [_mockFIRAppClass stopMocking];
  89. [_mockFIRInstallations stopMocking];
  90. [_mockInstallationToken stopMocking];
  91. [_mockURLSession stopMocking];
  92. }
  93. - (void)mockInstallationAuthCompletion:(FIRInstallationsAuthTokenResult *_Nullable)token
  94. error:(NSError *_Nullable)error {
  95. [OCMStub([_mockFIRInstallations authTokenWithCompletion:OCMOCK_ANY])
  96. andDo:^(NSInvocation *invocation) {
  97. void (^handler)(FIRInstallationsAuthTokenResult *_Nullable authTokenResult,
  98. NSError *_Nullable error);
  99. [invocation getArgument:&handler atIndex:2];
  100. handler(token, error);
  101. }];
  102. }
  103. - (void)verifyInstallationAuthCompletion {
  104. OCMVerify([_mockFIRInstallations authTokenWithCompletion:[OCMArg isNotNil]]);
  105. }
  106. - (void)rejectInstallationAuthCompletion {
  107. OCMReject([_mockFIRInstallations authTokenWithCompletion:[OCMArg isNotNil]]);
  108. }
  109. - (void)mockInstallationIdCompletion:(NSString *_Nullable)identifier
  110. error:(NSError *_Nullable)error {
  111. [OCMStub([_mockFIRInstallations installationIDWithCompletion:OCMOCK_ANY])
  112. andDo:^(NSInvocation *invocation) {
  113. void (^handler)(NSString *identifier, NSError *_Nullable error);
  114. [invocation getArgument:&handler atIndex:2];
  115. handler(identifier, error);
  116. }];
  117. }
  118. - (void)verifyInstallationIdCompletion {
  119. OCMVerify([_mockFIRInstallations installationIDWithCompletion:[OCMArg isNotNil]]);
  120. }
  121. - (void)rejectInstallationIdCompletion {
  122. OCMReject([_mockFIRInstallations installationIDWithCompletion:[OCMArg isNotNil]]);
  123. }
  124. - (void)mockUrlSessionResponse:(NSDictionary *)dictionary
  125. response:(NSURLResponse *)response
  126. error:(NSError *)error {
  127. NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&error];
  128. [self mockUrlSessionResponseWithData:data response:response error:error];
  129. }
  130. - (void)mockUrlSessionResponseWithData:(NSData *)data
  131. response:(NSURLResponse *)response
  132. error:(NSError *)error {
  133. [OCMStub([_mockURLSession dataTaskWithRequest:[OCMArg any]
  134. completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) {
  135. void (^handler)(NSData *data, NSURLResponse *response, NSError *error);
  136. [invocation getArgument:&handler atIndex:3];
  137. handler(data, response, error);
  138. }];
  139. }
  140. - (void)verifyUrlSessionResponseWithData {
  141. OCMVerify([_mockURLSession dataTaskWithRequest:[OCMArg isNotNil]
  142. completionHandler:[OCMArg isNotNil]]);
  143. }
  144. - (void)rejectUrlSessionResponseWithData {
  145. OCMReject([_mockURLSession dataTaskWithRequest:[OCMArg isNotNil]
  146. completionHandler:[OCMArg isNotNil]]);
  147. }
  148. - (void)testTryParseGoogleAPIErrorFromResponseSuccess {
  149. NSData *data = [NSJSONSerialization dataWithJSONObject:_mockAPINotEnabledResponse
  150. options:0
  151. error:nil];
  152. NSString *message = [FIRFADApiService tryParseGoogleAPIErrorFromResponse:data];
  153. XCTAssertTrue([message isEqualToString:_mockAPINotEnabledMessage]);
  154. }
  155. - (void)testTryParseGoogleAPIErrorFromNilResponse {
  156. NSString *message = [FIRFADApiService tryParseGoogleAPIErrorFromResponse:nil];
  157. XCTAssertTrue([message isEqualToString:@"No data in response."]);
  158. }
  159. - (void)testTryParseGoogleAPIErrorFromResponseParseFailure {
  160. NSData *data = [@"malformed{json[data" dataUsingEncoding:NSUTF8StringEncoding];
  161. NSString *message = [FIRFADApiService tryParseGoogleAPIErrorFromResponse:data];
  162. XCTAssertTrue(
  163. [message isEqualToString:@"Could not parse additional details about this API error."]);
  164. }
  165. - (void)testTryParseGoogleAPIErrorFromResponseNoErrorFailure {
  166. NSDictionary *errorDictionary = @{@"message" : @"This has no subdict"};
  167. NSData *data = [NSJSONSerialization dataWithJSONObject:errorDictionary options:0 error:nil];
  168. NSString *message = [FIRFADApiService tryParseGoogleAPIErrorFromResponse:data];
  169. XCTAssertTrue(
  170. [message isEqualToString:@"Could not parse additional details about this API error."]);
  171. }
  172. - (void)testTryParseGoogleAPIErrorFromResponseNoMessageFailure {
  173. NSDictionary *errorDictionary = @{@"error" : @{@"status" : @"This has no message"}};
  174. NSData *data = [NSJSONSerialization dataWithJSONObject:errorDictionary options:0 error:nil];
  175. NSString *message = [FIRFADApiService tryParseGoogleAPIErrorFromResponse:data];
  176. XCTAssertTrue(
  177. [message isEqualToString:@"Could not parse additional details about this API error."]);
  178. }
  179. - (void)testGenerateAuthTokenWithCompletionSuccess {
  180. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  181. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  182. XCTestExpectation *expectation =
  183. [self expectationWithDescription:@"Generate auth token succeeds."];
  184. [FIRFADApiService
  185. generateAuthTokenWithCompletion:^(NSString *_Nullable identifier,
  186. FIRInstallationsAuthTokenResult *_Nullable authTokenResult,
  187. NSError *_Nullable error) {
  188. XCTAssertNotNil(authTokenResult);
  189. XCTAssertNotNil(identifier);
  190. XCTAssertNil(error);
  191. XCTAssertTrue([identifier isEqualToString:self->_mockInstallationId]);
  192. XCTAssertTrue([[authTokenResult authToken] isEqualToString:self->_mockAuthToken]);
  193. [expectation fulfill];
  194. }];
  195. [self waitForExpectations:@[ expectation ] timeout:5.0];
  196. [self verifyInstallationAuthCompletion];
  197. [self verifyInstallationIdCompletion];
  198. }
  199. - (void)testGenerateAuthTokenWithCompletionAuthTokenFailure {
  200. [self mockInstallationAuthCompletion:nil
  201. error:[NSError errorWithDomain:kFakeErrorDomain
  202. code:1
  203. userInfo:@{}]];
  204. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  205. XCTestExpectation *expectation =
  206. [self expectationWithDescription:@"Generate auth token fails to generate auth token."];
  207. [FIRFADApiService
  208. generateAuthTokenWithCompletion:^(NSString *_Nullable identifier,
  209. FIRInstallationsAuthTokenResult *_Nullable authTokenResult,
  210. NSError *_Nullable error) {
  211. XCTAssertNil(identifier);
  212. XCTAssertNil(authTokenResult);
  213. XCTAssertNotNil(error);
  214. XCTAssertEqual(error.code, FIRFADApiTokenGenerationFailure);
  215. [expectation fulfill];
  216. }];
  217. [self waitForExpectations:@[ expectation ] timeout:5.0];
  218. [self verifyInstallationAuthCompletion];
  219. [self rejectInstallationIdCompletion];
  220. }
  221. - (void)testGenerateAuthTokenWithCompletionIDFailure {
  222. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  223. [self mockInstallationIdCompletion:nil
  224. error:[NSError errorWithDomain:kFakeErrorDomain
  225. code:1
  226. userInfo:@{}]];
  227. XCTestExpectation *expectation =
  228. [self expectationWithDescription:@"Generate auth token fails to find ID."];
  229. [FIRFADApiService
  230. generateAuthTokenWithCompletion:^(NSString *_Nullable identifier,
  231. FIRInstallationsAuthTokenResult *_Nullable authTokenResult,
  232. NSError *_Nullable error) {
  233. XCTAssertNil(identifier);
  234. XCTAssertNil(authTokenResult);
  235. XCTAssertNotNil(error);
  236. XCTAssertEqual([error code], FIRFADApiInstallationIdentifierError);
  237. [expectation fulfill];
  238. }];
  239. [self waitForExpectations:@[ expectation ] timeout:5.0];
  240. [self verifyInstallationAuthCompletion];
  241. [self verifyInstallationIdCompletion];
  242. }
  243. - (void)testFetchReleasesWithCompletionSuccess {
  244. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  245. OCMStub([fakeResponse statusCode]).andReturn(200);
  246. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  247. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  248. [self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
  249. XCTestExpectation *expectation =
  250. [self expectationWithDescription:@"Fetch releases succeeds with two releases."];
  251. [FIRFADApiService
  252. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  253. XCTAssertNil(error);
  254. XCTAssertNotNil(releases);
  255. XCTAssertEqual([releases count], 2);
  256. [expectation fulfill];
  257. }];
  258. [self waitForExpectations:@[ expectation ] timeout:5.0];
  259. [self verifyInstallationAuthCompletion];
  260. [self verifyInstallationIdCompletion];
  261. [self verifyUrlSessionResponseWithData];
  262. OCMVerify([fakeResponse statusCode]);
  263. }
  264. - (void)testFetchReleasesWithCompletionUnknownFailure {
  265. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  266. OCMStub([fakeResponse statusCode]).andReturn(200);
  267. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  268. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  269. [self mockUrlSessionResponse:_mockReleases
  270. response:fakeResponse
  271. error:[NSError errorWithDomain:kFakeErrorDomain code:1 userInfo:@{}]];
  272. XCTestExpectation *expectation =
  273. [self expectationWithDescription:@"Fetch releases fails with unknown error."];
  274. [FIRFADApiService
  275. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  276. XCTAssertNil(releases);
  277. XCTAssertNotNil(error);
  278. XCTAssertEqual(error.code, FIRApiErrorUnknownFailure);
  279. [expectation fulfill];
  280. }];
  281. [self waitForExpectations:@[ expectation ] timeout:5.0];
  282. [self verifyInstallationAuthCompletion];
  283. [self verifyInstallationIdCompletion];
  284. [self verifyUrlSessionResponseWithData];
  285. OCMVerify([fakeResponse statusCode]);
  286. }
  287. - (void)testFetchReleasesWithCompletionUnauthenticatedFailure {
  288. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  289. OCMStub([fakeResponse statusCode]).andReturn(401);
  290. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  291. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  292. [self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
  293. XCTestExpectation *expectation =
  294. [self expectationWithDescription:@"Fetch releases fails with unknown error."];
  295. [FIRFADApiService
  296. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  297. XCTAssertNil(releases);
  298. XCTAssertNotNil(error);
  299. XCTAssertEqual(error.code, FIRFADApiErrorUnauthenticated);
  300. [expectation fulfill];
  301. }];
  302. [self waitForExpectations:@[ expectation ] timeout:5.0];
  303. [self verifyInstallationAuthCompletion];
  304. [self verifyInstallationIdCompletion];
  305. [self verifyUrlSessionResponseWithData];
  306. OCMVerify([fakeResponse statusCode]);
  307. }
  308. - (void)testFetchReleasesWithCompletionUnauthorized400Failure {
  309. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  310. OCMStub([fakeResponse statusCode]).andReturn(400);
  311. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  312. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  313. [self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
  314. XCTestExpectation *expectation =
  315. [self expectationWithDescription:@"Fetch releases rejects with a 400."];
  316. [FIRFADApiService
  317. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  318. XCTAssertNil(releases);
  319. XCTAssertNotNil(error);
  320. XCTAssertEqual([error code], FIRFADApiErrorUnauthorized);
  321. [expectation fulfill];
  322. }];
  323. [self waitForExpectations:@[ expectation ] timeout:5.0];
  324. [self verifyInstallationAuthCompletion];
  325. [self verifyInstallationIdCompletion];
  326. [self verifyUrlSessionResponseWithData];
  327. OCMVerify([fakeResponse statusCode]);
  328. }
  329. - (void)testFetchReleasesWithCompletionUnauthorized403Failure {
  330. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  331. OCMStub([fakeResponse statusCode]).andReturn(403);
  332. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  333. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  334. [self mockUrlSessionResponse:_mockAPINotEnabledResponse response:fakeResponse error:nil];
  335. XCTestExpectation *expectation =
  336. [self expectationWithDescription:@"Fetch releases rejects with a 403."];
  337. [FIRFADApiService
  338. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  339. XCTAssertNil(releases);
  340. XCTAssertNotNil(error);
  341. XCTAssertEqual([error code], FIRFADApiErrorUnauthorized);
  342. [expectation fulfill];
  343. }];
  344. [self waitForExpectations:@[ expectation ] timeout:5.0];
  345. [self verifyInstallationAuthCompletion];
  346. [self verifyInstallationIdCompletion];
  347. [self verifyUrlSessionResponseWithData];
  348. OCMVerify([fakeResponse statusCode]);
  349. }
  350. - (void)testFetchReleasesWithCompletionUnauthorized404Failure {
  351. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  352. OCMStub([fakeResponse statusCode]).andReturn(404);
  353. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  354. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  355. [self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
  356. XCTestExpectation *expectation =
  357. [self expectationWithDescription:@"Fetch releases rejects with a 404."];
  358. [FIRFADApiService
  359. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  360. XCTAssertNil(releases);
  361. XCTAssertNotNil(error);
  362. XCTAssertEqual([error code], FIRFADApiErrorUnauthorized);
  363. [expectation fulfill];
  364. }];
  365. [self waitForExpectations:@[ expectation ] timeout:5.0];
  366. [self verifyInstallationAuthCompletion];
  367. [self verifyInstallationIdCompletion];
  368. [self verifyUrlSessionResponseWithData];
  369. OCMVerify([fakeResponse statusCode]);
  370. }
  371. - (void)testFetchReleasesWithCompletionTimeout408Failure {
  372. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  373. OCMStub([fakeResponse statusCode]).andReturn(408);
  374. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  375. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  376. [self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
  377. XCTestExpectation *expectation =
  378. [self expectationWithDescription:@"Fetch releases rejects with 408."];
  379. [FIRFADApiService
  380. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  381. XCTAssertNil(releases);
  382. XCTAssertNotNil(error);
  383. XCTAssertEqual([error code], FIRFADApiErrorTimeout);
  384. [expectation fulfill];
  385. }];
  386. [self waitForExpectations:@[ expectation ] timeout:5.0];
  387. [self verifyInstallationAuthCompletion];
  388. [self verifyInstallationIdCompletion];
  389. [self verifyUrlSessionResponseWithData];
  390. OCMVerify([fakeResponse statusCode]);
  391. }
  392. - (void)testFetchReleasesWithCompletionTimeout504Failure {
  393. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  394. OCMStub([fakeResponse statusCode]).andReturn(504);
  395. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  396. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  397. [self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
  398. XCTestExpectation *expectation =
  399. [self expectationWithDescription:@"Fetch releases rejects with a 504."];
  400. [FIRFADApiService
  401. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  402. XCTAssertNil(releases);
  403. XCTAssertNotNil(error);
  404. XCTAssertEqual([error code], FIRFADApiErrorTimeout);
  405. [expectation fulfill];
  406. }];
  407. [self waitForExpectations:@[ expectation ] timeout:5.0];
  408. [self verifyInstallationAuthCompletion];
  409. [self verifyInstallationIdCompletion];
  410. [self verifyUrlSessionResponseWithData];
  411. OCMVerify([fakeResponse statusCode]);
  412. }
  413. - (void)testFetchReleasesWithCompletionUnknownStatusCodeFailure {
  414. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  415. OCMStub([fakeResponse statusCode]).andReturn(500);
  416. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  417. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  418. [self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
  419. XCTestExpectation *expectation =
  420. [self expectationWithDescription:@"Fetch releases rejects with a 500."];
  421. [FIRFADApiService
  422. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  423. XCTAssertNil(releases);
  424. XCTAssertNotNil(error);
  425. XCTAssertEqual([error code], FIRApiErrorUnknownFailure);
  426. [expectation fulfill];
  427. }];
  428. [self waitForExpectations:@[ expectation ] timeout:5.0];
  429. [self verifyInstallationAuthCompletion];
  430. [self verifyInstallationIdCompletion];
  431. [self verifyUrlSessionResponseWithData];
  432. OCMVerify([fakeResponse statusCode]);
  433. }
  434. - (void)testFetchReleasesWithCompletionNoReleasesFoundSuccess {
  435. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  436. OCMStub([fakeResponse statusCode]).andReturn(200);
  437. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  438. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  439. [self mockUrlSessionResponse:@{@"releases" : @[]} response:fakeResponse error:nil];
  440. XCTestExpectation *expectation =
  441. [self expectationWithDescription:@"Fetch releases rejects with a not found exception."];
  442. [FIRFADApiService
  443. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  444. XCTAssertNotNil(releases);
  445. XCTAssertNil(error);
  446. XCTAssertEqual([releases count], 0);
  447. [expectation fulfill];
  448. }];
  449. [self waitForExpectations:@[ expectation ] timeout:5.0];
  450. [self verifyInstallationAuthCompletion];
  451. [self verifyInstallationIdCompletion];
  452. [self verifyUrlSessionResponseWithData];
  453. OCMVerify([fakeResponse statusCode]);
  454. }
  455. - (void)testFetchReleasesWithCompletionParsingFailure {
  456. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  457. OCMStub([fakeResponse statusCode]).andReturn(200);
  458. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  459. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  460. [self
  461. mockUrlSessionResponseWithData:[@"malformed{json[data" dataUsingEncoding:NSUTF8StringEncoding]
  462. response:fakeResponse
  463. error:nil];
  464. XCTestExpectation *expectation =
  465. [self expectationWithDescription:@"Fetch releases rejects with a parsing failure."];
  466. [FIRFADApiService
  467. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  468. XCTAssertNil(releases);
  469. XCTAssertNotNil(error);
  470. XCTAssertEqual([error code], FIRApiErrorParseFailure);
  471. [expectation fulfill];
  472. }];
  473. [self waitForExpectations:@[ expectation ] timeout:5.0];
  474. [self verifyInstallationAuthCompletion];
  475. [self verifyInstallationIdCompletion];
  476. [self verifyUrlSessionResponseWithData];
  477. OCMVerify([fakeResponse statusCode]);
  478. }
  479. @end