22 largeValueMetadataMock ,
33 largeValueMetadataMockFns ,
44} from '@sim/testing/mocks/large-value-metadata.mock'
5- import { storageServiceMockFns } from '@sim/testing/mocks/storage-service.mock'
5+ import { storageServiceMock , storageServiceMockFns } from '@sim/testing/mocks/storage-service.mock'
66import { uploadsMock } from '@sim/testing/mocks/uploads.mock'
77import { beforeEach , describe , expect , it , vi } from 'vitest'
88import { clearLargeValueCacheForTests } from '@/lib/execution/payloads/cache'
@@ -27,6 +27,7 @@ import type { BlockLog, UserFile } from '@/executor/types'
2727const { mockDownloadFile, mockUploadFile } = storageServiceMockFns
2828
2929vi . mock ( '@/lib/uploads' , ( ) => uploadsMock )
30+ vi . mock ( '@/lib/uploads/core/storage-service' , ( ) => storageServiceMock )
3031
3132vi . mock ( '@/lib/execution/payloads/large-value-metadata' , ( ) => largeValueMetadataMock )
3233
@@ -304,19 +305,17 @@ describe('compactExecutionPayload', () => {
304305
305306/**
306307 * A child workflow's spans as the workflow block reports them: a loop whose one
307- * iteration holds two block spans. With a 1 KiB threshold each block span stays
308- * inline, but the iteration's `children` array together is over it — the shape
309- * generic compaction turned into a manifest.
308+ * iteration holds two block spans, each with a `resultBytes` payload.
310309 */
311- function childWorkflowSpans ( ) : TraceSpan [ ] {
310+ function childWorkflowSpans ( resultBytes : number ) : TraceSpan [ ] {
312311 const blockSpan = ( id : string ) : TraceSpan => ( {
313312 id,
314313 name : id ,
315314 type : 'function' ,
316315 duration : 1 ,
317316 startTime : '2026-09-29T00:00:00.000Z' ,
318317 endTime : '2026-09-29T00:00:00.001Z' ,
319- output : { result : 'x' . repeat ( 600 ) } ,
318+ output : { result : 'x' . repeat ( resultBytes ) } ,
320319 } )
321320 return [
322321 {
@@ -341,6 +340,16 @@ function childWorkflowSpans(): TraceSpan[] {
341340 ]
342341}
343342
343+ /** Spans whose payloads each exceed the 4 KiB test threshold, so each spills on its own. */
344+ const spansWithLargePayloads = ( ) => childWorkflowSpans ( 8192 )
345+
346+ /**
347+ * Spans whose payloads each stay under the 4 KiB test threshold but whose
348+ * iteration `children` together exceed it — the shape generic compaction
349+ * turned into a manifest nested inside the tree.
350+ */
351+ const spansTooLargeAsAWhole = ( ) => childWorkflowSpans ( 2500 )
352+
344353/** Asserts the loop → iteration → block span nesting survived with every `children` an array. */
345354function expectSpanTree ( spans : unknown ) : void {
346355 expect ( Array . isArray ( spans ) ) . toBe ( true )
@@ -352,7 +361,7 @@ function expectSpanTree(spans: unknown): void {
352361}
353362
354363describe ( 'compacting span trees' , ( ) => {
355- const options = { thresholdBytes : 1024 , requireDurable : true , ...TEST_EXECUTION_CONTEXT }
364+ const options = { thresholdBytes : 4096 , requireDurable : true , ...TEST_EXECUTION_CONTEXT }
356365
357366 beforeEach ( ( ) => {
358367 clearLargeValueCacheForTests ( )
@@ -370,65 +379,64 @@ describe('compacting span trees', () => {
370379 ...overrides ,
371380 } )
372381
373- it ( 'splits a block output child span tree off shaped as a tree ' , async ( ) => {
382+ it ( 'splits a block output child span tree off, spilling each oversized payload ' , async ( ) => {
374383 const compacted = await compactBlockOutput (
375- { result : 'done' , childTraceSpans : childWorkflowSpans ( ) } ,
384+ { result : 'done' , childTraceSpans : spansWithLargePayloads ( ) } ,
376385 options
377386 )
378387
379388 expect ( compacted . output ) . toEqual ( { result : 'done' } )
380389 expectSpanTree ( compacted . childTraceSpans )
390+ const [ loop ] = compacted . childTraceSpans as TraceSpan [ ]
391+ const spilled = loop . children ?. [ 0 ] . children ?. [ 0 ]
392+ expect ( isLargeValueRef ( spilled ?. output ?. result ) ) . toBe ( true )
381393 } )
382394
383- it ( 'still spills an oversized span payload' , async ( ) => {
384- const spans = childWorkflowSpans ( )
385- const iteration = spans [ 0 ] . children ?. [ 0 ]
386- if ( iteration ?. children ) iteration . children [ 0 ] . output = { result : 'y' . repeat ( 4096 ) }
395+ it ( 'drops a block output child span tree too large as a whole' , async ( ) => {
396+ const compacted = await compactBlockOutput (
397+ { result : 'done' , childTraceSpans : spansTooLargeAsAWhole ( ) } ,
398+ options
399+ )
387400
388- const compacted = await compactBlockOutput ( { childTraceSpans : spans } , options )
401+ expect ( compacted . output ) . toEqual ( { result : 'done' } )
402+ expect ( compacted . childTraceSpans ) . toBeUndefined ( )
403+ } )
389404
390- const spilled = ( compacted . childTraceSpans as TraceSpan [ ] ) [ 0 ] . children ?. [ 0 ] . children ?. [ 0 ]
391- expect ( isLargeValueRef ( spilled ?. output ?. result ) ) . toBe ( true )
405+ it ( 'rejects a child span tree too large as a whole when large values are rejected' , async ( ) => {
406+ await expect (
407+ compactBlockOutput (
408+ { childTraceSpans : spansTooLargeAsAWhole ( ) } ,
409+ { ...options , rejectLargeValues : true }
410+ )
411+ ) . rejects . toThrow ( )
392412 } )
393413
394414 it ( 'still spills a block output whose fields together exceed the threshold' , async ( ) => {
395415 const compacted = await compactBlockOutput (
396- { first : 'a' . repeat ( 600 ) , second : 'b' . repeat ( 600 ) , childTraceSpans : childWorkflowSpans ( ) } ,
416+ {
417+ first : 'a' . repeat ( 2500 ) ,
418+ second : 'b' . repeat ( 2500 ) ,
419+ childTraceSpans : spansWithLargePayloads ( ) ,
420+ } ,
397421 options
398422 )
399423
400424 expect ( isLargeValueRef ( compacted . output ) ) . toBe ( true )
401425 expectSpanTree ( compacted . childTraceSpans )
402426 } )
403427
404- it ( 'keeps block log child span trees shaped as trees' , async ( ) => {
405- const [ compacted ] =
406- ( await compactBlockLogs (
407- [ childWorkflowLog ( { childTraceSpans : childWorkflowSpans ( ) } ) ] ,
408- options
409- ) ) ?? [ ]
410-
411- expectSpanTree ( compacted ?. childTraceSpans )
412- } )
413-
414- it ( 'keeps a block log output carrying child spans a record' , async ( ) => {
415- const [ compacted ] =
428+ it ( 'keeps block log child span trees whole or drops them' , async ( ) => {
429+ const compacted =
416430 ( await compactBlockLogs (
417431 [
418- childWorkflowLog ( {
419- output : {
420- first : 'a' . repeat ( 600 ) ,
421- second : 'b' . repeat ( 600 ) ,
422- childTraceSpans : childWorkflowSpans ( ) ,
423- } ,
424- } ) ,
432+ childWorkflowLog ( { childTraceSpans : spansWithLargePayloads ( ) } ) ,
433+ childWorkflowLog ( { childTraceSpans : spansTooLargeAsAWhole ( ) } ) ,
425434 ] ,
426435 options
427436 ) ) ?? [ ]
428437
429- expect ( isLargeValueRef ( compacted ?. output ) ) . toBe ( false )
430- expect ( compacted ?. output ?. first ) . toBe ( 'a' . repeat ( 600 ) )
431- expectSpanTree ( compacted ?. output ?. childTraceSpans )
438+ expectSpanTree ( compacted [ 0 ] ?. childTraceSpans )
439+ expect ( compacted [ 1 ] ?. childTraceSpans ) . toBeUndefined ( )
432440 } )
433441
434442 it ( 'keeps a nested child workflow span tree shaped as a tree' , async ( ) => {
@@ -439,17 +447,13 @@ describe('compacting span trees', () => {
439447 duration : 2 ,
440448 startTime : '2026-09-29T00:00:00.000Z' ,
441449 endTime : '2026-09-29T00:00:00.002Z' ,
442- output : {
443- first : 'a' . repeat ( 600 ) ,
444- second : 'b' . repeat ( 600 ) ,
445- childTraceSpans : childWorkflowSpans ( ) ,
446- } ,
450+ output : { result : 'done' , childTraceSpans : spansWithLargePayloads ( ) } ,
447451 }
448452
449453 const compacted = await compactBlockOutput ( { childTraceSpans : [ nestedWorkflowSpan ] } , options )
450454
451455 const [ nested ] = compacted . childTraceSpans as TraceSpan [ ]
452- expect ( nested . output ?. first ) . toBe ( 'a' . repeat ( 600 ) )
456+ expect ( nested . output ?. result ) . toBe ( 'done' )
453457 expectSpanTree ( nested . output ?. childTraceSpans )
454458 } )
455459
@@ -464,8 +468,6 @@ describe('compacting span trees', () => {
464468 }
465469 span . children = [ span ]
466470
467- const compacted = await compactBlockOutput ( { childTraceSpans : [ span ] } , options )
468-
469- expect ( ( compacted . childTraceSpans as TraceSpan [ ] ) [ 0 ] . id ) . toBe ( 'cyclic' )
471+ await expect ( compactBlockOutput ( { childTraceSpans : [ span ] } , options ) ) . resolves . toBeDefined ( )
470472 } )
471473} )
0 commit comments