2323 * per-test and why the hook timeout is generous.
2424 */
2525
26- import { afterEach , beforeEach , describe , expect , it } from 'vitest' ;
26+ import { afterEach , beforeEach , describe , expect , it , vi } from 'vitest' ;
27+
28+ import { fromTransport , type DataStoreTransport } from './data-store-transport.js' ;
29+ import { extractGeoreferencingOnDemand } from './on-demand-georeferencing.js' ;
30+ import { contiguousSourceBytes } from './source-bytes.js' ;
2731
2832const postedMessages : unknown [ ] = [ ] ;
2933let originalSelf : unknown ;
@@ -44,6 +48,7 @@ const IFC = [
4448 'DATA;' ,
4549 "#1=IFCPROJECT('0000000000000000000001',$,'P',$,$,$,$,$,$);" ,
4650 "#2=IFCWALL('0000000000000000000002',$,'Wall2',$,$,$,$,$,$);" ,
51+ "#3=IFCSITE('0000000000000000000003',$,'Site',$,$,$,$,$,.ELEMENT.,(47,0,0),(8,0,0),0.,$,$);" ,
4752 'ENDSEC;' ,
4853 'END-ISO-10303-21;' ,
4954 '' ,
@@ -120,7 +125,8 @@ beforeEach(async () => {
120125 originalPostMessage = g . postMessage ;
121126 g . self = globalThis ;
122127 g . postMessage = ( msg : unknown ) => postedMessages . push ( msg ) ;
123- await import ( './parser.worker.js?t=' + ++ importCounter ) ;
128+ importCounter += 1 ;
129+ await import ( './parser.worker.js?t=' + importCounter ) ;
124130} , WORKER_IMPORT_HOOK_TIMEOUT_MS ) ;
125131
126132afterEach ( ( ) => {
@@ -176,3 +182,34 @@ describe('parser.worker.ts and the #3790 set-entity-index handoff', () => {
176182 expect ( diagnostics ( ) . some ( ( m ) => m . includes ( 'stopped early' ) ) ) . toBe ( false ) ;
177183 } , 30_000 ) ;
178184} ) ;
185+
186+ // #3983: execute the real worker handler; verify the receiver needs no entity
187+ // lookups for its first georeference read (including the early spatial store).
188+ it ( 'prepares render metadata before publishing partial and complete stores (#3983)' , async ( ) => {
189+ const records = [ ...IFC . matchAll ( / # ( \d + ) = [ ^ ; ] + ; / g) ] ;
190+ post ( { type : 'set-entity-index' ,
191+ ids : Uint32Array . from ( records , r => Number ( r [ 1 ] ) ) ,
192+ starts : Uint32Array . from ( records , r => r . index ! ) ,
193+ lengths : Uint32Array . from ( records , r => r [ 0 ] . length ) ,
194+ } ) ;
195+ startParse ( ) ;
196+ await settle ( ) ;
197+ assertParsed ( ) ;
198+ const messages = postedMessages . filter ( ( m ) : m is { type : string ; payload : DataStoreTransport } =>
199+ [ 'partial-store' , 'complete' ] . includes ( ( m as { type : string } ) . type ) ) ;
200+ expect ( messages ) . toHaveLength ( 2 ) ;
201+ for ( const { payload } of messages ) {
202+ const source = contiguousSourceBytes ( new Uint8Array ( sharedSource ( ) ) , payload . sourceContentKey ?? undefined ) ;
203+ // Full-fixture FNV-1a, independently calculated with Python integer arithmetic.
204+ expect ( payload . sourceContentKey ) . toBe ( '16b-6d79a917' ) ;
205+ // toTransferable does not compute a key: this proves it arrived pre-seeded.
206+ expect ( source . toTransferable ( ) . contentKey ) . toBe ( payload . sourceContentKey ) ;
207+ const store = fromTransport ( structuredClone ( payload ) , source ) ;
208+ const lookups = vi . spyOn ( store . entityIndex . byId , 'get' ) ;
209+ const georef = extractGeoreferencingOnDemand ( store ) ;
210+ expect ( georef ?. source ) . toBe ( 'siteLocation' ) ;
211+ expect ( georef ?. projectedCRS ?. name ) . toBe ( 'EPSG:4326' ) ;
212+ expect ( lookups ) . not . toHaveBeenCalled ( ) ;
213+ lookups . mockRestore ( ) ;
214+ }
215+ } , 30_000 ) ;
0 commit comments