feat: add packaging and validation coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Copilot CLI
2026-03-21 09:30:37 +01:00
co-authored by Copilot
parent 9e509593d6
commit c8d23f91d1
11 changed files with 556 additions and 76 deletions
+56
View File
@@ -0,0 +1,56 @@
import { describe, expect, test } from 'bun:test';
import { resolveSidecarProcess } from '@main/sidecar/sidecarRuntime';
describe('resolveSidecarProcess', () => {
test('runs the sidecar project with dotnet from the repository root during development', () => {
expect(
resolveSidecarProcess({
isPackaged: false,
appPath: 'C:\\workspace\\personal\\repositories\\kopaya',
resourcesPath: 'C:\\workspace\\personal\\repositories\\kopaya\\resources',
platform: 'win32',
}),
).toEqual({
command: 'dotnet',
args: [
'run',
'--project',
'C:\\workspace\\personal\\repositories\\kopaya\\sidecar\\src\\Kopaya.AgentHost\\Kopaya.AgentHost.csproj',
'--',
'--stdio',
],
cwd: 'C:\\workspace\\personal\\repositories\\kopaya',
});
});
test('runs the bundled Windows sidecar executable from the resources directory when packaged', () => {
expect(
resolveSidecarProcess({
isPackaged: true,
appPath: 'C:\\workspace\\personal\\repositories\\kopaya\\release\\win-unpacked\\resources\\app',
resourcesPath: 'C:\\workspace\\personal\\repositories\\kopaya\\release\\win-unpacked\\resources',
platform: 'win32',
}),
).toEqual({
command: 'C:\\workspace\\personal\\repositories\\kopaya\\release\\win-unpacked\\resources\\sidecar\\Kopaya.AgentHost.exe',
args: ['--stdio'],
cwd: 'C:\\workspace\\personal\\repositories\\kopaya\\release\\win-unpacked\\resources\\sidecar',
});
});
test('omits the .exe suffix for bundled non-Windows sidecar binaries', () => {
expect(
resolveSidecarProcess({
isPackaged: true,
appPath: '/Applications/Kopaya.app/Contents/Resources/app',
resourcesPath: '/Applications/Kopaya.app/Contents/Resources',
platform: 'darwin',
}),
).toEqual({
command: '/Applications/Kopaya.app/Contents/Resources/sidecar/Kopaya.AgentHost',
args: ['--stdio'],
cwd: '/Applications/Kopaya.app/Contents/Resources/sidecar',
});
});
});
+62 -2
View File
@@ -2,9 +2,11 @@ import { describe, expect, test } from 'bun:test';
import { createBuiltinPatterns, validatePatternDefinition } from '@shared/domain/pattern';
const BUILTIN_TIMESTAMP = '2026-03-22T00:00:00.000Z';
describe('pattern validation', () => {
test('builtin patterns are valid except explicitly unavailable modes', () => {
const patterns = createBuiltinPatterns('2026-03-22T00:00:00.000Z');
const patterns = createBuiltinPatterns(BUILTIN_TIMESTAMP);
const validPatterns = patterns.filter((pattern) => pattern.availability !== 'unavailable');
@@ -14,11 +16,69 @@ describe('pattern validation', () => {
});
test('magentic pattern is marked unavailable', () => {
const magentic = createBuiltinPatterns('2026-03-22T00:00:00.000Z').find(
const magentic = createBuiltinPatterns(BUILTIN_TIMESTAMP).find(
(pattern) => pattern.mode === 'magentic',
);
expect(magentic).toBeDefined();
expect(validatePatternDefinition(magentic!)[0]?.message).toContain('unsupported');
});
test('single-agent mode reports agent count, warning, and model issues together', () => {
const singlePattern = createBuiltinPatterns(BUILTIN_TIMESTAMP).find(
(pattern) => pattern.mode === 'single',
);
expect(singlePattern).toBeDefined();
const issues = validatePatternDefinition({
...singlePattern!,
agents: [
{
...singlePattern!.agents[0],
instructions: ' ',
},
{
...singlePattern!.agents[0],
id: 'agent-reviewer',
name: 'Reviewer',
model: '',
},
],
});
expect(issues.find((issue) => issue.field === 'agents')?.message).toBe(
'Single-agent chat requires exactly one agent.',
);
expect(issues.find((issue) => issue.field === 'agents.instructions')?.level).toBe('warning');
expect(issues.find((issue) => issue.field === 'agents.instructions')?.message).toBe(
'Agent "Primary Agent" should have instructions.',
);
expect(issues.find((issue) => issue.field === 'agents.model')?.message).toBe(
'Agent "Reviewer" requires a model identifier.',
);
});
test('multi-agent orchestration modes reject single-agent configurations', () => {
const patterns = createBuiltinPatterns(BUILTIN_TIMESTAMP);
const handoff = patterns.find((pattern) => pattern.mode === 'handoff');
const groupChat = patterns.find((pattern) => pattern.mode === 'group-chat');
expect(handoff).toBeDefined();
expect(groupChat).toBeDefined();
expect(
validatePatternDefinition({
...handoff!,
agents: handoff!.agents.slice(0, 1),
}).find((issue) => issue.field === 'agents')?.message,
).toBe('Handoff orchestration requires at least two agents.');
expect(
validatePatternDefinition({
...groupChat!,
agents: groupChat!.agents.slice(0, 1),
}).find((issue) => issue.field === 'agents')?.message,
).toBe('Group chat requires at least two agents.');
});
});
+34
View File
@@ -0,0 +1,34 @@
import { describe, expect, test } from 'bun:test';
import { createWorkspaceSeed } from '@shared/domain/workspace';
describe('workspace seed', () => {
test('starts empty and seeds the built-in orchestration patterns with a shared timestamp', () => {
const workspace = createWorkspaceSeed();
expect(workspace.projects).toEqual([]);
expect(workspace.sessions).toEqual([]);
expect(workspace.selectedProjectId).toBeUndefined();
expect(workspace.selectedPatternId).toBeUndefined();
expect(workspace.selectedSessionId).toBeUndefined();
expect(workspace.patterns.map((pattern) => pattern.mode)).toEqual([
'single',
'sequential',
'concurrent',
'handoff',
'group-chat',
'magentic',
]);
for (const pattern of workspace.patterns) {
expect(pattern.createdAt).toBe(workspace.lastUpdatedAt);
expect(pattern.updatedAt).toBe(workspace.lastUpdatedAt);
}
const magentic = workspace.patterns.find((pattern) => pattern.mode === 'magentic');
expect(magentic?.availability).toBe('unavailable');
expect(magentic?.unavailabilityReason).toContain('unsupported');
});
});