⚡ 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('Bundle Verification', () => {
6 let bundle: PLCBundle;
7
8 beforeEach(async () => {
9 bundle = new PLCBundle(TEMP_DIR);
10 const mockIndex = createMockIndex();
11 await bundle.saveIndex(mockIndex);
12 });
13
14 test('returns error for missing bundle in index', async () => {
15 const result = await bundle.verifyBundle(999);
16
17 expect(result.valid).toBe(false);
18 expect(result.errors.length).toBeGreaterThan(0);
19 expect(result.errors[0]).toContain('not found');
20 });
21
22 test('validates with actual bundle file', async () => {
23 // Create a mock bundle file
24 const operations = createMockOperations(100);
25 const jsonl = operations.map(op => JSON.stringify(op)).join('\n') + '\n';
26 const uncompressed = new TextEncoder().encode(jsonl);
27 const compressed = Bun.zstdCompressSync(uncompressed);
28
29 // Calculate hashes
30 const hasher = new Bun.CryptoHasher("sha256");
31 hasher.update(compressed);
32 const compressedHash = Buffer.from(hasher.digest()).toString('hex');
33
34 const contentHasher = new Bun.CryptoHasher("sha256");
35 contentHasher.update(uncompressed);
36 const contentHash = Buffer.from(contentHasher.digest()).toString('hex');
37
38 // Write bundle file
39 const bundlePath = bundle.getBundlePath(1);
40 await Bun.write(bundlePath, compressed);
41
42 // Update index with correct hashes
43 const index = await bundle.loadIndex();
44 index.bundles[0].compressed_hash = compressedHash;
45 index.bundles[0].content_hash = contentHash;
46 await bundle.saveIndex(index);
47
48 // Now verify
49 const result = await bundle.verifyBundle(1);
50 expect(result.valid).toBe(true);
51 expect(result.errors.length).toBe(0);
52 });
53
54 test('returns error when bundle file does not exist', async () => {
55 // Bundle 1 exists in index but file doesn't exist
56 // (We don't create the file in this test)
57 const result = await bundle.verifyBundle(1);
58
59 expect(result).toBeDefined();
60 expect(result.valid).toBe(false);
61 expect(result.errors.length).toBeGreaterThan(0);
62 // Should have error about file not being readable
63 });
64
65 test('detects hash mismatch', async () => {
66 // Create a bundle file with wrong content
67 const operations = createMockOperations(100);
68 const jsonl = operations.map(op => JSON.stringify(op)).join('\n') + '\n';
69 const uncompressed = new TextEncoder().encode(jsonl);
70 const compressed = Bun.zstdCompressSync(uncompressed);
71
72 // Write bundle file
73 const bundlePath = bundle.getBundlePath(2);
74 await Bun.write(bundlePath, compressed);
75
76 // Index has different hashes (from mock)
77 // So verification should fail
78 const result = await bundle.verifyBundle(2);
79 expect(result.valid).toBe(false);
80 expect(result.errors.length).toBeGreaterThan(0);
81 expect(result.errors[0]).toContain('hash');
82 });
83});