blob: 00c4b8830d344104648115f2180d4757235ef47e [file] [log] [blame]
Jorge Canizalesb3be2292015-05-31 19:11:20 -07001/*
2 *
3 * Copyright 2015, Google Inc.
4 * 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>
Jorge Canizales631dc002015-08-06 23:08:41 -070038#import <GRPCClient/GRPCCall+OAuth2.h>
Jorge Canizalesb2bd0672015-08-01 23:19:11 -070039#import <GRPCClient/GRPCCall+Tests.h>
Jorge Canizalesbe808e32015-07-04 14:37:58 -070040#import <ProtoRPC/ProtoMethod.h>
Jorge Canizalesb3be2292015-05-31 19:11:20 -070041#import <RemoteTest/Messages.pbobjc.h>
Jorge Canizalesc42a38e2015-06-21 14:44:25 -070042#import <RxLibrary/GRXWriteable.h>
43#import <RxLibrary/GRXWriter+Immediate.h>
Jorge Canizalesb3be2292015-05-31 19:11:20 -070044
Jorge Canizalesb2bd0672015-08-01 23:19:11 -070045static NSString * const kHostAddress = @"localhost:5050";
Jorge Canizalesd666fd02015-06-12 18:59:11 -070046static NSString * const kPackage = @"grpc.testing";
47static NSString * const kService = @"TestService";
48
Jorge Canizales469d4b62015-07-03 12:02:38 -070049static ProtoMethod *kInexistentMethod;
50static ProtoMethod *kEmptyCallMethod;
51static ProtoMethod *kUnaryCallMethod;
Jorge Canizalesd666fd02015-06-12 18:59:11 -070052
Jorge Canizales2beb88c2015-10-28 15:13:40 -070053/** Observer class for testing that responseMetadata is KVO-compliant */
murgatroid9981b4fcb2015-08-24 10:56:44 -070054@interface PassthroughObserver : NSObject
Jorge Canizales2beb88c2015-10-28 15:13:40 -070055- (instancetype) initWithCallback:(void (^)(NSString*, id, NSDictionary*))callback
56 NS_DESIGNATED_INITIALIZER;
murgatroid9981b4fcb2015-08-24 10:56:44 -070057
58- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change
59 context:(void *)context;
60@end
61
62@implementation PassthroughObserver {
63 void (^_callback)(NSString*, id, NSDictionary*);
64}
65
Jorge Canizales2beb88c2015-10-28 15:13:40 -070066- (instancetype)init {
67 return [self initWithCallback:nil];
68}
69
murgatroid9981b4fcb2015-08-24 10:56:44 -070070- (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback {
Jorge Canizales2beb88c2015-10-28 15:13:40 -070071 if (!callback) {
72 return nil;
73 }
74 if ((self = [super init])) {
murgatroid9981b4fcb2015-08-24 10:56:44 -070075 _callback = callback;
76 }
77 return self;
murgatroid9981b4fcb2015-08-24 10:56:44 -070078}
79
Jorge Canizales2beb88c2015-10-28 15:13:40 -070080- (void)observeValueForKeyPath:(NSString *)keyPath
81 ofObject:(id)object
82 change:(NSDictionary *)change
83 context:(void *)context {
murgatroid9981b4fcb2015-08-24 10:56:44 -070084 _callback(keyPath, object, change);
murgatroid992e49a352015-08-24 14:40:45 -070085 [object removeObserver:self forKeyPath:keyPath];
murgatroid9981b4fcb2015-08-24 10:56:44 -070086}
87
88@end
89
Jorge Canizales2beb88c2015-10-28 15:13:40 -070090# pragma mark Tests
91
92/**
93 * A few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall) rather than
94 * a generated proto library on top of it. Its RPCs are sent to a local cleartext server.
95 *
96 * TODO(jcanizales): Run them also against a local SSL server and against a remote server.
97 */
Jorge Canizalesb3be2292015-05-31 19:11:20 -070098@interface GRPCClientTests : XCTestCase
99@end
100
101@implementation GRPCClientTests
102
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700103- (void)setUp {
Jorge Canizalesb2bd0672015-08-01 23:19:11 -0700104 // Register test server as non-SSL.
105 [GRPCCall useInsecureConnectionsForHost:kHostAddress];
106
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700107 // This method isn't implemented by the remote server.
Jorge Canizales469d4b62015-07-03 12:02:38 -0700108 kInexistentMethod = [[ProtoMethod alloc] initWithPackage:kPackage
Jorge Canizalesbe808e32015-07-04 14:37:58 -0700109 service:kService
110 method:@"Inexistent"];
Jorge Canizales469d4b62015-07-03 12:02:38 -0700111 kEmptyCallMethod = [[ProtoMethod alloc] initWithPackage:kPackage
Jorge Canizalesbe808e32015-07-04 14:37:58 -0700112 service:kService
113 method:@"EmptyCall"];
Jorge Canizales469d4b62015-07-03 12:02:38 -0700114 kUnaryCallMethod = [[ProtoMethod alloc] initWithPackage:kPackage
Jorge Canizalesbe808e32015-07-04 14:37:58 -0700115 service:kService
116 method:@"UnaryCall"];
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700117}
118
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700119- (void)testConnectionToRemoteServer {
120 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."];
121
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700122 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
Jorge Canizales5260f532015-07-04 14:47:41 -0700123 path:kInexistentMethod.HTTPPath
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700124 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700125
126 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
127 XCTFail(@"Received unexpected response: %@", value);
128 } completionHandler:^(NSError *errorOrNil) {
129 XCTAssertNotNil(errorOrNil, @"Finished without error!");
130 // TODO(jcanizales): The server should return code 12 UNIMPLEMENTED, not 5 NOT FOUND.
131 XCTAssertEqual(errorOrNil.code, 5, @"Finished with unexpected error: %@", errorOrNil);
132 [expectation fulfill];
133 }];
134
135 [call startWithWriteable:responsesWriteable];
136
Jorge Canizales5580e5d2015-07-15 23:35:48 -0700137 [self waitForExpectationsWithTimeout:4 handler:nil];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700138}
139
140- (void)testEmptyRPC {
141 __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."];
142 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
143
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700144 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
Jorge Canizales5260f532015-07-04 14:47:41 -0700145 path:kEmptyCallMethod.HTTPPath
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700146 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700147
148 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
149 XCTAssertNotNil(value, @"nil value received as response.");
150 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
151 [response fulfill];
152 } completionHandler:^(NSError *errorOrNil) {
153 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
154 [completion fulfill];
155 }];
156
157 [call startWithWriteable:responsesWriteable];
158
Jorge Canizales83c57cb2015-08-09 16:36:49 -0700159 [self waitForExpectationsWithTimeout:8 handler:nil];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700160}
161
162- (void)testSimpleProtoRPC {
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700163 __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700164 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
165
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700166 RMTSimpleRequest *request = [RMTSimpleRequest message];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700167 request.responseSize = 100;
168 request.fillUsername = YES;
169 request.fillOauthScope = YES;
Jorge Canizalesa8c5d962015-07-16 18:55:31 -0700170 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700171
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700172 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
Jorge Canizales5260f532015-07-04 14:47:41 -0700173 path:kUnaryCallMethod.HTTPPath
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700174 requestsWriter:requestsWriter];
175
176 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
177 XCTAssertNotNil(value, @"nil value received as response.");
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700178 XCTAssertGreaterThan(value.length, 0, @"Empty response received.");
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700179 RMTSimpleResponse *responseProto = [RMTSimpleResponse parseFromData:value error:NULL];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700180 // We expect empty strings, not nil:
Jorge Canizalesd666fd02015-06-12 18:59:11 -0700181 XCTAssertNotNil(responseProto.username, @"Response's username is nil.");
182 XCTAssertNotNil(responseProto.oauthScope, @"Response's OAuth scope is nil.");
183 [response fulfill];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700184 } completionHandler:^(NSError *errorOrNil) {
185 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
186 [completion fulfill];
187 }];
188
189 [call startWithWriteable:responsesWriteable];
190
Jorge Canizales83c57cb2015-08-09 16:36:49 -0700191 [self waitForExpectationsWithTimeout:8 handler:nil];
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700192}
193
Jorge Canizales2beb88c2015-10-28 15:13:40 -0700194// TODO(jcanizales): Activate this test against the remote server.
Jorge Canizalesd7981252015-06-12 19:15:18 -0700195- (void)testMetadata {
196 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."];
197
198 RMTSimpleRequest *request = [RMTSimpleRequest message];
199 request.fillUsername = YES;
200 request.fillOauthScope = YES;
Jorge Canizalesa8c5d962015-07-16 18:55:31 -0700201 GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
Jorge Canizalesd7981252015-06-12 19:15:18 -0700202
203 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
Jorge Canizales5260f532015-07-04 14:47:41 -0700204 path:kUnaryCallMethod.HTTPPath
Jorge Canizalesd7981252015-06-12 19:15:18 -0700205 requestsWriter:requestsWriter];
206
Jorge Canizales721b7a32015-08-07 10:11:16 -0700207 call.oauth2AccessToken = @"bogusToken";
Jorge Canizalesd7981252015-06-12 19:15:18 -0700208
209 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
210 XCTFail(@"Received unexpected response: %@", value);
211 } completionHandler:^(NSError *errorOrNil) {
212 XCTAssertNotNil(errorOrNil, @"Finished without error!");
213 XCTAssertEqual(errorOrNil.code, 16, @"Finished with unexpected error: %@", errorOrNil);
Jorge Canizales1ab2a712015-08-12 20:32:11 -0700214 XCTAssertEqualObjects(call.responseHeaders, errorOrNil.userInfo[kGRPCHeadersKey],
215 @"Headers in the NSError object and call object differ.");
216 XCTAssertEqualObjects(call.responseTrailers, errorOrNil.userInfo[kGRPCTrailersKey],
217 @"Trailers in the NSError object and call object differ.");
Jorge Canizales721b7a32015-08-07 10:11:16 -0700218 NSString *challengeHeader = call.oauth2ChallengeHeader;
Jorge Canizalesd7981252015-06-12 19:15:18 -0700219 XCTAssertGreaterThan(challengeHeader.length, 0,
Jorge Canizales1ab2a712015-08-12 20:32:11 -0700220 @"No challenge in response headers %@", call.responseHeaders);
Jorge Canizalesd7981252015-06-12 19:15:18 -0700221 [expectation fulfill];
222 }];
223
224 [call startWithWriteable:responsesWriteable];
225
Jorge Canizales5580e5d2015-07-15 23:35:48 -0700226 [self waitForExpectationsWithTimeout:4 handler:nil];
Jorge Canizalesd7981252015-06-12 19:15:18 -0700227}
228
murgatroid9981b4fcb2015-08-24 10:56:44 -0700229- (void)testResponseMetadataKVO {
230 __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."];
231 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
232 __weak XCTestExpectation *metadata = [self expectationWithDescription:@"Metadata changed."];
233
murgatroid9981b4fcb2015-08-24 10:56:44 -0700234 GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
235 path:kEmptyCallMethod.HTTPPath
236 requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
237
murgatroid992e49a352015-08-24 14:40:45 -0700238 PassthroughObserver *observer = [[PassthroughObserver alloc] initWithCallback:^(NSString *keypath, id object, NSDictionary * change) {
239 if ([keypath isEqual: @"responseHeaders"]) {
240 [metadata fulfill];
241 }
242 }];
243
murgatroid9981b4fcb2015-08-24 10:56:44 -0700244 [call addObserver:observer forKeyPath:@"responseHeaders" options:0 context:NULL];
245
246 id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
247 XCTAssertNotNil(value, @"nil value received as response.");
248 XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
249 [response fulfill];
250 } completionHandler:^(NSError *errorOrNil) {
251 XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
252 [completion fulfill];
253 }];
254
255 [call startWithWriteable:responsesWriteable];
256
257 [self waitForExpectationsWithTimeout:8 handler:nil];
258}
259
Jorge Canizalesb3be2292015-05-31 19:11:20 -0700260@end