File size: 5,031 Bytes
76289e7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | import { DatabaseSync } from "node:sqlite";
import { describe, expect, it } from "vitest";
import { OPENCLAW_STATE_SCHEMA_VERSION } from "../state/openclaw-state-db-contract.js";
import { resolveOpenClawStateSqlitePath } from "../state/openclaw-state-db.paths.js";
import { withOpenClawTestState } from "../test-utils/openclaw-test-state.js";
import {
createPluginStateKeyedStore,
resetPluginStateStoreForTests,
} from "./plugin-state-store.js";
describe("plugin state schema compatibility", () => {
it("cold-opens when an existing move table lacks its first-use column", async () => {
await withOpenClawTestState(
{ label: "plugin-state-placement-move-column", applyEnv: false },
async (state) => {
try {
const first = createPluginStateKeyedStore<{ owner: string }>("discord", {
namespace: "same-version-placement-move",
maxEntries: 10,
env: state.env,
});
await first.register("first", { owner: "discord" });
resetPluginStateStoreForTests();
const databasePath = resolveOpenClawStateSqlitePath(state.env);
const previousDatabase = new DatabaseSync(databasePath);
let versionBefore: unknown;
let metadataBefore: unknown;
try {
versionBefore = previousDatabase.prepare("PRAGMA user_version").get();
metadataBefore = previousDatabase
.prepare("SELECT * FROM schema_meta WHERE meta_key = 'primary'")
.get();
expect(versionBefore).toEqual({ user_version: OPENCLAW_STATE_SCHEMA_VERSION });
expect(
previousDatabase
.prepare(
"SELECT plugin_id, namespace, entry_key, value_json FROM plugin_state_entries",
)
.all(),
).toEqual([
{
plugin_id: "discord",
namespace: "same-version-placement-move",
entry_key: "first",
value_json: JSON.stringify({ owner: "discord" }),
},
]);
expect(
previousDatabase
.prepare("SELECT COUNT(*) AS count FROM worker_session_placement_moves")
.get(),
).toEqual({ count: 0 });
const columnsBefore = previousDatabase
.prepare("PRAGMA table_info(worker_session_placement_moves)")
.all()
.map((column) => (column as { name: string }).name);
expect(columnsBefore).toContain("target_machine_class");
previousDatabase.exec(
"ALTER TABLE worker_session_placement_moves DROP COLUMN target_machine_class;",
);
expect(
previousDatabase
.prepare("PRAGMA table_info(worker_session_placement_moves)")
.all()
.map((column) => (column as { name: string }).name),
).toEqual(columnsBefore.filter((column) => column !== "target_machine_class"));
} finally {
previousDatabase.close();
}
const second = createPluginStateKeyedStore<{ owner: string }>("telegram", {
namespace: "same-version-placement-move",
maxEntries: 10,
env: state.env,
});
await second.register("second", { owner: "telegram" });
resetPluginStateStoreForTests();
const reopenedDatabase = new DatabaseSync(databasePath);
try {
expect(
reopenedDatabase
.prepare(
`SELECT plugin_id, namespace, entry_key, value_json
FROM plugin_state_entries
ORDER BY plugin_id`,
)
.all(),
).toEqual([
{
plugin_id: "discord",
namespace: "same-version-placement-move",
entry_key: "first",
value_json: JSON.stringify({ owner: "discord" }),
},
{
plugin_id: "telegram",
namespace: "same-version-placement-move",
entry_key: "second",
value_json: JSON.stringify({ owner: "telegram" }),
},
]);
expect(
reopenedDatabase
.prepare("PRAGMA table_info(worker_session_placement_moves)")
.all()
.map((column) => (column as { name: string }).name),
).not.toContain("target_machine_class");
expect(reopenedDatabase.prepare("PRAGMA user_version").get()).toEqual(versionBefore);
expect(
reopenedDatabase
.prepare("SELECT * FROM schema_meta WHERE meta_key = 'primary'")
.get(),
).toEqual(metadataBefore);
} finally {
reopenedDatabase.close();
}
} finally {
resetPluginStateStoreForTests();
}
},
);
});
});
|