





















@@ -46,6 +46,15 @@ vi.mock("./subagent-orphan-recovery.js", () => ({
4646scheduleOrphanRecovery: vi.fn(),
4747}));
484849+function expectFields(value: unknown, expected: Record<string, unknown>): void {
50+expect(value).toBeTypeOf("object");
51+expect(value).not.toBeNull();
52+const record = value as Record<string, unknown>;
53+for (const [key, expectedValue] of Object.entries(expected)) {
54+expect(record[key], key).toEqual(expectedValue);
55+}
56+}
57+4958describe("subagent registry persistence", () => {
5059const envSnapshot = captureEnv(["OPENCLAW_STATE_DIR"]);
5160let tempStateDir: string | null = null;
@@ -288,11 +297,13 @@ describe("subagent registry persistence", () => {
288297289298// announce should NOT be called since cleanupHandled was true
290299const calls = (announceSpy.mock.calls as unknown as Array<[unknown]>).map((call) => call[0]);
291-expect(calls).not.toContainEqual(
292-expect.objectContaining({
293-childSessionKey: "agent:main:subagent:two",
294-}),
295-);
300+expect(
301+calls.some(
302+(call) =>
303+(call as { childSessionKey?: unknown } | undefined)?.childSessionKey ===
304+"agent:main:subagent:two",
305+),
306+).toBe(false);
296307});
297308298309it("maps legacy announce fields into cleanup state", async () => {
@@ -366,10 +377,8 @@ describe("subagent registry persistence", () => {
366377}
367378const second = loadSubagentRegistryFromDisk();
368379369-expect(second.get("run-cached")).toMatchObject({
370-requesterOrigin: { accountId: "cached-account" },
371-outcome: { status: "ok" },
372-});
380+expectFields(second.get("run-cached")?.requesterOrigin, { accountId: "cached-account" });
381+expectFields(second.get("run-cached")?.outcome, { status: "ok" });
373382expect(second.get("run-cached")?.endedAt).toBeUndefined();
374383expect(second.get("run-cached")?.cleanupHandled).toBeUndefined();
375384@@ -428,20 +437,18 @@ describe("subagent registry persistence", () => {
428437429438const restored = loadSubagentRegistryFromDisk();
430439const restoredEntry = restored.get("run-spaced");
431-expect(restoredEntry).toMatchObject({
440+expectFields(restoredEntry, {
432441childSessionKey: "agent:main:subagent:spaced-child",
433442controllerSessionKey: "agent:main:subagent:controller",
434443requesterSessionKey: "agent:main:main",
435444});
436445437446resetSubagentRegistryForTests({ persist: false });
438447addSubagentRunForTests(restoredEntry as never);
439-expect(listSubagentRunsForRequester("agent:main:main")).toEqual([
440-expect.objectContaining({
441-runId: "run-spaced",
442-}),
443-]);
444-expect(getSubagentRunByChildSessionKey("agent:main:subagent:spaced-child")).toMatchObject({
448+const restoredRuns = listSubagentRunsForRequester("agent:main:main");
449+expect(restoredRuns).toHaveLength(1);
450+expectFields(restoredRuns[0], { runId: "run-spaced" });
451+expectFields(getSubagentRunByChildSessionKey("agent:main:subagent:spaced-child"), {
445452runId: "run-spaced",
446453});
447454@@ -463,15 +470,15 @@ describe("subagent registry persistence", () => {
463470cleanup: "keep",
464471});
465472466-expect(listSubagentRunsForRequester("agent:main:main")).toEqual([
467- expect.objectContaining({
468- runId: "run-live",
469- childSessionKey: "agent:main:subagent:live-child",
470- controllerSessionKey: "agent:main:subagent:live-controller",
471- requesterSessionKey: "agent:main:main",
472-}),
473-]);
474-expect(getSubagentRunByChildSessionKey("agent:main:subagent:live-child")).toMatchObject({
473+const liveRuns = listSubagentRunsForRequester("agent:main:main");
474+expect(liveRuns).toHaveLength(1);
475+expectFields(liveRuns[0], {
476+runId: "run-live",
477+childSessionKey: "agent:main:subagent:live-child",
478+controllerSessionKey: "agent:main:subagent:live-controller",
479+requesterSessionKey: "agent:main:main",
480+});
481+expectFields(getSubagentRunByChildSessionKey("agent:main:subagent:live-child"), {
475482runId: "run-live",
476483});
477484});
@@ -672,9 +679,11 @@ describe("subagent registry persistence", () => {
672679673680it("keeps stale unended restored runs with abortedLastRun for restart recovery", async () => {
674681vi.mocked(callGateway).mockImplementationOnce(async (request) => {
675-expect(request).toMatchObject({
682+expectFields(request, {
676683method: "agent.wait",
677-params: { runId: "run-stale-aborted-restore" },
684+});
685+expectFields((request as { params?: unknown }).params, {
686+runId: "run-stale-aborted-restore",
678687});
679688return {
680689status: "pending",
@@ -711,12 +720,10 @@ describe("subagent registry persistence", () => {
711720restartRegistry();
712721await waitForRegistryWork(() => vi.mocked(callGateway).mock.calls.length > 0);
713722714-expect(callGateway).toHaveBeenCalledWith(
715-expect.objectContaining({
716-method: "agent.wait",
717-params: expect.objectContaining({ runId }),
718-}),
719-);
723+expect(callGateway).toHaveBeenCalledTimes(1);
724+const [request] = vi.mocked(callGateway).mock.calls[0] ?? [];
725+expectFields(request, { method: "agent.wait" });
726+expectFields((request as { params?: unknown } | undefined)?.params, { runId });
720727expect(
721728listSubagentRunsForRequester("agent:main:main").some((entry) => entry.runId === runId),
722729).toBe(true);
@@ -755,7 +762,7 @@ describe("subagent registry persistence", () => {
755762}
756763});
757764758-await expect(fs.access(attachmentsDir)).rejects.toMatchObject({ code: "ENOENT" });
765+await expect(fs.access(attachmentsDir)).rejects.toHaveProperty("code", "ENOENT");
759766const after = JSON.parse(await fs.readFile(registryPath, "utf8")) as {
760767runs?: Record<string, unknown>;
761768};
@@ -801,7 +808,7 @@ describe("subagent registry persistence", () => {
801808getSubagentRunByChildSessionKey(childSessionKey),
802809);
803810804-expect(resolved).toMatchObject({
811+expectFields(resolved, {
805812runId: "run-active",
806813 childSessionKey,
807814});
@@ -847,7 +854,7 @@ describe("subagent registry persistence", () => {
847854getLatestSubagentRunByChildSessionKey(childSessionKey),
848855);
849856850-expect(resolved).toMatchObject({
857+expectFields(resolved, {
851858runId: "run-current-ended",
852859 childSessionKey,
853860});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。