blob: 1167a715bb9c06158499428462f5902e17c4ac35 [file] [log] [blame]
Jorge Canizalesb3be2292015-05-31 19:11:20 -07001/*
2 *
Craig Tiller6169d5f2016-03-31 07:46:18 -07003 * Copyright 2015, Google Inc.
Jorge Canizalesb3be2292015-05-31 19:11:20 -07004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#import <UIKit/UIKit.h>
35#import <XCTest/XCTest.h>
36
Jorge Canizalesc42a38e2015-06-21 14:44:25 -070037#import <GRPCClient/GRPCCall.h>
Makarand Dharmapurikar1c2890d2016-02-24 14:35:46 -080038#import <GRPCClient/GRPCCall+ChannelArg.h>
Jorge Canizales631dc002015-08-06 23:08:41 -070039#import <GRPCClient/GRPCCall+OAuth2.h>
Jorge Canizalesb2bd0672015-08-01 23:19:11 -070040#import <GRPCClient/GRPCCall+Tests.h>
Jorge Canizalesbe808e32015-07-04 14:37:58 -070041#import <ProtoRPC/ProtoMethod.h>
Jorge Canizalesb3be2292015-05-31 19:11:20 -070042#import <RemoteTest/Messages.pbobjc.h>
Jorge Canizalesc42a38e2015-06-21 14:44:25 -070043#import <RxLibrary/GRXWriteable.h>
44#import <RxLibrary/GRXWriter+Immediate.h>
Makarand Dharmapurikara0414ee2016-02-23 16:58:46 -080045
Jorge Canizalesb2bd0672015-08-01 23:19:11 -070046static NSString * const kHostAddress = @"localhost:5050";
Jorge Canizalesd666fd02015-06-12 18:59:11 -070047static NSString * const kPackage = @"grpc.testing";
48static NSString * const kService = @"TestService";
Test User9656eca2016-02-18 14:47:22 -080049static NSString * const kRemoteSSLHost = @"grpc-test.sandbox.googleapis.com";
Makarand Dharmapurikara0414ee2016-02-23 16:58:46 -080050
Yuchen Zengdbe2b9e2016-06-15 20:23:04 -070051static GRPCProtoMethod *kInexistentMethod;
52static GRPCProtoMethod *kEmptyCallMethod;
53static GRPCProtoMethod *kUnaryCallMethod;
Jorge Canizalesd666fd02015-06-12 18:59:11 -070054
Jorge Canizales2beb88c2015-10-28 15:13:40 -070055/** Observer class for testing that responseMetadata is KVO-compliant */
murgatroid9981b4fcb2015-08-24 10:56:44 -070056@interface PassthroughObserver : NSObject
Jorge Canizales2beb88c2015-10-28 15:13:40 -070057- (instancetype) initWithCallback:(void (^)(NSString*, id, NSDictionary*))callback
58 NS_DESIGNATED_INITIALIZER;
murgatroid9981b4fcb2015-08-24 10:56:44 -070059
60- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change
61 context:(void *)context;
62@end
63
64@implementation PassthroughObserver {
65 void (^_callback)(NSString*, id, NSDictionary*);
66}
67
Jorge Canizales2beb88c2015-10-28 15:13:40 -070068- (instancetype)init {
69 return [self initWithCallback:nil];
70}
71
murgatroid9981b4fcb2015-08-24 10:56:44 -070072- (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback {
Jorge Canizales2beb88c2015-10-28 15:13:40 -070073 if (!callback) {
74 return nil;
75 }
76 if ((self = [super init])) {
murgatroid9981b4fcb2015-08-24 10:56:44 -070077 _callback = callback;
78 }
79 return self;
murgatroid9981b4fcb2015-08-24 10:56:44 -070080}
81
Jorge Canizales2beb88c2015-10-28 15:13:40 -070082- (void)observeValueForKeyPath:(NSString *)keyPath
83 ofObject:(id)object
84 change:(NSDictionary *)change
85 context:(void *)context {
murgatroid9981b4fcb2015-08-24 10:56:44 -070086 _callback(keyPath, object, change);
murgatroid992e49a352015-08-24 14:40:45 -070087 [object removeObserver:self forKeyPath:keyPath];
murgatroid9981b4fcb2015-08-24 10:56:44 -070088}
89
90@end
91
Jorge Canizales2beb88c2015-10-28 15:13:40 -070092# pragma mark Tests
93
94/**
95 * A few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall) rather than
96 * a generated proto library on top of it. Its RPCs are sent to a local cleartext server.
97 *
98 * TODO(jcanizales): Run them also against a local SSL server and against a remote server.
99 */
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700100@interface GRPCClientTests : XCTestCase
101@end
102
103@implementation GRPCClientTests
104
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700105- (void)setUp {
Makarand Dharmapurikarc2b34902016-02-26 16:38:17 -0800106 // Add a custom user agent prefix that will be used in test
107 [GRPCCall setUserAgentPrefix:@"Foo" forHost:kHostAddress];
Jorge Canizalesb2bd0672015-08-01 23:19:11 -0700108 // Register test server as non-SSL.
109 [GRPCCall useInsecureConnectionsForHost:kHostAddress];
110
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700111 // This method isn't implemented by the remote server.
Yuchen Zengdbe2b9e2016-06-15 20:23:04 -0700112 kInexistentMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
Yuchen Zengd5fd7dd2016-06-21 11:13:23 -0700113 service:kService
114 method:@"Inexistent"];
Yuchen Zengdbe2b9e2016-06-15 20:23:04 -0700115 kEmptyCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
Yuchen Zengd5fd7dd2016-06-21 11:13:23 -0700116 service:kService
117 method:@"EmptyCall"];
Yuchen Zengdbe2b9e2016-06-15 20:23:04 -0700118 kUnaryCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
Yuchen Zengd5fd7dd2016-06-21 11:13:23 -0700119 service:kService
120 method:@"UnaryCall"];
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700121}
122
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700123- (void)testConnectionToRemoteServer {
124 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."];
125
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700126 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
Jorge Canizales5260f532015-07-04 14:47:41 -0700127 path:kInexistentMethod.HTTPPath
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700128 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700129
130 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
131 XCTFail(@"Received unexpected response: %@", value);
132 } completionHandler:^(NSError *errorOrNil) {
133 XCTAssertNotNil(errorOrNil, @"Finished without error!");
Test User9656eca2016-02-18 14:47:22 -0800134 XCTAssertEqual(errorOrNil.code, 12, @"Finished with unexpected error: %@", errorOrNil);
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700135 [expectation fulfill];
136 }];
137
138 [call startWithWriteable:responsesWriteable];
139
Jorge Canizales5580e5d2015-07-15 23:35:48 -0700140 [self waitForExpectationsWithTimeout:4 handler:nil];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700141}
142
143- (void)testEmptyRPC {
144 __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."];
145 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
146
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700147 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
Jorge Canizales5260f532015-07-04 14:47:41 -0700148 path:kEmptyCallMethod.HTTPPath
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700149 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700150
151 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
152 XCTAssertNotNil(value, @"nil value received as response.");
153 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
154 [response fulfill];
155 } completionHandler:^(NSError *errorOrNil) {
156 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
157 [completion fulfill];
158 }];
159
160 [call startWithWriteable:responsesWriteable];
161
Jorge Canizales83c57cb2015-08-09 16:36:49 -0700162 [self waitForExpectationsWithTimeout:8 handler:nil];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700163}
164
165- (void)testSimpleProtoRPC {
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700166 __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700167 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
168
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700169 RMTSimpleRequest *request = [RMTSimpleRequest message];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700170 request.responseSize = 100;
171 request.fillUsername = YES;
172 request.fillOauthScope = YES;
Jorge Canizalesa8c5d962015-07-16 18:55:31 -0700173 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700174
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700175 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
Jorge Canizales5260f532015-07-04 14:47:41 -0700176 path:kUnaryCallMethod.HTTPPath
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700177 requestsWriter:requestsWriter];
178
179 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
180 XCTAssertNotNil(value, @"nil value received as response.");
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700181 XCTAssertGreaterThan(value.length, 0, @"Empty response received.");
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700182 RMTSimpleResponse *responseProto = [RMTSimpleResponse parseFromData:value error:NULL];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700183 // We expect empty strings, not nil:
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700184 XCTAssertNotNil(responseProto.username, @"Response's username is nil.");
185 XCTAssertNotNil(responseProto.oauthScope, @"Response's OAuth scope is nil.");
186 [response fulfill];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700187 } completionHandler:^(NSError *errorOrNil) {
188 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
189 [completion fulfill];
190 }];
191
192 [call startWithWriteable:responsesWriteable];
193
Jorge Canizales83c57cb2015-08-09 16:36:49 -0700194 [self waitForExpectationsWithTimeout:8 handler:nil];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700195}
196
Jorge Canizalesd7981252015-06-12 19:15:18 -0700197- (void)testMetadata {
198 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."];
199
200 RMTSimpleRequest *request = [RMTSimpleRequest message];
201 request.fillUsername = YES;
202 request.fillOauthScope = YES;
Jorge Canizalesa8c5d962015-07-16 18:55:31 -0700203 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
Jorge Canizalesd7981252015-06-12 19:15:18 -0700204
Test User9656eca2016-02-18 14:47:22 -0800205 GRPCCall *call = [[GRPCCall alloc] initWithHost:kRemoteSSLHost
Jorge Canizales5260f532015-07-04 14:47:41 -0700206 path:kUnaryCallMethod.HTTPPath
Jorge Canizalesd7981252015-06-12 19:15:18 -0700207 requestsWriter:requestsWriter];
208
Jorge Canizales721b7a32015-08-07 10:11:16 -0700209 call.oauth2AccessToken = @"bogusToken";
Jorge Canizalesd7981252015-06-12 19:15:18 -0700210
211 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
212 XCTFail(@"Received unexpected response: %@", value);
213 } completionHandler:^(NSError *errorOrNil) {
214 XCTAssertNotNil(errorOrNil, @"Finished without error!");
215 XCTAssertEqual(errorOrNil.code, 16, @"Finished with unexpected error: %@", errorOrNil);
Jorge Canizales1ab2a712015-08-12 20:32:11 -0700216 XCTAssertEqualObjects(call.responseHeaders, errorOrNil.userInfo[kGRPCHeadersKey],
217 @"Headers in the NSError object and call object differ.");
218 XCTAssertEqualObjects(call.responseTrailers, errorOrNil.userInfo[kGRPCTrailersKey],
219 @"Trailers in the NSError object and call object differ.");
Jorge Canizales721b7a32015-08-07 10:11:16 -0700220 NSString *challengeHeader = call.oauth2ChallengeHeader;
Jorge Canizalesd7981252015-06-12 19:15:18 -0700221 XCTAssertGreaterThan(challengeHeader.length, 0,
Jorge Canizales1ab2a712015-08-12 20:32:11 -0700222 @"No challenge in response headers %@", call.responseHeaders);
Jorge Canizalesd7981252015-06-12 19:15:18 -0700223 [expectation fulfill];
224 }];
225
226 [call startWithWriteable:responsesWriteable];
227
Jorge Canizales5580e5d2015-07-15 23:35:48 -0700228 [self waitForExpectationsWithTimeout:4 handler:nil];
Jorge Canizalesd7981252015-06-12 19:15:18 -0700229}
230
murgatroid9981b4fcb2015-08-24 10:56:44 -0700231- (void)testResponseMetadataKVO {
232 __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."];
233 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
234 __weak XCTestExpectation *metadata = [self expectationWithDescription:@"Metadata changed."];
235
murgatroid9981b4fcb2015-08-24 10:56:44 -0700236 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
237 path:kEmptyCallMethod.HTTPPath
238 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
239
murgatroid992e49a352015-08-24 14:40:45 -0700240 PassthroughObserver *observer = [[PassthroughObserver alloc] initWithCallback:^(NSString *keypath, id object, NSDictionary * change) {
241 if ([keypath isEqual: @"responseHeaders"]) {
242 [metadata fulfill];
243 }
244 }];
245
murgatroid9981b4fcb2015-08-24 10:56:44 -0700246 [call addObserver:observer forKeyPath:@"responseHeaders" options:0 context:NULL];
247
248 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
249 XCTAssertNotNil(value, @"nil value received as response.");
250 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
251 [response fulfill];
252 } completionHandler:^(NSError *errorOrNil) {
253 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
254 [completion fulfill];
255 }];
256
257 [call startWithWriteable:responsesWriteable];
258
259 [self waitForExpectationsWithTimeout:8 handler:nil];
260}
261
Makarand Dharmapurikarc2b34902016-02-26 16:38:17 -0800262- (void)testUserAgentPrefix {
263 __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."];
264 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
265
266 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
267 path:kEmptyCallMethod.HTTPPath
268 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
269 // Setting this special key in the header will cause the interop server to echo back the
270 // user-agent value, which we confirm.
271 call.requestHeaders[@"x-grpc-test-echo-useragent"] = @"";
272
273 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
274 XCTAssertNotNil(value, @"nil value received as response.");
275 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
Nicolas "Pixel" Nobleaad41562016-03-11 22:21:11 +0100276 /* This test needs to be more clever in regards to changing the version of the core.
Makarand Dharmapurikarc2b34902016-02-26 16:38:17 -0800277 XCTAssertEqualObjects(call.responseHeaders[@"x-grpc-test-echo-useragent"],
Makarand Dharmapurikarf1588b52016-02-26 17:05:47 -0800278 @"Foo grpc-objc/0.13.0 grpc-c/0.14.0-dev (ios)",
279 @"Did not receive expected user agent %@",
280 call.responseHeaders[@"x-grpc-test-echo-useragent"]);
Nicolas "Pixel" Nobleaad41562016-03-11 22:21:11 +0100281 */
Makarand Dharmapurikarc9dbf642016-02-26 16:59:08 -0800282 [response fulfill];
Makarand Dharmapurikarc2b34902016-02-26 16:38:17 -0800283 } completionHandler:^(NSError *errorOrNil) {
284 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
285 [completion fulfill];
286 }];
287
288 [call startWithWriteable:responsesWriteable];
289
290 [self waitForExpectationsWithTimeout:8 handler:nil];
291}
292
Makarand Dharmapurikar63f2b5b2016-02-24 13:54:48 -0800293// TODO(makarandd): Move to a different file that contains only unit tests
Test User9656eca2016-02-18 14:47:22 -0800294- (void)testExceptions {
295 // Try to set userAgentPrefix for host that is nil. This should cause
296 // an exception.
297 @try {
298 [GRPCCall setUserAgentPrefix:@"Foo" forHost:nil];
299 XCTFail(@"Did not receive an exception when host is nil");
300 } @catch(NSException *theException) {
301 NSLog(@"Received exception as expected: %@", theException.name);
302 }
303
304 // Try to set parameters to nil for GRPCCall. This should cause an exception
305 @try {
Yuchen Zengd5fd7dd2016-06-21 11:13:23 -0700306 (void)[[GRPCCall alloc] initWithHost:nil
307 path:nil
308 requestsWriter:nil];
Test User9656eca2016-02-18 14:47:22 -0800309 XCTFail(@"Did not receive an exception when parameters are nil");
310 } @catch(NSException *theException) {
311 NSLog(@"Received exception as expected: %@", theException.name);
312 }
313
314
315 // Set state to Finished by force
316 GRXWriter *requestsWriter = [GRXWriter emptyWriter];
317 [requestsWriter finishWithError:nil];
318 @try {
Yuchen Zengd5fd7dd2016-06-21 11:13:23 -0700319 (void)[[GRPCCall alloc] initWithHost:kHostAddress
320 path:kUnaryCallMethod.HTTPPath
321 requestsWriter:requestsWriter];
Test User9656eca2016-02-18 14:47:22 -0800322 XCTFail(@"Did not receive an exception when GRXWriter has incorrect state.");
323 } @catch(NSException *theException) {
324 NSLog(@"Received exception as expected: %@", theException.name);
325 }
326
327}
328
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700329@end