Skip to content
File

Blob: src/workerd/api/tests/decompression-stream-unhandled-rejection-test.js

javascript53 lines
1// Copyright (c) 2026 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4// Regression test for https://github.com/cloudflare/workerd/issues/6061
5// DecompressionStream generates unhandled rejections internally when
6// streams are consumed via Array.fromAsync, even when the caller
7// properly handles errors.
8 
9import { strictEqual } from 'node:assert';
10import { mock } from 'node:test';
11import { setTimeout } from 'node:timers/promises';
12 
13export const decompessionStreamUnhandledRejection = {
14 async test() {
15 const rejectionFn = mock.fn();
16 const unhandledRejectionFn = mock.fn();
17 globalThis.addEventListener('rejectionhandled', rejectionFn);
18 globalThis.addEventListener('unhandledrejection', unhandledRejectionFn);
19 
20 const valid = new Uint8Array([120, 156, 75, 4, 0, 0, 98, 0, 98]); // deflate('a')
21 const empty = new Uint8Array(1);
22 const invalid = new Uint8Array([...valid, ...empty]);
23 const double = new Uint8Array([...valid, ...valid]);
24 for (const chunks of [
25 [valid],
26 [invalid],
27 [valid, empty],
28 [valid, valid],
29 [double],
30 ]) {
31 try {
32 const stream = new Blob(chunks)
33 .stream()
34 .pipeThrough(new DecompressionStream('deflate'));
35 await Array.fromAsync(stream);
36 } catch (error) {
37 strictEqual(
38 error.message,
39 'Trailing bytes after end of compressed data'
40 );
41 }
42 }
43 
44 await setTimeout(200);
45 globalThis.removeEventListener('rejectionhandled', rejectionFn);
46 globalThis.removeEventListener('unhandledrejection', unhandledRejectionFn);
47 strictEqual(
48 rejectionFn.mock.callCount(),
49 unhandledRejectionFn.mock.callCount()
50 );
51 },
52};