BUYRequestOperationTests.m 12.7 KB
Newer Older
1 2 3 4
//
//  BUYRequestOperationTests.m
//  Mobile Buy SDK
//
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
//  Created by Shopify.
//  Copyright (c) 2015 Shopify Inc. All rights reserved.
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
25 26 27 28 29
//

#import <XCTest/XCTest.h>
#import <OHHTTPStubs/OHHTTPStubs.h>
#import "BUYRequestOperation.h"
Dima Bart committed
30
#import "BUYClient.h"
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

@interface BUYRequestOperationTests : XCTestCase

@property (strong, nonatomic) NSOperationQueue *queue;
@property (strong, nonatomic) NSURLSession *session;
@property (strong, nonatomic) NSMutableURLRequest *request;

@end

@implementation BUYRequestOperationTests

#pragma mark - Setup -

- (void)setUp
{
    [super setUp];
	
48
	self.request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://www.shopify.com"]];
49 50 51 52 53 54 55 56
	self.queue   = [NSOperationQueue new];
	self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:nil delegateQueue:self.queue];
}

#pragma mark - Tests -

- (void)testInit
{
Dima Bart committed
57
	BUYRequestOperation *operation = [BUYRequestOperation operationWithSession:self.session request:self.request payload:nil completion:^(NSDictionary *json, NSHTTPURLResponse *response, NSError *error) {
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
		// We don't start the session
	}];
									  
	XCTAssertNotNil(operation);
	XCTAssertEqualObjects(operation.session, self.session);
	XCTAssertEqualObjects(operation.originalRequest, self.request);
}

#pragma mark - No Queue Tests -

- (void)testOperationWithoutQueue
{
	[self stubRequests];
	
	XCTestExpectation *expectation = [self expectationWithDescription:@"Expect successful operation"];
	BUYRequestOperation *operation = [self operationFulfillingExpectation:expectation completion:nil];
	
	[operation start];
	
77
	[self waitForExpectationsWithTimeout:3.0 handler:^(NSError *error) {}];
78 79
}

80 81 82 83 84 85 86 87 88 89 90
#pragma mark - Data Tests -

- (void)testSuccessfulRequest
{
	NSDictionary *payload = @{
							  @"name"           : @"Water",
							  @"type"           : @"liquid",
							  @"melting_point"  : @0.0,
							  };
	
	NSData *payloadData           = [NSJSONSerialization dataWithJSONObject:payload options:0 error:nil];
Dima Bart committed
91
	OHHTTPStubsResponse *response = [OHHTTPStubsResponse responseWithData:payloadData statusCode:BUYStatusComplete headers:nil];
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
	
	[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
		return YES;
	} withStubResponse:^OHHTTPStubsResponse * (NSURLRequest *request) {
		return response;
	}];
	
	XCTestExpectation *expectation = [self expectationWithDescription:@"Expect successful operation"];
	BUYRequestOperation *operation = [self operationFulfillingExpectation:expectation responseCompletion:^(NSDictionary *json, NSHTTPURLResponse *response, NSError *error) {
		
		XCTAssertNotNil(json);
		XCTAssertNotNil(response);
		XCTAssertNil(error);
		
		XCTAssertEqualObjects(json, payload);
Dima Bart committed
107
		XCTAssertEqual(response.statusCode, BUYStatusComplete);
108 109 110
	}];
	
	[self.queue addOperation:operation];
111
	[self waitForExpectationsWithTimeout:3.0 handler:^(NSError *error) {}];
112 113
}

114 115 116 117 118 119 120 121 122 123
- (void)testFailedRequest
{
	NSDictionary *errorPayload = @{
								   @"error" : @{
										   @"reason" : @"Invalid length of name",
										   @"field"  : @"username",
										   },
								   };
	
	NSData *payloadData           = [NSJSONSerialization dataWithJSONObject:errorPayload options:0 error:nil];
Dima Bart committed
124
	OHHTTPStubsResponse *response = [OHHTTPStubsResponse responseWithData:payloadData statusCode:BUYStatusFailed headers:nil];
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
	
	[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
		return YES;
	} withStubResponse:^OHHTTPStubsResponse * (NSURLRequest *request) {
		return response;
	}];
	
	XCTestExpectation *expectation = [self expectationWithDescription:@"Expect failed operation"];
	BUYRequestOperation *operation = [self operationFulfillingExpectation:expectation responseCompletion:^(NSDictionary *json, NSHTTPURLResponse *response, NSError *error) {
		
		XCTAssertNil(json);
		XCTAssertNotNil(response);
		XCTAssertNotNil(error);
		
		XCTAssertEqualObjects(error.userInfo, errorPayload);
Dima Bart committed
140
		XCTAssertEqual(response.statusCode, BUYStatusFailed);
141 142 143
	}];
	
	[self.queue addOperation:operation];
144
	[self waitForExpectationsWithTimeout:3.0 handler:^(NSError *error) {}];
145 146
}

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
#pragma mark - Dependency Tests -

- (void)testSerialSuccessfulDependencies
{
	[self stubRequests];
	
	__block NSMutableString *container = [@"" mutableCopy];
	
	XCTestExpectation *expectation1 = [self expectationWithDescription:@"Expect operation 1"];
	BUYRequestOperation *operation1 = [self operationFulfillingExpectation:expectation1 completion:^{
		[container appendString:@"1"];
	}];
	
	XCTestExpectation *expectation2 = [self expectationWithDescription:@"Expect operation 2"];
	BUYRequestOperation *operation2 = [self operationFulfillingExpectation:expectation2 completion:^{
		[container appendString:@"2"];
	}];
	
	[operation2 addDependency:operation1];
	[self.queue addOperation:operation2];
	[self.queue addOperation:operation1];
	
169
	[self waitForExpectationsWithTimeout:10.0 handler:^(NSError *error) {}];
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
	XCTAssertEqualObjects(container, @"12");
}

- (void)testParallelSuccessfulDependencies
{
	[self stubRequests];
	
	__block NSMutableString *container = [@"" mutableCopy];
	
	XCTestExpectation *expectation1 = [self expectationWithDescription:@"Expect operation 1"];
	BUYRequestOperation *operation1 = [self operationFulfillingExpectation:expectation1 completion:^{
		[container appendString:@"1"];
	}];
	
	XCTestExpectation *expectation2 = [self expectationWithDescription:@"Expect operation 2"];
	BUYRequestOperation *operation2 = [self operationFulfillingExpectation:expectation2 completion:^{
		[container appendString:@"1"];
	}];
	
	XCTestExpectation *expectation3 = [self expectationWithDescription:@"Expect operation 3"];
	BUYRequestOperation *operation3 = [self operationFulfillingExpectation:expectation3 completion:^{
191
		[container appendString:@"3"];
192 193
	}];
	
194 195 196 197 198 199
	XCTestExpectation *expectation4 = [self expectationWithDescription:@"Expect operation 4"];
	BUYRequestOperation *operation4 = [self operationFulfillingExpectation:expectation4 completion:^{
		[container appendString:@"4"];
	}];
	
	[operation4 addDependency:operation3];
200 201 202
	[operation3 addDependency:operation1];
	[operation3 addDependency:operation2];
	
203
	[self.queue addOperation:operation4];
204 205 206 207
	[self.queue addOperation:operation3];
	[self.queue addOperation:operation2];
	[self.queue addOperation:operation1];
	
208
	[self waitForExpectationsWithTimeout:10.0 handler:^(NSError *error) {}];
209
	XCTAssertEqualObjects(container, @"1134");
210 211
}

Dima Bart committed
212 213 214 215 216 217 218 219 220 221 222
- (void)testPollingActivatedWithHandler
{
	[self stubRequestsWithDelay:0.1 status:BUYStatusProcessing];
	
	XCTestExpectation *completion  = [self expectationWithDescription:@"Should complete after polling"];
	BUYRequestOperation *operation = [self operationFulfillingExpectation:nil completion:^{
		[completion fulfill];
	}];
	
	__block int pollCount = 0;
	
223
	XCTestExpectation *expectation = [self expectationWithDescription:@"Should stop polling at 2 iterations"];
Dima Bart committed
224 225 226 227 228 229 230 231 232 233 234 235 236 237
	operation.pollingHandler = ^BOOL (NSDictionary *json, NSHTTPURLResponse *response, NSError *error) {
		
		[self stubRequestsWithDelay:0.1 status:BUYStatusProcessing];
		
		XCTAssertNotNil(json);
		XCTAssertNotNil(response);
		XCTAssertNil(error);
		
		if (response.statusCode == BUYStatusComplete) {
			[expectation fulfill];
		}
		
		if (response.statusCode == BUYStatusProcessing) {
			pollCount += 1;
238
			if (pollCount == 2) {
Dima Bart committed
239 240 241 242 243 244 245 246
				[self stubRequestsWithDelay:0.1 status:BUYStatusComplete];
			}
			return YES;
		}
		return NO;
	};
	
	[self.queue addOperation:operation];
247
	[self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {}];
Dima Bart committed
248 249
}

250 251 252 253 254 255 256 257 258 259 260
- (void)testCancellationBeforeExecution
{
	[self stubRequests];
	
	BUYRequestOperation *operation = [self operationFulfillingExpectation:nil completion:^{
		XCTAssert(NO, @"Operation should not call completion if cancelled.");
	}];
	
	[self createExpectationDelay];
	
	[self.queue addOperation:operation];
261
	[operation cancel];
262
	
263
	[self waitForExpectationsWithTimeout:3.0 handler:^(NSError *error) {}];
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
}

- (void)testCancellationDuringExecution
{
	[self stubRequestsWithDelay:2.0];
	
	BUYRequestOperation *operation = [self operationFulfillingExpectation:nil completion:^{
		XCTAssert(NO, @"Operation should not call completion if cancelled.");
	}];
	
	[self createExpectationDelay:3.0];
	[self.queue addOperation:operation];
	[self after:1.0 block:^{
		[operation cancel];
	}];
	
280
	[self waitForExpectationsWithTimeout:10.0 handler:^(NSError *error) {}];
281 282
}

283 284 285 286 287 288 289 290 291 292 293 294
- (void)testCancellationWithoutQueue
{
	[self stubRequestsWithDelay:0.5];
	
	BUYRequestOperation *operation = [self operationFulfillingExpectation:nil completion:^{
		XCTAssert(NO, @"Operation should not call completion if cancelled.");
	}];
	
	[operation start];
	[operation cancel];
	
	[self createExpectationDelay];
295
	[self waitForExpectationsWithTimeout:4.0 handler:^(NSError *error) {}];
296 297
}

Dima Bart committed
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
- (void)testCancellationDuringPolling
{
	[self stubRequestsWithDelay:0.1 status:BUYStatusProcessing];
	
	BUYRequestOperation *operation = [self operationFulfillingExpectation:nil completion:^{
		XCTAssert(NO, @"Operation should not call completion if cancelled.");
	}];
	
	__block int pollCount = 0;
	
	operation.pollingHandler = ^BOOL (NSDictionary *json, NSHTTPURLResponse *response, NSError *error) {
		pollCount += 1;
		return YES;
	};
	
	[self.queue addOperation:operation];
	
	[self after:0.5 block:^{
		[operation cancel];
	}];
	
	[self createExpectationDelay:1.0 block:YES];
	
	XCTAssertTrue(pollCount < 5);
}

324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
#pragma mark - Convenience -

- (void)asyncMain:(dispatch_block_t)block
{
	dispatch_async(dispatch_get_main_queue(), block);
}

- (void)after:(NSTimeInterval)delay block:(dispatch_block_t)block
{
	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), block);
}

- (void)createExpectationDelay
{
	[self createExpectationDelay:1.0];
}
Dima Bart committed
340 341 342 343 344
	 
 - (void)createExpectationDelay:(NSTimeInterval)delay
 {
	[self createExpectationDelay:delay block:NO];
 }
345

Dima Bart committed
346
 - (void)createExpectationDelay:(NSTimeInterval)delay block:(BOOL)block
347 348 349 350 351
{
	XCTestExpectation *expectation = [self expectationWithDescription:@"Delay"];
	[self after:delay block:^{
		[expectation fulfill];
	}];
Dima Bart committed
352 353
	
	if (block) {
354
		[self waitForExpectationsWithTimeout:delay + 0.1 handler:^(NSError *error) {}];
Dima Bart committed
355
	}
356 357 358
}

- (BUYRequestOperation *)operationFulfillingExpectation:(XCTestExpectation *)expectation completion:(dispatch_block_t)completion
359
{
Dima Bart committed
360
	return [self operationFulfillingExpectation:expectation responseCompletion:^(NSDictionary *json, NSHTTPURLResponse *response, NSError *error) {
361 362 363 364 365 366 367
		if (completion) {
			completion();
		}
	}];
}

- (BUYRequestOperation *)operationFulfillingExpectation:(XCTestExpectation *)expectation responseCompletion:(void(^)(NSDictionary *json, NSHTTPURLResponse *response, NSError *error))completion
368
{
Dima Bart committed
369
	BUYRequestOperation *operation = [BUYRequestOperation operationWithSession:self.session request:self.request payload:nil completion:^(NSDictionary *json, NSHTTPURLResponse *response, NSError *error) {
370 371
		[self asyncMain:^{
			if (completion) {
372
				completion(json, (id)response, error);
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
			}
			[expectation fulfill];
		}];
	}];
	
	return operation;
}

#pragma mark - Stubs -

- (void)stubRequests
{
	[self stubRequestsWithDelay:0.0];
}

- (void)stubRequestsWithDelay:(NSTimeInterval)delay
Dima Bart committed
389 390 391 392 393
{
	[self stubRequestsWithDelay:delay status:BUYStatusProcessing];
}

- (void)stubRequestsWithDelay:(NSTimeInterval)delay status:(int)status
394 395 396 397 398 399 400
{
	NSDictionary *payload = @{
							  @"first_name" : @"John",
							  @"last_name"  : @"Smith",
							  };
	
	NSData *payloadData           = [NSJSONSerialization dataWithJSONObject:payload options:0 error:nil];
Dima Bart committed
401
	OHHTTPStubsResponse *response = [OHHTTPStubsResponse responseWithData:payloadData statusCode:status headers:nil];
402 403 404 405 406 407 408 409 410 411
	response.requestTime          = delay;
	
	[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
		return YES;
	} withStubResponse:^OHHTTPStubsResponse * _Nonnull(NSURLRequest *request) {
		return response;
	}];
}

@end