122 lines
4.2 KiB
TypeScript
122 lines
4.2 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { mkdtempSync, writeFileSync, readFileSync, rmSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import {
|
|
createEmpty,
|
|
loadManifest,
|
|
saveManifest,
|
|
findBySignature,
|
|
addEntry,
|
|
markDeleted,
|
|
} from '../manifest';
|
|
import type { ManifestEntry } from '../types';
|
|
|
|
const sampleEntry = (sig: string, file = 'resources/js/A.vue'): ManifestEntry => ({
|
|
file,
|
|
line: 1,
|
|
tag: 'v-btn',
|
|
parentChain: ['A'],
|
|
signature: sig,
|
|
text: 'hello',
|
|
key: null,
|
|
ref: null,
|
|
createdAt: '2026-05-12T00:00:00.000Z',
|
|
});
|
|
|
|
describe('manifest', () => {
|
|
it('createEmpty returns a fresh manifest with lastId=0', () => {
|
|
const m = createEmpty();
|
|
expect(m.version).toBe(1);
|
|
expect(m.lastId).toBe(0);
|
|
expect(m.entries).toEqual({});
|
|
expect(m.deleted).toEqual({});
|
|
});
|
|
|
|
it('addEntry assigns next id and increments lastId', () => {
|
|
const m = createEmpty();
|
|
const id1 = addEntry(m, sampleEntry('sig-1'));
|
|
const id2 = addEntry(m, sampleEntry('sig-2'));
|
|
expect(id1).toBe(1);
|
|
expect(id2).toBe(2);
|
|
expect(m.lastId).toBe(2);
|
|
expect(m.entries['1'].signature).toBe('sig-1');
|
|
});
|
|
|
|
it('findBySignature returns matching id', () => {
|
|
const m = createEmpty();
|
|
addEntry(m, sampleEntry('sig-a'));
|
|
addEntry(m, sampleEntry('sig-b'));
|
|
expect(findBySignature(m, 'sig-b')).toBe(2);
|
|
expect(findBySignature(m, 'sig-x')).toBeNull();
|
|
});
|
|
|
|
it('markDeleted moves entry to deleted section, preserves lastId', () => {
|
|
const m = createEmpty();
|
|
addEntry(m, sampleEntry('sig-x'));
|
|
markDeleted(m, 1);
|
|
expect(m.entries['1']).toBeUndefined();
|
|
expect(m.deleted['1'].lastSignature).toBe('sig-x');
|
|
expect(m.lastId).toBe(1); // monotonic, not decremented
|
|
});
|
|
|
|
it('deleted ids are not reused by addEntry', () => {
|
|
const m = createEmpty();
|
|
addEntry(m, sampleEntry('sig-x'));
|
|
markDeleted(m, 1);
|
|
const id = addEntry(m, sampleEntry('sig-y'));
|
|
expect(id).toBe(2);
|
|
});
|
|
|
|
it('saveManifest then loadManifest roundtrips data', () => {
|
|
const dir = mkdtempSync(join(tmpdir(), 'dx-'));
|
|
const path = join(dir, 'm.json');
|
|
const m = createEmpty();
|
|
addEntry(m, sampleEntry('sig-1'));
|
|
saveManifest(path, m);
|
|
const loaded = loadManifest(path);
|
|
expect(loaded.lastId).toBe(1);
|
|
expect(loaded.entries['1'].signature).toBe('sig-1');
|
|
rmSync(dir, { recursive: true });
|
|
});
|
|
|
|
it('loadManifest returns createEmpty() if file missing', () => {
|
|
const m = loadManifest('/nonexistent/path-' + Math.random() + '.json');
|
|
expect(m.lastId).toBe(0);
|
|
});
|
|
|
|
it('saveManifest writes atomically (no partial file on crash)', () => {
|
|
const dir = mkdtempSync(join(tmpdir(), 'dx-'));
|
|
const path = join(dir, 'm.json');
|
|
const m = createEmpty();
|
|
addEntry(m, sampleEntry('sig-x'));
|
|
saveManifest(path, m);
|
|
// Atomic write should leave no .tmp residue
|
|
expect(() => readFileSync(path + '.tmp')).toThrow();
|
|
rmSync(dir, { recursive: true });
|
|
});
|
|
|
|
it('loadManifest throws on corrupt JSON with actionable message', () => {
|
|
const dir = mkdtempSync(join(tmpdir(), 'dx-'));
|
|
const path = join(dir, 'm.json');
|
|
writeFileSync(path, '{not valid json', 'utf8');
|
|
expect(() => loadManifest(path)).toThrow(/corrupt JSON/);
|
|
rmSync(dir, { recursive: true });
|
|
});
|
|
|
|
it('loadManifest throws on unsupported version', () => {
|
|
const dir = mkdtempSync(join(tmpdir(), 'dx-'));
|
|
const path = join(dir, 'm.json');
|
|
writeFileSync(path, JSON.stringify({ version: 2, lastId: 0, entries: {}, deleted: {} }), 'utf8');
|
|
expect(() => loadManifest(path)).toThrow(/version 2 unsupported/);
|
|
rmSync(dir, { recursive: true });
|
|
});
|
|
|
|
it('markDeleted is a no-op for unknown ids (does not throw)', () => {
|
|
const m = createEmpty();
|
|
expect(() => markDeleted(m, 9999)).not.toThrow();
|
|
expect(m.deleted['9999']).toBeUndefined();
|
|
expect(m.lastId).toBe(0);
|
|
});
|
|
});
|