mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 10:51:36 -04:00
Six helpers turned a non-OK response into an empty value, so their
consumers rendered a server error as "nothing here":
* getConfigBackups -> {dates: []}: "No backups available for this
config." A thrown request also left "Loading backups..." forever.
* getInventoryBackups -> []: "No backups yet."
* getDownloaders -> {downloaders: []} on a throw, the error body on a
non-OK response (which then crashed the .map): the RSS form's
Downloader select sat empty.
* searchMap -> {results: []}: "No map locations found".
* getMapSearchStatus -> {indexed: [], missing: []}: "No maps are
installed".
* flasherSearch -> [null, null], then `|| []`: "No .bin firmware
found".
Invariants now enforced and tested:
* On a non-OK response each helper returns undefined; an empty value
only ever comes from the server.
* Each consumer shows an error for a failed or thrown fetch and keeps
its "none" message for a confirmed empty result. The Downloader
select is disabled and says it is loading or could not fetch. A
failed map status is unknown, never "no maps are installed".
* The map search tab loads from the first render, and a query shorter
than the two characters the request needs now says so instead of
"No map locations found".
Archive browsers and recent tags are left as they are: a failed fetch
there hides an optional picker or suggestion list without claiming
anything.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
2.7 KiB
JavaScript
84 lines
2.7 KiB
JavaScript
import {
|
|
flasherSearch, getConfigBackups, getDownloaders, getInventoryBackups, getMapSearchStatus, searchMap,
|
|
} from './api';
|
|
|
|
// A helper that fails must say so. These used to turn a non-OK response into an empty value
|
|
// ([], {dates: []}, {results: []}, ...), so every consumer rendered "nothing here" for a server
|
|
// error. They now return undefined; an empty value only ever comes from the server.
|
|
|
|
jest.mock('./components/ui', () => ({
|
|
...jest.requireActual('./components/ui'),
|
|
toast: jest.fn(),
|
|
}));
|
|
|
|
const response = (ok, body = {}) => {
|
|
const r = {
|
|
ok,
|
|
status: ok ? 200 : 500,
|
|
json: async () => body,
|
|
text: async () => JSON.stringify(body),
|
|
};
|
|
r.clone = () => r;
|
|
return r;
|
|
};
|
|
|
|
let originalFetch;
|
|
beforeEach(() => {
|
|
originalFetch = global.fetch;
|
|
});
|
|
afterEach(() => {
|
|
global.fetch = originalFetch;
|
|
});
|
|
|
|
const failWith = () => {
|
|
global.fetch = jest.fn(() => Promise.resolve(response(false, {error: 'boom'})));
|
|
};
|
|
const succeedWith = (body) => {
|
|
global.fetch = jest.fn(() => Promise.resolve(response(true, body)));
|
|
};
|
|
|
|
describe('on a non-OK response, the helper returns undefined', () => {
|
|
test.each([
|
|
['getConfigBackups', () => getConfigBackups('wrolpi.yaml')],
|
|
['getInventoryBackups', () => getInventoryBackups('food')],
|
|
['getDownloaders', () => getDownloaders()],
|
|
['searchMap', () => searchMap('camp')],
|
|
['getMapSearchStatus', () => getMapSearchStatus()],
|
|
['flasherSearch', () => flasherSearch(null, null)],
|
|
])('%s', async (_name, call) => {
|
|
failWith();
|
|
expect(await call()).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('a confirmed empty result is still empty', () => {
|
|
test('getConfigBackups', async () => {
|
|
succeedWith({dates: []});
|
|
expect(await getConfigBackups('wrolpi.yaml')).toEqual({dates: []});
|
|
});
|
|
|
|
test('getInventoryBackups', async () => {
|
|
succeedWith({dates: []});
|
|
expect(await getInventoryBackups('food')).toEqual([]);
|
|
});
|
|
|
|
test('getDownloaders', async () => {
|
|
succeedWith({downloaders: []});
|
|
expect(await getDownloaders()).toEqual({downloaders: []});
|
|
});
|
|
|
|
test('searchMap', async () => {
|
|
succeedWith({results: [], total: 0});
|
|
expect(await searchMap('camp')).toEqual({results: [], total: 0});
|
|
});
|
|
|
|
test('getMapSearchStatus', async () => {
|
|
succeedWith({indexed: [], missing: []});
|
|
expect(await getMapSearchStatus()).toEqual({indexed: [], missing: []});
|
|
});
|
|
|
|
test('flasherSearch', async () => {
|
|
succeedWith({file_groups: [], totals: {file_groups: 0}});
|
|
expect(await flasherSearch(null, null)).toEqual([[], 0]);
|
|
});
|
|
});
|