11import { afterEach , describe , expect , test } from "bun:test"
2- import { Context } from "effect"
2+ import type { UpgradeWebSocket } from "hono/ws"
3+ import { Context , Effect , FileSystem , Layer , Path } from "effect"
4+ import { NodeFileSystem , NodePath } from "@effect/platform-node"
5+ import { Flag } from "@opencode-ai/core/flag/flag"
36import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server"
47import { McpPaths } from "../../src/server/routes/instance/httpapi/mcp"
58import { Instance } from "../../src/project/instance"
9+ import { InstanceRoutes } from "../../src/server/routes/instance"
610import * as Log from "@opencode-ai/core/util/log"
711import { resetDatabase } from "../fixture/db"
8- import { tmpdir } from "../fixture/fixture"
12+ import { provideInstance , tmpdir } from "../fixture/fixture"
13+ import { testEffect } from "../lib/effect"
914
1015void Log . init ( { print : false } )
1116
17+ const original = Flag . OPENCODE_EXPERIMENTAL_HTTPAPI
1218const context = Context . empty ( ) as Context . Context < unknown >
19+ const websocket = ( ( ) => ( ) => new Response ( null , { status : 501 } ) ) as unknown as UpgradeWebSocket
20+ const it = testEffect ( Layer . mergeAll ( NodeFileSystem . layer , NodePath . layer ) )
21+
22+ function app ( experimental : boolean ) {
23+ Flag . OPENCODE_EXPERIMENTAL_HTTPAPI = experimental
24+ return InstanceRoutes ( websocket )
25+ }
1326
1427function request ( route : string , directory : string , init ?: RequestInit ) {
1528 const headers = new Headers ( init ?. headers )
@@ -23,7 +36,51 @@ function request(route: string, directory: string, init?: RequestInit) {
2336 )
2437}
2538
39+ function withMcpProject < A , E , R > ( self : ( dir : string ) => Effect . Effect < A , E , R > ) {
40+ return Effect . gen ( function * ( ) {
41+ const fs = yield * FileSystem . FileSystem
42+ const path = yield * Path . Path
43+ const dir = yield * fs . makeTempDirectoryScoped ( { prefix : "opencode-test-" } )
44+
45+ yield * fs . writeFileString (
46+ path . join ( dir , "opencode.json" ) ,
47+ JSON . stringify ( {
48+ $schema : "https://opencode.ai/config.json" ,
49+ formatter : false ,
50+ lsp : false ,
51+ mcp : {
52+ demo : {
53+ type : "local" ,
54+ command : [ "echo" , "demo" ] ,
55+ enabled : false ,
56+ } ,
57+ } ,
58+ } ) ,
59+ )
60+ yield * Effect . addFinalizer ( ( ) =>
61+ Effect . promise ( ( ) => Instance . provide ( { directory : dir , fn : ( ) => Instance . dispose ( ) } ) ) . pipe ( Effect . ignore ) ,
62+ )
63+
64+ return yield * self ( dir ) . pipe ( provideInstance ( dir ) )
65+ } )
66+ }
67+
68+ const readResponse = Effect . fnUntraced ( function * ( input : {
69+ app : ReturnType < typeof InstanceRoutes >
70+ path : string
71+ headers : HeadersInit
72+ } ) {
73+ const response = yield * Effect . promise ( ( ) =>
74+ Promise . resolve ( input . app . request ( input . path , { method : "POST" , headers : input . headers } ) ) ,
75+ )
76+ return {
77+ status : response . status ,
78+ body : yield * Effect . promise ( ( ) => response . text ( ) ) ,
79+ }
80+ } )
81+
2682afterEach ( async ( ) => {
83+ Flag . OPENCODE_EXPERIMENTAL_HTTPAPI = original
2784 await Instance . disposeAll ( )
2885 await resetDatabase ( )
2986} )
@@ -107,4 +164,28 @@ describe("mcp HttpApi", () => {
107164 expect ( removed . status ) . toBe ( 200 )
108165 expect ( await removed . json ( ) ) . toEqual ( { success : true } )
109166 } )
167+
168+ it . live (
169+ "matches legacy unsupported OAuth error responses" ,
170+ withMcpProject ( ( dir ) =>
171+ Effect . gen ( function * ( ) {
172+ const headers = { "x-opencode-directory" : dir }
173+ const legacy = app ( false )
174+ const httpapi = app ( true )
175+
176+ yield * Effect . forEach ( [ "/mcp/demo/auth" , "/mcp/demo/auth/authenticate" ] , ( path ) =>
177+ Effect . gen ( function * ( ) {
178+ const legacyResponse = yield * readResponse ( { app : legacy , path, headers } )
179+ const httpapiResponse = yield * readResponse ( { app : httpapi , path, headers } )
180+
181+ expect ( legacyResponse ) . toEqual ( {
182+ status : 400 ,
183+ body : JSON . stringify ( { error : "MCP server demo does not support OAuth" } ) ,
184+ } )
185+ expect ( httpapiResponse ) . toEqual ( legacyResponse )
186+ } ) ,
187+ )
188+ } ) ,
189+ ) ,
190+ )
110191} )
0 commit comments