55 lines
3.6 KiB
JavaScript
55 lines
3.6 KiB
JavaScript
import fs from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import assert from 'node:assert/strict';
|
|
import { execFileSync, spawnSync } from 'node:child_process';
|
|
import { createHash } from 'node:crypto';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const root = path.dirname(fileURLToPath(import.meta.url));
|
|
const dir = process.argv[2] === 'live' ? '/home/agent/services/license/public' : path.join(root, 'preview');
|
|
const manifest = JSON.parse(await fs.readFile(path.join(dir, 'docs/manifest.json'), 'utf8'));
|
|
let links = 0;
|
|
for (const page of manifest.pages) {
|
|
const html = await fs.readFile(path.join(dir, 'docs', page.output), 'utf8');
|
|
assert.match(html, /<h1 /);
|
|
assert.doesNotMatch(html, /<script[^>]*>[^<]+<\/script>|href="javascript:|onerror=/i);
|
|
for (const match of html.matchAll(/(?:href|src)="(\/docs\/[^"#]+)(?:#[^"]*)?"/g)) { await fs.access(path.join(dir, match[1])); links++; }
|
|
}
|
|
console.log(JSON.stringify({ pages:manifest.pages.length, validPublicLinks:links, commits:manifest.commits, drafts:manifest.drafts }));
|
|
for (const image of manifest.images || []) {
|
|
const data = await fs.readFile(path.join(dir, 'docs', image.output));
|
|
assert.equal(createHash('sha256').update(data).digest('hex'), image.sha256);
|
|
if (process.argv[2] === 'live') {
|
|
const response = await fetch(`https://license.enelix.ch/docs/${image.output}`);
|
|
assert.equal(response.status, 200); assert.match(response.headers.get('content-type'), /image\/png/);
|
|
assert.equal(createHash('sha256').update(Buffer.from(await response.arrayBuffer())).digest('hex'), image.sha256);
|
|
}
|
|
}
|
|
if (process.argv[2] === 'live') {
|
|
for (const route of ['/docs/index.html', '/docs/erste-schritte.html', '/docs/installation.html', '/docs/faq.html', '/docs/preise.html', '/docs/ems/Manager.html', '/docs/utils/Shelly-Modul.html', '/api/catalog', '/api/session', '/']) {
|
|
const response = await fetch(`https://license.enelix.ch${route}`, { signal:AbortSignal.timeout(15000) });
|
|
assert.equal(response.status, 200, route);
|
|
const body = await response.text();
|
|
if (route === '/api/session') assert.equal(JSON.parse(body).authenticated, false);
|
|
if (route.startsWith('/docs/')) { assert.match(body, /Enelix Dokumentation/); assert.equal(response.headers.get('cache-control'), 'no-store'); }
|
|
if (route === '/') assert.match(body, /public-doc-links/);
|
|
console.log(JSON.stringify({ anonymous:route, status:response.status }));
|
|
}
|
|
} else {
|
|
const before = createHash('sha256').update(await fs.readFile(path.join(dir, 'docs/index.html'))).digest('hex');
|
|
const failureState = await fs.mkdtemp(path.join(root, 'failure-test-'));
|
|
try {
|
|
const mirror = path.join(failureState, 'ems.git'); await fs.mkdir(mirror);
|
|
execFileSync('git', ['init', '--bare', '--quiet'], { cwd:mirror });
|
|
execFileSync('git', ['remote', 'add', 'origin', 'https://invalid.example/documentation.git'], { cwd:mirror });
|
|
const result = spawnSync(process.execPath, [path.join(root, 'sync.mjs')], { encoding:'utf8', env:{ ...process.env, DOCS_PUBLIC_DIR:dir, DOCS_STATE_DIR:failureState, DOCS_ALLOW_DRAFTS:'true' } });
|
|
assert.equal(result.status, 1);
|
|
const after = createHash('sha256').update(await fs.readFile(path.join(dir, 'docs/index.html'))).digest('hex');
|
|
assert.equal(after, before);
|
|
const status = JSON.parse(await fs.readFile(path.join(dir, 'docs-status.json'), 'utf8'));
|
|
assert.equal(status.ok, false); assert.ok(status.lastSuccess);
|
|
console.log('Failure regression passed: last-good pages retained, failure visible, lock released.');
|
|
await assert.rejects(fs.access(path.join(failureState, 'sync.lock')));
|
|
} finally { await fs.rm(failureState, { recursive:true, force:true }); }
|
|
}
|