133 lines
5.4 KiB
JavaScript
133 lines
5.4 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert');
|
|
const {
|
|
summarizePerHoster,
|
|
classifyErrorCategory,
|
|
summarizeBatchErrors,
|
|
isRetryableCategory
|
|
} = require('../lib/stats');
|
|
|
|
function makeBatch(timestamp, results) {
|
|
return {
|
|
id: 'b-' + timestamp,
|
|
timestamp: new Date(timestamp).toISOString(),
|
|
files: [{ name: 'foo.mp4', size: 1, results }]
|
|
};
|
|
}
|
|
|
|
test('summarizePerHoster counts ok and fail per hoster across all batches', () => {
|
|
const history = [
|
|
makeBatch(1, [
|
|
{ hoster: 'voe.sx', status: 'done' },
|
|
{ hoster: 'byse.sx', status: 'error', error: 'Not video file format' }
|
|
]),
|
|
makeBatch(2, [
|
|
{ hoster: 'voe.sx', status: 'done' },
|
|
{ hoster: 'voe.sx', status: 'error', error: 'CSRF' },
|
|
{ hoster: 'byse.sx', status: 'done' }
|
|
])
|
|
];
|
|
const s = summarizePerHoster(history);
|
|
assert.strictEqual(s['voe.sx'].ok, 2);
|
|
assert.strictEqual(s['voe.sx'].fail, 1);
|
|
assert.strictEqual(s['voe.sx'].total, 3);
|
|
assert.strictEqual(Math.round(s['voe.sx'].rate * 100), 67);
|
|
assert.strictEqual(s['byse.sx'].ok, 1);
|
|
assert.strictEqual(s['byse.sx'].fail, 1);
|
|
assert.strictEqual(s['byse.sx'].rate, 0.5);
|
|
});
|
|
|
|
test('summarizePerHoster honors sinceMs cutoff', () => {
|
|
const history = [
|
|
makeBatch(1000, [{ hoster: 'voe.sx', status: 'done' }]),
|
|
makeBatch(5000, [{ hoster: 'voe.sx', status: 'error', error: 'x' }])
|
|
];
|
|
const s = summarizePerHoster(history, { sinceMs: 3000 });
|
|
assert.strictEqual(s['voe.sx'].ok, 0);
|
|
assert.strictEqual(s['voe.sx'].fail, 1);
|
|
});
|
|
|
|
test('summarizePerHoster honors lastNBatches (newest first)', () => {
|
|
const history = [
|
|
makeBatch(1000, [{ hoster: 'voe.sx', status: 'done' }]),
|
|
makeBatch(2000, [{ hoster: 'voe.sx', status: 'done' }]),
|
|
makeBatch(3000, [{ hoster: 'voe.sx', status: 'error', error: 'x' }])
|
|
];
|
|
const s = summarizePerHoster(history, { lastNBatches: 1 });
|
|
assert.strictEqual(s['voe.sx'].ok, 0);
|
|
assert.strictEqual(s['voe.sx'].fail, 1);
|
|
});
|
|
|
|
test('summarizePerHoster handles empty / malformed input', () => {
|
|
assert.deepStrictEqual(summarizePerHoster(null), {});
|
|
assert.deepStrictEqual(summarizePerHoster([]), {});
|
|
assert.deepStrictEqual(summarizePerHoster([{ id: 'x', files: null }]), {});
|
|
});
|
|
|
|
test('classifyErrorCategory: file-rejected phrases', () => {
|
|
assert.strictEqual(classifyErrorCategory('Byse lehnte Datei ab: Not video file format'), 'file-rejected');
|
|
assert.strictEqual(classifyErrorCategory('Duplicate file already exists'), 'file-rejected');
|
|
assert.strictEqual(classifyErrorCategory('Datei zu groß (Max: 5 GB)'), 'file-rejected');
|
|
});
|
|
|
|
test('classifyErrorCategory: account-error phrases', () => {
|
|
assert.strictEqual(classifyErrorCategory('Quota exceeded'), 'account-error');
|
|
assert.strictEqual(classifyErrorCategory('account banned'), 'account-error');
|
|
assert.strictEqual(classifyErrorCategory('not enough disk space'), 'account-error');
|
|
});
|
|
|
|
test('classifyErrorCategory: hoster-transient phrases', () => {
|
|
assert.strictEqual(classifyErrorCategory('CSRF-Token nicht gefunden'), 'hoster-transient');
|
|
assert.strictEqual(classifyErrorCategory('Kein Upload-Server erhalten: server busy'), 'hoster-transient');
|
|
assert.strictEqual(classifyErrorCategory('Kein Filecode'), 'hoster-transient');
|
|
});
|
|
|
|
test('classifyErrorCategory: network phrases', () => {
|
|
assert.strictEqual(classifyErrorCategory('socket hang up'), 'network');
|
|
assert.strictEqual(classifyErrorCategory('fetch failed'), 'network');
|
|
assert.strictEqual(classifyErrorCategory('Timeout while reading'), 'network');
|
|
});
|
|
|
|
test('classifyErrorCategory: aborted is its own bucket (not retryable)', () => {
|
|
assert.strictEqual(classifyErrorCategory('Abgebrochen'), 'aborted');
|
|
assert.strictEqual(isRetryableCategory('aborted'), false);
|
|
});
|
|
|
|
test('classifyErrorCategory: unknown for everything else', () => {
|
|
assert.strictEqual(classifyErrorCategory(''), 'unknown');
|
|
assert.strictEqual(classifyErrorCategory(null), 'unknown');
|
|
assert.strictEqual(classifyErrorCategory('Some weird thing'), 'unknown');
|
|
});
|
|
|
|
test('summarizeBatchErrors buckets results by category', () => {
|
|
const summary = {
|
|
files: [
|
|
{ name: 'a.mp4', results: [
|
|
{ hoster: 'voe.sx', status: 'done' },
|
|
{ hoster: 'byse.sx', status: 'error', error: 'Not video file format' }
|
|
] },
|
|
{ name: 'b.mp4', results: [
|
|
{ hoster: 'voe.sx', status: 'error', error: 'CSRF' },
|
|
{ hoster: 'doodstream.com', status: 'error', error: 'socket hang up' }
|
|
] }
|
|
]
|
|
};
|
|
const buckets = summarizeBatchErrors(summary);
|
|
assert.strictEqual(buckets['file-rejected'].length, 1);
|
|
assert.strictEqual(buckets['file-rejected'][0].hoster, 'byse.sx');
|
|
assert.strictEqual(buckets['hoster-transient'].length, 1);
|
|
assert.strictEqual(buckets['hoster-transient'][0].hoster, 'voe.sx');
|
|
assert.strictEqual(buckets['network'].length, 1);
|
|
assert.strictEqual(buckets['network'][0].hoster, 'doodstream.com');
|
|
assert.strictEqual(buckets['account-error'].length, 0);
|
|
});
|
|
|
|
test('isRetryableCategory: only transient + network + unknown retry-worthy', () => {
|
|
assert.strictEqual(isRetryableCategory('hoster-transient'), true);
|
|
assert.strictEqual(isRetryableCategory('network'), true);
|
|
assert.strictEqual(isRetryableCategory('unknown'), true);
|
|
assert.strictEqual(isRetryableCategory('file-rejected'), false);
|
|
assert.strictEqual(isRetryableCategory('account-error'), false);
|
|
assert.strictEqual(isRetryableCategory('aborted'), false);
|
|
});
|