110 lines
3.3 KiB
TypeScript
110 lines
3.3 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest';
|
|
import type { FFmpegAdapter } from '../../src/ffmpeg/ffmpeg.types';
|
|
import {
|
|
describeMountedInputAccess,
|
|
mountJobFiles,
|
|
} from '../../src/ffmpeg/virtual-fs';
|
|
|
|
function createFilesystemAdapter(
|
|
options: { keepWorkDirectory?: boolean; rejectWorkerFs?: boolean } = {}
|
|
) {
|
|
const directories = new Set(['/input', '/work']);
|
|
const files = new Set<string>();
|
|
const adapter = {
|
|
createDir: vi.fn(async (path: string) => {
|
|
directories.add(path);
|
|
}),
|
|
mount: vi.fn(async () => {
|
|
if (options.rejectWorkerFs) {
|
|
throw new Error('WORKERFS unavailable');
|
|
}
|
|
}),
|
|
unmount: vi.fn(async () => undefined),
|
|
writeFile: vi.fn(async (path: string) => {
|
|
files.add(path);
|
|
}),
|
|
deleteFile: vi.fn(async (path: string) => {
|
|
files.delete(path);
|
|
}),
|
|
deleteDir: vi.fn(async (path: string) => {
|
|
if (options.keepWorkDirectory && path.startsWith('/work/job-')) return;
|
|
directories.delete(path);
|
|
}),
|
|
listDir: vi.fn(async (path: string) =>
|
|
[...directories]
|
|
.filter(
|
|
(entry) =>
|
|
entry !== path && entry.slice(0, entry.lastIndexOf('/')) === path
|
|
)
|
|
.map((entry) => ({
|
|
name: entry.split('/').at(-1) ?? '',
|
|
isDir: true,
|
|
isLink: false,
|
|
}))
|
|
),
|
|
};
|
|
return adapter as unknown as FFmpegAdapter;
|
|
}
|
|
|
|
describe('per-job FFmpeg filesystem', () => {
|
|
it('uses generated paths and verifies complete cleanup', async () => {
|
|
const adapter = createFilesystemAdapter();
|
|
const mounted = await mountJobFiles(
|
|
adapter,
|
|
'../../job with separators',
|
|
[new File(['source'], '../../private name.MP4')],
|
|
['../unsafe result.mp4']
|
|
);
|
|
|
|
expect(mounted.inputPaths).toEqual([
|
|
'/input/job-job-with-separators/source-0.mp4',
|
|
]);
|
|
expect(mounted.outputPaths).toEqual([
|
|
'/work/job-job-with-separators/output-0.mp4',
|
|
]);
|
|
expect(mounted).toMatchObject({
|
|
inputMode: 'workerfs',
|
|
inputBytes: 6,
|
|
copiedInputBytes: 0,
|
|
});
|
|
expect(describeMountedInputAccess(mounted)).toContain(
|
|
'0 bytes copied into engine memory'
|
|
);
|
|
expect(mounted.inputPaths[0]).not.toContain('private');
|
|
expect(await mounted.cleanup()).toBe(true);
|
|
});
|
|
|
|
it('accounts for and explains the actual memory-copy fallback', async () => {
|
|
const adapter = createFilesystemAdapter({ rejectWorkerFs: true });
|
|
const mounted = await mountJobFiles(
|
|
adapter,
|
|
'copy-fallback',
|
|
[new File(['1234'], 'one.mp4'), new File(['567'], 'two.mp4')],
|
|
['result.mp4']
|
|
);
|
|
|
|
expect(mounted).toMatchObject({
|
|
inputMode: 'memory-copy',
|
|
inputBytes: 7,
|
|
copiedInputBytes: 7,
|
|
});
|
|
expect(adapter.writeFile).toHaveBeenCalledTimes(2);
|
|
expect(describeMountedInputAccess(mounted)).toBe(
|
|
'Input compatibility fallback: WORKERFS mounting was unavailable, so 7 bytes were copied into engine memory.'
|
|
);
|
|
expect(await mounted.cleanup()).toBe(true);
|
|
});
|
|
|
|
it('reports a directory that survives cleanup', async () => {
|
|
const adapter = createFilesystemAdapter({ keepWorkDirectory: true });
|
|
const mounted = await mountJobFiles(
|
|
adapter,
|
|
'job',
|
|
[new File(['source'], 'source.wav')],
|
|
['result.wav']
|
|
);
|
|
|
|
expect(await mounted.cleanup()).toBe(false);
|
|
});
|
|
});
|