Files
makelore/tests/unit/project-config.test.ts
inman 80e8386fa6
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled
feat: update Makelore modules and conversations
2026-07-31 10:08:41 +08:00

134 lines
5.2 KiB
TypeScript

import { mkdtemp, readFile, readdir } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { describe, expect, it } from 'vitest';
import {
createProjectConfig,
validateAgentConfigs,
type ProjectAgentConfig,
} from '../../shared/project-config';
import {
createInitialProjectConfig,
normalizeProjectConfig,
readProjectConfig,
writeProjectConfig,
} from '../../electron/opencode/project-config';
function createContact(overrides: Partial<ProjectAgentConfig> = {}): ProjectAgentConfig {
const now = '2026-07-11T00:00:00.000Z';
return {
id: 'agent-test',
avatarId: 'avatar-01',
roleName: '项目伙伴',
name: '小明',
builtIn: false,
enabled: true,
model: 'openai/gpt-4o-mini',
skillIds: [],
responsibility: {
mission: '把用户的想法整理成清晰的下一步。',
owns: [],
boundaries: [],
collaborators: [],
principles: [],
},
prompt: '',
archivedAt: null,
pinned: false,
createdAt: now,
updatedAt: now,
...overrides,
};
}
describe('project-owned contact configuration', () => {
it('creates an empty, template-free project config', () => {
const config = createProjectConfig('2026-07-11T00:00:00.000Z');
expect(config).toMatchObject({
initialized: false,
superpowersEnabled: false,
agents: [],
knowledgeDirectory: 'knowledge',
});
expect('templateId' in config).toBe(false);
});
it('requires a unique name, preset avatar, model, and responsibility for every contact', () => {
expect(validateAgentConfigs([])).toEqual([]);
expect(validateAgentConfigs([createContact({ name: '' })])).toContain('agent-test:name-required');
expect(validateAgentConfigs([createContact({ avatarId: 'upload-me' })])).toContain('agent-test:avatar-required');
expect(validateAgentConfigs([createContact({ model: null })])).toContain('agent-test:model-required');
expect(validateAgentConfigs([createContact({ responsibility: { mission: '', owns: [], boundaries: [], collaborators: [], principles: [] } })])).toContain('agent-test:responsibility-required');
expect(validateAgentConfigs([
createContact(),
createContact({ id: 'agent-test-2', name: '小明' }),
])).toContain('agent-test-2:name-duplicate');
expect(validateAgentConfigs([createContact()])).toEqual([]);
});
it('normalizes a manually configured contact without injecting skills or a built-in role', () => {
const config = createProjectConfig();
const normalized = normalizeProjectConfig({
...config,
initialized: true,
agents: [createContact()],
superpowersEnabled: undefined,
});
expect(normalized).toMatchObject({
superpowersEnabled: false,
agents: [{ builtIn: false, prompt: '', skillIds: [], model: 'openai/gpt-4o-mini' }],
});
expect('templateId' in normalized).toBe(false);
});
it('preserves the legacy built-in marker when reading an existing project', () => {
const config = createProjectConfig();
const normalized = normalizeProjectConfig({
...config,
initialized: true,
agents: [createContact({ builtIn: true })],
});
expect(normalized.agents[0]?.builtIn).toBe(true);
});
it('materializes a custom contact with its exact model and prompt', async () => {
const projectPath = await mkdtemp(path.join(tmpdir(), 'makelore-project-config-'));
const initial = await createInitialProjectConfig(projectPath);
const saved = await writeProjectConfig(projectPath, {
...initial,
initialized: true,
agents: [createContact()],
});
expect(saved.agents).toHaveLength(1);
const markdown = await readFile(path.join(projectPath, '.opencode', 'agent', 'agent-test.md'), 'utf8');
expect(markdown).toContain('model: "openai/gpt-4o-mini"');
expect(markdown).toContain('description: "小明"');
expect(markdown).toContain('把用户的想法整理成清晰的下一步');
expect(markdown).not.toContain('youth-plain-language: allow');
expect(markdown).toContain('"*": deny');
});
it('does not install default Agent files or template documents in a new project', async () => {
const projectPath = await mkdtemp(path.join(tmpdir(), 'makelore-project-config-empty-'));
const initial = await createInitialProjectConfig(projectPath);
const loaded = await readProjectConfig(projectPath);
expect(loaded).toMatchObject({ status: 'valid', config: { agents: [], initialized: false } });
await expect(readdir(path.join(projectPath, '.opencode', 'agent'))).rejects.toMatchObject({ code: 'ENOENT' });
expect(initial.agents).toEqual([]);
expect(await readFile(path.join(projectPath, 'VERSION.md'), 'utf8')).toContain('Version Owner: user');
expect(await readFile(path.join(projectPath, 'TASKS.md'), 'utf8')).toContain('Owner: user');
});
it('rejects an initialized contact with missing required basics at save time', async () => {
const projectPath = await mkdtemp(path.join(tmpdir(), 'makelore-project-config-invalid-'));
const initial = await createInitialProjectConfig(projectPath);
await expect(writeProjectConfig(projectPath, {
...initial,
initialized: true,
agents: [createContact({ model: null })],
})).rejects.toThrow('model-required');
});
});