Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/packages/firebird-ng/display/src/lib/services/geometry.service.spec.ts is written in an unsupported language. File is not indexed.

0001 /**
0002  * GeometryService and its worker: the worker comes from the application's
0003  * factory (withWorkers), a failed worker rejects what waits on it and is
0004  * replaced on the next load, and an aborted load cancels the worker request.
0005  * The worker is a fake that records requests and answers on demand.
0006  */
0007 
0008 import { TestBed } from '@angular/core/testing';
0009 import { provideHttpClient } from '@angular/common/http';
0010 import { ServerConfigService } from '@dexvis/app-features';
0011 import { withWorkers } from '@dexvis/firebird-ng/api';
0012 import type { GeometryLoadRequest, WorkerResponse } from '@dexvis/firebird-ng/workers/geometry';
0013 import { GeometryService } from './geometry.service';
0014 
0015 class FakeWorker {
0016   static instances: FakeWorker[] = [];
0017   readonly requests: Array<{ type: string; requestId: string }> = [];
0018   onmessage: ((event: { data: WorkerResponse }) => void) | null = null;
0019   onerror: ((event: { message: string }) => void) | null = null;
0020   terminated = false;
0021 
0022   constructor() {
0023     FakeWorker.instances.push(this);
0024   }
0025 
0026   postMessage(request: GeometryLoadRequest): void {
0027     this.requests.push(request);
0028   }
0029 
0030   terminate(): void {
0031     this.terminated = true;
0032   }
0033 }
0034 
0035 /** Lets the rule resolution before each worker request run. */
0036 async function settle(): Promise<void> {
0037   for (let i = 0; i < 5; i++) await new Promise(resolve => setTimeout(resolve, 0));
0038 }
0039 
0040 describe('GeometryService worker', () => {
0041   const worker = (index = 0) => FakeWorker.instances[index];
0042 
0043   function create(registerWorkers = true): GeometryService {
0044     const startFake = () => new FakeWorker() as unknown as Worker;
0045     TestBed.configureTestingModule({
0046       providers: [
0047         provideHttpClient(),
0048         ...(registerWorkers ? withWorkers({ geometry: startFake, rootFile: startFake }).providers : []),
0049       ],
0050     });
0051     TestBed.inject(ServerConfigService).setUnitTestConfig({});
0052     return TestBed.inject(GeometryService);
0053   }
0054 
0055   beforeEach(() => {
0056     FakeWorker.instances = [];
0057     vi.stubGlobal('Worker', FakeWorker);
0058     vi.spyOn(console, 'log').mockImplementation(() => undefined);
0059     vi.spyOn(console, 'error').mockImplementation(() => undefined);
0060     vi.spyOn(console, 'time').mockImplementation(() => undefined);
0061     vi.spyOn(console, 'timeEnd').mockImplementation(() => undefined);
0062     TestBed.resetTestingModule();
0063   });
0064 
0065   afterEach(() => {
0066     vi.unstubAllGlobals();
0067     vi.restoreAllMocks();
0068   });
0069 
0070   it('names the missing feature when the application registered no workers', async () => {
0071     await expect(create(false).loadGeometry('https://h/detector.root')).rejects.toThrow('add withWorkers');
0072   });
0073 
0074   it('rejects the waiting load when the worker fails, and starts a fresh worker for the next', async () => {
0075     const service = create();
0076     const load = service.loadGeometry('https://h/detector.root');
0077     await settle();
0078     expect(worker().requests).toEqual([expect.objectContaining({ type: 'load' })]);
0079 
0080     worker().onerror?.({ message: 'worker-ABC.js: 404' });
0081     await expect(load).rejects.toThrow('Geometry worker error: worker-ABC.js: 404');
0082     expect(worker().terminated).toBe(true);
0083 
0084     void service.loadGeometry('https://h/detector.root').catch(() => undefined);
0085     await settle();
0086     expect(FakeWorker.instances.length).toBe(2);
0087     expect(worker(1).requests).toEqual([expect.objectContaining({ type: 'load' })]);
0088   });
0089 
0090   it('cancels the worker request when the signal aborts, and rejects with the reason', async () => {
0091     const service = create();
0092     const abort = new AbortController();
0093     const load = service.loadGeometry('https://h/detector.root', { signal: abort.signal });
0094     await settle();
0095     const [request] = worker().requests;
0096 
0097     abort.abort(new DOMException('a newer load', 'AbortError'));
0098     expect(worker().requests[1]).toEqual({ type: 'cancel', requestId: request.requestId });
0099     worker().onmessage?.({ data: { type: 'cancelled', requestId: request.requestId } });
0100     await expect(load).rejects.toThrow('a newer load');
0101   });
0102 });