1// gRPC-Web library 2import { 3 grpc, 4} from "@improbable-eng/grpc-web"; 5 6import { debug } from "../../../client/grpc-web/src/debug"; 7import { assert } from "chai"; 8 9// Generated Test Classes 10import { 11 PingRequest, 12 PingResponse, TextMessage, 13} from "../_proto/improbable/grpcweb/test/test_pb"; 14import { FailService, TestService } from "../_proto/improbable/grpcweb/test/test_pb_service"; 15import { DEBUG, UncaughtExceptionListener } from "./util"; 16import { 17 headerTrailerCombos, runWithHttp1AndHttp2, runWithSupportedTransports 18} from "./testRpcCombinations"; 19import { conditionallyRunTestSuite, SuiteEnum } from "../suiteUtils"; 20 21conditionallyRunTestSuite(SuiteEnum.unary, () => { 22 runWithHttp1AndHttp2(({ testHostUrl, corsHostUrl, unavailableHost, emptyHost }) => { 23 runWithSupportedTransports(transport => { 24 it(`should reject a server-streaming method`, () => { 25 const ping = new PingRequest(); 26 ping.setValue("hello world"); 27 28 assert.throw(() => { 29 grpc.unary(TestService.PingList as any as grpc.UnaryMethodDefinition<PingRequest, PingResponse>, { 30 debug: DEBUG, 31 transport: transport, 32 request: ping, 33 host: testHostUrl, 34 onEnd: ({ status, statusMessage, headers, message, trailers }) => { 35 DEBUG && debug("status", status, "statusMessage", statusMessage, "headers", headers, "res", message, "trailers", trailers); 36 } 37 }) 38 }, ".unary cannot be used with server-streaming methods. Use .invoke or .client instead."); 39 }); 40 41 it(`should reject a client-streaming method`, () => { 42 const ping = new PingRequest(); 43 ping.setValue("hello world"); 44 45 assert.throw(() => { 46 grpc.unary(TestService.PingStream as any as grpc.UnaryMethodDefinition<PingRequest, PingResponse>, { 47 debug: DEBUG, 48 transport: transport, 49 request: ping, 50 host: testHostUrl, 51 onEnd: ({ status, statusMessage, headers, message, trailers }) => { 52 DEBUG && debug("status", status, "statusMessage", statusMessage, "headers", headers, "res", message, "trailers", trailers); 53 } 54 }) 55 }, ".unary cannot be used with client-streaming methods. Use .client instead."); 56 }); 57 58 headerTrailerCombos((withHeaders, withTrailers) => { 59 it(`should make a unary request`, (done) => { 60 const ping = new PingRequest(); 61 ping.setValue("hello world"); 62 ping.setSendHeaders(withHeaders); 63 ping.setSendTrailers(withTrailers); 64 65 grpc.unary(TestService.Ping, { 66 debug: DEBUG, 67 transport: transport, 68 request: ping, 69 host: testHostUrl, 70 onEnd: ({ status, statusMessage, headers, message, trailers }) => { 71 DEBUG && debug("status", status, "statusMessage", statusMessage, "headers", headers, "res", message, "trailers", trailers); 72 assert.strictEqual(status, grpc.Code.OK, "expected OK (0)"); 73 assert.isNotOk(statusMessage, "expected no message"); 74 if (withHeaders) { 75 assert.deepEqual(headers.get("HeaderTestKey1"), ["ServerValue1"]); 76 assert.deepEqual(headers.get("HeaderTestKey2"), ["ServerValue2"]); 77 } 78 assert.ok(message instanceof PingResponse); 79 const asPingResponse: PingResponse = message as PingResponse; 80 assert.deepEqual(asPingResponse.getValue(), "hello world"); 81 assert.deepEqual(asPingResponse.getCounter(), 252); 82 if (withTrailers) { 83 assert.deepEqual(trailers.get("TrailerTestKey1"), ["ServerValue1"]); 84 assert.deepEqual(trailers.get("TrailerTestKey2"), ["ServerValue2"]); 85 } 86 done(); 87 } 88 }); 89 }); 90 }); 91 92 headerTrailerCombos((withHeaders, withTrailers) => { 93 it(`should make a large unary request`, (done) => { 94 let text = ""; 95 const iterations = 1024; 96 for (let i = 0; i < iterations; i++) { 97 text += "0123456789ABCDEF"; 98 } 99 assert.equal(text.length, 16384, "generated message length"); 100 101 const textMessage = new TextMessage(); 102 textMessage.setText(text); 103 textMessage.setSendHeaders(withHeaders); 104 textMessage.setSendTrailers(withTrailers); 105 106 grpc.unary(TestService.Echo, { 107 debug: DEBUG, 108 transport: transport, 109 request: textMessage, 110 host: testHostUrl, 111 onEnd: ({ status, statusMessage, headers, message, trailers }) => { 112 DEBUG && debug("status", status, "statusMessage", statusMessage, "headers", headers, "res", message, "trailers", trailers); 113 assert.strictEqual(status, grpc.Code.OK, "expected OK (0)"); 114 assert.isNotOk(statusMessage, "expected no message"); 115 if (withHeaders) { 116 assert.deepEqual(headers.get("HeaderTestKey1"), ["ServerValue1"]); 117 assert.deepEqual(headers.get("HeaderTestKey2"), ["ServerValue2"]); 118 } 119 assert.ok(message instanceof TextMessage); 120 const asTextMessage: TextMessage = message as TextMessage; 121 assert.equal(asTextMessage.getText().length, 16384, "response message length"); 122 if (withTrailers) { 123 assert.deepEqual(trailers.get("TrailerTestKey1"), ["ServerValue1"]); 124 assert.deepEqual(trailers.get("TrailerTestKey2"), ["ServerValue2"]); 125 } 126 done(); 127 } 128 }); 129 }); 130 }); 131 132 headerTrailerCombos((withHeaders, withTrailers) => { 133 it(`should make a unary request with metadata`, (done) => { 134 const ping = new PingRequest(); 135 ping.setValue("hello world"); 136 ping.setCheckMetadata(true); 137 ping.setSendHeaders(withHeaders); 138 ping.setSendTrailers(withTrailers); 139 140 grpc.unary(TestService.Ping, { 141 debug: DEBUG, 142 transport: transport, 143 request: ping, 144 metadata: new grpc.Metadata({ 145 "HeaderTestKey1": "ClientValue1", 146 "HeaderTestKey2": "ClientValue2", 147 }), 148 host: testHostUrl, 149 onEnd: ({ status, statusMessage, headers, message, trailers }) => { 150 DEBUG && debug("status", status, "statusMessage", statusMessage, "headers", headers, "res", message, "trailers", trailers); 151 assert.strictEqual(status, grpc.Code.OK, "expected OK (0)"); 152 assert.isNotOk(statusMessage, "expected no message"); 153 if (withHeaders) { 154 assert.deepEqual(headers.get("HeaderTestKey1"), ["ServerValue1"]); 155 assert.deepEqual(headers.get("HeaderTestKey2"), ["ServerValue2"]); 156 } 157 assert.ok(message instanceof PingResponse); 158 const asPingResponse: PingResponse = message as PingResponse; 159 assert.deepEqual(asPingResponse.getValue(), "hello world"); 160 assert.deepEqual(asPingResponse.getCounter(), 252); 161 if (withTrailers) { 162 assert.deepEqual(trailers.get("TrailerTestKey1"), ["ServerValue1"]); 163 assert.deepEqual(trailers.get("TrailerTestKey2"), ["ServerValue2"]); 164 } 165 done(); 166 } 167 }); 168 }); 169 }); 170 171 headerTrailerCombos((withHeaders, withTrailers) => { 172 it(`should report status code for error with headers + trailers`, (done) => { 173 const ping = new PingRequest(); 174 ping.setFailureType(PingRequest.FailureType.CODE); 175 ping.setErrorCodeReturned(12); 176 ping.setSendHeaders(withHeaders); 177 ping.setSendTrailers(withTrailers); 178 179 grpc.unary(TestService.PingError, { 180 debug: DEBUG, 181 transport: transport, 182 request: ping, 183 host: testHostUrl, 184 onEnd: ({ status, statusMessage, headers, message, trailers }) => { 185 DEBUG && debug("status", status, "statusMessage", statusMessage, "headers", headers, "res", message, "trailers", trailers); 186 assert.strictEqual(status, grpc.Code.Unimplemented); 187 assert.strictEqual(statusMessage, "Intentionally returning error for PingError"); 188 if (withHeaders) { 189 assert.deepEqual(headers.get("HeaderTestKey1"), ["ServerValue1"]); 190 assert.deepEqual(headers.get("HeaderTestKey2"), ["ServerValue2"]); 191 } 192 assert.isNull(message); 193 assert.deepEqual(trailers.get("grpc-status"), ["12"]); 194 assert.deepEqual(trailers.get("grpc-message"), ["Intentionally returning error for PingError"]); 195 if (withTrailers) { 196 assert.deepEqual(trailers.get("TrailerTestKey1"), ["ServerValue1"]); 197 assert.deepEqual(trailers.get("TrailerTestKey2"), ["ServerValue2"]); 198 } 199 done(); 200 } 201 }); 202 }); 203 }); 204 205 if (!process.env.DISABLE_CORS_TESTS) { 206 it(`should report failure for a CORS failure`, (done) => { 207 const ping = new PingRequest(); 208 209 grpc.unary(FailService.NonExistant, { // The test server hasn't registered this service, so it should fail CORS 210 debug: DEBUG, 211 transport: transport, 212 request: ping, 213 // This test is actually calling the same server as the other tests, but the server should reject the OPTIONS call 214 // because the service isn't registered. This could be the same host as all other tests (that should be CORS 215 // requests because they differ by port from the page the tests are run from), but IE treats different ports on 216 // the same host as the same origin, so this request has to be made to a different host to trigger CORS behaviour. 217 host: corsHostUrl, 218 onEnd: ({ status, statusMessage, headers, message, trailers }) => { 219 DEBUG && debug("status", status, "statusMessage", statusMessage, "headers", headers, "res", message, "trailers", trailers); 220 // Some browsers return empty Headers for failed requests 221 assert.strictEqual(statusMessage, "Response closed without headers"); 222 assert.strictEqual(status, grpc.Code.Unknown); 223 done(); 224 } 225 }); 226 }); 227 } 228 229 it(`should report failure for a request to an invalid host`, (done) => { 230 const ping = new PingRequest(); 231 232 grpc.unary(TestService.Ping, { 233 debug: DEBUG, 234 transport: transport, 235 request: ping, 236 host: unavailableHost, // Should not be available 237 onEnd: ({ status, statusMessage, headers, message, trailers }) => { 238 DEBUG && debug("status", status, "statusMessage", statusMessage, "headers", headers, "res", message, "trailers", trailers); 239 assert.strictEqual(statusMessage, "Response closed without headers"); 240 assert.strictEqual(status, grpc.Code.Unknown); 241 assert.isNull(message); 242 done(); 243 } 244 }); 245 }); 246 247 it(`should report failure for a trailers-only response`, (done) => { 248 const ping = new PingRequest(); 249 250 grpc.unary(FailService.NonExistant, { // The test server hasn't registered this service, so it should return an error 251 debug: DEBUG, 252 transport: transport, 253 request: ping, 254 host: emptyHost, 255 onEnd: ({ status, statusMessage, headers, message, trailers }) => { 256 DEBUG && debug("status", status, "statusMessage", statusMessage, "headers", headers, "res", message, "trailers", trailers); 257 assert.strictEqual(statusMessage, "unknown service improbable.grpcweb.test.FailService"); 258 assert.strictEqual(status, 12); 259 assert.isNull(message); 260 assert.deepEqual(headers.get("grpc-status"), ["12"]); 261 assert.deepEqual(headers.get("grpc-message"), ["unknown service improbable.grpcweb.test.FailService"]); 262 assert.deepEqual(trailers.get("grpc-status"), ["12"]); 263 assert.deepEqual(trailers.get("grpc-message"), ["unknown service improbable.grpcweb.test.FailService"]); 264 done(); 265 } 266 }); 267 }); 268 269 describe(`exception handling`, () => { 270 let uncaughtHandler: UncaughtExceptionListener; 271 beforeEach(() => { 272 uncaughtHandler = new UncaughtExceptionListener(); 273 uncaughtHandler.attach(); 274 }); 275 276 afterEach(() => { 277 uncaughtHandler.detach(); 278 }); 279 280 it(`should not suppress exceptions`, (done) => { 281 const ping = new PingRequest(); 282 ping.setValue("hello world"); 283 284 grpc.unary(TestService.Ping, { 285 debug: DEBUG, 286 transport: transport, 287 request: ping, 288 host: testHostUrl, 289 onEnd: ({ status, statusMessage, headers, message, trailers }) => { 290 DEBUG && debug("status", status, "statusMessage", statusMessage, "headers", headers, "res", message, "trailers", trailers); 291 setTimeout(() => { 292 uncaughtHandler.detach(); 293 const exceptionsCaught = uncaughtHandler.getMessages(); 294 assert.lengthOf(exceptionsCaught, 1); 295 assert.include(exceptionsCaught[0], "onEnd exception"); 296 done(); 297 }, 100); 298 throw new Error("onEnd exception"); 299 } 300 }); 301 }); 302 }); 303 }); 304 }); 305}); 306