Integrate Oudelaa backend features, security, tests, and deployment updates

هذا الالتزام موجود في:
boutmoun123
2026-07-26 16:58:53 +03:00
الأصل 1b24ca4294
التزام 2fd5322ef7
178 ملفات معدلة مع 19068 إضافات و2592 حذوفات

عرض الملف

@@ -11,6 +11,12 @@ function parseArgs(argv) {
warmup: 5,
headers: {},
body: undefined,
gates: {
minSuccessRate: undefined,
minRequestsPerSecond: undefined,
maxP95Ms: undefined,
maxP99Ms: undefined,
},
};
for (let index = 0; index < argv.length; index += 1) {
@@ -53,6 +59,34 @@ function parseArgs(argv) {
continue;
}
if (arg === '--min-success-rate') {
assertOptionValue(arg, next);
options.gates.minSuccessRate = Number(next);
index += 1;
continue;
}
if (arg === '--min-rps') {
assertOptionValue(arg, next);
options.gates.minRequestsPerSecond = Number(next);
index += 1;
continue;
}
if (arg === '--max-p95') {
assertOptionValue(arg, next);
options.gates.maxP95Ms = Number(next);
index += 1;
continue;
}
if (arg === '--max-p99') {
assertOptionValue(arg, next);
options.gates.maxP99Ms = Number(next);
index += 1;
continue;
}
if (arg === '--header' && next) {
const separatorIndex = next.indexOf(':');
if (separatorIndex === -1) {
@@ -95,6 +129,14 @@ function parseArgs(argv) {
throw new Error('warmup must be zero or a positive integer');
}
validateGate(options.gates.minSuccessRate, 'min-success-rate', {
min: 0,
max: 100,
});
validateGate(options.gates.minRequestsPerSecond, 'min-rps', { min: 0 });
validateGate(options.gates.maxP95Ms, 'max-p95', { min: 0 });
validateGate(options.gates.maxP99Ms, 'max-p99', { min: 0 });
if (options.body && !options.headers['Content-Type']) {
options.headers['Content-Type'] = 'application/json';
}
@@ -102,6 +144,27 @@ function parseArgs(argv) {
return options;
}
function assertOptionValue(option, value) {
if (value === undefined) {
throw new Error(`${option} requires a value`);
}
}
function validateGate(value, name, limits) {
if (value === undefined) {
return;
}
const aboveMaximum = limits.max !== undefined && value > limits.max;
if (!Number.isFinite(value) || value < limits.min || aboveMaximum) {
const range =
limits.max === undefined
? `at least ${limits.min}`
: `between ${limits.min} and ${limits.max}`;
throw new Error(`${name} must be a finite number ${range}`);
}
}
function percentile(sortedValues, p) {
if (!sortedValues.length) {
return 0;
@@ -124,6 +187,9 @@ function average(values) {
function printUsage() {
console.log('Usage: node scripts/load-test.js --url <url> [--duration 15] [--concurrency 20]');
console.log('Optional: --method POST --header "Authorization: Bearer <token>" --body "{\"key\":\"value\"}"');
console.log(
'Performance gates: --min-success-rate 99.9 --min-rps 100 --max-p95 250 --max-p99 500',
);
}
async function warmup(options) {
@@ -218,6 +284,86 @@ function buildSummary(options, results, totalDurationMs) {
};
}
function evaluatePerformanceGates(summary, gates) {
const definitions = [
{
configuredValue: gates.minSuccessRate,
metric: 'successRate',
label: 'Success rate',
actual: summary.successRate,
operator: '>=',
unit: '%',
passes: (actual, threshold) => actual >= threshold,
},
{
configuredValue: gates.minRequestsPerSecond,
metric: 'requestsPerSecond',
label: 'Requests/second',
actual: summary.requestsPerSecond,
operator: '>=',
unit: ' req/s',
passes: (actual, threshold) => actual >= threshold,
},
{
configuredValue: gates.maxP95Ms,
metric: 'latencyMs.p95',
label: 'Latency p95',
actual: summary.latencyMs.p95,
operator: '<=',
unit: ' ms',
passes: (actual, threshold) => actual <= threshold,
},
{
configuredValue: gates.maxP99Ms,
metric: 'latencyMs.p99',
label: 'Latency p99',
actual: summary.latencyMs.p99,
operator: '<=',
unit: ' ms',
passes: (actual, threshold) => actual <= threshold,
},
];
const checks = definitions
.filter((definition) => definition.configuredValue !== undefined)
.map((definition) => ({
metric: definition.metric,
label: definition.label,
actual: definition.actual,
operator: definition.operator,
threshold: definition.configuredValue,
unit: definition.unit,
passed: definition.passes(definition.actual, definition.configuredValue),
}));
return {
configured: checks.length > 0,
passed: checks.every((check) => check.passed),
checks,
};
}
function printGateReport(gateResult) {
console.log('');
if (!gateResult.configured) {
console.log('Performance gates: not configured');
return;
}
const passedChecks = gateResult.checks.filter((check) => check.passed).length;
console.log(
`Performance gates: ${gateResult.passed ? 'PASSED' : 'FAILED'} (${passedChecks}/${gateResult.checks.length})`,
);
for (const check of gateResult.checks) {
const status = check.passed ? 'PASS' : 'FAIL';
console.log(
`[${status}] ${check.label}: ${check.actual}${check.unit} ${check.operator} ${check.threshold}${check.unit}`,
);
}
}
async function main() {
if (process.argv.includes('--help')) {
printUsage();
@@ -249,12 +395,27 @@ async function main() {
const totalDurationMs = performance.now() - startedAt;
const summary = buildSummary(options, results, totalDurationMs);
const performanceGates = evaluatePerformanceGates(summary, options.gates);
summary.performanceGates = performanceGates;
console.log('');
console.log(JSON.stringify(summary, null, 2));
printGateReport(performanceGates);
if (!performanceGates.passed) {
process.exitCode = 2;
}
}
main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exitCode = 1;
});
if (require.main === module) {
main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exitCode = 1;
});
}
module.exports = {
buildSummary,
evaluatePerformanceGates,
parseArgs,
};