beatsad_Vault2Dify/tests/sync-utils.test.mjs

222 lines
7.8 KiB
JavaScript
Raw Permalink Normal View History

2026-06-14 12:02:40 +00:00
import assert from "node:assert/strict";
import { build } from "esbuild";
import { pathToFileURL } from "node:url";
const outfile = "tests/.tmp-sync-utils.mjs";
await build({
entryPoints: ["src/sync-utils.ts"],
bundle: true,
platform: "node",
format: "esm",
outfile,
logLevel: "silent",
});
const utils = await import(pathToFileURL(`${process.cwd()}/${outfile}`).href);
function test(name, fn) {
try {
fn();
console.log(`ok - ${name}`);
} catch (error) {
console.error(`not ok - ${name}`);
throw error;
}
}
test("sanitizeBaseUrl trims spaces, trailing slashes, and known Dify API paths", () => {
assert.equal(utils.sanitizeBaseUrl(" https://dify.example.com/v1/ "), "https://dify.example.com");
assert.equal(utils.sanitizeBaseUrl("https://dify.example.com/v1/datasets"), "https://dify.example.com");
assert.equal(utils.sanitizeBaseUrl("http://dify-host.example.test:5000///"), "http://dify-host.example.test:5000");
});
test("sanitizeBaseUrl adds http when the scheme is omitted", () => {
assert.equal(utils.sanitizeBaseUrl("dify-host.example.test:5000"), "http://dify-host.example.test:5000");
assert.equal(utils.sanitizeBaseUrl(""), "");
});
test("normalizeFolderPath removes surrounding spaces and slashes", () => {
assert.equal(utils.normalizeFolderPath(" /Work/ProjectA/ "), "Work/ProjectA");
assert.equal(utils.normalizeFolderPath(""), "");
});
test("pathMatchesFolder treats the empty folder as the whole vault", () => {
assert.equal(utils.pathMatchesFolder("Work/ProjectA/Note.md", ""), true);
});
test("pathMatchesFolder matches the exact folder and descendants only", () => {
assert.equal(utils.pathMatchesFolder("Work/ProjectA", "Work/ProjectA"), true);
assert.equal(utils.pathMatchesFolder("Work/ProjectA/Note.md", "Work/ProjectA"), true);
assert.equal(utils.pathMatchesFolder("Work/ProjectArchive/Note.md", "Work/ProjectA"), false);
});
test("pathMatchesFolder supports single Markdown note mappings", () => {
assert.equal(utils.pathMatchesFolder("Work/Note.md", "Work/Note.md"), true);
assert.equal(utils.pathMatchesFolder("Work/Other.md", "Work/Note.md"), false);
assert.equal(utils.pathMatchesFolder("Work/Note.md/Child.md", "Work/Note.md"), false);
});
test("splitIds supports English and Chinese separators and removes duplicates", () => {
assert.deepEqual(utils.splitIds(" kb1,kb2kb1\nkb3kb4;kb2 "), ["kb1", "kb2", "kb3", "kb4"]);
});
test("clampNumber keeps values within range and falls back for invalid input", () => {
assert.equal(utils.clampNumber(10, 1, 20, 5), 10);
assert.equal(utils.clampNumber(0, 1, 20, 5), 1);
assert.equal(utils.clampNumber(25, 1, 20, 5), 20);
assert.equal(utils.clampNumber(Number.NaN, 1, 20, 5), 5);
});
test("getRecordKey combines dataset id and file path without losing slashes", () => {
assert.equal(utils.getRecordKey("dataset-1", "Work/ProjectA/Note.md"), "dataset-1::Work/ProjectA/Note.md");
});
test("migrateRenamedSyncRecords preserves old remote name so Dify can be renamed", () => {
const records = new Map([
[utils.getRecordKey("dataset-1", "Work/Old.md"), {
filePath: "Work/Old.md",
datasetId: "dataset-1",
documentId: "doc-1",
remoteName: "Work/Old.md",
hash: "hash-1",
lastModified: 100,
lastSyncedAt: "2026-06-07T03:00:00.000Z",
}],
]);
const changed = utils.migrateRenamedSyncRecords(records, "Work/Old.md", "Work/New.md", (path) => path);
assert.equal(changed, true);
assert.equal(records.has(utils.getRecordKey("dataset-1", "Work/Old.md")), false);
assert.deepEqual(records.get(utils.getRecordKey("dataset-1", "Work/New.md")), {
filePath: "Work/New.md",
datasetId: "dataset-1",
documentId: "doc-1",
remoteName: "Work/Old.md",
hash: "hash-1",
lastModified: 100,
lastSyncedAt: "2026-06-07T03:00:00.000Z",
deletedLocal: false,
});
});
test("recoverDeletedSyncRecordForCreatedFile reuses a recently deleted same-content document", () => {
const records = new Map([
[utils.getRecordKey("dataset-1", "Work/Old.md"), {
filePath: "Work/Old.md",
datasetId: "dataset-1",
documentId: "doc-1",
remoteName: "Work/Old.md",
hash: "same-hash",
lastModified: 100,
lastSyncedAt: "2026-06-07T03:00:10.000Z",
deletedLocal: true,
}],
]);
const recovered = utils.recoverDeletedSyncRecordForCreatedFile(
records,
"dataset-1",
"Work/New.md",
"same-hash",
(path) => path,
new Date("2026-06-07T03:00:20.000Z"),
30000,
);
assert.equal(recovered, true);
assert.equal(records.has(utils.getRecordKey("dataset-1", "Work/Old.md")), false);
assert.deepEqual(records.get(utils.getRecordKey("dataset-1", "Work/New.md")), {
filePath: "Work/New.md",
datasetId: "dataset-1",
documentId: "doc-1",
remoteName: "Work/Old.md",
hash: "same-hash",
lastModified: 100,
lastSyncedAt: "2026-06-07T03:00:10.000Z",
deletedLocal: false,
});
});
test("getDatasetIdsForPathFromMappings returns enabled matching datasets only", () => {
const mappings = [
{ folder: "Work", datasetIds: ["kb-work"], enabled: true },
{ folder: "Work/Archive", datasetIds: ["kb-archive"], enabled: false },
{ folder: "Reading", datasetIds: ["kb-reading"], enabled: true },
];
assert.deepEqual(utils.getDatasetIdsForPathFromMappings("Work/Note.md", mappings), ["kb-work"]);
assert.deepEqual(utils.getDatasetIdsForPathFromMappings("Reading/Book.md", mappings), ["kb-reading"]);
assert.deepEqual(utils.getDatasetIdsForPathFromMappings("Inbox/Note.md", mappings), []);
});
test("getDatasetIdsForPathFromMappings supports exact note mappings", () => {
const mappings = [
{ folder: "Work/Note.md", datasetIds: ["kb-note"], enabled: true },
{ folder: "Work", datasetIds: ["kb-work"], enabled: true },
];
assert.deepEqual(utils.getDatasetIdsForPathFromMappings("Work/Note.md", mappings), ["kb-note", "kb-work"]);
assert.deepEqual(utils.getDatasetIdsForPathFromMappings("Work/Other.md", mappings), ["kb-work"]);
});
test("getDatasetIdsForPathFromMappings deduplicates datasets across matching mappings", () => {
const mappings = [
{ folder: "", datasetIds: ["kb-all", "kb-shared"], enabled: true },
{ folder: "Work", datasetIds: ["kb-shared", "kb-work"], enabled: true },
];
assert.deepEqual(utils.getDatasetIdsForPathFromMappings("Work/Note.md", mappings), ["kb-all", "kb-shared", "kb-work"]);
});
test("removeMappingById only removes the local mapping entry", () => {
const mappings = [
{ id: "map-1", folder: "Work", datasetIds: ["kb-work"], enabled: true },
{ id: "map-2", folder: "Reading", datasetIds: ["kb-reading"], enabled: true },
];
const removed = utils.removeMappingById(mappings, "map-1");
assert.deepEqual(removed, [
{ id: "map-2", folder: "Reading", datasetIds: ["kb-reading"], enabled: true },
]);
assert.equal(mappings.length, 2);
assert.equal(typeof utils.deleteDocument, "undefined");
});
test("buildSettingsReviewSummary returns zero metrics and muted state before any sync", () => {
const summary = utils.buildSettingsReviewSummary({
connectionHealth: { status: "unknown" },
});
assert.deepEqual(summary.metrics, [
{ key: "total", value: 0, tone: "accent" },
{ key: "synced", value: 0, tone: "success" },
{ key: "skipped", value: 0, tone: "warning" },
{ key: "failed", value: 0, tone: "error" },
]);
assert.equal(summary.connectionTone, "muted");
assert.equal(summary.hasRecentSync, false);
});
test("buildSettingsReviewSummary maps recent sync and connection health into review tones", () => {
const summary = utils.buildSettingsReviewSummary({
connectionHealth: { status: "connected" },
recentSyncResult: {
total: 7,
synced: 4,
skipped: 2,
failed: 1,
},
});
assert.deepEqual(summary.metrics, [
{ key: "total", value: 7, tone: "accent" },
{ key: "synced", value: 4, tone: "success" },
{ key: "skipped", value: 2, tone: "warning" },
{ key: "failed", value: 1, tone: "error" },
]);
assert.equal(summary.connectionTone, "success");
assert.equal(summary.hasRecentSync, true);
});