146 lines
3.6 KiB
TypeScript
146 lines
3.6 KiB
TypeScript
import { describe, expect, it } from "bun:test"
|
|
import { decideSpawnActions, findSpawnTarget, type SessionMapping } from "./decision-engine"
|
|
import type { CapacityConfig, WindowState } from "./types"
|
|
|
|
function createState(
|
|
windowWidth: number,
|
|
windowHeight: number,
|
|
agentPanes: WindowState["agentPanes"],
|
|
): WindowState {
|
|
return {
|
|
windowWidth,
|
|
windowHeight,
|
|
mainPane: {
|
|
paneId: "%0",
|
|
width: Math.floor(windowWidth / 2),
|
|
height: windowHeight,
|
|
left: 0,
|
|
top: 0,
|
|
title: "main",
|
|
isActive: true,
|
|
},
|
|
agentPanes,
|
|
}
|
|
}
|
|
|
|
describe("tmux layout-aware split behavior", () => {
|
|
it("uses -v for first spawn in main-horizontal layout", () => {
|
|
const config: CapacityConfig = {
|
|
layout: "main-horizontal",
|
|
mainPaneSize: 60,
|
|
mainPaneMinWidth: 120,
|
|
agentPaneWidth: 40,
|
|
}
|
|
const state = createState(220, 44, [])
|
|
|
|
const decision = decideSpawnActions(state, "ses-1", "agent", config, [])
|
|
|
|
expect(decision.canSpawn).toBe(true)
|
|
expect(decision.actions[0]).toMatchObject({
|
|
type: "spawn",
|
|
splitDirection: "-v",
|
|
})
|
|
})
|
|
|
|
it("uses -h for first spawn in main-vertical layout", () => {
|
|
const config: CapacityConfig = {
|
|
layout: "main-vertical",
|
|
mainPaneSize: 60,
|
|
mainPaneMinWidth: 120,
|
|
agentPaneWidth: 40,
|
|
}
|
|
const state = createState(220, 44, [])
|
|
|
|
const decision = decideSpawnActions(state, "ses-1", "agent", config, [])
|
|
|
|
expect(decision.canSpawn).toBe(true)
|
|
expect(decision.actions[0]).toMatchObject({
|
|
type: "spawn",
|
|
splitDirection: "-h",
|
|
})
|
|
})
|
|
|
|
it("prefers horizontal split target in main-horizontal layout", () => {
|
|
const config: CapacityConfig = {
|
|
layout: "main-horizontal",
|
|
mainPaneSize: 60,
|
|
mainPaneMinWidth: 120,
|
|
agentPaneWidth: 40,
|
|
}
|
|
const state = createState(260, 60, [
|
|
{
|
|
paneId: "%1",
|
|
width: 120,
|
|
height: 30,
|
|
left: 0,
|
|
top: 30,
|
|
title: "agent",
|
|
isActive: false,
|
|
},
|
|
])
|
|
|
|
const target = findSpawnTarget(state, config)
|
|
|
|
expect(target).toEqual({ targetPaneId: "%1", splitDirection: "-h" })
|
|
})
|
|
|
|
it("defers when strict main-horizontal cannot split", () => {
|
|
const config: CapacityConfig = {
|
|
layout: "main-horizontal",
|
|
mainPaneSize: 60,
|
|
mainPaneMinWidth: 120,
|
|
agentPaneWidth: 40,
|
|
}
|
|
const state = createState(220, 44, [
|
|
{
|
|
paneId: "%1",
|
|
width: 60,
|
|
height: 44,
|
|
left: 0,
|
|
top: 22,
|
|
title: "old",
|
|
isActive: false,
|
|
},
|
|
])
|
|
const mappings: SessionMapping[] = [
|
|
{ sessionId: "old-ses", paneId: "%1", createdAt: new Date("2024-01-01") },
|
|
]
|
|
|
|
const decision = decideSpawnActions(state, "new-ses", "agent", config, mappings)
|
|
|
|
expect(decision.canSpawn).toBe(false)
|
|
expect(decision.actions).toHaveLength(0)
|
|
expect(decision.reason).toContain("defer")
|
|
})
|
|
|
|
it("still spawns in narrow main-vertical when vertical split is possible", () => {
|
|
const config: CapacityConfig = {
|
|
layout: "main-vertical",
|
|
mainPaneSize: 60,
|
|
mainPaneMinWidth: 120,
|
|
agentPaneWidth: 40,
|
|
}
|
|
const state = createState(169, 40, [
|
|
{
|
|
paneId: "%1",
|
|
width: 48,
|
|
height: 40,
|
|
left: 121,
|
|
top: 0,
|
|
title: "agent",
|
|
isActive: false,
|
|
},
|
|
])
|
|
|
|
const decision = decideSpawnActions(state, "new-ses", "agent", config, [])
|
|
|
|
expect(decision.canSpawn).toBe(true)
|
|
expect(decision.actions).toHaveLength(1)
|
|
expect(decision.actions[0]).toMatchObject({
|
|
type: "spawn",
|
|
targetPaneId: "%1",
|
|
splitDirection: "-v",
|
|
})
|
|
})
|
|
})
|