Files
makelore/tests/unit/coding-workspace-store.test.ts

125 lines
3.9 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { createCodingWorkspaceStore } from '@/stores/coding-workspace';
import type {
CodingConversationMetadata,
CodingProjectAgent,
CodingProjectConfig,
CodingProjectSummary,
} from '@/types/coding-project';
const project: CodingProjectSummary = {
id: 'project-1',
name: 'Local project',
createdAt: '2026-08-23T00:00:00.000Z',
updatedAt: '2026-08-23T00:00:00.000Z',
lastOpenedAt: '2026-08-23T00:00:00.000Z',
};
function agent(id: string, overrides: Partial<CodingProjectAgent> = {}): CodingProjectAgent {
return {
id,
avatarId: 'default',
roleName: '伙伴',
name: id,
builtIn: false,
enabled: true,
skillIds: [],
responsibility: {
mission: '协助当前项目',
owns: [],
boundaries: [],
collaborators: [],
principles: [],
},
prompt: '',
archivedAt: null,
pinned: false,
createdAt: '2026-08-23T00:00:00.000Z',
updatedAt: '2026-08-23T00:00:00.000Z',
model: null,
modelResolution: 'default',
...overrides,
};
}
function config(agents: CodingProjectAgent[]): CodingProjectConfig {
return {
schemaVersion: 2,
projectType: 'custom',
initialized: true,
agents,
knowledgeDirectory: 'knowledge',
legacyConversationNotice: 'none',
createdAt: '2026-08-23T00:00:00.000Z',
updatedAt: '2026-08-23T00:00:00.000Z',
};
}
function conversation(id: string, agentId: string): CodingConversationMetadata {
return {
id,
agentId,
title: '新对话',
archivedAt: null,
unread: false,
createdAt: '2026-08-23T00:00:00.000Z',
updatedAt: '2026-08-23T00:00:00.000Z',
model: null,
modelResolution: 'default',
};
}
describe('coding workspace store', () => {
it('loads local project metadata and selects the pinned Agent without touching runtime APIs', async () => {
const listProjects = vi.fn(async () => ({ projects: [project], activeProjectId: project.id }));
const getConfig = vi.fn(async () => ({
project,
config: config([agent('agent-a'), agent('agent-b', { pinned: true })]),
}));
const listConversations = vi.fn(async () => [conversation('conversation-b', 'agent-b')]);
const createConversation = vi.fn();
const store = createCodingWorkspaceStore({
listProjects,
getConfig,
listConversations,
createConversation,
});
await store.getState().load();
expect(store.getState()).toMatchObject({
activeProjectId: 'project-1',
selectedAgentId: 'agent-b',
loadState: 'ready',
conversations: [{ id: 'conversation-b' }],
});
expect(createConversation).not.toHaveBeenCalled();
});
it('creates a missing first Conversation once for concurrent callers', async () => {
let resolveCreate!: (value: CodingConversationMetadata) => void;
const createFlight = new Promise<CodingConversationMetadata>((resolve) => {
resolveCreate = resolve;
});
const createConversation = vi.fn(() => createFlight);
const store = createCodingWorkspaceStore({
listProjects: vi.fn(async () => ({ projects: [project], activeProjectId: project.id })),
getConfig: vi.fn(async () => ({ project, config: config([agent('agent-a')]) })),
listConversations: vi.fn(async () => []),
createConversation,
});
await store.getState().load();
const first = store.getState().ensureConversation('agent-a');
const second = store.getState().ensureConversation('agent-a');
expect(createConversation).toHaveBeenCalledTimes(1);
expect(store.getState().creatingAgentIds).toEqual({ 'agent-a': true });
const created = conversation('conversation-a', 'agent-a');
resolveCreate(created);
await expect(Promise.all([first, second])).resolves.toEqual([created, created]);
expect(store.getState().conversations).toEqual([created]);
expect(store.getState().creatingAgentIds).toEqual({});
});
});