feat(locks): show a lock indicator on the lane card (D)

This commit is contained in:
2026-08-04 11:32:34 +07:00
parent 4a8725f136
commit 1f3a5ff51d
6 changed files with 186 additions and 91 deletions
@@ -24,6 +24,9 @@ vi.mock("../../../lib/api", () => ({
down: vi.fn(),
preflight: vi.fn().mockResolvedValue({ blocked: [], warnings: [] }),
},
locks: {
list: vi.fn(),
},
},
}));
@@ -35,6 +38,8 @@ beforeEach(() => {
vi.mocked(api.lanes.runtime).mockReset();
vi.mocked(api.lanes.runtime).mockResolvedValue({ available: false });
});
vi.mocked(api.locks.list).mockReset();
vi.mocked(api.locks.list).mockResolvedValue({ locks: [] });
function makeLane(overrides: Partial<Lane> = {}): Lane {
return {
@@ -335,3 +340,24 @@ describe("LaneCard — the lane's own application stack", () => {
expect(screen.getByText(/health check failed/)).toBeInTheDocument();
});
});
describe("LaneCard — named locks", () => {
it("shows a lock badge when this lane holds a named lock", async () => {
vi.mocked(api.locks.list).mockResolvedValue({
locks: [{ name: "build", holder: "lane1", since: 0, ageSec: 120 }],
});
render(<LaneCard lane={makeLane({ slot: 1 })} onAction={vi.fn()} />);
expect(await screen.findByTestId("lane-locks-1")).toBeInTheDocument();
expect(screen.getByText(/lock/i)).toBeInTheDocument();
});
it("shows no lock badge when locks belong to a different lane", async () => {
vi.mocked(api.locks.list).mockResolvedValue({
locks: [{ name: "build", holder: "lane99", since: 0, ageSec: 120 }],
});
render(<LaneCard lane={makeLane({ slot: 1 })} onAction={vi.fn()} />);
expect(screen.queryByTestId("lane-locks-1")).not.toBeInTheDocument();
});
});