-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontract-validation.test.js
More file actions
63 lines (56 loc) · 2.45 KB
/
contract-validation.test.js
File metadata and controls
63 lines (56 loc) · 2.45 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
// contract-validation.test.js
// Deterministic contract validation tests for canaries-with-teeth
// Node.js, no external deps. Fails loudly on contract violation.
const fs = require('fs');
const path = require('path');
function fail(msg) {
console.error('CONTRACT VIOLATION:', msg);
process.exit(1);
}
function checkField(obj, field, type) {
if (!(field in obj)) fail(`Missing required field: ${field}`);
if (typeof obj[field] !== type) fail(`Field ${field} must be type ${type}`);
}
function checkRange(val, min, max, field) {
if (val < min || val > max) fail(`Field ${field} out of range: ${val}`);
}
// Validate function-health.contract.md
(function validateFunctionHealth() {
const contract = require('./contracts/function-health.contract.md.json');
// Usar latencyMsP95 según contrato actual
checkField(contract, 'latencyMsP95', 'number');
checkRange(contract.latencyMsP95, 0, 60000, 'latencyMsP95');
// Calcular errorRate si no está presente
let errorRate = contract.errorRate;
if (typeof errorRate !== 'number') {
if ('errorCount' in contract && 'invocationCount' in contract && contract.invocationCount > 0) {
errorRate = contract.errorCount / contract.invocationCount;
} else {
fail('Missing errorRate or fields to compute it');
}
}
checkRange(errorRate, 0, 1, 'errorRate');
checkField(contract, 'coldStartMs', 'number');
checkRange(contract.coldStartMs, 0, 60000, 'coldStartMs');
checkField(contract, 'logicalDrift', 'number');
checkRange(contract.logicalDrift, 0, 1, 'logicalDrift');
checkField(contract, 'healthStatus', 'string');
if (!['OK','WARN','BLOCK'].includes(contract.healthStatus)) fail('Invalid healthStatus');
})();
// Validate friction-budget.contract.md
(function validateFrictionBudget() {
const contract = require('./contracts/friction-budget.contract.md.json');
checkField(contract, 'frictionTypes', 'object');
if (!Array.isArray(contract.frictionTypes)) fail('frictionTypes must be array');
contract.frictionTypes.forEach(ft => {
checkField(ft, 'type', 'string');
checkField(ft, 'weight', 'number');
checkRange(ft.weight, 0, 1, 'weight');
});
checkField(contract, 'score', 'number');
checkRange(contract.score, 0, 100, 'score');
checkField(contract, 'budget', 'number');
checkRange(contract.budget, 0, 100, 'budget');
if (contract.score > contract.budget && !contract.reason) fail('Missing reason for over-budget');
})();
console.log('All contract validations passed.');