diff --git a/scripts/e2e/lib/auth-profile-store-assertions.mjs b/scripts/e2e/lib/auth-profile-store-assertions.mjs index 5fe6853a36e0..ac89e416a46f 100644 --- a/scripts/e2e/lib/auth-profile-store-assertions.mjs +++ b/scripts/e2e/lib/auth-profile-store-assertions.mjs @@ -1,6 +1,28 @@ // Shared auth profile store assertions for install/onboard E2E proof. +import fs from "node:fs"; +import path from "node:path"; +import { DatabaseSync } from "node:sqlite"; import { isRecord } from "../../lib/record-shared.mjs"; +export function readSharedAuthProfileStoreText(stateDir) { + const dbPath = path.join(stateDir, "state", "openclaw.sqlite"); + if (!fs.existsSync(dbPath)) { + return ""; + } + let db; + try { + db = new DatabaseSync(dbPath, { readOnly: true }); + const row = db + .prepare("SELECT store_json FROM auth_profile_stores WHERE store_key = ?") + .get("shared"); + return typeof row?.store_json === "string" ? row.store_json : ""; + } catch { + return ""; + } finally { + db?.close(); + } +} + function hasExpectedOpenAiEnvRef(profile) { if (!isRecord(profile)) { return false; diff --git a/scripts/e2e/lib/codex-on-demand/assertions.mjs b/scripts/e2e/lib/codex-on-demand/assertions.mjs index d6633c5fd927..06b742ac8d1b 100644 --- a/scripts/e2e/lib/codex-on-demand/assertions.mjs +++ b/scripts/e2e/lib/codex-on-demand/assertions.mjs @@ -2,8 +2,10 @@ import { spawnSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; -import { DatabaseSync } from "node:sqlite"; -import { assertOpenAiEnvAuthProfileStore } from "../auth-profile-store-assertions.mjs"; +import { + assertOpenAiEnvAuthProfileStore, + readSharedAuthProfileStoreText, +} from "../auth-profile-store-assertions.mjs"; import { assertPathInside, configPath, @@ -125,24 +127,7 @@ if (providerRuntime && providerRuntime !== "codex") { throw new Error(`unexpected OpenAI provider runtime: ${providerRuntime}`); } -function readAuthProfileStoreText(agentDir) { - const dbPath = path.join(agentDir, "openclaw-agent.sqlite"); - if (!fs.existsSync(dbPath)) { - throw new Error("auth profile SQLite store was not persisted"); - } - let db; - try { - db = new DatabaseSync(dbPath, { readOnly: true }); - const row = db - .prepare("SELECT store_json FROM auth_profile_store WHERE store_key = ?") - .get("primary"); - return typeof row?.store_json === "string" ? row.store_json : ""; - } finally { - db?.close(); - } -} - -const authRaw = readAuthProfileStoreText(path.join(stateDir(), "agents", "main", "agent")); +const authRaw = readSharedAuthProfileStoreText(stateDir()); if (!authRaw) { throw new Error("auth profile SQLite store row was not persisted"); } diff --git a/scripts/e2e/lib/npm-onboard-channel-agent/assertions.mjs b/scripts/e2e/lib/npm-onboard-channel-agent/assertions.mjs index 1f12c2905a3d..09580c55d383 100644 --- a/scripts/e2e/lib/npm-onboard-channel-agent/assertions.mjs +++ b/scripts/e2e/lib/npm-onboard-channel-agent/assertions.mjs @@ -1,12 +1,14 @@ // Assertions for npm onboard channel-agent E2E scenarios. import fs from "node:fs"; import path from "node:path"; -import { DatabaseSync } from "node:sqlite"; import { assertAgentReplyContainsMarker, assertOpenAiRequestLogUsed, } from "../agent-turn-output.mjs"; -import { assertOpenAiEnvAuthProfileStore } from "../auth-profile-store-assertions.mjs"; +import { + assertOpenAiEnvAuthProfileStore, + readSharedAuthProfileStoreText, +} from "../auth-profile-store-assertions.mjs"; import { readPositiveIntEnv } from "../env-limits.mjs"; import { applyMockOpenAiModelConfig, @@ -77,25 +79,6 @@ function extractStatusSection(text, title) { return stripAnsi(section.join("\n")); } -function readSharedAuthProfileStoreText(stateDir) { - const dbPath = path.join(stateDir, "state", "openclaw.sqlite"); - if (!fs.existsSync(dbPath)) { - return ""; - } - let db; - try { - db = new DatabaseSync(dbPath, { readOnly: true }); - const row = db - .prepare("SELECT store_json FROM auth_profile_stores WHERE store_key = ?") - .get("shared"); - return typeof row?.store_json === "string" ? row.store_json : ""; - } catch { - return ""; - } finally { - db?.close(); - } -} - function assertOnboardState() { const home = process.argv[3]; const stateDir = path.join(home, ".openclaw"); diff --git a/scripts/e2e/lib/release-scenarios/assertions.mjs b/scripts/e2e/lib/release-scenarios/assertions.mjs index aa8ce9abb883..1d0a1af9ae37 100644 --- a/scripts/e2e/lib/release-scenarios/assertions.mjs +++ b/scripts/e2e/lib/release-scenarios/assertions.mjs @@ -1,12 +1,14 @@ // Assertions for release scenario E2E packages and plugin state. import fs from "node:fs"; import path from "node:path"; -import { DatabaseSync } from "node:sqlite"; import { assertAgentReplyContainsMarker, assertOpenAiRequestLogUsed, } from "../agent-turn-output.mjs"; -import { assertOpenAiEnvAuthProfileStore } from "../auth-profile-store-assertions.mjs"; +import { + assertOpenAiEnvAuthProfileStore, + readSharedAuthProfileStoreText, +} from "../auth-profile-store-assertions.mjs"; import { applyMockOpenAiModelConfig, parseMockOpenAiPort, @@ -49,39 +51,16 @@ function authProfilesPath() { ); } -function authProfilesDatabasePath() { - return path.join( - process.env.HOME ?? "", - ".openclaw", - "agents", - "main", - "agent", - "openclaw-agent.sqlite", - ); -} - -function readAuthProfileStoreSqliteText() { - const dbPath = authProfilesDatabasePath(); - if (!fs.existsSync(dbPath)) { - return ""; - } - let db; - try { - db = new DatabaseSync(dbPath, { readOnly: true }); - const row = db - .prepare("SELECT store_json FROM auth_profile_store WHERE store_key = ?") - .get("primary"); - return typeof row?.store_json === "string" ? row.store_json : ""; - } catch { - return ""; - } finally { - db?.close(); - } +function stateDir() { + return process.env.OPENCLAW_STATE_DIR ?? path.dirname(configPath()); } function readStateText() { const paths = [configPath(), authProfilesPath()].filter((file) => fs.existsSync(file)); - return [...paths.map((file) => fs.readFileSync(file, "utf8")), readAuthProfileStoreSqliteText()] + return [ + ...paths.map((file) => fs.readFileSync(file, "utf8")), + readSharedAuthProfileStoreText(stateDir()), + ] .filter(Boolean) .join("\n"); } @@ -96,7 +75,7 @@ function configureMockOpenAi() { function assertOpenAiEnvRef() { const rawKey = process.argv[3]; assert(fs.existsSync(configPath()), "openclaw.json missing"); - assertOpenAiEnvAuthProfileStore(readAuthProfileStoreSqliteText(), { + assertOpenAiEnvAuthProfileStore(readSharedAuthProfileStoreText(stateDir()), { missingMessage: "OpenAI env ref was not persisted", envRefMessage: "OpenAI env ref was not persisted", rawKeyMessage: "raw OpenAI key was persisted", diff --git a/test/scripts/codex-install-assertions.test.ts b/test/scripts/codex-install-assertions.test.ts index 78810132e752..8476e2aae010 100644 --- a/test/scripts/codex-install-assertions.test.ts +++ b/test/scripts/codex-install-assertions.test.ts @@ -1,7 +1,7 @@ // Codex Install Assertions tests cover Codex plugin install E2E helpers. import { spawnSync } from "node:child_process"; import { createHash } from "node:crypto"; -import { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; import { DatabaseSync } from "node:sqlite"; @@ -50,12 +50,13 @@ function writeJson(filePath: string, value: unknown) { writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8"); } -function writeAuthProfileStoreSqlite(agentDir: string) { - mkdirSync(agentDir, { recursive: true }); - const db = new DatabaseSync(path.join(agentDir, "openclaw-agent.sqlite")); +function writeAuthProfileStoreSqlite(stateDir: string) { + const databasePath = path.join(stateDir, "state", "openclaw.sqlite"); + mkdirSync(path.dirname(databasePath), { recursive: true }); + const db = new DatabaseSync(databasePath); try { db.exec(` - CREATE TABLE IF NOT EXISTS auth_profile_store ( + CREATE TABLE IF NOT EXISTS auth_profile_stores ( store_key TEXT NOT NULL PRIMARY KEY, store_json TEXT NOT NULL, updated_at INTEGER NOT NULL @@ -63,11 +64,11 @@ function writeAuthProfileStoreSqlite(agentDir: string) { `); db.prepare( ` - INSERT INTO auth_profile_store (store_key, store_json, updated_at) + INSERT INTO auth_profile_stores (store_key, store_json, updated_at) VALUES (?, ?, ?) `, ).run( - "primary", + "shared", JSON.stringify({ version: 1, profiles: { @@ -574,7 +575,7 @@ function createCodexInstallFixture(root: string) { writeJson("/tmp/openclaw-plugins-list.json", { plugins: [{ id: "codex", enabled: true, status: "loaded" }], }); - writeAuthProfileStoreSqlite(path.join(stateDir, "agents", "main", "agent")); + writeAuthProfileStoreSqlite(stateDir); } describe("Codex install helpers", () => { @@ -692,6 +693,9 @@ describe("Codex install helpers", () => { expect(result.status).toBe(0); expect(result.stderr).toBe(""); + expect( + existsSync(path.join(root, "state", "agents", "main", "agent", "openclaw-agent.sqlite")), + ).toBe(false); }); it("rejects on-demand fixtures without the canonical SQLite install record", () => { diff --git a/test/scripts/release-scenarios-assertions.test.ts b/test/scripts/release-scenarios-assertions.test.ts index adc11f0a0168..7f3eb614acdd 100644 --- a/test/scripts/release-scenarios-assertions.test.ts +++ b/test/scripts/release-scenarios-assertions.test.ts @@ -1,6 +1,6 @@ // Release Scenarios Assertions tests cover release scenarios assertions script behavior. import { spawnSync } from "node:child_process"; -import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import { DatabaseSync } from "node:sqlite"; @@ -32,12 +32,13 @@ function runAssertion(args: string[], env?: NodeJS.ProcessEnv) { }); } -function writeAuthProfileStoreSqlite(agentDir: string, store: unknown) { - mkdirSync(agentDir, { recursive: true }); - const db = new DatabaseSync(path.join(agentDir, "openclaw-agent.sqlite")); +function writeAuthProfileStoreSqlite(stateDir: string, store: unknown) { + const databasePath = path.join(stateDir, "state", "openclaw.sqlite"); + mkdirSync(path.dirname(databasePath), { recursive: true }); + const db = new DatabaseSync(databasePath); try { db.exec(` - CREATE TABLE IF NOT EXISTS auth_profile_store ( + CREATE TABLE IF NOT EXISTS auth_profile_stores ( store_key TEXT NOT NULL PRIMARY KEY, store_json TEXT NOT NULL, updated_at INTEGER NOT NULL @@ -45,10 +46,10 @@ function writeAuthProfileStoreSqlite(agentDir: string, store: unknown) { `); db.prepare( ` - INSERT INTO auth_profile_store (store_key, store_json, updated_at) + INSERT INTO auth_profile_stores (store_key, store_json, updated_at) VALUES (?, ?, ?) `, - ).run("primary", JSON.stringify(store), Date.now()); + ).run("shared", JSON.stringify(store), Date.now()); } finally { db.close(); } @@ -196,7 +197,6 @@ describe("release scenario assertions", () => { const root = mkdtempSync(path.join(tmpdir(), "openclaw-release-scenarios-")); const home = path.join(root, "home"); const stateDir = path.join(home, ".openclaw"); - const agentDir = path.join(stateDir, "agents", "main", "agent"); const configPath = path.join(stateDir, "openclaw.json"); try { @@ -207,7 +207,7 @@ describe("release scenario assertions", () => { }, }, }); - writeAuthProfileStoreSqlite(agentDir, { + writeAuthProfileStoreSqlite(stateDir, { version: 1, profiles: { "openai:api-key": { @@ -221,10 +221,14 @@ describe("release scenario assertions", () => { const result = runAssertion(["assert-openai-env-ref", "sk-test-raw-key"], { HOME: home, OPENCLAW_CONFIG_PATH: configPath, + OPENCLAW_STATE_DIR: stateDir, }); expect(result.status).toBe(0); expect(result.stderr).toBe(""); + expect( + existsSync(path.join(stateDir, "agents", "main", "agent", "openclaw-agent.sqlite")), + ).toBe(false); } finally { rmSync(root, { force: true, recursive: true }); } @@ -234,7 +238,6 @@ describe("release scenario assertions", () => { const root = mkdtempSync(path.join(tmpdir(), "openclaw-release-scenarios-")); const home = path.join(root, "home"); const stateDir = path.join(home, ".openclaw"); - const agentDir = path.join(stateDir, "agents", "main", "agent"); const configPath = path.join(stateDir, "openclaw.json"); try { @@ -245,7 +248,7 @@ describe("release scenario assertions", () => { }, }, }); - writeAuthProfileStoreSqlite(agentDir, { + writeAuthProfileStoreSqlite(stateDir, { version: 1, profiles: { "openai:api-key": { note: "OPENAI_API_KEY" }, @@ -255,6 +258,7 @@ describe("release scenario assertions", () => { const result = runAssertion(["assert-openai-env-ref", "sk-test-raw-key"], { HOME: home, OPENCLAW_CONFIG_PATH: configPath, + OPENCLAW_STATE_DIR: stateDir, }); expect(result.status).not.toBe(0); @@ -268,7 +272,6 @@ describe("release scenario assertions", () => { const root = mkdtempSync(path.join(tmpdir(), "openclaw-release-scenarios-")); const home = path.join(root, "home"); const stateDir = path.join(home, ".openclaw"); - const agentDir = path.join(stateDir, "agents", "main", "agent"); const configPath = path.join(stateDir, "openclaw.json"); try { @@ -279,7 +282,7 @@ describe("release scenario assertions", () => { }, }, }); - writeAuthProfileStoreSqlite(agentDir, { + writeAuthProfileStoreSqlite(stateDir, { version: 1, profiles: { "openai:api-key": { @@ -293,6 +296,7 @@ describe("release scenario assertions", () => { const result = runAssertion(["assert-openai-env-ref", "sk-test-raw-key"], { HOME: home, OPENCLAW_CONFIG_PATH: configPath, + OPENCLAW_STATE_DIR: stateDir, }); expect(result.status).not.toBe(0); @@ -306,7 +310,6 @@ describe("release scenario assertions", () => { const root = mkdtempSync(path.join(tmpdir(), "openclaw-release-scenarios-")); const home = path.join(root, "home"); const stateDir = path.join(home, ".openclaw"); - const agentDir = path.join(stateDir, "agents", "main", "agent"); const configPath = path.join(stateDir, "openclaw.json"); try { @@ -322,7 +325,7 @@ describe("release scenario assertions", () => { }, }, }); - writeAuthProfileStoreSqlite(agentDir, { + writeAuthProfileStoreSqlite(stateDir, { version: 1, profiles: { "openai:api-key": { @@ -336,6 +339,7 @@ describe("release scenario assertions", () => { const result = runAssertion(["assert-openai-env-ref", "sk-test-raw-key"], { HOME: home, OPENCLAW_CONFIG_PATH: configPath, + OPENCLAW_STATE_DIR: stateDir, }); expect(result.status).not.toBe(0);