1- import { describe , test , expect , mock , beforeAll } from "bun:test"
1+ import { describe , test , expect , mock , beforeAll , beforeEach } from "bun:test"
22
33import { state } from "../src/lib/state"
44import { server } from "../src/server"
@@ -75,10 +75,8 @@ describe("POST /v1/responses — wired handler", () => {
7575 } )
7676
7777 test ( "missing copilot token returns 500" , async ( ) => {
78- // Temporarily clear the token via a describe-level wrapper so the
79- // assignment happens synchronously (no await between read and write).
80- const tokenBackup = state . copilotToken
81- state . copilotToken = undefined // synchronous — no race condition
78+ // Temporarily clear the token — write is synchronous, no await in between.
79+ state . copilotToken = undefined
8280
8381 const res = await server . request ( "/v1/responses" , {
8482 method : "POST" ,
@@ -87,7 +85,145 @@ describe("POST /v1/responses — wired handler", () => {
8785 } )
8886 expect ( res . status ) . toBe ( 500 )
8987
90- // eslint-disable-next-line require-atomic-updates
91- state . copilotToken = tokenBackup
88+ state . copilotToken = "test-token"
89+ } )
90+ } )
91+
92+ // ---------------------------------------------------------------------------
93+ // createResponses behavior: X-Initiator header and error propagation
94+ // ---------------------------------------------------------------------------
95+
96+ describe ( "createResponses behavior" , ( ) => {
97+ // Restore state before each test in this block
98+ beforeEach ( ( ) => {
99+ state . copilotToken = "test-token"
100+ state . vsCodeVersion = "1.99.0"
101+ state . accountType = "individual"
102+ state . manualApprove = false
103+ } )
104+
105+ test ( "X-Initiator = agent when assistant message present" , async ( ) => {
106+ const captureMock = mock (
107+ ( _url : string , opts : { headers : Record < string , string > } ) =>
108+ Promise . resolve ( {
109+ ok : true ,
110+ json : ( ) => Promise . resolve ( mockResponseBody ) ,
111+ headers : opts . headers ,
112+ } ) ,
113+ )
114+ // @ts -expect-error – mock doesn't implement full fetch signature
115+ globalThis . fetch = captureMock
116+
117+ await server . request ( "/v1/responses" , {
118+ method : "POST" ,
119+ headers : { "Content-Type" : "application/json" } ,
120+ body : JSON . stringify ( {
121+ model : "gpt-4o" ,
122+ stream : false ,
123+ input : [
124+ { type : "message" , role : "user" , content : "hello" } ,
125+ { type : "message" , role : "assistant" , content : "hi there" } ,
126+ ] ,
127+ } ) ,
128+ } )
129+
130+ expect ( captureMock ) . toHaveBeenCalled ( )
131+ const sentHeaders = (
132+ captureMock . mock . calls [ 0 ] [ 1 ] as { headers : Record < string , string > }
133+ ) . headers
134+ expect ( sentHeaders [ "X-Initiator" ] ) . toBe ( "agent" )
135+
136+ // Restore default mock
137+ // @ts -expect-error – mock doesn't implement full fetch signature
138+ globalThis . fetch = fetchMock
139+ } )
140+
141+ test ( "X-Initiator = user for pure user messages" , async ( ) => {
142+ const captureMock = mock (
143+ ( _url : string , opts : { headers : Record < string , string > } ) =>
144+ Promise . resolve ( {
145+ ok : true ,
146+ json : ( ) => Promise . resolve ( mockResponseBody ) ,
147+ headers : opts . headers ,
148+ } ) ,
149+ )
150+ // @ts -expect-error – mock doesn't implement full fetch signature
151+ globalThis . fetch = captureMock
152+
153+ await server . request ( "/v1/responses" , {
154+ method : "POST" ,
155+ headers : { "Content-Type" : "application/json" } ,
156+ body : JSON . stringify ( {
157+ model : "gpt-4o" ,
158+ stream : false ,
159+ input : [ { type : "message" , role : "user" , content : "just a user" } ] ,
160+ } ) ,
161+ } )
162+
163+ expect ( captureMock ) . toHaveBeenCalled ( )
164+ const sentHeaders = (
165+ captureMock . mock . calls [ 0 ] [ 1 ] as { headers : Record < string , string > }
166+ ) . headers
167+ expect ( sentHeaders [ "X-Initiator" ] ) . toBe ( "user" )
168+
169+ // @ts -expect-error – mock doesn't implement full fetch signature
170+ globalThis . fetch = fetchMock
171+ } )
172+
173+ test ( "X-Initiator = agent for function_call_output item" , async ( ) => {
174+ const captureMock = mock (
175+ ( _url : string , opts : { headers : Record < string , string > } ) =>
176+ Promise . resolve ( {
177+ ok : true ,
178+ json : ( ) => Promise . resolve ( mockResponseBody ) ,
179+ headers : opts . headers ,
180+ } ) ,
181+ )
182+ // @ts -expect-error – mock doesn't implement full fetch signature
183+ globalThis . fetch = captureMock
184+
185+ await server . request ( "/v1/responses" , {
186+ method : "POST" ,
187+ headers : { "Content-Type" : "application/json" } ,
188+ body : JSON . stringify ( {
189+ model : "gpt-4o" ,
190+ stream : false ,
191+ input : [
192+ { type : "function_call_output" , call_id : "call_1" , output : "{}" } ,
193+ ] ,
194+ } ) ,
195+ } )
196+
197+ expect ( captureMock ) . toHaveBeenCalled ( )
198+ const sentHeaders = (
199+ captureMock . mock . calls [ 0 ] [ 1 ] as { headers : Record < string , string > }
200+ ) . headers
201+ expect ( sentHeaders [ "X-Initiator" ] ) . toBe ( "agent" )
202+
203+ // @ts -expect-error – mock doesn't implement full fetch signature
204+ globalThis . fetch = fetchMock
205+ } )
206+
207+ test ( "upstream 4xx returns error response" , async ( ) => {
208+ const errorMock = mock ( ( ) =>
209+ Promise . resolve ( {
210+ ok : false ,
211+ status : 429 ,
212+ text : ( ) => Promise . resolve ( "rate limited" ) ,
213+ } ) ,
214+ )
215+ // @ts -expect-error – mock doesn't implement full fetch signature
216+ globalThis . fetch = errorMock
217+
218+ const res = await server . request ( "/v1/responses" , {
219+ method : "POST" ,
220+ headers : { "Content-Type" : "application/json" } ,
221+ body : JSON . stringify ( { model : "gpt-4o" , stream : false , input : [ ] } ) ,
222+ } )
223+
224+ expect ( res . status ) . toBe ( 429 )
225+
226+ // @ts -expect-error – mock doesn't implement full fetch signature
227+ globalThis . fetch = fetchMock
92228 } )
93229} )
0 commit comments