Download src/cli/daemon-cli/lifecycle-action-preflight.test.ts from SaylorTwift/openclaw: direct link, hf CLI and curl.
- Browser
- Download file 5.09 kB
-
https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/cli/daemon-cli/lifecycle-action-preflight.test.ts
- Command line
-
hf download hf://SaylorTwift/openclaw/src/cli/daemon-cli/lifecycle-action-preflight.test.ts
-
curl -L -o lifecycle-action-preflight.test.ts https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/cli/daemon-cli/lifecycle-action-preflight.test.ts
5.09 kB
| import fs from "node:fs/promises"; | |
| import path from "node:path"; | |
| import { afterEach, describe, expect, it, vi } from "vitest"; | |
| import { readConfigFileSnapshot, resetConfigRuntimeState } from "../../config/config.js"; | |
| import { withTestDir } from "../../test-helpers/temp-dir.js"; | |
| import { getServiceActionPreflightFailure } from "./lifecycle-action-preflight.js"; | |
| afterEach(() => { | |
| resetConfigRuntimeState(); | |
| vi.unstubAllEnvs(); | |
| }); | |
| async function withIsolatedLifecycleState( | |
| run: (params: { agentDir: string; configPath: string }) => Promise<void>, | |
| ): Promise<void> { | |
| await withTestDir({ prefix: "openclaw-lifecycle-action-preflight-" }, async (root) => { | |
| const stateDir = path.join(root, "state"); | |
| const configPath = path.join(root, "openclaw.json"); | |
| const agentDir = path.join(stateDir, "agents", "main", "agent"); | |
| await fs.mkdir(agentDir, { recursive: true }); | |
| await fs.writeFile(configPath, "{}\n"); | |
| vi.stubEnv("HOME", root); | |
| vi.stubEnv("OPENCLAW_STATE_DIR", stateDir); | |
| vi.stubEnv("OPENCLAW_CONFIG_PATH", configPath); | |
| resetConfigRuntimeState(); | |
| await run({ agentDir, configPath }); | |
| await expect(fs.access(path.join(stateDir, "state", "openclaw.sqlite"))).rejects.toMatchObject({ | |
| code: "ENOENT", | |
| }); | |
| }); | |
| } | |
| describe("getServiceActionPreflightFailure", () => { | |
| // A retired credential file no longer blocks the service: the Gateway boots and | |
| // marks that auth owner configured-unavailable, so one stale file cannot keep | |
| // every other channel and provider offline. | |
| it.each(["start", "restart", "stop", "uninstall"] as const)( | |
| "allows %s when a legacy credential file exists", | |
| async (action) => { | |
| await withIsolatedLifecycleState(async ({ agentDir }) => { | |
| await fs.writeFile(path.join(agentDir, "auth-profiles.json"), "{}\n"); | |
| await expect(getServiceActionPreflightFailure(action)).resolves.toBeNull(); | |
| }); | |
| }, | |
| ); | |
| it("allows stopping before plugin config migration while retaining the newer-writer guard", async () => { | |
| await withIsolatedLifecycleState(async ({ configPath }) => { | |
| const pluginRoot = path.join(path.dirname(configPath), "migration-fixture"); | |
| await fs.mkdir(pluginRoot); | |
| await fs.writeFile(path.join(pluginRoot, "index.js"), "export default {};\n"); | |
| await fs.writeFile( | |
| path.join(pluginRoot, "package.json"), | |
| JSON.stringify({ | |
| name: "migration-fixture", | |
| version: "2.0.0", | |
| openclaw: { extensions: ["./index.js"] }, | |
| }), | |
| ); | |
| await fs.writeFile( | |
| path.join(pluginRoot, "openclaw.plugin.json"), | |
| JSON.stringify({ | |
| id: "migration-fixture", | |
| configSchema: { | |
| type: "object", | |
| required: ["current"], | |
| properties: { current: { type: "boolean" } }, | |
| additionalProperties: false, | |
| }, | |
| }), | |
| ); | |
| const config = { | |
| plugins: { | |
| allow: ["migration-fixture"], | |
| load: { paths: [pluginRoot] }, | |
| entries: { "migration-fixture": { enabled: true, config: { legacy: true } } }, | |
| }, | |
| }; | |
| await fs.writeFile(configPath, JSON.stringify(config)); | |
| resetConfigRuntimeState(); | |
| const snapshot = await readConfigFileSnapshot({ observe: false }); | |
| expect(snapshot.valid).toBe(false); | |
| expect( | |
| snapshot.issues.some((issue) => issue.path.startsWith("plugins.entries.migration-fixture")), | |
| ).toBe(true); | |
| expect(await getServiceActionPreflightFailure("start")).not.toBeNull(); | |
| expect(await getServiceActionPreflightFailure("restart")).not.toBeNull(); | |
| expect(await getServiceActionPreflightFailure("stop")).toBeNull(); | |
| await fs.writeFile( | |
| configPath, | |
| JSON.stringify({ ...config, meta: { lastTouchedVersion: "9999.1.1" } }), | |
| ); | |
| resetConfigRuntimeState(); | |
| expect((await getServiceActionPreflightFailure("stop"))?.message).toContain( | |
| "older than the config", | |
| ); | |
| }); | |
| }); | |
| it.each(["start", "restart"] as const)( | |
| "allows %s when no legacy credential files exist", | |
| async (action) => { | |
| await withIsolatedLifecycleState(async () => { | |
| await expect(getServiceActionPreflightFailure(action)).resolves.toBeNull(); | |
| }); | |
| }, | |
| ); | |
| it.each(["start", "restart", "stop"] as const)( | |
| "renders actionable invalid-config diagnostics before %s", | |
| async (action) => { | |
| await withIsolatedLifecycleState(async ({ configPath }) => { | |
| await fs.writeFile( | |
| configPath, | |
| '{\n meta: { lastTouchedVersion: "9999.1.1" },\n gateway: { mode: "nope" },\n}\n', | |
| ); | |
| resetConfigRuntimeState(); | |
| const failure = await getServiceActionPreflightFailure(action); | |
| expect(failure?.message).toContain( | |
| 'openclaw.json:3 — gateway.mode: Invalid input (allowed: "local", "remote"), got: "nope"', | |
| ); | |
| expect(failure?.message).toContain( | |
| "Config was last written by OpenClaw 9999.1.1, but you are running", | |
| ); | |
| }); | |
| }, | |
| ); | |
| }); | |