mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 12:26:38 -06:00
test(memory): inventory Control UI migration routes
This commit is contained in:
@@ -570,7 +570,9 @@ export function createMemoryAuthorizationConformanceScenarios(): MemoryAuthoriza
|
||||
...staleMount,
|
||||
plan: {
|
||||
...staleMount.plan,
|
||||
mounts: staleMount.plan.mounts.map((mount) => ({ ...mount, storeId: "store-b" })),
|
||||
mounts: staleMount.plan.mounts.map((mount) =>
|
||||
Object.assign({}, mount, { storeId: "store-b" }),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -579,10 +581,9 @@ export function createMemoryAuthorizationConformanceScenarios(): MemoryAuthoriza
|
||||
...staleMountCapabilities,
|
||||
plan: {
|
||||
...staleMountCapabilities.plan,
|
||||
mounts: staleMountCapabilities.plan.mounts.map((mount) => ({
|
||||
...mount,
|
||||
capabilities: ["retrieve"],
|
||||
})),
|
||||
mounts: staleMountCapabilities.plan.mounts.map((mount) =>
|
||||
Object.assign({}, mount, { capabilities: ["retrieve"] }),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -591,7 +592,9 @@ export function createMemoryAuthorizationConformanceScenarios(): MemoryAuthoriza
|
||||
...staleMountAgent,
|
||||
plan: {
|
||||
...staleMountAgent.plan,
|
||||
mounts: staleMountAgent.plan.mounts.map((mount) => ({ ...mount, agentId: "agent-b" })),
|
||||
mounts: staleMountAgent.plan.mounts.map((mount) =>
|
||||
Object.assign({}, mount, { agentId: "agent-b" }),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -600,10 +603,9 @@ export function createMemoryAuthorizationConformanceScenarios(): MemoryAuthoriza
|
||||
...staleMountAudience,
|
||||
plan: {
|
||||
...staleMountAudience.plan,
|
||||
mounts: staleMountAudience.plan.mounts.map((mount) => ({
|
||||
...mount,
|
||||
audienceRevision: "audience-revision-2",
|
||||
})),
|
||||
mounts: staleMountAudience.plan.mounts.map((mount) =>
|
||||
Object.assign({}, mount, { audienceRevision: "audience-revision-2" }),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ function hasExactlyTheSameUniqueSet<T>(
|
||||
actualKeys.length === actualSet.size &&
|
||||
expectedKeys.length === expectedSet.size &&
|
||||
actualSet.size === expectedSet.size &&
|
||||
[...actualSet].every((key) => expectedSet.has(key))
|
||||
[...actualSet].every((candidateKey) => expectedSet.has(candidateKey))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ function mountKey(mount: MemoryAuthorizationConformanceMount): string {
|
||||
mount.storeId,
|
||||
mount.agentId,
|
||||
mount.audienceRevision,
|
||||
[...mount.capabilities].sort(),
|
||||
mount.capabilities.toSorted(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,9 @@ describe("project memory bootstrap", () => {
|
||||
it("renders budgeted curated candidates through the selected active runtime", async () => {
|
||||
const rendered = await prepareEntries(entries);
|
||||
|
||||
expect(getSelectedMemoryRuntime()?.getMemorySearchManager).toBe(runtimeMocks.getManager);
|
||||
expect(getSelectedMemoryRuntime()).toEqual(
|
||||
expect.objectContaining({ getMemorySearchManager: runtimeMocks.getManager }),
|
||||
);
|
||||
expect(runtimeMocks.getManager).toHaveBeenCalledOnce();
|
||||
expect(runtimeMocks.listCurated).toHaveBeenCalledWith({
|
||||
activeProjectKeys: ["github.com/OpenClaw/OpenClaw"],
|
||||
|
||||
@@ -423,6 +423,7 @@ export const MEMORY_AUTHORIZATION_PATH_INVENTORY = Object.freeze([
|
||||
"extensions/memory-core/src/session-backfill-gateway.runtime.ts",
|
||||
"extensions/memory-core/src/session-backfill.ts",
|
||||
"extensions/memory-core/src/session-ingestion.ts",
|
||||
"ui/src/pages/memory-import/memory-import-page.ts",
|
||||
),
|
||||
entry(
|
||||
"session-backfill-derived-memory-write",
|
||||
@@ -444,6 +445,7 @@ export const MEMORY_AUTHORIZATION_PATH_INVENTORY = Object.freeze([
|
||||
"extensions/memory-core/src/short-term-promotion-record.ts",
|
||||
"extensions/memory-core/src/short-term-promotion-artifacts.ts",
|
||||
"extensions/memory-core/src/dreaming-narrative.ts",
|
||||
"ui/src/pages/memory-import/memory-import-page.ts",
|
||||
),
|
||||
entry(
|
||||
"dreaming-session-transcript-ingestion",
|
||||
@@ -500,6 +502,8 @@ export const MEMORY_AUTHORIZATION_PATH_INVENTORY = Object.freeze([
|
||||
"src/wizard/setup.post-install-migration.ts",
|
||||
"src/wizard/setup.memory-import.ts",
|
||||
"src/plugin-sdk/migration-runtime.ts",
|
||||
"ui/src/components/onboarding-memory-import.ts",
|
||||
"ui/src/pages/memory-import/memory-import-page.ts",
|
||||
),
|
||||
entry(
|
||||
"memory-export",
|
||||
|
||||
@@ -141,6 +141,8 @@ const MEMORY_MIGRATION_IMPORT_ROUTE_SURFACES = [
|
||||
"src/wizard/setup.post-install-migration.ts",
|
||||
"src/wizard/setup.memory-import.ts",
|
||||
"src/plugin-sdk/migration-runtime.ts",
|
||||
"ui/src/components/onboarding-memory-import.ts",
|
||||
"ui/src/pages/memory-import/memory-import-page.ts",
|
||||
] as const;
|
||||
|
||||
const MEMORY_MIGRATION_IMPORT_ROOTS = [
|
||||
@@ -152,8 +154,22 @@ const MEMORY_MIGRATION_IMPORT_ROOTS = [
|
||||
"src/gateway/server-methods",
|
||||
"src/wizard",
|
||||
"src/plugin-sdk",
|
||||
"ui/src",
|
||||
] as const;
|
||||
|
||||
const SESSION_BACKFILL_INGRESS_ROOTS = ["extensions/memory-core/src", "ui/src"] as const;
|
||||
|
||||
const MEMORY_MIGRATION_IMPORT_REQUEST_METHODS = new Set([
|
||||
"migrations.memory.plan",
|
||||
"migrations.memory.apply",
|
||||
]);
|
||||
|
||||
const SESSION_BACKFILL_REQUEST_METHODS = new Set([
|
||||
"memory.sessionBackfill.preview",
|
||||
"memory.sessionBackfill.apply",
|
||||
"memory.sessionBackfill.rollback",
|
||||
]);
|
||||
|
||||
const MEMORY_CORE_DOCTOR_STATE_MIGRATION_REGISTRATION_SURFACES = [
|
||||
"extensions/memory-core/doctor-contract-api.ts",
|
||||
"src/plugins/doctor-contract-registry.ts",
|
||||
@@ -279,6 +295,19 @@ function isMigrationProviderApplyCall(expression: ts.LeftHandSideExpression): bo
|
||||
);
|
||||
}
|
||||
|
||||
function directClientRequestMethod(node: ts.CallExpression): string | null {
|
||||
if (
|
||||
!ts.isPropertyAccessExpression(node.expression) ||
|
||||
!ts.isIdentifier(node.expression.expression) ||
|
||||
node.expression.expression.text !== "client" ||
|
||||
node.expression.name.text !== "request"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const method = node.arguments[0];
|
||||
return method && ts.isStringLiteral(method) ? method.text : null;
|
||||
}
|
||||
|
||||
function containsMemoryStringLiteral(node: ts.Node): boolean {
|
||||
let found = false;
|
||||
const visit = (child: ts.Node) => {
|
||||
@@ -330,6 +359,10 @@ function listMemoryMigrationIngressMarkers(file: string, sourceText: string): st
|
||||
markers.push(node.getText(source));
|
||||
}
|
||||
if (ts.isCallExpression(node)) {
|
||||
const requestMethod = directClientRequestMethod(node);
|
||||
if (requestMethod && MEMORY_MIGRATION_IMPORT_REQUEST_METHODS.has(requestMethod)) {
|
||||
markers.push(requestMethod);
|
||||
}
|
||||
if (isNamedCall(node.expression, copyMemoryBindings)) {
|
||||
markers.push(node.getText(source));
|
||||
}
|
||||
@@ -357,6 +390,22 @@ function listMemoryMigrationIngressMarkers(file: string, sourceText: string): st
|
||||
return markers;
|
||||
}
|
||||
|
||||
function listSessionBackfillIngressMarkers(file: string, sourceText: string): string[] {
|
||||
const source = ts.createSourceFile(file, sourceText, ts.ScriptTarget.Latest, true);
|
||||
const markers: string[] = [];
|
||||
const visit = (node: ts.Node) => {
|
||||
if (ts.isCallExpression(node)) {
|
||||
const requestMethod = directClientRequestMethod(node);
|
||||
if (requestMethod && SESSION_BACKFILL_REQUEST_METHODS.has(requestMethod)) {
|
||||
markers.push(requestMethod);
|
||||
}
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
};
|
||||
visit(source);
|
||||
return markers;
|
||||
}
|
||||
|
||||
function listSessionTranscriptIngestionCalls(file: string, sourceText: string): string[] {
|
||||
const source = ts.createSourceFile(file, sourceText, ts.ScriptTarget.Latest, true);
|
||||
const callBindings = new Map(
|
||||
@@ -658,6 +707,7 @@ describe("memory authorization path inventory", () => {
|
||||
"extensions/memory-core/src/session-backfill-gateway.ts",
|
||||
"extensions/memory-core/src/session-backfill-gateway.runtime.ts",
|
||||
"extensions/memory-core/src/session-backfill.ts",
|
||||
"ui/src/pages/memory-import/memory-import-page.ts",
|
||||
]),
|
||||
});
|
||||
expect(entriesById.get("session-backfill-derived-memory-write")).toMatchObject({
|
||||
@@ -678,6 +728,7 @@ describe("memory authorization path inventory", () => {
|
||||
"extensions/memory-core/src/session-ingestion.ts",
|
||||
"extensions/memory-core/src/short-term-promotion-record.ts",
|
||||
"extensions/memory-core/src/short-term-promotion-artifacts.ts",
|
||||
"ui/src/pages/memory-import/memory-import-page.ts",
|
||||
]),
|
||||
});
|
||||
expect(entriesById.get("dreaming-session-transcript-ingestion")).toMatchObject({
|
||||
@@ -797,6 +848,32 @@ describe("memory authorization path inventory", () => {
|
||||
).toEqual(["provider-memory-import-apply"]);
|
||||
});
|
||||
|
||||
it("finds direct Control UI migration and session-backfill requests", () => {
|
||||
const source = `
|
||||
async function importMemory(
|
||||
client: { request: Function },
|
||||
unrelated: { request: Function },
|
||||
) {
|
||||
await client.request("migrations.memory.plan", {});
|
||||
await client.request("migrations.memory.apply", {});
|
||||
await client.request("memory.sessionBackfill.preview", {});
|
||||
await client.request("memory.sessionBackfill.apply", {});
|
||||
await client.request("memory.sessionBackfill.rollback", {});
|
||||
await unrelated.request("migrations.memory.plan", {});
|
||||
}
|
||||
`;
|
||||
|
||||
expect(listMemoryMigrationIngressMarkers("fixture.ts", source)).toEqual([
|
||||
"migrations.memory.plan",
|
||||
"migrations.memory.apply",
|
||||
]);
|
||||
expect(listSessionBackfillIngressMarkers("fixture.ts", source)).toEqual([
|
||||
"memory.sessionBackfill.preview",
|
||||
"memory.sessionBackfill.apply",
|
||||
"memory.sessionBackfill.rollback",
|
||||
]);
|
||||
});
|
||||
|
||||
it("finds direct and aliased short-term recall recorders without matching properties", () => {
|
||||
expect(
|
||||
listImportedShortTermRecallRecordingCalls(
|
||||
@@ -883,9 +960,9 @@ describe("memory authorization path inventory", () => {
|
||||
expect(missing).toEqual([]);
|
||||
});
|
||||
|
||||
it("does not allow an unrecorded production session transcript ingestion route", () => {
|
||||
it("does not allow an unrecorded production session transcript or Control UI backfill route", () => {
|
||||
const scanned = listRepoFilesSync(REPO_ROOT, {
|
||||
roots: ["extensions/memory-core/src"],
|
||||
roots: SESSION_BACKFILL_INGRESS_ROOTS,
|
||||
includeFile: isProductionTypeScript,
|
||||
});
|
||||
const inventoried = new Set(inventory.flatMap((item) => item.surfaces));
|
||||
@@ -895,6 +972,10 @@ describe("memory authorization path inventory", () => {
|
||||
listSessionTranscriptIngestionCalls(
|
||||
file,
|
||||
fs.readFileSync(path.join(REPO_ROOT, file), "utf8"),
|
||||
).length > 0 ||
|
||||
listSessionBackfillIngressMarkers(
|
||||
file,
|
||||
fs.readFileSync(path.join(REPO_ROOT, file), "utf8"),
|
||||
).length > 0,
|
||||
)
|
||||
.filter((file) => !inventoried.has(file));
|
||||
|
||||
Reference in New Issue
Block a user