Bonsai-Chat-WebGPU / src /lib /artifact-workspace.test.ts
WaveCut's picture
Add persistent multi-file artifact workspaces
33e51e5 verified
Raw History Blame Contribute Delete
2.12 kB
import { describe, expect, it } from 'vitest';
import {
inferArtifactMime,
normalizeArtifactFiles,
normalizeArtifactPath,
} from './artifact-workspace';
describe('artifact workspace paths', () => {
it('normalizes relative paths and infers browser MIME types', () => {
expect(normalizeArtifactPath('./src/components/chart.js')).toBe('src/components/chart.js');
expect(inferArtifactMime('styles/app.css')).toBe('text/css; charset=utf-8');
expect(inferArtifactMime('assets/logo.svg')).toBe('image/svg+xml; charset=utf-8');
});
it.each([
'/index.html',
'../index.html',
'src/../../index.html',
'src\\main.js',
'src//main.js',
'.bonsai-workspace.json',
])('rejects paths that escape or collide with the workspace: %s', (path) => {
expect(() => normalizeArtifactPath(path)).toThrow();
});
});
describe('artifact workspace files', () => {
it('builds one bounded manifest with a real HTML entry file', () => {
const deployment = normalizeArtifactFiles([
{ path: 'index.html', content: '<link rel="stylesheet" href="./styles/app.css">' },
{ path: 'styles/app.css', content: 'body { color: green; }' },
{ path: 'src/main.js', content: 'console.log("ready")' },
]);
expect(deployment.entryPath).toBe('index.html');
expect(deployment.files.map((file) => file.path)).toEqual([
'index.html',
'styles/app.css',
'src/main.js',
]);
expect(deployment.totalBytes).toBeGreaterThan(0);
});
it('rejects duplicate, missing-entry, oversized, and non-HTML-entry deployments', () => {
expect(() => normalizeArtifactFiles([
{ path: 'index.html', content: 'a' },
{ path: './index.html', content: 'b' },
])).toThrow('duplicated');
expect(() => normalizeArtifactFiles([{ path: 'main.js', content: 'x' }])).toThrow('entry file is missing');
expect(() => normalizeArtifactFiles([
{ path: 'index.html', content: 'x'.repeat(512 * 1024 + 1) },
])).toThrow('512 KiB');
expect(() => normalizeArtifactFiles([{ path: 'main.js', content: 'x' }], 'main.js')).toThrow('must be HTML');
});
});