beatsad_Vault2Dify/tests/sync-utils.test.mjs
2026-06-14 20:02:40 +08:00

221 lines
7.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
});