Multi-Hoster-Upload/tests/backup-crypto.test.js
Administrator 9305d806b0 test: add 4 new tests for untested code paths
- Concurrent saves preserve both hosters and globalSettings (write queue)
- Backup recovery when main config file is corrupted (.bak fallback)
- encrypt() rejects empty/null/undefined password
- decrypt() rejects empty/null password

All 63 tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:11:56 +01:00

72 lines
2.7 KiB
JavaScript

const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const { encrypt, decrypt } = require('../lib/backup-crypto');
describe('backup-crypto', () => {
const sampleConfig = {
hosters: { 'doodstream.com': { enabled: true, apiKey: 'test-key-123' } },
hosterSettings: { 'doodstream.com': { retries: 3 } },
globalSettings: { alwaysOnTop: false },
history: [{ file: 'test.mkv', link: 'https://example.com/abc' }]
};
it('encrypt then decrypt round-trips with correct password', () => {
const buf = encrypt(sampleConfig, 'mySecret!');
const result = decrypt(buf, 'mySecret!');
assert.deepStrictEqual(result, sampleConfig);
});
it('decrypt with wrong password throws', () => {
const buf = encrypt(sampleConfig, 'correct');
assert.throws(() => decrypt(buf, 'wrong'), /Falsches Passwort/);
});
it('decrypt with corrupted data throws', () => {
const buf = encrypt(sampleConfig, 'pw');
buf[buf.length - 1] ^= 0xff; // flip last byte
assert.throws(() => decrypt(buf, 'pw'), /Falsches Passwort/);
});
it('decrypt with invalid magic throws', () => {
// Buffer must be long enough to pass the length check (>= 4+16+12+16+1 = 49)
const buf = Buffer.alloc(60, 0x41); // 60 bytes of 'A'
assert.throws(() => decrypt(buf, 'pw'), /Keine gültige/);
});
it('decrypt with too-short buffer throws', () => {
assert.throws(() => decrypt(Buffer.alloc(10), 'pw'), /Ungültiges Backup-Format/);
});
it('handles empty config gracefully', () => {
const empty = { hosters: {}, hosterSettings: {}, globalSettings: {}, history: [] };
const buf = encrypt(empty, 'pw');
assert.deepStrictEqual(decrypt(buf, 'pw'), empty);
});
it('handles unicode passwords', () => {
const buf = encrypt(sampleConfig, 'Pässwört🔑');
const result = decrypt(buf, 'Pässwört🔑');
assert.deepStrictEqual(result, sampleConfig);
});
it('encrypt rejects empty password', () => {
assert.throws(() => encrypt(sampleConfig, ''), /Passwort/);
assert.throws(() => encrypt(sampleConfig, null), /Passwort/);
assert.throws(() => encrypt(sampleConfig, undefined), /Passwort/);
});
it('decrypt rejects empty password', () => {
const buf = encrypt(sampleConfig, 'valid');
assert.throws(() => decrypt(buf, ''), /Passwort/);
assert.throws(() => decrypt(buf, null), /Passwort/);
});
it('each encryption produces different output (random salt/iv)', () => {
const a = encrypt(sampleConfig, 'same');
const b = encrypt(sampleConfig, 'same');
assert.ok(!a.equals(b), 'two encryptions should differ');
// but both decrypt to same result
assert.deepStrictEqual(decrypt(a, 'same'), decrypt(b, 'same'));
});
});