mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 08:33:36 +00:00
Merge main into massive-terminal-upgrade
Resolves merge conflicts: - apps/server/src/routes/terminal/common.ts: Keep randomBytes import, use @automaker/utils for createLogger - apps/ui/eslint.config.mjs: Use main's explicit globals list with XMLHttpRequest and MediaQueryListEvent additions - apps/ui/src/components/views/terminal-view.tsx: Keep our terminal improvements (killAllSessions, beforeunload, better error handling) - apps/ui/src/config/terminal-themes.ts: Keep our search highlight colors for all themes - apps/ui/src/store/app-store.ts: Keep our terminal settings persistence improvements (merge function) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,66 +1,66 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { FeatureLoader } from "@/services/feature-loader.js";
|
||||
import * as fs from "fs/promises";
|
||||
import path from "path";
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { FeatureLoader } from '@/services/feature-loader.js';
|
||||
import * as fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
vi.mock("fs/promises");
|
||||
vi.mock('fs/promises');
|
||||
|
||||
describe("feature-loader.ts", () => {
|
||||
describe('feature-loader.ts', () => {
|
||||
let loader: FeatureLoader;
|
||||
const testProjectPath = "/test/project";
|
||||
const testProjectPath = '/test/project';
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
loader = new FeatureLoader();
|
||||
});
|
||||
|
||||
describe("getFeaturesDir", () => {
|
||||
it("should return features directory path", () => {
|
||||
describe('getFeaturesDir', () => {
|
||||
it('should return features directory path', () => {
|
||||
const result = loader.getFeaturesDir(testProjectPath);
|
||||
expect(result).toContain("test");
|
||||
expect(result).toContain("project");
|
||||
expect(result).toContain(".automaker");
|
||||
expect(result).toContain("features");
|
||||
expect(result).toContain('test');
|
||||
expect(result).toContain('project');
|
||||
expect(result).toContain('.automaker');
|
||||
expect(result).toContain('features');
|
||||
});
|
||||
});
|
||||
|
||||
describe("getFeatureImagesDir", () => {
|
||||
it("should return feature images directory path", () => {
|
||||
const result = loader.getFeatureImagesDir(testProjectPath, "feature-123");
|
||||
expect(result).toContain("features");
|
||||
expect(result).toContain("feature-123");
|
||||
expect(result).toContain("images");
|
||||
describe('getFeatureImagesDir', () => {
|
||||
it('should return feature images directory path', () => {
|
||||
const result = loader.getFeatureImagesDir(testProjectPath, 'feature-123');
|
||||
expect(result).toContain('features');
|
||||
expect(result).toContain('feature-123');
|
||||
expect(result).toContain('images');
|
||||
});
|
||||
});
|
||||
|
||||
describe("getFeatureDir", () => {
|
||||
it("should return feature directory path", () => {
|
||||
const result = loader.getFeatureDir(testProjectPath, "feature-123");
|
||||
expect(result).toContain("features");
|
||||
expect(result).toContain("feature-123");
|
||||
describe('getFeatureDir', () => {
|
||||
it('should return feature directory path', () => {
|
||||
const result = loader.getFeatureDir(testProjectPath, 'feature-123');
|
||||
expect(result).toContain('features');
|
||||
expect(result).toContain('feature-123');
|
||||
});
|
||||
});
|
||||
|
||||
describe("getFeatureJsonPath", () => {
|
||||
it("should return feature.json path", () => {
|
||||
const result = loader.getFeatureJsonPath(testProjectPath, "feature-123");
|
||||
expect(result).toContain("features");
|
||||
expect(result).toContain("feature-123");
|
||||
expect(result).toContain("feature.json");
|
||||
describe('getFeatureJsonPath', () => {
|
||||
it('should return feature.json path', () => {
|
||||
const result = loader.getFeatureJsonPath(testProjectPath, 'feature-123');
|
||||
expect(result).toContain('features');
|
||||
expect(result).toContain('feature-123');
|
||||
expect(result).toContain('feature.json');
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAgentOutputPath", () => {
|
||||
it("should return agent-output.md path", () => {
|
||||
const result = loader.getAgentOutputPath(testProjectPath, "feature-123");
|
||||
expect(result).toContain("features");
|
||||
expect(result).toContain("feature-123");
|
||||
expect(result).toContain("agent-output.md");
|
||||
describe('getAgentOutputPath', () => {
|
||||
it('should return agent-output.md path', () => {
|
||||
const result = loader.getAgentOutputPath(testProjectPath, 'feature-123');
|
||||
expect(result).toContain('features');
|
||||
expect(result).toContain('feature-123');
|
||||
expect(result).toContain('agent-output.md');
|
||||
});
|
||||
});
|
||||
|
||||
describe("generateFeatureId", () => {
|
||||
it("should generate unique feature ID with timestamp", () => {
|
||||
describe('generateFeatureId', () => {
|
||||
it('should generate unique feature ID with timestamp', () => {
|
||||
const id1 = loader.generateFeatureId();
|
||||
const id2 = loader.generateFeatureId();
|
||||
|
||||
@@ -75,372 +75,371 @@ describe("feature-loader.ts", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAll", () => {
|
||||
describe('getAll', () => {
|
||||
it("should return empty array when features directory doesn't exist", async () => {
|
||||
vi.mocked(fs.access).mockRejectedValue(new Error("ENOENT"));
|
||||
vi.mocked(fs.access).mockRejectedValue(new Error('ENOENT'));
|
||||
|
||||
const result = await loader.getAll(testProjectPath);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("should load all features from feature directories", async () => {
|
||||
it('should load all features from feature directories', async () => {
|
||||
vi.mocked(fs.access).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.readdir).mockResolvedValue([
|
||||
{ name: "feature-1", isDirectory: () => true } as any,
|
||||
{ name: "feature-2", isDirectory: () => true } as any,
|
||||
{ name: "file.txt", isDirectory: () => false } as any,
|
||||
{ name: 'feature-1', isDirectory: () => true } as any,
|
||||
{ name: 'feature-2', isDirectory: () => true } as any,
|
||||
{ name: 'file.txt', isDirectory: () => false } as any,
|
||||
]);
|
||||
|
||||
vi.mocked(fs.readFile)
|
||||
.mockResolvedValueOnce(
|
||||
JSON.stringify({
|
||||
id: "feature-1",
|
||||
category: "ui",
|
||||
description: "Feature 1",
|
||||
id: 'feature-1',
|
||||
category: 'ui',
|
||||
description: 'Feature 1',
|
||||
})
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
JSON.stringify({
|
||||
id: "feature-2",
|
||||
category: "backend",
|
||||
description: "Feature 2",
|
||||
id: 'feature-2',
|
||||
category: 'backend',
|
||||
description: 'Feature 2',
|
||||
})
|
||||
);
|
||||
|
||||
const result = await loader.getAll(testProjectPath);
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].id).toBe("feature-1");
|
||||
expect(result[1].id).toBe("feature-2");
|
||||
expect(result[0].id).toBe('feature-1');
|
||||
expect(result[1].id).toBe('feature-2');
|
||||
});
|
||||
|
||||
it("should skip features without id field", async () => {
|
||||
it('should skip features without id field', async () => {
|
||||
vi.mocked(fs.access).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.readdir).mockResolvedValue([
|
||||
{ name: "feature-1", isDirectory: () => true } as any,
|
||||
{ name: "feature-2", isDirectory: () => true } as any,
|
||||
{ name: 'feature-1', isDirectory: () => true } as any,
|
||||
{ name: 'feature-2', isDirectory: () => true } as any,
|
||||
]);
|
||||
|
||||
const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
|
||||
vi.mocked(fs.readFile)
|
||||
.mockResolvedValueOnce(
|
||||
JSON.stringify({
|
||||
category: "ui",
|
||||
description: "Missing ID",
|
||||
category: 'ui',
|
||||
description: 'Missing ID',
|
||||
})
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
JSON.stringify({
|
||||
id: "feature-2",
|
||||
category: "backend",
|
||||
description: "Feature 2",
|
||||
id: 'feature-2',
|
||||
category: 'backend',
|
||||
description: 'Feature 2',
|
||||
})
|
||||
);
|
||||
|
||||
const result = await loader.getAll(testProjectPath);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe("feature-2");
|
||||
expect(result[0].id).toBe('feature-2');
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
'[FeatureLoader]',
|
||||
expect.stringContaining("missing required 'id' field")
|
||||
);
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("should skip features with missing feature.json", async () => {
|
||||
it('should skip features with missing feature.json', async () => {
|
||||
vi.mocked(fs.access).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.readdir).mockResolvedValue([
|
||||
{ name: "feature-1", isDirectory: () => true } as any,
|
||||
{ name: "feature-2", isDirectory: () => true } as any,
|
||||
{ name: 'feature-1', isDirectory: () => true } as any,
|
||||
{ name: 'feature-2', isDirectory: () => true } as any,
|
||||
]);
|
||||
|
||||
const error: any = new Error("File not found");
|
||||
error.code = "ENOENT";
|
||||
const error: any = new Error('File not found');
|
||||
error.code = 'ENOENT';
|
||||
|
||||
vi.mocked(fs.readFile)
|
||||
.mockRejectedValueOnce(error)
|
||||
.mockResolvedValueOnce(
|
||||
JSON.stringify({
|
||||
id: "feature-2",
|
||||
category: "backend",
|
||||
description: "Feature 2",
|
||||
id: 'feature-2',
|
||||
category: 'backend',
|
||||
description: 'Feature 2',
|
||||
})
|
||||
);
|
||||
|
||||
const result = await loader.getAll(testProjectPath);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe("feature-2");
|
||||
expect(result[0].id).toBe('feature-2');
|
||||
});
|
||||
|
||||
it("should handle malformed JSON gracefully", async () => {
|
||||
it('should handle malformed JSON gracefully', async () => {
|
||||
vi.mocked(fs.access).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.readdir).mockResolvedValue([
|
||||
{ name: "feature-1", isDirectory: () => true } as any,
|
||||
{ name: 'feature-1', isDirectory: () => true } as any,
|
||||
]);
|
||||
|
||||
const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
|
||||
vi.mocked(fs.readFile).mockResolvedValue("invalid json{");
|
||||
vi.mocked(fs.readFile).mockResolvedValue('invalid json{');
|
||||
|
||||
const result = await loader.getAll(testProjectPath);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect(consoleSpy).toHaveBeenCalled();
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
'[FeatureLoader]',
|
||||
expect.stringContaining('Failed to parse feature.json')
|
||||
);
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("should sort features by creation order (timestamp)", async () => {
|
||||
it('should sort features by creation order (timestamp)', async () => {
|
||||
vi.mocked(fs.access).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.readdir).mockResolvedValue([
|
||||
{ name: "feature-3", isDirectory: () => true } as any,
|
||||
{ name: "feature-1", isDirectory: () => true } as any,
|
||||
{ name: "feature-2", isDirectory: () => true } as any,
|
||||
{ name: 'feature-3', isDirectory: () => true } as any,
|
||||
{ name: 'feature-1', isDirectory: () => true } as any,
|
||||
{ name: 'feature-2', isDirectory: () => true } as any,
|
||||
]);
|
||||
|
||||
vi.mocked(fs.readFile)
|
||||
.mockResolvedValueOnce(
|
||||
JSON.stringify({
|
||||
id: "feature-3000-xyz",
|
||||
category: "ui",
|
||||
id: 'feature-3000-xyz',
|
||||
category: 'ui',
|
||||
})
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
JSON.stringify({
|
||||
id: "feature-1000-abc",
|
||||
category: "ui",
|
||||
id: 'feature-1000-abc',
|
||||
category: 'ui',
|
||||
})
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
JSON.stringify({
|
||||
id: "feature-2000-def",
|
||||
category: "ui",
|
||||
id: 'feature-2000-def',
|
||||
category: 'ui',
|
||||
})
|
||||
);
|
||||
|
||||
const result = await loader.getAll(testProjectPath);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result[0].id).toBe("feature-1000-abc");
|
||||
expect(result[1].id).toBe("feature-2000-def");
|
||||
expect(result[2].id).toBe("feature-3000-xyz");
|
||||
expect(result[0].id).toBe('feature-1000-abc');
|
||||
expect(result[1].id).toBe('feature-2000-def');
|
||||
expect(result[2].id).toBe('feature-3000-xyz');
|
||||
});
|
||||
});
|
||||
|
||||
describe("get", () => {
|
||||
it("should return feature by ID", async () => {
|
||||
describe('get', () => {
|
||||
it('should return feature by ID', async () => {
|
||||
const featureData = {
|
||||
id: "feature-123",
|
||||
category: "ui",
|
||||
description: "Test feature",
|
||||
id: 'feature-123',
|
||||
category: 'ui',
|
||||
description: 'Test feature',
|
||||
};
|
||||
|
||||
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(featureData));
|
||||
|
||||
const result = await loader.get(testProjectPath, "feature-123");
|
||||
const result = await loader.get(testProjectPath, 'feature-123');
|
||||
|
||||
expect(result).toEqual(featureData);
|
||||
});
|
||||
|
||||
it("should return null when feature doesn't exist", async () => {
|
||||
const error: any = new Error("File not found");
|
||||
error.code = "ENOENT";
|
||||
const error: any = new Error('File not found');
|
||||
error.code = 'ENOENT';
|
||||
vi.mocked(fs.readFile).mockRejectedValue(error);
|
||||
|
||||
const result = await loader.get(testProjectPath, "feature-123");
|
||||
const result = await loader.get(testProjectPath, 'feature-123');
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("should throw on other errors", async () => {
|
||||
vi.mocked(fs.readFile).mockRejectedValue(new Error("Permission denied"));
|
||||
it('should throw on other errors', async () => {
|
||||
vi.mocked(fs.readFile).mockRejectedValue(new Error('Permission denied'));
|
||||
|
||||
await expect(
|
||||
loader.get(testProjectPath, "feature-123")
|
||||
).rejects.toThrow("Permission denied");
|
||||
await expect(loader.get(testProjectPath, 'feature-123')).rejects.toThrow('Permission denied');
|
||||
});
|
||||
});
|
||||
|
||||
describe("create", () => {
|
||||
it("should create new feature", async () => {
|
||||
describe('create', () => {
|
||||
it('should create new feature', async () => {
|
||||
vi.mocked(fs.mkdir).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
||||
|
||||
const featureData = {
|
||||
category: "ui",
|
||||
description: "New feature",
|
||||
category: 'ui',
|
||||
description: 'New feature',
|
||||
};
|
||||
|
||||
const result = await loader.create(testProjectPath, featureData);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
category: "ui",
|
||||
description: "New feature",
|
||||
category: 'ui',
|
||||
description: 'New feature',
|
||||
id: expect.stringMatching(/^feature-/),
|
||||
});
|
||||
expect(fs.writeFile).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should use provided ID if given", async () => {
|
||||
it('should use provided ID if given', async () => {
|
||||
vi.mocked(fs.mkdir).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
||||
|
||||
const result = await loader.create(testProjectPath, {
|
||||
id: "custom-id",
|
||||
category: "ui",
|
||||
description: "Test",
|
||||
id: 'custom-id',
|
||||
category: 'ui',
|
||||
description: 'Test',
|
||||
});
|
||||
|
||||
expect(result.id).toBe("custom-id");
|
||||
expect(result.id).toBe('custom-id');
|
||||
});
|
||||
|
||||
it("should set default category if not provided", async () => {
|
||||
it('should set default category if not provided', async () => {
|
||||
vi.mocked(fs.mkdir).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
||||
|
||||
const result = await loader.create(testProjectPath, {
|
||||
description: "Test",
|
||||
description: 'Test',
|
||||
});
|
||||
|
||||
expect(result.category).toBe("Uncategorized");
|
||||
expect(result.category).toBe('Uncategorized');
|
||||
});
|
||||
});
|
||||
|
||||
describe("update", () => {
|
||||
it("should update existing feature", async () => {
|
||||
describe('update', () => {
|
||||
it('should update existing feature', async () => {
|
||||
vi.mocked(fs.readFile).mockResolvedValue(
|
||||
JSON.stringify({
|
||||
id: "feature-123",
|
||||
category: "ui",
|
||||
description: "Old description",
|
||||
id: 'feature-123',
|
||||
category: 'ui',
|
||||
description: 'Old description',
|
||||
})
|
||||
);
|
||||
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
||||
|
||||
const result = await loader.update(testProjectPath, "feature-123", {
|
||||
description: "New description",
|
||||
const result = await loader.update(testProjectPath, 'feature-123', {
|
||||
description: 'New description',
|
||||
});
|
||||
|
||||
expect(result.description).toBe("New description");
|
||||
expect(result.category).toBe("ui");
|
||||
expect(result.description).toBe('New description');
|
||||
expect(result.category).toBe('ui');
|
||||
expect(fs.writeFile).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should throw if feature doesn't exist", async () => {
|
||||
const error: any = new Error("File not found");
|
||||
error.code = "ENOENT";
|
||||
const error: any = new Error('File not found');
|
||||
error.code = 'ENOENT';
|
||||
vi.mocked(fs.readFile).mockRejectedValue(error);
|
||||
|
||||
await expect(
|
||||
loader.update(testProjectPath, "feature-123", {})
|
||||
).rejects.toThrow("not found");
|
||||
await expect(loader.update(testProjectPath, 'feature-123', {})).rejects.toThrow('not found');
|
||||
});
|
||||
});
|
||||
|
||||
describe("delete", () => {
|
||||
it("should delete feature directory", async () => {
|
||||
describe('delete', () => {
|
||||
it('should delete feature directory', async () => {
|
||||
vi.mocked(fs.rm).mockResolvedValue(undefined);
|
||||
|
||||
const result = await loader.delete(testProjectPath, "feature-123");
|
||||
const result = await loader.delete(testProjectPath, 'feature-123');
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(fs.rm).toHaveBeenCalledWith(
|
||||
expect.stringContaining("feature-123"),
|
||||
{ recursive: true, force: true }
|
||||
);
|
||||
expect(fs.rm).toHaveBeenCalledWith(expect.stringContaining('feature-123'), {
|
||||
recursive: true,
|
||||
force: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("should return false on error", async () => {
|
||||
vi.mocked(fs.rm).mockRejectedValue(new Error("Permission denied"));
|
||||
it('should return false on error', async () => {
|
||||
vi.mocked(fs.rm).mockRejectedValue(new Error('Permission denied'));
|
||||
|
||||
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
const result = await loader.delete(testProjectPath, "feature-123");
|
||||
const result = await loader.delete(testProjectPath, 'feature-123');
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
'[FeatureLoader]',
|
||||
expect.stringContaining('Failed to delete feature'),
|
||||
expect.objectContaining({ message: 'Permission denied' })
|
||||
);
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAgentOutput", () => {
|
||||
it("should return agent output content", async () => {
|
||||
vi.mocked(fs.readFile).mockResolvedValue("Agent output content");
|
||||
describe('getAgentOutput', () => {
|
||||
it('should return agent output content', async () => {
|
||||
vi.mocked(fs.readFile).mockResolvedValue('Agent output content');
|
||||
|
||||
const result = await loader.getAgentOutput(testProjectPath, "feature-123");
|
||||
const result = await loader.getAgentOutput(testProjectPath, 'feature-123');
|
||||
|
||||
expect(result).toBe("Agent output content");
|
||||
expect(result).toBe('Agent output content');
|
||||
});
|
||||
|
||||
it("should return null when file doesn't exist", async () => {
|
||||
const error: any = new Error("File not found");
|
||||
error.code = "ENOENT";
|
||||
const error: any = new Error('File not found');
|
||||
error.code = 'ENOENT';
|
||||
vi.mocked(fs.readFile).mockRejectedValue(error);
|
||||
|
||||
const result = await loader.getAgentOutput(testProjectPath, "feature-123");
|
||||
const result = await loader.getAgentOutput(testProjectPath, 'feature-123');
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("should throw on other errors", async () => {
|
||||
vi.mocked(fs.readFile).mockRejectedValue(new Error("Permission denied"));
|
||||
it('should throw on other errors', async () => {
|
||||
vi.mocked(fs.readFile).mockRejectedValue(new Error('Permission denied'));
|
||||
|
||||
await expect(
|
||||
loader.getAgentOutput(testProjectPath, "feature-123")
|
||||
).rejects.toThrow("Permission denied");
|
||||
await expect(loader.getAgentOutput(testProjectPath, 'feature-123')).rejects.toThrow(
|
||||
'Permission denied'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("saveAgentOutput", () => {
|
||||
it("should save agent output to file", async () => {
|
||||
describe('saveAgentOutput', () => {
|
||||
it('should save agent output to file', async () => {
|
||||
vi.mocked(fs.mkdir).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
||||
|
||||
await loader.saveAgentOutput(
|
||||
testProjectPath,
|
||||
"feature-123",
|
||||
"Output content"
|
||||
);
|
||||
await loader.saveAgentOutput(testProjectPath, 'feature-123', 'Output content');
|
||||
|
||||
expect(fs.writeFile).toHaveBeenCalledWith(
|
||||
expect.stringContaining("agent-output.md"),
|
||||
"Output content",
|
||||
"utf-8"
|
||||
expect.stringContaining('agent-output.md'),
|
||||
'Output content',
|
||||
'utf-8'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("deleteAgentOutput", () => {
|
||||
it("should delete agent output file", async () => {
|
||||
describe('deleteAgentOutput', () => {
|
||||
it('should delete agent output file', async () => {
|
||||
vi.mocked(fs.unlink).mockResolvedValue(undefined);
|
||||
|
||||
await loader.deleteAgentOutput(testProjectPath, "feature-123");
|
||||
await loader.deleteAgentOutput(testProjectPath, 'feature-123');
|
||||
|
||||
expect(fs.unlink).toHaveBeenCalledWith(
|
||||
expect.stringContaining("agent-output.md")
|
||||
);
|
||||
expect(fs.unlink).toHaveBeenCalledWith(expect.stringContaining('agent-output.md'));
|
||||
});
|
||||
|
||||
it("should handle missing file gracefully", async () => {
|
||||
const error: any = new Error("File not found");
|
||||
error.code = "ENOENT";
|
||||
it('should handle missing file gracefully', async () => {
|
||||
const error: any = new Error('File not found');
|
||||
error.code = 'ENOENT';
|
||||
vi.mocked(fs.unlink).mockRejectedValue(error);
|
||||
|
||||
// Should not throw
|
||||
await expect(
|
||||
loader.deleteAgentOutput(testProjectPath, "feature-123")
|
||||
loader.deleteAgentOutput(testProjectPath, 'feature-123')
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("should throw on other errors", async () => {
|
||||
vi.mocked(fs.unlink).mockRejectedValue(new Error("Permission denied"));
|
||||
it('should throw on other errors', async () => {
|
||||
vi.mocked(fs.unlink).mockRejectedValue(new Error('Permission denied'));
|
||||
|
||||
await expect(
|
||||
loader.deleteAgentOutput(testProjectPath, "feature-123")
|
||||
).rejects.toThrow("Permission denied");
|
||||
await expect(loader.deleteAgentOutput(testProjectPath, 'feature-123')).rejects.toThrow(
|
||||
'Permission denied'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user