fix(#338): Add session cleanup on terminal states
- Add removeSession and scheduleSessionCleanup methods to AgentSpawnerService - Schedule session cleanup after completed/failed/killed transitions - Default 30 second delay before cleanup to allow status queries - Implement OnModuleDestroy to clean up pending timers - Add forwardRef injection to avoid circular dependency - Add comprehensive tests for cleanup functionality Refs #338
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
||||
import { AgentLifecycleService } from "./agent-lifecycle.service";
|
||||
import { AgentSpawnerService } from "./agent-spawner.service";
|
||||
import { ValkeyService } from "../valkey/valkey.service";
|
||||
import type { AgentState } from "../valkey/types";
|
||||
|
||||
@@ -12,6 +13,9 @@ describe("AgentLifecycleService", () => {
|
||||
publishEvent: ReturnType<typeof vi.fn>;
|
||||
listAgents: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
let mockSpawnerService: {
|
||||
scheduleSessionCleanup: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
|
||||
const mockAgentId = "test-agent-123";
|
||||
const mockTaskId = "test-task-456";
|
||||
@@ -26,8 +30,15 @@ describe("AgentLifecycleService", () => {
|
||||
listAgents: vi.fn(),
|
||||
};
|
||||
|
||||
// Create service with mock
|
||||
service = new AgentLifecycleService(mockValkeyService as unknown as ValkeyService);
|
||||
mockSpawnerService = {
|
||||
scheduleSessionCleanup: vi.fn(),
|
||||
};
|
||||
|
||||
// Create service with mocks
|
||||
service = new AgentLifecycleService(
|
||||
mockValkeyService as unknown as ValkeyService,
|
||||
mockSpawnerService as unknown as AgentSpawnerService
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -612,4 +623,87 @@ describe("AgentLifecycleService", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("session cleanup on terminal states", () => {
|
||||
it("should schedule session cleanup when transitioning to completed", async () => {
|
||||
const mockState: AgentState = {
|
||||
agentId: mockAgentId,
|
||||
status: "running",
|
||||
taskId: mockTaskId,
|
||||
startedAt: "2026-02-02T10:00:00Z",
|
||||
};
|
||||
|
||||
mockValkeyService.getAgentState.mockResolvedValue(mockState);
|
||||
mockValkeyService.updateAgentStatus.mockResolvedValue({
|
||||
...mockState,
|
||||
status: "completed",
|
||||
completedAt: "2026-02-02T11:00:00Z",
|
||||
});
|
||||
|
||||
await service.transitionToCompleted(mockAgentId);
|
||||
|
||||
expect(mockSpawnerService.scheduleSessionCleanup).toHaveBeenCalledWith(mockAgentId);
|
||||
});
|
||||
|
||||
it("should schedule session cleanup when transitioning to failed", async () => {
|
||||
const mockState: AgentState = {
|
||||
agentId: mockAgentId,
|
||||
status: "running",
|
||||
taskId: mockTaskId,
|
||||
startedAt: "2026-02-02T10:00:00Z",
|
||||
};
|
||||
const errorMessage = "Runtime error occurred";
|
||||
|
||||
mockValkeyService.getAgentState.mockResolvedValue(mockState);
|
||||
mockValkeyService.updateAgentStatus.mockResolvedValue({
|
||||
...mockState,
|
||||
status: "failed",
|
||||
error: errorMessage,
|
||||
completedAt: "2026-02-02T11:00:00Z",
|
||||
});
|
||||
|
||||
await service.transitionToFailed(mockAgentId, errorMessage);
|
||||
|
||||
expect(mockSpawnerService.scheduleSessionCleanup).toHaveBeenCalledWith(mockAgentId);
|
||||
});
|
||||
|
||||
it("should schedule session cleanup when transitioning to killed", async () => {
|
||||
const mockState: AgentState = {
|
||||
agentId: mockAgentId,
|
||||
status: "running",
|
||||
taskId: mockTaskId,
|
||||
startedAt: "2026-02-02T10:00:00Z",
|
||||
};
|
||||
|
||||
mockValkeyService.getAgentState.mockResolvedValue(mockState);
|
||||
mockValkeyService.updateAgentStatus.mockResolvedValue({
|
||||
...mockState,
|
||||
status: "killed",
|
||||
completedAt: "2026-02-02T11:00:00Z",
|
||||
});
|
||||
|
||||
await service.transitionToKilled(mockAgentId);
|
||||
|
||||
expect(mockSpawnerService.scheduleSessionCleanup).toHaveBeenCalledWith(mockAgentId);
|
||||
});
|
||||
|
||||
it("should not schedule session cleanup when transitioning to running", async () => {
|
||||
const mockState: AgentState = {
|
||||
agentId: mockAgentId,
|
||||
status: "spawning",
|
||||
taskId: mockTaskId,
|
||||
};
|
||||
|
||||
mockValkeyService.getAgentState.mockResolvedValue(mockState);
|
||||
mockValkeyService.updateAgentStatus.mockResolvedValue({
|
||||
...mockState,
|
||||
status: "running",
|
||||
startedAt: "2026-02-02T10:00:00Z",
|
||||
});
|
||||
|
||||
await service.transitionToRunning(mockAgentId);
|
||||
|
||||
expect(mockSpawnerService.scheduleSessionCleanup).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user