GRPCClientTests.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /*
  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. #import <UIKit/UIKit.h>
  34. #import <XCTest/XCTest.h>
  35. #import <GRPCClient/GRPCCall.h>
  36. #import <GRPCClient/GRPCCall+OAuth2.h>
  37. #import <GRPCClient/GRPCCall+Tests.h>
  38. #import <ProtoRPC/ProtoMethod.h>
  39. #import <RemoteTest/Messages.pbobjc.h>
  40. #import <RxLibrary/GRXWriteable.h>
  41. #import <RxLibrary/GRXWriter+Immediate.h>
  42. // These are a few tests similar to InteropTests, but which use the generic gRPC client (GRPCCall)
  43. // rather than a generated proto library on top of it.
  44. static NSString * const kHostAddress = @"localhost:5050";
  45. static NSString * const kPackage = @"grpc.testing";
  46. static NSString * const kService = @"TestService";
  47. static ProtoMethod *kInexistentMethod;
  48. static ProtoMethod *kEmptyCallMethod;
  49. static ProtoMethod *kUnaryCallMethod;
  50. // This is an observer class for testing that responseMetadata is KVO-compliant
  51. @interface PassthroughObserver : NSObject
  52. - (instancetype) initWithCallback:(void (^)(NSString*, id, NSDictionary*))callback;
  53. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change
  54. context:(void *)context;
  55. @end
  56. @implementation PassthroughObserver {
  57. void (^_callback)(NSString*, id, NSDictionary*);
  58. }
  59. - (instancetype)initWithCallback:(void (^)(NSString *, id, NSDictionary *))callback {
  60. self = [super init];
  61. if (self) {
  62. _callback = callback;
  63. }
  64. return self;
  65. }
  66. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
  67. {
  68. _callback(keyPath, object, change);
  69. [object removeObserver:self forKeyPath:keyPath];
  70. }
  71. @end
  72. @interface GRPCClientTests : XCTestCase
  73. @end
  74. @implementation GRPCClientTests
  75. - (void)setUp {
  76. // Register test server as non-SSL.
  77. [GRPCCall useInsecureConnectionsForHost:kHostAddress];
  78. // This method isn't implemented by the remote server.
  79. kInexistentMethod = [[ProtoMethod alloc] initWithPackage:kPackage
  80. service:kService
  81. method:@"Inexistent"];
  82. kEmptyCallMethod = [[ProtoMethod alloc] initWithPackage:kPackage
  83. service:kService
  84. method:@"EmptyCall"];
  85. kUnaryCallMethod = [[ProtoMethod alloc] initWithPackage:kPackage
  86. service:kService
  87. method:@"UnaryCall"];
  88. }
  89. - (void)testConnectionToRemoteServer {
  90. __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."];
  91. GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
  92. path:kInexistentMethod.HTTPPath
  93. requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
  94. id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
  95. XCTFail(@"Received unexpected response: %@", value);
  96. } completionHandler:^(NSError *errorOrNil) {
  97. XCTAssertNotNil(errorOrNil, @"Finished without error!");
  98. // TODO(jcanizales): The server should return code 12 UNIMPLEMENTED, not 5 NOT FOUND.
  99. XCTAssertEqual(errorOrNil.code, 5, @"Finished with unexpected error: %@", errorOrNil);
  100. [expectation fulfill];
  101. }];
  102. [call startWithWriteable:responsesWriteable];
  103. [self waitForExpectationsWithTimeout:4 handler:nil];
  104. }
  105. - (void)testEmptyRPC {
  106. __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."];
  107. __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
  108. GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
  109. path:kEmptyCallMethod.HTTPPath
  110. requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
  111. id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
  112. XCTAssertNotNil(value, @"nil value received as response.");
  113. XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
  114. [response fulfill];
  115. } completionHandler:^(NSError *errorOrNil) {
  116. XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
  117. [completion fulfill];
  118. }];
  119. [call startWithWriteable:responsesWriteable];
  120. [self waitForExpectationsWithTimeout:8 handler:nil];
  121. }
  122. - (void)testSimpleProtoRPC {
  123. __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."];
  124. __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
  125. RMTSimpleRequest *request = [RMTSimpleRequest message];
  126. request.responseSize = 100;
  127. request.fillUsername = YES;
  128. request.fillOauthScope = YES;
  129. GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
  130. GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
  131. path:kUnaryCallMethod.HTTPPath
  132. requestsWriter:requestsWriter];
  133. id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
  134. XCTAssertNotNil(value, @"nil value received as response.");
  135. XCTAssertGreaterThan(value.length, 0, @"Empty response received.");
  136. RMTSimpleResponse *responseProto = [RMTSimpleResponse parseFromData:value error:NULL];
  137. // We expect empty strings, not nil:
  138. XCTAssertNotNil(responseProto.username, @"Response's username is nil.");
  139. XCTAssertNotNil(responseProto.oauthScope, @"Response's OAuth scope is nil.");
  140. [response fulfill];
  141. } completionHandler:^(NSError *errorOrNil) {
  142. XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
  143. [completion fulfill];
  144. }];
  145. [call startWithWriteable:responsesWriteable];
  146. [self waitForExpectationsWithTimeout:8 handler:nil];
  147. }
  148. - (void)testMetadata {
  149. __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."];
  150. RMTSimpleRequest *request = [RMTSimpleRequest message];
  151. request.fillUsername = YES;
  152. request.fillOauthScope = YES;
  153. GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
  154. GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
  155. path:kUnaryCallMethod.HTTPPath
  156. requestsWriter:requestsWriter];
  157. call.oauth2AccessToken = @"bogusToken";
  158. id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
  159. XCTFail(@"Received unexpected response: %@", value);
  160. } completionHandler:^(NSError *errorOrNil) {
  161. XCTAssertNotNil(errorOrNil, @"Finished without error!");
  162. XCTAssertEqual(errorOrNil.code, 16, @"Finished with unexpected error: %@", errorOrNil);
  163. XCTAssertEqualObjects(call.responseHeaders, errorOrNil.userInfo[kGRPCHeadersKey],
  164. @"Headers in the NSError object and call object differ.");
  165. XCTAssertEqualObjects(call.responseTrailers, errorOrNil.userInfo[kGRPCTrailersKey],
  166. @"Trailers in the NSError object and call object differ.");
  167. NSString *challengeHeader = call.oauth2ChallengeHeader;
  168. XCTAssertGreaterThan(challengeHeader.length, 0,
  169. @"No challenge in response headers %@", call.responseHeaders);
  170. [expectation fulfill];
  171. }];
  172. [call startWithWriteable:responsesWriteable];
  173. [self waitForExpectationsWithTimeout:4 handler:nil];
  174. }
  175. - (void)testResponseMetadataKVO {
  176. __weak XCTestExpectation *response = [self expectationWithDescription:@"Empty response received."];
  177. __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
  178. __weak XCTestExpectation *metadata = [self expectationWithDescription:@"Metadata changed."];
  179. GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
  180. path:kEmptyCallMethod.HTTPPath
  181. requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
  182. PassthroughObserver *observer = [[PassthroughObserver alloc] initWithCallback:^(NSString *keypath, id object, NSDictionary * change) {
  183. if ([keypath isEqual: @"responseHeaders"]) {
  184. [metadata fulfill];
  185. }
  186. }];
  187. [call addObserver:observer forKeyPath:@"responseHeaders" options:0 context:NULL];
  188. id<GRXWriteable> responsesWriteable = [[GRXWriteable alloc] initWithValueHandler:^(NSData *value) {
  189. XCTAssertNotNil(value, @"nil value received as response.");
  190. XCTAssertEqual([value length], 0, @"Non-empty response received: %@", value);
  191. [response fulfill];
  192. } completionHandler:^(NSError *errorOrNil) {
  193. XCTAssertNil(errorOrNil, @"Finished with unexpected error: %@", errorOrNil);
  194. [completion fulfill];
  195. }];
  196. [call startWithWriteable:responsesWriteable];
  197. [self waitForExpectationsWithTimeout:8 handler:nil];
  198. }
  199. @end