@iservu-inc/adf-cli 0.4.36 → 0.5.1

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,322 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const KnowledgeGraph = require('../lib/analysis/knowledge-graph');
5
+
6
+ describe('KnowledgeGraph', () => {
7
+ let tempDir;
8
+ let kg;
9
+
10
+ beforeEach(async () => {
11
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'kg-test-'));
12
+ kg = new KnowledgeGraph(tempDir);
13
+ });
14
+
15
+ afterEach(async () => {
16
+ await fs.remove(tempDir);
17
+ });
18
+
19
+ describe('add', () => {
20
+ it('should add new information', () => {
21
+ const extractedInfo = [
22
+ {
23
+ type: 'tech_stack',
24
+ content: 'React and Node.js',
25
+ confidence: 90,
26
+ source: 'tech-question'
27
+ }
28
+ ];
29
+
30
+ kg.add(extractedInfo);
31
+
32
+ expect(kg.has('tech_stack', 70)).toBe(true);
33
+ expect(kg.getConfidence('tech_stack')).toBe(90);
34
+ });
35
+
36
+ it('should update existing information with higher confidence', () => {
37
+ const info1 = [
38
+ { type: 'tech_stack', content: 'React for frontend development', confidence: 70, source: 'q1' }
39
+ ];
40
+ const info2 = [
41
+ { type: 'tech_stack', content: 'React for the frontend', confidence: 90, source: 'q2' }
42
+ ];
43
+
44
+ kg.add(info1);
45
+ kg.add(info2);
46
+
47
+ expect(kg.getConfidence('tech_stack')).toBe(90);
48
+ const items = kg.get('tech_stack');
49
+ // Should have merged or both sources tracked
50
+ const allSources = items.flatMap(i => i.sources);
51
+ expect(allSources).toContain('q1');
52
+ expect(allSources).toContain('q2');
53
+ });
54
+
55
+ it('should not replace existing information with lower confidence', () => {
56
+ const info1 = [
57
+ { type: 'tech_stack', content: 'React and Node', confidence: 90, source: 'q1' }
58
+ ];
59
+ const info2 = [
60
+ { type: 'tech_stack', content: 'React', confidence: 70, source: 'q2' }
61
+ ];
62
+
63
+ kg.add(info1);
64
+ kg.add(info2);
65
+
66
+ expect(kg.getConfidence('tech_stack')).toBe(90);
67
+ const items = kg.get('tech_stack');
68
+ expect(items[0].content).toContain('Node');
69
+ });
70
+
71
+ it('should add dissimilar information as separate items', () => {
72
+ const info1 = [
73
+ { type: 'features', content: 'User authentication', confidence: 85, source: 'q1' }
74
+ ];
75
+ const info2 = [
76
+ { type: 'features', content: 'Data export to CSV', confidence: 80, source: 'q2' }
77
+ ];
78
+
79
+ kg.add(info1);
80
+ kg.add(info2);
81
+
82
+ const items = kg.get('features');
83
+ expect(items).toHaveLength(2);
84
+ });
85
+
86
+ it('should merge similar information', () => {
87
+ const info1 = [
88
+ { type: 'tech_stack', content: 'React for frontend', confidence: 85, source: 'q1' }
89
+ ];
90
+ const info2 = [
91
+ { type: 'tech_stack', content: 'React for the frontend', confidence: 80, source: 'q2' }
92
+ ];
93
+
94
+ kg.add(info1);
95
+ kg.add(info2);
96
+
97
+ const items = kg.get('tech_stack');
98
+ // Should merge because very similar
99
+ expect(items.length).toBeLessThanOrEqual(2);
100
+ });
101
+ });
102
+
103
+ describe('has', () => {
104
+ it('should return true if information exists with sufficient confidence', () => {
105
+ const info = [
106
+ { type: 'project_goal', content: 'Build a CRM', confidence: 85, source: 'q1' }
107
+ ];
108
+
109
+ kg.add(info);
110
+
111
+ expect(kg.has('project_goal', 80)).toBe(true);
112
+ expect(kg.has('project_goal', 90)).toBe(false);
113
+ });
114
+
115
+ it('should return false for missing information types', () => {
116
+ expect(kg.has('tech_stack')).toBe(false);
117
+ });
118
+
119
+ it('should use default minConfidence of 70', () => {
120
+ const info = [
121
+ { type: 'platform', content: 'Web', confidence: 75, source: 'q1' }
122
+ ];
123
+
124
+ kg.add(info);
125
+
126
+ expect(kg.has('platform')).toBe(true);
127
+ });
128
+ });
129
+
130
+ describe('get', () => {
131
+ it('should return all items of a type', () => {
132
+ const info = [
133
+ { type: 'features', content: 'Auth', confidence: 85, source: 'q1' },
134
+ { type: 'features', content: 'Export', confidence: 80, source: 'q2' }
135
+ ];
136
+
137
+ kg.add(info);
138
+
139
+ const items = kg.get('features');
140
+ expect(items).toHaveLength(2);
141
+ });
142
+
143
+ it('should return empty array for non-existent type', () => {
144
+ const items = kg.get('nonexistent');
145
+ expect(items).toEqual([]);
146
+ });
147
+ });
148
+
149
+ describe('getConfidence', () => {
150
+ it('should return highest confidence for a type', () => {
151
+ const info = [
152
+ { type: 'tech_stack', content: 'React', confidence: 80, source: 'q1' },
153
+ { type: 'tech_stack', content: 'Node', confidence: 90, source: 'q2' }
154
+ ];
155
+
156
+ kg.add(info);
157
+
158
+ expect(kg.getConfidence('tech_stack')).toBe(90);
159
+ });
160
+
161
+ it('should return 0 for non-existent type', () => {
162
+ expect(kg.getConfidence('nonexistent')).toBe(0);
163
+ });
164
+ });
165
+
166
+ describe('getSummary', () => {
167
+ it('should return summary of all knowledge', () => {
168
+ const info = [
169
+ { type: 'tech_stack', content: 'React', confidence: 90, source: 'q1' },
170
+ { type: 'architecture', content: 'SPA', confidence: 85, source: 'q2' },
171
+ { type: 'platform', content: 'Web', confidence: 80, source: 'q3' }
172
+ ];
173
+
174
+ kg.add(info);
175
+
176
+ const summary = kg.getSummary();
177
+
178
+ expect(summary.tech_stack.maxConfidence).toBe(90);
179
+ expect(summary.architecture.maxConfidence).toBe(85);
180
+ expect(summary.platform.maxConfidence).toBe(80);
181
+ expect(summary.tech_stack.sources).toContain('q1');
182
+ });
183
+ });
184
+
185
+ describe('getDisplaySummary', () => {
186
+ it('should return displayable summary with icons', () => {
187
+ const info = [
188
+ { type: 'tech_stack', content: 'React', confidence: 90, source: 'q1' },
189
+ { type: 'project_goal', content: 'Build CRM', confidence: 95, source: 'q2' }
190
+ ];
191
+
192
+ kg.add(info);
193
+
194
+ const display = kg.getDisplaySummary();
195
+
196
+ expect(display.length).toBe(2);
197
+ expect(display[0].confidence).toBe(95); // Sorted by confidence
198
+ expect(display[0].icon).toBe('🎯');
199
+ expect(display[1].icon).toBe('🔧');
200
+ });
201
+
202
+ it('should filter out low confidence items', () => {
203
+ const info = [
204
+ { type: 'tech_stack', content: 'React', confidence: 90, source: 'q1' },
205
+ { type: 'timeline', content: 'Soon', confidence: 50, source: 'q2' }
206
+ ];
207
+
208
+ kg.add(info);
209
+
210
+ const display = kg.getDisplaySummary();
211
+
212
+ // Should only include tech_stack (90% > 60%)
213
+ expect(display).toHaveLength(1);
214
+ expect(display[0].type).toBe('tech stack');
215
+ });
216
+ });
217
+
218
+ describe('calculateSimilarity', () => {
219
+ it('should calculate high similarity for similar texts', () => {
220
+ const similarity = kg.calculateSimilarity(
221
+ 'React for frontend development',
222
+ 'React for the frontend'
223
+ );
224
+
225
+ expect(similarity).toBeGreaterThan(0.5);
226
+ });
227
+
228
+ it('should calculate low similarity for different texts', () => {
229
+ const similarity = kg.calculateSimilarity(
230
+ 'React for frontend',
231
+ 'PostgreSQL database'
232
+ );
233
+
234
+ expect(similarity).toBeLessThan(0.5);
235
+ });
236
+
237
+ it('should return 1 for identical texts', () => {
238
+ const similarity = kg.calculateSimilarity(
239
+ 'same text',
240
+ 'same text'
241
+ );
242
+
243
+ expect(similarity).toBe(1);
244
+ });
245
+ });
246
+
247
+ describe('save and load', () => {
248
+ it('should save knowledge graph to disk', async () => {
249
+ const info = [
250
+ { type: 'tech_stack', content: 'React', confidence: 90, source: 'q1' }
251
+ ];
252
+
253
+ kg.add(info);
254
+ await kg.save();
255
+
256
+ const filePath = path.join(tempDir, '_knowledge_graph.json');
257
+ expect(await fs.pathExists(filePath)).toBe(true);
258
+
259
+ const data = await fs.readJson(filePath);
260
+ expect(data.knowledge).toBeDefined();
261
+ expect(data.knowledge.length).toBeGreaterThan(0);
262
+ });
263
+
264
+ it('should load knowledge graph from disk', async () => {
265
+ const info = [
266
+ { type: 'tech_stack', content: 'React', confidence: 90, source: 'q1' },
267
+ { type: 'platform', content: 'Web', confidence: 85, source: 'q2' }
268
+ ];
269
+
270
+ kg.add(info);
271
+ await kg.save();
272
+
273
+ // Create new instance and load
274
+ const kg2 = new KnowledgeGraph(tempDir);
275
+ const loaded = await kg2.load();
276
+
277
+ expect(loaded).toBe(true);
278
+ expect(kg2.has('tech_stack')).toBe(true);
279
+ expect(kg2.has('platform')).toBe(true);
280
+ expect(kg2.getConfidence('tech_stack')).toBe(90);
281
+ });
282
+
283
+ it('should return false when loading from non-existent file', async () => {
284
+ const loaded = await kg.load();
285
+ expect(loaded).toBe(false);
286
+ });
287
+ });
288
+
289
+ describe('getStats', () => {
290
+ it('should return statistics about knowledge graph', () => {
291
+ const info = [
292
+ { type: 'tech_stack', content: 'React', confidence: 90, source: 'q1' },
293
+ { type: 'tech_stack', content: 'Node', confidence: 85, source: 'q2' },
294
+ { type: 'platform', content: 'Web', confidence: 75, source: 'q3' },
295
+ { type: 'architecture', content: 'SPA', confidence: 80, source: 'q4' }
296
+ ];
297
+
298
+ kg.add(info);
299
+
300
+ const stats = kg.getStats();
301
+
302
+ expect(stats.totalItems).toBe(4);
303
+ expect(stats.highConfidenceItems).toBe(3); // 90, 85, 80 are >= 80
304
+ expect(stats.types).toBe(3); // tech_stack, platform, architecture
305
+ expect(stats.typeList).toContain('tech_stack');
306
+ });
307
+ });
308
+
309
+ describe('clear', () => {
310
+ it('should clear all knowledge', () => {
311
+ const info = [
312
+ { type: 'tech_stack', content: 'React', confidence: 90, source: 'q1' }
313
+ ];
314
+
315
+ kg.add(info);
316
+ expect(kg.has('tech_stack')).toBe(true);
317
+
318
+ kg.clear();
319
+ expect(kg.has('tech_stack')).toBe(false);
320
+ });
321
+ });
322
+ });
@@ -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
+ });