@fragno-dev/test 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +41 -31
- package/CHANGELOG.md +69 -0
- package/dist/adapters.d.ts +4 -7
- package/dist/adapters.d.ts.map +1 -1
- package/dist/adapters.js +18 -302
- package/dist/adapters.js.map +1 -1
- package/dist/db-test.d.ts +120 -18
- package/dist/db-test.d.ts.map +1 -1
- package/dist/db-test.js +203 -55
- package/dist/db-test.js.map +1 -1
- package/dist/durable-hooks.d.ts +6 -2
- package/dist/durable-hooks.d.ts.map +1 -1
- package/dist/durable-hooks.js +10 -5
- package/dist/durable-hooks.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/model-checker-actors.d.ts.map +1 -1
- package/dist/model-checker-actors.js +1 -1
- package/dist/model-checker-actors.js.map +1 -1
- package/dist/model-checker-adapter.d.ts +1 -1
- package/dist/model-checker-adapter.d.ts.map +1 -1
- package/dist/model-checker-adapter.js.map +1 -1
- package/dist/model-checker.d.ts.map +1 -1
- package/dist/model-checker.js.map +1 -1
- package/dist/test-adapters/drizzle-pglite.js +116 -0
- package/dist/test-adapters/drizzle-pglite.js.map +1 -0
- package/dist/test-adapters/in-memory.js +39 -0
- package/dist/test-adapters/in-memory.js.map +1 -0
- package/dist/test-adapters/kysely-pglite.js +105 -0
- package/dist/test-adapters/kysely-pglite.js.map +1 -0
- package/dist/test-adapters/kysely-sqlite.js +87 -0
- package/dist/test-adapters/kysely-sqlite.js.map +1 -0
- package/dist/test-adapters/model-checker.js +41 -0
- package/dist/test-adapters/model-checker.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +32 -33
- package/src/adapter-conformance.test.ts +3 -1
- package/src/adapters.ts +24 -455
- package/src/db-roundtrip-guard.test.ts +206 -0
- package/src/db-test.test.ts +131 -77
- package/src/db-test.ts +530 -96
- package/src/durable-hooks.test.ts +58 -0
- package/src/durable-hooks.ts +23 -8
- package/src/index.test.ts +188 -104
- package/src/index.ts +6 -2
- package/src/model-checker-actors.test.ts +5 -2
- package/src/model-checker-actors.ts +2 -1
- package/src/model-checker-adapter.ts +3 -2
- package/src/model-checker.test.ts +4 -1
- package/src/model-checker.ts +4 -3
- package/src/test-adapters/drizzle-pglite.ts +162 -0
- package/src/test-adapters/in-memory.ts +56 -0
- package/src/test-adapters/kysely-pglite.ts +151 -0
- package/src/test-adapters/kysely-sqlite.ts +119 -0
- package/src/test-adapters/model-checker.ts +58 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-checker.js","names":["uow: TypedUnitOfWork<TSchema, [], unknown> | null","defaultHistoryConfig: ModelCheckerHistory","normalized: Record<string, unknown>","snapshot: { table: string; rows: unknown[] }[]","schedules: ModelCheckerStep[][]","current: ModelCheckerStep[]","phase: ModelCheckerPhase","schedule: ModelCheckerStep[]","candidates: number[]","currentTxId: number | undefined","lastStateHash: string | null","schedules: ModelCheckerScheduleResult[]"],"sources":["../src/model-checker.ts"],"sourcesContent":["import type { FragnoRuntime } from \"@fragno-dev/core\";\nimport {\n runWithTraceRecorder,\n type FragnoCoreTraceEvent,\n} from \"@fragno-dev/core/internal/trace-context\";\nimport { FragnoId, FragnoReference, type AnySchema } from \"@fragno-dev/db/schema\";\nimport type { SimpleQueryInterface } from \"@fragno-dev/db/query\";\nimport type { TypedUnitOfWork } from \"@fragno-dev/db\";\nimport type { MutationOperation } from \"@fragno-dev/db/unit-of-work\";\n\nexport type ModelCheckerMode = \"exhaustive\" | \"bounded-exhaustive\" | \"random\" | \"infinite\";\nexport type ModelCheckerPhase = \"retrieve\" | \"mutate\";\n\nexport type ModelCheckerStep = {\n txId: number;\n phase: ModelCheckerPhase;\n};\n\nexport type ModelCheckerTraceHashMode = \"state\" | \"trace\" | \"state+trace\";\n\nexport type NormalizedMutationOperation = {\n type: \"create\" | \"update\" | \"delete\" | \"check\";\n table: string;\n namespace?: string | null;\n id?: unknown;\n checkVersion?: boolean;\n set?: Record<string, unknown>;\n values?: Record<string, unknown>;\n generatedExternalId?: string;\n};\n\nexport type ModelCheckerTraceEvent =\n | {\n type: \"schedule-step\";\n step: ModelCheckerStep;\n stepIndex: number;\n }\n | {\n type: \"retrieve-output\";\n txId?: number;\n uowName?: string;\n output: unknown;\n }\n | {\n type: \"mutation-input\";\n txId?: number;\n uowName?: string;\n operations: NormalizedMutationOperation[];\n }\n | {\n type: \"mutation-result\";\n txId?: number;\n uowName?: string;\n success: boolean;\n createdIds: unknown[];\n error?: string;\n }\n | {\n type: \"runtime\";\n operation: \"time.now\" | \"random.float\" | \"random.uuid\" | \"random.cuid\";\n value: unknown;\n }\n | {\n type: \"external\";\n name: string;\n payload: unknown;\n };\n\nexport type ModelCheckerTrace = {\n events: ModelCheckerTraceEvent[];\n};\n\nexport type ModelCheckerTraceRecorder = (event: ModelCheckerTraceEvent) => void;\n\nexport type ModelCheckerTraceHasher = (trace: ModelCheckerTrace) => Promise<string> | string;\n\nexport type RawUowTransactionContext<TSchema extends AnySchema, TUowConfig> = {\n queryEngine: SimpleQueryInterface<TSchema, TUowConfig>;\n createUnitOfWork: (name?: string, config?: TUowConfig) => TypedUnitOfWork<TSchema, [], unknown>;\n runtime?: FragnoRuntime;\n};\n\nexport type RawUowMutateContext<\n TSchema extends AnySchema,\n TUowConfig,\n TRetrieve,\n> = RawUowTransactionContext<TSchema, TUowConfig> & {\n retrieveResult: TRetrieve;\n};\n\nexport interface RawUowTransaction<\n TRetrieve = unknown,\n TMutate = unknown,\n TSchema extends AnySchema = AnySchema,\n TUowConfig = void,\n> {\n name?: string;\n retrieve(ctx: RawUowTransactionContext<TSchema, TUowConfig>): TRetrieve | Promise<TRetrieve>;\n mutate?(ctx: RawUowMutateContext<TSchema, TUowConfig, TRetrieve>): TMutate | Promise<TMutate>;\n}\n\nexport type RawUowTransactionBuilder<\n TRetrieve = unknown,\n TMutate = unknown,\n TSchema extends AnySchema = AnySchema,\n TUowConfig = void,\n> = {\n name?: string;\n config?: TUowConfig;\n retrieve: (\n uow: TypedUnitOfWork<TSchema, [], unknown>,\n ctx: RawUowTransactionContext<TSchema, TUowConfig>,\n ) => TRetrieve | Promise<TRetrieve>;\n mutate?: (\n uow: TypedUnitOfWork<TSchema, [], unknown>,\n ctx: RawUowMutateContext<TSchema, TUowConfig, TRetrieve>,\n ) => TMutate | Promise<TMutate>;\n};\n\nexport const createRawUowTransaction = <\n TRetrieve = unknown,\n TMutate = unknown,\n TSchema extends AnySchema = AnySchema,\n TUowConfig = void,\n>(\n builder: RawUowTransactionBuilder<TRetrieve, TMutate, TSchema, TUowConfig>,\n): RawUowTransaction<TRetrieve, TMutate, TSchema, TUowConfig> => {\n let uow: TypedUnitOfWork<TSchema, [], unknown> | null = null;\n const getUow = (ctx: RawUowTransactionContext<TSchema, TUowConfig>, phase: ModelCheckerPhase) => {\n if (!uow) {\n if (phase === \"mutate\") {\n throw new Error(\"Raw UOW mutate invoked before retrieve.\");\n }\n uow = ctx.createUnitOfWork(builder.name, builder.config);\n }\n return uow;\n };\n\n const mutate = builder.mutate;\n return {\n name: builder.name,\n retrieve: (ctx) => builder.retrieve(getUow(ctx, \"retrieve\"), ctx),\n mutate: mutate ? (ctx) => mutate(getUow(ctx, \"mutate\"), ctx) : undefined,\n };\n};\n\nexport type ModelCheckerExecutionContext<TSchema extends AnySchema, TUowConfig> = {\n ctx: RawUowTransactionContext<TSchema, TUowConfig>;\n cleanup?: () => Promise<void>;\n};\n\nexport type ModelCheckerHistory =\n | false\n | { type: \"lru\"; maxEntries: number }\n | { type: \"unbounded\" };\n\nexport type ModelCheckerBounds = {\n maxSteps?: number;\n};\n\nexport type ModelCheckerStateHasherContext<TSchema extends AnySchema, TUowConfig> = {\n schema: TSchema;\n queryEngine: SimpleQueryInterface<TSchema, TUowConfig>;\n};\n\nexport type ModelCheckerConfig<TSchema extends AnySchema, TUowConfig = void> = {\n schema: TSchema;\n mode?: ModelCheckerMode;\n seed?: number;\n maxSchedules?: number;\n bounds?: ModelCheckerBounds;\n history?: ModelCheckerHistory;\n stopWhenFrontierExhausted?: boolean;\n stateHasher?: (ctx: ModelCheckerStateHasherContext<TSchema, TUowConfig>) => Promise<string>;\n traceRecorder?: ModelCheckerTraceRecorder;\n traceHasher?: ModelCheckerTraceHasher;\n traceHashMode?: ModelCheckerTraceHashMode;\n runtime?: FragnoRuntime;\n createContext: () => Promise<ModelCheckerExecutionContext<TSchema, TUowConfig>>;\n setup?: (ctx: RawUowTransactionContext<TSchema, TUowConfig>) => Promise<void>;\n buildTransactions: (\n ctx: RawUowTransactionContext<TSchema, TUowConfig>,\n ) => RawUowTransaction<unknown, unknown, TSchema, TUowConfig>[];\n};\n\nexport type ModelCheckerScheduleResult = {\n schedule: ModelCheckerStep[];\n stateHash: string;\n traceHash?: string;\n trace?: ModelCheckerTrace;\n};\n\nexport type ModelCheckerRunResult = {\n schedules: ModelCheckerScheduleResult[];\n visitedPaths: number;\n};\n\ntype HistoryTracker = {\n add: (key: string) => boolean;\n size: () => number;\n};\n\ntype TransactionPlan = {\n stepsPerTransaction: number[];\n};\n\ntype ScheduleExecutionResult = {\n stateHash: string;\n newPathsAdded: boolean;\n traceHash?: string;\n trace?: ModelCheckerTrace;\n};\n\nconst defaultHistoryConfig: ModelCheckerHistory = { type: \"lru\", maxEntries: 5000 };\n\nconst mulberry32 = (seed: number) => {\n let t = seed >>> 0;\n return () => {\n t += 1831565813;\n let r = Math.imul(t ^ (t >>> 15), t | 1);\n r ^= r + Math.imul(r ^ (r >>> 7), r | 61);\n return ((r ^ (r >>> 14)) >>> 0) / 4294967296;\n };\n};\n\nconst serializeSchedulePrefix = (schedule: ModelCheckerStep[]): string =>\n schedule.map((step) => `${step.txId}:${step.phase}`).join(\"|\");\n\nconst buildPathKey = (pathHash: string, prefix: ModelCheckerStep[]): string =>\n `${pathHash}::${serializeSchedulePrefix(prefix)}`;\n\nconst createHistoryTracker = (history: ModelCheckerHistory): HistoryTracker | null => {\n if (!history) {\n return null;\n }\n\n if (history.type === \"unbounded\") {\n const set = new Set<string>();\n return {\n add: (key: string) => {\n const has = set.has(key);\n if (!has) {\n set.add(key);\n }\n return !has;\n },\n size: () => set.size,\n };\n }\n\n const maxEntries = Math.max(history.maxEntries, 1);\n const map = new Map<string, true>();\n return {\n add: (key: string) => {\n const existed = map.delete(key);\n map.set(key, true);\n if (map.size > maxEntries) {\n const first = map.keys().next().value;\n if (first !== undefined) {\n map.delete(first);\n }\n }\n return !existed;\n },\n size: () => map.size,\n };\n};\n\nconst normalizeForHash = (value: unknown): unknown => {\n if (value === null || value === undefined) {\n return value;\n }\n\n if (typeof value === \"bigint\") {\n return value.toString();\n }\n\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (value instanceof FragnoId) {\n return {\n externalId: value.externalId,\n internalId: value.internalId?.toString(),\n version: value.version,\n };\n }\n\n if (value instanceof FragnoReference) {\n return value.internalId.toString();\n }\n\n if (Array.isArray(value)) {\n return value.map((entry) => normalizeForHash(entry));\n }\n\n if (typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n const sortedKeys = Object.keys(record).sort();\n const normalized: Record<string, unknown> = {};\n for (const key of sortedKeys) {\n normalized[key] = normalizeForHash(record[key]);\n }\n return normalized;\n }\n\n return value;\n};\n\nconst stableStringify = (value: unknown): string => JSON.stringify(normalizeForHash(value));\n\nexport const defaultStateHasher = async <TSchema extends AnySchema, TUowConfig>(\n ctx: ModelCheckerStateHasherContext<TSchema, TUowConfig>,\n): Promise<string> => {\n const tableNames = Object.keys(ctx.schema.tables).sort();\n const snapshot: { table: string; rows: unknown[] }[] = [];\n\n for (const tableName of tableNames) {\n const rows = await ctx.queryEngine.find(tableName, (b) =>\n b.whereIndex(\"primary\").orderByIndex(\"primary\", \"asc\"),\n );\n snapshot.push({ table: tableName, rows });\n }\n\n return stableStringify(snapshot);\n};\n\nexport const defaultTraceHasher = async (trace: ModelCheckerTrace): Promise<string> =>\n stableStringify(trace.events);\n\nconst resolvePathHash = (\n stateHash: string,\n traceHash: string | undefined,\n mode: ModelCheckerTraceHashMode,\n): string => {\n if (mode === \"state\") {\n return stateHash;\n }\n if (!traceHash) {\n throw new Error(\"Trace hash mode requires traceHasher to be configured.\");\n }\n if (mode === \"trace\") {\n return traceHash;\n }\n return `${stateHash}::${traceHash}`;\n};\n\nconst wrapRuntimeForTrace = (\n runtime: FragnoRuntime,\n record: (event: ModelCheckerTraceEvent) => void,\n): FragnoRuntime => ({\n time: {\n now: () => {\n const value = runtime.time.now();\n record({ type: \"runtime\", operation: \"time.now\", value });\n return value;\n },\n },\n random: {\n float: () => {\n const value = runtime.random.float();\n record({ type: \"runtime\", operation: \"random.float\", value });\n return value;\n },\n uuid: () => {\n const value = runtime.random.uuid();\n record({ type: \"runtime\", operation: \"random.uuid\", value });\n return value;\n },\n cuid: () => {\n const value = runtime.random.cuid();\n record({ type: \"runtime\", operation: \"random.cuid\", value });\n return value;\n },\n },\n});\n\nconst captureCoreTrace = (\n record: (event: ModelCheckerTraceEvent) => void,\n event: FragnoCoreTraceEvent,\n) => {\n record({ type: \"external\", name: event.type, payload: event });\n};\n\nconst createTraceContext = (\n recorder: ModelCheckerTraceRecorder | undefined,\n traceHasher: ModelCheckerTraceHasher | undefined,\n) => {\n const events = recorder || traceHasher ? ([] as ModelCheckerTraceEvent[]) : null;\n const record = (event: ModelCheckerTraceEvent) => {\n if (events) {\n events.push(event);\n }\n recorder?.(event);\n };\n\n return { events, record };\n};\n\nconst normalizeMutationOperation = (\n op: MutationOperation<AnySchema>,\n): NormalizedMutationOperation => {\n if (op.type === \"create\") {\n return {\n type: \"create\",\n table: op.table,\n namespace: op.namespace,\n values: normalizeForHash(op.values) as Record<string, unknown>,\n generatedExternalId: op.generatedExternalId,\n };\n }\n\n if (op.type === \"update\") {\n return {\n type: \"update\",\n table: op.table,\n namespace: op.namespace,\n id: normalizeForHash(op.id),\n checkVersion: op.checkVersion,\n set: normalizeForHash(op.set) as Record<string, unknown>,\n };\n }\n\n if (op.type === \"delete\") {\n return {\n type: \"delete\",\n table: op.table,\n namespace: op.namespace,\n id: normalizeForHash(op.id),\n checkVersion: op.checkVersion,\n };\n }\n\n return {\n type: \"check\",\n table: op.table,\n namespace: op.namespace,\n id: normalizeForHash(op.id),\n };\n};\n\nconst wrapCreateUnitOfWorkForTrace = <TSchema extends AnySchema, TUowConfig>(\n createUnitOfWork: (name?: string, config?: TUowConfig) => TypedUnitOfWork<TSchema, [], unknown>,\n getTxId: () => number | undefined,\n record: (event: ModelCheckerTraceEvent) => void,\n) => {\n return (name?: string, config?: TUowConfig) => {\n const uow = createUnitOfWork(name, config);\n return new Proxy(uow, {\n get(target, prop, receiver) {\n if (prop === \"executeRetrieve\") {\n return async () => {\n const result = await target.executeRetrieve();\n record({\n type: \"retrieve-output\",\n txId: getTxId(),\n uowName: target.name,\n output: normalizeForHash(result),\n });\n return result;\n };\n }\n\n if (prop === \"executeMutations\") {\n return async () => {\n const txId = getTxId();\n record({\n type: \"mutation-input\",\n txId,\n uowName: target.name,\n operations: target.getMutationOperations().map(normalizeMutationOperation),\n });\n try {\n const result = await target.executeMutations();\n record({\n type: \"mutation-result\",\n txId,\n uowName: target.name,\n success: result.success,\n createdIds: result.success ? target.getCreatedIds().map(normalizeForHash) : [],\n });\n return result;\n } catch (error) {\n record({\n type: \"mutation-result\",\n txId,\n uowName: target.name,\n success: false,\n createdIds: [],\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n };\n }\n\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") {\n return value.bind(target);\n }\n return value;\n },\n });\n };\n};\n\nconst planTransactions = <TSchema extends AnySchema, TUowConfig>(\n transactions: RawUowTransaction<unknown, unknown, TSchema, TUowConfig>[],\n): TransactionPlan => {\n if (transactions.length === 0) {\n throw new Error(\"Model checker requires at least one transaction.\");\n }\n\n const stepsPerTransaction = transactions.map((tx) => {\n if (!tx.retrieve) {\n throw new Error(\"Model checker transactions must define a retrieve step.\");\n }\n return tx.mutate ? 2 : 1;\n });\n\n return { stepsPerTransaction };\n};\n\nconst generateAllSchedules = (stepsPerTransaction: number[]): ModelCheckerStep[][] => {\n const schedules: ModelCheckerStep[][] = [];\n const totalSteps = stepsPerTransaction.reduce((sum, steps) => sum + steps, 0);\n const progress = stepsPerTransaction.map(() => 0);\n const current: ModelCheckerStep[] = [];\n\n const walk = () => {\n if (current.length === totalSteps) {\n schedules.push([...current]);\n return;\n }\n\n for (let txId = 0; txId < stepsPerTransaction.length; txId += 1) {\n if (progress[txId] >= stepsPerTransaction[txId]) {\n continue;\n }\n const phase: ModelCheckerPhase = progress[txId] === 0 ? \"retrieve\" : \"mutate\";\n current.push({ txId, phase });\n progress[txId] += 1;\n walk();\n progress[txId] -= 1;\n current.pop();\n }\n };\n\n walk();\n return schedules;\n};\n\nconst generateRandomSchedule = (\n stepsPerTransaction: number[],\n rng: () => number,\n): ModelCheckerStep[] => {\n const totalSteps = stepsPerTransaction.reduce((sum, steps) => sum + steps, 0);\n const progress = stepsPerTransaction.map(() => 0);\n const schedule: ModelCheckerStep[] = [];\n\n for (let stepIndex = 0; stepIndex < totalSteps; stepIndex += 1) {\n const candidates: number[] = [];\n for (let txId = 0; txId < stepsPerTransaction.length; txId += 1) {\n if (progress[txId] < stepsPerTransaction[txId]) {\n candidates.push(txId);\n }\n }\n const pick = candidates[Math.floor(rng() * candidates.length)];\n const phase: ModelCheckerPhase = progress[pick] === 0 ? \"retrieve\" : \"mutate\";\n schedule.push({ txId: pick, phase });\n progress[pick] += 1;\n }\n\n return schedule;\n};\n\nconst executeSchedule = async <TSchema extends AnySchema, TUowConfig>(\n schedule: ModelCheckerStep[],\n config: ModelCheckerConfig<TSchema, TUowConfig>,\n history: HistoryTracker | null,\n stateHasher: (ctx: ModelCheckerStateHasherContext<TSchema, TUowConfig>) => Promise<string>,\n): Promise<ScheduleExecutionResult> => {\n const traceHashMode = config.traceHashMode ?? \"state\";\n const traceHasher =\n config.traceHasher ?? (traceHashMode === \"state\" ? undefined : defaultTraceHasher);\n const traceContext = createTraceContext(config.traceRecorder, traceHasher);\n const traceEnabled = Boolean(traceContext.events);\n const runtime =\n config.runtime && traceEnabled\n ? wrapRuntimeForTrace(config.runtime, traceContext.record)\n : config.runtime;\n\n const { ctx, cleanup } = await config.createContext();\n try {\n let currentTxId: number | undefined;\n const tracedContext = traceEnabled\n ? {\n ...ctx,\n createUnitOfWork: wrapCreateUnitOfWorkForTrace(\n ctx.createUnitOfWork,\n () => currentTxId,\n traceContext.record,\n ),\n runtime,\n }\n : { ...ctx, runtime };\n\n if (config.setup) {\n await config.setup(tracedContext);\n }\n\n const transactions = config.buildTransactions(tracedContext);\n const transactionState = transactions.map(() => ({ retrieveResult: undefined as unknown }));\n let lastStateHash: string | null = null;\n let newPathsAdded = false;\n\n const runSchedule = async () => {\n for (let stepIndex = 0; stepIndex < schedule.length; stepIndex += 1) {\n const step = schedule[stepIndex];\n if (!step) {\n throw new Error(\"Schedule step is missing.\");\n }\n const tx = transactions[step.txId];\n if (!tx) {\n throw new Error(`Transaction ${step.txId} is missing.`);\n }\n\n currentTxId = step.txId;\n if (traceEnabled) {\n traceContext.record({ type: \"schedule-step\", step, stepIndex });\n }\n\n if (step.phase === \"retrieve\") {\n transactionState[step.txId].retrieveResult = await tx.retrieve(tracedContext);\n } else {\n if (!tx.mutate) {\n throw new Error(`Transaction ${step.txId} does not define a mutate step.`);\n }\n await tx.mutate({\n ...tracedContext,\n retrieveResult: transactionState[step.txId].retrieveResult,\n });\n }\n\n if (history) {\n lastStateHash = await stateHasher({\n schema: config.schema,\n queryEngine: tracedContext.queryEngine,\n });\n const traceHash = traceHasher\n ? await traceHasher({ events: traceContext.events ?? [] })\n : undefined;\n const prefix = schedule.slice(0, stepIndex + 1);\n const added = history.add(\n buildPathKey(resolvePathHash(lastStateHash, traceHash, traceHashMode), prefix),\n );\n if (added) {\n newPathsAdded = true;\n }\n }\n }\n\n currentTxId = undefined;\n\n if (!lastStateHash) {\n lastStateHash = await stateHasher({\n schema: config.schema,\n queryEngine: tracedContext.queryEngine,\n });\n }\n\n const finalTraceHash = traceHasher\n ? await traceHasher({ events: traceContext.events ?? [] })\n : undefined;\n\n return {\n stateHash: lastStateHash,\n newPathsAdded,\n traceHash: finalTraceHash,\n trace: traceContext.events ? { events: traceContext.events } : undefined,\n };\n };\n\n if (traceEnabled) {\n return await runWithTraceRecorder(\n (event) => captureCoreTrace(traceContext.record, event),\n runSchedule,\n );\n }\n\n return await runSchedule();\n } finally {\n if (cleanup) {\n await cleanup();\n }\n }\n};\n\nconst getTransactionPlan = async <TSchema extends AnySchema, TUowConfig>(\n config: ModelCheckerConfig<TSchema, TUowConfig>,\n): Promise<TransactionPlan> => {\n const { ctx, cleanup } = await config.createContext();\n try {\n const context = config.runtime ? { ...ctx, runtime: config.runtime } : ctx;\n if (config.setup) {\n await config.setup(context);\n }\n const transactions = config.buildTransactions(context);\n return planTransactions(transactions);\n } finally {\n if (cleanup) {\n await cleanup();\n }\n }\n};\n\nexport const runModelChecker = async <TSchema extends AnySchema, TUowConfig>(\n config: ModelCheckerConfig<TSchema, TUowConfig>,\n): Promise<ModelCheckerRunResult> => {\n const mode = config.mode ?? \"exhaustive\";\n const historyConfig = config.history ?? defaultHistoryConfig;\n const history = createHistoryTracker(historyConfig);\n const stateHasher = config.stateHasher ?? defaultStateHasher;\n const { stepsPerTransaction } = await getTransactionPlan(config);\n const schedules: ModelCheckerScheduleResult[] = [];\n const maxSchedules = config.maxSchedules;\n const seed = config.seed ?? 1;\n\n if (mode === \"exhaustive\" || mode === \"bounded-exhaustive\") {\n if (mode === \"bounded-exhaustive\") {\n const bounds = config.bounds;\n if (bounds?.maxSteps !== undefined) {\n const totalSteps = stepsPerTransaction.reduce((sum, steps) => sum + steps, 0);\n if (totalSteps > bounds.maxSteps) {\n throw new Error(\n `bounded-exhaustive requires maxSteps >= ${totalSteps}, ` +\n `but received ${bounds.maxSteps}`,\n );\n }\n }\n }\n const allSchedules = generateAllSchedules(stepsPerTransaction);\n for (const schedule of allSchedules) {\n if (maxSchedules !== undefined && schedules.length >= maxSchedules) {\n break;\n }\n const result = await executeSchedule(schedule, config, history, stateHasher);\n schedules.push({\n schedule,\n stateHash: result.stateHash,\n traceHash: result.traceHash,\n trace: result.trace,\n });\n }\n } else if (mode === \"random\") {\n const rng = mulberry32(seed);\n const total = maxSchedules ?? 1;\n for (let i = 0; i < total; i += 1) {\n const schedule = generateRandomSchedule(stepsPerTransaction, rng);\n const result = await executeSchedule(schedule, config, history, stateHasher);\n schedules.push({\n schedule,\n stateHash: result.stateHash,\n traceHash: result.traceHash,\n trace: result.trace,\n });\n }\n } else {\n const rng = mulberry32(seed);\n const stopWhenFrontierExhausted = config.stopWhenFrontierExhausted ?? false;\n let iterations = 0;\n let frontierExhausted = false;\n\n while (!frontierExhausted) {\n if (maxSchedules !== undefined && iterations >= maxSchedules) {\n break;\n }\n const schedule = generateRandomSchedule(stepsPerTransaction, rng);\n const result = await executeSchedule(schedule, config, history, stateHasher);\n schedules.push({\n schedule,\n stateHash: result.stateHash,\n traceHash: result.traceHash,\n trace: result.trace,\n });\n iterations += 1;\n\n if (stopWhenFrontierExhausted && history) {\n frontierExhausted = !result.newPathsAdded;\n }\n }\n }\n\n return {\n schedules,\n visitedPaths: history?.size() ?? 0,\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"model-checker.js","names":["uow: TypedUnitOfWork<TSchema, [], unknown> | null","defaultHistoryConfig: ModelCheckerHistory","normalized: Record<string, unknown>","snapshot: { table: string; rows: unknown[] }[]","schedules: ModelCheckerStep[][]","current: ModelCheckerStep[]","phase: ModelCheckerPhase","schedule: ModelCheckerStep[]","candidates: number[]","currentTxId: number | undefined","lastStateHash: string | null","schedules: ModelCheckerScheduleResult[]"],"sources":["../src/model-checker.ts"],"sourcesContent":["import {\n runWithTraceRecorder,\n type FragnoCoreTraceEvent,\n} from \"@fragno-dev/core/internal/trace-context\";\nimport type { SimpleQueryInterface } from \"@fragno-dev/db/query\";\nimport { FragnoId, FragnoReference, type AnySchema } from \"@fragno-dev/db/schema\";\nimport type { MutationOperation } from \"@fragno-dev/db/unit-of-work\";\n\nimport type { FragnoRuntime } from \"@fragno-dev/core\";\nimport type { TypedUnitOfWork } from \"@fragno-dev/db\";\n\nexport type ModelCheckerMode = \"exhaustive\" | \"bounded-exhaustive\" | \"random\" | \"infinite\";\nexport type ModelCheckerPhase = \"retrieve\" | \"mutate\";\n\nexport type ModelCheckerStep = {\n txId: number;\n phase: ModelCheckerPhase;\n};\n\nexport type ModelCheckerTraceHashMode = \"state\" | \"trace\" | \"state+trace\";\n\nexport type NormalizedMutationOperation = {\n type: \"create\" | \"update\" | \"delete\" | \"check\";\n table: string;\n namespace?: string | null;\n id?: unknown;\n checkVersion?: boolean;\n set?: Record<string, unknown>;\n values?: Record<string, unknown>;\n generatedExternalId?: string;\n};\n\nexport type ModelCheckerTraceEvent =\n | {\n type: \"schedule-step\";\n step: ModelCheckerStep;\n stepIndex: number;\n }\n | {\n type: \"retrieve-output\";\n txId?: number;\n uowName?: string;\n output: unknown;\n }\n | {\n type: \"mutation-input\";\n txId?: number;\n uowName?: string;\n operations: NormalizedMutationOperation[];\n }\n | {\n type: \"mutation-result\";\n txId?: number;\n uowName?: string;\n success: boolean;\n createdIds: unknown[];\n error?: string;\n }\n | {\n type: \"runtime\";\n operation: \"time.now\" | \"random.float\" | \"random.uuid\" | \"random.cuid\";\n value: unknown;\n }\n | {\n type: \"external\";\n name: string;\n payload: unknown;\n };\n\nexport type ModelCheckerTrace = {\n events: ModelCheckerTraceEvent[];\n};\n\nexport type ModelCheckerTraceRecorder = (event: ModelCheckerTraceEvent) => void;\n\nexport type ModelCheckerTraceHasher = (trace: ModelCheckerTrace) => Promise<string> | string;\n\nexport type RawUowTransactionContext<TSchema extends AnySchema, TUowConfig> = {\n queryEngine: SimpleQueryInterface<TSchema, TUowConfig>;\n createUnitOfWork: (name?: string, config?: TUowConfig) => TypedUnitOfWork<TSchema, [], unknown>;\n runtime?: FragnoRuntime;\n};\n\nexport type RawUowMutateContext<\n TSchema extends AnySchema,\n TUowConfig,\n TRetrieve,\n> = RawUowTransactionContext<TSchema, TUowConfig> & {\n retrieveResult: TRetrieve;\n};\n\nexport interface RawUowTransaction<\n TRetrieve = unknown,\n TMutate = unknown,\n TSchema extends AnySchema = AnySchema,\n TUowConfig = void,\n> {\n name?: string;\n retrieve(ctx: RawUowTransactionContext<TSchema, TUowConfig>): TRetrieve | Promise<TRetrieve>;\n mutate?(ctx: RawUowMutateContext<TSchema, TUowConfig, TRetrieve>): TMutate | Promise<TMutate>;\n}\n\nexport type RawUowTransactionBuilder<\n TRetrieve = unknown,\n TMutate = unknown,\n TSchema extends AnySchema = AnySchema,\n TUowConfig = void,\n> = {\n name?: string;\n config?: TUowConfig;\n retrieve: (\n uow: TypedUnitOfWork<TSchema, [], unknown>,\n ctx: RawUowTransactionContext<TSchema, TUowConfig>,\n ) => TRetrieve | Promise<TRetrieve>;\n mutate?: (\n uow: TypedUnitOfWork<TSchema, [], unknown>,\n ctx: RawUowMutateContext<TSchema, TUowConfig, TRetrieve>,\n ) => TMutate | Promise<TMutate>;\n};\n\nexport const createRawUowTransaction = <\n TRetrieve = unknown,\n TMutate = unknown,\n TSchema extends AnySchema = AnySchema,\n TUowConfig = void,\n>(\n builder: RawUowTransactionBuilder<TRetrieve, TMutate, TSchema, TUowConfig>,\n): RawUowTransaction<TRetrieve, TMutate, TSchema, TUowConfig> => {\n let uow: TypedUnitOfWork<TSchema, [], unknown> | null = null;\n const getUow = (ctx: RawUowTransactionContext<TSchema, TUowConfig>, phase: ModelCheckerPhase) => {\n if (!uow) {\n if (phase === \"mutate\") {\n throw new Error(\"Raw UOW mutate invoked before retrieve.\");\n }\n uow = ctx.createUnitOfWork(builder.name, builder.config);\n }\n return uow;\n };\n\n const mutate = builder.mutate;\n return {\n name: builder.name,\n retrieve: (ctx) => builder.retrieve(getUow(ctx, \"retrieve\"), ctx),\n mutate: mutate ? (ctx) => mutate(getUow(ctx, \"mutate\"), ctx) : undefined,\n };\n};\n\nexport type ModelCheckerExecutionContext<TSchema extends AnySchema, TUowConfig> = {\n ctx: RawUowTransactionContext<TSchema, TUowConfig>;\n cleanup?: () => Promise<void>;\n};\n\nexport type ModelCheckerHistory =\n | false\n | { type: \"lru\"; maxEntries: number }\n | { type: \"unbounded\" };\n\nexport type ModelCheckerBounds = {\n maxSteps?: number;\n};\n\nexport type ModelCheckerStateHasherContext<TSchema extends AnySchema, TUowConfig> = {\n schema: TSchema;\n queryEngine: SimpleQueryInterface<TSchema, TUowConfig>;\n};\n\nexport type ModelCheckerConfig<TSchema extends AnySchema, TUowConfig = void> = {\n schema: TSchema;\n mode?: ModelCheckerMode;\n seed?: number;\n maxSchedules?: number;\n bounds?: ModelCheckerBounds;\n history?: ModelCheckerHistory;\n stopWhenFrontierExhausted?: boolean;\n stateHasher?: (ctx: ModelCheckerStateHasherContext<TSchema, TUowConfig>) => Promise<string>;\n traceRecorder?: ModelCheckerTraceRecorder;\n traceHasher?: ModelCheckerTraceHasher;\n traceHashMode?: ModelCheckerTraceHashMode;\n runtime?: FragnoRuntime;\n createContext: () => Promise<ModelCheckerExecutionContext<TSchema, TUowConfig>>;\n setup?: (ctx: RawUowTransactionContext<TSchema, TUowConfig>) => Promise<void>;\n buildTransactions: (\n ctx: RawUowTransactionContext<TSchema, TUowConfig>,\n ) => RawUowTransaction<unknown, unknown, TSchema, TUowConfig>[];\n};\n\nexport type ModelCheckerScheduleResult = {\n schedule: ModelCheckerStep[];\n stateHash: string;\n traceHash?: string;\n trace?: ModelCheckerTrace;\n};\n\nexport type ModelCheckerRunResult = {\n schedules: ModelCheckerScheduleResult[];\n visitedPaths: number;\n};\n\ntype HistoryTracker = {\n add: (key: string) => boolean;\n size: () => number;\n};\n\ntype TransactionPlan = {\n stepsPerTransaction: number[];\n};\n\ntype ScheduleExecutionResult = {\n stateHash: string;\n newPathsAdded: boolean;\n traceHash?: string;\n trace?: ModelCheckerTrace;\n};\n\nconst defaultHistoryConfig: ModelCheckerHistory = { type: \"lru\", maxEntries: 5000 };\n\nconst mulberry32 = (seed: number) => {\n let t = seed >>> 0;\n return () => {\n t += 1831565813;\n let r = Math.imul(t ^ (t >>> 15), t | 1);\n r ^= r + Math.imul(r ^ (r >>> 7), r | 61);\n return ((r ^ (r >>> 14)) >>> 0) / 4294967296;\n };\n};\n\nconst serializeSchedulePrefix = (schedule: ModelCheckerStep[]): string =>\n schedule.map((step) => `${step.txId}:${step.phase}`).join(\"|\");\n\nconst buildPathKey = (pathHash: string, prefix: ModelCheckerStep[]): string =>\n `${pathHash}::${serializeSchedulePrefix(prefix)}`;\n\nconst createHistoryTracker = (history: ModelCheckerHistory): HistoryTracker | null => {\n if (!history) {\n return null;\n }\n\n if (history.type === \"unbounded\") {\n const set = new Set<string>();\n return {\n add: (key: string) => {\n const has = set.has(key);\n if (!has) {\n set.add(key);\n }\n return !has;\n },\n size: () => set.size,\n };\n }\n\n const maxEntries = Math.max(history.maxEntries, 1);\n const map = new Map<string, true>();\n return {\n add: (key: string) => {\n const existed = map.delete(key);\n map.set(key, true);\n if (map.size > maxEntries) {\n const first = map.keys().next().value;\n if (first !== undefined) {\n map.delete(first);\n }\n }\n return !existed;\n },\n size: () => map.size,\n };\n};\n\nconst normalizeForHash = (value: unknown): unknown => {\n if (value === null || value === undefined) {\n return value;\n }\n\n if (typeof value === \"bigint\") {\n return value.toString();\n }\n\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (value instanceof FragnoId) {\n return {\n externalId: value.externalId,\n internalId: value.internalId?.toString(),\n version: value.version,\n };\n }\n\n if (value instanceof FragnoReference) {\n return value.internalId.toString();\n }\n\n if (Array.isArray(value)) {\n return value.map((entry) => normalizeForHash(entry));\n }\n\n if (typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n const sortedKeys = Object.keys(record).sort();\n const normalized: Record<string, unknown> = {};\n for (const key of sortedKeys) {\n normalized[key] = normalizeForHash(record[key]);\n }\n return normalized;\n }\n\n return value;\n};\n\nconst stableStringify = (value: unknown): string => JSON.stringify(normalizeForHash(value));\n\nexport const defaultStateHasher = async <TSchema extends AnySchema, TUowConfig>(\n ctx: ModelCheckerStateHasherContext<TSchema, TUowConfig>,\n): Promise<string> => {\n const tableNames = Object.keys(ctx.schema.tables).sort();\n const snapshot: { table: string; rows: unknown[] }[] = [];\n\n for (const tableName of tableNames) {\n const rows = await ctx.queryEngine.find(tableName, (b) =>\n b.whereIndex(\"primary\").orderByIndex(\"primary\", \"asc\"),\n );\n snapshot.push({ table: tableName, rows });\n }\n\n return stableStringify(snapshot);\n};\n\nexport const defaultTraceHasher = async (trace: ModelCheckerTrace): Promise<string> =>\n stableStringify(trace.events);\n\nconst resolvePathHash = (\n stateHash: string,\n traceHash: string | undefined,\n mode: ModelCheckerTraceHashMode,\n): string => {\n if (mode === \"state\") {\n return stateHash;\n }\n if (!traceHash) {\n throw new Error(\"Trace hash mode requires traceHasher to be configured.\");\n }\n if (mode === \"trace\") {\n return traceHash;\n }\n return `${stateHash}::${traceHash}`;\n};\n\nconst wrapRuntimeForTrace = (\n runtime: FragnoRuntime,\n record: (event: ModelCheckerTraceEvent) => void,\n): FragnoRuntime => ({\n time: {\n now: () => {\n const value = runtime.time.now();\n record({ type: \"runtime\", operation: \"time.now\", value });\n return value;\n },\n },\n random: {\n float: () => {\n const value = runtime.random.float();\n record({ type: \"runtime\", operation: \"random.float\", value });\n return value;\n },\n uuid: () => {\n const value = runtime.random.uuid();\n record({ type: \"runtime\", operation: \"random.uuid\", value });\n return value;\n },\n cuid: () => {\n const value = runtime.random.cuid();\n record({ type: \"runtime\", operation: \"random.cuid\", value });\n return value;\n },\n },\n});\n\nconst captureCoreTrace = (\n record: (event: ModelCheckerTraceEvent) => void,\n event: FragnoCoreTraceEvent,\n) => {\n record({ type: \"external\", name: event.type, payload: event });\n};\n\nconst createTraceContext = (\n recorder: ModelCheckerTraceRecorder | undefined,\n traceHasher: ModelCheckerTraceHasher | undefined,\n) => {\n const events = recorder || traceHasher ? ([] as ModelCheckerTraceEvent[]) : null;\n const record = (event: ModelCheckerTraceEvent) => {\n if (events) {\n events.push(event);\n }\n recorder?.(event);\n };\n\n return { events, record };\n};\n\nconst normalizeMutationOperation = (\n op: MutationOperation<AnySchema>,\n): NormalizedMutationOperation => {\n if (op.type === \"create\") {\n return {\n type: \"create\",\n table: op.table,\n namespace: op.namespace,\n values: normalizeForHash(op.values) as Record<string, unknown>,\n generatedExternalId: op.generatedExternalId,\n };\n }\n\n if (op.type === \"update\") {\n return {\n type: \"update\",\n table: op.table,\n namespace: op.namespace,\n id: normalizeForHash(op.id),\n checkVersion: op.checkVersion,\n set: normalizeForHash(op.set) as Record<string, unknown>,\n };\n }\n\n if (op.type === \"delete\") {\n return {\n type: \"delete\",\n table: op.table,\n namespace: op.namespace,\n id: normalizeForHash(op.id),\n checkVersion: op.checkVersion,\n };\n }\n\n return {\n type: \"check\",\n table: op.table,\n namespace: op.namespace,\n id: normalizeForHash(op.id),\n };\n};\n\nconst wrapCreateUnitOfWorkForTrace = <TSchema extends AnySchema, TUowConfig>(\n createUnitOfWork: (name?: string, config?: TUowConfig) => TypedUnitOfWork<TSchema, [], unknown>,\n getTxId: () => number | undefined,\n record: (event: ModelCheckerTraceEvent) => void,\n) => {\n return (name?: string, config?: TUowConfig) => {\n const uow = createUnitOfWork(name, config);\n return new Proxy(uow, {\n get(target, prop, receiver) {\n if (prop === \"executeRetrieve\") {\n return async () => {\n const result = await target.executeRetrieve();\n record({\n type: \"retrieve-output\",\n txId: getTxId(),\n uowName: target.name,\n output: normalizeForHash(result),\n });\n return result;\n };\n }\n\n if (prop === \"executeMutations\") {\n return async () => {\n const txId = getTxId();\n record({\n type: \"mutation-input\",\n txId,\n uowName: target.name,\n operations: target.getMutationOperations().map(normalizeMutationOperation),\n });\n try {\n const result = await target.executeMutations();\n record({\n type: \"mutation-result\",\n txId,\n uowName: target.name,\n success: result.success,\n createdIds: result.success ? target.getCreatedIds().map(normalizeForHash) : [],\n });\n return result;\n } catch (error) {\n record({\n type: \"mutation-result\",\n txId,\n uowName: target.name,\n success: false,\n createdIds: [],\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n };\n }\n\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") {\n return value.bind(target);\n }\n return value;\n },\n });\n };\n};\n\nconst planTransactions = <TSchema extends AnySchema, TUowConfig>(\n transactions: RawUowTransaction<unknown, unknown, TSchema, TUowConfig>[],\n): TransactionPlan => {\n if (transactions.length === 0) {\n throw new Error(\"Model checker requires at least one transaction.\");\n }\n\n const stepsPerTransaction = transactions.map((tx) => {\n if (!tx.retrieve) {\n throw new Error(\"Model checker transactions must define a retrieve step.\");\n }\n return tx.mutate ? 2 : 1;\n });\n\n return { stepsPerTransaction };\n};\n\nconst generateAllSchedules = (stepsPerTransaction: number[]): ModelCheckerStep[][] => {\n const schedules: ModelCheckerStep[][] = [];\n const totalSteps = stepsPerTransaction.reduce((sum, steps) => sum + steps, 0);\n const progress = stepsPerTransaction.map(() => 0);\n const current: ModelCheckerStep[] = [];\n\n const walk = () => {\n if (current.length === totalSteps) {\n schedules.push([...current]);\n return;\n }\n\n for (let txId = 0; txId < stepsPerTransaction.length; txId += 1) {\n if (progress[txId] >= stepsPerTransaction[txId]) {\n continue;\n }\n const phase: ModelCheckerPhase = progress[txId] === 0 ? \"retrieve\" : \"mutate\";\n current.push({ txId, phase });\n progress[txId] += 1;\n walk();\n progress[txId] -= 1;\n current.pop();\n }\n };\n\n walk();\n return schedules;\n};\n\nconst generateRandomSchedule = (\n stepsPerTransaction: number[],\n rng: () => number,\n): ModelCheckerStep[] => {\n const totalSteps = stepsPerTransaction.reduce((sum, steps) => sum + steps, 0);\n const progress = stepsPerTransaction.map(() => 0);\n const schedule: ModelCheckerStep[] = [];\n\n for (let stepIndex = 0; stepIndex < totalSteps; stepIndex += 1) {\n const candidates: number[] = [];\n for (let txId = 0; txId < stepsPerTransaction.length; txId += 1) {\n if (progress[txId] < stepsPerTransaction[txId]) {\n candidates.push(txId);\n }\n }\n const pick = candidates[Math.floor(rng() * candidates.length)];\n const phase: ModelCheckerPhase = progress[pick] === 0 ? \"retrieve\" : \"mutate\";\n schedule.push({ txId: pick, phase });\n progress[pick] += 1;\n }\n\n return schedule;\n};\n\nconst executeSchedule = async <TSchema extends AnySchema, TUowConfig>(\n schedule: ModelCheckerStep[],\n config: ModelCheckerConfig<TSchema, TUowConfig>,\n history: HistoryTracker | null,\n stateHasher: (ctx: ModelCheckerStateHasherContext<TSchema, TUowConfig>) => Promise<string>,\n): Promise<ScheduleExecutionResult> => {\n const traceHashMode = config.traceHashMode ?? \"state\";\n const traceHasher =\n config.traceHasher ?? (traceHashMode === \"state\" ? undefined : defaultTraceHasher);\n const traceContext = createTraceContext(config.traceRecorder, traceHasher);\n const traceEnabled = Boolean(traceContext.events);\n const runtime =\n config.runtime && traceEnabled\n ? wrapRuntimeForTrace(config.runtime, traceContext.record)\n : config.runtime;\n\n const { ctx, cleanup } = await config.createContext();\n try {\n let currentTxId: number | undefined;\n const tracedContext = traceEnabled\n ? {\n ...ctx,\n createUnitOfWork: wrapCreateUnitOfWorkForTrace(\n ctx.createUnitOfWork,\n () => currentTxId,\n traceContext.record,\n ),\n runtime,\n }\n : { ...ctx, runtime };\n\n if (config.setup) {\n await config.setup(tracedContext);\n }\n\n const transactions = config.buildTransactions(tracedContext);\n const transactionState = transactions.map(() => ({ retrieveResult: undefined as unknown }));\n let lastStateHash: string | null = null;\n let newPathsAdded = false;\n\n const runSchedule = async () => {\n for (let stepIndex = 0; stepIndex < schedule.length; stepIndex += 1) {\n const step = schedule[stepIndex];\n if (!step) {\n throw new Error(\"Schedule step is missing.\");\n }\n const tx = transactions[step.txId];\n if (!tx) {\n throw new Error(`Transaction ${step.txId} is missing.`);\n }\n\n currentTxId = step.txId;\n if (traceEnabled) {\n traceContext.record({ type: \"schedule-step\", step, stepIndex });\n }\n\n if (step.phase === \"retrieve\") {\n transactionState[step.txId].retrieveResult = await tx.retrieve(tracedContext);\n } else {\n if (!tx.mutate) {\n throw new Error(`Transaction ${step.txId} does not define a mutate step.`);\n }\n await tx.mutate({\n ...tracedContext,\n retrieveResult: transactionState[step.txId].retrieveResult,\n });\n }\n\n if (history) {\n lastStateHash = await stateHasher({\n schema: config.schema,\n queryEngine: tracedContext.queryEngine,\n });\n const traceHash = traceHasher\n ? await traceHasher({ events: traceContext.events ?? [] })\n : undefined;\n const prefix = schedule.slice(0, stepIndex + 1);\n const added = history.add(\n buildPathKey(resolvePathHash(lastStateHash, traceHash, traceHashMode), prefix),\n );\n if (added) {\n newPathsAdded = true;\n }\n }\n }\n\n currentTxId = undefined;\n\n if (!lastStateHash) {\n lastStateHash = await stateHasher({\n schema: config.schema,\n queryEngine: tracedContext.queryEngine,\n });\n }\n\n const finalTraceHash = traceHasher\n ? await traceHasher({ events: traceContext.events ?? [] })\n : undefined;\n\n return {\n stateHash: lastStateHash,\n newPathsAdded,\n traceHash: finalTraceHash,\n trace: traceContext.events ? { events: traceContext.events } : undefined,\n };\n };\n\n if (traceEnabled) {\n return await runWithTraceRecorder(\n (event) => captureCoreTrace(traceContext.record, event),\n runSchedule,\n );\n }\n\n return await runSchedule();\n } finally {\n if (cleanup) {\n await cleanup();\n }\n }\n};\n\nconst getTransactionPlan = async <TSchema extends AnySchema, TUowConfig>(\n config: ModelCheckerConfig<TSchema, TUowConfig>,\n): Promise<TransactionPlan> => {\n const { ctx, cleanup } = await config.createContext();\n try {\n const context = config.runtime ? { ...ctx, runtime: config.runtime } : ctx;\n if (config.setup) {\n await config.setup(context);\n }\n const transactions = config.buildTransactions(context);\n return planTransactions(transactions);\n } finally {\n if (cleanup) {\n await cleanup();\n }\n }\n};\n\nexport const runModelChecker = async <TSchema extends AnySchema, TUowConfig>(\n config: ModelCheckerConfig<TSchema, TUowConfig>,\n): Promise<ModelCheckerRunResult> => {\n const mode = config.mode ?? \"exhaustive\";\n const historyConfig = config.history ?? defaultHistoryConfig;\n const history = createHistoryTracker(historyConfig);\n const stateHasher = config.stateHasher ?? defaultStateHasher;\n const { stepsPerTransaction } = await getTransactionPlan(config);\n const schedules: ModelCheckerScheduleResult[] = [];\n const maxSchedules = config.maxSchedules;\n const seed = config.seed ?? 1;\n\n if (mode === \"exhaustive\" || mode === \"bounded-exhaustive\") {\n if (mode === \"bounded-exhaustive\") {\n const bounds = config.bounds;\n if (bounds?.maxSteps !== undefined) {\n const totalSteps = stepsPerTransaction.reduce((sum, steps) => sum + steps, 0);\n if (totalSteps > bounds.maxSteps) {\n throw new Error(\n `bounded-exhaustive requires maxSteps >= ${totalSteps}, ` +\n `but received ${bounds.maxSteps}`,\n );\n }\n }\n }\n const allSchedules = generateAllSchedules(stepsPerTransaction);\n for (const schedule of allSchedules) {\n if (maxSchedules !== undefined && schedules.length >= maxSchedules) {\n break;\n }\n const result = await executeSchedule(schedule, config, history, stateHasher);\n schedules.push({\n schedule,\n stateHash: result.stateHash,\n traceHash: result.traceHash,\n trace: result.trace,\n });\n }\n } else if (mode === \"random\") {\n const rng = mulberry32(seed);\n const total = maxSchedules ?? 1;\n for (let i = 0; i < total; i += 1) {\n const schedule = generateRandomSchedule(stepsPerTransaction, rng);\n const result = await executeSchedule(schedule, config, history, stateHasher);\n schedules.push({\n schedule,\n stateHash: result.stateHash,\n traceHash: result.traceHash,\n trace: result.trace,\n });\n }\n } else {\n const rng = mulberry32(seed);\n const stopWhenFrontierExhausted = config.stopWhenFrontierExhausted ?? false;\n let iterations = 0;\n let frontierExhausted = false;\n\n while (!frontierExhausted) {\n if (maxSchedules !== undefined && iterations >= maxSchedules) {\n break;\n }\n const schedule = generateRandomSchedule(stepsPerTransaction, rng);\n const result = await executeSchedule(schedule, config, history, stateHasher);\n schedules.push({\n schedule,\n stateHash: result.stateHash,\n traceHash: result.traceHash,\n trace: result.trace,\n });\n iterations += 1;\n\n if (stopWhenFrontierExhausted && history) {\n frontierExhausted = !result.newPathsAdded;\n }\n }\n }\n\n return {\n schedules,\n visitedPaths: history?.size() ?? 0,\n };\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { internalFragmentDef } from "@fragno-dev/db";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { rm } from "node:fs/promises";
|
|
4
|
+
import { SqlAdapter } from "@fragno-dev/db/adapters/sql";
|
|
5
|
+
import { PGLiteDriverConfig } from "@fragno-dev/db/drivers";
|
|
6
|
+
import { drizzle } from "drizzle-orm/pglite";
|
|
7
|
+
import { KyselyPGlite } from "kysely-pglite";
|
|
8
|
+
import { PGlite } from "@electric-sql/pglite";
|
|
9
|
+
|
|
10
|
+
//#region src/test-adapters/drizzle-pglite.ts
|
|
11
|
+
const createCommonTestContextMethods = (ormMap) => ({ getOrm: (namespace) => {
|
|
12
|
+
const orm = ormMap.get(namespace);
|
|
13
|
+
if (!orm) throw new Error(`No ORM found for namespace: ${String(namespace)}`);
|
|
14
|
+
return orm;
|
|
15
|
+
} });
|
|
16
|
+
const runInternalFragmentMigrations = async (adapter) => {
|
|
17
|
+
const dependencies = internalFragmentDef.dependencies;
|
|
18
|
+
if (!dependencies) return;
|
|
19
|
+
const databaseDeps = dependencies({
|
|
20
|
+
config: {},
|
|
21
|
+
options: {
|
|
22
|
+
databaseAdapter: adapter,
|
|
23
|
+
databaseNamespace: null
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
if (databaseDeps?.schema) {
|
|
27
|
+
await adapter.prepareMigrations(databaseDeps.schema, databaseDeps.namespace).executeWithDriver(adapter.driver, 0);
|
|
28
|
+
return {
|
|
29
|
+
schema: databaseDeps.schema,
|
|
30
|
+
namespace: databaseDeps.namespace
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const resolveSchemaName = (adapter, namespace) => {
|
|
35
|
+
if (adapter.namingStrategy.namespaceScope !== "schema") return null;
|
|
36
|
+
if (!namespace || namespace.length === 0) return null;
|
|
37
|
+
return adapter.namingStrategy.namespaceToSchema(namespace);
|
|
38
|
+
};
|
|
39
|
+
async function createDrizzlePgliteAdapter(config, schemas) {
|
|
40
|
+
const databasePath = config.databasePath;
|
|
41
|
+
let internalSchemaConfig;
|
|
42
|
+
const createDatabase = async () => {
|
|
43
|
+
const pglite = new PGlite(databasePath);
|
|
44
|
+
const { dialect } = new KyselyPGlite(pglite);
|
|
45
|
+
const adapter$1 = new SqlAdapter({
|
|
46
|
+
dialect,
|
|
47
|
+
driverConfig: new PGLiteDriverConfig(),
|
|
48
|
+
uowConfig: config.uowConfig
|
|
49
|
+
});
|
|
50
|
+
internalSchemaConfig = await runInternalFragmentMigrations(adapter$1);
|
|
51
|
+
const ormMap$1 = /* @__PURE__ */ new Map();
|
|
52
|
+
for (const { schema, namespace, migrateToVersion } of schemas) {
|
|
53
|
+
const preparedMigrations = adapter$1.prepareMigrations(schema, namespace);
|
|
54
|
+
if (migrateToVersion !== void 0) await preparedMigrations.execute(0, migrateToVersion, { updateVersionInMigration: false });
|
|
55
|
+
else await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });
|
|
56
|
+
const orm = adapter$1.createQueryEngine(schema, namespace);
|
|
57
|
+
ormMap$1.set(namespace, orm);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
drizzle: drizzle(pglite),
|
|
61
|
+
adapter: adapter$1,
|
|
62
|
+
ormMap: ormMap$1
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
const { drizzle: drizzleDb, adapter, ormMap } = await createDatabase();
|
|
66
|
+
const resetDatabase = async () => {
|
|
67
|
+
if (databasePath && databasePath !== ":memory:") throw new Error("resetDatabase is only supported for in-memory databases");
|
|
68
|
+
const schemasToTruncate = internalSchemaConfig ? [internalSchemaConfig, ...schemas] : schemas;
|
|
69
|
+
const useTruncate = adapter.adapterMetadata?.databaseType === "postgresql";
|
|
70
|
+
for (const { schema, namespace } of schemasToTruncate) {
|
|
71
|
+
const tableNames = Object.keys(schema.tables);
|
|
72
|
+
if (useTruncate) {
|
|
73
|
+
const qualifiedTables = tableNames.map((tableName) => {
|
|
74
|
+
const physicalTableName = adapter.namingStrategy.tableName(tableName, namespace);
|
|
75
|
+
const schemaName = resolveSchemaName(adapter, namespace);
|
|
76
|
+
return schemaName ? `"${schemaName}"."${physicalTableName}"` : `"${physicalTableName}"`;
|
|
77
|
+
});
|
|
78
|
+
if (qualifiedTables.length > 0) await drizzleDb.execute(`TRUNCATE ${qualifiedTables.join(", ")} CASCADE`);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
for (const tableName of tableNames.slice().reverse()) {
|
|
82
|
+
const physicalTableName = adapter.namingStrategy.tableName(tableName, namespace);
|
|
83
|
+
const schemaName = resolveSchemaName(adapter, namespace);
|
|
84
|
+
const qualifiedTable = schemaName ? `"${schemaName}"."${physicalTableName}"` : `"${physicalTableName}"`;
|
|
85
|
+
await drizzleDb.execute(`DELETE FROM ${qualifiedTable}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const cleanup = async () => {
|
|
90
|
+
await adapter.close();
|
|
91
|
+
if (databasePath && databasePath !== ":memory:" && existsSync(databasePath)) await rm(databasePath, {
|
|
92
|
+
recursive: true,
|
|
93
|
+
force: true
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
return {
|
|
97
|
+
testContext: {
|
|
98
|
+
get drizzle() {
|
|
99
|
+
return drizzleDb;
|
|
100
|
+
},
|
|
101
|
+
get adapter() {
|
|
102
|
+
return adapter;
|
|
103
|
+
},
|
|
104
|
+
...createCommonTestContextMethods(ormMap),
|
|
105
|
+
resetDatabase,
|
|
106
|
+
cleanup
|
|
107
|
+
},
|
|
108
|
+
get adapter() {
|
|
109
|
+
return adapter;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
//#endregion
|
|
115
|
+
export { createDrizzlePgliteAdapter };
|
|
116
|
+
//# sourceMappingURL=drizzle-pglite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drizzle-pglite.js","names":["internalSchemaConfig: SchemaConfig | undefined","adapter","ormMap"],"sources":["../../src/test-adapters/drizzle-pglite.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { rm } from \"node:fs/promises\";\n\nimport { SqlAdapter } from \"@fragno-dev/db/adapters/sql\";\nimport { PGLiteDriverConfig } from \"@fragno-dev/db/drivers\";\nimport type { SimpleQueryInterface } from \"@fragno-dev/db/query\";\nimport type { AnySchema } from \"@fragno-dev/db/schema\";\nimport { drizzle } from \"drizzle-orm/pglite\";\nimport { KyselyPGlite } from \"kysely-pglite\";\n\nimport { internalFragmentDef } from \"@fragno-dev/db\";\n\nimport { PGlite } from \"@electric-sql/pglite\";\n\nimport type { AdapterFactoryResult, DrizzlePgliteAdapter, SchemaConfig } from \"../adapters\";\n\nconst createCommonTestContextMethods = (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ormMap: Map<string | null, SimpleQueryInterface<any, any>>,\n) => ({\n getOrm: <TSchema extends AnySchema>(namespace: string | null): SimpleQueryInterface<TSchema> => {\n const orm = ormMap.get(namespace);\n if (!orm) {\n throw new Error(`No ORM found for namespace: ${String(namespace)}`);\n }\n return orm as SimpleQueryInterface<TSchema>;\n },\n});\n\nconst runInternalFragmentMigrations = async (\n adapter: SqlAdapter,\n): Promise<SchemaConfig | undefined> => {\n const dependencies = internalFragmentDef.dependencies;\n if (!dependencies) {\n return undefined;\n }\n\n const databaseDeps = dependencies({\n config: {},\n options: { databaseAdapter: adapter, databaseNamespace: null },\n });\n if (databaseDeps?.schema) {\n const migrations = adapter.prepareMigrations(databaseDeps.schema, databaseDeps.namespace);\n await migrations.executeWithDriver(adapter.driver, 0);\n return { schema: databaseDeps.schema, namespace: databaseDeps.namespace };\n }\n return undefined;\n};\n\nconst resolveSchemaName = (adapter: SqlAdapter, namespace: string | null): string | null => {\n if (adapter.namingStrategy.namespaceScope !== \"schema\") {\n return null;\n }\n if (!namespace || namespace.length === 0) {\n return null;\n }\n return adapter.namingStrategy.namespaceToSchema(namespace);\n};\n\nexport async function createDrizzlePgliteAdapter(\n config: DrizzlePgliteAdapter,\n schemas: SchemaConfig[],\n): Promise<AdapterFactoryResult<DrizzlePgliteAdapter>> {\n const databasePath = config.databasePath;\n let internalSchemaConfig: SchemaConfig | undefined;\n\n const createDatabase = async () => {\n const pglite = new PGlite(databasePath);\n const { dialect } = new KyselyPGlite(pglite);\n\n const adapter = new SqlAdapter({\n dialect,\n driverConfig: new PGLiteDriverConfig(),\n uowConfig: config.uowConfig,\n });\n\n internalSchemaConfig = await runInternalFragmentMigrations(adapter);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ormMap = new Map<string | null, SimpleQueryInterface<any, any>>();\n for (const { schema, namespace, migrateToVersion } of schemas) {\n const preparedMigrations = adapter.prepareMigrations(schema, namespace);\n if (migrateToVersion !== undefined) {\n await preparedMigrations.execute(0, migrateToVersion, {\n updateVersionInMigration: false,\n });\n } else {\n await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });\n }\n\n const orm = adapter.createQueryEngine(schema, namespace);\n ormMap.set(namespace, orm);\n }\n\n // oxlint-disable-next-line typescript/no-explicit-any\n const db = drizzle(pglite) as any;\n\n return { drizzle: db, adapter, ormMap };\n };\n\n const { drizzle: drizzleDb, adapter, ormMap } = await createDatabase();\n\n const resetDatabase = async () => {\n if (databasePath && databasePath !== \":memory:\") {\n throw new Error(\"resetDatabase is only supported for in-memory databases\");\n }\n\n const schemasToTruncate = internalSchemaConfig ? [internalSchemaConfig, ...schemas] : schemas;\n const useTruncate = adapter.adapterMetadata?.databaseType === \"postgresql\";\n\n for (const { schema, namespace } of schemasToTruncate) {\n const tableNames = Object.keys(schema.tables);\n if (useTruncate) {\n const qualifiedTables = tableNames.map((tableName) => {\n const physicalTableName = adapter.namingStrategy.tableName(tableName, namespace);\n const schemaName = resolveSchemaName(adapter, namespace);\n return schemaName ? `\"${schemaName}\".\"${physicalTableName}\"` : `\"${physicalTableName}\"`;\n });\n if (qualifiedTables.length > 0) {\n await drizzleDb.execute(`TRUNCATE ${qualifiedTables.join(\", \")} CASCADE`);\n }\n continue;\n }\n\n for (const tableName of tableNames.slice().reverse()) {\n const physicalTableName = adapter.namingStrategy.tableName(tableName, namespace);\n const schemaName = resolveSchemaName(adapter, namespace);\n const qualifiedTable = schemaName\n ? `\"${schemaName}\".\"${physicalTableName}\"`\n : `\"${physicalTableName}\"`;\n await drizzleDb.execute(`DELETE FROM ${qualifiedTable}`);\n }\n }\n };\n\n const cleanup = async () => {\n await adapter.close();\n\n if (databasePath && databasePath !== \":memory:\" && existsSync(databasePath)) {\n await rm(databasePath, { recursive: true, force: true });\n }\n };\n\n const commonMethods = createCommonTestContextMethods(ormMap);\n\n return {\n testContext: {\n get drizzle() {\n return drizzleDb;\n },\n get adapter() {\n return adapter;\n },\n ...commonMethods,\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { InMemoryAdapter } from "@fragno-dev/db/adapters/in-memory";
|
|
2
|
+
|
|
3
|
+
//#region src/test-adapters/in-memory.ts
|
|
4
|
+
const createCommonTestContextMethods = (ormMap) => ({ getOrm: (namespace) => {
|
|
5
|
+
const orm = ormMap.get(namespace);
|
|
6
|
+
if (!orm) throw new Error(`No ORM found for namespace: ${String(namespace)}`);
|
|
7
|
+
return orm;
|
|
8
|
+
} });
|
|
9
|
+
async function createInMemoryAdapter(config, schemas) {
|
|
10
|
+
const adapter = new InMemoryAdapter(config.options);
|
|
11
|
+
const ormMap = /* @__PURE__ */ new Map();
|
|
12
|
+
for (const { schema, namespace } of schemas) {
|
|
13
|
+
const orm = adapter.createQueryEngine(schema, namespace);
|
|
14
|
+
ormMap.set(namespace, orm);
|
|
15
|
+
}
|
|
16
|
+
const resetDatabase = async () => {
|
|
17
|
+
await adapter.reset();
|
|
18
|
+
};
|
|
19
|
+
const cleanup = async () => {
|
|
20
|
+
await adapter.close();
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
testContext: {
|
|
24
|
+
get adapter() {
|
|
25
|
+
return adapter;
|
|
26
|
+
},
|
|
27
|
+
...createCommonTestContextMethods(ormMap),
|
|
28
|
+
resetDatabase,
|
|
29
|
+
cleanup
|
|
30
|
+
},
|
|
31
|
+
get adapter() {
|
|
32
|
+
return adapter;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
export { createInMemoryAdapter };
|
|
39
|
+
//# sourceMappingURL=in-memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in-memory.js","names":[],"sources":["../../src/test-adapters/in-memory.ts"],"sourcesContent":["import { InMemoryAdapter } from \"@fragno-dev/db/adapters/in-memory\";\nimport type { SimpleQueryInterface } from \"@fragno-dev/db/query\";\nimport type { AnySchema } from \"@fragno-dev/db/schema\";\n\nimport type { AdapterFactoryResult, InMemoryAdapterConfig, SchemaConfig } from \"../adapters\";\n\nconst createCommonTestContextMethods = (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ormMap: Map<string | null, SimpleQueryInterface<any, any>>,\n) => ({\n getOrm: <TSchema extends AnySchema>(namespace: string | null): SimpleQueryInterface<TSchema> => {\n const orm = ormMap.get(namespace);\n if (!orm) {\n throw new Error(`No ORM found for namespace: ${String(namespace)}`);\n }\n return orm as SimpleQueryInterface<TSchema>;\n },\n});\n\nexport async function createInMemoryAdapter(\n config: InMemoryAdapterConfig,\n schemas: SchemaConfig[],\n): Promise<AdapterFactoryResult<InMemoryAdapterConfig>> {\n const adapter = new InMemoryAdapter(config.options);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ormMap = new Map<string | null, SimpleQueryInterface<any, any>>();\n for (const { schema, namespace } of schemas) {\n const orm = adapter.createQueryEngine(schema, namespace);\n ormMap.set(namespace, orm);\n }\n\n const resetDatabase = async () => {\n await adapter.reset();\n };\n\n const cleanup = async () => {\n await adapter.close();\n };\n\n const commonMethods = createCommonTestContextMethods(ormMap);\n\n return {\n testContext: {\n get adapter() {\n return adapter;\n },\n ...commonMethods,\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n"],"mappings":";;;AAMA,MAAM,kCAEJ,YACI,EACJ,SAAoC,cAA4D;CAC9F,MAAM,MAAM,OAAO,IAAI,UAAU;AACjC,KAAI,CAAC,IACH,OAAM,IAAI,MAAM,+BAA+B,OAAO,UAAU,GAAG;AAErE,QAAO;GAEV;AAED,eAAsB,sBACpB,QACA,SACsD;CACtD,MAAM,UAAU,IAAI,gBAAgB,OAAO,QAAQ;CAGnD,MAAM,yBAAS,IAAI,KAAoD;AACvE,MAAK,MAAM,EAAE,QAAQ,eAAe,SAAS;EAC3C,MAAM,MAAM,QAAQ,kBAAkB,QAAQ,UAAU;AACxD,SAAO,IAAI,WAAW,IAAI;;CAG5B,MAAM,gBAAgB,YAAY;AAChC,QAAM,QAAQ,OAAO;;CAGvB,MAAM,UAAU,YAAY;AAC1B,QAAM,QAAQ,OAAO;;AAKvB,QAAO;EACL,aAAa;GACX,IAAI,UAAU;AACZ,WAAO;;GAET,GAPkB,+BAA+B,OAAO;GAQxD;GACA;GACD;EACD,IAAI,UAAU;AACZ,UAAO;;EAEV"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { internalFragmentDef } from "@fragno-dev/db";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { rm } from "node:fs/promises";
|
|
4
|
+
import { SqlAdapter } from "@fragno-dev/db/adapters/sql";
|
|
5
|
+
import { PGLiteDriverConfig } from "@fragno-dev/db/drivers";
|
|
6
|
+
import { KyselyPGlite } from "kysely-pglite";
|
|
7
|
+
import { Kysely } from "kysely";
|
|
8
|
+
|
|
9
|
+
//#region src/test-adapters/kysely-pglite.ts
|
|
10
|
+
const createCommonTestContextMethods = (ormMap) => ({ getOrm: (namespace) => {
|
|
11
|
+
const orm = ormMap.get(namespace);
|
|
12
|
+
if (!orm) throw new Error(`No ORM found for namespace: ${String(namespace)}`);
|
|
13
|
+
return orm;
|
|
14
|
+
} });
|
|
15
|
+
const runInternalFragmentMigrations = async (adapter) => {
|
|
16
|
+
const dependencies = internalFragmentDef.dependencies;
|
|
17
|
+
if (!dependencies) return;
|
|
18
|
+
const databaseDeps = dependencies({
|
|
19
|
+
config: {},
|
|
20
|
+
options: {
|
|
21
|
+
databaseAdapter: adapter,
|
|
22
|
+
databaseNamespace: null
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
if (databaseDeps?.schema) {
|
|
26
|
+
await adapter.prepareMigrations(databaseDeps.schema, databaseDeps.namespace).executeWithDriver(adapter.driver, 0);
|
|
27
|
+
return {
|
|
28
|
+
schema: databaseDeps.schema,
|
|
29
|
+
namespace: databaseDeps.namespace
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const resolveSchemaName = (adapter, namespace) => {
|
|
34
|
+
if (adapter.namingStrategy.namespaceScope !== "schema") return null;
|
|
35
|
+
if (!namespace || namespace.length === 0) return null;
|
|
36
|
+
return adapter.namingStrategy.namespaceToSchema(namespace);
|
|
37
|
+
};
|
|
38
|
+
async function createKyselyPgliteAdapter(config, schemas) {
|
|
39
|
+
const databasePath = config.databasePath;
|
|
40
|
+
let internalSchemaConfig;
|
|
41
|
+
const createDatabase = async () => {
|
|
42
|
+
const kyselyPglite$1 = await KyselyPGlite.create(databasePath);
|
|
43
|
+
const kysely$1 = new Kysely({ dialect: kyselyPglite$1.dialect });
|
|
44
|
+
const adapter$1 = new SqlAdapter({
|
|
45
|
+
dialect: kyselyPglite$1.dialect,
|
|
46
|
+
driverConfig: new PGLiteDriverConfig(),
|
|
47
|
+
uowConfig: config.uowConfig
|
|
48
|
+
});
|
|
49
|
+
internalSchemaConfig = await runInternalFragmentMigrations(adapter$1);
|
|
50
|
+
const ormMap$1 = /* @__PURE__ */ new Map();
|
|
51
|
+
for (const { schema, namespace, migrateToVersion } of schemas) {
|
|
52
|
+
const preparedMigrations = adapter$1.prepareMigrations(schema, namespace);
|
|
53
|
+
if (migrateToVersion !== void 0) await preparedMigrations.execute(0, migrateToVersion, { updateVersionInMigration: false });
|
|
54
|
+
else await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });
|
|
55
|
+
const orm = adapter$1.createQueryEngine(schema, namespace);
|
|
56
|
+
ormMap$1.set(namespace, orm);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
kysely: kysely$1,
|
|
60
|
+
adapter: adapter$1,
|
|
61
|
+
kyselyPglite: kyselyPglite$1,
|
|
62
|
+
ormMap: ormMap$1
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
const { kysely, adapter, kyselyPglite, ormMap } = await createDatabase();
|
|
66
|
+
const resetDatabase = async () => {
|
|
67
|
+
if (databasePath && databasePath !== ":memory:") throw new Error("resetDatabase is only supported for in-memory databases");
|
|
68
|
+
const schemasToTruncate = internalSchemaConfig ? [internalSchemaConfig, ...schemas] : schemas;
|
|
69
|
+
for (const { schema, namespace } of schemasToTruncate) for (const tableName of Object.keys(schema.tables)) {
|
|
70
|
+
const physicalTableName = adapter.namingStrategy.tableName(tableName, namespace);
|
|
71
|
+
const schemaName = resolveSchemaName(adapter, namespace);
|
|
72
|
+
await (schemaName ? kysely.withSchema(schemaName) : kysely).deleteFrom(physicalTableName).execute();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const cleanup = async () => {
|
|
76
|
+
await kysely.destroy();
|
|
77
|
+
try {
|
|
78
|
+
await kyselyPglite.client.close();
|
|
79
|
+
} catch {}
|
|
80
|
+
if (databasePath && databasePath !== ":memory:" && existsSync(databasePath)) await rm(databasePath, {
|
|
81
|
+
recursive: true,
|
|
82
|
+
force: true
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
testContext: {
|
|
87
|
+
get kysely() {
|
|
88
|
+
return kysely;
|
|
89
|
+
},
|
|
90
|
+
get adapter() {
|
|
91
|
+
return adapter;
|
|
92
|
+
},
|
|
93
|
+
...createCommonTestContextMethods(ormMap),
|
|
94
|
+
resetDatabase,
|
|
95
|
+
cleanup
|
|
96
|
+
},
|
|
97
|
+
get adapter() {
|
|
98
|
+
return adapter;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
//#endregion
|
|
104
|
+
export { createKyselyPgliteAdapter };
|
|
105
|
+
//# sourceMappingURL=kysely-pglite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kysely-pglite.js","names":["internalSchemaConfig: SchemaConfig | undefined","kyselyPglite","kysely","adapter","ormMap"],"sources":["../../src/test-adapters/kysely-pglite.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { rm } from \"node:fs/promises\";\n\nimport { SqlAdapter } from \"@fragno-dev/db/adapters/sql\";\nimport { PGLiteDriverConfig } from \"@fragno-dev/db/drivers\";\nimport type { SimpleQueryInterface } from \"@fragno-dev/db/query\";\nimport type { AnySchema } from \"@fragno-dev/db/schema\";\nimport { Kysely } from \"kysely\";\nimport { KyselyPGlite } from \"kysely-pglite\";\n\nimport { internalFragmentDef } from \"@fragno-dev/db\";\n\nimport type { AdapterFactoryResult, KyselyPgliteAdapter, SchemaConfig } from \"../adapters\";\n\nconst createCommonTestContextMethods = (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ormMap: Map<string | null, SimpleQueryInterface<any, any>>,\n) => ({\n getOrm: <TSchema extends AnySchema>(namespace: string | null): SimpleQueryInterface<TSchema> => {\n const orm = ormMap.get(namespace);\n if (!orm) {\n throw new Error(`No ORM found for namespace: ${String(namespace)}`);\n }\n return orm as SimpleQueryInterface<TSchema>;\n },\n});\n\nconst runInternalFragmentMigrations = async (\n adapter: SqlAdapter,\n): Promise<SchemaConfig | undefined> => {\n const dependencies = internalFragmentDef.dependencies;\n if (!dependencies) {\n return undefined;\n }\n\n const databaseDeps = dependencies({\n config: {},\n options: { databaseAdapter: adapter, databaseNamespace: null },\n });\n if (databaseDeps?.schema) {\n const migrations = adapter.prepareMigrations(databaseDeps.schema, databaseDeps.namespace);\n await migrations.executeWithDriver(adapter.driver, 0);\n return { schema: databaseDeps.schema, namespace: databaseDeps.namespace };\n }\n return undefined;\n};\n\nconst resolveSchemaName = (adapter: SqlAdapter, namespace: string | null): string | null => {\n if (adapter.namingStrategy.namespaceScope !== \"schema\") {\n return null;\n }\n if (!namespace || namespace.length === 0) {\n return null;\n }\n return adapter.namingStrategy.namespaceToSchema(namespace);\n};\n\nexport async function createKyselyPgliteAdapter(\n config: KyselyPgliteAdapter,\n schemas: SchemaConfig[],\n): Promise<AdapterFactoryResult<KyselyPgliteAdapter>> {\n const databasePath = config.databasePath;\n let internalSchemaConfig: SchemaConfig | undefined;\n\n const createDatabase = async () => {\n const kyselyPglite = await KyselyPGlite.create(databasePath);\n\n // oxlint-disable-next-line typescript/no-explicit-any\n const kysely = new Kysely<any>({\n dialect: kyselyPglite.dialect,\n });\n\n const adapter = new SqlAdapter({\n dialect: kyselyPglite.dialect,\n driverConfig: new PGLiteDriverConfig(),\n uowConfig: config.uowConfig,\n });\n internalSchemaConfig = await runInternalFragmentMigrations(adapter);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ormMap = new Map<string | null, SimpleQueryInterface<any, any>>();\n\n for (const { schema, namespace, migrateToVersion } of schemas) {\n const preparedMigrations = adapter.prepareMigrations(schema, namespace);\n if (migrateToVersion !== undefined) {\n await preparedMigrations.execute(0, migrateToVersion, {\n updateVersionInMigration: false,\n });\n } else {\n await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });\n }\n\n const orm = adapter.createQueryEngine(schema, namespace);\n ormMap.set(namespace, orm);\n }\n\n return { kysely, adapter, kyselyPglite, ormMap };\n };\n\n const { kysely, adapter, kyselyPglite, ormMap } = await createDatabase();\n\n const resetDatabase = async () => {\n if (databasePath && databasePath !== \":memory:\") {\n throw new Error(\"resetDatabase is only supported for in-memory databases\");\n }\n\n const schemasToTruncate = internalSchemaConfig ? [internalSchemaConfig, ...schemas] : schemas;\n\n for (const { schema, namespace } of schemasToTruncate) {\n for (const tableName of Object.keys(schema.tables)) {\n const physicalTableName = adapter.namingStrategy.tableName(tableName, namespace);\n const schemaName = resolveSchemaName(adapter, namespace);\n const scopedKysely = schemaName ? kysely.withSchema(schemaName) : kysely;\n await scopedKysely.deleteFrom(physicalTableName).execute();\n }\n }\n };\n\n const cleanup = async () => {\n await kysely.destroy();\n\n try {\n await kyselyPglite.client.close();\n } catch {\n // Ignore if already closed\n }\n\n if (databasePath && databasePath !== \":memory:\" && existsSync(databasePath)) {\n await rm(databasePath, { recursive: true, force: true });\n }\n };\n\n const commonMethods = createCommonTestContextMethods(ormMap);\n\n return {\n testContext: {\n get kysely() {\n return kysely;\n },\n get adapter() {\n return adapter;\n },\n ...commonMethods,\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n"],"mappings":";;;;;;;;;AAcA,MAAM,kCAEJ,YACI,EACJ,SAAoC,cAA4D;CAC9F,MAAM,MAAM,OAAO,IAAI,UAAU;AACjC,KAAI,CAAC,IACH,OAAM,IAAI,MAAM,+BAA+B,OAAO,UAAU,GAAG;AAErE,QAAO;GAEV;AAED,MAAM,gCAAgC,OACpC,YACsC;CACtC,MAAM,eAAe,oBAAoB;AACzC,KAAI,CAAC,aACH;CAGF,MAAM,eAAe,aAAa;EAChC,QAAQ,EAAE;EACV,SAAS;GAAE,iBAAiB;GAAS,mBAAmB;GAAM;EAC/D,CAAC;AACF,KAAI,cAAc,QAAQ;AAExB,QADmB,QAAQ,kBAAkB,aAAa,QAAQ,aAAa,UAAU,CACxE,kBAAkB,QAAQ,QAAQ,EAAE;AACrD,SAAO;GAAE,QAAQ,aAAa;GAAQ,WAAW,aAAa;GAAW;;;AAK7E,MAAM,qBAAqB,SAAqB,cAA4C;AAC1F,KAAI,QAAQ,eAAe,mBAAmB,SAC5C,QAAO;AAET,KAAI,CAAC,aAAa,UAAU,WAAW,EACrC,QAAO;AAET,QAAO,QAAQ,eAAe,kBAAkB,UAAU;;AAG5D,eAAsB,0BACpB,QACA,SACoD;CACpD,MAAM,eAAe,OAAO;CAC5B,IAAIA;CAEJ,MAAM,iBAAiB,YAAY;EACjC,MAAMC,iBAAe,MAAM,aAAa,OAAO,aAAa;EAG5D,MAAMC,WAAS,IAAI,OAAY,EAC7B,SAASD,eAAa,SACvB,CAAC;EAEF,MAAME,YAAU,IAAI,WAAW;GAC7B,SAASF,eAAa;GACtB,cAAc,IAAI,oBAAoB;GACtC,WAAW,OAAO;GACnB,CAAC;AACF,yBAAuB,MAAM,8BAA8BE,UAAQ;EAGnE,MAAMC,2BAAS,IAAI,KAAoD;AAEvE,OAAK,MAAM,EAAE,QAAQ,WAAW,sBAAsB,SAAS;GAC7D,MAAM,qBAAqBD,UAAQ,kBAAkB,QAAQ,UAAU;AACvE,OAAI,qBAAqB,OACvB,OAAM,mBAAmB,QAAQ,GAAG,kBAAkB,EACpD,0BAA0B,OAC3B,CAAC;OAEF,OAAM,mBAAmB,QAAQ,GAAG,OAAO,SAAS,EAAE,0BAA0B,OAAO,CAAC;GAG1F,MAAM,MAAMA,UAAQ,kBAAkB,QAAQ,UAAU;AACxD,YAAO,IAAI,WAAW,IAAI;;AAG5B,SAAO;GAAE;GAAQ;GAAS;GAAc;GAAQ;;CAGlD,MAAM,EAAE,QAAQ,SAAS,cAAc,WAAW,MAAM,gBAAgB;CAExE,MAAM,gBAAgB,YAAY;AAChC,MAAI,gBAAgB,iBAAiB,WACnC,OAAM,IAAI,MAAM,0DAA0D;EAG5E,MAAM,oBAAoB,uBAAuB,CAAC,sBAAsB,GAAG,QAAQ,GAAG;AAEtF,OAAK,MAAM,EAAE,QAAQ,eAAe,kBAClC,MAAK,MAAM,aAAa,OAAO,KAAK,OAAO,OAAO,EAAE;GAClD,MAAM,oBAAoB,QAAQ,eAAe,UAAU,WAAW,UAAU;GAChF,MAAM,aAAa,kBAAkB,SAAS,UAAU;AAExD,UADqB,aAAa,OAAO,WAAW,WAAW,GAAG,QAC/C,WAAW,kBAAkB,CAAC,SAAS;;;CAKhE,MAAM,UAAU,YAAY;AAC1B,QAAM,OAAO,SAAS;AAEtB,MAAI;AACF,SAAM,aAAa,OAAO,OAAO;UAC3B;AAIR,MAAI,gBAAgB,iBAAiB,cAAc,WAAW,aAAa,CACzE,OAAM,GAAG,cAAc;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;;AAM5D,QAAO;EACL,aAAa;GACX,IAAI,SAAS;AACX,WAAO;;GAET,IAAI,UAAU;AACZ,WAAO;;GAET,GAVkB,+BAA+B,OAAO;GAWxD;GACA;GACD;EACD,IAAI,UAAU;AACZ,UAAO;;EAEV"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { internalFragmentDef } from "@fragno-dev/db";
|
|
2
|
+
import { SqlAdapter } from "@fragno-dev/db/adapters/sql";
|
|
3
|
+
import { SQLocalDriverConfig } from "@fragno-dev/db/drivers";
|
|
4
|
+
import { Kysely } from "kysely";
|
|
5
|
+
import { SQLocalKysely } from "sqlocal/kysely";
|
|
6
|
+
|
|
7
|
+
//#region src/test-adapters/kysely-sqlite.ts
|
|
8
|
+
const createCommonTestContextMethods = (ormMap) => ({ getOrm: (namespace) => {
|
|
9
|
+
const orm = ormMap.get(namespace);
|
|
10
|
+
if (!orm) throw new Error(`No ORM found for namespace: ${String(namespace)}`);
|
|
11
|
+
return orm;
|
|
12
|
+
} });
|
|
13
|
+
const runInternalFragmentMigrations = async (adapter) => {
|
|
14
|
+
const dependencies = internalFragmentDef.dependencies;
|
|
15
|
+
if (!dependencies) return;
|
|
16
|
+
const databaseDeps = dependencies({
|
|
17
|
+
config: {},
|
|
18
|
+
options: {
|
|
19
|
+
databaseAdapter: adapter,
|
|
20
|
+
databaseNamespace: null
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
if (databaseDeps?.schema) {
|
|
24
|
+
await adapter.prepareMigrations(databaseDeps.schema, databaseDeps.namespace).executeWithDriver(adapter.driver, 0);
|
|
25
|
+
return {
|
|
26
|
+
schema: databaseDeps.schema,
|
|
27
|
+
namespace: databaseDeps.namespace
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
async function createKyselySqliteAdapter(config, schemas) {
|
|
32
|
+
let internalSchemaConfig;
|
|
33
|
+
const createDatabase = async () => {
|
|
34
|
+
const { dialect } = new SQLocalKysely(":memory:");
|
|
35
|
+
const kysely$1 = new Kysely({ dialect });
|
|
36
|
+
const adapter$1 = new SqlAdapter({
|
|
37
|
+
dialect,
|
|
38
|
+
driverConfig: new SQLocalDriverConfig(),
|
|
39
|
+
uowConfig: config.uowConfig
|
|
40
|
+
});
|
|
41
|
+
internalSchemaConfig = await runInternalFragmentMigrations(adapter$1);
|
|
42
|
+
const ormMap$1 = /* @__PURE__ */ new Map();
|
|
43
|
+
for (const { schema, namespace, migrateToVersion } of schemas) {
|
|
44
|
+
const preparedMigrations = adapter$1.prepareMigrations(schema, namespace);
|
|
45
|
+
if (migrateToVersion !== void 0) await preparedMigrations.execute(0, migrateToVersion, { updateVersionInMigration: false });
|
|
46
|
+
else await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });
|
|
47
|
+
const orm = adapter$1.createQueryEngine(schema, namespace);
|
|
48
|
+
ormMap$1.set(namespace, orm);
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
kysely: kysely$1,
|
|
52
|
+
adapter: adapter$1,
|
|
53
|
+
ormMap: ormMap$1
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
let { kysely, adapter, ormMap } = await createDatabase();
|
|
57
|
+
const resetDatabase = async () => {
|
|
58
|
+
const schemasToTruncate = internalSchemaConfig ? [internalSchemaConfig, ...schemas] : schemas;
|
|
59
|
+
for (const { schema, namespace } of schemasToTruncate) for (const tableName of Object.keys(schema.tables)) {
|
|
60
|
+
const physicalTableName = adapter.namingStrategy.tableName(tableName, namespace);
|
|
61
|
+
await kysely.deleteFrom(physicalTableName).execute();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const cleanup = async () => {
|
|
65
|
+
await kysely.destroy();
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
testContext: {
|
|
69
|
+
get kysely() {
|
|
70
|
+
return kysely;
|
|
71
|
+
},
|
|
72
|
+
get adapter() {
|
|
73
|
+
return adapter;
|
|
74
|
+
},
|
|
75
|
+
...createCommonTestContextMethods(ormMap),
|
|
76
|
+
resetDatabase,
|
|
77
|
+
cleanup
|
|
78
|
+
},
|
|
79
|
+
get adapter() {
|
|
80
|
+
return adapter;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
export { createKyselySqliteAdapter };
|
|
87
|
+
//# sourceMappingURL=kysely-sqlite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kysely-sqlite.js","names":["internalSchemaConfig: SchemaConfig | undefined","kysely","adapter","ormMap"],"sources":["../../src/test-adapters/kysely-sqlite.ts"],"sourcesContent":["import { SqlAdapter } from \"@fragno-dev/db/adapters/sql\";\nimport { SQLocalDriverConfig } from \"@fragno-dev/db/drivers\";\nimport type { SimpleQueryInterface } from \"@fragno-dev/db/query\";\nimport type { AnySchema } from \"@fragno-dev/db/schema\";\nimport { Kysely } from \"kysely\";\nimport { SQLocalKysely } from \"sqlocal/kysely\";\n\nimport { internalFragmentDef } from \"@fragno-dev/db\";\n\nimport type { KyselySqliteAdapter, AdapterFactoryResult, SchemaConfig } from \"../adapters\";\n\nconst createCommonTestContextMethods = (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ormMap: Map<string | null, SimpleQueryInterface<any, any>>,\n) => ({\n getOrm: <TSchema extends AnySchema>(namespace: string | null): SimpleQueryInterface<TSchema> => {\n const orm = ormMap.get(namespace);\n if (!orm) {\n throw new Error(`No ORM found for namespace: ${String(namespace)}`);\n }\n return orm as SimpleQueryInterface<TSchema>;\n },\n});\n\nconst runInternalFragmentMigrations = async (\n adapter: SqlAdapter,\n): Promise<SchemaConfig | undefined> => {\n const dependencies = internalFragmentDef.dependencies;\n if (!dependencies) {\n return undefined;\n }\n\n const databaseDeps = dependencies({\n config: {},\n options: { databaseAdapter: adapter, databaseNamespace: null },\n });\n if (databaseDeps?.schema) {\n const migrations = adapter.prepareMigrations(databaseDeps.schema, databaseDeps.namespace);\n await migrations.executeWithDriver(adapter.driver, 0);\n return { schema: databaseDeps.schema, namespace: databaseDeps.namespace };\n }\n return undefined;\n};\n\nexport async function createKyselySqliteAdapter(\n config: KyselySqliteAdapter,\n schemas: SchemaConfig[],\n): Promise<AdapterFactoryResult<KyselySqliteAdapter>> {\n let internalSchemaConfig: SchemaConfig | undefined;\n\n const createDatabase = async () => {\n const { dialect } = new SQLocalKysely(\":memory:\");\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const kysely = new Kysely<any>({\n dialect,\n });\n\n const adapter = new SqlAdapter({\n dialect,\n driverConfig: new SQLocalDriverConfig(),\n uowConfig: config.uowConfig,\n });\n internalSchemaConfig = await runInternalFragmentMigrations(adapter);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ormMap = new Map<string | null, SimpleQueryInterface<any, any>>();\n for (const { schema, namespace, migrateToVersion } of schemas) {\n const preparedMigrations = adapter.prepareMigrations(schema, namespace);\n if (migrateToVersion !== undefined) {\n await preparedMigrations.execute(0, migrateToVersion, {\n updateVersionInMigration: false,\n });\n } else {\n await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });\n }\n\n const orm = adapter.createQueryEngine(schema, namespace);\n ormMap.set(namespace, orm);\n }\n\n return { kysely, adapter, ormMap };\n };\n\n let { kysely, adapter, ormMap } = await createDatabase();\n\n const resetDatabase = async () => {\n const schemasToTruncate = internalSchemaConfig ? [internalSchemaConfig, ...schemas] : schemas;\n\n for (const { schema, namespace } of schemasToTruncate) {\n for (const tableName of Object.keys(schema.tables)) {\n const physicalTableName = adapter.namingStrategy.tableName(tableName, namespace);\n await kysely.deleteFrom(physicalTableName).execute();\n }\n }\n };\n\n const cleanup = async () => {\n await kysely.destroy();\n };\n\n const commonMethods = createCommonTestContextMethods(ormMap);\n\n return {\n testContext: {\n get kysely() {\n return kysely;\n },\n get adapter() {\n return adapter;\n },\n ...commonMethods,\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n"],"mappings":";;;;;;;AAWA,MAAM,kCAEJ,YACI,EACJ,SAAoC,cAA4D;CAC9F,MAAM,MAAM,OAAO,IAAI,UAAU;AACjC,KAAI,CAAC,IACH,OAAM,IAAI,MAAM,+BAA+B,OAAO,UAAU,GAAG;AAErE,QAAO;GAEV;AAED,MAAM,gCAAgC,OACpC,YACsC;CACtC,MAAM,eAAe,oBAAoB;AACzC,KAAI,CAAC,aACH;CAGF,MAAM,eAAe,aAAa;EAChC,QAAQ,EAAE;EACV,SAAS;GAAE,iBAAiB;GAAS,mBAAmB;GAAM;EAC/D,CAAC;AACF,KAAI,cAAc,QAAQ;AAExB,QADmB,QAAQ,kBAAkB,aAAa,QAAQ,aAAa,UAAU,CACxE,kBAAkB,QAAQ,QAAQ,EAAE;AACrD,SAAO;GAAE,QAAQ,aAAa;GAAQ,WAAW,aAAa;GAAW;;;AAK7E,eAAsB,0BACpB,QACA,SACoD;CACpD,IAAIA;CAEJ,MAAM,iBAAiB,YAAY;EACjC,MAAM,EAAE,YAAY,IAAI,cAAc,WAAW;EAEjD,MAAMC,WAAS,IAAI,OAAY,EAC7B,SACD,CAAC;EAEF,MAAMC,YAAU,IAAI,WAAW;GAC7B;GACA,cAAc,IAAI,qBAAqB;GACvC,WAAW,OAAO;GACnB,CAAC;AACF,yBAAuB,MAAM,8BAA8BA,UAAQ;EAGnE,MAAMC,2BAAS,IAAI,KAAoD;AACvE,OAAK,MAAM,EAAE,QAAQ,WAAW,sBAAsB,SAAS;GAC7D,MAAM,qBAAqBD,UAAQ,kBAAkB,QAAQ,UAAU;AACvE,OAAI,qBAAqB,OACvB,OAAM,mBAAmB,QAAQ,GAAG,kBAAkB,EACpD,0BAA0B,OAC3B,CAAC;OAEF,OAAM,mBAAmB,QAAQ,GAAG,OAAO,SAAS,EAAE,0BAA0B,OAAO,CAAC;GAG1F,MAAM,MAAMA,UAAQ,kBAAkB,QAAQ,UAAU;AACxD,YAAO,IAAI,WAAW,IAAI;;AAG5B,SAAO;GAAE;GAAQ;GAAS;GAAQ;;CAGpC,IAAI,EAAE,QAAQ,SAAS,WAAW,MAAM,gBAAgB;CAExD,MAAM,gBAAgB,YAAY;EAChC,MAAM,oBAAoB,uBAAuB,CAAC,sBAAsB,GAAG,QAAQ,GAAG;AAEtF,OAAK,MAAM,EAAE,QAAQ,eAAe,kBAClC,MAAK,MAAM,aAAa,OAAO,KAAK,OAAO,OAAO,EAAE;GAClD,MAAM,oBAAoB,QAAQ,eAAe,UAAU,WAAW,UAAU;AAChF,SAAM,OAAO,WAAW,kBAAkB,CAAC,SAAS;;;CAK1D,MAAM,UAAU,YAAY;AAC1B,QAAM,OAAO,SAAS;;AAKxB,QAAO;EACL,aAAa;GACX,IAAI,SAAS;AACX,WAAO;;GAET,IAAI,UAAU;AACZ,WAAO;;GAET,GAVkB,+BAA+B,OAAO;GAWxD;GACA;GACD;EACD,IAAI,UAAU;AACZ,UAAO;;EAEV"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ModelCheckerAdapter } from "../model-checker-adapter.js";
|
|
2
|
+
import { InMemoryAdapter } from "@fragno-dev/db/adapters/in-memory";
|
|
3
|
+
|
|
4
|
+
//#region src/test-adapters/model-checker.ts
|
|
5
|
+
const createCommonTestContextMethods = (ormMap) => ({ getOrm: (namespace) => {
|
|
6
|
+
const orm = ormMap.get(namespace);
|
|
7
|
+
if (!orm) throw new Error(`No ORM found for namespace: ${String(namespace)}`);
|
|
8
|
+
return orm;
|
|
9
|
+
} });
|
|
10
|
+
async function createModelCheckerAdapter(config, schemas) {
|
|
11
|
+
const baseAdapter = new InMemoryAdapter(config.options);
|
|
12
|
+
const adapter = new ModelCheckerAdapter(baseAdapter);
|
|
13
|
+
const ormMap = /* @__PURE__ */ new Map();
|
|
14
|
+
for (const { schema, namespace } of schemas) {
|
|
15
|
+
const orm = adapter.createQueryEngine(schema, namespace);
|
|
16
|
+
ormMap.set(namespace, orm);
|
|
17
|
+
}
|
|
18
|
+
const resetDatabase = async () => {
|
|
19
|
+
await baseAdapter.reset();
|
|
20
|
+
};
|
|
21
|
+
const cleanup = async () => {
|
|
22
|
+
await adapter.close();
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
testContext: {
|
|
26
|
+
get adapter() {
|
|
27
|
+
return adapter;
|
|
28
|
+
},
|
|
29
|
+
...createCommonTestContextMethods(ormMap),
|
|
30
|
+
resetDatabase,
|
|
31
|
+
cleanup
|
|
32
|
+
},
|
|
33
|
+
get adapter() {
|
|
34
|
+
return adapter;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
export { createModelCheckerAdapter };
|
|
41
|
+
//# sourceMappingURL=model-checker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-checker.js","names":[],"sources":["../../src/test-adapters/model-checker.ts"],"sourcesContent":["import { InMemoryAdapter } from \"@fragno-dev/db/adapters/in-memory\";\nimport type { SimpleQueryInterface } from \"@fragno-dev/db/query\";\nimport type { AnySchema } from \"@fragno-dev/db/schema\";\n\nimport type { AdapterFactoryResult, ModelCheckerAdapterConfig, SchemaConfig } from \"../adapters\";\nimport { ModelCheckerAdapter } from \"../model-checker-adapter\";\n\nconst createCommonTestContextMethods = (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ormMap: Map<string | null, SimpleQueryInterface<any, any>>,\n) => ({\n getOrm: <TSchema extends AnySchema>(namespace: string | null): SimpleQueryInterface<TSchema> => {\n const orm = ormMap.get(namespace);\n if (!orm) {\n throw new Error(`No ORM found for namespace: ${String(namespace)}`);\n }\n return orm as SimpleQueryInterface<TSchema>;\n },\n});\n\nexport async function createModelCheckerAdapter(\n config: ModelCheckerAdapterConfig,\n schemas: SchemaConfig[],\n): Promise<AdapterFactoryResult<ModelCheckerAdapterConfig>> {\n const baseAdapter = new InMemoryAdapter(config.options);\n const adapter = new ModelCheckerAdapter(baseAdapter);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ormMap = new Map<string | null, SimpleQueryInterface<any, any>>();\n for (const { schema, namespace } of schemas) {\n const orm = adapter.createQueryEngine(schema, namespace);\n ormMap.set(namespace, orm);\n }\n\n const resetDatabase = async () => {\n await baseAdapter.reset();\n };\n\n const cleanup = async () => {\n await adapter.close();\n };\n\n const commonMethods = createCommonTestContextMethods(ormMap);\n\n return {\n testContext: {\n get adapter() {\n return adapter;\n },\n ...commonMethods,\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n"],"mappings":";;;;AAOA,MAAM,kCAEJ,YACI,EACJ,SAAoC,cAA4D;CAC9F,MAAM,MAAM,OAAO,IAAI,UAAU;AACjC,KAAI,CAAC,IACH,OAAM,IAAI,MAAM,+BAA+B,OAAO,UAAU,GAAG;AAErE,QAAO;GAEV;AAED,eAAsB,0BACpB,QACA,SAC0D;CAC1D,MAAM,cAAc,IAAI,gBAAgB,OAAO,QAAQ;CACvD,MAAM,UAAU,IAAI,oBAAoB,YAAY;CAGpD,MAAM,yBAAS,IAAI,KAAoD;AACvE,MAAK,MAAM,EAAE,QAAQ,eAAe,SAAS;EAC3C,MAAM,MAAM,QAAQ,kBAAkB,QAAQ,UAAU;AACxD,SAAO,IAAI,WAAW,IAAI;;CAG5B,MAAM,gBAAgB,YAAY;AAChC,QAAM,YAAY,OAAO;;CAG3B,MAAM,UAAU,YAAY;AAC1B,QAAM,QAAQ,OAAO;;AAKvB,QAAO;EACL,aAAa;GACX,IAAI,UAAU;AACZ,WAAO;;GAET,GAPkB,+BAA+B,OAAO;GAQxD;GACA;GACD;EACD,IAAI,UAAU;AACZ,UAAO;;EAEV"}
|