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 { 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((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({}); }); });