@@ -14,6 +14,7 @@ import * as Effect from "effect/Effect";
1414import * as Layer from "effect/Layer" ;
1515import { vi } from "vite-plus/test" ;
1616
17+ import { AcpRegistryError } from "./AcpRegistrySupport.ts" ;
1718import { makeCopilotTextGeneration } from "./copilotTextGeneration.ts" ;
1819
1920const mockAgentPath = NodePath . join (
@@ -40,7 +41,11 @@ interface MockLog {
4041 readonly clientResponse ?: { readonly id : string ; readonly result ?: unknown } ;
4142}
4243
43- const harness = ( input : { readonly scenario : string ; readonly userConfig ?: string } ) =>
44+ const harness = ( input : {
45+ readonly scenario : string ;
46+ readonly userConfig ?: string ;
47+ readonly resolveFailure ?: AcpRegistryError ;
48+ } ) =>
4449 Effect . gen ( function * ( ) {
4550 const root = NodeFS . mkdtempSync ( NodePath . join ( NodeOS . tmpdir ( ) , "t3-copilot-text-test-" ) ) ;
4651 yield * Effect . addFinalizer ( ( ) =>
@@ -63,19 +68,21 @@ const harness = (input: { readonly scenario: string; readonly userConfig?: strin
6368 const textGeneration = yield * makeCopilotTextGeneration ( {
6469 environment : { COPILOT_HOME : userHome } ,
6570 resolveSpawn : ( cwd ) =>
66- Effect . succeed ( {
67- command,
68- // A local install's own arguments must not reach the helper.
69- args : [ "--acp" , "--additional-mcp-config" , "@mcp.json" , "--allow-all" ] ,
70- cwd,
71- env : {
72- ...process . env ,
73- T3_MOCK_SCENARIO : input . scenario ,
74- T3_MOCK_LOG : logPath ,
75- COPILOT_ALLOW_ALL : "true" ,
76- COPILOT_CUSTOM_INSTRUCTIONS_DIRS : userHome ,
77- } ,
78- } ) ,
71+ input . resolveFailure
72+ ? Effect . fail ( input . resolveFailure )
73+ : Effect . succeed ( {
74+ command,
75+ // A local install's own arguments must not reach the helper.
76+ args : [ "--acp" , "--additional-mcp-config" , "@mcp.json" , "--allow-all" ] ,
77+ cwd,
78+ env : {
79+ ...process . env ,
80+ T3_MOCK_SCENARIO : input . scenario ,
81+ T3_MOCK_LOG : logPath ,
82+ COPILOT_ALLOW_ALL : "true" ,
83+ COPILOT_CUSTOM_INSTRUCTIONS_DIRS : userHome ,
84+ } ,
85+ } ) ,
7986 } ) ;
8087 const readLog = ( ) : ReadonlyArray < MockLog > =>
8188 NodeFS . existsSync ( logPath )
@@ -203,33 +210,42 @@ describe("Copilot text generation", () => {
203210 } ) . pipe ( Effect . scoped , Effect . provide ( layer ) ) ,
204211 ) ;
205212
206- it . live (
207- "refuses before launching when sign-in state is unreadable or a review guide is asked" ,
208- ( ) =>
209- Effect . gen ( function * ( ) {
210- const unreadable = yield * harness ( { scenario : "reply" , userConfig : "{ not json" } ) ;
211- const signInError = yield * Effect . flip ( generateTitle ( unreadable . textGeneration , "default" ) ) ;
212- assert . equal (
213- signInError . detail ,
214- "Could not read the GitHub Copilot sign-in state for text generation." ,
215- ) ;
216- assert . deepEqual ( unreadable . readLog ( ) , [ ] ) ;
217-
218- const review = yield * harness ( { scenario : "reply" } ) ;
219- const reviewError = yield * Effect . flip (
220- review . textGeneration . generateReviewGuide ( {
221- cwd : process . cwd ( ) ,
222- number : 1 ,
223- title : "Title" ,
224- description : "" ,
225- baseBranch : "main" ,
226- baseCommit : "abc" ,
227- settledReview : "" ,
228- modelSelection : createModelSelection ( instanceId , "gpt-6-luna" ) ,
229- } ) ,
230- ) ;
231- assert . match ( reviewError . detail , / ^ C o p i l o t c a n n o t i n v e s t i g a t e a c h e c k o u t r e a d - o n l y \. / ) ;
232- assert . deepEqual ( review . readLog ( ) , [ ] ) ;
233- } ) . pipe ( Effect . scoped , Effect . provide ( layer ) ) ,
213+ it . live ( "refuses before launching on sign-in, resolver or unsupported operation failures" , ( ) =>
214+ Effect . gen ( function * ( ) {
215+ const unreadable = yield * harness ( { scenario : "reply" , userConfig : "{ not json" } ) ;
216+ const signInError = yield * Effect . flip ( generateTitle ( unreadable . textGeneration , "default" ) ) ;
217+ assert . equal (
218+ signInError . detail ,
219+ "Could not read the GitHub Copilot sign-in state for text generation." ,
220+ ) ;
221+ assert . deepEqual ( unreadable . readLog ( ) , [ ] ) ;
222+
223+ const resolveFailure = new AcpRegistryError ( {
224+ reason : "runner_unavailable" ,
225+ detail : "arbitrary-output-secret-marker" ,
226+ } ) ;
227+ const unavailable = yield * harness ( { scenario : "reply" , resolveFailure } ) ;
228+ const launchError = yield * Effect . flip ( generateTitle ( unavailable . textGeneration , "default" ) ) ;
229+ assert . equal ( launchError . detail , "Could not launch GitHub Copilot (runner_unavailable)." ) ;
230+ assert . notInclude ( launchError . detail , resolveFailure . detail ) ;
231+ assert . strictEqual ( launchError . cause , resolveFailure ) ;
232+ assert . deepEqual ( unavailable . readLog ( ) , [ ] ) ;
233+
234+ const review = yield * harness ( { scenario : "reply" } ) ;
235+ const reviewError = yield * Effect . flip (
236+ review . textGeneration . generateReviewGuide ( {
237+ cwd : process . cwd ( ) ,
238+ number : 1 ,
239+ title : "Title" ,
240+ description : "" ,
241+ baseBranch : "main" ,
242+ baseCommit : "abc" ,
243+ settledReview : "" ,
244+ modelSelection : createModelSelection ( instanceId , "gpt-6-luna" ) ,
245+ } ) ,
246+ ) ;
247+ assert . match ( reviewError . detail , / ^ C o p i l o t c a n n o t i n v e s t i g a t e a c h e c k o u t r e a d - o n l y \. / ) ;
248+ assert . deepEqual ( review . readLog ( ) , [ ] ) ;
249+ } ) . pipe ( Effect . scoped , Effect . provide ( layer ) ) ,
234250 ) ;
235251} ) ;
0 commit comments