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