f6124f3e17
* ACP: harden Discord recovery and reset flow * CI: harden bundled vitest excludes * ACP: fix Claude launch and reset recovery * Discord: use follow-up replies after slash defer * ACP: route bound resets through gateway service * ACP: unify bound reset authority * ACPX: update OpenClaw branch to 0.5.2 * ACP: fix rebuilt branch replay fallout * ACP: fix CI regressions after ACPX 0.5.2 update --------- Co-authored-by: Onur <2453968+osolmaz@users.noreply.github.com>
102 lines
3.2 KiB
TypeScript
102 lines
3.2 KiB
TypeScript
import type { AcpSessionStore } from "acpx/runtime";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import type { AcpRuntime } from "../runtime-api.js";
|
|
import { AcpxRuntime } from "./runtime.js";
|
|
|
|
function makeRuntime(baseStore: AcpSessionStore): {
|
|
runtime: AcpxRuntime;
|
|
wrappedStore: AcpSessionStore & { markFresh: (sessionKey: string) => void };
|
|
delegate: { close: AcpRuntime["close"] };
|
|
} {
|
|
const runtime = new AcpxRuntime({
|
|
cwd: "/tmp",
|
|
sessionStore: baseStore,
|
|
agentRegistry: {
|
|
resolve: () => "codex",
|
|
list: () => ["codex"],
|
|
},
|
|
permissionMode: "approve-reads",
|
|
});
|
|
|
|
return {
|
|
runtime,
|
|
wrappedStore: (
|
|
runtime as unknown as {
|
|
sessionStore: AcpSessionStore & { markFresh: (sessionKey: string) => void };
|
|
}
|
|
).sessionStore,
|
|
delegate: (runtime as unknown as { delegate: { close: AcpRuntime["close"] } }).delegate,
|
|
};
|
|
}
|
|
|
|
describe("AcpxRuntime fresh reset wrapper", () => {
|
|
beforeEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it("keeps stale persistent loads hidden until a fresh record is saved", async () => {
|
|
const baseStore: AcpSessionStore = {
|
|
load: vi.fn(async () => ({ acpxRecordId: "stale" }) as never),
|
|
save: vi.fn(async () => {}),
|
|
};
|
|
|
|
const { runtime, wrappedStore } = makeRuntime(baseStore);
|
|
|
|
expect(await wrappedStore.load("agent:codex:acp:binding:test")).toEqual({
|
|
acpxRecordId: "stale",
|
|
});
|
|
expect(baseStore.load).toHaveBeenCalledTimes(1);
|
|
|
|
await runtime.prepareFreshSession({
|
|
sessionKey: "agent:codex:acp:binding:test",
|
|
});
|
|
|
|
expect(await wrappedStore.load("agent:codex:acp:binding:test")).toBeUndefined();
|
|
expect(baseStore.load).toHaveBeenCalledTimes(1);
|
|
expect(await wrappedStore.load("agent:codex:acp:binding:test")).toBeUndefined();
|
|
expect(baseStore.load).toHaveBeenCalledTimes(1);
|
|
|
|
await wrappedStore.save({
|
|
acpxRecordId: "fresh-record",
|
|
name: "agent:codex:acp:binding:test",
|
|
} as never);
|
|
|
|
expect(await wrappedStore.load("agent:codex:acp:binding:test")).toEqual({
|
|
acpxRecordId: "stale",
|
|
});
|
|
expect(baseStore.load).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it("marks the session fresh after discardPersistentState close", async () => {
|
|
const baseStore: AcpSessionStore = {
|
|
load: vi.fn(async () => ({ acpxRecordId: "stale" }) as never),
|
|
save: vi.fn(async () => {}),
|
|
};
|
|
|
|
const { runtime, wrappedStore, delegate } = makeRuntime(baseStore);
|
|
const close = vi.spyOn(delegate, "close").mockResolvedValue(undefined);
|
|
|
|
await runtime.close({
|
|
handle: {
|
|
sessionKey: "agent:codex:acp:binding:test",
|
|
backend: "acpx",
|
|
runtimeSessionName: "agent:codex:acp:binding:test",
|
|
},
|
|
reason: "new-in-place-reset",
|
|
discardPersistentState: true,
|
|
});
|
|
|
|
expect(close).toHaveBeenCalledWith({
|
|
handle: {
|
|
sessionKey: "agent:codex:acp:binding:test",
|
|
backend: "acpx",
|
|
runtimeSessionName: "agent:codex:acp:binding:test",
|
|
},
|
|
reason: "new-in-place-reset",
|
|
discardPersistentState: true,
|
|
});
|
|
expect(await wrappedStore.load("agent:codex:acp:binding:test")).toBeUndefined();
|
|
expect(baseStore.load).not.toHaveBeenCalled();
|
|
});
|
|
});
|