1*61c4878aSAndroid Build Coastguard Worker// Copyright 2022 The Pigweed Authors 2*61c4878aSAndroid Build Coastguard Worker// 3*61c4878aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); you may not 4*61c4878aSAndroid Build Coastguard Worker// use this file except in compliance with the License. You may obtain a copy of 5*61c4878aSAndroid Build Coastguard Worker// the License at 6*61c4878aSAndroid Build Coastguard Worker// 7*61c4878aSAndroid Build Coastguard Worker// https://www.apache.org/licenses/LICENSE-2.0 8*61c4878aSAndroid Build Coastguard Worker// 9*61c4878aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*61c4878aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11*61c4878aSAndroid Build Coastguard Worker// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12*61c4878aSAndroid Build Coastguard Worker// License for the specific language governing permissions and limitations under 13*61c4878aSAndroid Build Coastguard Worker// the License. 14*61c4878aSAndroid Build Coastguard Worker 15*61c4878aSAndroid Build Coastguard Worker/* eslint-env browser */ 16*61c4878aSAndroid Build Coastguard Worker 17*61c4878aSAndroid Build Coastguard Workerimport { SomeMessage } from 'pigweedjs/protos/pw_rpc/ts/test2_pb'; 18*61c4878aSAndroid Build Coastguard Workerimport { Status } from 'pigweedjs/pw_status'; 19*61c4878aSAndroid Build Coastguard Worker 20*61c4878aSAndroid Build Coastguard Workerimport { Call, ServerStreamingCall } from './call'; 21*61c4878aSAndroid Build Coastguard Workerimport { Channel, Method, Service } from './descriptors'; 22*61c4878aSAndroid Build Coastguard Workerimport { PendingCalls, Rpc } from './rpc_classes'; 23*61c4878aSAndroid Build Coastguard Worker 24*61c4878aSAndroid Build Coastguard Workerclass FakeRpc { 25*61c4878aSAndroid Build Coastguard Worker readonly channel: any = undefined; 26*61c4878aSAndroid Build Coastguard Worker readonly service: any = undefined; 27*61c4878aSAndroid Build Coastguard Worker readonly method: any = undefined; 28*61c4878aSAndroid Build Coastguard Worker 29*61c4878aSAndroid Build Coastguard Worker getIdSet(callId: number): [number, number, number, number] { 30*61c4878aSAndroid Build Coastguard Worker return [1, 2, 3, callId]; 31*61c4878aSAndroid Build Coastguard Worker } 32*61c4878aSAndroid Build Coastguard Worker 33*61c4878aSAndroid Build Coastguard Worker getIdString(callId: number): string { 34*61c4878aSAndroid Build Coastguard Worker return '1.2.3.' + callId; 35*61c4878aSAndroid Build Coastguard Worker } 36*61c4878aSAndroid Build Coastguard Worker} 37*61c4878aSAndroid Build Coastguard Worker 38*61c4878aSAndroid Build Coastguard Workerdescribe('Call', () => { 39*61c4878aSAndroid Build Coastguard Worker let call: Call; 40*61c4878aSAndroid Build Coastguard Worker 41*61c4878aSAndroid Build Coastguard Worker beforeEach(() => { 42*61c4878aSAndroid Build Coastguard Worker const noop = () => { 43*61c4878aSAndroid Build Coastguard Worker // Do nothing. 44*61c4878aSAndroid Build Coastguard Worker }; 45*61c4878aSAndroid Build Coastguard Worker const pendingCalls = new PendingCalls(); 46*61c4878aSAndroid Build Coastguard Worker const rpc = new FakeRpc(); 47*61c4878aSAndroid Build Coastguard Worker call = new Call(pendingCalls, rpc, noop, noop, noop, 4); 48*61c4878aSAndroid Build Coastguard Worker }); 49*61c4878aSAndroid Build Coastguard Worker 50*61c4878aSAndroid Build Coastguard Worker function newMessage(magicNumber = 1): SomeMessage { 51*61c4878aSAndroid Build Coastguard Worker const message = new SomeMessage(); 52*61c4878aSAndroid Build Coastguard Worker message.setMagicNumber(magicNumber); 53*61c4878aSAndroid Build Coastguard Worker return message; 54*61c4878aSAndroid Build Coastguard Worker } 55*61c4878aSAndroid Build Coastguard Worker 56*61c4878aSAndroid Build Coastguard Worker it('getResponse returns all responses.', async () => { 57*61c4878aSAndroid Build Coastguard Worker const message1 = newMessage(1); 58*61c4878aSAndroid Build Coastguard Worker const message2 = newMessage(2); 59*61c4878aSAndroid Build Coastguard Worker const message3 = newMessage(3); 60*61c4878aSAndroid Build Coastguard Worker 61*61c4878aSAndroid Build Coastguard Worker // Queue three responses 62*61c4878aSAndroid Build Coastguard Worker call.handleResponse(message1); 63*61c4878aSAndroid Build Coastguard Worker call.handleResponse(message2); 64*61c4878aSAndroid Build Coastguard Worker call.handleResponse(message3); 65*61c4878aSAndroid Build Coastguard Worker 66*61c4878aSAndroid Build Coastguard Worker let responses = call.getResponses(2); 67*61c4878aSAndroid Build Coastguard Worker expect((await responses.next()).value).toEqual(message1); 68*61c4878aSAndroid Build Coastguard Worker expect((await responses.next()).value).toEqual(message2); 69*61c4878aSAndroid Build Coastguard Worker expect((await responses.next()).done).toEqual(true); 70*61c4878aSAndroid Build Coastguard Worker 71*61c4878aSAndroid Build Coastguard Worker responses = call.getResponses(1); 72*61c4878aSAndroid Build Coastguard Worker expect((await responses.next()).value).toEqual(message3); 73*61c4878aSAndroid Build Coastguard Worker expect((await responses.next()).done).toEqual(true); 74*61c4878aSAndroid Build Coastguard Worker }); 75*61c4878aSAndroid Build Coastguard Worker 76*61c4878aSAndroid Build Coastguard Worker it('getResponse early returns on stream end.', async () => { 77*61c4878aSAndroid Build Coastguard Worker const message = newMessage(); 78*61c4878aSAndroid Build Coastguard Worker const responses = call.getResponses(2); 79*61c4878aSAndroid Build Coastguard Worker 80*61c4878aSAndroid Build Coastguard Worker // Queue one response and an early completion. 81*61c4878aSAndroid Build Coastguard Worker call.handleResponse(message); 82*61c4878aSAndroid Build Coastguard Worker call.handleCompletion(0); 83*61c4878aSAndroid Build Coastguard Worker 84*61c4878aSAndroid Build Coastguard Worker expect((await responses.next()).value).toEqual(message); 85*61c4878aSAndroid Build Coastguard Worker expect((await responses.next()).done).toEqual(true); 86*61c4878aSAndroid Build Coastguard Worker }); 87*61c4878aSAndroid Build Coastguard Worker 88*61c4878aSAndroid Build Coastguard Worker it('getResponse promise is rejected on stream error.', async () => { 89*61c4878aSAndroid Build Coastguard Worker expect.assertions(2); 90*61c4878aSAndroid Build Coastguard Worker const message = newMessage(); 91*61c4878aSAndroid Build Coastguard Worker const responses = call.getResponses(3); 92*61c4878aSAndroid Build Coastguard Worker 93*61c4878aSAndroid Build Coastguard Worker call.handleResponse(message); 94*61c4878aSAndroid Build Coastguard Worker expect((await responses.next()).value).toEqual(message); 95*61c4878aSAndroid Build Coastguard Worker 96*61c4878aSAndroid Build Coastguard Worker call.handleResponse(message); 97*61c4878aSAndroid Build Coastguard Worker call.handleError(1); 98*61c4878aSAndroid Build Coastguard Worker 99*61c4878aSAndroid Build Coastguard Worker // Promise is rejected as soon as an error is received, even if there is a 100*61c4878aSAndroid Build Coastguard Worker // response in the queue. 101*61c4878aSAndroid Build Coastguard Worker responses.next().catch((e: Error) => { 102*61c4878aSAndroid Build Coastguard Worker expect(e.name).toEqual('TypeError'); 103*61c4878aSAndroid Build Coastguard Worker }); 104*61c4878aSAndroid Build Coastguard Worker }); 105*61c4878aSAndroid Build Coastguard Worker 106*61c4878aSAndroid Build Coastguard Worker it('getResponse waits if queue is empty', async () => { 107*61c4878aSAndroid Build Coastguard Worker const message1 = newMessage(1); 108*61c4878aSAndroid Build Coastguard Worker const message2 = newMessage(2); 109*61c4878aSAndroid Build Coastguard Worker const responses = call.getResponses(2); 110*61c4878aSAndroid Build Coastguard Worker 111*61c4878aSAndroid Build Coastguard Worker // Queue two responses after a small delay 112*61c4878aSAndroid Build Coastguard Worker setTimeout(() => { 113*61c4878aSAndroid Build Coastguard Worker call.handleResponse(message1); 114*61c4878aSAndroid Build Coastguard Worker call.handleResponse(message2); 115*61c4878aSAndroid Build Coastguard Worker call.handleCompletion(0); 116*61c4878aSAndroid Build Coastguard Worker expect(call.completed).toEqual(true); 117*61c4878aSAndroid Build Coastguard Worker }, 200); 118*61c4878aSAndroid Build Coastguard Worker 119*61c4878aSAndroid Build Coastguard Worker expect(call.completed).toEqual(false); 120*61c4878aSAndroid Build Coastguard Worker expect((await responses.next()).value).toEqual(message1); 121*61c4878aSAndroid Build Coastguard Worker expect((await responses.next()).value).toEqual(message2); 122*61c4878aSAndroid Build Coastguard Worker expect((await responses.next()).done).toEqual(true); 123*61c4878aSAndroid Build Coastguard Worker }); 124*61c4878aSAndroid Build Coastguard Worker 125*61c4878aSAndroid Build Coastguard Worker it('getResponse without count fetches all results', async () => { 126*61c4878aSAndroid Build Coastguard Worker const message1 = newMessage(1); 127*61c4878aSAndroid Build Coastguard Worker const message2 = newMessage(2); 128*61c4878aSAndroid Build Coastguard Worker const responses = call.getResponses(); 129*61c4878aSAndroid Build Coastguard Worker 130*61c4878aSAndroid Build Coastguard Worker call.handleResponse(message1); 131*61c4878aSAndroid Build Coastguard Worker expect((await responses.next()).value).toEqual(message1); 132*61c4878aSAndroid Build Coastguard Worker 133*61c4878aSAndroid Build Coastguard Worker setTimeout(() => { 134*61c4878aSAndroid Build Coastguard Worker call.handleResponse(message2); 135*61c4878aSAndroid Build Coastguard Worker call.handleCompletion(0); 136*61c4878aSAndroid Build Coastguard Worker expect(call.completed).toEqual(true); 137*61c4878aSAndroid Build Coastguard Worker }, 200); 138*61c4878aSAndroid Build Coastguard Worker 139*61c4878aSAndroid Build Coastguard Worker expect(call.completed).toEqual(false); 140*61c4878aSAndroid Build Coastguard Worker expect((await responses.next()).value).toEqual(message2); 141*61c4878aSAndroid Build Coastguard Worker expect((await responses.next()).done).toEqual(true); 142*61c4878aSAndroid Build Coastguard Worker }); 143*61c4878aSAndroid Build Coastguard Worker 144*61c4878aSAndroid Build Coastguard Worker it('getResponses limits to maximum number of responses', async () => { 145*61c4878aSAndroid Build Coastguard Worker const allResponses = []; 146*61c4878aSAndroid Build Coastguard Worker const noop = () => { 147*61c4878aSAndroid Build Coastguard Worker // Do nothing. 148*61c4878aSAndroid Build Coastguard Worker }; 149*61c4878aSAndroid Build Coastguard Worker const pendingCalls = new PendingCalls(); 150*61c4878aSAndroid Build Coastguard Worker const rpc = new FakeRpc(); 151*61c4878aSAndroid Build Coastguard Worker const streamCall = new ServerStreamingCall( 152*61c4878aSAndroid Build Coastguard Worker pendingCalls, 153*61c4878aSAndroid Build Coastguard Worker rpc, 154*61c4878aSAndroid Build Coastguard Worker (res) => allResponses.push(res), 155*61c4878aSAndroid Build Coastguard Worker noop, 156*61c4878aSAndroid Build Coastguard Worker noop, 157*61c4878aSAndroid Build Coastguard Worker 4, 158*61c4878aSAndroid Build Coastguard Worker ); 159*61c4878aSAndroid Build Coastguard Worker 160*61c4878aSAndroid Build Coastguard Worker const message1 = newMessage(1); 161*61c4878aSAndroid Build Coastguard Worker const message2 = newMessage(2); 162*61c4878aSAndroid Build Coastguard Worker const message3 = newMessage(3); 163*61c4878aSAndroid Build Coastguard Worker const message4 = newMessage(4); 164*61c4878aSAndroid Build Coastguard Worker const message5 = newMessage(5); 165*61c4878aSAndroid Build Coastguard Worker const message6 = newMessage(6); 166*61c4878aSAndroid Build Coastguard Worker 167*61c4878aSAndroid Build Coastguard Worker setTimeout(() => { 168*61c4878aSAndroid Build Coastguard Worker streamCall.handleResponse(message1); 169*61c4878aSAndroid Build Coastguard Worker streamCall.handleResponse(message2); 170*61c4878aSAndroid Build Coastguard Worker streamCall.handleResponse(message3); 171*61c4878aSAndroid Build Coastguard Worker streamCall.handleResponse(message4); 172*61c4878aSAndroid Build Coastguard Worker streamCall.handleResponse(message5); 173*61c4878aSAndroid Build Coastguard Worker streamCall.handleResponse(message6); 174*61c4878aSAndroid Build Coastguard Worker streamCall.handleCompletion(Status.OK); 175*61c4878aSAndroid Build Coastguard Worker }, 200); 176*61c4878aSAndroid Build Coastguard Worker 177*61c4878aSAndroid Build Coastguard Worker // All 5 responses are received, but only the most recent 4 are stored. 178*61c4878aSAndroid Build Coastguard Worker const [status, responses] = await streamCall.complete(); 179*61c4878aSAndroid Build Coastguard Worker expect(status).toEqual(Status.OK); 180*61c4878aSAndroid Build Coastguard Worker expect(allResponses).toEqual([ 181*61c4878aSAndroid Build Coastguard Worker message1, 182*61c4878aSAndroid Build Coastguard Worker message2, 183*61c4878aSAndroid Build Coastguard Worker message3, 184*61c4878aSAndroid Build Coastguard Worker message4, 185*61c4878aSAndroid Build Coastguard Worker message5, 186*61c4878aSAndroid Build Coastguard Worker message6, 187*61c4878aSAndroid Build Coastguard Worker ]); 188*61c4878aSAndroid Build Coastguard Worker expect(responses).toEqual([message3, message4, message5, message6]); 189*61c4878aSAndroid Build Coastguard Worker }); 190*61c4878aSAndroid Build Coastguard Worker}); 191