@@ -2,6 +2,7 @@ import { buildAssetPackSandboxHarness } from '../asset-pack-harness';
22import { VercelSandboxPipelineHost } from '../vercel-sandbox-host' ;
33import type {
44 PipelineHarnessFile ,
5+ PipelineHarnessHostEvent ,
56 SandboxCommandResult ,
67 SandboxCreateOptions ,
78 SandboxFactory ,
@@ -80,4 +81,110 @@ describe('VercelSandboxPipelineHost', () => {
8081 expect ( result . stopped ) . toBe ( true ) ;
8182 expect ( fakeSandbox . stopped ) . toBe ( true ) ;
8283 } ) ;
84+
85+ it ( 'emits host lifecycle events for streaming harness observers' , async ( ) => {
86+ const fakeSandbox = new FakeSandbox ( ) ;
87+ const events : PipelineHarnessHostEvent [ ] = [ ] ;
88+ const factory : SandboxFactory = {
89+ create : async ( ) => fakeSandbox ,
90+ } ;
91+ const host = new VercelSandboxPipelineHost ( {
92+ sandboxFactory : factory ,
93+ onEvent : ( event ) => {
94+ events . push ( event ) ;
95+ } ,
96+ } ) ;
97+ const plan = buildAssetPackSandboxHarness ( {
98+ read : {
99+ id : 'read-1' ,
100+ prompt : 'Read the deposited repository revision.' ,
101+ } ,
102+ deposit : {
103+ id : 'deposit-1' ,
104+ } ,
105+ sourceRevision : {
106+ repositoryFullName : 'engineeredsoftware/ENGI' ,
107+ branch : 'main' ,
108+ commit : '31bbc0c5227b6b3aed5d107fd8507d35ec22970a' ,
109+ } ,
110+ } ) ;
111+
112+ await host . runHarness ( plan ) ;
113+
114+ expect ( events . map ( ( event ) => event . type ) ) . toEqual ( [
115+ 'sandbox-create-started' ,
116+ 'sandbox-created' ,
117+ 'harness-files-written' ,
118+ 'command-started' ,
119+ 'command-completed' ,
120+ 'command-started' ,
121+ 'command-completed' ,
122+ 'artifacts-read' ,
123+ 'sandbox-stopped' ,
124+ ] ) ;
125+ expect ( events . find ( ( event ) => event . type === 'command-completed' ) ) . toMatchObject ( {
126+ stdoutLength : expect . any ( Number ) ,
127+ stderrLength : expect . any ( Number ) ,
128+ } ) ;
129+ } ) ;
130+
131+ it ( 'passes access-token auth fields to Sandbox.create when OIDC is unavailable' , async ( ) => {
132+ const previous = {
133+ VERCEL_TOKEN : process . env . VERCEL_TOKEN ,
134+ VERCEL_TEAM_ID : process . env . VERCEL_TEAM_ID ,
135+ VERCEL_PROJECT_ID : process . env . VERCEL_PROJECT_ID ,
136+ VERCEL_OIDC_TOKEN : process . env . VERCEL_OIDC_TOKEN ,
137+ } ;
138+ process . env . VERCEL_TOKEN = 'test-token' ;
139+ process . env . VERCEL_TEAM_ID = 'team_test' ;
140+ process . env . VERCEL_PROJECT_ID = 'prj_test' ;
141+ delete process . env . VERCEL_OIDC_TOKEN ;
142+
143+ try {
144+ const fakeSandbox = new FakeSandbox ( ) ;
145+ const createOptions : SandboxCreateOptions [ ] = [ ] ;
146+ const factory : SandboxFactory = {
147+ create : async ( options ) => {
148+ createOptions . push ( options ) ;
149+ return fakeSandbox ;
150+ } ,
151+ } ;
152+ const host = new VercelSandboxPipelineHost ( { sandboxFactory : factory } ) ;
153+ const plan = buildAssetPackSandboxHarness ( {
154+ read : {
155+ id : 'read-1' ,
156+ prompt : 'Read the deposited repository revision.' ,
157+ } ,
158+ deposit : {
159+ id : 'deposit-1' ,
160+ } ,
161+ sourceRevision : {
162+ repositoryFullName : 'engineeredsoftware/ENGI' ,
163+ branch : 'main' ,
164+ commit : '31bbc0c5227b6b3aed5d107fd8507d35ec22970a' ,
165+ } ,
166+ } ) ;
167+
168+ await host . runHarness ( plan ) ;
169+
170+ expect ( createOptions [ 0 ] ) . toMatchObject ( {
171+ token : 'test-token' ,
172+ teamId : 'team_test' ,
173+ projectId : 'prj_test' ,
174+ } ) ;
175+ } finally {
176+ restoreEnv ( 'VERCEL_TOKEN' , previous . VERCEL_TOKEN ) ;
177+ restoreEnv ( 'VERCEL_TEAM_ID' , previous . VERCEL_TEAM_ID ) ;
178+ restoreEnv ( 'VERCEL_PROJECT_ID' , previous . VERCEL_PROJECT_ID ) ;
179+ restoreEnv ( 'VERCEL_OIDC_TOKEN' , previous . VERCEL_OIDC_TOKEN ) ;
180+ }
181+ } ) ;
83182} ) ;
183+
184+ function restoreEnv ( key : string , value : string | undefined ) : void {
185+ if ( value === undefined ) {
186+ delete process . env [ key ] ;
187+ } else {
188+ process . env [ key ] = value ;
189+ }
190+ }
0 commit comments