Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/packages/firebird-ng/display/src/lib/utils/data-fetching.utils.spec.ts is written in an unsupported language. File is not indexed.

0001 import { afterEach, describe, expect, it, vi } from 'vitest';
0002 import JSZip from 'jszip';
0003 import { fetchTextFile, isDexZipMember, loadJSONFileEvents, loadZipFileEvents, readDexFile, readDexZip } from './data-fetching.utils';
0004 
0005 const DEX = {
0006   type: 'firebird-dex-json',
0007   version: '1.0',
0008   events: [{ id: 'event_0', pieces: [] }],
0009 };
0010 
0011 describe('readDexFile', () => {
0012   it('reads a plain .firebird.json file', async () => {
0013     const file = new File([JSON.stringify(DEX)], 'events.firebird.json');
0014     expect(await readDexFile(file)).toEqual(DEX);
0015   });
0016 
0017   it('reads the .json member of a dropped .zip', async () => {
0018     const zip = new JSZip();
0019     zip.file('events.firebird.json', JSON.stringify(DEX));
0020     const blob = await zip.generateAsync({ type: 'blob' });
0021     const file = new File([blob], 'events.firebird.zip');
0022     expect(await readDexFile(file)).toEqual(DEX);
0023   });
0024 
0025   it('throws on a file that is not JSON', async () => {
0026     const file = new File(['not json'], 'notes.json');
0027     await expect(readDexFile(file)).rejects.toThrow();
0028   });
0029 });
0030 
0031 describe('DEX zip members', () => {
0032   async function zipOf(members: Array<[string, string | null]>): Promise<Blob> {
0033     const zip = new JSZip();
0034     for (const [name, text] of members) {
0035       if (text === null) zip.folder(name);
0036       else zip.file(name, text);
0037     }
0038     return zip.generateAsync({ type: 'blob' });
0039   }
0040 
0041   it('accepts .json files only, outside __MACOSX and not dot-files', () => {
0042     expect(isDexZipMember('events.firebird.json')).toBe(true);
0043     expect(isDexZipMember('data/events.JSON')).toBe(true);
0044     expect(isDexZipMember('__MACOSX/._events.firebird.json')).toBe(false);
0045     expect(isDexZipMember('data/__MACOSX/events.json')).toBe(false);
0046     expect(isDexZipMember('._events.json')).toBe(false);
0047     expect(isDexZipMember('data/.hidden.json')).toBe(false);
0048     expect(isDexZipMember('folder.json/')).toBe(false);
0049     expect(isDexZipMember('readme.txt')).toBe(false);
0050   });
0051 
0052   it('skips the macOS metadata members that break JSON parsing', async () => {
0053     const blob = await zipOf([
0054       ['__MACOSX/', null],
0055       ['__MACOSX/._events.firebird.json', '\u0000\u0005binary resource fork'],
0056       ['.DS_Store', 'junk'],
0057       ['events.firebird.json', JSON.stringify(DEX)],
0058     ]);
0059     expect(await readDexZip(blob, 'mac.zip')).toEqual(DEX);
0060   });
0061 
0062   it('reads only the first DEX member, in archive order (pyrobird reads the same one)', async () => {
0063     const second = { ...DEX, events: [{ id: 'event_9', pieces: [] }] };
0064     const blob = await zipOf([
0065       ['notes.txt', 'hello'],
0066       ['first.firebird.json', JSON.stringify(DEX)],
0067       ['second.firebird.json', JSON.stringify(second)],
0068     ]);
0069     expect(await readDexZip(blob, 'two.zip')).toEqual(DEX);
0070   });
0071 
0072   it('inflates nothing but the chosen member', async () => {
0073     const blob = await zipOf([
0074       ['broken.txt', 'not json at all'],
0075       ['events.firebird.json', JSON.stringify(DEX)],
0076     ]);
0077     const archive = await JSZip.loadAsync(blob);
0078     const inflated: string[] = [];
0079     for (const file of Object.values(archive.files)) {
0080       const original = file.async.bind(file);
0081       (file as unknown as { async: unknown }).async = ((type: 'string') => {
0082         inflated.push(file.name);
0083         return original(type);
0084       }) as never;
0085     }
0086     const loadAsync = vi.spyOn(JSZip, 'loadAsync').mockResolvedValue(archive);
0087     expect(await readDexZip(blob, 'events.zip')).toEqual(DEX);
0088     expect(inflated).toEqual(['events.firebird.json']);
0089     loadAsync.mockRestore();
0090   });
0091 
0092   it('says so when no member holds DEX', async () => {
0093     const blob = await zipOf([['__MACOSX/._events.json', 'x'], ['readme.txt', 'x']]);
0094     await expect(readDexZip(blob, 'empty.zip')).rejects.toThrow("'empty.zip' holds no .json member");
0095   });
0096 });
0097 
0098 describe('fetching', () => {
0099   afterEach(() => vi.unstubAllGlobals());
0100 
0101   it('rejects a failed binary fetch with the HTTP status instead of unzipping the error page', async () => {
0102     vi.stubGlobal('fetch', async () => new Response('<html>gone</html>', { status: 404, statusText: 'Not Found' }));
0103     await expect(loadZipFileEvents('https://h/e.firebird.zip'))
0104       .rejects.toThrow('HTTP 404 Not Found (https://h/e.firebird.zip)');
0105   });
0106 
0107   it("carries the server's JSON error message", async () => {
0108     vi.stubGlobal('fetch', async () => new Response(
0109       JSON.stringify({ error: 'Event 50-60 is out of range: the file holds 2 events (0..1)' }),
0110       { status: 400, statusText: 'BAD REQUEST' }));
0111     await expect(fetchTextFile('https://h/api/v1/convert/auto/50-60?f=x.root'))
0112       .rejects.toThrow('HTTP 400 BAD REQUEST: Event 50-60 is out of range: the file holds 2 events (0..1)');
0113   });
0114 });
0115 
0116 describe('HTML answers for missing files', () => {
0117   afterEach(() => vi.unstubAllGlobals());
0118   const indexPage = '<!doctype html><html><body>app</body></html>';
0119 
0120   it('says that a JSON request got an HTML page', async () => {
0121     vi.stubGlobal('fetch', async () => new Response(indexPage, { status: 200 }));
0122     await expect(loadJSONFileEvents('assets/data/missing.firebird.json'))
0123       .rejects.toThrow("'assets/data/missing.firebird.json' is not JSON (the server sent an HTML page instead; the file is probably missing)");
0124   });
0125 
0126   it('says that a zip request got an HTML page', async () => {
0127     vi.stubGlobal('fetch', async () => new Response(indexPage, { status: 200 }));
0128     await expect(loadZipFileEvents('assets/data/missing.firebird.zip'))
0129       .rejects.toThrow("'assets/data/missing.firebird.zip' is not a readable zip archive (the server sent an HTML page instead");
0130   });
0131 });