100 lines
3.6 KiB
TypeScript
100 lines
3.6 KiB
TypeScript
import { describe, it, expect, beforeEach } from "vitest";
|
|
import { create } from "zustand";
|
|
import { immer } from "zustand/middleware/immer";
|
|
import { createSessionRuntimeSlice } from "./session-runtime-slice";
|
|
import type { SessionRuntimeSlice, UserShellInfo } from "./types";
|
|
|
|
function makeStore() {
|
|
return create<SessionRuntimeSlice>()(immer<SessionRuntimeSlice>(createSessionRuntimeSlice));
|
|
}
|
|
|
|
const ENV = "env-1";
|
|
function shell(id: string, overrides: Partial<UserShellInfo> = {}): UserShellInfo {
|
|
return {
|
|
terminalId: id,
|
|
kind: "ordinary",
|
|
seq: 1,
|
|
state: "open",
|
|
ptyStatus: "running",
|
|
customName: null,
|
|
displayName: "Terminal 1",
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("userShells reducers", () => {
|
|
let store: ReturnType<typeof makeStore>;
|
|
|
|
beforeEach(() => {
|
|
store = makeStore();
|
|
});
|
|
|
|
it("setUserShells populates the env list", () => {
|
|
store.getState().setUserShells(ENV, [shell("a"), shell("b", { seq: 2 })]);
|
|
const list = store.getState().userShells.byEnvironmentId[ENV];
|
|
expect(list).toHaveLength(2);
|
|
expect(list?.[0].terminalId).toBe("a");
|
|
expect(list?.[1].seq).toBe(2);
|
|
});
|
|
|
|
it("addUserShell appends idempotently", () => {
|
|
store.getState().addUserShell(ENV, shell("a"));
|
|
store.getState().addUserShell(ENV, shell("a"));
|
|
expect(store.getState().userShells.byEnvironmentId[ENV]).toHaveLength(1);
|
|
});
|
|
|
|
it("removeUserShell removes by terminalId", () => {
|
|
store.getState().setUserShells(ENV, [shell("a"), shell("b")]);
|
|
store.getState().removeUserShell(ENV, "a");
|
|
const list = store.getState().userShells.byEnvironmentId[ENV];
|
|
expect(list).toHaveLength(1);
|
|
expect(list?.[0].terminalId).toBe("b");
|
|
});
|
|
|
|
it("keeps an optimistically removed shell out of a deferred stale list response", async () => {
|
|
store.getState().setUserShells(ENV, [shell("a"), shell("b")]);
|
|
let resolveList!: (shells: UserShellInfo[]) => void;
|
|
const deferredList = new Promise<UserShellInfo[]>((resolve) => {
|
|
resolveList = resolve;
|
|
});
|
|
const applyList = deferredList.then((shells) => store.getState().setUserShells(ENV, shells));
|
|
|
|
store.getState().removeUserShell(ENV, "a");
|
|
resolveList([shell("a"), shell("b")]);
|
|
await applyList;
|
|
|
|
expect(
|
|
store.getState().userShells.byEnvironmentId[ENV]?.map((item) => item.terminalId),
|
|
).toEqual(["b"]);
|
|
});
|
|
|
|
it("keeps an optimistically removed shell out of stale add events", () => {
|
|
store.getState().setUserShells(ENV, [shell("a")]);
|
|
store.getState().removeUserShell(ENV, "a");
|
|
|
|
store.getState().addUserShell(ENV, shell("a"));
|
|
|
|
expect(store.getState().userShells.byEnvironmentId[ENV]).toEqual([]);
|
|
});
|
|
|
|
it("updateUserShell patches the matching row", () => {
|
|
store.getState().setUserShells(ENV, [shell("a", { customName: null })]);
|
|
store.getState().updateUserShell(ENV, "a", { customName: "build watcher" });
|
|
const list = store.getState().userShells.byEnvironmentId[ENV];
|
|
expect(list?.[0].customName).toBe("build watcher");
|
|
});
|
|
|
|
it("updateUserShell leaves other rows untouched", () => {
|
|
store.getState().setUserShells(ENV, [shell("a"), shell("b", { state: "open" })]);
|
|
store.getState().updateUserShell(ENV, "b", { state: "parked" });
|
|
const list = store.getState().userShells.byEnvironmentId[ENV];
|
|
expect(list?.[0].state).toBe("open");
|
|
expect(list?.[1].state).toBe("parked");
|
|
});
|
|
|
|
it("updateUserShell is a no-op for unknown environmentId", () => {
|
|
store.getState().updateUserShell("missing-env", "a", { state: "parked" });
|
|
expect(store.getState().userShells.byEnvironmentId["missing-env"]).toBeUndefined();
|
|
});
|
|
});
|