mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
feat: add privacy-preserving telemetry and error aggregation
- Update smoke-test.mjs to output structured summary with logs - Update telemetry-aggregator.mjs to collect from unit, smoke, and load tests - Update run-unit-tests.mjs to log output to artifacts/test_unit.log - Ensure PII sanitization in telemetry reports
This commit is contained in:
1 parent
87c494c1f3
commit
edbd66df52
3 files changed
+92
-6
No files matched your search
@@ -38,7 +38,7 @@ import http from 'http';
|
||||
|
||||
const ARTIFACTS_DIR = 'artifacts';
|
||||
const LOG_FILE = path.join(ARTIFACTS_DIR, `smoke-${new Date().toISOString().split('T')[0].replace(/-/g, '')}.log`);
|
||||
const REPORT_FILE = path.join(ARTIFACTS_DIR, 'smoke-report.json');
|
||||
const REPORT_FILE = path.join(ARTIFACTS_DIR, `smoke-summary-${new Date().toISOString().replace(/[:.]/g, '-')}.json`);
|
||||
const UI_SCREENSHOT = path.join(ARTIFACTS_DIR, 'smoke-ui.png');
|
||||
|
||||
if (!fs.existsSync(ARTIFACTS_DIR)) {
|
||||
@@ -285,12 +285,16 @@ async function runSmokeTest() {
|
||||
|
||||
} catch (error) {
|
||||
log(`Smoke Test Failed: ${error.stack}`, 'ERROR');
|
||||
summary.status = "FAIL";
|
||||
summary.status = "failure";
|
||||
summary.error = error.message;
|
||||
exitCode = 1;
|
||||
} finally {
|
||||
// --- Write Report ---
|
||||
try {
|
||||
if (fs.existsSync(LOG_FILE)) {
|
||||
const logs = fs.readFileSync(LOG_FILE, 'utf8').split('\n').filter(Boolean);
|
||||
summary.logs = logs;
|
||||
}
|
||||
fs.writeFileSync(REPORT_FILE, JSON.stringify(summary, null, 2));
|
||||
log(`Summary written to ${REPORT_FILE}`);
|
||||
} catch (e) {
|
||||
|
||||
@@ -61,9 +61,13 @@ function suggestOwner(stack) {
|
||||
|
||||
function collectUnitTests(logFile = 'test_output.log') {
|
||||
const errors = [];
|
||||
if (!fs.existsSync(logFile)) return errors;
|
||||
// Check root and artifacts dir
|
||||
const potentialPaths = [logFile, path.join(ARTIFACTS_DIR, logFile)];
|
||||
const filepath = potentialPaths.find(p => fs.existsSync(p));
|
||||
|
||||
const content = fs.readFileSync(logFile, 'utf8');
|
||||
if (!filepath) return errors;
|
||||
|
||||
const content = fs.readFileSync(filepath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
let currentError = null;
|
||||
let insideErrorBlock = false;
|
||||
@@ -182,7 +186,7 @@ function collectSmokeTests() {
|
||||
const content = JSON.parse(fs.readFileSync(path.join(ARTIFACTS_DIR, file), 'utf8'));
|
||||
|
||||
// Expected format: { timestamp, status, error, logs }
|
||||
if (content.status === 'failure') {
|
||||
if (content.status && (content.status.toLowerCase() === 'failure' || content.status.toLowerCase() === 'fail')) {
|
||||
const errorMsg = content.error || 'Unknown Smoke Test Failure';
|
||||
const sanitizedError = sanitize(errorMsg);
|
||||
|
||||
@@ -258,6 +262,62 @@ function collectFuzzReports() {
|
||||
return errors;
|
||||
}
|
||||
|
||||
function collectLoadTests() {
|
||||
const errors = [];
|
||||
if (!fs.existsSync(ARTIFACTS_DIR)) return errors;
|
||||
|
||||
const files = fs.readdirSync(ARTIFACTS_DIR).filter(f => f.startsWith('load-report-') && f.endsWith('.json'));
|
||||
// Also check load-report.json
|
||||
if (fs.existsSync(path.join(ARTIFACTS_DIR, 'load-report.json'))) {
|
||||
files.push('load-report.json');
|
||||
}
|
||||
|
||||
const uniqueFiles = [...new Set(files)];
|
||||
|
||||
for (const file of uniqueFiles) {
|
||||
try {
|
||||
const content = JSON.parse(fs.readFileSync(path.join(ARTIFACTS_DIR, file), 'utf8'));
|
||||
|
||||
// Check for explicit errors
|
||||
if (content.metrics && content.metrics.errors > 0) {
|
||||
const errorBreakdown = content.metrics.error_breakdown ? JSON.stringify(content.metrics.error_breakdown, null, 2) : 'No breakdown';
|
||||
const sanitizedBreakdown = sanitize(errorBreakdown);
|
||||
errors.push({
|
||||
source: `load-test:${file}`,
|
||||
title: `Load Test Errors: ${content.metrics.errors} failures`,
|
||||
details: [sanitizedBreakdown],
|
||||
stack: `Load Test Errors:\n${sanitizedBreakdown}`,
|
||||
fingerprint: getFingerprint(`load-test-errors-${sanitizedBreakdown}`),
|
||||
severity: 'High',
|
||||
owner: 'P2P Team'
|
||||
});
|
||||
}
|
||||
|
||||
// Check for bottlenecks
|
||||
if (content.bottlenecks && content.bottlenecks.length > 0) {
|
||||
for (let i = 0; i < content.bottlenecks.length; i++) {
|
||||
const bn = content.bottlenecks[i];
|
||||
const rem = content.remediation ? content.remediation[i] : '';
|
||||
const sanitizedBn = sanitize(bn);
|
||||
errors.push({
|
||||
source: `load-test:${file}`,
|
||||
title: `Load Test Bottleneck: ${sanitizedBn}`,
|
||||
details: [sanitize(rem)],
|
||||
stack: `Bottleneck: ${sanitizedBn}\nRemediation: ${sanitize(rem)}`,
|
||||
fingerprint: getFingerprint(`load-test-bottleneck-${sanitizedBn}`),
|
||||
severity: 'Medium',
|
||||
owner: 'P2P Team'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.warn(`Failed to parse load report ${file}:`, err.message);
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
function collectAgentLogs() {
|
||||
const errors = [];
|
||||
if (!fs.existsSync(ARTIFACTS_DIR)) return errors;
|
||||
@@ -419,6 +479,13 @@ async function main() {
|
||||
errors.push(...smokeTestErrors);
|
||||
}
|
||||
|
||||
// Load Tests
|
||||
const loadTestErrors = collectLoadTests();
|
||||
if (loadTestErrors.length > 0) {
|
||||
console.log(`Collected ${loadTestErrors.length} load test errors.`);
|
||||
errors.push(...loadTestErrors);
|
||||
}
|
||||
|
||||
// Fuzz Reports
|
||||
const fuzzErrors = collectFuzzReports();
|
||||
if (fuzzErrors.length > 0) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { readdir, readFile } from "node:fs/promises";
|
||||
import fs from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import path from "node:path";
|
||||
|
||||
@@ -101,14 +102,28 @@ async function run() {
|
||||
return;
|
||||
}
|
||||
|
||||
const logFile = path.join(rootDir, "artifacts", "test_unit.log");
|
||||
if (!fs.existsSync(path.dirname(logFile))) {
|
||||
fs.mkdirSync(path.dirname(logFile), { recursive: true });
|
||||
}
|
||||
const logStream = fs.createWriteStream(logFile);
|
||||
|
||||
for (const testFile of selectedTests) {
|
||||
const relativePath = path.relative(rootDir, testFile);
|
||||
console.log(`\n→ Running ${relativePath}`);
|
||||
logStream.write(`\n→ Running ${relativePath}\n`);
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
const child = spawn(process.execPath, ["--import", setupImport.href, testFile], {
|
||||
stdio: "inherit",
|
||||
stdio: ["inherit", "pipe", "pipe"],
|
||||
});
|
||||
|
||||
child.stdout.pipe(process.stdout, { end: false });
|
||||
child.stdout.pipe(logStream, { end: false });
|
||||
|
||||
child.stderr.pipe(process.stderr, { end: false });
|
||||
child.stderr.pipe(logStream, { end: false });
|
||||
|
||||
let finished = false;
|
||||
let timeoutId = null;
|
||||
|
||||
|
||||
Reference in new issue
Block a user