xref: /aosp_15_r20/external/pigweed/pw_rpc/ts/call_test.ts (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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