clone

a deno framework for local-first web apps that open instantly, work offline and sync when users choose

feat/fix-sync-enrollment-startup
lofi / package / schema / mod_test.ts
mod_test.ts 4.0K
1import { schema } from "jazz-tools";2import {3 defineNestedApp,4 defineNestedPermissions,5 flattenNestedSchema,6 isEncryptedColumn,7 mergeNestedPermissions,8 s,9} from "./mod.ts";10import { clearEncryptedColumnRegistry } from "./encrypted.ts";1112// The facade re-exports, never reimplements: every curated member must be the13// pinned Jazz 2 original by identity, so semantics cannot drift. The define14// entry points are the one narrowing of that rule: they delegate to the15// originals unchanged but first record encrypted columns into the registry,16// so they are asserted as wrappers below rather than as identities here.17const curatedMembers = [18 "string",19 "boolean",20 "int",21 "float",22 "timestamp",23 "bytes",24 "json",25 "enum",26 "ref",27 "array",28 "add",29 "drop",30 "renameFrom",31 "table",32 "defineMigration",33 "renameTableFrom",34 "definePermissions",35 "permissionIntrospectionColumns",36] as const;3738const wrappedDefineMembers = ["defineSchema", "defineApp", "defineSliceableApp"] as const;3940Deno.test("schema facade exposes every curated member as the Jazz original", () => {41 for (const member of curatedMembers) {42 if (!(member in schema)) {43 throw new Error(`jazz-tools schema namespace no longer provides "${member}"`);44 }45 if (s[member] !== (schema as Record<string, unknown>)[member]) {46 throw new Error(`facade member "${member}" is not the jazz-tools original`);47 }48 }49});5051Deno.test("the define entry points delegate to Jazz and feed the encrypted registry", () => {52 for (const member of wrappedDefineMembers) {53 if (!(member in schema)) {54 throw new Error(`jazz-tools schema namespace no longer provides "${member}"`);55 }56 if (s[member] === (schema as Record<string, unknown>)[member]) {57 throw new Error(58 `facade member "${member}" lost its registration wrapper — encrypted columns ` +59 "would no longer register",60 );61 }62 }63 clearEncryptedColumnRegistry();64 try {65 // Delegation is observable: defineSchema returns its definition unchanged66 // (the pinned original's contract) while registering the sealed column.67 const definition = {68 diary: s.table({69 title: s.string(),70 body: s.encryptedText("modtest.diary.body"),71 }),72 };73 const defined: unknown = s.defineSchema(definition as never);74 if (defined !== definition) {75 throw new Error("defineSchema no longer returns the definition unchanged");76 }77 if (!isEncryptedColumn("diary", "body")) {78 throw new Error("defineSchema did not register the encrypted column");79 }80 } finally {81 clearEncryptedColumnRegistry();82 }83});8485// The nested-namespace members are the one deliberate exception to the86// re-export rule: they are lofi-owned (a naming layer over the pinned87// defineSliceableApp), so they must be lofi's functions, not Jazz members.88Deno.test("the nested-namespace members are the lofi-owned originals", () => {89 const lofiMembers = {90 defineNestedApp,91 defineNestedPermissions,92 mergeNestedPermissions,93 flattenNestedSchema,94 } as const;95 for (const [name, member] of Object.entries(lofiMembers)) {96 if (s[name as keyof typeof lofiMembers] !== member) {97 throw new Error(`facade member "${name}" is not the lofi nested-namespace original`);98 }99 if (name in schema) {100 throw new Error(101 `jazz-tools now provides "${name}" — resolve the collision with the lofi member`,102 );103 }104 }105});106107Deno.test("a schema declared through the facade round-trips app and permissions", () => {108 const app = s.defineApp({109 tasks: s.table({110 text: s.string(),111 completed: s.boolean(),112 createdAt: s.timestamp(),113 }),114 });115 if (typeof app.tasks?.where !== "function") {116 throw new Error("facade-declared app does not expose a queryable table");117 }118 const permissions = s.definePermissions(app, ({ policy }) => {119 policy.tasks.allowInsert.always();120 });121 if (!permissions || typeof permissions !== "object") {122 throw new Error("facade-declared permissions did not compile");123 }124});