import { afterEach, beforeEach, describe, it } from "@std/testing/bdd";
import { expect } from "@std/expect";
import { Identity } from "@commonfabric/identity";
import { StorageManager } from "../src/storage/cache.deno.ts";
import type { RuntimeProgram } from "../src/harness/types.ts";
import { Runtime } from "../src/runtime.ts";
import { runtimePresets } from "../src/runtime-presets.ts";
import { setCfcImplementationIdentity } from "../src/cfc/trust-authority.ts";
import type { Cell } from "../src/cell.ts";
import type { JSONSchema } from "../src/builder/types.ts";
// A document stores its policy envelope from the schema that defines it: a
// pattern's argument from the pattern's input schema, a created cell from the
// cell's. A writer policy that schema reached where no syntax names the
// writer would leave the document with no stored write restriction at all,
// writable by any handler and by any other code holding WRITE on the space.
// Each such shape must either keep its writer, so that only the named
// handler writes the field, or fail compilation.
const signer = await Identity.fromPassphrase(
"cfc-writer-claim-document-shapes",
);
const space = signer.did();
/**
* A program whose default pattern returns `value`, a field protected by
* `writerA`'s policy, with `setA` and `setB` writing it through `writerA` and
* through `writerB`, a handler of the same shape that the policy does not
* name. `head` declares the pattern and its input; `body` binds `value`;
* `returned` is what the pattern returns, those three by default.
*/
const program = (
head: string,
body: string,
returned = "{ value, setA: writerA({ value }), setB: writerB({ value }) }",
): RuntimeProgram => ({
main: "/main.tsx",
files: [{
name: "/main.tsx",
contents: `///
import { type Cfc, computed, type CurrentPrincipal, handler, pattern, type RepresentsCurrentUser, type Stream, Writable, type WriteAuthorizedBy } from "commonfabric";
type Policy = WriteAuthorizedBy;
type Owned = RepresentsCurrentUser, { ownerPrincipal: CurrentPrincipal }>>;
export const writerA = handler<{ v: string }, { value: Writable }>(
({ v }, { value }) => { value.set(v); },
);
export const writerB = handler<{ v: string }, { value: Writable }>(
({ v }, { value }) => { value.set(v); },
);
${head}
${body}
return ${returned};
});
`,
}],
});
/**
* What became of the policy: compilation failed on the writer it could not
* read, only the named writer writes the field, or other writers do too.
*/
type Outcome = "refused" | "enforced" | "open";
describe("a writer policy a document's schema reaches", () => {
let storageManager: ReturnType;
let runtime: Runtime;
beforeEach(() => {
storageManager = StorageManager.emulate({ as: signer });
runtime = new Runtime(runtimePresets.patternTest({
apiUrl: new URL(import.meta.url),
storageManager,
experimental: {},
}));
});
afterEach(async () => {
await runtime?.dispose();
await storageManager?.close();
});
const outcomeOf = async (
source: RuntimeProgram,
name: string,
): Promise => {
const tx = runtime.edit();
let pattern;
try {
pattern = await runtime.patternManager.compilePattern(source, {
space,
tx,
});
} catch (error) {
tx.abort();
expect(String(error)).toContain("could not be read");
return "refused";
}
const cell = runtime.getCell<{ value: string }>(space, name, undefined, tx);
const running = runtime.run(tx, pattern, {}, cell);
runtime.prepareTxForCommit(tx);
expect((await tx.commit().settled).error).toBeUndefined();
await running.pull();
await runtime.idle();
const value = running.key("value") as Cell;
const send = async (stream: "setA" | "setB", v: string) => {
(running.key(stream) as unknown as { send: (e: unknown) => void }).send({
v,
});
await runtime.idle();
await running.pull();
await runtime.idle();
};
// Other code holding WRITE on the space, writing the document the field
// resolves to by its bare link, through a schema that declares nothing.
const { schema: _schema, ...bare } = value.resolveAsCell()
.getAsNormalizedFullLink();
const member = await runtime.editWithRetry((memberTx) => {
setCfcImplementationIdentity(memberTx, {
kind: "verified",
moduleIdentity: "sha256:member-code",
symbol: "member",
bindingPath: ["member"],
});
runtime.getCellFromLink(bare, { type: "string" } as JSONSchema, memberTx)
.set("from member code");
});
await send("setB", "from writerB");
const afterOthers = value.get();
await send("setA", "from writerA");
expect(value.get()).toBe("from writerA");
return member.error === undefined || afterOthers === "from member code" ||
afterOthers === "from writerB"
? "open"
: "enforced";
};
/**
* Sets up `source` in a result cell named `name`, and returns the error, if
* any, of other code holding WRITE on the space writing the field at `path`
* of the result by its bare link.
*/
const memberWriteError = async (
source: RuntimeProgram,
name: string,
path: readonly string[],
): Promise => {
const tx = runtime.edit();
const pattern = await runtime.patternManager.compilePattern(source, {
space,
tx,
});
const cell = runtime.getCell(space, name, undefined, tx);
const running = runtime.run(tx, pattern, {}, cell);
runtime.prepareTxForCommit(tx);
expect((await tx.commit().settled).error).toBeUndefined();
await running.pull();
await runtime.idle();
const field = path.reduce(
(target: Cell, key) => target.key(key) as Cell,
running as Cell,
);
const { schema: _schema, ...bare } = field.resolveAsCell()
.getAsNormalizedFullLink();
const member = await runtime.editWithRetry((memberTx) => {
setCfcImplementationIdentity(memberTx, {
kind: "verified",
moduleIdentity: "sha256:member-code",
symbol: "member",
bindingPath: ["member"],
});
runtime.getCellFromLink(bare, { type: "string" } as JSONSchema, memberTx)
.set("from member code");
});
return member.error?.message;
};
it("enforces a policy written on the input's own field", async () => {
expect(
await outcomeOf(
program(
"export default pattern<{ value: Policy }>((input) => {",
"const value = input.value;",
),
"direct-field",
),
).toBe("enforced");
});
it("enforces a policy written as a created cell's type argument", async () => {
expect(
await outcomeOf(
program(
"export default pattern<{ initial: string }>((input) => {",
'const value = new Writable("seed").for("value");',
),
"explicit-cell",
),
).toBe("enforced");
});
const shapes: Record = {
"a generic alias": program(
`type Box = { value: T };
export default pattern>>((input) => {`,
"const value = input.value;",
),
"an interface's generic base": program(
`interface Base { value: T }
interface In extends Base> {}
export default pattern((input) => {`,
"const value = input.value;",
),
"`Record`": program(
"export default pattern<{ byId: Record> }>((input) => {",
'const value = input.byId["k"];',
),
"an index signature": program(
"export default pattern<{ byId: { [key: string]: WriteAuthorizedBy } }>((input) => {",
'const value = input.byId["k"];',
),
"a tuple": program(
"export default pattern<{ pair: [WriteAuthorizedBy] }>((input) => {",
"const value = input.pair[0];",
),
"an owner policy in a generic alias": program(
`type Box = { value: T };
export default pattern>>((input) => {`,
"const value = input.value;",
),
"a created cell's value": program(
"export default pattern<{ initial: string }>((input) => {",
`const seed = "seed" as WriteAuthorizedBy;
const value = new Writable(seed).for("value");`,
),
};
for (const [reach, source] of Object.entries(shapes)) {
it(`enforces or refuses a policy reached through ${reach}`, async () => {
expect(await outcomeOf(source, reach)).not.toBe("open");
});
}
it("refuses fresh data an inferred result holds under a writer policy read from its type", async () => {
// The result document holds that data itself, so it would store no write
// restriction for it.
const tx = runtime.edit();
await expect(
runtime.patternManager.compilePattern(
program(
"export default pattern<{ initial: string }>((input) => {",
'const value = "seed" as WriteAuthorizedBy;',
"{ value }",
),
{ space, tx },
),
).rejects.toThrow("could not be read");
tx.abort();
});
describe("fresh data an inferred result returns under an owner policy", () => {
// The result document holds that data itself, whatever shape the object
// returning it takes, so it stores the whole policy or compilation fails.
for (
const [shape, body, returned] of [
[
"a spread object",
"",
'{ ...{ value: "seed" as Owned } }',
],
[
"a spread constant",
'const fresh = { value: "seed" as Owned };',
"{ ...fresh }",
],
[
"a static computed key",
"",
'{ box: { ["value"]: "seed" as Owned } }',
],
[
"an array spread",
"",
'{ list: [...["seed" as Owned]] }',
],
[
"twenty nested objects",
"",
`{ box: ${
"{ nested: ".repeat(20)
}"seed" as Owned${" }".repeat(20)} }`,
],
] as const
) {
it(`refuses it through ${shape}`, async () => {
const tx = runtime.edit();
await expect(
runtime.patternManager.compilePattern(
program(
"export default pattern<{ initial: string }>((input) => {",
body,
returned,
),
{ space, tx },
),
).rejects.toThrow("could not be read");
tx.abort();
});
}
for (
const returned of ["{ viewed, fresh }", "{ fresh, viewed }"] as const
) {
it(`refuses it returned as ${returned}, beside a view of the same named type`, async () => {
const tx = runtime.edit();
await expect(
runtime.patternManager.compilePattern(
program(
`interface Erased { value: [T][0] }
interface Item { box: Erased> }
export default pattern<{ initial: string }>((input) => {`,
`const viewed = computed(() => ({ box: { value: "seed" } }) as Item);
const fresh = { box: { value: "seed" } } as Item;`,
returned,
),
{ space, tx },
),
).rejects.toThrow("could not be read");
tx.abort();
});
}
for (
const [where, body, returned] of [
[
"in the result a map callback returns",
"",
'{ list: items.map((item) => ({ item, value: "seed" as Owned })) }',
],
[
"in the result a map callback returns, where a computed reads the list",
'const list = items.map((item) => ({ item, value: "seed" as Owned }));',
"{ first: computed(() => list[0]) }",
],
] as const
) {
it(`refuses it ${where}`, async () => {
// The element pattern's result is a document of its own.
const tx = runtime.edit();
await expect(
runtime.patternManager.compilePattern(
program(
"export default pattern<{ items: string[] }>(({ items }) => {",
body,
returned,
),
{ space, tx },
),
).rejects.toThrow("could not be read");
tx.abort();
});
}
it("refuses it beside a view of a type named as its definition would be", async () => {
const tx = runtime.edit();
await expect(
runtime.patternManager.compilePattern(
program(
`interface Erased { value: [T][0] }
interface Item { box: Erased> }
interface Item_document { box: Erased> }
export default pattern<{ initial: string }>((input) => {`,
`const viewed = computed(() => ({ box: { value: "seed" } }) as Item_document);
const fresh = { box: { value: "seed" } } as Item;`,
"{ viewed, fresh }",
),
{ space, tx },
),
).rejects.toThrow("could not be read");
tx.abort();
});
it("stores the whole policy through a constant whose declared type is an index signature, which refuses other writers", async () => {
expect(
await memberWriteError(
program(
"export default pattern<{ initial: string }>((input) => {",
'const fresh: { [key: string]: Owned } = { value: "seed" };',
"{ box: fresh }",
),
"fresh-index-signature",
["box", "value"],
),
).toContain("writeAuthorizedBy failed at /box/value");
});
});
for (
const [how, declaration, reference] of [
["a constant", "const schema = toSchema();", "schema"],
[
"a property of a constant bound to an object literal",
"const generated = { input: toSchema() };\nconst schemas = generated;",
"schemas.input",
],
[
"a nested property",
"const schemas = { outer: { input: toSchema() } };",
"schemas.outer.input",
],
] as const
) {
it(`refuses a writer an input schema passed through ${how} cannot read`, async () => {
// The `toSchema` call the reference leads to defines the argument
// document, as it would written in place.
const source: RuntimeProgram = {
main: "/main.tsx",
files: [{
name: "/main.tsx",
contents: `///
import { handler, pattern, toSchema, Writable, type WriteAuthorizedBy } from "commonfabric";
export const writerA = handler<{ v: string }, { value: Writable }>(
({ v }, { value }) => { value.set(v); },
);
type Box = { value: [W][0] };
type In = Box>;
${declaration}
export default pattern((input: In) => ({ value: input.value }), ${reference});
`,
}],
};
const tx = runtime.edit();
await expect(
runtime.patternManager.compilePattern(source, { space, tx }),
).rejects.toThrow(
"The writer binding of `WriteAuthorizedBy` could not be read",
);
tx.abort();
});
}
for (
const [how, declaration, argument] of [
[
"reassigns",
"function make(schema: Schema) {\n schema = toSchema();\n return schema;\n}",
'{ type: "object" }',
],
[
"writes into",
"function make(schemas: { input?: Schema }) {\n schemas.input = toSchema();\n return schemas.input;\n}",
"{}",
],
] as const
) {
it(`refuses a created cell's schema a function returns from a parameter it ${how}`, async () => {
// The parameter no longer holds what the call passed it, so the
// schema cannot be read back to the `toSchema` call that makes it.
const source: RuntimeProgram = {
main: "/main.tsx",
files: [{
name: "/main.tsx",
contents: `///
import { handler, pattern, toSchema, Writable, type WriteAuthorizedBy } from "commonfabric";
export const writerA = handler<{ v: string }, { value: Writable }>(
({ v }, { value }) => { value.set(v); },
);
type Schema = ReturnType;
interface Erased { value: [W][0] }
type In = { byId: Erased> };
${declaration}
export default pattern<{}>(() => {
const x = new Writable({ byId: { value: "seed" } }, make(${argument})).for("x");
return { x };
});
`,
}],
};
const tx = runtime.edit();
await expect(
runtime.patternManager.compilePattern(source, { space, tx }),
).rejects.toThrow("could not be read back");
tx.abort();
});
}
describe("fresh data a callback's return annotation types", () => {
// No document the result links to stores the policy: the result document
// holds the data itself, and stores the policy the annotation declares.
const annotated = (resultType: string, returned: string) =>
program(
`type Box = { value: T };
type Erased = { value: [W][0] };
export default pattern<{ initial: string }>((input): ${resultType} => {`,
"",
returned,
);
it("stores the whole policy declared through a generic alias, which refuses the pattern's own write", async () => {
const tx = runtime.edit();
const pattern = await runtime.patternManager.compilePattern(
annotated("Box>", '{ value: "seed" }'),
{ space, tx },
);
const cell = runtime.getCell<{ value: string }>(
space,
"annotated-generic-alias",
undefined,
tx,
);
runtime.run(tx, pattern, {}, cell);
runtime.prepareTxForCommit(tx);
expect((await tx.commit().settled).error?.message).toContain(
"writeAuthorizedBy requires a trusted verified binding identity at /value",
);
});
it("stores the whole policy declared through an index signature, which refuses other writers", async () => {
expect(
await memberWriteError(
annotated(
"{ byId: { [key: string]: Owned } }",
'{ byId: { k: "seed" } }',
),
"annotated-index-signature",
["byId", "k"],
),
).toContain("writeAuthorizedBy failed at /byId/k");
});
it("refuses a policy declared through an indexed access", async () => {
const tx = runtime.edit();
await expect(
runtime.patternManager.compilePattern(
annotated(
"{ byId: Erased> }",
'{ byId: { value: "seed" } }',
),
{ space, tx },
),
).rejects.toThrow("could not be read");
tx.abort();
});
});
describe("read in part by a view", () => {
// A result inferred from the pattern's callback, and a computed's result,
// read an owner policy from its type, where its writer cannot be read.
// They leave its owner and its claim naming the current principal out
// with the writer, since either alone refuses every write against it.
it("sets up an inferred result returning an owner-protected input, and enforces the policy through it", async () => {
expect(
await outcomeOf(
program(
"export default pattern<{ value: Owned }>((input) => {",
"const value = input.value;",
),
"owner-inferred-result",
),
).toBe("enforced");
});
it("writes the result of a computed that copies an owner-protected value", async () => {
const tx = runtime.edit();
const pattern = await runtime.patternManager.compilePattern(
program(
"export default pattern<{ value: Owned }, { copy: string; setA: Stream<{ v: string }> }>((input) => {",
"const value = input.value;",
"{ copy: computed(() => value), setA: writerA({ value }) }",
),
{ space, tx },
);
const cell = runtime.getCell<{ copy: string }>(
space,
"owner-computed-copy",
undefined,
tx,
);
const running = runtime.run(tx, pattern, {}, cell);
runtime.prepareTxForCommit(tx);
expect((await tx.commit().settled).error).toBeUndefined();
await running.pull();
await runtime.idle();
(running.key("setA") as unknown as { send: (e: unknown) => void }).send({
v: "from writerA",
});
await runtime.idle();
await running.pull();
await runtime.idle();
expect(running.key("copy").get()).toBe("from writerA");
});
});
});