fix full-suite isolation regressions

This commit is contained in:
YeonGyu-Kim
2026-05-07 17:47:53 +09:00
parent 102b5f96e7
commit ee938aa097
62 changed files with 1238 additions and 899 deletions
@@ -10,6 +10,18 @@ import { listActiveTeams, loadRuntimeState, saveRuntimeState, transitionRuntimeS
import type { RuntimeState } from "../types"
import { DELETABLE_MEMBER_STATUSES, removeWorktrees } from "./shutdown-helpers"
export type DeleteTeamDeps = {
canVisualize: typeof canVisualize
removeTeamLayout: typeof removeTeamLayout
log: typeof log
}
const defaultDeleteTeamDeps: DeleteTeamDeps = {
canVisualize,
removeTeamLayout,
log,
}
const DELETABLE_TEAM_STATUSES = new Set<RuntimeState["status"]>([
"active",
"shutdown_requested",
@@ -37,6 +49,7 @@ export async function deleteTeam(
tmuxMgr?: TmuxSessionManager,
bgMgr?: BackgroundManager,
options?: { force?: boolean },
deps: DeleteTeamDeps = defaultDeleteTeamDeps,
): Promise<{ removedWorktrees: string[]; removedLayout: boolean }> {
const runtimeState = await loadRuntimeState(teamRunId, config)
const nonLeadMembers = runtimeState.members.filter((member) => member.agentType !== "leader")
@@ -86,7 +99,7 @@ export async function deleteTeam(
}
}
const removedLayout = config.tmux_visualization && tmuxMgr !== undefined && canVisualize()
const removedLayout = config.tmux_visualization && tmuxMgr !== undefined && deps.canVisualize()
if (removedLayout) {
const memberPaneIds = runtimeState.members
.filter((member) => member.agentType !== "leader" && member.tmuxPaneId)
@@ -101,15 +114,15 @@ export async function deleteTeam(
if (options?.force === true) {
try {
await removeTeamLayout(teamRunId, cleanupTarget, tmuxMgr)
await deps.removeTeamLayout(teamRunId, cleanupTarget, tmuxMgr)
} catch (error) {
log("team delete layout cleanup failed", {
deps.log("team delete layout cleanup failed", {
teamRunId,
error: error instanceof Error ? error.message : String(error),
})
}
} else {
await removeTeamLayout(teamRunId, cleanupTarget, tmuxMgr)
await deps.removeTeamLayout(teamRunId, cleanupTarget, tmuxMgr)
}
}
@@ -6,10 +6,9 @@ import path from "node:path"
import { sendMessage } from "../team-mailbox/send"
import { getRuntimeStateDir, resolveBaseDir } from "../team-registry/paths"
import * as logger from "../../../shared/logger"
import * as layoutModule from "../team-layout-tmux/layout"
import * as runtimeStateStore from "../team-state-store/store"
import { loadRuntimeState, transitionRuntimeState } from "../team-state-store/store"
import type { DeleteTeamDeps } from "./delete-team"
import {
createFixture,
createTestMessage,
@@ -291,9 +290,12 @@ describe("team-runtime shutdown", () => {
transitionedStatuses.push(transition(currentRuntimeState).status)
return await originalTransitionRuntimeState(teamRunId, transition, config)
})
spyOn(layoutModule, "canVisualize").mockReturnValue(true)
spyOn(layoutModule, "removeTeamLayout").mockRejectedValue(new Error("layout failed"))
const logSpy = spyOn(logger, "log").mockImplementation(() => {})
const logMock = mock(() => {})
const deps = {
canVisualize: () => true,
removeTeamLayout: async () => { throw new Error("layout failed") },
log: logMock,
} satisfies DeleteTeamDeps
await updateMemberStatuses(fixture.teamRunId, fixture.config, {
"member-a": "running",
"member-b": "idle",
@@ -309,12 +311,13 @@ describe("team-runtime shutdown", () => {
{ getServerUrl: () => "http://localhost" } as never,
undefined,
{ force: true },
deps,
)
// then
expect(result.removedLayout).toBe(true)
expect(transitionedStatuses).toContain("deleted")
expect(logSpy).toHaveBeenCalledWith("team delete layout cleanup failed", {
expect(logMock).toHaveBeenCalledWith("team delete layout cleanup failed", {
teamRunId: fixture.teamRunId,
error: "layout failed",
})
@@ -329,8 +332,12 @@ describe("team-runtime shutdown", () => {
// given
const fixture = await createFixture()
temporaryDirectories.push(fixture.baseDir)
spyOn(layoutModule, "canVisualize").mockReturnValue(true)
const removeLayoutSpy = spyOn(layoutModule, "removeTeamLayout").mockResolvedValue(undefined)
const removeLayoutMock = mock(async () => {})
const deps = {
canVisualize: () => true,
removeTeamLayout: removeLayoutMock,
log: () => {},
} satisfies DeleteTeamDeps
await updateMemberStatuses(fixture.teamRunId, fixture.config, {
"member-a": "shutdown_approved",
"member-b": "completed",
@@ -341,11 +348,14 @@ describe("team-runtime shutdown", () => {
fixture.teamRunId,
{ ...fixture.config, tmux_visualization: false },
{ getServerUrl: () => "http://localhost" } as never,
undefined,
undefined,
deps,
)
// then
expect(result.removedLayout).toBe(false)
expect(removeLayoutSpy).not.toHaveBeenCalled()
expect(removeLayoutMock).not.toHaveBeenCalled()
})
test("cancels team background tasks before deleting when force=true", async () => {