@iservu-inc/adf-cli 0.4.35 → 0.5.0

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.
@@ -0,0 +1,342 @@
1
+ const QuestionMapper = require('../lib/analysis/question-mapper');
2
+ const KnowledgeGraph = require('../lib/analysis/knowledge-graph');
3
+ const fs = require('fs-extra');
4
+ const path = require('path');
5
+ const os = require('os');
6
+
7
+ describe('QuestionMapper', () => {
8
+ let mapper;
9
+ let kg;
10
+ let tempDir;
11
+
12
+ beforeEach(async () => {
13
+ mapper = new QuestionMapper();
14
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'qm-test-'));
15
+ kg = new KnowledgeGraph(tempDir);
16
+ });
17
+
18
+ afterEach(async () => {
19
+ await fs.remove(tempDir);
20
+ });
21
+
22
+ describe('mapQuestion', () => {
23
+ it('should map tech stack question correctly', () => {
24
+ const question = {
25
+ id: 'tech-stack',
26
+ text: 'What tech stack will you use?'
27
+ };
28
+
29
+ const mapping = mapper.mapQuestion(question);
30
+
31
+ expect(mapping.types).toContain('tech_stack');
32
+ expect(mapping.priority).toBeLessThanOrEqual(2); // High priority
33
+ expect(mapping.confidence).toBeGreaterThan(70);
34
+ });
35
+
36
+ it('should map goal question correctly', () => {
37
+ const question = {
38
+ id: 'project-goal',
39
+ text: 'What are you building?'
40
+ };
41
+
42
+ const mapping = mapper.mapQuestion(question);
43
+
44
+ expect(mapping.types).toContain('project_goal');
45
+ expect(mapping.priority).toBe(1); // Highest priority
46
+ expect(mapping.confidence).toBeGreaterThan(70);
47
+ });
48
+
49
+ it('should map user question correctly', () => {
50
+ const question = {
51
+ id: 'target-users',
52
+ text: 'Who are your target users?'
53
+ };
54
+
55
+ const mapping = mapper.mapQuestion(question);
56
+
57
+ expect(mapping.types).toContain('target_users');
58
+ expect(mapping.confidence).toBeGreaterThan(70);
59
+ });
60
+
61
+ it('should map architecture question correctly', () => {
62
+ const question = {
63
+ id: 'architecture',
64
+ text: 'How will you structure your application?'
65
+ };
66
+
67
+ const mapping = mapper.mapQuestion(question);
68
+
69
+ expect(mapping.types).toContain('architecture');
70
+ expect(mapping.confidence).toBeGreaterThan(70);
71
+ });
72
+
73
+ it('should map platform question correctly', () => {
74
+ const question = {
75
+ id: 'platform',
76
+ text: 'What platform will this run on?'
77
+ };
78
+
79
+ const mapping = mapper.mapQuestion(question);
80
+
81
+ expect(mapping.types).toContain('platform');
82
+ expect(mapping.confidence).toBeGreaterThan(70);
83
+ });
84
+
85
+ it('should return default mapping for unrecognized questions', () => {
86
+ const question = {
87
+ id: 'random',
88
+ text: 'Something totally unrelated?'
89
+ };
90
+
91
+ const mapping = mapper.mapQuestion(question);
92
+
93
+ expect(mapping.types.length).toBeGreaterThan(0); // Has some type
94
+ expect(mapping.confidence).toBeGreaterThan(0); // Has some confidence
95
+ });
96
+
97
+ it('should map multiple information types for comprehensive questions', () => {
98
+ const question = {
99
+ id: 'goal-and-tech',
100
+ text: 'What are you building and what technology will you use?'
101
+ };
102
+
103
+ const mapping = mapper.mapQuestion(question);
104
+
105
+ expect(mapping.types).toContain('project_goal');
106
+ expect(mapping.types).toContain('tech_stack');
107
+ expect(mapping.types.length).toBeGreaterThan(1);
108
+ });
109
+ });
110
+
111
+ describe('canSkipQuestion', () => {
112
+ it('should allow skipping when all information types are satisfied', () => {
113
+ const question = {
114
+ id: 'tech-stack',
115
+ text: 'What tech stack will you use?'
116
+ };
117
+
118
+ // Add tech stack knowledge
119
+ kg.add([
120
+ { type: 'tech_stack', content: 'React and Node.js', confidence: 90, source: 'prev-q' }
121
+ ]);
122
+
123
+ const result = mapper.canSkipQuestion(question, kg, 70);
124
+
125
+ expect(result.canSkip).toBe(true);
126
+ expect(result.reason).toContain('tech stack');
127
+ expect(result.satisfiedTypes).toHaveLength(1);
128
+ expect(result.missingTypes).toHaveLength(0);
129
+ });
130
+
131
+ it('should not skip when confidence is below threshold', () => {
132
+ const question = {
133
+ id: 'tech-stack',
134
+ text: 'What tech stack?'
135
+ };
136
+
137
+ kg.add([
138
+ { type: 'tech_stack', content: 'React', confidence: 60, source: 'prev-q' }
139
+ ]);
140
+
141
+ const result = mapper.canSkipQuestion(question, kg, 70);
142
+
143
+ expect(result.canSkip).toBe(false);
144
+ });
145
+
146
+ it('should not skip when information is missing', () => {
147
+ const question = {
148
+ id: 'tech-stack',
149
+ text: 'What tech stack will you use?'
150
+ };
151
+
152
+ // No knowledge added
153
+
154
+ const result = mapper.canSkipQuestion(question, kg, 70);
155
+
156
+ expect(result.canSkip).toBe(false);
157
+ expect(result.satisfiedTypes).toHaveLength(0);
158
+ expect(result.missingTypes.length).toBeGreaterThan(0);
159
+ });
160
+
161
+ it('should handle partial satisfaction', () => {
162
+ const question = {
163
+ id: 'goal-and-tech',
164
+ text: 'What are you building and what technology?'
165
+ };
166
+
167
+ // Only have goal, missing tech
168
+ kg.add([
169
+ { type: 'project_goal', content: 'Building a CRM', confidence: 90, source: 'prev-q' }
170
+ ]);
171
+
172
+ const result = mapper.canSkipQuestion(question, kg, 70);
173
+
174
+ expect(result.canSkip).toBe(false);
175
+ expect(result.satisfiedTypes.length).toBeGreaterThan(0);
176
+ expect(result.missingTypes.length).toBeGreaterThan(0);
177
+ expect(result.reason).toContain('Partial');
178
+ });
179
+
180
+ it('should respect custom minConfidence threshold', () => {
181
+ const question = {
182
+ id: 'tech-stack',
183
+ text: 'What tech stack?'
184
+ };
185
+
186
+ kg.add([
187
+ { type: 'tech_stack', content: 'React', confidence: 75, source: 'prev-q' }
188
+ ]);
189
+
190
+ // Should skip with 70 threshold
191
+ expect(mapper.canSkipQuestion(question, kg, 70).canSkip).toBe(true);
192
+
193
+ // Should not skip with 80 threshold
194
+ expect(mapper.canSkipQuestion(question, kg, 80).canSkip).toBe(false);
195
+ });
196
+ });
197
+
198
+ describe('reorderQuestions', () => {
199
+ it('should prioritize questions with missing information', () => {
200
+ const questions = [
201
+ { id: 'tech-stack', text: 'What tech stack?' },
202
+ { id: 'timeline', text: 'What is the timeline?' },
203
+ { id: 'platform', text: 'What platform?' }
204
+ ];
205
+
206
+ // Already have tech stack
207
+ kg.add([
208
+ { type: 'tech_stack', content: 'React', confidence: 90, source: 'prev' }
209
+ ]);
210
+
211
+ const reordered = mapper.reorderQuestions(questions, kg);
212
+
213
+ // Questions without satisfied info should come first
214
+ expect(reordered[0].question.id).not.toBe('tech-stack');
215
+ expect(reordered[2].question.id).toBe('tech-stack'); // Should be last
216
+ expect(reordered[2].relevanceScore).toBe(0); // Can skip
217
+ });
218
+
219
+ it('should assign high scores to unsatisfied questions', () => {
220
+ const questions = [
221
+ { id: 'tech-stack', text: 'What tech stack?' }
222
+ ];
223
+
224
+ const reordered = mapper.reorderQuestions(questions, kg);
225
+
226
+ expect(reordered[0].relevanceScore).toBe(100);
227
+ });
228
+
229
+ it('should assign zero score to fully satisfied questions', () => {
230
+ const questions = [
231
+ { id: 'tech-stack', text: 'What tech stack?' }
232
+ ];
233
+
234
+ kg.add([
235
+ { type: 'tech_stack', content: 'React', confidence: 90, source: 'prev' },
236
+ { type: 'architecture', content: 'SPA', confidence: 85, source: 'prev' }
237
+ ]);
238
+
239
+ const reordered = mapper.reorderQuestions(questions, kg);
240
+
241
+ expect(reordered[0].relevanceScore).toBe(0);
242
+ expect(reordered[0].skipInfo.canSkip).toBe(true);
243
+ });
244
+
245
+ it('should boost high-priority questions', () => {
246
+ const questions = [
247
+ { id: 'timeline', text: 'Timeline?' }, // Priority 4
248
+ { id: 'goal', text: 'What building?' } // Priority 1
249
+ ];
250
+
251
+ // Add some knowledge so we can see priority effects
252
+ kg.add([
253
+ { type: 'timeline', content: '2 weeks', confidence: 60, source: 'prev' }
254
+ ]);
255
+
256
+ const reordered = mapper.reorderQuestions(questions, kg);
257
+
258
+ // Goal question should have higher score due to priority boost
259
+ const goalItem = reordered.find(r => r.question.id === 'goal');
260
+ const timelineItem = reordered.find(r => r.question.id === 'timeline');
261
+
262
+ expect(goalItem.relevanceScore).toBeGreaterThanOrEqual(timelineItem.relevanceScore);
263
+ });
264
+
265
+ it('should handle partially satisfied questions', () => {
266
+ const questions = [
267
+ { id: 'goal', text: 'What building?' },
268
+ { id: 'tech', text: 'What tech?' }
269
+ ];
270
+
271
+ // Partial knowledge - only have goal
272
+ kg.add([
273
+ { type: 'project_goal', content: 'CRM', confidence: 90, source: 'prev' }
274
+ ]);
275
+
276
+ const reordered = mapper.reorderQuestions(questions, kg);
277
+
278
+ // Tech should have full score (100), goal should be skippable (0)
279
+ const techItem = reordered.find(r => r.question.id === 'tech');
280
+ const goalItem = reordered.find(r => r.question.id === 'goal');
281
+
282
+ expect(techItem.relevanceScore).toBeGreaterThan(goalItem.relevanceScore);
283
+ });
284
+ });
285
+
286
+ describe('getStats', () => {
287
+ it('should calculate statistics correctly', () => {
288
+ const questions = [
289
+ { id: 'tech-stack', text: 'What tech stack?' },
290
+ { id: 'platform', text: 'What platform?' },
291
+ { id: 'timeline', text: 'Timeline?' },
292
+ { id: 'users', text: 'Who are users?' }
293
+ ];
294
+
295
+ // Add some knowledge
296
+ kg.add([
297
+ { type: 'tech_stack', content: 'React', confidence: 90, source: 'prev' },
298
+ { type: 'platform', content: 'Web', confidence: 85, source: 'prev' }
299
+ ]);
300
+
301
+ const stats = mapper.getStats(questions, kg);
302
+
303
+ expect(stats.total).toBe(4);
304
+ expect(stats.canSkip).toBeGreaterThanOrEqual(2); // tech_stack and platform
305
+ expect(stats.needed).toBeGreaterThan(0);
306
+ expect(stats.estimatedTimeSaved).toBeGreaterThan(0);
307
+ });
308
+
309
+ it('should handle empty knowledge graph', () => {
310
+ const questions = [
311
+ { id: 'tech-stack', text: 'What tech stack?' },
312
+ { id: 'platform', text: 'What platform?' }
313
+ ];
314
+
315
+ const stats = mapper.getStats(questions, kg);
316
+
317
+ expect(stats.total).toBe(2);
318
+ expect(stats.canSkip).toBe(0);
319
+ expect(stats.needed).toBe(2);
320
+ expect(stats.estimatedTimeSaved).toBe(0);
321
+ });
322
+
323
+ it('should estimate time saved correctly', () => {
324
+ const questions = [
325
+ { id: 'tech-stack', text: 'What tech stack?' },
326
+ { id: 'platform', text: 'What platform?' },
327
+ { id: 'timeline', text: 'What is the timeline?' }
328
+ ];
329
+
330
+ // Can skip 2 questions
331
+ kg.add([
332
+ { type: 'tech_stack', content: 'React', confidence: 90, source: 'prev' },
333
+ { type: 'platform', content: 'Web', confidence: 90, source: 'prev' }
334
+ ]);
335
+
336
+ const stats = mapper.getStats(questions, kg);
337
+
338
+ // If 2 can be skipped, time saved should be around 3 minutes (1.5 * 2)
339
+ expect(stats.estimatedTimeSaved).toBeGreaterThanOrEqual(1.5);
340
+ });
341
+ });
342
+ });