@codemcp/workflows 4.10.1 → 4.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/components/beads/beads-instruction-generator.d.ts +3 -4
- package/dist/components/beads/beads-instruction-generator.d.ts.map +1 -1
- package/dist/components/beads/beads-instruction-generator.js +12 -7
- package/dist/components/beads/beads-instruction-generator.js.map +1 -1
- package/dist/components/beads/beads-task-backend-client.d.ts.map +1 -1
- package/dist/components/beads/beads-task-backend-client.js +1 -4
- package/dist/components/beads/beads-task-backend-client.js.map +1 -1
- package/dist/plugin-system/beads-plugin.d.ts +70 -0
- package/dist/plugin-system/beads-plugin.d.ts.map +1 -0
- package/dist/plugin-system/beads-plugin.js +459 -0
- package/dist/plugin-system/beads-plugin.js.map +1 -0
- package/dist/plugin-system/index.d.ts +9 -0
- package/dist/plugin-system/index.d.ts.map +1 -0
- package/dist/plugin-system/index.js +9 -0
- package/dist/plugin-system/index.js.map +1 -0
- package/dist/plugin-system/plugin-interfaces.d.ts +99 -0
- package/dist/plugin-system/plugin-interfaces.d.ts.map +1 -0
- package/dist/plugin-system/plugin-interfaces.js +9 -0
- package/dist/plugin-system/plugin-interfaces.js.map +1 -0
- package/dist/plugin-system/plugin-registry.d.ts +44 -0
- package/dist/plugin-system/plugin-registry.d.ts.map +1 -0
- package/dist/plugin-system/plugin-registry.js +132 -0
- package/dist/plugin-system/plugin-registry.js.map +1 -0
- package/dist/server-config.d.ts.map +1 -1
- package/dist/server-config.js +28 -8
- package/dist/server-config.js.map +1 -1
- package/dist/tool-handlers/conduct-review.d.ts.map +1 -1
- package/dist/tool-handlers/conduct-review.js +1 -2
- package/dist/tool-handlers/conduct-review.js.map +1 -1
- package/dist/tool-handlers/proceed-to-phase.d.ts +0 -5
- package/dist/tool-handlers/proceed-to-phase.d.ts.map +1 -1
- package/dist/tool-handlers/proceed-to-phase.js +15 -93
- package/dist/tool-handlers/proceed-to-phase.js.map +1 -1
- package/dist/tool-handlers/start-development.d.ts +0 -13
- package/dist/tool-handlers/start-development.d.ts.map +1 -1
- package/dist/tool-handlers/start-development.js +29 -124
- package/dist/tool-handlers/start-development.js.map +1 -1
- package/dist/tool-handlers/whats-next.d.ts.map +1 -1
- package/dist/tool-handlers/whats-next.js +1 -0
- package/dist/tool-handlers/whats-next.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/beads/beads-instruction-generator.ts +12 -12
- package/src/components/beads/beads-task-backend-client.ts +1 -4
- package/src/plugin-system/beads-plugin.ts +641 -0
- package/src/plugin-system/index.ts +20 -0
- package/src/plugin-system/plugin-interfaces.ts +154 -0
- package/src/plugin-system/plugin-registry.ts +190 -0
- package/src/server-config.ts +30 -8
- package/src/tool-handlers/conduct-review.ts +1 -2
- package/src/tool-handlers/proceed-to-phase.ts +19 -135
- package/src/tool-handlers/start-development.ts +35 -205
- package/src/tool-handlers/whats-next.ts +1 -0
- package/src/types.ts +2 -0
- package/test/e2e/beads-plugin-integration.test.ts +1609 -0
- package/test/e2e/plugin-system-integration.test.ts +1729 -0
- package/test/unit/beads-plugin-behavioral.test.ts +512 -0
- package/test/unit/beads-plugin.test.ts +94 -0
- package/test/unit/plugin-error-handling.test.ts +240 -0
- package/test/unit/proceed-to-phase-plugin-integration.test.ts +150 -0
- package/test/unit/server-config-plugin-registry.test.ts +81 -0
- package/test/unit/start-development-goal-extraction.test.ts +22 -16
- package/test/utils/test-helpers.ts +3 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/components/server-components-factory.d.ts +0 -39
- package/dist/components/server-components-factory.d.ts.map +0 -1
- package/dist/components/server-components-factory.js +0 -62
- package/dist/components/server-components-factory.js.map +0 -1
- package/src/components/server-components-factory.ts +0 -86
- package/test/e2e/component-substitution.test.ts +0 -208
- package/test/unit/beads-integration-filename.test.ts +0 -93
- package/test/unit/server-components-factory.test.ts +0 -279
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Component Substitution E2E Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests that the strategy pattern component substitution works correctly
|
|
5
|
-
* in different task backend configurations.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
9
|
-
import { createTempProjectWithDefaultStateMachine } from '../utils/temp-files';
|
|
10
|
-
import {
|
|
11
|
-
DirectServerInterface,
|
|
12
|
-
createSuiteIsolatedE2EScenario,
|
|
13
|
-
assertToolSuccess,
|
|
14
|
-
initializeDevelopment,
|
|
15
|
-
} from '../utils/e2e-test-setup';
|
|
16
|
-
|
|
17
|
-
vi.unmock('fs');
|
|
18
|
-
vi.unmock('fs/promises');
|
|
19
|
-
|
|
20
|
-
describe('Component Substitution', () => {
|
|
21
|
-
let client: DirectServerInterface;
|
|
22
|
-
let cleanup: () => Promise<void>;
|
|
23
|
-
|
|
24
|
-
describe('Markdown Backend Strategy', () => {
|
|
25
|
-
beforeEach(async () => {
|
|
26
|
-
// Ensure markdown backend is detected
|
|
27
|
-
process.env.TASK_BACKEND = 'markdown';
|
|
28
|
-
|
|
29
|
-
const scenario = await createSuiteIsolatedE2EScenario({
|
|
30
|
-
suiteName: 'component-substitution-markdown',
|
|
31
|
-
tempProjectFactory: createTempProjectWithDefaultStateMachine,
|
|
32
|
-
});
|
|
33
|
-
client = scenario.client;
|
|
34
|
-
cleanup = scenario.cleanup;
|
|
35
|
-
|
|
36
|
-
await initializeDevelopment(client);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
afterEach(async () => {
|
|
40
|
-
delete process.env.TASK_BACKEND;
|
|
41
|
-
if (cleanup) {
|
|
42
|
-
await cleanup();
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should use markdown-based components for plan management', async () => {
|
|
47
|
-
const result = await client.callTool('whats_next', {
|
|
48
|
-
user_input: 'test markdown component substitution',
|
|
49
|
-
});
|
|
50
|
-
const response = assertToolSuccess(result);
|
|
51
|
-
|
|
52
|
-
expect(response.phase).toBeTruthy();
|
|
53
|
-
expect(response.instructions).toBeTruthy();
|
|
54
|
-
expect(response.plan_file_path).toBeTruthy();
|
|
55
|
-
|
|
56
|
-
// Verify plan file operations work with markdown backend
|
|
57
|
-
expect(response.plan_file_path).toContain('.vibe');
|
|
58
|
-
expect(response.plan_file_path).toMatch(/\.md$/);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should generate markdown-compatible instructions', async () => {
|
|
62
|
-
const result = await client.callTool('whats_next', {
|
|
63
|
-
user_input: 'create feature with markdown backend',
|
|
64
|
-
});
|
|
65
|
-
const response = assertToolSuccess(result);
|
|
66
|
-
|
|
67
|
-
// Instructions should be generated using markdown-based strategy
|
|
68
|
-
expect(response.instructions).toContain('Plan File Guidance');
|
|
69
|
-
expect(response.instructions).toContain('Project Context');
|
|
70
|
-
expect(typeof response.instructions).toBe('string');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should handle phase transitions with markdown backend', async () => {
|
|
74
|
-
// Initialize conversation
|
|
75
|
-
await client.callTool('whats_next', { user_input: 'start project' });
|
|
76
|
-
|
|
77
|
-
const result = await client.callTool('proceed_to_phase', {
|
|
78
|
-
target_phase: 'design',
|
|
79
|
-
reason: 'requirements complete',
|
|
80
|
-
review_state: 'not-required',
|
|
81
|
-
});
|
|
82
|
-
const response = assertToolSuccess(result);
|
|
83
|
-
|
|
84
|
-
expect(response.phase).toBe('design');
|
|
85
|
-
expect(response.instructions).toBeTruthy();
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
describe('Backend Strategy Configuration', () => {
|
|
90
|
-
// Note: Actual beads fallback testing requires specific environment setup
|
|
91
|
-
// These tests document the expected behavior when backend fallback occurs
|
|
92
|
-
|
|
93
|
-
it.todo('should fallback to default components when beads unavailable');
|
|
94
|
-
it.todo('should generate compatible instructions with fallback strategy');
|
|
95
|
-
|
|
96
|
-
// For now, focus on testing the factory pattern mechanism itself
|
|
97
|
-
// rather than specific backend availability scenarios
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
describe('Component Factory Integration', () => {
|
|
101
|
-
beforeEach(async () => {
|
|
102
|
-
const scenario = await createSuiteIsolatedE2EScenario({
|
|
103
|
-
suiteName: 'component-substitution-factory',
|
|
104
|
-
tempProjectFactory: createTempProjectWithDefaultStateMachine,
|
|
105
|
-
});
|
|
106
|
-
client = scenario.client;
|
|
107
|
-
cleanup = scenario.cleanup;
|
|
108
|
-
|
|
109
|
-
await initializeDevelopment(client);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
afterEach(async () => {
|
|
113
|
-
if (cleanup) {
|
|
114
|
-
await cleanup();
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('should maintain consistent behavior across component substitutions', async () => {
|
|
119
|
-
// Test multiple operations to ensure consistent component behavior
|
|
120
|
-
const first = await client.callTool('whats_next', {
|
|
121
|
-
user_input: 'first operation',
|
|
122
|
-
});
|
|
123
|
-
const firstResponse = assertToolSuccess(first);
|
|
124
|
-
|
|
125
|
-
const second = await client.callTool('proceed_to_phase', {
|
|
126
|
-
target_phase: 'design',
|
|
127
|
-
reason: 'ready to design',
|
|
128
|
-
review_state: 'not-required',
|
|
129
|
-
});
|
|
130
|
-
const secondResponse = assertToolSuccess(second);
|
|
131
|
-
|
|
132
|
-
const third = await client.callTool('whats_next', {
|
|
133
|
-
user_input: 'continue after transition',
|
|
134
|
-
});
|
|
135
|
-
const thirdResponse = assertToolSuccess(third);
|
|
136
|
-
|
|
137
|
-
// All responses should be consistent and functional
|
|
138
|
-
expect(firstResponse.conversation_id).toBe(
|
|
139
|
-
secondResponse.conversation_id
|
|
140
|
-
);
|
|
141
|
-
expect(secondResponse.conversation_id).toBe(
|
|
142
|
-
thirdResponse.conversation_id
|
|
143
|
-
);
|
|
144
|
-
expect(thirdResponse.phase).toBe('design');
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('should properly inject dependencies through factory pattern', async () => {
|
|
148
|
-
const result = await client.callTool('whats_next', {
|
|
149
|
-
user_input: 'test dependency injection',
|
|
150
|
-
});
|
|
151
|
-
const response = assertToolSuccess(result);
|
|
152
|
-
|
|
153
|
-
// Verify that components work together properly (dependency injection successful)
|
|
154
|
-
expect(response.instructions).toBeTruthy();
|
|
155
|
-
expect(response.plan_file_path).toBeTruthy();
|
|
156
|
-
|
|
157
|
-
// Components should be working together to produce complete responses
|
|
158
|
-
expect(response.phase).toBeTruthy();
|
|
159
|
-
expect(response.conversation_id).toBeTruthy();
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('should handle component errors gracefully', async () => {
|
|
163
|
-
// Test that factory-created components handle edge cases
|
|
164
|
-
const result = await client.callTool('whats_next', {
|
|
165
|
-
user_input: '', // Empty input to test robustness
|
|
166
|
-
});
|
|
167
|
-
const response = assertToolSuccess(result);
|
|
168
|
-
|
|
169
|
-
// Should still work with empty input
|
|
170
|
-
expect(response.phase).toBeTruthy();
|
|
171
|
-
expect(response.instructions).toBeTruthy();
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
describe('Backend Detection Integration', () => {
|
|
176
|
-
beforeEach(async () => {
|
|
177
|
-
const scenario = await createSuiteIsolatedE2EScenario({
|
|
178
|
-
suiteName: 'component-substitution-detection',
|
|
179
|
-
tempProjectFactory: createTempProjectWithDefaultStateMachine,
|
|
180
|
-
});
|
|
181
|
-
client = scenario.client;
|
|
182
|
-
cleanup = scenario.cleanup;
|
|
183
|
-
|
|
184
|
-
await initializeDevelopment(client);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
afterEach(async () => {
|
|
188
|
-
if (cleanup) {
|
|
189
|
-
await cleanup();
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('should detect task backend and create appropriate components', async () => {
|
|
194
|
-
const result = await client.callTool('whats_next', {
|
|
195
|
-
user_input: 'test backend detection',
|
|
196
|
-
});
|
|
197
|
-
const response = assertToolSuccess(result);
|
|
198
|
-
|
|
199
|
-
// Verify the factory correctly detected and created appropriate components
|
|
200
|
-
expect(response.phase).toBeTruthy();
|
|
201
|
-
expect(response.instructions).toBeTruthy();
|
|
202
|
-
|
|
203
|
-
// The response should indicate which components are being used
|
|
204
|
-
// (in practice, this would be markdown components since beads isn't available in tests)
|
|
205
|
-
expect(response.plan_file_path).toMatch(/\.md$/);
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
});
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for BeadsIntegration epic title formatting with filename
|
|
3
|
-
*
|
|
4
|
-
* Tests that epic titles include plan filenames correctly
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
8
|
-
import { BeadsIntegration } from '@codemcp/workflows-core';
|
|
9
|
-
|
|
10
|
-
// Mock execSync to avoid actual beads CLI calls
|
|
11
|
-
vi.mock('node:child_process', () => ({
|
|
12
|
-
execSync: vi.fn().mockReturnValue('✓ Created issue: test-epic-123'),
|
|
13
|
-
}));
|
|
14
|
-
|
|
15
|
-
describe('BeadsIntegration - Epic Title with Filename', () => {
|
|
16
|
-
let beadsIntegration: BeadsIntegration;
|
|
17
|
-
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
beadsIntegration = new BeadsIntegration('/test/project');
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('should include filename in epic title when provided', async () => {
|
|
23
|
-
const { execSync } = await import('node:child_process');
|
|
24
|
-
const mockExecSync = vi.mocked(execSync);
|
|
25
|
-
|
|
26
|
-
await beadsIntegration.createProjectEpic(
|
|
27
|
-
'my-project',
|
|
28
|
-
'epcc',
|
|
29
|
-
'Build an awesome feature',
|
|
30
|
-
'development-plan-feature-auth.md'
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
// Verify that execSync was called with the expected title format
|
|
34
|
-
expect(mockExecSync).toHaveBeenCalledWith(
|
|
35
|
-
expect.stringContaining(
|
|
36
|
-
'bd create "my-project: epcc (development-plan-feature-auth.md)"'
|
|
37
|
-
),
|
|
38
|
-
expect.any(Object)
|
|
39
|
-
);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('should use original title format when filename not provided', async () => {
|
|
43
|
-
const { execSync } = await import('node:child_process');
|
|
44
|
-
const mockExecSync = vi.mocked(execSync);
|
|
45
|
-
|
|
46
|
-
await beadsIntegration.createProjectEpic(
|
|
47
|
-
'my-project',
|
|
48
|
-
'epcc',
|
|
49
|
-
'Build an awesome feature'
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
// Verify that execSync was called with the original title format
|
|
53
|
-
expect(mockExecSync).toHaveBeenCalledWith(
|
|
54
|
-
expect.stringContaining('bd create "my-project: epcc"'),
|
|
55
|
-
expect.any(Object)
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
// Ensure it doesn't contain parentheses when no filename
|
|
59
|
-
const call = mockExecSync.mock.calls[0][0] as string;
|
|
60
|
-
expect(call).not.toContain('(');
|
|
61
|
-
expect(call).not.toContain(')');
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should handle various filename formats', async () => {
|
|
65
|
-
const { execSync } = await import('node:child_process');
|
|
66
|
-
const mockExecSync = vi.mocked(execSync);
|
|
67
|
-
|
|
68
|
-
const testCases = [
|
|
69
|
-
'development-plan-main.md',
|
|
70
|
-
'development-plan-feature-dashboard.md',
|
|
71
|
-
'development-plan-bugfix-123.md',
|
|
72
|
-
'plan.md',
|
|
73
|
-
];
|
|
74
|
-
|
|
75
|
-
for (const filename of testCases) {
|
|
76
|
-
mockExecSync.mockClear();
|
|
77
|
-
|
|
78
|
-
await beadsIntegration.createProjectEpic(
|
|
79
|
-
'test-project',
|
|
80
|
-
'waterfall',
|
|
81
|
-
undefined,
|
|
82
|
-
filename
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
expect(mockExecSync).toHaveBeenCalledWith(
|
|
86
|
-
expect.stringContaining(
|
|
87
|
-
`bd create "test-project: waterfall (${filename})"`
|
|
88
|
-
),
|
|
89
|
-
expect.any(Object)
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
});
|
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ServerComponentsFactory Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests the factory pattern implementation for component creation
|
|
5
|
-
* based on task backend configuration.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
9
|
-
import { ServerComponentsFactory } from '../../src/components/server-components-factory.js';
|
|
10
|
-
import {
|
|
11
|
-
TaskBackendManager,
|
|
12
|
-
type TaskBackendConfig,
|
|
13
|
-
} from '@codemcp/workflows-core';
|
|
14
|
-
|
|
15
|
-
// Mock TaskBackendManager
|
|
16
|
-
vi.mock('@codemcp/workflows-core', async () => {
|
|
17
|
-
const actual = await vi.importActual('@codemcp/workflows-core');
|
|
18
|
-
return {
|
|
19
|
-
...actual,
|
|
20
|
-
TaskBackendManager: {
|
|
21
|
-
detectTaskBackend: vi.fn(),
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe('ServerComponentsFactory', () => {
|
|
27
|
-
let factory: ServerComponentsFactory;
|
|
28
|
-
let mockDetectTaskBackend: ReturnType<typeof vi.fn>;
|
|
29
|
-
|
|
30
|
-
beforeEach(() => {
|
|
31
|
-
mockDetectTaskBackend = vi.mocked(TaskBackendManager.detectTaskBackend);
|
|
32
|
-
// Default to markdown backend
|
|
33
|
-
mockDetectTaskBackend.mockReturnValue({
|
|
34
|
-
backend: 'markdown',
|
|
35
|
-
isAvailable: true,
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
afterEach(() => {
|
|
40
|
-
vi.clearAllMocks();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe('Constructor', () => {
|
|
44
|
-
it('should use TaskBackendManager.detectTaskBackend() when no options provided', () => {
|
|
45
|
-
factory = new ServerComponentsFactory();
|
|
46
|
-
|
|
47
|
-
expect(mockDetectTaskBackend).toHaveBeenCalled();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should use provided task backend configuration', () => {
|
|
51
|
-
const customBackend: TaskBackendConfig = {
|
|
52
|
-
backend: 'beads',
|
|
53
|
-
isAvailable: true,
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
factory = new ServerComponentsFactory({ taskBackend: customBackend });
|
|
57
|
-
|
|
58
|
-
// Should not call detectTaskBackend when explicit config provided
|
|
59
|
-
expect(mockDetectTaskBackend).not.toHaveBeenCalled();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should handle undefined options gracefully', () => {
|
|
63
|
-
factory = new ServerComponentsFactory(undefined);
|
|
64
|
-
|
|
65
|
-
expect(mockDetectTaskBackend).toHaveBeenCalled();
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
describe('createPlanManager', () => {
|
|
70
|
-
it('should create default PlanManager for markdown backend', () => {
|
|
71
|
-
mockDetectTaskBackend.mockReturnValue({
|
|
72
|
-
backend: 'markdown',
|
|
73
|
-
isAvailable: true,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
factory = new ServerComponentsFactory();
|
|
77
|
-
const planManager = factory.createPlanManager();
|
|
78
|
-
|
|
79
|
-
expect(planManager).toBeDefined();
|
|
80
|
-
expect(planManager.constructor.name).toBe('PlanManager');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should create default PlanManager when beads backend unavailable', () => {
|
|
84
|
-
mockDetectTaskBackend.mockReturnValue({
|
|
85
|
-
backend: 'beads',
|
|
86
|
-
isAvailable: false,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
factory = new ServerComponentsFactory();
|
|
90
|
-
const planManager = factory.createPlanManager();
|
|
91
|
-
|
|
92
|
-
expect(planManager).toBeDefined();
|
|
93
|
-
expect(planManager.constructor.name).toBe('PlanManager');
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should create BeadsPlanManager for beads backend when available', () => {
|
|
97
|
-
// Now that BeadsPlanManager is implemented, test that it's created correctly
|
|
98
|
-
const beadsBackend: TaskBackendConfig = {
|
|
99
|
-
backend: 'beads',
|
|
100
|
-
isAvailable: true,
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
factory = new ServerComponentsFactory({ taskBackend: beadsBackend });
|
|
104
|
-
const planManager = factory.createPlanManager();
|
|
105
|
-
|
|
106
|
-
expect(planManager).toBeDefined();
|
|
107
|
-
expect(planManager.constructor.name).toBe('BeadsPlanManager');
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should create consistent instances across multiple calls', () => {
|
|
111
|
-
factory = new ServerComponentsFactory();
|
|
112
|
-
|
|
113
|
-
const planManager1 = factory.createPlanManager();
|
|
114
|
-
const planManager2 = factory.createPlanManager();
|
|
115
|
-
|
|
116
|
-
// Should create new instances (not singletons)
|
|
117
|
-
expect(planManager1).not.toBe(planManager2);
|
|
118
|
-
expect(planManager1.constructor.name).toBe(planManager2.constructor.name);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
describe('createInstructionGenerator', () => {
|
|
123
|
-
it('should create default InstructionGenerator for markdown backend', () => {
|
|
124
|
-
mockDetectTaskBackend.mockReturnValue({
|
|
125
|
-
backend: 'markdown',
|
|
126
|
-
isAvailable: true,
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
factory = new ServerComponentsFactory();
|
|
130
|
-
const instructionGenerator = factory.createInstructionGenerator();
|
|
131
|
-
|
|
132
|
-
expect(instructionGenerator).toBeDefined();
|
|
133
|
-
expect(instructionGenerator.constructor.name).toBe(
|
|
134
|
-
'InstructionGenerator'
|
|
135
|
-
);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('should create default InstructionGenerator when beads backend unavailable', () => {
|
|
139
|
-
mockDetectTaskBackend.mockReturnValue({
|
|
140
|
-
backend: 'beads',
|
|
141
|
-
isAvailable: false,
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
factory = new ServerComponentsFactory();
|
|
145
|
-
const instructionGenerator = factory.createInstructionGenerator();
|
|
146
|
-
|
|
147
|
-
expect(instructionGenerator).toBeDefined();
|
|
148
|
-
expect(instructionGenerator.constructor.name).toBe(
|
|
149
|
-
'InstructionGenerator'
|
|
150
|
-
);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('should create BeadsInstructionGenerator for beads backend when available', () => {
|
|
154
|
-
// Now that BeadsInstructionGenerator is implemented, test that it's created correctly
|
|
155
|
-
const beadsBackend: TaskBackendConfig = {
|
|
156
|
-
backend: 'beads',
|
|
157
|
-
isAvailable: true,
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
factory = new ServerComponentsFactory({ taskBackend: beadsBackend });
|
|
161
|
-
const instructionGenerator = factory.createInstructionGenerator();
|
|
162
|
-
|
|
163
|
-
expect(instructionGenerator).toBeDefined();
|
|
164
|
-
expect(instructionGenerator.constructor.name).toBe(
|
|
165
|
-
'BeadsInstructionGenerator'
|
|
166
|
-
);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it('should create InstructionGenerator with proper dependency injection', () => {
|
|
170
|
-
factory = new ServerComponentsFactory();
|
|
171
|
-
const instructionGenerator = factory.createInstructionGenerator();
|
|
172
|
-
|
|
173
|
-
expect(instructionGenerator).toBeDefined();
|
|
174
|
-
expect(instructionGenerator.constructor.name).toBe(
|
|
175
|
-
'InstructionGenerator'
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
// Verify the instance is functional (dependency was injected correctly)
|
|
179
|
-
// without checking internal implementation details
|
|
180
|
-
expect(typeof instructionGenerator.generateInstructions).toBe('function');
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('should create consistent instances across multiple calls', () => {
|
|
184
|
-
factory = new ServerComponentsFactory();
|
|
185
|
-
|
|
186
|
-
const instructionGenerator1 = factory.createInstructionGenerator();
|
|
187
|
-
const instructionGenerator2 = factory.createInstructionGenerator();
|
|
188
|
-
|
|
189
|
-
// Should create new instances (not singletons)
|
|
190
|
-
expect(instructionGenerator1).not.toBe(instructionGenerator2);
|
|
191
|
-
expect(instructionGenerator1.constructor.name).toBe(
|
|
192
|
-
instructionGenerator2.constructor.name
|
|
193
|
-
);
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
describe('getTaskBackend', () => {
|
|
198
|
-
it('should return the current task backend configuration', () => {
|
|
199
|
-
const customBackend: TaskBackendConfig = {
|
|
200
|
-
backend: 'beads',
|
|
201
|
-
isAvailable: true,
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
factory = new ServerComponentsFactory({ taskBackend: customBackend });
|
|
205
|
-
const taskBackend = factory.getTaskBackend();
|
|
206
|
-
|
|
207
|
-
expect(taskBackend).toEqual(customBackend);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('should return detected task backend when no explicit config provided', () => {
|
|
211
|
-
const detectedBackend: TaskBackendConfig = {
|
|
212
|
-
backend: 'markdown',
|
|
213
|
-
isAvailable: true,
|
|
214
|
-
};
|
|
215
|
-
mockDetectTaskBackend.mockReturnValue(detectedBackend);
|
|
216
|
-
|
|
217
|
-
factory = new ServerComponentsFactory();
|
|
218
|
-
const taskBackend = factory.getTaskBackend();
|
|
219
|
-
|
|
220
|
-
expect(taskBackend).toEqual(detectedBackend);
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
describe('Backend Integration', () => {
|
|
225
|
-
it('should handle unknown backend types gracefully', () => {
|
|
226
|
-
const unknownBackend: TaskBackendConfig = {
|
|
227
|
-
backend: 'beads' as TaskBackendConfig['backend'],
|
|
228
|
-
isAvailable: true,
|
|
229
|
-
};
|
|
230
|
-
// Simulate an unknown backend type for testing
|
|
231
|
-
(unknownBackend as unknown as { backend: string }).backend =
|
|
232
|
-
'unknown-backend';
|
|
233
|
-
|
|
234
|
-
factory = new ServerComponentsFactory({ taskBackend: unknownBackend });
|
|
235
|
-
|
|
236
|
-
// Should fall back to default implementations
|
|
237
|
-
const planManager = factory.createPlanManager();
|
|
238
|
-
const instructionGenerator = factory.createInstructionGenerator();
|
|
239
|
-
|
|
240
|
-
expect(planManager.constructor.name).toBe('PlanManager');
|
|
241
|
-
expect(instructionGenerator.constructor.name).toBe(
|
|
242
|
-
'InstructionGenerator'
|
|
243
|
-
);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it('should handle backend availability flag correctly', () => {
|
|
247
|
-
const unavailableBackend: TaskBackendConfig = {
|
|
248
|
-
backend: 'beads',
|
|
249
|
-
isAvailable: false,
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
factory = new ServerComponentsFactory({
|
|
253
|
-
taskBackend: unavailableBackend,
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
// Should fall back to default implementations when backend unavailable
|
|
257
|
-
const planManager = factory.createPlanManager();
|
|
258
|
-
const instructionGenerator = factory.createInstructionGenerator();
|
|
259
|
-
|
|
260
|
-
expect(planManager.constructor.name).toBe('PlanManager');
|
|
261
|
-
expect(instructionGenerator.constructor.name).toBe(
|
|
262
|
-
'InstructionGenerator'
|
|
263
|
-
);
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
describe('Future Extensibility', () => {
|
|
268
|
-
// These tests document the expected behavior once beads implementations are created
|
|
269
|
-
it.todo(
|
|
270
|
-
'should create BeadsPlanManager when beads backend is available and implemented'
|
|
271
|
-
);
|
|
272
|
-
it.todo(
|
|
273
|
-
'should create BeadsInstructionGenerator when beads backend is available and implemented'
|
|
274
|
-
);
|
|
275
|
-
it.todo(
|
|
276
|
-
'should support additional backends (github, linear) through same factory pattern'
|
|
277
|
-
);
|
|
278
|
-
});
|
|
279
|
-
});
|