147 lines
5.4 KiB
JavaScript
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 });
|
|
}
|
|
});
|