Files
openclaw/extensions/nostr/doctor-contract-api.test.ts
Peter Steinberger 8cb53c7b55 perf(doctor): keep bundled doctor contract closures dependency-light (#120698)
* 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.
2026-08-08 17:51:31 -07:00

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();
});
});