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