afc2276a12
- Extracted OpenAI compatibility detection and resolution logic into dedicated `openai-completions-compat` module. - Refactored `detectCompat()` and `getCompat()` to delegate to new compat module functions with simplified conditional logic. - Fixed OAuth redirect URI validation to preserve exact configured values without trailing slash normalization. - Improved session deletion to return boolean status and display error messages in UI instead of silently failing. - Added `/session delete` command with Delete key support and confirmation dialogs for session management.
101 lines
2.9 KiB
TypeScript
101 lines
2.9 KiB
TypeScript
import { afterEach, describe, expect, it, mock, vi } from "bun:test";
|
|
import type { SessionInfo } from "../../../src/session/session-manager";
|
|
|
|
const themeModulePath = new URL("../../../src/modes/theme/theme.ts", import.meta.url).pathname;
|
|
|
|
mock.module(themeModulePath, () => ({
|
|
theme: {
|
|
fg: (_tone: string, text: string) => text,
|
|
bold: (text: string) => text,
|
|
nav: { cursor: ">" },
|
|
sep: { dot: "·" },
|
|
boxSharp: { horizontal: "-", vertical: "|" },
|
|
},
|
|
}));
|
|
|
|
import { SessionSelectorComponent } from "../../../src/modes/components/session-selector";
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
function createSession(id: string, title: string): SessionInfo {
|
|
return {
|
|
path: `/tmp/${id}.jsonl`,
|
|
id,
|
|
cwd: "/tmp",
|
|
title,
|
|
created: new Date("2024-01-01T00:00:00Z"),
|
|
modified: new Date("2024-01-02T00:00:00Z"),
|
|
messageCount: 1,
|
|
firstMessage: `${title} first message`,
|
|
allMessagesText: `${title} first message`,
|
|
};
|
|
}
|
|
|
|
function createSelector(onDelete: (session: SessionInfo) => Promise<boolean>): SessionSelectorComponent {
|
|
return new SessionSelectorComponent(
|
|
[createSession("session-a", "Alpha"), createSession("session-b", "Beta")],
|
|
() => {},
|
|
() => {},
|
|
() => {},
|
|
onDelete,
|
|
);
|
|
}
|
|
|
|
function renderText(selector: SessionSelectorComponent): string {
|
|
return selector.render(120).join("\n");
|
|
}
|
|
|
|
describe("SessionSelectorComponent delete confirmation", () => {
|
|
it("keeps the session visible and shows the error when delete fails after confirmation", async () => {
|
|
const onDelete = vi.fn(async () => {
|
|
throw new Error("disk failed");
|
|
});
|
|
const selector = createSelector(onDelete);
|
|
|
|
selector.handleInput("\x1b[3~");
|
|
expect(renderText(selector)).toContain("Delete session?");
|
|
expect(renderText(selector)).toContain("Alpha");
|
|
|
|
selector.handleInput("\n");
|
|
await Bun.sleep(0);
|
|
|
|
const rendered = renderText(selector);
|
|
expect(onDelete).toHaveBeenCalledTimes(1);
|
|
expect(rendered).toContain("Error: disk failed");
|
|
expect(rendered).toContain("Alpha");
|
|
expect(rendered).toContain("Beta");
|
|
expect(rendered).not.toContain("Delete session?");
|
|
});
|
|
|
|
it("keeps the session visible when delete is canceled upstream", async () => {
|
|
const onDelete = vi.fn(async () => false);
|
|
const selector = createSelector(onDelete);
|
|
|
|
selector.handleInput("\x1b[3~");
|
|
selector.handleInput("\n");
|
|
await Bun.sleep(0);
|
|
|
|
const rendered = renderText(selector);
|
|
expect(onDelete).toHaveBeenCalledTimes(1);
|
|
expect(rendered).toContain("Alpha");
|
|
expect(rendered).toContain("Beta");
|
|
expect(rendered).not.toContain("Error:");
|
|
});
|
|
|
|
it("removes the session row after a successful delete", async () => {
|
|
const onDelete = vi.fn(async () => true);
|
|
const selector = createSelector(onDelete);
|
|
|
|
selector.handleInput("\x1b[3~");
|
|
selector.handleInput("\n");
|
|
await Bun.sleep(0);
|
|
|
|
const rendered = renderText(selector);
|
|
expect(onDelete).toHaveBeenCalledTimes(1);
|
|
expect(rendered).not.toContain("Alpha");
|
|
expect(rendered).toContain("Beta");
|
|
});
|
|
});
|