Files
openclaw/test/gateway-external-state-ownership.e2e.test.ts
Peter Steinberger f31d9d8fa9 fix: prevent externally supervised state schema drift (#121069)
* fix: prevent externally supervised state schema drift

* refactor: isolate schema ownership support code

* test: follow canonical additive column order

* test: keep older schema fixture valid

* fix: preserve additive schema compatibility

* chore: remove release-owned changelog entry

* test: follow schema compatibility owner

* style: format schema compatibility test

* refactor: split sqlite schema sql helpers

* fix: preserve desktop schema compatibility

* fix: preserve ownership gates across platforms

* fix: preserve detached updater long paths

* fix: close external state ownership races

* chore: refresh plugin sdk api baseline
2026-08-09 14:04:40 -07:00

84 lines
2.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { createOpenClawTestInstance } from "./helpers/openclaw-test-instance.js";
describe("Gateway external shared-state ownership", () => {
it("refuses unmarked startup and accepts the external supervisor marker", async () => {
const instance = await createOpenClawTestInstance({
name: "gateway-external-state-owner",
env: { OPENCLAW_SUPERVISOR_MODE: "external" },
startTimeoutMs: 30_000,
});
try {
const claim = await instance.cli([
"database",
"ownership",
"claim",
"--manager",
"gateway-supervisor",
"--json",
]);
expect(claim.code, claim.stderr).toBe(0);
const claimed = JSON.parse(claim.stdout) as {
databasePath: string;
ownership: { managerId: string };
status: string;
};
expect(claimed).toMatchObject({
status: "external",
ownership: { managerId: "gateway-supervisor" },
});
const preflight = await instance.cli([
"database",
"preflight",
claimed.databasePath,
"--json",
]);
expect(preflight.code, preflight.stderr).toBe(0);
expect(JSON.parse(preflight.stdout)).toMatchObject({
schema: "openclaw.state-schema-preflight.v1",
status: "exact",
requiresWrite: false,
});
const unreadable = await instance.cli([
"database",
"preflight",
`${instance.stateDir}/missing.sqlite`,
"--json",
]);
expect(unreadable.code).toBe(1);
expect(JSON.parse(unreadable.stdout)).toMatchObject({
schema: "openclaw.state-schema-preflight.v1",
status: "indeterminate",
});
const status = await instance.cli(["database", "ownership", "status", "--json"]);
expect(status.code, status.stderr).toBe(0);
expect(JSON.parse(status.stdout)).toMatchObject({
status: "external",
ownership: { managerId: "gateway-supervisor" },
});
const conflictingClaim = await instance.cli([
"database",
"ownership",
"claim",
"--manager",
"replacement-manager",
"--json",
]);
expect(conflictingClaim.code).toBe(1);
expect(JSON.parse(conflictingClaim.stdout)).toMatchObject({
error: expect.stringContaining("already claimed by external manager gateway-supervisor"),
});
delete instance.env.OPENCLAW_SUPERVISOR_MODE;
await expect(instance.startGateway()).rejects.toThrow(/gateway-supervisor/u);
expect(instance.logs()).toMatch(/OPENCLAW_SUPERVISOR_MODE=external/u);
instance.env.OPENCLAW_SUPERVISOR_MODE = "external";
await instance.startGateway();
expect(instance.child).toBeDefined();
} finally {
await instance.cleanup();
}
});
});