-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrisk-engine.test.js
More file actions
205 lines (179 loc) · 7.39 KB
/
Copy pathrisk-engine.test.js
File metadata and controls
205 lines (179 loc) · 7.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/**
* Unit Tests for HeatShe Risk Engine
* Run with: node risk-engine.test.js
*/
// Load the risk engine
const RiskEngine = require('./risk-engine.js');
// Test utilities
let testsPassed = 0;
let testsFailed = 0;
function assert(condition, message) {
if (condition) {
testsPassed++;
console.log(`✓ ${message}`);
} else {
testsFailed++;
console.log(`✗ ${message}`);
}
}
function assertEquals(actual, expected, message) {
assert(actual === expected, `${message} (expected ${expected}, got ${actual})`);
}
function assertApprox(actual, expected, margin, message) {
const diff = Math.abs(actual - expected);
assert(diff <= margin, `${message} (expected ~${expected}, got ${actual}, margin ${margin})`);
}
// ─── TEST SUITE ───
console.log('\n🧪 HeatShe Risk Engine Test Suite\n');
// Test 1: Validation
console.log('📋 Validation Tests');
let result = RiskEngine.validate({
district: 50,
occupation: 75,
hours: 80,
peakSun: 90,
age: 30,
pregnancy: 0,
temperature: 5
});
assert(result.valid === true, 'Valid inputs should pass');
result = RiskEngine.validate({
district: 150, // Invalid
occupation: 75,
hours: 80,
peakSun: 90,
age: 30,
pregnancy: 0,
temperature: 5
});
assert(result.valid === false, 'Invalid district risk (>100) should fail');
result = RiskEngine.validate({
district: 50,
// Missing occupation
hours: 80,
peakSun: 90,
age: 30,
pregnancy: 0,
temperature: 5
});
assert(result.valid === false, 'Missing required field should fail');
// Test 2: Formula Calculation
console.log('\n🔢 Formula Calculation Tests');
result = RiskEngine.calculate({
district: 50,
occupation: 80,
hours: 60,
peakSun: 70,
age: 20,
pregnancy: 0,
temperature: 0
});
assert(result.valid === true, 'Calculation should succeed');
// Expected: (0.16*50 + 0.30*80 + 0.20*60 + 0.20*70 + 0.09*20 + 0.05*0) * 10 + 0
// = (8 + 24 + 12 + 14 + 1.8 + 0) * 10 + 0 = 59.8 * 10 + 0 = 598 / 10 ≈ 60
assertApprox(result.finalScore, 61, 2, 'Formula calculation should match expected value');
// Test 3: Risk Categories
console.log('\n🎯 Risk Category Tests');
result = RiskEngine.calculate({
district: 10, occupation: 20, hours: 15, peakSun: 10, age: 5, pregnancy: 0, temperature: -5
});
assertEquals(result.category.key, 'safe', 'Low score should be Safe');
result = RiskEngine.calculate({
district: 50, occupation: 50, hours: 50, peakSun: 50, age: 50, pregnancy: 0, temperature: 5
});
assertEquals(result.category.key, 'caution', 'Medium score should be Caution');
result = RiskEngine.calculate({
district: 70, occupation: 80, hours: 75, peakSun: 85, age: 60, pregnancy: 0, temperature: 10
});
assert(['high', 'critical'].includes(result.category.key), 'High score should be High Risk or Critical');
result = RiskEngine.calculate({
district: 100, occupation: 100, hours: 100, peakSun: 100, age: 100, pregnancy: 100, temperature: 20
});
assertEquals(result.category.key, 'critical', 'Maximum inputs should be Critical');
// Test 4: Temperature Impact
console.log('\n🌡️ Temperature Impact Tests');
result1 = RiskEngine.calculate({
district: 50, occupation: 50, hours: 50, peakSun: 50, age: 50, pregnancy: 0, temperature: 0
});
result2 = RiskEngine.calculate({
district: 50, occupation: 50, hours: 50, peakSun: 50, age: 50, pregnancy: 0, temperature: 10
});
assert(result2.finalScore > result1.finalScore, 'Higher temperature should increase risk');
assertEquals(result2.finalScore - result1.finalScore, 10, 'Temperature should add directly to score');
// Test 5: Top Factors Identification
console.log('\n🏆 Top Factors Identification Tests');
result = RiskEngine.calculate({
district: 20, occupation: 95, hours: 85, peakSun: 90, age: 10, pregnancy: 0, temperature: 0
});
assert(result.topFactors.length === 3, 'Should return top 3 factors');
assert(result.topFactors[0].name === 'occupation', 'Occupation should be top factor');
assert(result.topFactors[1].name === 'peakSun', 'Peak sun should be second');
assert(result.topFactors[2].name === 'hours', 'Hours should be third');
// Test 6: Pregnancy Impact
console.log('\n🤰 Pregnancy Status Impact Tests');
result1 = RiskEngine.calculate({
district: 50, occupation: 50, hours: 50, peakSun: 50, age: 35, pregnancy: 0, temperature: 0
});
result2 = RiskEngine.calculate({
district: 50, occupation: 50, hours: 50, peakSun: 50, age: 35, pregnancy: 100, temperature: 0
});
assert(result2.finalScore > result1.finalScore, 'Pregnancy should increase risk');
// Test 7: Age Impact
console.log('\n🎂 Age Factor Tests');
result1 = RiskEngine.calculate({
district: 50, occupation: 50, hours: 50, peakSun: 50, age: 20, pregnancy: 0, temperature: 0
});
result2 = RiskEngine.calculate({
district: 50, occupation: 50, hours: 50, peakSun: 50, age: 80, pregnancy: 0, temperature: 0
});
assert(result2.finalScore > result1.finalScore, 'Higher age should increase risk');
// Test 8: Explanation Generation
console.log('\n📝 Explanation Generation Tests');
result = RiskEngine.calculate({
district: 20, occupation: 20, hours: 20, peakSun: 20, age: 20, pregnancy: 0, temperature: 0
});
assert(result.explanation !== '', 'Should generate explanation');
assert(result.explanation.includes('LOW') || result.explanation.includes('Safe'), 'Safe score should mention low risk');
// Test 9: Multilingual Support
console.log('\n🌐 Multilingual Support Tests');
result_en = RiskEngine.calculate({
district: 50, occupation: 50, hours: 50, peakSun: 50, age: 50, pregnancy: 0, temperature: 0
}, 'en');
result_kn = RiskEngine.calculate({
district: 50, occupation: 50, hours: 50, peakSun: 50, age: 50, pregnancy: 0, temperature: 0
}, 'kn');
assert(result_en.explanation !== result_kn.explanation, 'English and Kannada explanations should differ');
assert(result_en.category.labelKn !== '', 'Kannada category label should be present');
// Test 10: Recommendations
console.log('\n💡 Recommendations Tests');
result = RiskEngine.calculate({
district: 100, occupation: 100, hours: 100, peakSun: 100, age: 100, pregnancy: 100, temperature: 20
});
assert(result.recommendations.length > 0, 'Should generate recommendations');
assert(result.recommendations[0].action !== '', 'Recommendations should have action text');
// Test 11: Boundary Cases
console.log('\n🚨 Boundary Case Tests');
result = RiskEngine.calculate({
district: 0, occupation: 0, hours: 0, peakSun: 0, age: 0, pregnancy: 0, temperature: -10
});
assert(result.finalScore >= 0, 'Score should never be negative');
result = RiskEngine.calculate({
district: 100, occupation: 100, hours: 100, peakSun: 100, age: 100, pregnancy: 100, temperature: 40
});
assert(result.finalScore <= 100, 'Score should never exceed 100');
// Test 12: Weights
console.log('\n⚖️ Weights Verification Tests');
const weights = RiskEngine.getWeights();
const weightSum = Object.values(weights).reduce((a, b) => a + b, 0);
assertApprox(weightSum, 1.0, 0.01, 'Weights should sum to 1.0');
// ─── TEST SUMMARY ───
console.log(`\n${'═'.repeat(50)}`);
console.log(`✅ Tests Passed: ${testsPassed}`);
console.log(`❌ Tests Failed: ${testsFailed}`);
console.log(`📊 Total: ${testsPassed + testsFailed}`);
console.log(`${'═'.repeat(50)}\n`);
if (testsFailed === 0) {
console.log('🎉 All tests passed! Risk engine is production-ready.\n');
} else {
console.log(`⚠️ ${testsFailed} test(s) failed. Please review.\n`);
}