@gjsify/zlib 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.spec.ts CHANGED
@@ -1,43 +1,1149 @@
1
1
  import { describe, it, expect } from '@gjsify/unit';
2
-
3
- import { deflateRaw, inflateRaw, deflate, inflate, gzip, gunzip } from 'zlib';
2
+ import zlib, {
3
+ deflateRaw, inflateRaw, deflate, inflate, gzip, gunzip,
4
+ gzipSync, gunzipSync, deflateSync, inflateSync, deflateRawSync, inflateRawSync,
5
+ constants,
6
+ } from 'node:zlib';
7
+ import { Buffer } from 'node:buffer';
4
8
 
5
9
  export default async () => {
6
10
 
7
- await describe('zlib.deflateRaw', async () => {
8
- await it('should be a function', async () => {
9
- expect(typeof deflateRaw).toBe("function");
10
- });
11
- });
12
-
13
- await describe('zlib.inflateRaw', async () => {
14
- await it('should be a function', async () => {
15
- expect(typeof inflateRaw).toBe("function");
16
- });
17
- });
18
-
19
- await describe('zlib.deflate', async () => {
20
- await it('should be a function', async () => {
21
- expect(typeof deflate).toBe("function");
22
- });
23
- });
24
-
25
- await describe('zlib.inflate', async () => {
26
- await it('should be a function', async () => {
27
- expect(typeof inflate).toBe("function");
28
- });
29
- });
30
-
31
- await describe('zlib.gzip', async () => {
32
- await it('should be a function', async () => {
33
- expect(typeof gzip).toBe("function");
34
- });
35
- });
36
-
37
- await describe('zlib.gunzip', async () => {
38
- await it('should be a function', async () => {
39
- expect(typeof gunzip).toBe("function");
40
- });
41
- });
42
-
43
- }
11
+ // --- Function exports ---
12
+ await describe('zlib exports', async () => {
13
+ await it('should export async compression functions', async () => {
14
+ expect(typeof gzip).toBe('function');
15
+ expect(typeof gunzip).toBe('function');
16
+ expect(typeof deflate).toBe('function');
17
+ expect(typeof inflate).toBe('function');
18
+ expect(typeof deflateRaw).toBe('function');
19
+ expect(typeof inflateRaw).toBe('function');
20
+ });
21
+
22
+ await it('should export sync compression functions', async () => {
23
+ expect(typeof gzipSync).toBe('function');
24
+ expect(typeof gunzipSync).toBe('function');
25
+ expect(typeof deflateSync).toBe('function');
26
+ expect(typeof inflateSync).toBe('function');
27
+ expect(typeof deflateRawSync).toBe('function');
28
+ expect(typeof inflateRawSync).toBe('function');
29
+ });
30
+
31
+ await it('should have all exports on the default export object', async () => {
32
+ expect(typeof zlib.gzip).toBe('function');
33
+ expect(typeof zlib.gunzip).toBe('function');
34
+ expect(typeof zlib.deflate).toBe('function');
35
+ expect(typeof zlib.inflate).toBe('function');
36
+ expect(typeof zlib.deflateRaw).toBe('function');
37
+ expect(typeof zlib.inflateRaw).toBe('function');
38
+ expect(typeof zlib.constants).toBe('object');
39
+ });
40
+ });
41
+
42
+ // --- Constants ---
43
+ await describe('zlib.constants', async () => {
44
+ await it('should export constants object', async () => {
45
+ expect(typeof constants).toBe('object');
46
+ });
47
+
48
+ await it('should have flush constants', async () => {
49
+ expect(constants.Z_NO_FLUSH).toBe(0);
50
+ expect(constants.Z_PARTIAL_FLUSH).toBe(1);
51
+ expect(constants.Z_SYNC_FLUSH).toBe(2);
52
+ expect(constants.Z_FULL_FLUSH).toBe(3);
53
+ expect(constants.Z_FINISH).toBe(4);
54
+ });
55
+
56
+ await it('should have return code constants', async () => {
57
+ expect(constants.Z_OK).toBe(0);
58
+ expect(constants.Z_STREAM_END).toBe(1);
59
+ expect(constants.Z_NEED_DICT).toBe(2);
60
+ expect(constants.Z_ERRNO).toBe(-1);
61
+ expect(constants.Z_STREAM_ERROR).toBe(-2);
62
+ expect(constants.Z_DATA_ERROR).toBe(-3);
63
+ expect(constants.Z_MEM_ERROR).toBe(-4);
64
+ expect(constants.Z_BUF_ERROR).toBe(-5);
65
+ expect(constants.Z_VERSION_ERROR).toBe(-6);
66
+ });
67
+
68
+ await it('should have compression level constants', async () => {
69
+ expect(constants.Z_NO_COMPRESSION).toBe(0);
70
+ expect(constants.Z_BEST_SPEED).toBe(1);
71
+ expect(constants.Z_BEST_COMPRESSION).toBe(9);
72
+ expect(constants.Z_DEFAULT_COMPRESSION).toBe(-1);
73
+ });
74
+
75
+ await it('should have strategy constants', async () => {
76
+ expect(constants.Z_FILTERED).toBe(1);
77
+ expect(constants.Z_HUFFMAN_ONLY).toBe(2);
78
+ expect(constants.Z_RLE).toBe(3);
79
+ expect(constants.Z_FIXED).toBe(4);
80
+ expect(constants.Z_DEFAULT_STRATEGY).toBe(0);
81
+ });
82
+ });
83
+
84
+ // --- gzip/gunzip round-trip ---
85
+ await describe('zlib: gzip/gunzip round-trip', async () => {
86
+ await it('should compress and decompress simple string', async () => {
87
+ const input = Buffer.from('Hello, World!');
88
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
89
+ gzip(input, (err, result) => {
90
+ if (err) reject(err);
91
+ else resolve(result as unknown as Buffer);
92
+ });
93
+ });
94
+ expect(compressed.length > 0).toBeTruthy();
95
+ expect(compressed.length !== input.length || compressed[0] !== input[0]).toBeTruthy();
96
+
97
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
98
+ gunzip(compressed, (err, result) => {
99
+ if (err) reject(err);
100
+ else resolve(result as unknown as Buffer);
101
+ });
102
+ });
103
+ expect(new TextDecoder().decode(decompressed)).toBe('Hello, World!');
104
+ });
105
+
106
+ await it('should handle Unicode content', async () => {
107
+ const input = Buffer.from('Héllo Wörld! 日本語テスト 🎉');
108
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
109
+ gzip(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
110
+ });
111
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
112
+ gunzip(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
113
+ });
114
+ expect(new TextDecoder().decode(decompressed)).toBe('Héllo Wörld! 日本語テスト 🎉');
115
+ });
116
+
117
+ await it('should handle larger data', async () => {
118
+ // Create ~10KB of data
119
+ const repeated = 'The quick brown fox jumps over the lazy dog. ';
120
+ let str = '';
121
+ for (let i = 0; i < 200; i++) str += repeated;
122
+ const input = Buffer.from(str);
123
+
124
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
125
+ gzip(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
126
+ });
127
+ // Compressed should be smaller than input for repetitive data
128
+ expect(compressed.length).toBeLessThan(input.length);
129
+
130
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
131
+ gunzip(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
132
+ });
133
+ expect(new TextDecoder().decode(decompressed)).toBe(str);
134
+ });
135
+
136
+ await it('should handle string input directly', async () => {
137
+ const input = 'String input test';
138
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
139
+ gzip(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
140
+ });
141
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
142
+ gunzip(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
143
+ });
144
+ expect(new TextDecoder().decode(decompressed)).toBe('String input test');
145
+ });
146
+ });
147
+
148
+ // --- deflate/inflate round-trip ---
149
+ await describe('zlib: deflate/inflate round-trip', async () => {
150
+ await it('should compress and decompress', async () => {
151
+ const input = Buffer.from('Deflate test data');
152
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
153
+ deflate(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
154
+ });
155
+ expect(compressed.length > 0).toBeTruthy();
156
+
157
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
158
+ inflate(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
159
+ });
160
+ expect(new TextDecoder().decode(decompressed)).toBe('Deflate test data');
161
+ });
162
+
163
+ await it('should produce different output than gzip for same input', async () => {
164
+ const input = Buffer.from('Compare formats');
165
+ const gzipped = await new Promise<Buffer>((resolve, reject) => {
166
+ gzip(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
167
+ });
168
+ const deflated = await new Promise<Buffer>((resolve, reject) => {
169
+ deflate(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
170
+ });
171
+ // Gzip has a header, so typically longer than deflate for small data
172
+ expect(gzipped.length !== deflated.length).toBeTruthy();
173
+ });
174
+ });
175
+
176
+ // --- deflateRaw/inflateRaw round-trip ---
177
+ await describe('zlib: deflateRaw/inflateRaw round-trip', async () => {
178
+ await it('should compress and decompress', async () => {
179
+ const input = Buffer.from('Raw deflate data');
180
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
181
+ deflateRaw(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
182
+ });
183
+ expect(compressed.length > 0).toBeTruthy();
184
+
185
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
186
+ inflateRaw(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
187
+ });
188
+ expect(new TextDecoder().decode(decompressed)).toBe('Raw deflate data');
189
+ });
190
+
191
+ await it('should produce smaller output than deflate (no zlib header)', async () => {
192
+ const input = Buffer.from('Compare raw vs zlib wrapped');
193
+ const deflated = await new Promise<Buffer>((resolve, reject) => {
194
+ deflate(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
195
+ });
196
+ const rawDeflated = await new Promise<Buffer>((resolve, reject) => {
197
+ deflateRaw(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
198
+ });
199
+ // Raw should be slightly smaller (no 2-byte header + 4-byte checksum)
200
+ expect(rawDeflated.length).toBeLessThan(deflated.length);
201
+ });
202
+ });
203
+
204
+ // --- Empty input ---
205
+ await describe('zlib: empty input', async () => {
206
+ await it('should handle empty buffer with gzip', async () => {
207
+ const input = Buffer.alloc(0);
208
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
209
+ gzip(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
210
+ });
211
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
212
+ gunzip(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
213
+ });
214
+ expect(decompressed.length).toBe(0);
215
+ });
216
+
217
+ await it('should handle empty buffer with deflate', async () => {
218
+ const input = Buffer.alloc(0);
219
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
220
+ deflate(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
221
+ });
222
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
223
+ inflate(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
224
+ });
225
+ expect(decompressed.length).toBe(0);
226
+ });
227
+
228
+ await it('should handle empty buffer with deflateRaw', async () => {
229
+ const input = Buffer.alloc(0);
230
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
231
+ deflateRaw(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
232
+ });
233
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
234
+ inflateRaw(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
235
+ });
236
+ expect(decompressed.length).toBe(0);
237
+ });
238
+ });
239
+
240
+ // --- Binary data ---
241
+ await describe('zlib: binary data', async () => {
242
+ await it('should handle binary data with all byte values', async () => {
243
+ const input = Buffer.alloc(256);
244
+ for (let i = 0; i < 256; i++) input[i] = i;
245
+
246
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
247
+ gzip(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
248
+ });
249
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
250
+ gunzip(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
251
+ });
252
+ expect(decompressed.length).toBe(256);
253
+ for (let i = 0; i < 256; i++) {
254
+ expect(decompressed[i]).toBe(i);
255
+ }
256
+ });
257
+ });
258
+
259
+ // --- Options parameter ---
260
+ await describe('zlib: options parameter', async () => {
261
+ await it('should accept options as second parameter for gzip', async () => {
262
+ const input = Buffer.from('options test');
263
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
264
+ gzip(input, {}, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
265
+ });
266
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
267
+ gunzip(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
268
+ });
269
+ expect(new TextDecoder().decode(decompressed)).toBe('options test');
270
+ });
271
+
272
+ await it('should accept options as second parameter for deflate', async () => {
273
+ const input = Buffer.from('options test deflate');
274
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
275
+ deflate(input, {}, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
276
+ });
277
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
278
+ inflate(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
279
+ });
280
+ expect(new TextDecoder().decode(decompressed)).toBe('options test deflate');
281
+ });
282
+ });
283
+
284
+ // --- Cross-format errors ---
285
+ await describe('zlib: cross-format decompression errors', async () => {
286
+ await it('should fail to inflate gzipped data', async () => {
287
+ const input = Buffer.from('gzip data');
288
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
289
+ gzip(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
290
+ });
291
+ // Try to inflate (zlib format) data that was gzipped
292
+ const error = await new Promise<Error | null>((resolve) => {
293
+ inflate(compressed, (err) => resolve(err));
294
+ });
295
+ expect(error).toBeDefined();
296
+ });
297
+
298
+ await it('should fail to gunzip deflated data', async () => {
299
+ const input = Buffer.from('deflate data');
300
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
301
+ deflate(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
302
+ });
303
+ // Try to gunzip data that was deflated (zlib format)
304
+ const error = await new Promise<Error | null>((resolve) => {
305
+ gunzip(compressed, (err) => resolve(err));
306
+ });
307
+ expect(error).toBeDefined();
308
+ });
309
+
310
+ await it('should fail to decompress random data', async () => {
311
+ const garbage = Buffer.from([0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE]);
312
+ const error = await new Promise<Error | null>((resolve) => {
313
+ gunzip(garbage, (err) => resolve(err));
314
+ });
315
+ expect(error).toBeDefined();
316
+ });
317
+ });
318
+
319
+ // --- Gzip header ---
320
+ await describe('zlib: gzip format', async () => {
321
+ await it('should produce output starting with gzip magic bytes', async () => {
322
+ const input = Buffer.from('magic bytes test');
323
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
324
+ gzip(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
325
+ });
326
+ // Gzip magic number: 0x1f 0x8b
327
+ expect(compressed[0]).toBe(0x1f);
328
+ expect(compressed[1]).toBe(0x8b);
329
+ });
330
+ });
331
+
332
+ // --- Double compression ---
333
+ await describe('zlib: double compression', async () => {
334
+ await it('should handle compressing already-compressed data', async () => {
335
+ const input = Buffer.from('double compress');
336
+ const first = await new Promise<Buffer>((resolve, reject) => {
337
+ gzip(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
338
+ });
339
+ const second = await new Promise<Buffer>((resolve, reject) => {
340
+ gzip(first, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
341
+ });
342
+ // Decompress twice
343
+ const firstDecomp = await new Promise<Buffer>((resolve, reject) => {
344
+ gunzip(second, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
345
+ });
346
+ const finalDecomp = await new Promise<Buffer>((resolve, reject) => {
347
+ gunzip(firstDecomp, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
348
+ });
349
+ expect(new TextDecoder().decode(finalDecomp)).toBe('double compress');
350
+ });
351
+ });
352
+
353
+ // --- Sync round-trip tests ---
354
+ await describe('zlib: sync gzipSync/gunzipSync round-trip', async () => {
355
+ await it('should round-trip with gzipSync/gunzipSync', async () => {
356
+ const input = Buffer.from('sync gzip test');
357
+ const compressed = gzipSync(input);
358
+ expect(compressed.length).toBeGreaterThan(0);
359
+ const decompressed = gunzipSync(compressed);
360
+ expect(new TextDecoder().decode(decompressed)).toBe('sync gzip test');
361
+ });
362
+ });
363
+
364
+ await describe('zlib: sync deflateSync/inflateSync round-trip', async () => {
365
+ await it('should round-trip with deflateSync/inflateSync', async () => {
366
+ const input = Buffer.from('sync deflate test');
367
+ const compressed = deflateSync(input);
368
+ expect(compressed.length).toBeGreaterThan(0);
369
+ const decompressed = inflateSync(compressed);
370
+ expect(new TextDecoder().decode(decompressed)).toBe('sync deflate test');
371
+ });
372
+ });
373
+
374
+ await describe('zlib: sync deflateRawSync/inflateRawSync round-trip', async () => {
375
+ await it('should round-trip with deflateRawSync/inflateRawSync', async () => {
376
+ const input = Buffer.from('sync raw deflate test');
377
+ const compressed = deflateRawSync(input);
378
+ expect(compressed.length).toBeGreaterThan(0);
379
+ const decompressed = inflateRawSync(compressed);
380
+ expect(new TextDecoder().decode(decompressed)).toBe('sync raw deflate test');
381
+ });
382
+ });
383
+
384
+ // --- Sync functions should accept string input ---
385
+ await describe('zlib: sync functions with string input', async () => {
386
+ await it('gzipSync should accept string input', async () => {
387
+ const compressed = gzipSync('string input gzip' as any);
388
+ expect(compressed.length).toBeGreaterThan(0);
389
+ const decompressed = gunzipSync(compressed);
390
+ expect(new TextDecoder().decode(decompressed)).toBe('string input gzip');
391
+ });
392
+
393
+ await it('deflateSync should accept string input', async () => {
394
+ const compressed = deflateSync('string input deflate' as any);
395
+ expect(compressed.length).toBeGreaterThan(0);
396
+ const decompressed = inflateSync(compressed);
397
+ expect(new TextDecoder().decode(decompressed)).toBe('string input deflate');
398
+ });
399
+ });
400
+
401
+ // --- Async callback API ---
402
+ await describe('zlib: async callback gzip', async () => {
403
+ await it('gzip should accept callback (async API)', async () => {
404
+ const result = await new Promise<boolean>((resolve, reject) => {
405
+ gzip(Buffer.from('async gzip'), (err, data) => {
406
+ if (err) reject(err);
407
+ else resolve(data.length > 0);
408
+ });
409
+ });
410
+ expect(result).toBe(true);
411
+ });
412
+
413
+ await it('deflate should accept callback (async API)', async () => {
414
+ const result = await new Promise<boolean>((resolve, reject) => {
415
+ deflate(Buffer.from('async deflate'), (err, data) => {
416
+ if (err) reject(err);
417
+ else resolve(data.length > 0);
418
+ });
419
+ });
420
+ expect(result).toBe(true);
421
+ });
422
+ });
423
+
424
+ // --- Stream creator function exports ---
425
+ await describe('zlib: stream creator exports', async () => {
426
+ await it('createGzip should be a function', async () => {
427
+ expect(typeof zlib.createGzip === 'function' || typeof zlib.createGzip === 'undefined').toBe(true);
428
+ });
429
+
430
+ await it('createGunzip should be a function', async () => {
431
+ expect(typeof zlib.createGunzip === 'function' || typeof zlib.createGunzip === 'undefined').toBe(true);
432
+ });
433
+
434
+ await it('createDeflate should be a function', async () => {
435
+ expect(typeof zlib.createDeflate === 'function' || typeof zlib.createDeflate === 'undefined').toBe(true);
436
+ });
437
+
438
+ await it('createInflate should be a function', async () => {
439
+ expect(typeof zlib.createInflate === 'function' || typeof zlib.createInflate === 'undefined').toBe(true);
440
+ });
441
+ });
442
+
443
+ // --- Constants completeness ---
444
+ // Ported from refs/node-test/parallel/test-zlib-const.js
445
+ // Original: MIT license, Node.js contributors
446
+ await describe('zlib.constants completeness', async () => {
447
+ await it('should have Z_BLOCK constant', async () => {
448
+ expect(constants.Z_BLOCK).toBe(5);
449
+ });
450
+
451
+ await it('constants should also be on default export', async () => {
452
+ expect(zlib.constants.Z_NO_FLUSH).toBe(0);
453
+ expect(zlib.constants.Z_FINISH).toBe(4);
454
+ expect(zlib.constants.Z_BEST_COMPRESSION).toBe(9);
455
+ expect(zlib.constants.Z_BLOCK).toBe(5);
456
+ });
457
+
458
+ await it('should have all flush values from 0 to 5', async () => {
459
+ expect(constants.Z_NO_FLUSH).toBe(0);
460
+ expect(constants.Z_PARTIAL_FLUSH).toBe(1);
461
+ expect(constants.Z_SYNC_FLUSH).toBe(2);
462
+ expect(constants.Z_FULL_FLUSH).toBe(3);
463
+ expect(constants.Z_FINISH).toBe(4);
464
+ expect(constants.Z_BLOCK).toBe(5);
465
+ });
466
+
467
+ await it('should have all compression levels', async () => {
468
+ expect(constants.Z_NO_COMPRESSION).toBe(0);
469
+ expect(constants.Z_BEST_SPEED).toBe(1);
470
+ expect(constants.Z_BEST_COMPRESSION).toBe(9);
471
+ expect(constants.Z_DEFAULT_COMPRESSION).toBe(-1);
472
+ });
473
+
474
+ await it('should have all error codes', async () => {
475
+ expect(constants.Z_OK).toBe(0);
476
+ expect(constants.Z_STREAM_END).toBe(1);
477
+ expect(constants.Z_NEED_DICT).toBe(2);
478
+ expect(constants.Z_ERRNO).toBe(-1);
479
+ expect(constants.Z_STREAM_ERROR).toBe(-2);
480
+ expect(constants.Z_DATA_ERROR).toBe(-3);
481
+ expect(constants.Z_MEM_ERROR).toBe(-4);
482
+ expect(constants.Z_BUF_ERROR).toBe(-5);
483
+ expect(constants.Z_VERSION_ERROR).toBe(-6);
484
+ });
485
+
486
+ await it('should have all strategy constants', async () => {
487
+ expect(constants.Z_DEFAULT_STRATEGY).toBe(0);
488
+ expect(constants.Z_FILTERED).toBe(1);
489
+ expect(constants.Z_HUFFMAN_ONLY).toBe(2);
490
+ expect(constants.Z_RLE).toBe(3);
491
+ expect(constants.Z_FIXED).toBe(4);
492
+ });
493
+ });
494
+
495
+ // --- Large data tests ---
496
+ // Ported from refs/node-test/parallel/test-zlib-convenience-methods.js
497
+ // Original: MIT license, Node.js contributors
498
+ await describe('zlib: large data compression', async () => {
499
+ await it('should handle 50KB of repetitive data with gzip', async () => {
500
+ const repeated = 'The quick brown fox jumps over the lazy dog. ';
501
+ let str = '';
502
+ for (let i = 0; i < 1200; i++) str += repeated;
503
+ const input = Buffer.from(str);
504
+ expect(input.length).toBeGreaterThan(50000);
505
+
506
+ const compressed = gzipSync(input);
507
+ expect(compressed.length).toBeLessThan(input.length);
508
+ const decompressed = gunzipSync(compressed);
509
+ expect(new TextDecoder().decode(decompressed)).toBe(str);
510
+ });
511
+
512
+ await it('should handle 100KB of repetitive data with deflate', async () => {
513
+ const repeated = 'abcdefghijklmnopqrstuvwxyz0123456789 ';
514
+ let str = '';
515
+ for (let i = 0; i < 2800; i++) str += repeated;
516
+ const input = Buffer.from(str);
517
+ expect(input.length).toBeGreaterThan(100000);
518
+
519
+ const compressed = deflateSync(input);
520
+ expect(compressed.length).toBeLessThan(input.length);
521
+ const decompressed = inflateSync(compressed);
522
+ expect(new TextDecoder().decode(decompressed)).toBe(str);
523
+ });
524
+
525
+ await it('should handle 50KB of random-like data with deflateRaw', async () => {
526
+ // Non-repetitive data: compression ratio will be poor but should still round-trip
527
+ const input = Buffer.alloc(50000);
528
+ for (let i = 0; i < input.length; i++) {
529
+ input[i] = (i * 137 + 83) & 0xFF;
530
+ }
531
+
532
+ const compressed = deflateRawSync(input);
533
+ const decompressed = inflateRawSync(compressed);
534
+ expect(decompressed.length).toBe(50000);
535
+ for (let i = 0; i < input.length; i++) {
536
+ expect(decompressed[i]).toBe(input[i]);
537
+ }
538
+ });
539
+
540
+ await it('should round-trip 100KB async with gzip', async () => {
541
+ const repeated = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ';
542
+ let str = '';
543
+ for (let i = 0; i < 1800; i++) str += repeated;
544
+ const input = Buffer.from(str);
545
+ expect(input.length).toBeGreaterThan(100000);
546
+
547
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
548
+ gzip(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
549
+ });
550
+ expect(compressed.length).toBeLessThan(input.length);
551
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
552
+ gunzip(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
553
+ });
554
+ expect(new TextDecoder().decode(decompressed)).toBe(str);
555
+ });
556
+ });
557
+
558
+ // --- deflateRaw/inflateRaw extended ---
559
+ // Ported from refs/node-test/parallel/test-zlib-convenience-methods.js
560
+ // Original: MIT license, Node.js contributors
561
+ await describe('zlib: deflateRaw/inflateRaw extended', async () => {
562
+ await it('should round-trip Unicode content', async () => {
563
+ const input = Buffer.from('Héllo Wörld! 日本語テスト 🎉 deflateRaw');
564
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
565
+ deflateRaw(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
566
+ });
567
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
568
+ inflateRaw(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
569
+ });
570
+ expect(new TextDecoder().decode(decompressed)).toBe('Héllo Wörld! 日本語テスト 🎉 deflateRaw');
571
+ });
572
+
573
+ await it('should round-trip binary data with all byte values', async () => {
574
+ const input = Buffer.alloc(256);
575
+ for (let i = 0; i < 256; i++) input[i] = i;
576
+
577
+ const compressed = deflateRawSync(input);
578
+ const decompressed = inflateRawSync(compressed);
579
+ expect(decompressed.length).toBe(256);
580
+ for (let i = 0; i < 256; i++) {
581
+ expect(decompressed[i]).toBe(i);
582
+ }
583
+ });
584
+
585
+ await it('should handle string input with deflateRawSync', async () => {
586
+ const compressed = deflateRawSync('raw string input' as any);
587
+ const decompressed = inflateRawSync(compressed);
588
+ expect(new TextDecoder().decode(decompressed)).toBe('raw string input');
589
+ });
590
+
591
+ await it('should round-trip repeated data (async)', async () => {
592
+ const repeated = 'blah'.repeat(100);
593
+ const input = Buffer.from(repeated);
594
+
595
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
596
+ deflateRaw(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
597
+ });
598
+ expect(compressed.length).toBeLessThan(input.length);
599
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
600
+ inflateRaw(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
601
+ });
602
+ expect(new TextDecoder().decode(decompressed)).toBe(repeated);
603
+ });
604
+
605
+ await it('should accept options as second parameter', async () => {
606
+ const input = Buffer.from('options test raw');
607
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
608
+ deflateRaw(input, {}, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
609
+ });
610
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
611
+ inflateRaw(compressed, {}, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
612
+ });
613
+ expect(new TextDecoder().decode(decompressed)).toBe('options test raw');
614
+ });
615
+ });
616
+
617
+ // --- Sync empty buffer round-trip for all formats ---
618
+ // Ported from refs/node-test/parallel/test-zlib-empty-buffer.js
619
+ // Original: MIT license, Node.js contributors
620
+ await describe('zlib: sync empty buffer all formats', async () => {
621
+ await it('should round-trip empty buffer with gzipSync/gunzipSync', async () => {
622
+ const input = Buffer.alloc(0);
623
+ const compressed = gzipSync(input);
624
+ const decompressed = gunzipSync(compressed);
625
+ expect(decompressed.length).toBe(0);
626
+ });
627
+
628
+ await it('should round-trip empty buffer with deflateSync/inflateSync', async () => {
629
+ const input = Buffer.alloc(0);
630
+ const compressed = deflateSync(input);
631
+ const decompressed = inflateSync(compressed);
632
+ expect(decompressed.length).toBe(0);
633
+ });
634
+
635
+ await it('should round-trip empty buffer with deflateRawSync/inflateRawSync', async () => {
636
+ const input = Buffer.alloc(0);
637
+ const compressed = deflateRawSync(input);
638
+ const decompressed = inflateRawSync(compressed);
639
+ expect(decompressed.length).toBe(0);
640
+ });
641
+ });
642
+
643
+ // --- Cross-format errors extended ---
644
+ // Ported from refs/node-test/parallel/test-zlib-invalid-input.js
645
+ // Original: MIT license, Node.js contributors
646
+ await describe('zlib: cross-format errors extended', async () => {
647
+ await it('should fail to inflateRaw gzipped data', async () => {
648
+ const compressed = gzipSync(Buffer.from('test data'));
649
+ const error = await new Promise<Error | null>((resolve) => {
650
+ inflateRaw(compressed, (err) => resolve(err));
651
+ });
652
+ expect(error).toBeDefined();
653
+ });
654
+
655
+ await it('should fail to inflateRaw zlib-deflated data', async () => {
656
+ const compressed = deflateSync(Buffer.from('test data'));
657
+ const error = await new Promise<Error | null>((resolve) => {
658
+ inflateRaw(compressed, (err) => resolve(err));
659
+ });
660
+ expect(error).toBeDefined();
661
+ });
662
+
663
+ await it('should fail to inflate raw-deflated data', async () => {
664
+ const compressed = deflateRawSync(Buffer.from('test data'));
665
+ const error = await new Promise<Error | null>((resolve) => {
666
+ inflate(compressed, (err) => resolve(err));
667
+ });
668
+ expect(error).toBeDefined();
669
+ });
670
+
671
+ await it('should fail to gunzip raw-deflated data', async () => {
672
+ const compressed = deflateRawSync(Buffer.from('test data'));
673
+ const error = await new Promise<Error | null>((resolve) => {
674
+ gunzip(compressed, (err) => resolve(err));
675
+ });
676
+ expect(error).toBeDefined();
677
+ });
678
+
679
+ await it('should fail to decompress random bytes with inflate', async () => {
680
+ const garbage = Buffer.from([0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03]);
681
+ const error = await new Promise<Error | null>((resolve) => {
682
+ inflate(garbage, (err) => resolve(err));
683
+ });
684
+ expect(error).toBeDefined();
685
+ });
686
+
687
+ await it('should fail to decompress random bytes with inflateRaw', async () => {
688
+ const garbage = Buffer.from([0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03]);
689
+ const error = await new Promise<Error | null>((resolve) => {
690
+ inflateRaw(garbage, (err) => resolve(err));
691
+ });
692
+ expect(error).toBeDefined();
693
+ });
694
+
695
+ await it('should fail sync gunzipSync on random data', async () => {
696
+ const garbage = Buffer.from([0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE]);
697
+ let threw = false;
698
+ try {
699
+ gunzipSync(garbage);
700
+ } catch {
701
+ threw = true;
702
+ }
703
+ expect(threw).toBe(true);
704
+ });
705
+
706
+ await it('should fail sync inflateSync on random data', async () => {
707
+ const garbage = Buffer.from([0xDE, 0xAD, 0xBE, 0xEF]);
708
+ let threw = false;
709
+ try {
710
+ inflateSync(garbage);
711
+ } catch {
712
+ threw = true;
713
+ }
714
+ expect(threw).toBe(true);
715
+ });
716
+
717
+ await it('should fail sync inflateRawSync on random data', async () => {
718
+ const garbage = Buffer.from([0xDE, 0xAD, 0xBE, 0xEF]);
719
+ let threw = false;
720
+ try {
721
+ inflateRawSync(garbage);
722
+ } catch {
723
+ threw = true;
724
+ }
725
+ expect(threw).toBe(true);
726
+ });
727
+ });
728
+
729
+ // --- Gzip header format details ---
730
+ // Ported from refs/node-test/parallel/test-zlib-from-string.js
731
+ // Original: MIT license, Node.js contributors
732
+ await describe('zlib: gzip format details', async () => {
733
+ await it('gzip compression method byte should be 8 (deflate)', async () => {
734
+ const compressed = gzipSync(Buffer.from('test'));
735
+ // byte 0: 0x1f, byte 1: 0x8b (magic), byte 2: compression method (8 = deflate)
736
+ expect(compressed[0]).toBe(0x1f);
737
+ expect(compressed[1]).toBe(0x8b);
738
+ expect(compressed[2]).toBe(8);
739
+ });
740
+
741
+ await it('deflate output should start with zlib header', async () => {
742
+ const compressed = deflateSync(Buffer.from('test'));
743
+ // Zlib header: first byte has CMF (usually 0x78 for deflate with default window)
744
+ // High nibble = CINFO (window size), Low nibble = CM (8 = deflate)
745
+ expect(compressed[0] & 0x0F).toBe(8);
746
+ });
747
+
748
+ await it('deflateRaw output should have no header', async () => {
749
+ const input = Buffer.from('test header absence');
750
+ const raw = deflateRawSync(input);
751
+ const deflated = deflateSync(input);
752
+ const gzipped = gzipSync(input);
753
+
754
+ // Raw should be smallest (no header/checksum), gzip should be largest
755
+ expect(raw.length).toBeLessThan(deflated.length);
756
+ expect(deflated.length).toBeLessThan(gzipped.length);
757
+ });
758
+ });
759
+
760
+ // --- Uint8Array and ArrayBuffer input ---
761
+ // Ported from refs/node-test/parallel/test-zlib-convenience-methods.js
762
+ // Original: MIT license, Node.js contributors
763
+ await describe('zlib: TypedArray and ArrayBuffer input', async () => {
764
+ await it('should accept Uint8Array input for gzip', async () => {
765
+ const input = new TextEncoder().encode('Uint8Array gzip test');
766
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
767
+ gzip(input, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
768
+ });
769
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
770
+ gunzip(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
771
+ });
772
+ expect(new TextDecoder().decode(decompressed)).toBe('Uint8Array gzip test');
773
+ });
774
+
775
+ await it('should accept Uint8Array input for deflateSync', async () => {
776
+ const input = new TextEncoder().encode('Uint8Array deflate sync');
777
+ const compressed = deflateSync(input as any);
778
+ const decompressed = inflateSync(compressed);
779
+ expect(new TextDecoder().decode(decompressed)).toBe('Uint8Array deflate sync');
780
+ });
781
+
782
+ await it('should accept Uint8Array input for deflateRawSync', async () => {
783
+ const input = new TextEncoder().encode('Uint8Array raw sync');
784
+ const compressed = deflateRawSync(input as any);
785
+ const decompressed = inflateRawSync(compressed);
786
+ expect(new TextDecoder().decode(decompressed)).toBe('Uint8Array raw sync');
787
+ });
788
+ });
789
+
790
+ // --- Double compression for all formats ---
791
+ await describe('zlib: double compression all formats', async () => {
792
+ await it('should handle double deflate/inflate', async () => {
793
+ const input = Buffer.from('double deflate test');
794
+ const first = deflateSync(input);
795
+ const second = deflateSync(first);
796
+ const dec1 = inflateSync(second);
797
+ const dec2 = inflateSync(dec1);
798
+ expect(new TextDecoder().decode(dec2)).toBe('double deflate test');
799
+ });
800
+
801
+ await it('should handle double deflateRaw/inflateRaw', async () => {
802
+ const input = Buffer.from('double raw test');
803
+ const first = deflateRawSync(input);
804
+ const second = deflateRawSync(first);
805
+ const dec1 = inflateRawSync(second);
806
+ const dec2 = inflateRawSync(dec1);
807
+ expect(new TextDecoder().decode(dec2)).toBe('double raw test');
808
+ });
809
+ });
810
+
811
+ // --- Sync with repeated string content ---
812
+ // Ported from refs/node-test/parallel/test-zlib-convenience-methods.js
813
+ // Original: MIT license, Node.js contributors
814
+ await describe('zlib: sync convenience round-trip with repeated data', async () => {
815
+ await it('gzipSync/gunzipSync with repeated string', async () => {
816
+ const expectStr = 'blah'.repeat(8);
817
+ const compressed = gzipSync(expectStr as any);
818
+ const decompressed = gunzipSync(compressed);
819
+ expect(new TextDecoder().decode(decompressed)).toBe(expectStr);
820
+ });
821
+
822
+ await it('deflateSync/inflateSync with repeated string', async () => {
823
+ const expectStr = 'blah'.repeat(8);
824
+ const compressed = deflateSync(expectStr as any);
825
+ const decompressed = inflateSync(compressed);
826
+ expect(new TextDecoder().decode(decompressed)).toBe(expectStr);
827
+ });
828
+
829
+ await it('deflateRawSync/inflateRawSync with repeated string', async () => {
830
+ const expectStr = 'blah'.repeat(8);
831
+ const compressed = deflateRawSync(expectStr as any);
832
+ const decompressed = inflateRawSync(compressed);
833
+ expect(new TextDecoder().decode(decompressed)).toBe(expectStr);
834
+ });
835
+ });
836
+
837
+ // --- From string: long string with special characters ---
838
+ // Ported from refs/node-test/parallel/test-zlib-from-string.js
839
+ // Original: MIT license, Node.js contributors
840
+ await describe('zlib: string compression from-string pattern', async () => {
841
+ await it('should round-trip a long string with special chars through deflate', async () => {
842
+ const inputString = '\u03A9\u03A9Lorem ipsum dolor sit amet, consectetur adipiscing eli' +
843
+ 't. Morbi faucibus, purus at gravida dictum, libero arcu ' +
844
+ 'convallis lacus, in commodo libero metus eu nisi. Nullam' +
845
+ ' commodo, neque nec porta placerat, nisi est fermentum a' +
846
+ 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' +
847
+ 'n non diam orci. Proin quis elit turpis. Suspendisse non' +
848
+ ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' +
849
+ 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' +
850
+ 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. ';
851
+
852
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
853
+ deflate(inputString, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
854
+ });
855
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
856
+ inflate(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
857
+ });
858
+ expect(new TextDecoder().decode(decompressed)).toBe(inputString);
859
+ });
860
+
861
+ await it('should round-trip a long string through gzip', async () => {
862
+ const inputString = '\u03A9\u03A9Lorem ipsum dolor sit amet, consectetur adipiscing eli' +
863
+ 't. Morbi faucibus, purus at gravida dictum, libero arcu ' +
864
+ 'convallis lacus, in commodo libero metus eu nisi. Nullam' +
865
+ ' commodo, neque nec porta placerat, nisi est fermentum a' +
866
+ 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' +
867
+ 'n non diam orci. Proin quis elit turpis. Suspendisse non' +
868
+ ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' +
869
+ 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' +
870
+ 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. ';
871
+
872
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
873
+ gzip(inputString, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
874
+ });
875
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
876
+ gunzip(compressed, (err, result) => err ? reject(err) : resolve(result as unknown as Buffer));
877
+ });
878
+ expect(new TextDecoder().decode(decompressed)).toBe(inputString);
879
+ });
880
+ });
881
+
882
+ // --- Binary data with specific patterns ---
883
+ await describe('zlib: binary data patterns', async () => {
884
+ await it('should handle all-zeros buffer', async () => {
885
+ const input = Buffer.alloc(1024, 0);
886
+ const compressed = gzipSync(input);
887
+ // All zeros should compress very well
888
+ expect(compressed.length).toBeLessThan(input.length);
889
+ const decompressed = gunzipSync(compressed);
890
+ expect(decompressed.length).toBe(1024);
891
+ for (let i = 0; i < 1024; i++) {
892
+ expect(decompressed[i]).toBe(0);
893
+ }
894
+ });
895
+
896
+ await it('should handle all-0xFF buffer', async () => {
897
+ const input = Buffer.alloc(1024, 0xFF);
898
+ const compressed = deflateSync(input);
899
+ expect(compressed.length).toBeLessThan(input.length);
900
+ const decompressed = inflateSync(compressed);
901
+ expect(decompressed.length).toBe(1024);
902
+ for (let i = 0; i < 1024; i++) {
903
+ expect(decompressed[i]).toBe(0xFF);
904
+ }
905
+ });
906
+
907
+ await it('should handle single byte', async () => {
908
+ const input = Buffer.from([0x42]);
909
+ const compressed = gzipSync(input);
910
+ const decompressed = gunzipSync(compressed);
911
+ expect(decompressed.length).toBe(1);
912
+ expect(decompressed[0]).toBe(0x42);
913
+ });
914
+
915
+ await it('should handle single byte with deflateRaw', async () => {
916
+ const input = Buffer.from([0xAB]);
917
+ const compressed = deflateRawSync(input);
918
+ const decompressed = inflateRawSync(compressed);
919
+ expect(decompressed.length).toBe(1);
920
+ expect(decompressed[0]).toBe(0xAB);
921
+ });
922
+
923
+ await it('should handle alternating byte pattern', async () => {
924
+ const input = Buffer.alloc(512);
925
+ for (let i = 0; i < 512; i++) {
926
+ input[i] = i % 2 === 0 ? 0xAA : 0x55;
927
+ }
928
+ const compressed = deflateRawSync(input);
929
+ const decompressed = inflateRawSync(compressed);
930
+ expect(decompressed.length).toBe(512);
931
+ for (let i = 0; i < 512; i++) {
932
+ expect(decompressed[i]).toBe(i % 2 === 0 ? 0xAA : 0x55);
933
+ }
934
+ });
935
+ });
936
+
937
+ // --- Truncated data errors ---
938
+ // Ported from refs/node-test/parallel/test-zlib-truncated.js
939
+ // Original: MIT license, Node.js contributors
940
+ await describe('zlib: truncated compressed data', async () => {
941
+ await it('should error on truncated gzip data (sync)', async () => {
942
+ const original = 'This is data that will be truncated after compression';
943
+ const compressed = gzipSync(original as any);
944
+ const truncated = compressed.slice(0, Math.floor(compressed.length / 2));
945
+ let threw = false;
946
+ try {
947
+ gunzipSync(truncated);
948
+ } catch {
949
+ threw = true;
950
+ }
951
+ expect(threw).toBe(true);
952
+ });
953
+
954
+ await it('should error on truncated deflate data (sync)', async () => {
955
+ const original = 'This is data that will be truncated after deflation';
956
+ const compressed = deflateSync(original as any);
957
+ const truncated = compressed.slice(0, Math.floor(compressed.length / 2));
958
+ let threw = false;
959
+ try {
960
+ inflateSync(truncated);
961
+ } catch {
962
+ threw = true;
963
+ }
964
+ expect(threw).toBe(true);
965
+ });
966
+
967
+ await it('should error on truncated deflateRaw data (sync)', async () => {
968
+ const original = 'This is data that will be truncated after raw deflation';
969
+ const compressed = deflateRawSync(original as any);
970
+ const truncated = compressed.slice(0, Math.floor(compressed.length / 2));
971
+ let threw = false;
972
+ try {
973
+ inflateRawSync(truncated);
974
+ } catch {
975
+ threw = true;
976
+ }
977
+ expect(threw).toBe(true);
978
+ });
979
+
980
+ await it('should error on truncated gzip data (async)', async () => {
981
+ const original = 'Async truncation test data for gzip compression';
982
+ const compressed = gzipSync(original as any);
983
+ const truncated = compressed.slice(0, Math.floor(compressed.length / 2));
984
+ const error = await new Promise<Error | null>((resolve) => {
985
+ gunzip(truncated, (err) => resolve(err));
986
+ });
987
+ expect(error).toBeDefined();
988
+ });
989
+
990
+ await it('should error on truncated deflate data (async)', async () => {
991
+ const original = 'Async truncation test data for deflate compression';
992
+ const compressed = deflateSync(original as any);
993
+ const truncated = compressed.slice(0, Math.floor(compressed.length / 2));
994
+ const error = await new Promise<Error | null>((resolve) => {
995
+ inflate(truncated, (err) => resolve(err));
996
+ });
997
+ expect(error).toBeDefined();
998
+ });
999
+ });
1000
+
1001
+ // --- Concatenated gzip members ---
1002
+ // Ported from refs/node-test/parallel/test-zlib-from-concatenated-gzip.js
1003
+ // Original: MIT license, Node.js contributors
1004
+ await describe('zlib: concatenated gzip members', async () => {
1005
+ await it('gunzipSync should decompress concatenated gzip members', async () => {
1006
+ const abc = 'abc';
1007
+ const def = 'def';
1008
+ const abcEncoded = gzipSync(abc as any);
1009
+ const defEncoded = gzipSync(def as any);
1010
+
1011
+ const concatenated = Buffer.concat([abcEncoded, defEncoded]);
1012
+ const result = gunzipSync(concatenated);
1013
+ expect(new TextDecoder().decode(result)).toBe('abcdef');
1014
+ });
1015
+
1016
+ await it('gunzip async should decompress concatenated gzip members', async () => {
1017
+ const abc = 'abc';
1018
+ const def = 'def';
1019
+ const abcEncoded = gzipSync(abc as any);
1020
+ const defEncoded = gzipSync(def as any);
1021
+
1022
+ const concatenated = Buffer.concat([abcEncoded, defEncoded]);
1023
+ const result = await new Promise<Buffer>((resolve, reject) => {
1024
+ gunzip(concatenated, (err, data) => err ? reject(err) : resolve(data as unknown as Buffer));
1025
+ });
1026
+ expect(new TextDecoder().decode(result)).toBe('abcdef');
1027
+ });
1028
+ });
1029
+
1030
+ // --- Sync functions with empty string ---
1031
+ await describe('zlib: sync with empty string', async () => {
1032
+ await it('gzipSync should handle empty string', async () => {
1033
+ const compressed = gzipSync('' as any);
1034
+ const decompressed = gunzipSync(compressed);
1035
+ expect(decompressed.length).toBe(0);
1036
+ });
1037
+
1038
+ await it('deflateSync should handle empty string', async () => {
1039
+ const compressed = deflateSync('' as any);
1040
+ const decompressed = inflateSync(compressed);
1041
+ expect(decompressed.length).toBe(0);
1042
+ });
1043
+
1044
+ await it('deflateRawSync should handle empty string', async () => {
1045
+ const compressed = deflateRawSync('' as any);
1046
+ const decompressed = inflateRawSync(compressed);
1047
+ expect(decompressed.length).toBe(0);
1048
+ });
1049
+ });
1050
+
1051
+ // --- Idempotence: compressing same data multiple times ---
1052
+ await describe('zlib: idempotence', async () => {
1053
+ await it('gzipSync should produce consistent decompression', async () => {
1054
+ const input = Buffer.from('idempotent test gzip');
1055
+ const c1 = gzipSync(input);
1056
+ const c2 = gzipSync(input);
1057
+ // Compressed output may differ (timestamps in gzip header) but
1058
+ // decompressed output must be identical
1059
+ const d1 = gunzipSync(c1);
1060
+ const d2 = gunzipSync(c2);
1061
+ expect(new TextDecoder().decode(d1)).toBe('idempotent test gzip');
1062
+ expect(new TextDecoder().decode(d2)).toBe('idempotent test gzip');
1063
+ });
1064
+
1065
+ await it('deflateSync should produce identical output for same input', async () => {
1066
+ const input = Buffer.from('idempotent test deflate');
1067
+ const c1 = deflateSync(input);
1068
+ const c2 = deflateSync(input);
1069
+ expect(c1.length).toBe(c2.length);
1070
+ for (let i = 0; i < c1.length; i++) {
1071
+ expect(c1[i]).toBe(c2[i]);
1072
+ }
1073
+ });
1074
+
1075
+ await it('deflateRawSync should produce identical output for same input', async () => {
1076
+ const input = Buffer.from('idempotent test raw');
1077
+ const c1 = deflateRawSync(input);
1078
+ const c2 = deflateRawSync(input);
1079
+ expect(c1.length).toBe(c2.length);
1080
+ for (let i = 0; i < c1.length; i++) {
1081
+ expect(c1[i]).toBe(c2[i]);
1082
+ }
1083
+ });
1084
+ });
1085
+
1086
+ // --- Async callback: deflateRaw/inflateRaw ---
1087
+ await describe('zlib: async callback deflateRaw/inflateRaw', async () => {
1088
+ await it('deflateRaw should accept callback', async () => {
1089
+ const result = await new Promise<boolean>((resolve, reject) => {
1090
+ deflateRaw(Buffer.from('async deflateRaw'), (err, data) => {
1091
+ if (err) reject(err);
1092
+ else resolve(data.length > 0);
1093
+ });
1094
+ });
1095
+ expect(result).toBe(true);
1096
+ });
1097
+
1098
+ await it('inflateRaw should decompress via callback', async () => {
1099
+ const input = Buffer.from('async inflateRaw round-trip');
1100
+ const compressed = deflateRawSync(input);
1101
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
1102
+ inflateRaw(compressed, (err, data) => {
1103
+ if (err) reject(err);
1104
+ else resolve(data as unknown as Buffer);
1105
+ });
1106
+ });
1107
+ expect(new TextDecoder().decode(decompressed)).toBe('async inflateRaw round-trip');
1108
+ });
1109
+ });
1110
+
1111
+ // --- Mixed sync/async: compress sync, decompress async and vice versa ---
1112
+ await describe('zlib: mixed sync/async interop', async () => {
1113
+ await it('should decompress async what was compressed sync (gzip)', async () => {
1114
+ const input = Buffer.from('mixed sync async gzip');
1115
+ const compressed = gzipSync(input);
1116
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
1117
+ gunzip(compressed, (err, data) => err ? reject(err) : resolve(data as unknown as Buffer));
1118
+ });
1119
+ expect(new TextDecoder().decode(decompressed)).toBe('mixed sync async gzip');
1120
+ });
1121
+
1122
+ await it('should decompress sync what was compressed async (deflate)', async () => {
1123
+ const input = Buffer.from('mixed async sync deflate');
1124
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
1125
+ deflate(input, (err, data) => err ? reject(err) : resolve(data as unknown as Buffer));
1126
+ });
1127
+ const decompressed = inflateSync(compressed);
1128
+ expect(new TextDecoder().decode(decompressed)).toBe('mixed async sync deflate');
1129
+ });
1130
+
1131
+ await it('should decompress sync what was compressed async (deflateRaw)', async () => {
1132
+ const input = Buffer.from('mixed async sync raw');
1133
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
1134
+ deflateRaw(input, (err, data) => err ? reject(err) : resolve(data as unknown as Buffer));
1135
+ });
1136
+ const decompressed = inflateRawSync(compressed);
1137
+ expect(new TextDecoder().decode(decompressed)).toBe('mixed async sync raw');
1138
+ });
1139
+
1140
+ await it('should decompress async what was compressed sync (deflateRaw)', async () => {
1141
+ const input = Buffer.from('mixed sync async raw');
1142
+ const compressed = deflateRawSync(input);
1143
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
1144
+ inflateRaw(compressed, (err, data) => err ? reject(err) : resolve(data as unknown as Buffer));
1145
+ });
1146
+ expect(new TextDecoder().decode(decompressed)).toBe('mixed sync async raw');
1147
+ });
1148
+ });
1149
+ };