117 lines
5.2 KiB
JavaScript
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();
|
|
}
|
|
});
|