Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/packages/root2dex/src/entries.spec.ts is written in an unsupported language. File is not indexed.

0001 /**
0002  * Entry selections: the browser converter must treat selections the way the
0003  * pyrobird convert endpoint does (clamp to the file with one warning, reject
0004  * an oversized request or one with no entry in the file), with the same
0005  * messages, and must never expand a selection before its size is checked and
0006  * its ranges are clamped.
0007  */
0008 
0009 import { describe, expect, it } from 'vitest';
0010 import { existsSync } from 'node:fs';
0011 import { resolve } from 'node:path';
0012 import {
0013   DEFAULT_MAX_ENTRIES,
0014   EntrySelectionError,
0015   clampRanges,
0016   countEntries,
0017   formatRanges,
0018   outOfRangeParts,
0019   parseEntryRanges,
0020   selectEntries,
0021 } from './entries';
0022 import { convertRootToDex, parseEntryNumbers } from './convert';
0023 
0024 const REPO = resolve(__dirname, '../../..');
0025 const EIC_FILE = resolve(
0026   REPO,
0027   'pyrobird/tests/unit_tests/data/reco_2024-09_craterlake_2evt.edm4eic.root',
0028 );
0029 
0030 describe('parseEntryRanges', () => {
0031   it('keeps ranges as pairs; parseEntryNumbers is their expansion', () => {
0032     expect(parseEntryRanges('1,2-5,8')).toEqual([[1, 1], [2, 5], [8, 8]]);
0033     expect(parseEntryNumbers('1,2-5,8')).toEqual([1, 2, 3, 4, 5, 8]);
0034     expect(parseEntryRanges('0-20000000')).toEqual([[0, 20000000]]);
0035   });
0036 
0037   it('rejects text outside the grammar', () => {
0038     expect(() => parseEntryRanges('a-b')).toThrow(/Invalid entry format/);
0039     expect(() => parseEntryRanges('5-1')).toThrow(/start must be <= end/);
0040   });
0041 });
0042 
0043 describe('selectEntries', () => {
0044   it('rejects a huge selection by its size before expanding it', () => {
0045     const started = performance.now();
0046     expect(() => selectEntries(parseEntryRanges('0-1000000000000'), 3, DEFAULT_MAX_ENTRIES))
0047       .toThrow('The selection names 1000000000001 entries; at most 1000 are allowed per request.');
0048     expect(performance.now() - started).toBeLessThan(100);
0049   });
0050 
0051   it('clamps a huge selection to the file without expanding it, also with no cap', () => {
0052     const started = performance.now();
0053     expect(selectEntries(parseEntryRanges('0-1000000000000'), 3)).toEqual({
0054       entries: [0, 1, 2],
0055       warning: 'Event 3-1000000000000 is out of range: the file holds 3 events (0..2); converting 0-2',
0056     });
0057     expect(performance.now() - started).toBeLessThan(100);
0058   });
0059 
0060   it('counts the request before the clamp: an oversized request stays rejected', () => {
0061     expect(() => selectEntries(parseEntryRanges('0-1999'), 10, DEFAULT_MAX_ENTRIES))
0062       .toThrow('The selection names 2000 entries; at most 1000 are allowed per request.');
0063   });
0064 
0065   it('drops the entries outside the file with one warning, as pyrobird does', () => {
0066     expect(selectEntries(parseEntryRanges('0-5'), 2)).toEqual({
0067       entries: [0, 1],
0068       warning: 'Event 2-5 is out of range: the file holds 2 events (0..1); converting 0-1',
0069     });
0070     expect(selectEntries(parseEntryRanges('-2-1'), 2)).toEqual({
0071       entries: [0, 1],
0072       warning: 'Event -2--1 is out of range: the file holds 2 events (0..1); converting 0-1',
0073     });
0074     expect(selectEntries(parseEntryRanges('4,9,1-2,1'), 5)).toEqual({
0075       entries: [4, 1, 2, 1],
0076       warning: 'Event 9 is out of range: the file holds 5 events (0..4); converting 4, 1-2, 1',
0077     });
0078   });
0079 
0080   it('rejects a selection with no entry in the file, naming the entry count', () => {
0081     expect(() => selectEntries(parseEntryRanges('50-60'), 10))
0082       .toThrow('Event 50-60 is out of range: the file holds 10 events (0..9)');
0083     expect(() => selectEntries(parseEntryRanges('0'), 0))
0084       .toThrow('Event 0 is out of range: the file holds no events');
0085     expect(() => selectEntries(parseEntryRanges('9'), 2)).toThrow(EntrySelectionError);
0086   });
0087 
0088   it('selects nothing from an empty selection, without a warning', () => {
0089     expect(selectEntries([], 5)).toEqual({ entries: [], warning: null });
0090   });
0091 
0092   it('clamps ranges to the file in the order given', () => {
0093     expect(clampRanges([[-2, 1], [3, 9], [7, 8]], 5)).toEqual([[0, 1], [3, 4]]);
0094     expect(clampRanges([[5, 9]], 5)).toEqual([]);
0095   });
0096 
0097   it('lists at most five offending ranges', () => {
0098     const ranges = parseEntryRanges('10,11,12,13,14,15,16');
0099     expect(formatRanges(outOfRangeParts(ranges, 2)))
0100       .toBe('10, 11, 12, 13, 14 (and 2 more)');
0101   });
0102 
0103   it('expands an in-range selection in the requested order, repeats included', () => {
0104     const ranges = parseEntryRanges('3,0-1,3');
0105     expect(countEntries(ranges)).toBe(4);
0106     expect(selectEntries(ranges, 4, DEFAULT_MAX_ENTRIES)).toEqual({ entries: [3, 0, 1, 3], warning: null });
0107   });
0108 });
0109 
0110 describe('convertRootToDex', () => {
0111   it.runIf(existsSync(EIC_FILE))('converts the entries the file holds and warns once about the rest', async () => {
0112     const warnings: string[] = [];
0113     const dex = await convertRootToDex(EIC_FILE, '0-5', { onWarning: message => warnings.push(message) });
0114     expect(dex.events.map(event => event.id)).toEqual([0, 1]);
0115     expect(warnings).toEqual(['Event 2-5 is out of range: the file holds 2 events (0..1); converting 0-1']);
0116   });
0117 
0118   it.runIf(existsSync(EIC_FILE))('rejects a selection with no entry in the file before converting', async () => {
0119     await expect(convertRootToDex(EIC_FILE, '50-60'))
0120       .rejects.toThrow('Event 50-60 is out of range: the file holds 2 events (0..1)');
0121   });
0122 });