import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { archiveBase, createArchive, extractArchive } from './archive.js'; let dir: string; beforeEach(async () => { dir = await mkdtemp(join(tmpdir(), 'dorfteich-archive-')); }); afterEach(async () => { await rm(dir, { recursive: true, force: true }); }); describe('archiveBase', () => { it('requires a shared parent directory', () => { expect(() => archiveBase(['/data/uploads', '/other/plugins'])).toThrow(/share one parent/); expect(archiveBase(['/data/uploads', '/data/plugins'])).toEqual({ base: '/data', names: ['uploads', 'plugins'], }); }); }); describe('create/extract round-trip (real tar)', () => { it('restores the archived files byte-identically', async () => { const source = join(dir, 'source'); await mkdir(join(source, 'uploads', 'ab'), { recursive: true }); await mkdir(join(source, 'plugins'), { recursive: true }); await writeFile(join(source, 'uploads', 'ab', 'file.png'), 'png-bytes'); await writeFile(join(source, 'plugins', 'manifest.json'), '{"id":"toc"}'); const archive = join(dir, 'files.tar.gz'); await createArchive(archive, [join(source, 'uploads'), join(source, 'plugins')]); const target = join(dir, 'target'); await mkdir(target, { recursive: true }); await extractArchive(archive, [join(target, 'uploads'), join(target, 'plugins')]); expect(await readFile(join(target, 'uploads', 'ab', 'file.png'), 'utf8')).toBe('png-bytes'); expect(await readFile(join(target, 'plugins', 'manifest.json'), 'utf8')).toBe('{"id":"toc"}'); }); it('produces a valid empty archive when no data directory exists yet', async () => { const archive = join(dir, 'empty.tar.gz'); await createArchive(archive, [join(dir, 'none', 'uploads'), join(dir, 'none', 'plugins')]); const target = join(dir, 'extract'); await mkdir(target, { recursive: true }); await extractArchive(archive, [join(target, 'uploads'), join(target, 'plugins')]); }); });