feat: add per-provider timeout for usage panel to prevent blocking on slow responses

fix: update scheduler filesystem path from .kb to .fusion for task validation

style: clean up TaskDetailModal styling with reusable CSS classes

test: add comprehensive tests for file-service operations with mocked filesystem
This commit is contained in:
gsxdsm
2026-04-01 22:38:18 -07:00
parent 3ecc64a62d
commit 04b194982d
30 changed files with 1281 additions and 142 deletions

View File

@@ -77,6 +77,7 @@ function createMockStore(overrides: Record<string, any> = {}) {
parseFileScopeFromPrompt: vi.fn().mockResolvedValue([]),
getSettings: vi.fn().mockResolvedValue({ ...DEFAULT_SETTINGS }),
getRootDir: vi.fn().mockReturnValue("/tmp/root"),
getTasksDir: vi.fn().mockReturnValue("/tmp/root/.fusion/tasks"),
updateStep: vi.fn().mockImplementation(async (id: string, step: number, status: StepStatus) => {
return makeTaskDetail(id, "in-progress");
}),

View File

@@ -49,6 +49,7 @@ function createMockStore(overrides: Partial<TaskStore> = {}): TaskStore {
parseFileScopeFromPrompt: vi.fn().mockResolvedValue([]),
logEntry: vi.fn().mockResolvedValue(undefined),
getRootDir: vi.fn().mockReturnValue("/test/project"),
getTasksDir: vi.fn().mockReturnValue("/test/project/.fusion/tasks"),
on: vi.fn(),
off: vi.fn(),
...overrides,
@@ -276,7 +277,7 @@ describe("Scheduler", () => {
vi.mocked(existsSync).mockImplementation((path) => {
const value = String(path);
return value.includes(".kb/tasks/FN-010") || value.includes("PROMPT.md");
return value.includes(".fusion/tasks/FN-010") || value.includes("PROMPT.md");
});
vi.mocked(readFile).mockResolvedValue("# Prompt\n" as any);
@@ -301,6 +302,7 @@ describe("Scheduler", () => {
getSettings: vi.fn().mockResolvedValue({ maxConcurrent: 2, maxWorktrees: 4 }),
updateTask: vi.fn().mockResolvedValue(undefined),
getRootDir: vi.fn().mockReturnValue("/test/project"),
getTasksDir: vi.fn().mockReturnValue("/test/project/.fusion/tasks"),
});
// Set up mocks directly on the store
@@ -341,6 +343,7 @@ describe("Scheduler", () => {
getSettings: vi.fn().mockResolvedValue({ maxConcurrent: 2, maxWorktrees: 4 }),
updateTask: vi.fn().mockResolvedValue(undefined),
getRootDir: vi.fn().mockReturnValue("/test/project"),
getTasksDir: vi.fn().mockReturnValue("/test/project/.fusion/tasks"),
});
const moveTask = vi.fn().mockResolvedValue(undefined);
@@ -381,6 +384,7 @@ describe("Scheduler", () => {
getSettings: vi.fn().mockResolvedValue({ maxConcurrent: 2, maxWorktrees: 4 }),
updateTask: vi.fn().mockResolvedValue(undefined),
getRootDir: vi.fn().mockReturnValue("/test/project"),
getTasksDir: vi.fn().mockReturnValue("/test/project/.fusion/tasks"),
});
const moveTask = vi.fn().mockResolvedValue(undefined);
@@ -418,6 +422,7 @@ describe("Scheduler", () => {
getSettings: vi.fn().mockResolvedValue({ maxConcurrent: 2, maxWorktrees: 4 }),
updateTask: vi.fn().mockResolvedValue(undefined),
getRootDir: vi.fn().mockReturnValue("/test/project"),
getTasksDir: vi.fn().mockReturnValue("/test/project/.fusion/tasks"),
});
const moveTask = vi.fn().mockResolvedValue(undefined);
@@ -459,6 +464,7 @@ describe("Scheduler", () => {
getSettings: vi.fn().mockResolvedValue({ maxConcurrent: 2, maxWorktrees: 4 }),
updateTask: vi.fn().mockResolvedValue(undefined),
getRootDir: vi.fn().mockReturnValue("/test/project"),
getTasksDir: vi.fn().mockReturnValue("/test/project/.fusion/tasks"),
});
const moveTask = vi.fn().mockResolvedValue(undefined);

View File

@@ -184,7 +184,7 @@ export class Scheduler {
* @returns Object with `valid: true` if checks pass, or `valid: false` with a `reason` string if they fail
*/
private async validateTaskFilesystem(id: string): Promise<{ valid: boolean; reason?: string }> {
const taskDir = join(this.store.getRootDir(), ".kb", "tasks", id);
const taskDir = join(this.store.getTasksDir(), id);
// Check if task directory exists
if (!existsSync(taskDir)) {