blamouche_obsidian-any-ai-code/tests/session-utils.test.ts
blamouche 073efef75c Run multiple runtimes in parallel via session tabs
Refactor the sidebar panel from a single-process view into a tabbed
multi-session view: each tab is an independent CliSession with its own
PTY process and xterm terminal. A New session / + control opens a session
from any configured runtime; multiple sessions of the same runtime are
allowed and labels are disambiguated. Stop/Restart/Clear and the @file/
@folder buttons act on the active tab; a single ResizeObserver refits only
the visible terminal and re-fits on tab activation.

Automations now spawn their own session tab for the declared runtime (or
the default when none is declared), await CLI readiness, then send the
prompt — replacing the old skip-if-not-running/runtime-mismatch logic.
Add settings autoCloseAutomationSessions and maxConcurrentSessions; the
Automations modal "Run now" button is always enabled.

Extract pure helpers to session-utils.ts (resolveRuntimeForAutomation,
runtimeMatches, nextSessionLabel, canOpenSession) with unit tests. Update
README and the generated example automation. Bump to 0.2.9 (manifest,
versions.json, package.json, prompt-hub version).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:12:26 +02:00

86 lines
2.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
canOpenSession,
nextSessionLabel,
resolveRuntimeForAutomation,
runtimeMatches
} from "../session-utils";
import type { CliRuntimeConfig } from "../runtime-utils";
const runtimes: CliRuntimeConfig[] = [
{ id: "claude", name: "Claude", command: "claude" },
{ id: "codex", name: "Codex", command: "codex" },
{ id: "abc123", name: "My Gemini", command: "gemini" }
];
describe("runtimeMatches", () => {
it("matches by id", () => {
expect(runtimeMatches(runtimes[0], "claude")).toBe(true);
});
it("matches by name case-insensitively", () => {
expect(runtimeMatches(runtimes[2], "my gemini")).toBe(true);
});
it("returns false on no match or empty target", () => {
expect(runtimeMatches(runtimes[0], "codex")).toBe(false);
expect(runtimeMatches(runtimes[0], " ")).toBe(false);
});
});
describe("resolveRuntimeForAutomation", () => {
it("resolves a declared runtime by name", () => {
expect(resolveRuntimeForAutomation(runtimes, "Codex", "claude")?.id).toBe("codex");
});
it("resolves a declared runtime by id", () => {
expect(resolveRuntimeForAutomation(runtimes, "abc123", "claude")?.id).toBe("abc123");
});
it("returns null when a declared runtime is unknown", () => {
expect(resolveRuntimeForAutomation(runtimes, "Nope", "claude")).toBeNull();
});
it("falls back to the default runtime when none is declared", () => {
expect(resolveRuntimeForAutomation(runtimes, null, "codex")?.id).toBe("codex");
expect(resolveRuntimeForAutomation(runtimes, "", "abc123")?.id).toBe("abc123");
});
it("falls back to the first runtime when the default id is invalid", () => {
expect(resolveRuntimeForAutomation(runtimes, null, "missing")?.id).toBe("claude");
});
it("returns null when there are no runtimes", () => {
expect(resolveRuntimeForAutomation([], null, "claude")).toBeNull();
});
});
describe("nextSessionLabel", () => {
it("uses the runtime name when free", () => {
expect(nextSessionLabel([], "Claude")).toBe("Claude");
});
it("disambiguates duplicates", () => {
expect(nextSessionLabel(["Claude"], "Claude")).toBe("Claude (2)");
expect(nextSessionLabel(["Claude", "Claude (2)"], "Claude")).toBe("Claude (3)");
});
it("falls back to (Unnamed) for blank names", () => {
expect(nextSessionLabel([], " ")).toBe("(Unnamed)");
});
});
describe("canOpenSession", () => {
it("allows when under the cap", () => {
expect(canOpenSession(2, 8)).toBe(true);
});
it("blocks at the cap", () => {
expect(canOpenSession(8, 8)).toBe(false);
});
it("treats 0 or negative as unlimited", () => {
expect(canOpenSession(100, 0)).toBe(true);
expect(canOpenSession(100, -1)).toBe(true);
});
});