@@ -33,16 +33,17 @@ import { idToBytes } from '@sandchest/contract'
3333import type { CreateSandboxResponse , GetSandboxResponse , ReplayBundle } from '@sandchest/contract'
3434import type { BufferedEvent } from '../services/redis.js'
3535import { RUN_API_INTEGRATION_TESTS } from '../test-support.js'
36+ import type { NodeClientApi } from '../services/node-client.js'
3637
3738const TEST_ORG = 'org_test_123'
3839const TEST_USER = 'user_test_456'
3940
40- function createTestEnv ( ) {
41+ function createTestEnv ( overrides ?: { nodeClient ?: NodeClientApi } ) {
4142 const sandboxRepo = createInMemorySandboxRepo ( )
4243 const execRepo = createInMemoryExecRepo ( )
4344 const sessionRepo = createInMemorySessionRepo ( )
4445 const objectStorage = createInMemoryObjectStorage ( )
45- const nodeClient = createInMemoryNodeClient ( )
46+ const nodeClient = overrides ?. nodeClient ?? createInMemoryNodeClient ( )
4647 const redis = createInMemoryRedisApi ( )
4748 const artifactRepo = createInMemoryArtifactRepo ( )
4849 const quotaApi = createInMemoryQuotaApi ( )
@@ -777,6 +778,48 @@ describe.skipIf(!RUN_API_INTEGRATION_TESTS)('POST /v1/sandboxes/:id/fork — quo
777778 } )
778779} )
779780
781+ describe . skipIf ( ! RUN_API_INTEGRATION_TESTS ) ( 'POST /v1/sandboxes/:id/fork — node failure handling' , ( ) => {
782+ test ( 'returns internal_error with node message and marks fork failed' , async ( ) => {
783+ const baseNodeClient = createInMemoryNodeClient ( )
784+ const failingNodeClient : NodeClientApi = {
785+ ...baseNodeClient ,
786+ forkSandbox : ( ) => Effect . die ( new Error ( 'simulated fork failure' ) ) ,
787+ }
788+ const env = createTestEnv ( { nodeClient : failingNodeClient } )
789+ const parentId = await createRunningSandbox ( env )
790+
791+ const result = await env . runTest (
792+ Effect . gen ( function * ( ) {
793+ const client = yield * HttpClient . HttpClient
794+ const response = yield * client . execute (
795+ HttpClientRequest . post ( `/v1/sandboxes/${ parentId } /fork` ) . pipe (
796+ HttpClientRequest . bodyUnsafeJson ( { } ) ,
797+ ) ,
798+ )
799+ const body = yield * response . json
800+ return { status : response . status , body : body as { error : string ; message : string } }
801+ } ) ,
802+ )
803+
804+ expect ( result . status ) . toBe ( 500 )
805+ expect ( result . body . error ) . toBe ( 'internal_error' )
806+ expect ( result . body . message ) . toContain ( 'simulated fork failure' )
807+
808+ const tree = await env . runTest (
809+ Effect . gen ( function * ( ) {
810+ const client = yield * HttpClient . HttpClient
811+ const response = yield * client . execute (
812+ HttpClientRequest . get ( `/v1/sandboxes/${ parentId } /forks` ) ,
813+ )
814+ return ( yield * response . json ) as { tree : Array < { status : string ; failure_reason ?: string | null } > }
815+ } ) ,
816+ )
817+
818+ const failedFork = tree . tree . find ( ( node ) => node . status === 'failed' )
819+ expect ( failedFork ) . toBeDefined ( )
820+ } )
821+ } )
822+
780823// ---------------------------------------------------------------------------
781824// GET /v1/sandboxes/:id/stream — sandbox-level SSE
782825// ---------------------------------------------------------------------------
0 commit comments