fix web ui smoke and brand assets
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { request } from 'node:https';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const baseUrl = (process.env.LISGLOSIPS_BASE_URL || 'https://100.90.90.91').replace(/\/$/, '');
|
||||
const timeoutMs = Number(process.env.LISGLOSIPS_SMOKE_TIMEOUT_MS || 30000);
|
||||
const maxAttempts = Number(process.env.LISGLOSIPS_SMOKE_ATTEMPTS || 2);
|
||||
const reportDir = resolve(dirname(fileURLToPath(import.meta.url)), '../reports');
|
||||
|
||||
function requestUrlOnce(path, attempt) {
|
||||
return new Promise((resolveRequest) => {
|
||||
const startedAt = Date.now();
|
||||
const url = new URL(path, baseUrl);
|
||||
const req = request(
|
||||
url,
|
||||
{
|
||||
method: 'GET',
|
||||
rejectUnauthorized: process.env.LISGLOSIPS_REJECT_UNAUTHORIZED === '1',
|
||||
timeout: timeoutMs,
|
||||
headers: {
|
||||
Accept: 'application/json,text/html;q=0.9,*/*;q=0.8',
|
||||
'User-Agent': 'lisglosips-remote-smoke/1.0',
|
||||
},
|
||||
},
|
||||
(res) => {
|
||||
const chunks = [];
|
||||
res.on('data', (chunk) => chunks.push(chunk));
|
||||
res.on('end', () => {
|
||||
const body = Buffer.concat(chunks).toString('utf8');
|
||||
resolveRequest({
|
||||
path,
|
||||
url: url.toString(),
|
||||
attempt,
|
||||
ok: true,
|
||||
statusCode: res.statusCode || 0,
|
||||
durationMs: Date.now() - startedAt,
|
||||
contentType: String(res.headers['content-type'] || ''),
|
||||
body,
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
req.on('timeout', () => {
|
||||
req.destroy(new Error(`Request timed out after ${timeoutMs}ms`));
|
||||
});
|
||||
req.on('error', (error) => {
|
||||
resolveRequest({
|
||||
path,
|
||||
url: url.toString(),
|
||||
attempt,
|
||||
ok: false,
|
||||
statusCode: 0,
|
||||
durationMs: Date.now() - startedAt,
|
||||
contentType: '',
|
||||
body: '',
|
||||
error: error.message,
|
||||
});
|
||||
});
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function requestUrl(path) {
|
||||
let lastResult;
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
||||
lastResult = await requestUrlOnce(path, attempt);
|
||||
if (lastResult.ok) {
|
||||
return lastResult;
|
||||
}
|
||||
}
|
||||
|
||||
return lastResult;
|
||||
}
|
||||
|
||||
function parseJson(result) {
|
||||
try {
|
||||
return JSON.parse(result.body);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function expectStatus(result, expectedStatus) {
|
||||
return {
|
||||
name: `${result.path} returns ${expectedStatus}`,
|
||||
pass: result.ok && result.statusCode === expectedStatus,
|
||||
detail: result.error || `status=${result.statusCode}, duration=${result.durationMs}ms, attempt=${result.attempt}`,
|
||||
};
|
||||
}
|
||||
|
||||
function expectBody(result, name, predicate, detail) {
|
||||
return {
|
||||
name,
|
||||
pass: result.ok && predicate(result),
|
||||
detail: detail(result),
|
||||
};
|
||||
}
|
||||
|
||||
function reportLine(check) {
|
||||
return `| ${check.pass ? 'PASS' : 'FAIL'} | ${check.name} | ${check.detail.replace(/\|/g, '\\|')} |`;
|
||||
}
|
||||
|
||||
const checks = [];
|
||||
const results = {
|
||||
root: await requestUrl('/'),
|
||||
live: await requestUrl('/api/v2/health/live'),
|
||||
ready: await requestUrl('/api/v2/health/ready'),
|
||||
captcha: await requestUrl('/api/v2/auth/captcha'),
|
||||
};
|
||||
|
||||
checks.push(expectStatus(results.root, 200));
|
||||
checks.push(
|
||||
expectBody(
|
||||
results.root,
|
||||
'frontend HTML contains app root',
|
||||
(result) => result.contentType.includes('text/html') && result.body.includes('<div id="root"></div>'),
|
||||
(result) => `contentType=${result.contentType}, bytes=${Buffer.byteLength(result.body, 'utf8')}, attempt=${result.attempt}`
|
||||
)
|
||||
);
|
||||
|
||||
checks.push(expectStatus(results.live, 200));
|
||||
checks.push(
|
||||
expectBody(
|
||||
results.live,
|
||||
'live health reports ok',
|
||||
(result) => {
|
||||
const body = parseJson(result);
|
||||
return body?.status === 'ok' && body?.service === 'api' && body?.checks?.process === 'ok';
|
||||
},
|
||||
(result) => `body=${JSON.stringify(parseJson(result))}`
|
||||
)
|
||||
);
|
||||
|
||||
checks.push(expectStatus(results.ready, 200));
|
||||
checks.push(
|
||||
expectBody(
|
||||
results.ready,
|
||||
'ready health reports database and redis ok',
|
||||
(result) => {
|
||||
const body = parseJson(result);
|
||||
return body?.status === 'ok' && body?.checks?.database === 'ok' && body?.checks?.redis === 'ok';
|
||||
},
|
||||
(result) => `body=${JSON.stringify(parseJson(result))}`
|
||||
)
|
||||
);
|
||||
|
||||
checks.push(expectStatus(results.captcha, 200));
|
||||
checks.push(
|
||||
expectBody(
|
||||
results.captcha,
|
||||
'captcha endpoint returns id, image, and expiry',
|
||||
(result) => {
|
||||
const body = parseJson(result);
|
||||
return typeof body?.captchaId === 'string' && body.imageDataUrl?.startsWith('data:image/svg+xml;base64,') && typeof body.expiresAt === 'string';
|
||||
},
|
||||
(result) => {
|
||||
const body = parseJson(result);
|
||||
return body ? `captchaId=${body.captchaId}, expiresAt=${body.expiresAt}, attempt=${result.attempt}` : 'body is not JSON';
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
const now = new Date();
|
||||
const stamp = now.toISOString().replace(/[-:]/g, '').replace(/\..+$/, 'Z');
|
||||
const reportPath = resolve(reportDir, `REMOTE_SMOKE_${stamp}.md`);
|
||||
const failed = checks.filter((check) => !check.pass);
|
||||
|
||||
const report = [
|
||||
'# Remote Smoke Test Report',
|
||||
'',
|
||||
`Date: ${now.toISOString()}`,
|
||||
`Base URL: ${baseUrl}`,
|
||||
`Timeout: ${timeoutMs}ms`,
|
||||
`Attempts: ${maxAttempts}`,
|
||||
'',
|
||||
'| Result | Check | Detail |',
|
||||
'| --- | --- | --- |',
|
||||
...checks.map(reportLine),
|
||||
'',
|
||||
'## Raw Endpoints',
|
||||
'',
|
||||
...Object.values(results).map((result) => `- ${result.path}: status=${result.statusCode}, duration=${result.durationMs}ms, attempt=${result.attempt}, contentType=${result.contentType || 'n/a'}`),
|
||||
'',
|
||||
].join('\n');
|
||||
|
||||
await mkdir(reportDir, { recursive: true });
|
||||
await writeFile(reportPath, report, 'utf8');
|
||||
|
||||
for (const check of checks) {
|
||||
console.log(`${check.pass ? 'PASS' : 'FAIL'} ${check.name} - ${check.detail}`);
|
||||
}
|
||||
console.log(`Report: ${reportPath}`);
|
||||
|
||||
if (failed.length > 0) {
|
||||
process.exitCode = 1;
|
||||
}
|
||||
Reference in New Issue
Block a user