ever-brain / packages /api /test /resolver-service.test.ts
abrez ‎
hello to knowiki
a5d1c1b
Raw History Blame Contribute Delete
3.92 kB
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ResolverService } from '../src/services/resolver-service.js';
import { githubService } from '../src/services/github-service.js';
import { treeCache } from '../src/services/tree-cache.js';
describe('ResolverService', () => {
let resolver: ResolverService;
beforeEach(() => {
treeCache.clear();
resolver = new ResolverService();
vi.restoreAllMocks();
});
it('discovers and filters tree items properly', async () => {
vi.spyOn(githubService, 'getGitTree').mockResolvedValue([
{ path: 'README.md', mode: '100644', type: 'blob', sha: '1', size: 150, url: '' },
{ path: 'knowledge/arch.md', mode: '100644', type: 'blob', sha: '2', size: 300, url: '' },
{ path: 'skills/api/SKILL.md', mode: '100644', type: 'blob', sha: '3', size: 450, url: '' },
{ path: 'images/logo.png', mode: '100644', type: 'blob', sha: '4', size: 2000, url: '' }, // Binary
{ path: '.git/config', mode: '100644', type: 'blob', sha: '5', size: 50, url: '' }, // Git internal
{ path: 'docs', mode: '040000', type: 'tree', sha: '6', url: '' }, // Directory tree
]);
const result = await resolver.getOrDiscoverFiles('acme', 'project', 'main');
expect(result.repository).toBe('acme/project');
expect(result.branch).toBe('main');
expect(result.totalFiles).toBe(3);
expect(result.files.map((f) => f.path)).toEqual([
'README.md',
'knowledge/arch.md',
'skills/api/SKILL.md',
]);
});
it('applies prefix filtering correctly', async () => {
vi.spyOn(githubService, 'getGitTree').mockResolvedValue([
{ path: 'README.md', mode: '100644', type: 'blob', sha: '1', size: 100, url: '' },
{ path: 'knowledge/arch.md', mode: '100644', type: 'blob', sha: '2', size: 200, url: '' },
{ path: 'knowledge/security.md', mode: '100644', type: 'blob', sha: '3', size: 300, url: '' },
{ path: 'skills/api/SKILL.md', mode: '100644', type: 'blob', sha: '4', size: 400, url: '' },
]);
const result = await resolver.getOrDiscoverFiles('acme', 'project', 'main', false, 'knowledge/');
expect(result.totalFiles).toBe(2);
expect(result.files.map((f) => f.path)).toEqual([
'knowledge/arch.md',
'knowledge/security.md',
]);
});
it('normalizes case-insensitive file lookups', async () => {
vi.spyOn(githubService, 'getGitTree').mockResolvedValue([
{ path: 'readme.md', mode: '100644', type: 'blob', sha: '1', size: 100, url: '' },
{ path: 'Knowledge/Architecture.md', mode: '100644', type: 'blob', sha: '2', size: 200, url: '' },
]);
// Request uppercase README.md -> resolves to actual lowercase readme.md
const resolvedReadme = await resolver.resolveFilePath('acme', 'project', 'main', 'README.md');
expect(resolvedReadme.exactPath).toBe('readme.md');
// Request lowercase knowledge/architecture.md -> resolves to actual Knowledge/Architecture.md
const resolvedArch = await resolver.resolveFilePath('acme', 'project', 'main', 'knowledge/architecture.md');
expect(resolvedArch.exactPath).toBe('Knowledge/Architecture.md');
});
it('uses cache on subsequent calls unless fresh=true', async () => {
const gitTreeSpy = vi.spyOn(githubService, 'getGitTree').mockResolvedValue([
{ path: 'README.md', mode: '100644', type: 'blob', sha: '1', size: 100, url: '' },
]);
// First call: hits GitHub
await resolver.getOrDiscoverFiles('acme', 'project', 'main', false);
expect(gitTreeSpy).toHaveBeenCalledTimes(1);
// Second call: hits cache
await resolver.getOrDiscoverFiles('acme', 'project', 'main', false);
expect(gitTreeSpy).toHaveBeenCalledTimes(1);
// Third call with fresh=true: bypasses cache and hits GitHub again
await resolver.getOrDiscoverFiles('acme', 'project', 'main', true);
expect(gitTreeSpy).toHaveBeenCalledTimes(2);
});
});