Files
boc/SIL/validation/robustness-test.mjs
T
Bernt 05ed037fe8 pilot.landvex.com: HTTPS + Full Stack Verified
- DNS: pilot.landvex.com -> 16.170.83.169
- TLS: Let's Encrypt certificate (expires 2026-09-30)
- Nginx: reverse proxy with SSL termination
- API: https://pilot.landvex.com/api/v1/missions
- UI: https://pilot.landvex.com/
- Upload: POST /api/v1/missions/import (multipart/form-data)

Verified:
 https://pilot.landvex.com/health
 https://pilot.landvex.com/version
 https://pilot.landvex.com/api/v1/missions (list)
 https://pilot.landvex.com/api/v1/missions/:id (get)
 POST /api/v1/missions/import (video upload)
 UI loads with title 'LandveX Intelligence Lab'

Next: Pilot 001 — Break the system!
2026-07-02 17:34:19 +00:00

402 lines
12 KiB
JavaScript

#!/usr/bin/env node
// ═══════════════════════════════════════════════════════════════════════════
// SIL Validation — Fråga 3: Hur robust är SIL mot förändring?
// Simulerar refactoring och mäter degradation
// ═══════════════════════════════════════════════════════════════════════════
import { execSync } from 'child_process';
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
import { createHash } from 'crypto';
const TEST_DIR = '/home/bernt/.openclaw/workspace/SIL/validation/robustness-tests';
const RESULTS_LOG = '/home/bernt/.openclaw/workspace/SIL/validation/robustness-results.jsonl';
class RobustnessTester {
constructor(repoPath) {
this.repoPath = repoPath;
if (!existsSync(TEST_DIR)) {
mkdirSync(TEST_DIR, { recursive: true });
}
}
/**
* Kör alla robusthetstester
*/
runAllTests() {
console.log('🧪 Kör robusthetstester...\n');
const tests = [
{ name: 'rename_service', fn: this.testRenameService.bind(this) },
{ name: 'move_code', fn: this.testMoveCode.bind(this) },
{ name: 'split_module', fn: this.testSplitModule.bind(this) },
{ name: 'add_dependency', fn: this.testAddDependency.bind(this) },
{ name: 'change_structure', fn: this.testChangeStructure.bind(this) },
{ name: 'introduce_violation', fn: this.testIntroduceViolation.bind(this) }
];
const results = [];
for (const test of tests) {
console.log(`🔍 ${test.name}...`);
try {
const result = test.fn();
results.push({ name: test.name, ...result });
console.log(` ${result.passed ? '✅' : '❌'} Degradation: ${result.degradation}`);
} catch (e) {
results.push({ name: test.name, error: e.message, passed: false });
console.log(` ❌ Fel: ${e.message}`);
}
}
const report = this.generateReport(results);
this.saveResults(results);
this.printReport(report);
return report;
}
/**
* Test 1: Byt namn på en tjänst
*/
testRenameService() {
// Simulera: byt namn på "wallet" till "payment"
const before = this.runAnalysis();
// Simulerad förändring: ändra i grafen
const modifiedGraph = this.simulateRename('wallet', 'payment');
const after = this.runAnalysisWithGraph(modifiedGraph);
return this.compareResults(before, after, 'rename_service');
}
/**
* Test 2: Flytta kod mellan paket
*/
testMoveCode() {
const before = this.runAnalysis();
// Simulera: flytta auth-kod till nytt paket
const modifiedGraph = this.simulateMove('auth', 'auth-v2');
const after = this.runAnalysisWithGraph(modifiedGraph);
return this.compareResults(before, after, 'move_code');
}
/**
* Test 3: Dela upp modul
*/
testSplitModule() {
const before = this.runAnalysis();
// Simulera: dela upp mission i mission-core och mission-api
const modifiedGraph = this.simulateSplit('mission', ['mission-core', 'mission-api']);
const after = this.runAnalysisWithGraph(modifiedGraph);
return this.compareResults(before, after, 'split_module');
}
/**
* Test 4: Lägg till nytt beroende
*/
testAddDependency() {
const before = this.runAnalysis();
// Simulera: lägg till Redis-beroende
const modifiedGraph = this.simulateAddDependency('wallet', 'redis');
const after = this.runAnalysisWithGraph(modifiedGraph);
return this.compareResults(before, after, 'add_dependency');
}
/**
* Test 5: Ändra katalogstruktur
*/
testChangeStructure() {
const before = this.runAnalysis();
// Simulera: flytta alla services till src/services/
const modifiedGraph = this.simulateRestructure();
const after = this.runAnalysisWithGraph(modifiedGraph);
return this.compareResults(before, after, 'change_structure');
}
/**
* Test 6: Introducera avsiktlig arkitekturavvikelse
*/
testIntroduceViolation() {
const before = this.runAnalysis();
// Simulera: lägg till direkt DB-anrop i UI
const modifiedGraph = this.simulateViolation();
const after = this.runAnalysisWithGraph(modifiedGraph);
return this.compareResults(before, after, 'introduce_violation');
}
// ── Simuleringar ────────────────────────────────────────────────────────
simulateRename(oldName, newName) {
const graph = this.loadGraph();
// Byt namn på noder
for (const node of graph.nodes) {
if (node.id === oldName) {
node.id = newName;
node.label = newName;
}
}
// Uppdatera kanter
for (const edge of graph.edges) {
if (edge.from === oldName) edge.from = newName;
if (edge.to === oldName) edge.to = newName;
}
return graph;
}
simulateMove(oldLocation, newLocation) {
const graph = this.loadGraph();
// Lägg till ny nod
graph.nodes.push({
id: newLocation,
label: newLocation,
type: 'service'
});
// Flytta kanter
for (const edge of graph.edges) {
if (edge.from === oldLocation) {
edge.from = newLocation;
}
}
return graph;
}
simulateSplit(moduleName, newModules) {
const graph = this.loadGraph();
// Ta bort gammal nod
graph.nodes = graph.nodes.filter(n => n.id !== moduleName);
// Lägg till nya noder
for (const mod of newModules) {
graph.nodes.push({
id: mod,
label: mod,
type: 'service'
});
}
// Uppdatera kanter
for (const edge of graph.edges) {
if (edge.from === moduleName) edge.from = newModules[0];
if (edge.to === moduleName) edge.to = newModules[0];
}
return graph;
}
simulateAddDependency(from, to) {
const graph = this.loadGraph();
graph.edges.push({
from,
to,
relation: 'depends_on',
confidence: 0.8
});
return graph;
}
simulateRestructure() {
const graph = this.loadGraph();
// Simulera att alla services får nytt prefix
for (const node of graph.nodes) {
if (node.type === 'service') {
node.id = `src/services/${node.id}`;
}
}
return graph;
}
simulateViolation() {
const graph = this.loadGraph();
// Lägg till en förbjuden kant: UI → DB
graph.edges.push({
from: 'app',
to: 'database',
relation: 'direct_access',
confidence: 0.99
});
return graph;
}
// ── Analys ──────────────────────────────────────────────────────────────
runAnalysis() {
try {
const output = execSync(
`node /home/bernt/.openclaw/workspace/SIL/pr-analyzer.mjs ${this.repoPath} HEAD~1 HEAD 2>&1`,
{ encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 }
);
return this.parseAnalysis(output);
} catch (e) {
return { error: e.message };
}
}
runAnalysisWithGraph(graph) {
// Spara temporär graf
const tempPath = `${TEST_DIR}/temp-graph.json`;
writeFileSync(tempPath, JSON.stringify(graph));
try {
const output = execSync(
`node /home/bernt/.openclaw/workspace/SIL/pr-analyzer.mjs ${this.repoPath} HEAD~1 HEAD 2>&1`,
{ encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 }
);
return this.parseAnalysis(output);
} catch (e) {
return { error: e.message };
}
}
parseAnalysis(output) {
// Extrahera nyckeldata från output
const components = [];
const lines = output.split('\n');
for (const line of lines) {
const match = line.match(/✅\s+(.+)/);
if (match && !line.includes('Körning')) {
components.push(match[1].trim());
}
}
return { components };
}
loadGraph() {
try {
return JSON.parse(readFileSync('/home/bernt/.openclaw/workspace/SYSTEM_GRAPH.json', 'utf8'));
} catch {
return { nodes: [], edges: [] };
}
}
// ── Jämförelse ──────────────────────────────────────────────────────────
compareResults(before, after, testName) {
if (before.error || after.error) {
return {
testName,
passed: false,
degradation: '100%',
reason: before.error || after.error
};
}
const beforeComponents = new Set(before.components || []);
const afterComponents = new Set(after.components || []);
const intersection = [...beforeComponents].filter(c => afterComponents.has(c));
const union = new Set([...beforeComponents, ...afterComponents]);
const similarity = union.size > 0 ? intersection.length / union.size : 1;
const degradation = Math.round((1 - similarity) * 100) + '%';
return {
testName,
passed: similarity >= 0.9, // Max 10% degradation
degradation,
similarity: Math.round(similarity * 100) + '%',
before: before.components?.length || 0,
after: after.components?.length || 0
};
}
// ── Rapport ─────────────────────────────────────────────────────────────
generateReport(results) {
const passed = results.filter(r => r.passed).length;
const total = results.length;
const avgDegradation = results
.filter(r => !r.error)
.reduce((a, r) => a + parseFloat(r.degradation), 0) / total;
return {
timestamp: new Date().toISOString(),
totalTests: total,
passed,
failed: total - passed,
avgDegradation: Math.round(avgDegradation) + '%',
meetsCriteria: avgDegradation < 10,
results
};
}
printReport(report) {
console.log('\n╔═══════════════════════════════════════════════════════════════╗');
console.log('║ VALIDATION: FRÅGA 3 ║');
console.log('║ Hur robust är SIL mot förändring? ║');
console.log('╚═══════════════════════════════════════════════════════════════╝');
console.log();
console.log('📊 RESULTAT\n');
console.log(` Tester: ${report.totalTests}`);
console.log(` Godkända: ${report.passed}`);
console.log(` Misslyckade: ${report.failed}`);
console.log(` Genomsnittlig degradation: ${report.avgDegradation}`);
console.log();
console.log('📋 PER TEST\n');
for (const result of report.results) {
const icon = result.passed ? '✅' : '❌';
console.log(` ${icon} ${result.testName}`);
console.log(` Degradation: ${result.degradation}`);
console.log(` Similarity: ${result.similarity || 'N/A'}`);
if (result.reason) {
console.log(` Fel: ${result.reason}`);
}
}
console.log();
console.log('🎯 SUCCESS-KRITERIUM\n');
console.log(` Krav: <10% degradation vid refactoring`);
console.log(` Resultat: ${report.avgDegradation}`);
console.log(` ${report.meetsCriteria ? '✅ UPPFYLLT' : '❌ EJ UPPFYLLT'}`);
console.log();
console.log('═══════════════════════════════════════════════════════════════\n');
}
saveResults(results) {
for (const result of results) {
writeFileSync(RESULTS_LOG, JSON.stringify(result) + '\n', { flag: 'a' });
}
}
}
// ── Main ──────────────────────────────────────────────────────────────────
const repoPath = process.argv[2] || '/home/bernt/repos/quixzoom.com';
const tester = new RobustnessTester(repoPath);
tester.runAllTests();