mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-19 10:04:44 +00:00
test(gateway/cron): assert symmetric agentId derivation across enqueue and wake
When `cron.wake` is called with only an agent-prefixed `sessionKey` (no explicit `agentId`), the gateway cron adapter must derive the same agentId on both `enqueueSystemEvent` and `requestHeartbeat` so events land in (and heartbeats fire on) the same agent target. Pre-PR, only `requestHeartbeat` derived agentId from the key; `enqueueSystemEvent` ran through `resolveCronSessionKey` with the configured-default agent and was rerouted to that agent's main session under multi-agent deployments where `main` exists but is not the default. The new test exercises the cron-adapter directly via `state.cron.state.deps` with a multi-agent config (`primary` default + `ops` non-default) and a `agent:ops:cron:nightly:run:abc-123` foreign-agent session key, asserting that both call sites resolve the agent target to "ops" rather than falling back to "primary". Refs #78687. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Peter Steinberger
parent
8399ff888f
commit
764bb7fbf7
@@ -525,6 +525,84 @@ describe("buildGatewayCronService", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("derives agentId symmetrically for enqueue and wake when only an agent-prefixed sessionKey is supplied", () => {
|
||||
// Multi-agent setup where the configured default ("primary") is NOT the
|
||||
// agent referenced in the sessionKey ("ops"). Pre-PR, enqueue went through
|
||||
// resolveCronSessionKey which treated a non-default agent's key as foreign
|
||||
// and rerouted to primary's main session, while requestHeartbeat correctly
|
||||
// derived agentId from the key — so wake hit ops while the event landed in
|
||||
// primary's queue. Both adapter call sites now derive agentId from the
|
||||
// session key the same way.
|
||||
const cfg = {
|
||||
session: { mainKey: "main" },
|
||||
cron: { store: path.join(os.tmpdir(), `server-cron-symmetric-${Date.now()}`, "cron.json") },
|
||||
agents: {
|
||||
list: [
|
||||
{ id: "primary", default: true, model: "test/primary" },
|
||||
{ id: "ops", model: "test/ops" },
|
||||
],
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
loadConfigMock.mockReturnValue(cfg);
|
||||
|
||||
const state = buildGatewayCronService({
|
||||
cfg,
|
||||
deps: {} as CliDeps,
|
||||
broadcast: () => {},
|
||||
});
|
||||
try {
|
||||
const cronDeps = (
|
||||
state.cron as unknown as {
|
||||
state?: {
|
||||
deps?: {
|
||||
enqueueSystemEvent?: (
|
||||
text: string,
|
||||
opts?: { agentId?: string; sessionKey?: string; contextKey?: string },
|
||||
) => void;
|
||||
requestHeartbeat?: (opts?: {
|
||||
agentId?: string;
|
||||
sessionKey?: string | null;
|
||||
source?: string;
|
||||
intent?: string;
|
||||
reason?: string;
|
||||
}) => void;
|
||||
};
|
||||
};
|
||||
}
|
||||
).state?.deps;
|
||||
|
||||
const foreignKey = "agent:ops:cron:nightly:run:abc-123";
|
||||
|
||||
cronDeps?.enqueueSystemEvent?.("hello", {
|
||||
sessionKey: foreignKey,
|
||||
contextKey: "cron:test",
|
||||
});
|
||||
cronDeps?.requestHeartbeat?.({
|
||||
source: "cron",
|
||||
intent: "event",
|
||||
reason: "cron:test",
|
||||
sessionKey: foreignKey,
|
||||
});
|
||||
|
||||
// Both must derive agentId="ops" from the key, NOT fall back to the
|
||||
// configured default "primary". The exact resolved sessionKey is
|
||||
// delegated to resolveCronSessionKey (already covered by other tests);
|
||||
// here we only assert the agent target is consistent across both sides.
|
||||
const enqueueCall = enqueueSystemEventMock.mock.calls.at(-1);
|
||||
const wakeCall = requestHeartbeatMock.mock.calls.at(-1);
|
||||
const enqueueSessionKey = (enqueueCall?.[1] as { sessionKey?: string } | undefined)
|
||||
?.sessionKey;
|
||||
const wakeOpts = wakeCall?.[0] as { agentId?: string; sessionKey?: string } | undefined;
|
||||
|
||||
expect(enqueueSessionKey).toBeDefined();
|
||||
expect(enqueueSessionKey).toMatch(/^agent:ops:/);
|
||||
expect(wakeOpts?.agentId).toBe("ops");
|
||||
expect(wakeOpts?.sessionKey).toMatch(/^agent:ops:/);
|
||||
} finally {
|
||||
state.cron.stop();
|
||||
}
|
||||
});
|
||||
|
||||
it("preserves trust downgrades when cron enqueues system events", () => {
|
||||
const cfg = createCronConfig("server-cron-untrusted");
|
||||
loadConfigMock.mockReturnValue(cfg);
|
||||
|
||||
Reference in New Issue
Block a user