mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 12:56:01 -06:00
8cb53c7b55
* perf(doctor): keep bundled doctor contract closures dependency-light Doctor contract enumeration cold-loads each plugin's doctor-contract-api closure via jiti, so a static value import of openclaw/plugin-sdk/runtime-doctor pulled the state-db/kysely graph (~4.3s per closure) into listPluginDoctorLegacyConfigRules / listPluginDoctorStateMigrationEntries. - migrate all light doctor-contract closures (66 files) to the dependency-light openclaw/plugin-sdk/runtime-doctor-migrations subpath - voice-call: load detect/repairOpenClawStateDatabaseSchema* lazily inside the migration bodies; keep only a type-only static runtime-doctor import - matrix: split pure credential record shapes/normalizers into credentials-state.ts so the doctor closure no longer imports the sync plugin-state store through credentials-read - guard: doctor-contract-closure-guard.test.ts now forbids static value imports of runtime-doctor in closures alongside agent-runtime * fix(matrix): keep credential revocation record type module-local Knip production scan flags the export as consumer-less; the type is only referenced by the exported union and revocation guard signature.
140 lines
4.4 KiB
TypeScript
140 lines
4.4 KiB
TypeScript
// Nostr tests cover doctor contract api plugin behavior.
|
|
import fs from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { expectDefined } from "@openclaw/normalization-core";
|
|
import {
|
|
createPluginStateKeyedStoreForTests,
|
|
resetPluginStateStoreForTests,
|
|
} from "openclaw/plugin-sdk/plugin-state-test-runtime";
|
|
import type {
|
|
OpenKeyedStoreOptions,
|
|
PluginDoctorStateMigrationContext,
|
|
} from "openclaw/plugin-sdk/runtime-doctor-migrations";
|
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
import { stateMigrations } from "./doctor-contract-api.js";
|
|
|
|
function requireStateMigration(index: number) {
|
|
return expectDefined(stateMigrations[index], `Nostr state migration ${index}`);
|
|
}
|
|
|
|
function createDoctorContext(env: NodeJS.ProcessEnv): PluginDoctorStateMigrationContext {
|
|
return {
|
|
openPluginStateKeyedStore<T>(options: OpenKeyedStoreOptions) {
|
|
return createPluginStateKeyedStoreForTests<T>("nostr", {
|
|
...options,
|
|
env: options.env ?? env,
|
|
});
|
|
},
|
|
};
|
|
}
|
|
|
|
describe("nostr doctor state migration", () => {
|
|
let stateDir = "";
|
|
let env: NodeJS.ProcessEnv;
|
|
|
|
beforeEach(async () => {
|
|
resetPluginStateStoreForTests();
|
|
stateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-nostr-doctor-"));
|
|
env = { ...process.env, OPENCLAW_STATE_DIR: stateDir };
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await fs.rm(stateDir, { recursive: true, force: true });
|
|
});
|
|
|
|
it("imports legacy bus and profile state into plugin state", async () => {
|
|
const nostrDir = path.join(stateDir, "nostr");
|
|
const busPath = path.join(nostrDir, "bus-state-main.json");
|
|
const profilePath = path.join(nostrDir, "profile-state-main.json");
|
|
await fs.mkdir(nostrDir, { recursive: true });
|
|
await fs.writeFile(
|
|
busPath,
|
|
JSON.stringify({
|
|
version: 1,
|
|
lastProcessedAt: 1700,
|
|
gatewayStartedAt: 1600,
|
|
}),
|
|
);
|
|
await fs.writeFile(
|
|
profilePath,
|
|
JSON.stringify({
|
|
version: 1,
|
|
lastPublishedAt: 1800,
|
|
lastPublishedEventId: "event-1",
|
|
lastPublishResults: { "wss://relay.example": "ok", bad: "nope" },
|
|
}),
|
|
);
|
|
|
|
const context = createDoctorContext(env);
|
|
const busResult = await requireStateMigration(0).migrateLegacyState({
|
|
config: {},
|
|
env,
|
|
stateDir,
|
|
oauthDir: path.join(stateDir, "oauth"),
|
|
context,
|
|
});
|
|
const profileResult = await requireStateMigration(1).migrateLegacyState({
|
|
config: {},
|
|
env,
|
|
stateDir,
|
|
oauthDir: path.join(stateDir, "oauth"),
|
|
context,
|
|
});
|
|
|
|
expect(busResult.warnings).toEqual([]);
|
|
expect(profileResult.warnings).toEqual([]);
|
|
await expect(fs.access(busPath)).rejects.toThrow();
|
|
await expect(fs.access(profilePath)).rejects.toThrow();
|
|
await expect(fs.access(`${busPath}.migrated`)).resolves.toBeUndefined();
|
|
await expect(fs.access(`${profilePath}.migrated`)).resolves.toBeUndefined();
|
|
await expect(
|
|
context.openPluginStateKeyedStore({ namespace: "bus-state", maxEntries: 256 }).lookup("main"),
|
|
).resolves.toEqual({
|
|
version: 2,
|
|
lastProcessedAt: 1700,
|
|
gatewayStartedAt: 1600,
|
|
recentEventIds: [],
|
|
});
|
|
await expect(
|
|
context
|
|
.openPluginStateKeyedStore({ namespace: "profile-state", maxEntries: 256 })
|
|
.lookup("main"),
|
|
).resolves.toEqual({
|
|
version: 1,
|
|
lastPublishedAt: 1800,
|
|
lastPublishedEventId: "event-1",
|
|
lastPublishResults: { "wss://relay.example": "ok" },
|
|
});
|
|
});
|
|
|
|
it("preserves legacy account key bytes when importing state files", async () => {
|
|
const nostrDir = path.join(stateDir, "nostr");
|
|
const busPath = path.join(nostrDir, "bus-state-Team.A.json");
|
|
await fs.mkdir(nostrDir, { recursive: true });
|
|
await fs.writeFile(
|
|
busPath,
|
|
JSON.stringify({
|
|
version: 1,
|
|
lastProcessedAt: 1700,
|
|
gatewayStartedAt: 1600,
|
|
}),
|
|
);
|
|
|
|
const context = createDoctorContext(env);
|
|
await requireStateMigration(0).migrateLegacyState({
|
|
config: {},
|
|
env,
|
|
stateDir,
|
|
oauthDir: path.join(stateDir, "oauth"),
|
|
context,
|
|
});
|
|
|
|
const store = context.openPluginStateKeyedStore({ namespace: "bus-state", maxEntries: 256 });
|
|
await expect(store.lookup("Team.A")).resolves.toMatchObject({
|
|
lastProcessedAt: 1700,
|
|
});
|
|
await expect(store.lookup("team-a")).resolves.toBeUndefined();
|
|
});
|
|
});
|