diff --git a/packages/cultcache-ts/test/decode-document-payload.test.ts b/packages/cultcache-ts/test/decode-document-payload.test.ts index 92fd2146..a63859ee 100644 --- a/packages/cultcache-ts/test/decode-document-payload.test.ts +++ b/packages/cultcache-ts/test/decode-document-payload.test.ts @@ -31,3 +31,9 @@ test("a payload is read by its definition's formatter, a codec's, or as a plain // Without the definition the codec payload is its positional array, which is why a caller that holds one passes it. assert.deepEqual(decodeDocumentPayload(undefined, encode(["a", 1])), ["a", 1]); }); + +test("a definition that declares both a codec and a formatter is refused where its payload is read", () => { + const both = { ...codec, formatter: formatted.formatter } as typeof codec; + assert.throws(() => decodeDocumentPayload(both, encode(["a", 1])), (error: Error) => + error.message === 'CultCache document type "t.codec" declares both a codec and a formatter.'); +}); diff --git a/packages/cultmesh-browser/test/cultmesh-browser.test.ts b/packages/cultmesh-browser/test/cultmesh-browser.test.ts index 61b560c2..fb65fbf3 100644 --- a/packages/cultmesh-browser/test/cultmesh-browser.test.ts +++ b/packages/cultmesh-browser/test/cultmesh-browser.test.ts @@ -982,3 +982,8 @@ test("decodeCultNetPayload reads a payload through the formatter it is given, an assert.deepEqual(decodeCultNetPayload<{ count: number }>(record, formatter), { count: 7 }); assert.deepEqual(decodeCultNetPayload(record), [7]); }); + +test("decodeCultNetPayload refuses a payload that is not messagepack, naming the encoding and not the payload", () => { + const record = { schemaId: "t.v0", recordKey: "k", payloadEncoding: "json", payload: encodeWireValue("secret-value") } as unknown as CultNetRawDocumentRecord; + assert.throws(() => decodeCultNetPayload(record), (error: Error) => error.message === "Unsupported CultNet document payload 'json'."); +}); diff --git a/packages/cultmesh-browser/test/payload-decode-owner.test.ts b/packages/cultmesh-browser/test/payload-decode-owner.test.ts index 5ed8f915..e5a23f78 100644 --- a/packages/cultmesh-browser/test/payload-decode-owner.test.ts +++ b/packages/cultmesh-browser/test/payload-decode-owner.test.ts @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; -import { readdirSync, readFileSync } from "node:fs"; -import { join } from "node:path"; +import { existsSync, readdirSync, readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import test from "node:test"; @@ -10,7 +10,11 @@ import test from "node:test"; // Limits: a text scan. It reads the package's src files for the module specifier and the manifest for the dependency // name. It does not follow a re-export from another package, a specifier assembled at run time, or a hand-written // decoder; the dependency absence is what makes the specifier unresolvable here when the workspace does not hoist it. -const root = fileURLToPath(new URL("../../", import.meta.url)); +// The package root: the nearest directory above this file (compiled or run in place) that holds a package.json. +function packageRoot(from: string): string { + return existsSync(join(from, "package.json")) ? from : packageRoot(dirname(from)); +} +const root = packageRoot(dirname(fileURLToPath(import.meta.url))); function sourceFiles(dir: string): string[] { return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => @@ -23,7 +27,7 @@ test("no source file imports msgpack and the manifest does not list it", () => { assert.equal(manifest[section]?.["@msgpack/msgpack"], undefined, section); } const files = sourceFiles(join(root, "src")); - assert.ok(files.length > 5, "fixture: the scan reads the package's sources"); + assert.ok(files.length >= 1, "fixture: the scan reads the package's sources"); const offenders = files.filter((file) => /@msgpack\/msgpack/.test(readFileSync(file, "utf8"))); assert.deepEqual(offenders, []); }); diff --git a/packages/cultmesh-ts/test/payload-decode-owner.test.ts b/packages/cultmesh-ts/test/payload-decode-owner.test.ts index e267a9c2..f6471bb4 100644 --- a/packages/cultmesh-ts/test/payload-decode-owner.test.ts +++ b/packages/cultmesh-ts/test/payload-decode-owner.test.ts @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; -import { readdirSync, readFileSync } from "node:fs"; -import { join } from "node:path"; +import { existsSync, readdirSync, readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; import test from "node:test"; // A document payload is decoded by cultcache-ts alone (decodeDocumentPayload); every other value on a wire goes through @@ -9,7 +9,11 @@ import test from "node:test"; // Limits: a text scan. It reads the package's src files for the module specifier and the manifest for the dependency // name. It does not follow a re-export from another package, a specifier assembled at run time, or a hand-written // decoder; the dependency absence is what makes the specifier unresolvable here when the workspace does not hoist it. -const root = join(__dirname, "..", ".."); +// The package root: the nearest directory above this file (compiled or run in place) that holds a package.json. +function packageRoot(from: string): string { + return existsSync(join(from, "package.json")) ? from : packageRoot(dirname(from)); +} +const root = packageRoot(__dirname); function sourceFiles(dir: string): string[] { return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => @@ -22,7 +26,7 @@ test("no source file imports msgpack and the manifest does not list it", () => { assert.equal(manifest[section]?.["@msgpack/msgpack"], undefined, section); } const files = sourceFiles(join(root, "src")); - assert.ok(files.length > 5, "fixture: the scan reads the package's sources"); + assert.ok(files.length >= 1, "fixture: the scan reads the package's sources"); const offenders = files.filter((file) => /@msgpack\/msgpack/.test(readFileSync(file, "utf8"))); assert.deepEqual(offenders, []); });