FIRFADApiServiceTests.m 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  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/Sources/Private/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)testTryParseGoogleAPIErrorFromResponseParseFailure {
  156. NSData *data = [@"malformed{json[data" dataUsingEncoding:NSUTF8StringEncoding];
  157. NSString *message = [FIRFADApiService tryParseGoogleAPIErrorFromResponse:data];
  158. XCTAssertTrue(
  159. [message isEqualToString:@"Could not parse additional details about this API error."]);
  160. }
  161. - (void)testTryParseGoogleAPIErrorFromResponseNoErrorFailure {
  162. NSDictionary *errorDictionary = @{@"message" : @"This has no subdict"};
  163. NSData *data = [NSJSONSerialization dataWithJSONObject:errorDictionary options:0 error:nil];
  164. NSString *message = [FIRFADApiService tryParseGoogleAPIErrorFromResponse:data];
  165. XCTAssertTrue(
  166. [message isEqualToString:@"Could not parse additional details about this API error."]);
  167. }
  168. - (void)testTryParseGoogleAPIErrorFromResponseNoMessageFailure {
  169. NSDictionary *errorDictionary = @{@"error" : @{@"status" : @"This has no message"}};
  170. NSData *data = [NSJSONSerialization dataWithJSONObject:errorDictionary options:0 error:nil];
  171. NSString *message = [FIRFADApiService tryParseGoogleAPIErrorFromResponse:data];
  172. XCTAssertTrue(
  173. [message isEqualToString:@"Could not parse additional details about this API error."]);
  174. }
  175. - (void)testGenerateAuthTokenWithCompletionSuccess {
  176. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  177. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  178. XCTestExpectation *expectation =
  179. [self expectationWithDescription:@"Generate auth token succeeds."];
  180. [FIRFADApiService
  181. generateAuthTokenWithCompletion:^(NSString *_Nullable identifier,
  182. FIRInstallationsAuthTokenResult *_Nullable authTokenResult,
  183. NSError *_Nullable error) {
  184. XCTAssertNotNil(authTokenResult);
  185. XCTAssertNotNil(identifier);
  186. XCTAssertNil(error);
  187. XCTAssertTrue([identifier isEqualToString:self->_mockInstallationId]);
  188. XCTAssertTrue([[authTokenResult authToken] isEqualToString:self->_mockAuthToken]);
  189. [expectation fulfill];
  190. }];
  191. [self waitForExpectations:@[ expectation ] timeout:5.0];
  192. [self verifyInstallationAuthCompletion];
  193. [self verifyInstallationIdCompletion];
  194. }
  195. - (void)testGenerateAuthTokenWithCompletionAuthTokenFailure {
  196. [self mockInstallationAuthCompletion:nil
  197. error:[NSError errorWithDomain:kFakeErrorDomain
  198. code:1
  199. userInfo:@{}]];
  200. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  201. XCTestExpectation *expectation =
  202. [self expectationWithDescription:@"Generate auth token fails to generate auth token."];
  203. [FIRFADApiService
  204. generateAuthTokenWithCompletion:^(NSString *_Nullable identifier,
  205. FIRInstallationsAuthTokenResult *_Nullable authTokenResult,
  206. NSError *_Nullable error) {
  207. XCTAssertNil(identifier);
  208. XCTAssertNil(authTokenResult);
  209. XCTAssertNotNil(error);
  210. XCTAssertEqual(error.code, FIRFADApiTokenGenerationFailure);
  211. [expectation fulfill];
  212. }];
  213. [self waitForExpectations:@[ expectation ] timeout:5.0];
  214. [self verifyInstallationAuthCompletion];
  215. [self rejectInstallationIdCompletion];
  216. }
  217. - (void)testGenerateAuthTokenWithCompletionIDFailure {
  218. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  219. [self mockInstallationIdCompletion:nil
  220. error:[NSError errorWithDomain:kFakeErrorDomain
  221. code:1
  222. userInfo:@{}]];
  223. XCTestExpectation *expectation =
  224. [self expectationWithDescription:@"Generate auth token fails to find ID."];
  225. [FIRFADApiService
  226. generateAuthTokenWithCompletion:^(NSString *_Nullable identifier,
  227. FIRInstallationsAuthTokenResult *_Nullable authTokenResult,
  228. NSError *_Nullable error) {
  229. XCTAssertNil(identifier);
  230. XCTAssertNil(authTokenResult);
  231. XCTAssertNotNil(error);
  232. XCTAssertEqual([error code], FIRFADApiInstallationIdentifierError);
  233. [expectation fulfill];
  234. }];
  235. [self waitForExpectations:@[ expectation ] timeout:5.0];
  236. [self verifyInstallationAuthCompletion];
  237. [self verifyInstallationIdCompletion];
  238. }
  239. - (void)testFetchReleasesWithCompletionSuccess {
  240. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  241. OCMStub([fakeResponse statusCode]).andReturn(200);
  242. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  243. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  244. [self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
  245. XCTestExpectation *expectation =
  246. [self expectationWithDescription:@"Fetch releases succeeds with two releases."];
  247. [FIRFADApiService
  248. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  249. XCTAssertNil(error);
  250. XCTAssertNotNil(releases);
  251. XCTAssertEqual([releases count], 2);
  252. [expectation fulfill];
  253. }];
  254. [self waitForExpectations:@[ expectation ] timeout:5.0];
  255. [self verifyInstallationAuthCompletion];
  256. [self verifyInstallationIdCompletion];
  257. [self verifyUrlSessionResponseWithData];
  258. OCMVerify([fakeResponse statusCode]);
  259. }
  260. - (void)testFetchReleasesWithCompletionUnknownFailure {
  261. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  262. OCMStub([fakeResponse statusCode]).andReturn(200);
  263. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  264. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  265. [self mockUrlSessionResponse:_mockReleases
  266. response:fakeResponse
  267. error:[NSError errorWithDomain:kFakeErrorDomain code:1 userInfo:@{}]];
  268. XCTestExpectation *expectation =
  269. [self expectationWithDescription:@"Fetch releases fails with unknown error."];
  270. [FIRFADApiService
  271. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  272. XCTAssertNil(releases);
  273. XCTAssertNotNil(error);
  274. XCTAssertEqual(error.code, FIRApiErrorUnknownFailure);
  275. [expectation fulfill];
  276. }];
  277. [self waitForExpectations:@[ expectation ] timeout:5.0];
  278. [self verifyInstallationAuthCompletion];
  279. [self verifyInstallationIdCompletion];
  280. [self verifyUrlSessionResponseWithData];
  281. OCMVerify([fakeResponse statusCode]);
  282. }
  283. - (void)testFetchReleasesWithCompletionUnauthenticatedFailure {
  284. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  285. OCMStub([fakeResponse statusCode]).andReturn(401);
  286. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  287. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  288. [self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
  289. XCTestExpectation *expectation =
  290. [self expectationWithDescription:@"Fetch releases fails with unknown error."];
  291. [FIRFADApiService
  292. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  293. XCTAssertNil(releases);
  294. XCTAssertNotNil(error);
  295. XCTAssertEqual(error.code, FIRFADApiErrorUnauthenticated);
  296. [expectation fulfill];
  297. }];
  298. [self waitForExpectations:@[ expectation ] timeout:5.0];
  299. [self verifyInstallationAuthCompletion];
  300. [self verifyInstallationIdCompletion];
  301. [self verifyUrlSessionResponseWithData];
  302. OCMVerify([fakeResponse statusCode]);
  303. }
  304. - (void)testFetchReleasesWithCompletionUnauthorized400Failure {
  305. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  306. OCMStub([fakeResponse statusCode]).andReturn(400);
  307. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  308. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  309. [self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
  310. XCTestExpectation *expectation =
  311. [self expectationWithDescription:@"Fetch releases rejects with a 400."];
  312. [FIRFADApiService
  313. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  314. XCTAssertNil(releases);
  315. XCTAssertNotNil(error);
  316. XCTAssertEqual([error code], FIRFADApiErrorUnauthorized);
  317. [expectation fulfill];
  318. }];
  319. [self waitForExpectations:@[ expectation ] timeout:5.0];
  320. [self verifyInstallationAuthCompletion];
  321. [self verifyInstallationIdCompletion];
  322. [self verifyUrlSessionResponseWithData];
  323. OCMVerify([fakeResponse statusCode]);
  324. }
  325. - (void)testFetchReleasesWithCompletionUnauthorized403Failure {
  326. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  327. OCMStub([fakeResponse statusCode]).andReturn(403);
  328. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  329. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  330. [self mockUrlSessionResponse:_mockAPINotEnabledResponse response:fakeResponse error:nil];
  331. XCTestExpectation *expectation =
  332. [self expectationWithDescription:@"Fetch releases rejects with a 403."];
  333. [FIRFADApiService
  334. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  335. XCTAssertNil(releases);
  336. XCTAssertNotNil(error);
  337. XCTAssertEqual([error code], FIRFADApiErrorUnauthorized);
  338. [expectation fulfill];
  339. }];
  340. [self waitForExpectations:@[ expectation ] timeout:5.0];
  341. [self verifyInstallationAuthCompletion];
  342. [self verifyInstallationIdCompletion];
  343. [self verifyUrlSessionResponseWithData];
  344. OCMVerify([fakeResponse statusCode]);
  345. }
  346. - (void)testFetchReleasesWithCompletionUnauthorized404Failure {
  347. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  348. OCMStub([fakeResponse statusCode]).andReturn(404);
  349. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  350. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  351. [self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
  352. XCTestExpectation *expectation =
  353. [self expectationWithDescription:@"Fetch releases rejects with a 404."];
  354. [FIRFADApiService
  355. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  356. XCTAssertNil(releases);
  357. XCTAssertNotNil(error);
  358. XCTAssertEqual([error code], FIRFADApiErrorUnauthorized);
  359. [expectation fulfill];
  360. }];
  361. [self waitForExpectations:@[ expectation ] timeout:5.0];
  362. [self verifyInstallationAuthCompletion];
  363. [self verifyInstallationIdCompletion];
  364. [self verifyUrlSessionResponseWithData];
  365. OCMVerify([fakeResponse statusCode]);
  366. }
  367. - (void)testFetchReleasesWithCompletionTimeout408Failure {
  368. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  369. OCMStub([fakeResponse statusCode]).andReturn(408);
  370. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  371. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  372. [self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
  373. XCTestExpectation *expectation =
  374. [self expectationWithDescription:@"Fetch releases rejects with 408."];
  375. [FIRFADApiService
  376. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  377. XCTAssertNil(releases);
  378. XCTAssertNotNil(error);
  379. XCTAssertEqual([error code], FIRFADApiErrorTimeout);
  380. [expectation fulfill];
  381. }];
  382. [self waitForExpectations:@[ expectation ] timeout:5.0];
  383. [self verifyInstallationAuthCompletion];
  384. [self verifyInstallationIdCompletion];
  385. [self verifyUrlSessionResponseWithData];
  386. OCMVerify([fakeResponse statusCode]);
  387. }
  388. - (void)testFetchReleasesWithCompletionTimeout504Failure {
  389. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  390. OCMStub([fakeResponse statusCode]).andReturn(504);
  391. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  392. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  393. [self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
  394. XCTestExpectation *expectation =
  395. [self expectationWithDescription:@"Fetch releases rejects with a 504."];
  396. [FIRFADApiService
  397. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  398. XCTAssertNil(releases);
  399. XCTAssertNotNil(error);
  400. XCTAssertEqual([error code], FIRFADApiErrorTimeout);
  401. [expectation fulfill];
  402. }];
  403. [self waitForExpectations:@[ expectation ] timeout:5.0];
  404. [self verifyInstallationAuthCompletion];
  405. [self verifyInstallationIdCompletion];
  406. [self verifyUrlSessionResponseWithData];
  407. OCMVerify([fakeResponse statusCode]);
  408. }
  409. - (void)testFetchReleasesWithCompletionUnknownStatusCodeFailure {
  410. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  411. OCMStub([fakeResponse statusCode]).andReturn(500);
  412. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  413. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  414. [self mockUrlSessionResponse:_mockReleases response:fakeResponse error:nil];
  415. XCTestExpectation *expectation =
  416. [self expectationWithDescription:@"Fetch releases rejects with a 500."];
  417. [FIRFADApiService
  418. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  419. XCTAssertNil(releases);
  420. XCTAssertNotNil(error);
  421. XCTAssertEqual([error code], FIRApiErrorUnknownFailure);
  422. [expectation fulfill];
  423. }];
  424. [self waitForExpectations:@[ expectation ] timeout:5.0];
  425. [self verifyInstallationAuthCompletion];
  426. [self verifyInstallationIdCompletion];
  427. [self verifyUrlSessionResponseWithData];
  428. OCMVerify([fakeResponse statusCode]);
  429. }
  430. - (void)testFetchReleasesWithCompletionNoReleasesFoundSuccess {
  431. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  432. OCMStub([fakeResponse statusCode]).andReturn(200);
  433. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  434. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  435. [self mockUrlSessionResponse:@{@"releases" : @[]} response:fakeResponse error:nil];
  436. XCTestExpectation *expectation =
  437. [self expectationWithDescription:@"Fetch releases rejects with a not found exception."];
  438. [FIRFADApiService
  439. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  440. XCTAssertNotNil(releases);
  441. XCTAssertNil(error);
  442. XCTAssertEqual([releases count], 0);
  443. [expectation fulfill];
  444. }];
  445. [self waitForExpectations:@[ expectation ] timeout:5.0];
  446. [self verifyInstallationAuthCompletion];
  447. [self verifyInstallationIdCompletion];
  448. [self verifyUrlSessionResponseWithData];
  449. OCMVerify([fakeResponse statusCode]);
  450. }
  451. - (void)testFetchReleasesWithCompletionParsingFailure {
  452. NSHTTPURLResponse *fakeResponse = OCMClassMock([NSHTTPURLResponse class]);
  453. OCMStub([fakeResponse statusCode]).andReturn(200);
  454. [self mockInstallationAuthCompletion:_mockInstallationToken error:nil];
  455. [self mockInstallationIdCompletion:_mockInstallationId error:nil];
  456. [self
  457. mockUrlSessionResponseWithData:[@"malformed{json[data" dataUsingEncoding:NSUTF8StringEncoding]
  458. response:fakeResponse
  459. error:nil];
  460. XCTestExpectation *expectation =
  461. [self expectationWithDescription:@"Fetch releases rejects with a parsing failure."];
  462. [FIRFADApiService
  463. fetchReleasesWithCompletion:^(NSArray *_Nullable releases, NSError *_Nullable error) {
  464. XCTAssertNil(releases);
  465. XCTAssertNotNil(error);
  466. XCTAssertEqual([error code], FIRApiErrorParseFailure);
  467. [expectation fulfill];
  468. }];
  469. [self waitForExpectations:@[ expectation ] timeout:5.0];
  470. [self verifyInstallationAuthCompletion];
  471. [self verifyInstallationIdCompletion];
  472. [self verifyUrlSessionResponseWithData];
  473. OCMVerify([fakeResponse statusCode]);
  474. }
  475. @end