Runner: Build-Auftraege Review-Korrekturen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4ed7e5d7a5
commit
4fffd8b218
2 files changed
+119
-32
No files matched your search
@@ -3,7 +3,9 @@ import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, readdirSync, statSync, utimesSync, symlinkSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { createJobs } from '../src/jobs.js';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { PassThrough } from 'node:stream';
|
||||
import { createJobs, createGit } from '../src/jobs.js';
|
||||
|
||||
const TOKEN = 'tok_SECRET_123456';
|
||||
const B64 = Buffer.from(`x-access-token:${TOKEN}`).toString('base64');
|
||||
@@ -161,3 +163,71 @@ test('pruneCache: > 14 Tage weg, dann älteste bis <= Limit', async () => {
|
||||
assert.deepEqual(readdirSync(s.config.cacheRoot).sort(), ['b__r', 'c__r']);
|
||||
rmSync(s.root, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
function fakeSpawn() {
|
||||
const calls = [];
|
||||
const fn = (cmd, args, opts) => {
|
||||
const c = new EventEmitter(); c.stdout = new PassThrough(); c.stderr = new PassThrough(); c.killed = null;
|
||||
c.kill = (sig) => { c.killed = sig; };
|
||||
calls.push({ cmd, args, opts, child: c });
|
||||
return c;
|
||||
};
|
||||
return { fn, calls };
|
||||
}
|
||||
|
||||
test('createGit: Token nicht in argv/URL, Umgebung minimal + GIT_CONFIG_*', async () => {
|
||||
const sp = fakeSpawn();
|
||||
const git = createGit({ giteaUrl: 'https://git.x/', giteaToken: TOKEN }, { spawnFn: sp.fn });
|
||||
const p = git({ repo: 'own/rep', branch: 'main', dir: '/w/x' });
|
||||
const { args, opts, child } = sp.calls[0];
|
||||
assert.ok(!args.join(' ').includes(TOKEN) && !args.join(' ').includes(B64));
|
||||
assert.ok(args.includes('https://git.x/own/rep.git'));
|
||||
assert.deepEqual(args.slice(args.indexOf('clone')), ['clone', '--depth', '1', '--branch', 'main', '--', 'https://git.x/own/rep.git', '/w/x']);
|
||||
assert.ok(args.includes('http.lowSpeedLimit=1000') && args.includes('http.lowSpeedTime=60'));
|
||||
assert.deepEqual(Object.keys(opts.env).sort(), ['GIT_CONFIG_COUNT', 'GIT_CONFIG_KEY_0', 'GIT_CONFIG_NOSYSTEM', 'GIT_CONFIG_VALUE_0', 'GIT_TERMINAL_PROMPT', 'HOME', 'PATH']);
|
||||
assert.equal(opts.env.GIT_CONFIG_KEY_0, 'http.extraHeader');
|
||||
assert.equal(opts.env.GIT_CONFIG_VALUE_0, `Authorization: Basic ${B64}`);
|
||||
child.emit('close', 0);
|
||||
await p;
|
||||
});
|
||||
|
||||
test('createGit: Zeitlimit beendet git mit SIGKILL und lehnt ab', async () => {
|
||||
const sp = fakeSpawn();
|
||||
const git = createGit({ giteaUrl: 'https://git.x', giteaToken: TOKEN }, { spawnFn: sp.fn });
|
||||
await assert.rejects(git({ repo: 'own/rep', branch: 'main', dir: '/w/x', timeoutMs: 1000 }), /Zeitlimit/);
|
||||
assert.equal(sp.calls[0].child.killed, 'SIGKILL');
|
||||
});
|
||||
|
||||
test('publish: fehlt <sub> und nur .old existiert, wird .old zurückgeholt (Build-Fehler danach)', async () => {
|
||||
const s = setup({ files: { 'package.json': PKG() }, dockerImpl: (args) => ({ code: args.includes('build') ? 2 : 0 }) });
|
||||
// Zustand nach Absturz: nur demo.old; Erfolg-Pfad über static
|
||||
mkdirSync(join(s.config.previewsRoot, 'demo.old')); writeFileSync(join(s.config.previewsRoot, 'demo.old', 'index.html'), 'GUT');
|
||||
const s2 = setup({ files: { 'index.html': 'NEU' } });
|
||||
mkdirSync(join(s2.config.previewsRoot, 'demo.old')); writeFileSync(join(s2.config.previewsRoot, 'demo.old', 'index.html'), 'GUT');
|
||||
const r = await wait(s2.jobs, s2.jobs.submit(job()));
|
||||
assert.equal(r.state, 'done');
|
||||
assert.equal(readFileSync(join(s2.config.previewsRoot, 'demo', 'index.html'), 'utf8'), 'NEU');
|
||||
});
|
||||
|
||||
test('publish: Aufräumfehler nach dem Tausch lässt den Lauf nicht scheitern', async () => {
|
||||
const root = mkdtempSync(join(tmpdir(), 'jobs-'));
|
||||
const config = { giteaUrl: 'x', giteaToken: 't', previewsRoot: join(root, 'p'), workRoot: join(root, 'w'), cacheRoot: join(root, 'c'), minFreeBytes: 0, buildTimeoutMs: 1000 };
|
||||
mkdirSync(join(config.previewsRoot, 'demo'), { recursive: true });
|
||||
const jobs = createJobs({ config, docker: {}, git: async ({ dir }) => { mkdirSync(dir, { recursive: true }); writeFileSync(join(dir, 'index.html'), 'N'); }, mem: () => 1e12,
|
||||
fs: { chownR: async () => {}, rm: async (p, o) => { if (p.endsWith('demo.old') && existsSync(p)) throw new Error('boom'); return (await import('node:fs/promises')).rm(p, o); } } });
|
||||
const r = await wait(jobs, jobs.submit(job()));
|
||||
assert.equal(r.state, 'done');
|
||||
assert.equal(readFileSync(join(config.previewsRoot, 'demo', 'index.html'), 'utf8'), 'N');
|
||||
assert.ok(r.log.some((l) => /Aufräumen fehlgeschlagen/.test(l)));
|
||||
});
|
||||
|
||||
test('mem() wirft ⇒ wie wenig Speicher; Cache während des Laufs vor pruneCache geschützt', async () => {
|
||||
let boom = true; let s; let during;
|
||||
s = setup({ files: { 'package.json': PKG() }, mem: () => { if (boom) throw new Error('x'); return 1e12; },
|
||||
dockerImpl: async () => { const d = join(s.config.cacheRoot, 'own__rep'); const t = (Date.now() - 40 * 864e5) / 1000; utimesSync(d, t, t); await s.jobs.pruneCache(); during = existsSync(d); return { code: 1 }; } });
|
||||
const id = s.jobs.submit(job({ mode: 'build' }));
|
||||
assert.equal(s.jobs.get(id).state, 'queued');
|
||||
boom = false; s.timers[0].fn();
|
||||
await wait(s.jobs, id);
|
||||
assert.equal(during, true);
|
||||
});
|
||||
Reference in new issue
Block a user