xref: /aosp_15_r20/external/pigweed/pw_transfer/ts/transfer_test.ts (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1// Copyright 2022 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15/* eslint-env browser */
16
17import {
18  Channel,
19  Client,
20  decode,
21  MethodStub,
22  ServiceClient,
23} from 'pigweedjs/pw_rpc';
24import { Status } from 'pigweedjs/pw_status';
25import {
26  PacketType,
27  RpcPacket,
28} from 'pigweedjs/protos/pw_rpc/internal/packet_pb';
29import { ProtoCollection } from 'pigweedjs/protos/collection';
30import { Chunk } from 'pigweedjs/protos/pw_transfer/transfer_pb';
31
32import { Manager } from './client';
33import { ProgressStats } from './transfer';
34
35const DEFAULT_TIMEOUT_S = 0.3;
36
37describe('Transfer client', () => {
38  const textEncoder = new TextEncoder();
39  const textDecoder = new TextDecoder();
40  let client: Client;
41  let service: ServiceClient;
42  let sentChunks: Chunk[];
43  let packetsToSend: Uint8Array[][];
44
45  beforeEach(() => {
46    const lib = new ProtoCollection();
47    const channels: Channel[] = [new Channel(1, handleRequest)];
48    client = Client.fromProtoSet(channels, lib);
49    service = client.channel(1)!.service('pw.transfer.Transfer')!;
50
51    sentChunks = [];
52    packetsToSend = [];
53  });
54
55  function handleRequest(data: Uint8Array): void {
56    const packet = decode(data);
57    if (packet.getType() !== PacketType.CLIENT_STREAM) {
58      return;
59    }
60
61    const chunk = Chunk.deserializeBinary(packet.getPayload_asU8());
62    sentChunks.push(chunk);
63
64    if (packetsToSend.length > 0) {
65      const responses = packetsToSend.shift()!;
66      for (const response of responses) {
67        client.processPacket(response);
68      }
69    }
70  }
71
72  function receivedData(): Uint8Array {
73    let length = 0;
74    sentChunks.forEach((chunk: Chunk) => {
75      length += chunk.getData().length;
76    });
77    const data = new Uint8Array(length);
78    let offset = 0;
79    sentChunks.forEach((chunk: Chunk) => {
80      data.set(chunk.getData() as Uint8Array, offset);
81      offset += chunk.getData().length;
82    });
83    return data;
84  }
85
86  function enqueueServerError(method: MethodStub, error: Status): void {
87    const packet = new RpcPacket();
88    packet.setType(PacketType.SERVER_ERROR);
89    packet.setChannelId(1);
90    packet.setServiceId(service.id);
91    packet.setMethodId(method.id);
92    packet.setCallId(method.rpcs.nextCallId);
93    packet.setStatus(error);
94    packetsToSend.push([packet.serializeBinary()]);
95  }
96
97  function enqueueServerResponses(method: MethodStub, responses: Chunk[][]) {
98    for (const responseGroup of responses) {
99      const serializedGroup = [];
100      for (const response of responseGroup) {
101        const packet = new RpcPacket();
102        packet.setType(PacketType.SERVER_STREAM);
103        packet.setChannelId(1);
104        packet.setServiceId(service.id);
105        packet.setMethodId(method.id);
106        packet.setCallId(method.rpcs.nextCallId);
107        packet.setStatus(Status.OK);
108        packet.setPayload(response.serializeBinary());
109        serializedGroup.push(packet.serializeBinary());
110      }
111      packetsToSend.push(serializedGroup);
112    }
113  }
114
115  function buildChunk(
116    sessionId: number,
117    offset: number,
118    data: string,
119    remainingBytes: number,
120  ): Chunk {
121    const chunk = new Chunk();
122    chunk.setTransferId(sessionId);
123    chunk.setOffset(offset);
124    chunk.setData(textEncoder.encode(data));
125    chunk.setRemainingBytes(remainingBytes);
126    return chunk;
127  }
128
129  it('read transfer basic', async () => {
130    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
131
132    const chunk1 = buildChunk(3, 0, 'abc', 0);
133    enqueueServerResponses(service.method('Read')!, [[chunk1]]);
134
135    const data = await manager.read(3);
136    expect(textDecoder.decode(data)).toEqual('abc');
137    expect(sentChunks).toHaveLength(2);
138    expect(sentChunks[sentChunks.length - 1].hasStatus()).toBe(true);
139    expect(sentChunks[sentChunks.length - 1].getStatus()).toEqual(Status.OK);
140  });
141
142  it('read transfer multichunk', async () => {
143    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
144
145    const chunk1 = buildChunk(3, 0, 'abc', 3);
146    const chunk2 = buildChunk(3, 3, 'def', 0);
147    enqueueServerResponses(service.method('Read')!, [[chunk1, chunk2]]);
148
149    const data = await manager.read(3);
150    expect(data).toEqual(textEncoder.encode('abcdef'));
151    expect(sentChunks).toHaveLength(2);
152    expect(sentChunks[sentChunks.length - 1].hasStatus()).toBe(true);
153    expect(sentChunks[sentChunks.length - 1].getStatus()).toEqual(Status.OK);
154  });
155
156  it('read transfer progress callback', async () => {
157    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
158
159    const chunk1 = buildChunk(3, 0, 'abc', 3);
160    const chunk2 = buildChunk(3, 3, 'def', 0);
161    enqueueServerResponses(service.method('Read')!, [[chunk1, chunk2]]);
162
163    const progress: Array<ProgressStats> = [];
164
165    const data = await manager.read(3, (stats: ProgressStats) => {
166      progress.push(stats);
167    });
168    expect(textDecoder.decode(data)).toEqual('abcdef');
169    expect(sentChunks).toHaveLength(2);
170    expect(sentChunks[sentChunks.length - 1].hasStatus()).toBe(true);
171    expect(sentChunks[sentChunks.length - 1].getStatus()).toEqual(Status.OK);
172
173    expect(progress).toEqual([
174      new ProgressStats(3, 3, 6),
175      new ProgressStats(6, 6, 6),
176    ]);
177  });
178
179  it('read transfer retry bad offset', async () => {
180    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
181
182    const chunk1 = buildChunk(3, 0, '123', 6);
183    const chunk2 = buildChunk(3, 1, '456', 3); // Incorrect offset; expecting 3
184    const chunk3 = buildChunk(3, 3, '456', 3);
185    const chunk4 = buildChunk(3, 6, '789', 0);
186
187    enqueueServerResponses(service.method('Read')!, [
188      [chunk1, chunk2],
189      [chunk3, chunk4],
190    ]);
191
192    const data = await manager.read(3);
193    expect(data).toEqual(textEncoder.encode('123456789'));
194    expect(sentChunks).toHaveLength(3);
195    expect(sentChunks[sentChunks.length - 1].hasStatus()).toBe(true);
196    expect(sentChunks[sentChunks.length - 1].getStatus()).toEqual(Status.OK);
197  });
198
199  it('read transfer retry re-sent offset', async () => {
200    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
201
202    const chunk1 = buildChunk(3, 0, '01234567', 8);
203    const chunk2 = buildChunk(3, 4, '4567', 8);
204    const chunk3 = buildChunk(3, 8, '89abcdef', 0);
205
206    enqueueServerResponses(service.method('Read')!, [
207      [chunk1, chunk2],
208      [chunk3],
209    ]);
210
211    const data = await manager.read(3);
212    expect(data).toEqual(textEncoder.encode('0123456789abcdef'));
213    expect(sentChunks).toHaveLength(3);
214    expect(sentChunks[1].getType()).toEqual(Chunk.Type.PARAMETERS_CONTINUE);
215    expect(sentChunks[1].getOffset()).toEqual(8);
216    expect(sentChunks[sentChunks.length - 1].hasStatus()).toBe(true);
217    expect(sentChunks[sentChunks.length - 1].getStatus()).toEqual(Status.OK);
218  });
219
220  it('read transfer retry timeout', async () => {
221    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
222
223    const chunk = buildChunk(3, 0, 'xyz', 0);
224    enqueueServerResponses(service.method('Read')!, [[], [chunk]]);
225
226    const data = await manager.read(3);
227    expect(textDecoder.decode(data)).toEqual('xyz');
228
229    // Two transfer parameter requests should have been sent.
230    expect(sentChunks).toHaveLength(3);
231    expect(sentChunks[sentChunks.length - 1].hasStatus()).toBe(true);
232    expect(sentChunks[sentChunks.length - 1].getStatus()).toEqual(Status.OK);
233  });
234
235  it('read transfer timeout', async () => {
236    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
237
238    await manager
239      .read(27)
240      .then(() => {
241        fail('Unexpected completed promise');
242      })
243      .catch((error) => {
244        expect(error.id).toEqual(27);
245        expect(Status[error.status]).toEqual(Status[Status.DEADLINE_EXCEEDED]);
246        expect(sentChunks).toHaveLength(4);
247      });
248  });
249
250  it('read transfer error', async () => {
251    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
252
253    const chunk = new Chunk();
254    chunk.setStatus(Status.NOT_FOUND);
255    chunk.setTransferId(31);
256    enqueueServerResponses(service.method('Read')!, [[chunk]]);
257
258    await manager
259      .read(31)
260      .then(() => {
261        fail('Unexpected completed promise');
262      })
263      .catch((error) => {
264        expect(error.id).toEqual(31);
265        expect(Status[error.status]).toEqual(Status[Status.NOT_FOUND]);
266      });
267  });
268
269  it('read transfer server error', async () => {
270    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
271
272    enqueueServerError(service.method('Read')!, Status.NOT_FOUND);
273    await manager
274      .read(31)
275      .then((data) => {
276        fail('Unexpected completed promise');
277      })
278      .catch((error) => {
279        expect(error.id).toEqual(31);
280        expect(Status[error.status]).toEqual(Status[Status.INTERNAL]);
281      });
282  });
283
284  it('write transfer basic', async () => {
285    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
286
287    const chunk = new Chunk();
288    chunk.setTransferId(4);
289    chunk.setOffset(0);
290    chunk.setPendingBytes(32);
291    chunk.setMaxChunkSizeBytes(8);
292
293    const completeChunk = new Chunk();
294    completeChunk.setTransferId(4);
295    completeChunk.setStatus(Status.OK);
296
297    enqueueServerResponses(service.method('Write')!, [
298      [chunk],
299      [completeChunk],
300    ]);
301
302    await manager.write(4, textEncoder.encode('hello'));
303    expect(sentChunks).toHaveLength(2);
304    expect(receivedData()).toEqual(textEncoder.encode('hello'));
305  });
306
307  it('write transfer max chunk size', async () => {
308    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
309
310    const chunk = new Chunk();
311    chunk.setTransferId(4);
312    chunk.setOffset(0);
313    chunk.setPendingBytes(32);
314    chunk.setMaxChunkSizeBytes(8);
315
316    const completeChunk = new Chunk();
317    completeChunk.setTransferId(4);
318    completeChunk.setStatus(Status.OK);
319
320    enqueueServerResponses(service.method('Write')!, [
321      [chunk],
322      [completeChunk],
323    ]);
324
325    await manager.write(4, textEncoder.encode('hello world'));
326    expect(sentChunks).toHaveLength(3);
327    expect(receivedData()).toEqual(textEncoder.encode('hello world'));
328    expect(sentChunks[1].getData()).toEqual(textEncoder.encode('hello wo'));
329    expect(sentChunks[2].getData()).toEqual(textEncoder.encode('rld'));
330  });
331
332  it('write transfer multiple parameters', async () => {
333    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
334
335    const chunk = new Chunk();
336    chunk.setTransferId(4);
337    chunk.setOffset(0);
338    chunk.setPendingBytes(8);
339    chunk.setMaxChunkSizeBytes(8);
340
341    const chunk2 = new Chunk();
342    chunk2.setTransferId(4);
343    chunk2.setOffset(8);
344    chunk2.setPendingBytes(8);
345    chunk2.setMaxChunkSizeBytes(8);
346
347    const completeChunk = new Chunk();
348    completeChunk.setTransferId(4);
349    completeChunk.setStatus(Status.OK);
350
351    enqueueServerResponses(service.method('Write')!, [
352      [chunk],
353      [chunk2],
354      [completeChunk],
355    ]);
356
357    await manager.write(4, textEncoder.encode('data to write'));
358    expect(sentChunks).toHaveLength(3);
359    expect(receivedData()).toEqual(textEncoder.encode('data to write'));
360    expect(sentChunks[1].getData()).toEqual(textEncoder.encode('data to '));
361    expect(sentChunks[2].getData()).toEqual(textEncoder.encode('write'));
362  });
363
364  it('write transfer parameters update', async () => {
365    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
366
367    const chunk = new Chunk();
368    chunk.setTransferId(4);
369    chunk.setOffset(0);
370    chunk.setPendingBytes(8);
371    chunk.setMaxChunkSizeBytes(4);
372    chunk.setType(Chunk.Type.PARAMETERS_RETRANSMIT);
373    chunk.setWindowEndOffset(8);
374
375    const chunk2 = new Chunk();
376    chunk2.setTransferId(4);
377    chunk2.setOffset(4);
378    chunk2.setPendingBytes(8);
379    chunk2.setType(Chunk.Type.PARAMETERS_CONTINUE);
380    chunk2.setWindowEndOffset(12);
381
382    const chunk3 = new Chunk();
383    chunk3.setTransferId(4);
384    chunk3.setOffset(8);
385    chunk3.setPendingBytes(8);
386    chunk3.setType(Chunk.Type.PARAMETERS_CONTINUE);
387    chunk3.setWindowEndOffset(16);
388
389    const chunk4 = new Chunk();
390    chunk4.setTransferId(4);
391    chunk4.setOffset(12);
392    chunk4.setPendingBytes(8);
393    chunk4.setType(Chunk.Type.PARAMETERS_CONTINUE);
394    chunk4.setWindowEndOffset(20);
395
396    const chunk5 = new Chunk();
397    chunk5.setTransferId(4);
398    chunk5.setOffset(16);
399    chunk5.setPendingBytes(8);
400    chunk5.setType(Chunk.Type.PARAMETERS_CONTINUE);
401    chunk5.setWindowEndOffset(24);
402
403    const chunk6 = new Chunk();
404    chunk6.setTransferId(4);
405    chunk6.setOffset(20);
406    chunk6.setPendingBytes(8);
407    chunk6.setType(Chunk.Type.PARAMETERS_CONTINUE);
408    chunk6.setWindowEndOffset(28);
409
410    const completeChunk = new Chunk();
411    completeChunk.setTransferId(4);
412    completeChunk.setStatus(Status.OK);
413
414    enqueueServerResponses(service.method('Write')!, [
415      [chunk],
416      [chunk2],
417      [chunk3],
418      [chunk4],
419      [chunk5],
420      [chunk6],
421      [completeChunk],
422    ]);
423
424    await manager.write(4, textEncoder.encode('hello this is a message'));
425    expect(receivedData()).toEqual(
426      textEncoder.encode('hello this is a message'),
427    );
428    expect(sentChunks[1].getData()).toEqual(textEncoder.encode('hell'));
429    expect(sentChunks[2].getData()).toEqual(textEncoder.encode('o th'));
430    expect(sentChunks[3].getData()).toEqual(textEncoder.encode('is i'));
431    expect(sentChunks[4].getData()).toEqual(textEncoder.encode('s a '));
432    expect(sentChunks[5].getData()).toEqual(textEncoder.encode('mess'));
433    expect(sentChunks[6].getData()).toEqual(textEncoder.encode('age'));
434  });
435
436  it('write transfer progress callback', async () => {
437    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
438
439    const chunk = new Chunk();
440    chunk.setTransferId(4);
441    chunk.setOffset(0);
442    chunk.setPendingBytes(8);
443    chunk.setMaxChunkSizeBytes(8);
444
445    const chunk2 = new Chunk();
446    chunk2.setTransferId(4);
447    chunk2.setOffset(8);
448    chunk2.setPendingBytes(8);
449    chunk2.setMaxChunkSizeBytes(8);
450
451    const completeChunk = new Chunk();
452    completeChunk.setTransferId(4);
453    completeChunk.setStatus(Status.OK);
454
455    enqueueServerResponses(service.method('Write')!, [
456      [chunk],
457      [chunk2],
458      [completeChunk],
459    ]);
460
461    const progress: Array<ProgressStats> = [];
462    await manager.write(
463      4,
464      textEncoder.encode('data to write'),
465      (stats: ProgressStats) => {
466        progress.push(stats);
467      },
468    );
469    expect(sentChunks).toHaveLength(3);
470    expect(receivedData()).toEqual(textEncoder.encode('data to write'));
471    expect(sentChunks[1].getData()).toEqual(textEncoder.encode('data to '));
472    expect(sentChunks[2].getData()).toEqual(textEncoder.encode('write'));
473
474    console.log(progress);
475    expect(progress).toEqual([
476      new ProgressStats(8, 0, 13),
477      new ProgressStats(13, 8, 13),
478      new ProgressStats(13, 13, 13),
479    ]);
480  });
481
482  it('write transfer rewind', async () => {
483    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
484
485    const chunk1 = new Chunk();
486    chunk1.setTransferId(4);
487    chunk1.setOffset(0);
488    chunk1.setPendingBytes(8);
489    chunk1.setMaxChunkSizeBytes(8);
490
491    const chunk2 = new Chunk();
492    chunk2.setTransferId(4);
493    chunk2.setOffset(8);
494    chunk2.setPendingBytes(8);
495    chunk2.setMaxChunkSizeBytes(8);
496
497    const chunk3 = new Chunk();
498    chunk3.setTransferId(4);
499    chunk3.setOffset(4); // Rewind
500    chunk3.setPendingBytes(8);
501    chunk3.setMaxChunkSizeBytes(8);
502
503    const chunk4 = new Chunk();
504    chunk4.setTransferId(4);
505    chunk4.setOffset(12); // Rewind
506    chunk4.setPendingBytes(16);
507    chunk4.setMaxChunkSizeBytes(16);
508
509    const completeChunk = new Chunk();
510    completeChunk.setTransferId(4);
511    completeChunk.setStatus(Status.OK);
512
513    enqueueServerResponses(service.method('Write')!, [
514      [chunk1],
515      [chunk2],
516      [chunk3],
517      [chunk4],
518      [completeChunk],
519    ]);
520
521    await manager.write(4, textEncoder.encode('pigweed data transfer'));
522    expect(sentChunks).toHaveLength(5);
523    expect(sentChunks[1].getData()).toEqual(textEncoder.encode('pigweed '));
524    expect(sentChunks[2].getData()).toEqual(textEncoder.encode('data tra'));
525    expect(sentChunks[3].getData()).toEqual(textEncoder.encode('eed data'));
526    expect(sentChunks[4].getData()).toEqual(textEncoder.encode(' transfer'));
527  });
528
529  it('write transfer bad offset', async () => {
530    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
531
532    const chunk1 = new Chunk();
533    chunk1.setTransferId(4);
534    chunk1.setOffset(0);
535    chunk1.setPendingBytes(8);
536    chunk1.setMaxChunkSizeBytes(8);
537
538    const chunk2 = new Chunk();
539    chunk2.setTransferId(4);
540    chunk2.setOffset(100); // larger offset than data
541    chunk2.setPendingBytes(8);
542    chunk2.setMaxChunkSizeBytes(8);
543
544    const completeChunk = new Chunk();
545    completeChunk.setTransferId(4);
546    completeChunk.setStatus(Status.OK);
547
548    enqueueServerResponses(service.method('Write')!, [
549      [chunk1],
550      [chunk2],
551      [completeChunk],
552    ]);
553
554    await manager
555      .write(4, textEncoder.encode('small data'))
556      .then(() => {
557        fail('Unexpected succesful promise');
558      })
559      .catch((error) => {
560        expect(error.id).toEqual(4);
561        expect(Status[error.status]).toEqual(Status[Status.OUT_OF_RANGE]);
562      });
563  });
564
565  it('write transfer error', async () => {
566    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
567
568    const chunk = new Chunk();
569    chunk.setTransferId(21);
570    chunk.setStatus(Status.UNAVAILABLE);
571
572    enqueueServerResponses(service.method('Write')!, [[chunk]]);
573
574    await manager
575      .write(21, textEncoder.encode('no write'))
576      .then(() => {
577        fail('Unexpected succesful promise');
578      })
579      .catch((error) => {
580        expect(error.id).toEqual(21);
581        expect(Status[error.status]).toEqual(Status[Status.UNAVAILABLE]);
582      });
583  });
584
585  it('write transfer server error', async () => {
586    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
587
588    const chunk = new Chunk();
589    chunk.setTransferId(21);
590    chunk.setStatus(Status.NOT_FOUND);
591
592    enqueueServerError(service.method('Write')!, Status.NOT_FOUND);
593
594    await manager
595      .write(21, textEncoder.encode('server error'))
596      .then(() => {
597        fail('Unexpected succesful promise');
598      })
599      .catch((error) => {
600        expect(error.id).toEqual(21);
601        expect(Status[error.status]).toEqual(Status[Status.INTERNAL]);
602      });
603  });
604
605  it('write transfer timeout after initial chunk', async () => {
606    const manager = new Manager(service, 0.001, 4, 2);
607
608    await manager
609      .write(22, textEncoder.encode('no server response!'))
610      .then(() => {
611        fail('unexpected succesful write');
612      })
613      .catch((error) => {
614        expect(sentChunks).toHaveLength(3); // Initial chunk + two retries.
615        expect(error.id).toEqual(22);
616        expect(Status[error.status]).toEqual(Status[Status.DEADLINE_EXCEEDED]);
617      });
618  });
619
620  it('write transfer timeout after intermediate chunk', async () => {
621    const manager = new Manager(service, DEFAULT_TIMEOUT_S, 4, 2);
622
623    const chunk = new Chunk();
624    chunk.setTransferId(22);
625    chunk.setPendingBytes(10);
626    chunk.setMaxChunkSizeBytes(5);
627
628    enqueueServerResponses(service.method('Write')!, [[chunk]]);
629
630    await manager
631      .write(22, textEncoder.encode('0123456789'))
632      .then(() => {
633        fail('unexpected succesful write');
634      })
635      .catch((error) => {
636        const expectedChunk1 = new Chunk();
637        expectedChunk1.setTransferId(22);
638        expectedChunk1.setResourceId(22);
639        expectedChunk1.setType(Chunk.Type.START);
640        const expectedChunk2 = new Chunk();
641        expectedChunk2.setTransferId(22);
642        expectedChunk2.setData(textEncoder.encode('01234'));
643        expectedChunk2.setType(Chunk.Type.DATA);
644        const lastChunk = new Chunk();
645        lastChunk.setTransferId(22);
646        lastChunk.setData(textEncoder.encode('56789'));
647        lastChunk.setOffset(5);
648        lastChunk.setRemainingBytes(0);
649        lastChunk.setType(Chunk.Type.DATA);
650
651        const expectedChunks = [
652          expectedChunk1,
653          expectedChunk2,
654          lastChunk,
655          lastChunk, // retry 1
656          lastChunk, // retry 2
657        ];
658
659        expect(sentChunks).toEqual(expectedChunks);
660
661        expect(error.id).toEqual(22);
662        expect(Status[error.status]).toEqual(Status[Status.DEADLINE_EXCEEDED]);
663      });
664  });
665
666  it('write zero pending bytes is internal error', async () => {
667    const manager = new Manager(service, DEFAULT_TIMEOUT_S);
668
669    const chunk = new Chunk();
670    chunk.setTransferId(23);
671    chunk.setPendingBytes(0);
672
673    enqueueServerResponses(service.method('Write')!, [[chunk]]);
674
675    await manager
676      .write(23, textEncoder.encode('no write'))
677      .then(() => {
678        fail('Unexpected succesful promise');
679      })
680      .catch((error) => {
681        expect(error.id).toEqual(23);
682        expect(Status[error.status]).toEqual(Status[Status.INTERNAL]);
683      });
684  });
685});
686