Add Velum project workflow and standalone migration tool
This commit is contained in:
47
tests/migration.test.ts
Normal file
47
tests/migration.test.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { afterEach, expect, it, mock } from "bun:test";
|
||||
import { promises as fs } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { createHash } from "node:crypto";
|
||||
import { migrate } from "../tools/migrate-ki-datenschutz.mjs";
|
||||
|
||||
mock.module("@raycast/api", () => ({ environment: { supportPath: tmpdir() } }));
|
||||
const { restorePath, restoreText } = await import("../src/lib/velum-project");
|
||||
const roots: string[] = [];
|
||||
afterEach(async () => { await Promise.all(roots.splice(0).map((root) => fs.rm(root, { recursive: true, force: true }))); });
|
||||
|
||||
it("migriert einen künstlichen Altbestand verlustarm ohne den Quellordner zu ändern", async () => {
|
||||
const root = await fs.mkdtemp(join(tmpdir(), "velum-legacy-test-"));
|
||||
roots.push(root);
|
||||
const project = join(root, "Projekt");
|
||||
const targetSupport = join(root, "VelumSupport");
|
||||
const oldSupport = join(root, "OldSupport");
|
||||
await fs.mkdir(project);
|
||||
await fs.mkdir(join(oldSupport, "projects"), { recursive: true });
|
||||
const legacyName = "KUNDE ALPHA.txt";
|
||||
const legacyContent = "kundeAlphaService und KUNDE ALPHA";
|
||||
await fs.writeFile(join(project, legacyName), legacyContent);
|
||||
const projectId = createHash("sha1").update(project).digest("hex").slice(0, 16);
|
||||
const manifestFile = join(oldSupport, "projects", `${projectId}.json`);
|
||||
await fs.writeFile(manifestFile, JSON.stringify({
|
||||
version: 1, projectPath: project, projectTerms: [{ id: "t1", term: "Müller GmbH", category: "Kunde", enabled: true, aliases: [] }],
|
||||
variantTable: [
|
||||
{ termId: "t1", termVariant: "Müller GmbH", placeholderVariant: "KUNDE ALPHA", style: "typed", boundary: "strict" },
|
||||
{ termId: "t1", termVariant: "muellerGmbh", placeholderVariant: "kundeAlpha", style: "camel", boundary: "identifier" },
|
||||
],
|
||||
}));
|
||||
const preview = await migrate({ project, manifestFile, targetSupport });
|
||||
expect(preview.applied).toBe(false);
|
||||
expect(preview.summary.files).toBe(1);
|
||||
expect(await fs.readFile(join(project, legacyName), "utf8")).toBe(legacyContent);
|
||||
expect(await fs.readdir(root)).not.toContain("VelumSupport");
|
||||
|
||||
const result = await migrate({ project, manifestFile, targetSupport, apply: true });
|
||||
expect(result.applied).toBe(true);
|
||||
const run = JSON.parse(await fs.readFile(join(result.runDir!, "run.json"), "utf8"));
|
||||
const file = run.files[0];
|
||||
const content = await fs.readFile(join(result.runDir!, "files", file.output), "utf8");
|
||||
expect(restoreText(content, run.mapping, run.matchModes)).toBe("muellerGmbhService und Müller GmbH");
|
||||
expect(restorePath(file.output, run.mapping, run.matchModes)).toBe("Müller GmbH.txt");
|
||||
expect(await fs.readFile(join(project, legacyName), "utf8")).toBe(legacyContent);
|
||||
});
|
||||
47
tests/velum-project.test.ts
Normal file
47
tests/velum-project.test.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { afterEach, describe, expect, it, mock } from "bun:test";
|
||||
import { promises as fs } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
mock.module("@raycast/api", () => ({ environment: { supportPath: tmpdir() } }));
|
||||
const project = await import("../src/lib/velum-project");
|
||||
const roots: string[] = [];
|
||||
afterEach(async () => { await Promise.all(roots.splice(0).map((root) => fs.rm(root, { recursive: true, force: true }))); });
|
||||
|
||||
describe("Velum-Projektstände", () => {
|
||||
it("speichert Projektregeln und Bearbeitungsstände getrennt vom Quellordner", async () => {
|
||||
const root = await fs.mkdtemp(join(tmpdir(), "velum-project-test-"));
|
||||
roots.push(root);
|
||||
const source = join(root, "Quelle");
|
||||
const support = join(root, "Raycast");
|
||||
await fs.mkdir(source);
|
||||
await fs.writeFile(join(source, "Notiz.txt"), "Max Mustermann");
|
||||
const rules = project.parseProjectRules("PERSON: Max Mustermann\nKundenSuite");
|
||||
await project.saveProjectRules(source, rules, support);
|
||||
expect(await project.loadProjectRules(source, support)).toEqual(rules);
|
||||
const created = await project.createRun(source, support);
|
||||
created.run.mapping = { PERSON_01: { original: "Max Mustermann", type: "PERSON" } };
|
||||
created.run.files.push({ source: "Notiz.txt", output: "PERSON_01.txt", kind: "text" });
|
||||
await project.saveRun(created.directory, created.run);
|
||||
expect((await project.listRuns(source, support))[0].run.files[0].output).toBe("PERSON_01.txt");
|
||||
expect(await fs.readFile(join(source, "Notiz.txt"), "utf8")).toBe("Max Mustermann");
|
||||
});
|
||||
|
||||
it("ersetzt nur bestätigte Vorkommen und stellt Dateinamen wieder her", () => {
|
||||
const hits = [
|
||||
{ id: "a", start: 0, end: 3, placeholder: "PERSON_01" },
|
||||
{ id: "b", start: 8, end: 11, placeholder: "PERSON_01" },
|
||||
];
|
||||
expect(project.replaceSpans("Max und Max", hits, new Set(["a"]))).toBe("PERSON_01 und Max");
|
||||
expect(project.restorePath("PERSON_01_Bericht.txt", {
|
||||
PERSON_01: { original: "Max Mustermann", type: "PERSON" },
|
||||
})).toBe("Max Mustermann_Bericht.txt");
|
||||
expect(() => project.safeRelativePath("../geheim.txt")).toThrow();
|
||||
});
|
||||
|
||||
it("übernimmt API-Trefferpositionen auch nach einem Emoji korrekt", () => {
|
||||
const text = "😀 Max";
|
||||
const spans = project.toUtf16Offsets(text, [{ id: "max", start: 2, end: 5, placeholder: "PERSON_01" }]);
|
||||
expect(project.replaceSpans(text, spans, new Set(["max"]))).toBe("😀 PERSON_01");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user