mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
test: add unit tests for exec-utils, feature verification, project analysis, and task execution
- Introduced comprehensive unit tests for exec-utils, covering execAsync, extendedPath, execEnv, and isENOENT functions. - Added tests for FeatureVerificationService to validate feature verification and commit processes. - Implemented tests for ProjectAnalyzer to ensure project analysis functionality. - Created tests for TaskExecutor to validate task execution and event emissions. These additions enhance test coverage and ensure the reliability of core functionalities.
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
// Use vi.hoisted for mock functions that need to be used in vi.mock factories
|
||||
const {
|
||||
mockExecuteQuery,
|
||||
mockProcessStream,
|
||||
mockMkdir,
|
||||
mockWriteFile,
|
||||
mockValidateWorkingDirectory,
|
||||
} = vi.hoisted(() => ({
|
||||
mockExecuteQuery: vi.fn(),
|
||||
mockProcessStream: vi.fn(),
|
||||
mockMkdir: vi.fn(),
|
||||
mockWriteFile: vi.fn(),
|
||||
mockValidateWorkingDirectory: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('@automaker/platform', () => ({
|
||||
secureFs: {
|
||||
mkdir: mockMkdir,
|
||||
writeFile: mockWriteFile,
|
||||
},
|
||||
getAutomakerDir: (projectPath: string) => `${projectPath}/.automaker`,
|
||||
}));
|
||||
|
||||
vi.mock('@automaker/utils', async () => {
|
||||
const actual = await vi.importActual('@automaker/utils');
|
||||
return {
|
||||
...actual,
|
||||
processStream: mockProcessStream,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@automaker/model-resolver', () => ({
|
||||
resolveModelString: () => 'claude-sonnet-4-20250514',
|
||||
DEFAULT_MODELS: { claude: 'claude-sonnet-4-20250514' },
|
||||
}));
|
||||
|
||||
vi.mock('@/providers/provider-factory.js', () => ({
|
||||
ProviderFactory: {
|
||||
getProviderForModel: () => ({
|
||||
executeQuery: mockExecuteQuery,
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/sdk-options.js', () => ({
|
||||
validateWorkingDirectory: mockValidateWorkingDirectory,
|
||||
}));
|
||||
|
||||
import { ProjectAnalyzer } from '@/services/auto-mode/project-analyzer.js';
|
||||
|
||||
describe('ProjectAnalyzer', () => {
|
||||
let analyzer: ProjectAnalyzer;
|
||||
let mockEvents: { emit: ReturnType<typeof vi.fn> };
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockEvents = { emit: vi.fn() };
|
||||
|
||||
mockExecuteQuery.mockReturnValue(
|
||||
(async function* () {
|
||||
yield { type: 'text', text: 'Analysis result' };
|
||||
})()
|
||||
);
|
||||
|
||||
mockProcessStream.mockResolvedValue({
|
||||
text: '# Project Analysis\nThis is a test project.',
|
||||
toolUses: [],
|
||||
});
|
||||
|
||||
analyzer = new ProjectAnalyzer(mockEvents as any);
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should create analyzer instance', () => {
|
||||
expect(analyzer).toBeInstanceOf(ProjectAnalyzer);
|
||||
});
|
||||
});
|
||||
|
||||
describe('analyze', () => {
|
||||
it('should validate working directory', async () => {
|
||||
await analyzer.analyze('/project');
|
||||
|
||||
expect(mockValidateWorkingDirectory).toHaveBeenCalledWith('/project');
|
||||
});
|
||||
|
||||
it('should emit start event', async () => {
|
||||
await analyzer.analyze('/project');
|
||||
|
||||
expect(mockEvents.emit).toHaveBeenCalledWith(
|
||||
'auto-mode:event',
|
||||
expect.objectContaining({
|
||||
type: 'auto_mode_feature_start',
|
||||
projectPath: '/project',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should call provider executeQuery with correct options', async () => {
|
||||
await analyzer.analyze('/project');
|
||||
|
||||
expect(mockExecuteQuery).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
cwd: '/project',
|
||||
maxTurns: 5,
|
||||
allowedTools: ['Read', 'Glob', 'Grep'],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should save analysis to file', async () => {
|
||||
await analyzer.analyze('/project');
|
||||
|
||||
expect(mockMkdir).toHaveBeenCalledWith('/project/.automaker', { recursive: true });
|
||||
expect(mockWriteFile).toHaveBeenCalledWith(
|
||||
'/project/.automaker/project-analysis.md',
|
||||
'# Project Analysis\nThis is a test project.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should emit complete event on success', async () => {
|
||||
await analyzer.analyze('/project');
|
||||
|
||||
expect(mockEvents.emit).toHaveBeenCalledWith(
|
||||
'auto-mode:event',
|
||||
expect.objectContaining({
|
||||
type: 'auto_mode_feature_complete',
|
||||
passes: true,
|
||||
message: 'Project analysis completed',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should emit error event on failure', async () => {
|
||||
mockProcessStream.mockRejectedValue(new Error('Analysis failed'));
|
||||
|
||||
await analyzer.analyze('/project');
|
||||
|
||||
expect(mockEvents.emit).toHaveBeenCalledWith(
|
||||
'auto-mode:event',
|
||||
expect.objectContaining({
|
||||
type: 'auto_mode_error',
|
||||
error: expect.stringContaining('Analysis failed'),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle stream with onText callback', async () => {
|
||||
await analyzer.analyze('/project');
|
||||
|
||||
expect(mockProcessStream).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
onText: expect.any(Function),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user