Files
makelore/tests/unit/opencode-project-store.test.ts
2026-07-29 17:22:35 +08:00

107 lines
3.8 KiB
TypeScript

import path from 'node:path';
import { describe, expect, it, vi } from 'vitest';
import {
createProjectStorageFromStore,
createMemoryProjectStorage,
createProjectStore,
getProjectIdForPath,
} from '@electron/opencode/project-store';
function expectedNormalizedPath(input: string): string {
const normalized = path.normalize(path.resolve(input.trim()));
return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
}
describe('opencode project store', () => {
it('normalizes equivalent paths to the same project id', () => {
const base = path.join('D:', 'repo', 'packages', 'ui');
const equivalent = path.join('D:', 'repo', 'packages', 'ui', '..', 'ui', '.');
const id = getProjectIdForPath(base);
expect(id).toMatch(/^prj_[0-9a-f]{16}$/);
expect(getProjectIdForPath(equivalent)).toBe(id);
});
it('opens the exact normalized folder without expanding to the git root', async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-05-12T01:02:03.000Z'));
try {
const store = createProjectStore(createMemoryProjectStorage());
const input = 'D:/repo/packages/ui';
const project = await store.openFolder(input);
expect(project.path).toBe(expectedNormalizedPath(input));
expect(project.path).not.toBe(expectedNormalizedPath('D:/repo'));
expect(project.name).toBe('ui');
await expect(store.getActiveProject()).resolves.toEqual(project);
} finally {
vi.useRealTimers();
}
});
it('keeps subfolders in the same repo separate and lists newest first', async () => {
vi.useFakeTimers();
try {
const store = createProjectStore(createMemoryProjectStorage());
vi.setSystemTime(new Date('2026-05-12T00:00:00.000Z'));
const api = await store.openFolder('D:/repo/packages/api');
vi.setSystemTime(new Date('2026-05-12T00:01:00.000Z'));
const ui = await store.openFolder('D:/repo/packages/ui');
vi.setSystemTime(new Date('2026-05-12T00:02:00.000Z'));
const reopenedApi = await store.openFolder('D:/repo/packages/api');
expect(reopenedApi.id).toBe(api.id);
expect(reopenedApi.createdAt).toBe(api.createdAt);
expect(reopenedApi.updatedAt).toBe('2026-05-12T00:02:00.000Z');
expect(reopenedApi.lastOpenedAt).toBe('2026-05-12T00:02:00.000Z');
const projects = await store.listProjects();
expect(ui.id).not.toBe(api.id);
expect(projects.map((project) => project.id)).toEqual([api.id, ui.id]);
expect(projects.map((project) => project.path)).toEqual([
expectedNormalizedPath('D:/repo/packages/api'),
expectedNormalizedPath('D:/repo/packages/ui'),
]);
} finally {
vi.useRealTimers();
}
});
it('removes a project from the list without touching other projects', async () => {
const store = createProjectStore(createMemoryProjectStorage());
const api = await store.openFolder('D:/repo/packages/api');
const ui = await store.openFolder('D:/repo/packages/ui');
await expect(store.getActiveProject()).resolves.toEqual(ui);
await store.removeProject(ui.id);
await expect(store.listProjects()).resolves.toEqual([api]);
await expect(store.getActiveProject()).resolves.toBeNull();
});
it('persists project data through a key-value backing store', async () => {
const backing = new Map<string, unknown>();
const storage = createProjectStorageFromStore({
get: (key) => backing.get(key),
set: (key, value) => backing.set(key, value),
});
const firstStore = createProjectStore(storage);
const project = await firstStore.openFolder('D:/repo/packages/ui');
const secondStore = createProjectStore(storage);
await expect(secondStore.getActiveProject()).resolves.toEqual(project);
await expect(secondStore.listProjects()).resolves.toEqual([project]);
});
});