chore: pr feedback
This commit is contained in:
parent
7623a1710b
commit
1fc48a5686
2 changed files with 46 additions and 7 deletions
|
|
@ -162,6 +162,21 @@ test("websocket lifecycle updates agent connection status", async () => {
|
||||||
expect(stop).toHaveBeenCalledWith(true);
|
expect(stop).toHaveBeenCalledWith(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("websocket open failure closes the upgraded socket", async () => {
|
||||||
|
agentsServiceMocks.markAgentConnecting.mockRejectedValueOnce(new Error("db unavailable"));
|
||||||
|
const serve = vi
|
||||||
|
.spyOn(Bun, "serve")
|
||||||
|
.mockReturnValue(fromPartial({ port: 3001, stop: vi.fn(() => Promise.resolve()) }));
|
||||||
|
const { runtime } = await startRuntime();
|
||||||
|
const websocket = serve.mock.calls[0]?.[0].websocket;
|
||||||
|
const socket = createSocket("connection-1");
|
||||||
|
|
||||||
|
await websocket?.open?.(fromPartial(socket));
|
||||||
|
|
||||||
|
expect(socket.close).toHaveBeenCalled();
|
||||||
|
await Effect.runPromise(runtime.stop);
|
||||||
|
});
|
||||||
|
|
||||||
test("shutdown closes all sessions and stops the server when marking one agent offline fails", async () => {
|
test("shutdown closes all sessions and stops the server when marking one agent offline fails", async () => {
|
||||||
agentsServiceMocks.markAgentOffline.mockRejectedValueOnce(new Error("db unavailable"));
|
agentsServiceMocks.markAgentOffline.mockRejectedValueOnce(new Error("db unavailable"));
|
||||||
const stop = vi.fn(() => Promise.resolve());
|
const stop = vi.fn(() => Promise.resolve());
|
||||||
|
|
@ -182,7 +197,7 @@ test("shutdown closes all sessions and stops the server when marking one agent o
|
||||||
expect(stop).toHaveBeenCalledWith(true);
|
expect(stop).toHaveBeenCalledWith(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("closing a replaced connection does not report the active agent as disconnected", async () => {
|
test("closing a replaced connection reports disconnect without marking the active agent offline", async () => {
|
||||||
const serve = vi
|
const serve = vi
|
||||||
.spyOn(Bun, "serve")
|
.spyOn(Bun, "serve")
|
||||||
.mockReturnValue(fromPartial({ port: 3001, stop: vi.fn(() => Promise.resolve()) }));
|
.mockReturnValue(fromPartial({ port: 3001, stop: vi.fn(() => Promise.resolve()) }));
|
||||||
|
|
@ -190,14 +205,39 @@ test("closing a replaced connection does not report the active agent as disconne
|
||||||
const websocket = serve.mock.calls[0]?.[0].websocket;
|
const websocket = serve.mock.calls[0]?.[0].websocket;
|
||||||
const oldSocket = createSocket("connection-1");
|
const oldSocket = createSocket("connection-1");
|
||||||
const newSocket = createSocket("connection-2");
|
const newSocket = createSocket("connection-2");
|
||||||
|
const offlineCallsBeforeClose = agentsServiceMocks.markAgentOffline.mock.calls.length;
|
||||||
|
|
||||||
await websocket?.open?.(fromPartial(oldSocket));
|
await websocket?.open?.(fromPartial(oldSocket));
|
||||||
await websocket?.open?.(fromPartial(newSocket));
|
await websocket?.open?.(fromPartial(newSocket));
|
||||||
|
await websocket?.message?.(fromPartial(newSocket), createAgentMessage("agent.ready", { agentId: LOCAL_AGENT_ID }));
|
||||||
await websocket?.close?.(fromPartial(oldSocket), 1000, "replaced");
|
await websocket?.close?.(fromPartial(oldSocket), 1000, "replaced");
|
||||||
|
|
||||||
expect(onEvent).not.toHaveBeenCalledWith(
|
expect(onEvent).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({ type: "agent.disconnected", agentId: LOCAL_AGENT_ID }),
|
expect.objectContaining({ type: "agent.disconnected", agentId: LOCAL_AGENT_ID }),
|
||||||
);
|
);
|
||||||
|
expect(agentsServiceMocks.markAgentOffline).toHaveBeenCalledTimes(offlineCallsBeforeClose);
|
||||||
|
expect(
|
||||||
|
await Effect.runPromise(
|
||||||
|
runtime.sendBackup(LOCAL_AGENT_ID, {
|
||||||
|
jobId: "job-1",
|
||||||
|
scheduleId: "schedule-1",
|
||||||
|
organizationId: "org-1",
|
||||||
|
sourcePath: "/tmp/source",
|
||||||
|
repositoryConfig: { backend: "local" as const, path: "/tmp/repository" },
|
||||||
|
options: {},
|
||||||
|
runtime: {
|
||||||
|
password: "password",
|
||||||
|
cacheDir: "/tmp/cache",
|
||||||
|
passFile: "/tmp/pass",
|
||||||
|
defaultExcludes: [],
|
||||||
|
rcloneConfigFile: "/tmp/rclone.conf",
|
||||||
|
},
|
||||||
|
webhooks: { pre: null, post: null },
|
||||||
|
webhookAllowedOrigins: [],
|
||||||
|
webhookTimeoutMs: 60_000,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
await Effect.runPromise(runtime.stop);
|
await Effect.runPromise(runtime.stop);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ export function createAgentManagerRuntime(onEvent: (event: AgentManagerEvent) =>
|
||||||
const getSession = (agentId: string) => getSessionHandle(agentId)?.session;
|
const getSession = (agentId: string) => getSessionHandle(agentId)?.session;
|
||||||
|
|
||||||
const handleSessionEvent = (params: { agentId: string; agentName: string; sessionId: string }) => {
|
const handleSessionEvent = (params: { agentId: string; agentName: string; sessionId: string }) => {
|
||||||
const { agentId, agentName, sessionId } = params;
|
const { agentId, agentName } = params;
|
||||||
|
|
||||||
return (event: ControllerAgentSessionEvent) => {
|
return (event: ControllerAgentSessionEvent) => {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
|
|
@ -89,10 +89,6 @@ export function createAgentManagerRuntime(onEvent: (event: AgentManagerEvent) =>
|
||||||
return Effect.promise(() => agentsService.markAgentSeen(agentId, at));
|
return Effect.promise(() => agentsService.markAgentSeen(agentId, at));
|
||||||
}
|
}
|
||||||
case "agent.disconnected": {
|
case "agent.disconnected": {
|
||||||
if (getSession(agentId)?.connectionId !== sessionId) {
|
|
||||||
return Effect.void;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Effect.sync(() => onEvent({ type: "agent.disconnected", agentId, agentName }));
|
return Effect.sync(() => onEvent({ type: "agent.disconnected", agentId, agentName }));
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
|
@ -231,6 +227,9 @@ export function createAgentManagerRuntime(onEvent: (event: AgentManagerEvent) =>
|
||||||
websocket: {
|
websocket: {
|
||||||
open: async (ws) => {
|
open: async (ws) => {
|
||||||
await runWebSocketHandler(ws, "open", handleOpen(ws));
|
await runWebSocketHandler(ws, "open", handleOpen(ws));
|
||||||
|
if (getSession(ws.data.agentId)?.connectionId !== ws.data.id) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
message: async (ws, data) => {
|
message: async (ws, data) => {
|
||||||
await runWebSocketHandler(ws, "message", handleMessage(ws, data));
|
await runWebSocketHandler(ws, "message", handleMessage(ws, data));
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue