Files
suaveplan/packages/networking/drive/drive-template/tests/template-malformed.test.ts
2026-05-27 23:47:53 +01:00

158 lines
4.8 KiB
TypeScript

/**
* DTP-02 / DTP-09 / DTP-26 — schema-version pinning + per-file
* consistency invariants + structured error context.
*/
import { describe, expect, it } from "@suaveplan/testing/runner";
import type {
DriveTemplate,
FileTemplate,
FolderTemplate,
} from "../src/index.js";
import {
assertFile,
assertFolderFileInvariants,
type DriveTemplateError,
DTP_CODES,
TemplateNonceLru,
validateTemplate,
} from "../src/index.js";
import { DEFAULT_POLICY, makeKeys, signed } from "./_helpers.js";
const baseFile: FileTemplate = {
name: "x.bin",
fileLocId: "loc-1",
fileId: "f".repeat(64),
size: 100,
totalChunks: 1,
chunkSize: 1024,
checksum: "blake3:abcd",
flags: DEFAULT_POLICY,
manifestKey: "manifest:f",
manifestSig: new Uint8Array(64).fill(1),
};
describe("DTP-02 — schema version pinned to 1", () => {
it("schemaVersion ≠ 1 rejected with TEMPLATE_VERSION_UNSUPPORTED", {
timeout: 5000,
}, () => {
const keys = makeKeys();
const tpl = signed({ issuer: keys.publicHex }, keys);
// Cast through unknown to bypass the literal-1 type guard.
const broken = { ...tpl, schemaVersion: 2 } as unknown as DriveTemplate;
try {
validateTemplate(broken, {
clock: () => Date.now(),
nonceLru: new TemplateNonceLru(),
});
throw new Error("expected VERSION_UNSUPPORTED");
} catch (err) {
expect((err as DriveTemplateError).code).toBe(
DTP_CODES.TEMPLATE_VERSION_UNSUPPORTED
);
}
});
});
describe("DTP-09 — per-file consistency invariants", () => {
it("chunkSize 164 rejected", { timeout: 5000 }, () => {
try {
assertFile({ ...baseFile, chunkSize: 164 }, "/x.bin");
throw new Error("expected MALFORMED");
} catch (err) {
expect((err as DriveTemplateError).code).toBe(
DTP_CODES.TEMPLATE_MALFORMED
);
expect((err as DriveTemplateError).context).toMatchObject({
file: "/x.bin",
});
}
});
it("totalChunks insufficient for size rejected", { timeout: 5000 }, () => {
try {
assertFile(
{ ...baseFile, totalChunks: 1, chunkSize: 1024, size: 4096 },
"/x.bin"
);
throw new Error("expected MALFORMED");
} catch (err) {
expect((err as DriveTemplateError).code).toBe(
DTP_CODES.TEMPLATE_MALFORMED
);
}
});
it("valid file passes through assertFile (positive path)", {
timeout: 5000,
}, () => {
expect(() => assertFile(baseFile, "/x.bin")).not.toThrow();
});
it("empty manifestKey rejected", { timeout: 5000 }, () => {
try {
assertFile({ ...baseFile, manifestKey: "" }, "/x.bin");
throw new Error("expected MALFORMED");
} catch (err) {
expect((err as DriveTemplateError).code).toBe(
DTP_CODES.TEMPLATE_MALFORMED
);
}
});
it("malformed checksum rejected", { timeout: 5000 }, () => {
try {
assertFile({ ...baseFile, checksum: "md5:abc" }, "/x.bin");
throw new Error("expected MALFORMED");
} catch (err) {
expect((err as DriveTemplateError).code).toBe(
DTP_CODES.TEMPLATE_MALFORMED
);
}
});
it("empty manifestSig rejected", { timeout: 5000 }, () => {
try {
assertFile(
{ ...baseFile, manifestSig: new Uint8Array(0) },
"/x.bin"
);
throw new Error("expected MALFORMED");
} catch (err) {
expect((err as DriveTemplateError).code).toBe(
DTP_CODES.TEMPLATE_MALFORMED
);
}
});
it("nested folder: deepest file's failure surfaces with full path", {
timeout: 5000,
}, () => {
const tree: FolderTemplate = {
name: "/",
children: [
{
name: "photos",
children: [
{
name: "wedding",
children: [{ ...baseFile, chunkSize: 164 }],
},
],
},
],
};
try {
assertFolderFileInvariants(tree);
throw new Error("expected MALFORMED");
} catch (err) {
expect((err as DriveTemplateError).code).toBe(
DTP_CODES.TEMPLATE_MALFORMED
);
const ctx = (err as DriveTemplateError).context as { file: string };
expect(ctx.file).toContain("photos");
expect(ctx.file).toContain("wedding");
}
});
});