Files
pikasTech-HWLAB/internal/db/schema.test.ts
T

112 lines
4.3 KiB
TypeScript

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import path from "node:path";
import { test } from "bun:test";
import { fileURLToPath } from "node:url";
import { TABLES, assertProtocolRecord, assertProtocolRecords } from "../protocol/index.mjs";
import {
CLOUD_CORE_MIGRATIONS,
CLOUD_CORE_MIGRATION_ID,
CLOUD_RUNTIME_DURABLE_ADAPTER_SCHEMA_VERSION,
CLOUD_RUNTIME_DURABLE_MIGRATION_TABLE,
CLOUD_RUNTIME_DURABLE_MIGRATION_TABLE_COLUMNS,
CLOUD_RUNTIME_DURABLE_TABLE_COLUMNS,
FROZEN_CLOUD_CORE_TABLES,
assertFrozenCloudCoreTables,
requiredRuntimeDurableColumns
} from "./schema.ts";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
test("cloud core schema keeps the frozen L0 table names", () => {
assert.deepEqual(FROZEN_CLOUD_CORE_TABLES, TABLES);
assertFrozenCloudCoreTables();
assert.equal(CLOUD_CORE_MIGRATIONS[0].connectsToDatabase, false);
});
test("initial migration skeleton declares every frozen table", async () => {
const sql = await readFile(path.join(repoRoot, "internal/db/migrations/0001_cloud_core_skeleton.sql"), "utf8");
for (const table of TABLES) {
assert.match(sql, new RegExp(`CREATE TABLE IF NOT EXISTS ${table}\\b`), `missing ${table}`);
}
assert.match(sql, /\brequest_id\b/);
assert.match(sql, /\bactor\b/);
assert.match(sql, /\bsource\b/);
assert.match(sql, /\boperation\b/);
assert.match(sql, /\btarget\b/);
assert.match(sql, /\bresult\b/);
assert.match(sql, /\btimestamp\b/);
});
test("initial migration exposes columns required by the durable runtime adapter", async () => {
const sql = await readFile(path.join(repoRoot, "internal/db/migrations/0001_cloud_core_skeleton.sql"), "utf8");
assert.ok(requiredRuntimeDurableColumns().length > 0);
for (const [table, columns] of Object.entries(CLOUD_RUNTIME_DURABLE_TABLE_COLUMNS)) {
const tableMatch = sql.match(new RegExp(`CREATE TABLE IF NOT EXISTS ${table}\\s*\\(([\\s\\S]*?)\\);`, "u"));
assert.ok(tableMatch, `missing ${table}`);
for (const column of columns) {
assert.match(tableMatch[1], new RegExp(`\\b${column}\\b`), `missing ${table}.${column}`);
}
}
const migrationMatch = sql.match(
new RegExp(`CREATE TABLE IF NOT EXISTS ${CLOUD_RUNTIME_DURABLE_MIGRATION_TABLE}\\s*\\(([\\s\\S]*?)\\);`, "u")
);
assert.ok(migrationMatch, `missing ${CLOUD_RUNTIME_DURABLE_MIGRATION_TABLE}`);
for (const column of CLOUD_RUNTIME_DURABLE_MIGRATION_TABLE_COLUMNS) {
assert.match(migrationMatch[1], new RegExp(`\\b${column}\\b`), `missing migration ledger column ${column}`);
}
assert.match(sql, new RegExp(`'${CLOUD_CORE_MIGRATION_ID}'`, "u"));
assert.match(sql, new RegExp(`'${CLOUD_RUNTIME_DURABLE_ADAPTER_SCHEMA_VERSION}'`, "u"));
assert.equal(CLOUD_CORE_MIGRATIONS[0].runtimeDurableMigrationTable, CLOUD_RUNTIME_DURABLE_MIGRATION_TABLE);
});
test("protocol record guards catch schema drift before runtime writes", () => {
assertProtocolRecord("gatewaySession", {
gatewaySessionId: "gws_01J00000000000000000000000",
projectId: "prj_01J00000000000000000000000",
serviceId: "hwlab-gateway-simu",
gatewayId: "gtw_01J00000000000000000000000",
endpoint: "http://127.0.0.1:7101",
status: "connected",
environment: "dev",
startedAt: "2026-05-21T00:00:00.000Z",
lastSeenAt: "2026-05-21T00:00:00.000Z"
});
assertProtocolRecords("boxCapability", [
{
capabilityId: "cap_01J00000000000000000000000",
resourceId: "res_01J00000000000000000000000",
projectId: "prj_01J00000000000000000000000",
name: "shell.exec",
direction: "bidirectional",
valueType: "object",
mutatesState: true,
createdAt: "2026-05-21T00:00:00.000Z",
updatedAt: "2026-05-21T00:00:00.000Z"
}
]);
assert.throws(
() =>
assertProtocolRecord("evidenceRecord", {
evidenceId: "evd_01J00000000000000000000000",
projectId: "prj_01J00000000000000000000000",
operationId: "op_01J00000000000000000000000",
kind: "trace",
uri: "memory://hwlab/evidence/evd_01J00000000000000000000000",
sha256: "0".repeat(64),
serviceId: "hwlab-cloud-api",
environment: "dev",
createdAt: "2026-05-21T00:00:00.000Z",
evidenceType: "trace"
}),
/evidenceRecord.evidenceType is not part of the frozen protocol schema/
);
});