@@ -14,6 +14,11 @@ import {
1414import { NextRequest } from 'next/server'
1515import { afterAll , beforeEach , describe , expect , it , vi } from 'vitest'
1616import { functionExecuteBodySchema } from '@/lib/api/contracts'
17+ import {
18+ BILLING_ATTRIBUTION_HEADER ,
19+ type BillingAttributionSnapshot ,
20+ serializeBillingAttributionHeader ,
21+ } from '@/lib/billing/core/billing-attribution'
1722import { OrchestrationError } from '@/lib/core/orchestration/types'
1823import { INTERNAL_EXECUTION_DEADLINE_HEADER } from '@/lib/execution/execution-deadline-header'
1924import {
@@ -28,6 +33,21 @@ import {
2833 SandboxOutputLimitError ,
2934} from '@/lib/execution/remote-sandbox/output-limits'
3035
36+ function billingAttributionHeaders ( workspaceId = 'workspace-1' ) : Record < string , string > {
37+ const attribution : BillingAttributionSnapshot = {
38+ actorUserId : 'user-123' ,
39+ workspaceId,
40+ organizationId : null ,
41+ billedAccountUserId : 'user-123' ,
42+ billingEntity : { type : 'user' , id : 'user-123' } ,
43+ billingPeriod : {
44+ start : '2026-08-01T00:00:00.000Z' ,
45+ end : '2026-09-01T00:00:00.000Z' ,
46+ } ,
47+ payerSubscription : null ,
48+ }
49+ return { [ BILLING_ATTRIBUTION_HEADER ] : serializeBillingAttributionHeader ( attribution ) }
50+ }
3151const {
3252 mockExecuteInSandbox,
3353 mockExecuteInIsolatedVM,
@@ -304,6 +324,29 @@ describe('Function execution request', () => {
304324 expect ( mockWriteWorkspaceFileByPath ) . toHaveBeenCalledTimes ( 1 )
305325 } )
306326
327+ it ( 'fails closed before deriving an export workspace from incomplete workflow context' , async ( ) => {
328+ envFlagsMock . isRemoteSandboxEnabled = true
329+
330+ const req = createMockRequest ( 'POST' , {
331+ code : 'print("done")' ,
332+ language : 'python' ,
333+ workflowId : 'workflow-victim' ,
334+ outputs : {
335+ files : [ { path : 'files/README.md' , mode : 'overwrite' , sandboxPath : '/tmp/out.txt' } ] ,
336+ } ,
337+ } )
338+
339+ const response = await POST ( req )
340+
341+ expect ( response . status ) . toBe ( 503 )
342+ await expect ( response . json ( ) ) . resolves . toMatchObject ( {
343+ retryable : false ,
344+ code : 'sandbox_usage_attribution_invalid' ,
345+ } )
346+ expect ( mockExecuteInSandbox ) . not . toHaveBeenCalled ( )
347+ expect ( mockWriteWorkspaceFileByPath ) . not . toHaveBeenCalled ( )
348+ } )
349+
307350 it ( 'runs import-free JavaScript in isolated-vm without a remote provider' , async ( ) => {
308351 const req = createMockRequest ( 'POST' , {
309352 code : 'return "test"' ,
@@ -487,6 +530,85 @@ describe('Function execution request', () => {
487530 expect ( mockExecuteInIsolatedVM ) . not . toHaveBeenCalled ( )
488531 } )
489532
533+ it . each ( [
534+ {
535+ label : 'execution ID' ,
536+ body : { workflowId : 'workflow-1' , workspaceId : 'workspace-1' } ,
537+ } ,
538+ {
539+ label : 'trusted billing attribution' ,
540+ body : {
541+ workflowId : 'workflow-1' ,
542+ workspaceId : 'workspace-1' ,
543+ executionId : 'execution-1' ,
544+ } ,
545+ } ,
546+ ] ) (
547+ 'fails before remote sandbox creation when workflow usage lacks $label' ,
548+ async ( { body } ) => {
549+ envFlagsMock . isRemoteSandboxEnabled = true
550+
551+ const response = await POST (
552+ createMockRequest ( 'POST' , { code : 'print("ready")' , language : 'python' , ...body } )
553+ )
554+
555+ expect ( response . status ) . toBe ( 503 )
556+ await expect ( response . json ( ) ) . resolves . toMatchObject ( {
557+ success : false ,
558+ retryable : false ,
559+ code : 'sandbox_usage_attribution_invalid' ,
560+ } )
561+ expect ( mockExecuteInSandbox ) . not . toHaveBeenCalled ( )
562+ }
563+ )
564+
565+ it ( 'passes complete trusted workflow attribution to remote sandbox billing' , async ( ) => {
566+ envFlagsMock . isRemoteSandboxEnabled = true
567+
568+ const response = await POST (
569+ createMockRequest (
570+ 'POST' ,
571+ {
572+ code : 'print("ready")' ,
573+ language : 'python' ,
574+ workflowId : 'workflow-1' ,
575+ workspaceId : 'workspace-1' ,
576+ executionId : 'execution-1' ,
577+ } ,
578+ billingAttributionHeaders ( )
579+ )
580+ )
581+
582+ expect ( response . status ) . toBe ( 200 )
583+ expect ( mockExecuteInSandbox ) . toHaveBeenCalledWith (
584+ expect . objectContaining ( {
585+ usageContext : expect . objectContaining ( {
586+ workspaceId : 'workspace-1' ,
587+ workflowId : 'workflow-1' ,
588+ executionId : 'execution-1' ,
589+ billingAttribution : expect . objectContaining ( { actorUserId : 'user-123' } ) ,
590+ } ) ,
591+ } )
592+ )
593+ } )
594+
595+ it ( 'allows a non-workflow remote call without sandbox usage attribution' , async ( ) => {
596+ envFlagsMock . isRemoteSandboxEnabled = true
597+
598+ const response = await POST (
599+ createMockRequest ( 'POST' , {
600+ code : 'print("ready")' ,
601+ language : 'python' ,
602+ workspaceId : 'workspace-1' ,
603+ } )
604+ )
605+
606+ expect ( response . status ) . toBe ( 200 )
607+ expect ( mockExecuteInSandbox ) . toHaveBeenCalledWith (
608+ expect . objectContaining ( { usageContext : undefined } )
609+ )
610+ } )
611+
490612 it ( 'forces import-free JavaScript into the remote runtime when a Sim sandbox is selected' , async ( ) => {
491613 envFlagsMock . isRemoteSandboxEnabled = true
492614
0 commit comments