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

114 lines
5.2 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert');
const { buildEntry, filterEntries, createUploadHistory } = require('../src/features/uploadHistory.js');
function memoryFs(initial) {
const files = new Map(initial ? [['h.json', initial]] : []);
return {
files,
readFileSync: (f) => { if (!files.has(f)) throw new Error('ENOENT'); return files.get(f); },
writeFileSync: (f, c) => { files.set(f, c); }
};
}
test('buildEntry: erfolgreicher Upload', () => {
const e = buildEntry('Drag & Drop', { owner: 'a', repo: 'b' }, { ok: true, results: [{}, {}, {}] }, 1000, null, 4000);
assert.strictEqual(e.status, 'ok');
assert.strictEqual(e.repo, 'a/b');
assert.strictEqual(e.files, 3);
assert.strictEqual(e.durationMs, 3000);
assert.strictEqual(e.error, '');
});
test('buildEntry: abgebrochen ist kein Fehler und trägt keinen Fehlertext', () => {
const e = buildEntry('Ordner', { owner: 'a', repo: 'b' }, { ok: false, cancelled: true, error: 'Upload abgebrochen' }, 0, null, 10);
assert.strictEqual(e.status, 'abgebrochen');
assert.strictEqual(e.error, '');
});
test('buildEntry: Fehler und geworfene Ausnahmen', () => {
const failed = buildEntry('Dateien', { owner: 'a', repo: 'b' }, { ok: false, error: 'kaputt', failedCount: 2 }, 0, null, 10);
assert.strictEqual(failed.status, 'fehler');
assert.strictEqual(failed.error, 'kaputt');
assert.strictEqual(failed.failed, 2);
const thrown = buildEntry('Dateien', {}, null, 0, new Error('boom'), 10);
assert.strictEqual(thrown.status, 'fehler');
assert.strictEqual(thrown.error, 'boom');
assert.strictEqual(thrown.repo, '');
});
test('buildEntry: Einzeldatei zählt als eine Datei, lange Fehler werden gekürzt', () => {
assert.strictEqual(buildEntry('x', {}, { ok: true, singleFile: true }, 0, null, 1).files, 1);
assert.strictEqual(buildEntry('x', {}, { ok: false, error: 'e'.repeat(1000) }, 0, null, 1).error.length, 300);
});
test('filterEntries: nach Status und Repo-Suche', () => {
const list = [
{ repo: 'a/alpha', status: 'ok' },
{ repo: 'a/beta', status: 'fehler' },
{ repo: 'b/Alpha-2', status: 'abgebrochen' }
];
assert.strictEqual(filterEntries(list).length, 3);
assert.deepStrictEqual(filterEntries(list, { status: 'fehler' }).map(e => e.repo), ['a/beta']);
assert.deepStrictEqual(filterEntries(list, { query: 'ALPHA' }).map(e => e.repo), ['a/alpha', 'b/Alpha-2']);
assert.strictEqual(filterEntries(list, { status: 'ok', query: 'beta' }).length, 0);
});
test('Verlauf: neueste zuerst, auf max Einträge begrenzt', () => {
const history = createUploadHistory({ file: 'h.json', max: 3, fs: memoryFs() });
for (let i = 1; i <= 5; i++) history.record({ n: i });
assert.deepStrictEqual(history.read().map(e => e.n), [5, 4, 3]);
});
test('Verlauf: kaputte oder fehlende Datei ergibt eine leere Liste', () => {
assert.deepStrictEqual(createUploadHistory({ file: 'h.json', fs: memoryFs() }).read(), []);
assert.deepStrictEqual(createUploadHistory({ file: 'h.json', fs: memoryFs('{kaputt') }).read(), []);
assert.deepStrictEqual(createUploadHistory({ file: 'h.json', fs: memoryFs('{"a":1}') }).read(), []);
});
test('Verlauf: leeren', () => {
const history = createUploadHistory({ file: 'h.json', fs: memoryFs() });
history.record({ n: 1 });
history.clear();
assert.deepStrictEqual(history.read(), []);
});
test('Verlauf: Schreibfehler werfen nicht', () => {
const broken = { readFileSync: () => '[]', writeFileSync: () => { throw new Error('Platte voll'); } };
assert.strictEqual(createUploadHistory({ file: 'h.json', fs: broken }).record({ n: 1 }), false);
});
const { toCsv, formatDate } = require('../src/features/uploadHistory.js');
test('formatDate: TT.MM.JJJJ HH:MM:SS, ungültige Eingabe ergibt leeren Text', () => {
assert.match(formatDate('2026-10-22T07:11:53.080Z'), /^\d{2}\.\d{2}\.2026 \d{2}:\d{2}:\d{2}$/);
assert.strictEqual(formatDate('kein Datum'), '');
});
test('toCsv: BOM, Semikolon, Windows-Zeilenenden, Kopfzeile', () => {
const csv = toCsv([{ at: '2026-10-22T07:11:53.080Z', repo: 'a/b', kind: 'Drag & Drop', files: 3, failed: 0, status: 'ok', durationMs: 2500, error: '' }]);
assert.strictEqual(csv.charCodeAt(0), 0xFEFF);
const lines = csv.slice(1).split('\r\n');
assert.strictEqual(lines[0], 'Datum;Repo;Art;Dateien;Fehlgeschlagen;Ergebnis;Dauer (s);Fehler');
assert.match(lines[1], /;a\/b;Drag & Drop;3;0;Erfolgreich;2\.5;$/);
assert.strictEqual(lines[2], '');
});
test('toCsv: Anführungszeichen, Semikolon und Zeilenumbrüche werden maskiert', () => {
const csv = toCsv([{ at: '2026-10-22T07:11:53Z', repo: 'a/b', kind: 'x', files: null, failed: 1, status: 'fehler', durationMs: 100, error: 'sagt "nein"; und\nmehr' }]);
assert.ok(csv.includes('"sagt ""nein""; und\nmehr"'));
assert.match(csv, /;Fehler;0\.1;/);
});
test('toCsv: Excel-Formeln werden entschärft', () => {
const csv = toCsv([{ at: '2026-10-22T07:11:53Z', repo: '=cmd|calc', kind: '+x', files: 1, failed: 0, status: 'ok', durationMs: 0, error: '@SUMME(A1)' }]);
assert.ok(csv.includes(";'=cmd|calc;"));
assert.ok(csv.includes(";'+x;"));
assert.ok(csv.includes(";'@SUMME(A1)"));
});
test('toCsv: leere Liste liefert nur die Kopfzeile', () => {
assert.strictEqual(toCsv([]).slice(1).split('\r\n').filter(Boolean).length, 1);
});