Files
Git-Manager-Gui/__tests__/backup-progress.test.js
T
2026-10-22 06:53:45 +00:00

117 lines
5.2 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert');
const http = require('node:http');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
require('../src/git/gitBinary.js').useBundledGit();
const { registerFeatureIpc } = require('../src/features/featuresIpc.js');
// Drei Repos; das ZIP von "langsam" kommt in kleinen Stücken über ~3 s, damit ein Abbruch mitten hineinfällt
function fakeGitea() {
const server = http.createServer((req, res) => {
const u = new URL(req.url, 'http://x');
const send = (code, obj) => { res.statusCode = code; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(obj)); };
if (u.pathname === '/api/v1/user/repos') {
if (u.searchParams.get('page') !== '1') return send(200, []);
const port = server.address().port;
return send(200, ['eins', 'langsam', 'drei'].map(name => ({
name, owner: { login: 'M_Viper' }, default_branch: 'main', empty: false,
clone_url: `http://127.0.0.1:${port}/M_Viper/${name}.git`
})));
}
const m = u.pathname.match(/^\/api\/v1\/repos\/M_Viper\/(\w+)\/archive\/main\.zip$/);
if (m) {
const total = 64 * 1024;
res.writeHead(200, { 'Content-Type': 'application/zip', 'Content-Length': String(total) });
if (m[1] !== 'langsam') return res.end(Buffer.alloc(total, 1));
let sent = 0;
const t = setInterval(() => {
if (res.destroyed) return clearInterval(t);
res.write(Buffer.alloc(2048, 1));
sent += 2048;
if (sent >= total) { clearInterval(t); res.end(); }
}, 100);
return;
}
send(404, { message: 'not found' });
});
return new Promise(r => server.listen(0, '127.0.0.1', () => r({ server, url: `http://127.0.0.1:${server.address().port}` })));
}
function setup(url) {
const handlers = {};
const events = [];
registerFeatureIpc({
ipcMain: { handle: (name, fn) => { handlers[name] = fn; } },
app: { getPath: () => fs.mkdtempSync(path.join(os.tmpdir(), 'gm-ud-')) },
readCredentials: () => ({ giteaURL: url, giteaToken: 't' }),
buildItemsFromPaths: async () => ({ items: [], skipped: [], ignored: [] }),
clearGitTransportBlock: () => false,
broadcast: (channel, p) => { if (channel === 'repo-backup-progress') events.push(p); }
});
return { call: (name, data) => handlers[name]({ sender: { send() {} } }, data), events };
}
test('Backup meldet Fortschritt: Phasen, aktuelles Repo, Bytes und Ergebnis je Repo', async () => {
const g = await fakeGitea();
try {
const { call, events } = setup(g.url);
const target = fs.mkdtempSync(path.join(os.tmpdir(), 'gm-bk-'));
await call('repo-backup-set-config', { targetDir: target });
const r = await call('repo-backup-run-now', {});
assert.strictEqual(r.summary.ok, 3, JSON.stringify(r));
const phases = [...new Set(events.map(e => e.phase))];
assert.deepStrictEqual(phases, ['start', 'list', 'running', 'done']);
assert.deepStrictEqual(events.find(e => e.phase === 'running').repos, ['M_Viper/eins', 'M_Viper/langsam', 'M_Viper/drei']);
assert.ok(events.some(e => e.repo === 'M_Viper/langsam' && e.bytes > 0 && e.bytesTotal === 64 * 1024), 'Download-Fortschritt fehlt');
const last = events[events.length - 1];
assert.strictEqual(last.results.length, 3);
assert.ok(last.results.every(x => x.ok && x.method === 'ZIP'));
assert.ok(!events.some(e => 'abort' in e), 'interne Felder dürfen nicht nach außen gehen');
// Während kein Backup läuft, gibt es auch keinen Zwischenstand
const state = await call('repo-backup-get-config', {});
assert.strictEqual(state.running, false);
assert.strictEqual(state.progress, null);
} finally {
g.server.close();
}
});
test('Abbrechen beendet den laufenden ZIP-Download sofort und überspringt den Rest', async () => {
const g = await fakeGitea();
try {
const { call, events } = setup(g.url);
const target = fs.mkdtempSync(path.join(os.tmpdir(), 'gm-bk-'));
await call('repo-backup-set-config', { targetDir: target });
const started = Date.now();
const running = call('repo-backup-run-now', {});
// warten, bis der langsame Download läuft
while (!events.some(e => e.repo === 'M_Viper/langsam' && e.bytes > 0)) {
if (Date.now() - started > 10000) throw new Error('Download startete nicht');
await new Promise(r => setTimeout(r, 50));
}
const state = await call('repo-backup-get-config', {});
assert.strictEqual(state.running, true);
assert.strictEqual(state.progress.repo, 'M_Viper/langsam');
assert.strictEqual((await call('repo-backup-cancel', {})).ok, true);
const r = await running;
assert.ok(Date.now() - started < 2500, 'Abbruch muss den Download sofort beenden');
assert.strictEqual(r.ok, false);
assert.strictEqual(r.summary.cancelled, true);
assert.strictEqual(r.summary.ok, 1);
assert.strictEqual(r.summary.skipped, 2);
// keine halbe Datei zurücklassen
const archiv = path.join(target, 'M_Viper', 'langsam-archiv');
assert.deepStrictEqual(fs.existsSync(archiv) ? fs.readdirSync(archiv) : [], []);
assert.strictEqual((await call('repo-backup-cancel', {})).ok, false);
} finally {
g.server.close();
}
});