mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-29 22:58:45 +00:00
Reapply "refactor: move runtime state to SQLite"
This reverts commit 694ca50e97.
This commit is contained in:
@@ -565,8 +565,6 @@ describe("scripts/changed-lanes", () => {
|
||||
"guarded extension wildcard re-exports",
|
||||
"plugin-sdk wildcard re-exports",
|
||||
"duplicate scan target coverage",
|
||||
"dependency pin guard",
|
||||
"package patch guard",
|
||||
"typecheck core tests",
|
||||
"lint core",
|
||||
"lint scripts",
|
||||
@@ -1106,8 +1104,6 @@ describe("scripts/changed-lanes", () => {
|
||||
args: ["lint:extensions:no-plugin-sdk-wildcard-reexports"],
|
||||
},
|
||||
{ name: "duplicate scan target coverage", args: ["dup:check:coverage"] },
|
||||
{ name: "dependency pin guard", args: ["deps:pins:check"] },
|
||||
{ name: "package patch guard", args: ["deps:patches:check"] },
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1128,8 +1124,6 @@ describe("scripts/changed-lanes", () => {
|
||||
args: ["lint:extensions:no-plugin-sdk-wildcard-reexports"],
|
||||
},
|
||||
{ name: "duplicate scan target coverage", args: ["dup:check:coverage"] },
|
||||
{ name: "dependency pin guard", args: ["deps:pins:check"] },
|
||||
{ name: "package patch guard", args: ["deps:patches:check"] },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
94
test/scripts/check-kysely-guardrails.test.ts
Normal file
94
test/scripts/check-kysely-guardrails.test.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { collectKyselyGuardrailViolations } from "../../scripts/check-kysely-guardrails.mjs";
|
||||
|
||||
function messagesFor(content: string, relativePath = "src/example/store.sqlite.ts"): string[] {
|
||||
return collectKyselyGuardrailViolations(content, relativePath).map(
|
||||
(violation) => violation.message,
|
||||
);
|
||||
}
|
||||
|
||||
describe("Kysely guardrails", () => {
|
||||
it("rejects explicit sync-helper row generics for builder queries", () => {
|
||||
expect(
|
||||
messagesFor(`
|
||||
import { executeSqliteQuerySync } from "../infra/kysely-sync.js";
|
||||
|
||||
executeSqliteQuerySync<{ id: string }>(db, query);
|
||||
`),
|
||||
).toContain("sync helper row generic at call site; let Kysely infer builder result rows");
|
||||
});
|
||||
|
||||
it("rejects persisted row casts to enum-like types in SQLite stores", () => {
|
||||
expect(
|
||||
messagesFor(`
|
||||
type TaskStatus = "running" | "succeeded";
|
||||
|
||||
function rowToRecord(row: { status: string }) {
|
||||
return {
|
||||
status: row.status as TaskStatus,
|
||||
};
|
||||
}
|
||||
`),
|
||||
).toContain(
|
||||
"persisted SQLite enum-like values must be parsed through closed validators, not cast",
|
||||
);
|
||||
});
|
||||
|
||||
it("allows explicit local escape hatches for reviewed persisted casts", () => {
|
||||
expect(
|
||||
messagesFor(`
|
||||
type TaskStatus = "running" | "succeeded";
|
||||
|
||||
function rowToRecord(row: { status: string }) {
|
||||
return {
|
||||
status: row.status as TaskStatus, // sqlite-allow-persisted-cast
|
||||
};
|
||||
}
|
||||
`),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("rejects typed raw SQL outside allowlisted boundaries", () => {
|
||||
expect(
|
||||
messagesFor(
|
||||
`
|
||||
import { sql } from "kysely";
|
||||
|
||||
const count = sql<number>\`COUNT(*)\`;
|
||||
`,
|
||||
"src/example/report.ts",
|
||||
),
|
||||
).toContain("typed raw sql snippet needs a small helper or allowlisted boundary");
|
||||
});
|
||||
|
||||
it("rejects direct raw node:sqlite prepare in new production files", () => {
|
||||
expect(
|
||||
messagesFor(
|
||||
`
|
||||
import { requireNodeSqlite } from "../infra/node-sqlite.js";
|
||||
|
||||
const sqlite = requireNodeSqlite();
|
||||
const db = new sqlite.DatabaseSync(":memory:");
|
||||
db.prepare("select 1").get();
|
||||
`,
|
||||
"src/example/raw-store.ts",
|
||||
),
|
||||
).toContain(
|
||||
"new raw node:sqlite access requires Kysely or an explicit raw SQLite allowlist entry",
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps ordinary static Kysely reference strings valid", () => {
|
||||
expect(
|
||||
messagesFor(`
|
||||
import { executeSqliteQuerySync, getNodeSqliteKysely } from "../infra/kysely-sync.js";
|
||||
|
||||
const query = getNodeSqliteKysely<{ task_runs: { task_id: string } }>(db)
|
||||
.selectFrom("task_runs")
|
||||
.select(["task_id"])
|
||||
.where("task_id", "=", taskId);
|
||||
executeSqliteQuerySync(db, query);
|
||||
`),
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -1454,7 +1454,8 @@ test -f "$TMPDIR/docker-cmd-seen"
|
||||
|
||||
expect(runner).toContain("scripts/e2e/lib/plugin-update/unchanged-scenario.sh");
|
||||
expect(probe).toContain("plugin install record changed unexpectedly");
|
||||
expect(probe).toContain("index.installRecords ?? index.records ?? config.plugins?.installs");
|
||||
expect(probe).toContain("readInstalledPluginRecords()");
|
||||
expect(probe).toContain('records["lossless-claw"] ?? records["@example/lossless-claw"]');
|
||||
expect(scenario).toContain("Config changed unexpectedly for modern package");
|
||||
expect(scenario).not.toContain("before_hash");
|
||||
});
|
||||
|
||||
@@ -832,15 +832,31 @@ describe("install.sh", () => {
|
||||
const tmp = mkdtempSync(join(tmpdir(), "openclaw-install-nvm-"));
|
||||
const home = join(tmp, "home");
|
||||
const systemBin = join(tmp, "system-bin");
|
||||
const nvmBin = join(home, ".nvm/versions/node/v22.22.1/bin");
|
||||
const nvmBin = join(home, ".nvm/versions/node/v24.13.0/bin");
|
||||
mkdirSync(systemBin, { recursive: true });
|
||||
mkdirSync(nvmBin, { recursive: true });
|
||||
mkdirSync(join(home, ".nvm"), { recursive: true });
|
||||
|
||||
const systemNode = join(systemBin, "node");
|
||||
const nvmNode = join(nvmBin, "node");
|
||||
writeFileSync(systemNode, "#!/bin/sh\necho v8.11.3\n");
|
||||
writeFileSync(nvmNode, "#!/bin/sh\necho v22.22.1\n");
|
||||
writeFileSync(
|
||||
systemNode,
|
||||
[
|
||||
"#!/bin/sh",
|
||||
'if [ "${1:-}" = "-p" ]; then echo "8 11"; exit 0; fi',
|
||||
"echo v8.11.3",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
writeFileSync(
|
||||
nvmNode,
|
||||
[
|
||||
"#!/bin/sh",
|
||||
'if [ "${1:-}" = "-p" ]; then echo "24 13"; exit 0; fi',
|
||||
"echo v24.13.0",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
chmodSync(systemNode, 0o755);
|
||||
chmodSync(nvmNode, 0o755);
|
||||
writeFileSync(
|
||||
@@ -850,7 +866,7 @@ describe("install.sh", () => {
|
||||
"export NVM_DIR",
|
||||
"nvm() {",
|
||||
' if [ "$1" = "use" ]; then',
|
||||
' export PATH="$NVM_DIR/versions/node/v22.22.1/bin:$PATH"',
|
||||
' export PATH="$NVM_DIR/versions/node/v24.13.0/bin:$PATH"',
|
||||
" return 0",
|
||||
" fi",
|
||||
" return 0",
|
||||
@@ -887,7 +903,7 @@ describe("install.sh", () => {
|
||||
const output = result?.stdout ?? "";
|
||||
expect(output).toContain("status=0");
|
||||
expect(output).toContain(`path=${nvmNode}`);
|
||||
expect(output).toContain("version=v22.22.1");
|
||||
expect(output).toContain("version=v24.13.0");
|
||||
});
|
||||
|
||||
it("installs Homebrew lazily before macOS Git installs", () => {
|
||||
@@ -915,8 +931,24 @@ describe("install.sh", () => {
|
||||
|
||||
const staleNode = join(staleBin, "node");
|
||||
const supportedNode = join(supportedBin, "node");
|
||||
writeFileSync(staleNode, "#!/bin/sh\necho v20.20.0\n");
|
||||
writeFileSync(supportedNode, "#!/bin/sh\necho v22.22.0\n");
|
||||
writeFileSync(
|
||||
staleNode,
|
||||
[
|
||||
"#!/bin/sh",
|
||||
'if [ "${1:-}" = "-p" ]; then echo "20 20"; exit 0; fi',
|
||||
"echo v20.20.0",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
writeFileSync(
|
||||
supportedNode,
|
||||
[
|
||||
"#!/bin/sh",
|
||||
'if [ "${1:-}" = "-p" ]; then echo "24 13"; exit 0; fi',
|
||||
"echo v24.13.0",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
chmodSync(staleNode, 0o755);
|
||||
chmodSync(supportedNode, 0o755);
|
||||
|
||||
@@ -956,7 +988,7 @@ describe("install.sh", () => {
|
||||
expect(output).toContain("promote=0");
|
||||
expect(output).toContain("active=0");
|
||||
expect(output).toContain(`path=${supportedNode}`);
|
||||
expect(output).toContain("version=v22.22.0");
|
||||
expect(output).toContain("version=v24.13.0");
|
||||
});
|
||||
|
||||
it("persists a supported Linux Node path before noninteractive shell guards", () => {
|
||||
|
||||
@@ -132,8 +132,11 @@ describe("production lint suppressions", () => {
|
||||
"scripts/lib/plugin-npm-release.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/agents/agent-scope.ts|no-control-regex|1",
|
||||
"src/agents/code-mode.worker.ts|unicorn/require-post-message-target-origin|1",
|
||||
"src/agents/embedded-agent-runner/run/images.ts|no-control-regex|1",
|
||||
"src/agents/pi-embedded-runner/run/images.ts|no-control-regex|1",
|
||||
"src/agents/runtime-worker.entry.ts|unicorn/require-post-message-target-origin|1",
|
||||
"src/agents/runtime-worker.ts|unicorn/require-post-message-target-origin|1",
|
||||
"src/agents/subagent-attachments.ts|no-control-regex|1",
|
||||
"src/agents/subagent-registry.store.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/agents/subagent-spawn.ts|no-control-regex|1",
|
||||
"src/channels/plugins/channel-runtime-surface.types.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/channels/plugins/contracts/test-helpers.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
@@ -159,6 +162,7 @@ describe("production lint suppressions", () => {
|
||||
"src/plugin-sdk/test-helpers/package-manifest-contract.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/plugin-sdk/test-helpers/public-surface-loader.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/plugin-sdk/test-helpers/subagent-hooks.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/plugins/hook-types.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/plugins/hooks.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
"src/plugins/host-hook-runtime.ts|typescript/no-unnecessary-type-parameters|2",
|
||||
"src/plugins/host-hook-state.ts|typescript/no-unnecessary-type-parameters|1",
|
||||
|
||||
@@ -13,12 +13,10 @@ describe("live Docker state staging", () => {
|
||||
expect(script).toContain("--exclude=.artifacts");
|
||||
});
|
||||
|
||||
it("keeps host-only generated registry state out of the container copy", () => {
|
||||
it("keeps host workspace artifacts out of the container state copy", () => {
|
||||
const script = readFileSync(stageScriptPath, "utf8");
|
||||
|
||||
expect(script).toContain("--exclude=workspace");
|
||||
expect(script).toContain("--exclude=sandboxes");
|
||||
expect(script).toContain("--exclude=plugins/installs.json");
|
||||
expect(script).toContain("host-absolute paths");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -412,10 +412,9 @@ if (isPrlctl) {
|
||||
expect(missingKey.stderr).toContain("PARALLELS_TEST_MISSING_KEY is required");
|
||||
});
|
||||
|
||||
it("seeds agent workspace state before OS smoke agent turns", () => {
|
||||
it("seeds configured agent workspace files before OS smoke agent turns", () => {
|
||||
const workspace = readFileSync(TS_PATHS.agentWorkspace, "utf8");
|
||||
|
||||
expect(workspace).toContain("workspace-state.json");
|
||||
expect(workspace).toContain("IDENTITY.md");
|
||||
expect(workspace).toContain("BOOTSTRAP.md");
|
||||
|
||||
@@ -694,7 +693,7 @@ if (isPrlctl) {
|
||||
expect(script).toContain("agent turn attempt $attempt failed or finished without OK response");
|
||||
expect(script).not.toContain("$config.models.providers");
|
||||
expect(script).not.toContain("timeoutSeconds = 300");
|
||||
expect(script).toContain('"$sessionId.jsonl"');
|
||||
expect(script).not.toContain("$sessionId.jsonl");
|
||||
});
|
||||
|
||||
it("gives GPT-5.5 enough Parallels model time on slower desktop guests", () => {
|
||||
|
||||
23
test/scripts/pre-commit-filter-staged-files.test.ts
Normal file
23
test/scripts/pre-commit-filter-staged-files.test.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const scriptPath = path.join(process.cwd(), "scripts", "pre-commit", "filter-staged-files.mjs");
|
||||
|
||||
function filterFiles(mode: "format" | "lint", files: string[]): string[] {
|
||||
const output = execFileSync(process.execPath, [scriptPath, mode, "--", ...files], {
|
||||
encoding: "utf8",
|
||||
});
|
||||
return output.split("\0").filter(Boolean);
|
||||
}
|
||||
|
||||
describe("pre-commit staged-file filter", () => {
|
||||
it("does not format generated Kysely declaration files", () => {
|
||||
expect(
|
||||
filterFiles("format", [
|
||||
"src/state/openclaw-state-db.generated.d.ts",
|
||||
"src/state/openclaw-state-db.ts",
|
||||
]),
|
||||
).toEqual(["src/state/openclaw-state-db.ts"]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user