fix: rTorrent null-safety, configurable SAB_HISTORY_LIMIT, lastError visibility (#68)
Build and Push Docker Image / build (push) Successful in 59s
Docs Check / Markdown lint (push) Failing after 1m45s
Licence Check / Licence compatibility and copyright header verification (push) Successful in 2m7s
CI / Security audit (push) Successful in 2m33s
Docs Check / Mermaid diagram parse check (push) Successful in 2m55s
CI / Swagger Validation & Coverage (push) Successful in 3m19s
CI / Tests & coverage (push) Successful in 3m29s
Build and Push Docker Image / build (push) Successful in 59s
Docs Check / Markdown lint (push) Failing after 1m45s
Licence Check / Licence compatibility and copyright header verification (push) Successful in 2m7s
CI / Security audit (push) Successful in 2m33s
Docs Check / Mermaid diagram parse check (push) Successful in 2m55s
CI / Swagger Validation & Coverage (push) Successful in 3m19s
CI / Tests & coverage (push) Successful in 3m29s
- RTorrentClient: guard d.multicall2 returning non-array, per-row try/catch, explicit Number()/String() coercions, _extractArrInfo null-safe - RTorrentClient.getClientStatus: coerce rates through Number.isFinite - SABnzbdClient: history limit now reads SAB_HISTORY_LIMIT env var (default 10) - DownloadClient: added _recordLastError, _clearLastError, getLastError on base - All four clients call _recordLastError on failure, _clearLastError on success - DownloadClientRegistry.getAllClientStatuses: includes lastError in result - GET /api/status/status: exposes downloadClients[] array with per-client lastError - Tests: RTorrentClient null-safety + lastError, SABnzbd history limit + lastError, downloadClients.test expectation updated for new lastError field
This commit is contained in:
@@ -420,4 +420,68 @@ describe('RTorrentClient', () => {
|
||||
expect(status).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Null-safety (Issue #68)', () => {
|
||||
it('should return [] when d.multicall2 returns a non-array', async () => {
|
||||
mockMethodCall.mockImplementation((method, params, callback) => {
|
||||
callback(null, null);
|
||||
});
|
||||
const downloads = await client.getActiveDownloads();
|
||||
expect(downloads).toEqual([]);
|
||||
});
|
||||
|
||||
it('should skip malformed individual torrent rows instead of throwing', async () => {
|
||||
const torrents = [
|
||||
// valid row
|
||||
['hashA', 'Name A', 100, 50, 0, 0, 1, 1, 0, '/dl', ''],
|
||||
// malformed row (not an array)
|
||||
'not-an-array',
|
||||
// row with null/undefined fields
|
||||
['hashB', null, null, null, null, null, null, null, null, null, null]
|
||||
];
|
||||
mockMethodCall.mockImplementation((method, params, callback) => {
|
||||
callback(null, torrents);
|
||||
});
|
||||
const downloads = await client.getActiveDownloads();
|
||||
expect(downloads).toHaveLength(2);
|
||||
expect(downloads[0].id).toBe('hashA');
|
||||
expect(downloads[1].id).toBe('hashB');
|
||||
expect(downloads[1].title).toBe('');
|
||||
expect(downloads[1].size).toBe(0);
|
||||
});
|
||||
|
||||
it('_extractArrInfo should return {} for non-string filename', () => {
|
||||
expect(client._extractArrInfo(null)).toEqual({});
|
||||
expect(client._extractArrInfo(undefined)).toEqual({});
|
||||
expect(client._extractArrInfo(123)).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('lastError tracking (Issue #68)', () => {
|
||||
it('should record lastError on getActiveDownloads failure', async () => {
|
||||
mockMethodCall.mockImplementation((method, params, callback) => {
|
||||
callback(new Error('boom'));
|
||||
});
|
||||
await client.getActiveDownloads();
|
||||
expect(client.getLastError()).not.toBeNull();
|
||||
expect(client.getLastError().operation).toBe('getActiveDownloads');
|
||||
expect(client.getLastError().message).toBe('boom');
|
||||
});
|
||||
|
||||
it('should clear lastError on successful call', async () => {
|
||||
// First, fail.
|
||||
mockMethodCall.mockImplementationOnce((method, params, callback) => {
|
||||
callback(new Error('boom'));
|
||||
});
|
||||
await client.getActiveDownloads();
|
||||
expect(client.getLastError()).not.toBeNull();
|
||||
|
||||
// Then, succeed.
|
||||
mockMethodCall.mockImplementation((method, params, callback) => {
|
||||
callback(null, []);
|
||||
});
|
||||
await client.getActiveDownloads();
|
||||
expect(client.getLastError()).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user