⚡ Zero-dependency plcbundle library exclusively for Bun
1import { describe, test, expect, beforeEach } from 'bun:test';
2import { PLCBundle } from '../src/plcbundle';
3import { TEMP_DIR, createMockIndex, createMockOperations } from './setup';
4
5describe('Multi-threaded Processing', () => {
6 let bundle: PLCBundle;
7 let modulePath: string;
8
9 beforeEach(async () => {
10 bundle = new PLCBundle(TEMP_DIR);
11
12 const mockIndex = createMockIndex();
13 await bundle.saveIndex(mockIndex);
14
15 // Create test bundles
16 for (let i = 1; i <= 5; i++) {
17 const operations = createMockOperations(100);
18 const jsonl = operations.map(op => JSON.stringify(op)).join('\n') + '\n';
19 const compressed = Bun.zstdCompressSync(new TextEncoder().encode(jsonl));
20 await Bun.write(bundle.getBundlePath(i), compressed);
21 }
22
23 // Create test module - use absolute path
24 modulePath = `${process.cwd()}/${TEMP_DIR}/test-module.ts`;
25 await Bun.write(modulePath, `
26 export function detect({ op }) {
27 return op.did.length > 10 ? ['long-did'] : [];
28 }
29 `);
30 });
31
32 test('module loads correctly', async () => {
33 const mod = await import(modulePath);
34 expect(mod.detect).toBeDefined();
35 });
36
37 test('single-threaded processing works', async () => {
38 const stats = await bundle.processBundles(1, 2, {
39 module: modulePath,
40 threads: 1,
41 });
42
43 expect(stats.totalOps).toBe(200); // 2 bundles * 100 ops
44 expect(stats.totalBytes).toBeGreaterThan(0);
45 });
46
47 test('multi-threaded processing with 2 threads', async () => {
48 const stats = await bundle.processBundles(1, 4, {
49 module: modulePath,
50 threads: 2,
51 });
52
53 expect(stats.totalOps).toBe(400); // 4 bundles * 100 ops
54 expect(stats.totalBytes).toBeGreaterThan(0);
55 });
56
57 test('multi-threaded produces same op count as single-threaded', async () => {
58 const stats1 = await bundle.processBundles(1, 3, {
59 module: modulePath,
60 threads: 1,
61 });
62
63 const stats2 = await bundle.processBundles(1, 3, {
64 module: modulePath,
65 threads: 2,
66 });
67
68 expect(stats1.totalOps).toBe(stats2.totalOps);
69 expect(stats1.totalBytes).toBe(stats2.totalBytes);
70 });
71
72 test('progress callback works with multi-threading', async () => {
73 let progressCalls = 0;
74
75 await bundle.processBundles(1, 5, {
76 module: modulePath,
77 threads: 2,
78 onProgress: () => {
79 progressCalls++;
80 },
81 });
82
83 // May or may not be called depending on threshold
84 expect(typeof progressCalls).toBe('number');
85 });
86
87 test('validates threads parameter', async () => {
88 let errorThrown = false;
89
90 try {
91 await bundle.processBundles(1, 1, () => {}, {
92 threads: 4, // Without module - should throw
93 });
94 } catch (error) {
95 errorThrown = true;
96 expect((error as Error).message).toContain('module');
97 }
98
99 expect(errorThrown).toBe(true);
100 });
101});