Files
Git-Manager-Gui/__tests__/uploadCancel.test.js
T
2026-10-22 09:13:16 +00:00

147 lines
5.4 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');
const {
uploadItemsViaGiteaApi,
createUploadCancelledError,
isUploadCancelledError,
throwIfUploadCancelled
} = require('../src/git/apiHandler.js');
test('Abbruchfehler: Erkennung und throwIfUploadCancelled', () => {
assert.strictEqual(isUploadCancelledError(createUploadCancelledError()), true);
assert.strictEqual(isUploadCancelledError(new Error('anderer Fehler')), false);
assert.strictEqual(isUploadCancelledError(null), false);
assert.doesNotThrow(() => throwIfUploadCancelled(undefined));
assert.doesNotThrow(() => throwIfUploadCancelled(new AbortController().signal));
const c = new AbortController();
c.abort();
assert.throws(() => throwIfUploadCancelled(c.signal), (e) => isUploadCancelledError(e));
});
function makeTempFile() {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gm-cancel-'));
const file = path.join(dir, 'a.txt');
fs.writeFileSync(file, 'hallo');
return { dir, file };
}
test('API-Upload: bereits abgebrochenes Signal sendet nichts', async () => {
const { dir, file } = makeTempFile();
try {
const c = new AbortController();
c.abort();
await assert.rejects(
uploadItemsViaGiteaApi({
token: 't', url: 'http://127.0.0.1:1', owner: 'o', repo: 'r', branch: 'main',
items: [{ localFile: file, targetPath: 'a.txt' }], message: 'm', signal: c.signal
}),
(e) => isUploadCancelledError(e)
);
} finally { fs.rmSync(dir, { recursive: true, force: true }); }
});
test('API-Upload: Abbruch während der Übertragung wirft Abbruchfehler und landet nicht in "failed"', async () => {
const { dir, file } = makeTempFile();
let postSeen = false;
const server = http.createServer((req, res) => {
if (req.method === 'POST') {
postSeen = true;
setTimeout(() => { res.writeHead(201, { 'content-type': 'application/json' }); res.end('{}'); }, 3000);
return;
}
res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify({ default_branch: 'main', tree: [], entries: [] }));
});
await new Promise(r => server.listen(0, '127.0.0.1', r));
const { port } = server.address();
try {
const c = new AbortController();
const started = Date.now();
setTimeout(() => c.abort(), 400);
await assert.rejects(
uploadItemsViaGiteaApi({
token: 't', url: `http://127.0.0.1:${port}`, owner: 'o', repo: 'r', branch: 'main',
items: [{ localFile: file, targetPath: 'a.txt' }], message: 'm', signal: c.signal
}),
(e) => isUploadCancelledError(e)
);
assert.ok(postSeen, 'Der POST muss gestartet sein, sonst prüft der Test nichts');
assert.ok(Date.now() - started < 2500, 'Abbruch muss sofort greifen, nicht erst nach der Serverantwort');
} finally {
server.closeAllConnections?.();
await new Promise(r => server.close(r));
fs.rmSync(dir, { recursive: true, force: true });
}
});
const { createPauseGate } = require('../src/features/pauseGate.js');
function gitea(onPost) {
return http.createServer((req, res) => {
if (req.method === 'POST') {
onPost();
res.writeHead(201, { 'content-type': 'application/json' });
res.end('{}');
return;
}
res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify({ default_branch: 'main', tree: [], entries: [] }));
});
}
test('API-Upload: während der Pause wird nichts gesendet, nach resume() schon', async () => {
const { dir, file } = makeTempFile();
let posts = 0;
const server = gitea(() => { posts++; });
await new Promise(r => server.listen(0, '127.0.0.1', r));
const { port } = server.address();
const gate = createPauseGate();
try {
gate.pause();
const upload = uploadItemsViaGiteaApi({
token: 't', url: `http://127.0.0.1:${port}`, owner: 'o', repo: 'r', branch: 'main',
items: [{ localFile: file, targetPath: 'a.txt' }], message: 'm',
waitIfPaused: (s) => gate.wait(s)
});
await new Promise(r => setTimeout(r, 700));
assert.strictEqual(posts, 0, 'Während der Pause darf kein Paket gesendet werden');
gate.resume();
const result = await upload;
assert.strictEqual(posts, 1);
assert.deepStrictEqual(result.uploaded, ['a.txt']);
} finally {
await new Promise(r => server.close(r));
fs.rmSync(dir, { recursive: true, force: true });
}
});
test('API-Upload: Abbruch während der Pause beendet den Upload sofort', async () => {
const { dir, file } = makeTempFile();
let posts = 0;
const server = gitea(() => { posts++; });
await new Promise(r => server.listen(0, '127.0.0.1', r));
const { port } = server.address();
const gate = createPauseGate();
try {
gate.pause();
const c = new AbortController();
const upload = uploadItemsViaGiteaApi({
token: 't', url: `http://127.0.0.1:${port}`, owner: 'o', repo: 'r', branch: 'main',
items: [{ localFile: file, targetPath: 'a.txt' }], message: 'm',
signal: c.signal, waitIfPaused: (s) => gate.wait(s)
});
setTimeout(() => c.abort(), 400);
await assert.rejects(upload, (e) => isUploadCancelledError(e));
assert.strictEqual(posts, 0);
assert.strictEqual(gate.waitingCount(), 0);
} finally {
await new Promise(r => server.close(r));
fs.rmSync(dir, { recursive: true, force: true });
}
});