agent-ecosystem/test/main/services/team/ChangeExtractorService.test.ts
iliya 1ccc1432fc feat: enhance task change handling and improve plugin catalog integration
- Added a 'source' field to PluginCatalogItem to distinguish between official and third-party plugins.
- Refactored ChangeExtractorService to improve caching mechanisms and normalize file paths for better consistency.
- Updated TaskBoundaryParser to support task IDs with underscores, enhancing task identification.
- Enhanced TeamMcpConfigBuilder to merge user-defined MCP configurations with generated ones, improving configuration management.
- Improved UI components to display source information for plugins and MCP servers, enhancing user experience and clarity.
2026-03-11 18:16:40 +02:00

148 lines
4.4 KiB
TypeScript

import * as os from 'os';
import * as path from 'path';
import { afterEach, describe, expect, it, vi } from 'vitest';
import * as fs from 'fs/promises';
import { ChangeExtractorService } from '../../../../src/main/services/team/ChangeExtractorService';
import { setClaudeBasePathOverride } from '../../../../src/main/utils/pathDecoder';
describe('ChangeExtractorService', () => {
let tmpDir: string | null = null;
afterEach(async () => {
setClaudeBasePathOverride(null);
if (tmpDir) {
await fs.rm(tmpDir, { recursive: true, force: true });
tmpDir = null;
}
});
it('does not reuse detailed task-change cache across different scope inputs', async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'change-extractor-service-'));
setClaudeBasePathOverride(tmpDir);
const aliceLogPath = path.join(tmpDir, 'alice.jsonl');
await fs.writeFile(
aliceLogPath,
JSON.stringify({
timestamp: '2026-03-01T10:00:00.000Z',
type: 'assistant',
message: {
role: 'assistant',
content: [
{
type: 'tool_use',
id: 'tool-1',
name: 'Write',
input: { file_path: '/repo/src/file.ts', content: 'export const value = 1;\n' },
},
],
},
}) + '\n',
'utf8'
);
const findLogsForTask = vi.fn(async (_teamName: string, _taskId: string, options?: any) =>
options?.owner === 'alice' ? [{ filePath: aliceLogPath, memberName: 'alice' }] : []
);
const parseBoundaries = vi.fn(async () => ({
boundaries: [],
scopes: [],
isSingleTaskSession: true,
detectedMechanism: 'none' as const,
}));
const service = new ChangeExtractorService(
{
findLogsForTask,
findMemberLogPaths: vi.fn(async () => []),
} as any,
{ parseBoundaries } as any,
{ getConfig: vi.fn(async () => ({ projectPath: '/repo' })) } as any
);
const empty = await service.getTaskChanges('team-a', '1', { owner: 'bob', status: 'completed' });
const populated = await service.getTaskChanges('team-a', '1', {
owner: 'alice',
status: 'completed',
});
expect(empty.files).toHaveLength(0);
expect(populated.files).toHaveLength(1);
expect(findLogsForTask).toHaveBeenCalledTimes(2);
});
it('merges fallback changes for the same Windows file across slash variants', async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'change-extractor-service-'));
setClaudeBasePathOverride(tmpDir);
const firstLogPath = path.join(tmpDir, 'first.jsonl');
const secondLogPath = path.join(tmpDir, 'second.jsonl');
await fs.writeFile(
firstLogPath,
JSON.stringify({
timestamp: '2026-03-01T10:00:00.000Z',
type: 'assistant',
message: {
role: 'assistant',
content: [
{
type: 'tool_use',
id: 'tool-1',
name: 'Write',
input: { file_path: 'C:\\repo\\src\\same.ts', content: 'first\n' },
},
],
},
}) + '\n',
'utf8'
);
await fs.writeFile(
secondLogPath,
JSON.stringify({
timestamp: '2026-03-01T10:01:00.000Z',
type: 'assistant',
message: {
role: 'assistant',
content: [
{
type: 'tool_use',
id: 'tool-2',
name: 'Write',
input: { file_path: 'C:/repo/src/same.ts', content: 'second\n' },
},
],
},
}) + '\n',
'utf8'
);
const service = new ChangeExtractorService(
{
findLogsForTask: vi.fn(async () => [
{ filePath: firstLogPath, memberName: 'alice' },
{ filePath: secondLogPath, memberName: 'alice' },
]),
findMemberLogPaths: vi.fn(async () => []),
} as any,
{
parseBoundaries: vi.fn(async () => ({
boundaries: [],
scopes: [],
isSingleTaskSession: true,
detectedMechanism: 'none' as const,
})),
} as any,
{ getConfig: vi.fn(async () => ({ projectPath: 'C:\\repo' })) } as any
);
const result = await service.getTaskChanges('team-a', '1', {
owner: 'alice',
status: 'completed',
});
expect(result.files).toHaveLength(1);
expect(result.files[0]?.relativePath).toBe('src/same.ts');
expect(result.totalLinesAdded).toBe(2);
});
});