22 * @vitest -environment node
33 */
44import { describe , expect , it , vi } from 'vitest'
5+ import { mergeFileKeys , mergeLargeValueKeys } from '@/lib/execution/payloads/access-keys'
56import { BlockType } from '@/executor/constants'
67import { DAGBuilder } from '@/executor/dag/builder'
78import { DAGExecutor } from '@/executor/execution/executor'
@@ -10,6 +11,15 @@ import type { ExecutionContext, ExecutionResult } from '@/executor/types'
1011import { buildSentinelStartId } from '@/executor/utils/subflow-utils'
1112import type { SerializedBlock , SerializedWorkflow } from '@/serializer/types'
1213
14+ /** Reaches the executor's private context factory, which every run's root context comes from. */
15+ function createExecutionContext ( executor : DAGExecutor , workflowId = 'wf-1' ) : ExecutionContext {
16+ return (
17+ executor as unknown as {
18+ createExecutionContext : ( workflowId : string ) => { context : ExecutionContext }
19+ }
20+ ) . createExecutionContext ( workflowId ) . context
21+ }
22+
1323function createExecutor ( ) : DAGExecutor {
1424 return new DAGExecutor ( {
1525 workflow : {
@@ -404,12 +414,7 @@ describe('DAGExecutor createExecutionContext useDraftState', () => {
404414 : ( { useDraftState : opts . metadataUseDraftState } as ExecutionContext [ 'metadata' ] ) ,
405415 } ,
406416 } )
407- const { context } = (
408- executor as unknown as {
409- createExecutionContext : ( workflowId : string ) => { context : ExecutionContext }
410- }
411- ) . createExecutionContext ( 'wf-1' )
412- return context . metadata . useDraftState
417+ return createExecutionContext ( executor ) . metadata . useDraftState
413418 }
414419
415420 it ( 'honors explicit useDraftState=true even when isDeployedContext is true (table dispatcher)' , ( ) => {
@@ -442,33 +447,21 @@ describe('DAGExecutor executor delegation origin', () => {
442447 contextExtensions : { executorDelegationOrigin } ,
443448 } )
444449
445- const { context } = (
446- executor as unknown as {
447- createExecutionContext : ( workflowId : string ) => { context : ExecutionContext }
448- }
449- ) . createExecutionContext ( 'child-workflow' )
450+ const context = createExecutionContext ( executor , 'child-workflow' )
450451
451452 expect ( context . workflowId ) . toBe ( 'child-workflow' )
452453 expect ( context . executorDelegationOrigin ) . toBe ( executorDelegationOrigin )
453454 } )
454455} )
455456
456457describe ( 'DAGExecutor run-scoped permission config cache' , ( ) => {
457- function createContext ( executor : DAGExecutor ) : ExecutionContext {
458- return (
459- executor as unknown as {
460- createExecutionContext : ( workflowId : string ) => { context : ExecutionContext }
461- }
462- ) . createExecutionContext ( 'wf-1' ) . context
463- }
464-
465458 it ( 'seeds one cache per run that survives per-block context copies' , ( ) => {
466459 const executor = new DAGExecutor ( {
467460 workflow : { version : '1' , blocks : [ ] , connections : [ ] } ,
468461 contextExtensions : { workspaceId : 'ws-1' } ,
469462 } )
470463
471- const context = createContext ( executor )
464+ const context = createExecutionContext ( executor )
472465 const blockContext = { ...context }
473466
474467 expect ( context . permissionConfigCache ) . toBeInstanceOf ( Map )
@@ -477,9 +470,40 @@ describe('DAGExecutor run-scoped permission config cache', () => {
477470
478471 it ( 'never shares the cache between runs' , ( ) => {
479472 const workflow = { version : '1' , blocks : [ ] , connections : [ ] }
480- const parent = createContext ( new DAGExecutor ( { workflow, contextExtensions : { } } ) )
481- const child = createContext ( new DAGExecutor ( { workflow, contextExtensions : { } } ) )
473+ const parent = createExecutionContext ( new DAGExecutor ( { workflow, contextExtensions : { } } ) )
474+ const child = createExecutionContext ( new DAGExecutor ( { workflow, contextExtensions : { } } ) )
482475
483476 expect ( child . permissionConfigCache ) . not . toBe ( parent . permissionConfigCache )
484477 } )
485478} )
479+
480+ describe ( 'DAGExecutor exact access key lists' , ( ) => {
481+ function createContext ( contextExtensions : Record < string , unknown > ) : ExecutionContext {
482+ return createExecutionContext (
483+ new DAGExecutor ( {
484+ workflow : { version : '1' , blocks : [ ] , connections : [ ] } ,
485+ contextExtensions,
486+ } )
487+ )
488+ }
489+
490+ it ( 'keeps keys a block records on its context copy for later blocks' , ( ) => {
491+ const context = createContext ( { } )
492+
493+ mergeLargeValueKeys ( { ...context } , [ 'large-value-key' ] )
494+ mergeFileKeys ( { ...context } , [ 'file-key' ] )
495+
496+ expect ( context . largeValueKeys ) . toEqual ( [ 'large-value-key' ] )
497+ expect ( context . fileKeys ) . toEqual ( [ 'file-key' ] )
498+ } )
499+
500+ it ( 'shares the lists a run passes in rather than copying them' , ( ) => {
501+ const largeValueKeys = [ 'inherited-large-value-key' ]
502+ const fileKeys = [ 'inherited-file-key' ]
503+
504+ const context = createContext ( { largeValueKeys, fileKeys } )
505+
506+ expect ( context . largeValueKeys ) . toBe ( largeValueKeys )
507+ expect ( context . fileKeys ) . toBe ( fileKeys )
508+ } )
509+ } )
0 commit comments