81 lines
2.9 KiB
TypeScript
81 lines
2.9 KiB
TypeScript
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
|
import { mkdtemp, rm, utimes, writeFile } from 'node:fs/promises';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
|
|
import { loadOrBuildWebappBundle } from '../src/webapp/bundle.ts';
|
|
import { resolveWebappKeys } from '../src/webapp/keys.ts';
|
|
import { buildStoreZip } from '../src/webapp/zip-store.ts';
|
|
|
|
describe('webapp bundle cache', () => {
|
|
let frontendPath = '';
|
|
let cacheDir = '';
|
|
let stateParent = '';
|
|
|
|
beforeAll(async () => {
|
|
stateParent = await mkdtemp(join(tmpdir(), 'hbg-bundle-'));
|
|
frontendPath = join(stateParent, 'frontend');
|
|
cacheDir = join(stateParent, 'cache');
|
|
await Bun.$`mkdir -p ${frontendPath}`.quiet();
|
|
await writeFile(join(frontendPath, 'index.html'), '<html>ok</html>\n');
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await rm(stateParent, { recursive: true, force: true });
|
|
});
|
|
|
|
test('loadOrBuildWebappBundle reuses disk cache on second call', async () => {
|
|
const keys = await resolveWebappKeys('test-webapp-signing-secret', join(stateParent, 'signing.key'));
|
|
|
|
const first = await loadOrBuildWebappBundle(
|
|
frontendPath,
|
|
keys,
|
|
'v1',
|
|
cacheDir,
|
|
);
|
|
expect(first.content.byteLength).toBeGreaterThan(0);
|
|
expect(await Bun.file(join(cacheDir, 'bundle.zip')).exists()).toBe(true);
|
|
|
|
const second = await loadOrBuildWebappBundle(
|
|
frontendPath,
|
|
keys,
|
|
'v1',
|
|
cacheDir,
|
|
);
|
|
expect(Buffer.from(second.content).equals(Buffer.from(first.content))).toBe(true);
|
|
expect(second.metadata.signature).toBe(first.metadata.signature);
|
|
});
|
|
|
|
test('cache invalidates when a frontend file changes', async () => {
|
|
const keys = await resolveWebappKeys('test-webapp-signing-secret', join(stateParent, 'signing.key'));
|
|
const before = await loadOrBuildWebappBundle(
|
|
frontendPath,
|
|
keys,
|
|
'v1',
|
|
cacheDir,
|
|
);
|
|
|
|
await writeFile(join(frontendPath, 'index.html'), '<html>changed</html>\n');
|
|
// Ensure mtime advances even on fast filesystems
|
|
const now = new Date(Date.now() + 2000);
|
|
await utimes(join(frontendPath, 'index.html'), now, now);
|
|
|
|
const after = await loadOrBuildWebappBundle(
|
|
frontendPath,
|
|
keys,
|
|
'v1',
|
|
cacheDir,
|
|
);
|
|
expect(Buffer.from(after.content).equals(Buffer.from(before.content))).toBe(false);
|
|
});
|
|
|
|
test('buildStoreZip produces a valid zip local header', () => {
|
|
const zip = buildStoreZip([
|
|
{ path: 'a.txt', content: new TextEncoder().encode('hello') },
|
|
]);
|
|
expect(zip[0]).toBe(0x50);
|
|
expect(zip[1]).toBe(0x4b);
|
|
expect(zip[2]).toBe(0x03);
|
|
expect(zip[3]).toBe(0x04);
|
|
});
|
|
});
|