Skip to content

Commit cbdb2d9

Browse files
authored
test(server): expand workspace routing fixed-id coverage (#26458)
1 parent 96bde05 commit cbdb2d9

2 files changed

Lines changed: 83 additions & 8 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { WorkspaceID } from "@/control-plane/schema"
2+
import { Flag } from "@opencode-ai/core/flag/flag"
3+
import { Effect, Scope } from "effect"
4+
5+
/**
6+
* Scoped override for `Flag.OPENCODE_WORKSPACE_ID`. Saves the previous value
7+
* on entry and restores it via finalizer when the surrounding scope closes —
8+
* preserves the original try/finally semantics regardless of test outcome.
9+
*/
10+
export function withFixedWorkspaceID(id: WorkspaceID): Effect.Effect<void, never, Scope.Scope> {
11+
return Effect.gen(function* () {
12+
const previous = Flag.OPENCODE_WORKSPACE_ID
13+
Flag.OPENCODE_WORKSPACE_ID = id
14+
yield* Effect.addFinalizer(() =>
15+
Effect.sync(() => {
16+
Flag.OPENCODE_WORKSPACE_ID = previous
17+
}),
18+
)
19+
})
20+
}

packages/opencode/test/server/httpapi-instance-context.test.ts

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { instanceRouterMiddleware } from "../../src/server/routes/instance/httpa
2121
import { workspaceRouterMiddleware } from "../../src/server/routes/instance/httpapi/middleware/workspace-routing"
2222
import { resetDatabase } from "../fixture/db"
2323
import { disposeAllInstances, tmpdirScoped } from "../fixture/fixture"
24+
import { withFixedWorkspaceID } from "../fixture/flag"
2425
import { waitGlobalBusEvent } from "./global-bus"
2526
import { testEffect } from "../lib/effect"
2627

@@ -204,16 +205,10 @@ describe("HttpApi instance context middleware", () => {
204205
}),
205206
)
206207

207-
it.live("uses configured workspace id instead of routing to requested workspaces", () =>
208+
it.live("uses configured workspace id instead of routing to the requested workspace", () =>
208209
Effect.gen(function* () {
209-
const originalWorkspaceID = Flag.OPENCODE_WORKSPACE_ID
210210
const fixedWorkspaceID = WorkspaceID.ascending()
211-
Flag.OPENCODE_WORKSPACE_ID = fixedWorkspaceID
212-
yield* Effect.addFinalizer(() =>
213-
Effect.sync(() => {
214-
Flag.OPENCODE_WORKSPACE_ID = originalWorkspaceID
215-
}),
216-
)
211+
yield* withFixedWorkspaceID(fixedWorkspaceID)
217212

218213
const dir = yield* tmpdirScoped({ git: true })
219214
const project = yield* Project.use.fromDirectory(dir)
@@ -238,6 +233,66 @@ describe("HttpApi instance context middleware", () => {
238233
}),
239234
)
240235

236+
it.live("falls through to local instead of MissingWorkspace when configured workspace id is set", () =>
237+
Effect.gen(function* () {
238+
const fixedWorkspaceID = WorkspaceID.ascending()
239+
yield* withFixedWorkspaceID(fixedWorkspaceID)
240+
241+
const dir = yield* tmpdirScoped({ git: true })
242+
yield* Project.use.fromDirectory(dir)
243+
yield* serveProbe()
244+
245+
// Reference a workspace id that is not registered locally. Without the
246+
// configured env override, this would short-circuit to a 500
247+
// MissingWorkspace response. With the env set, planRequest must skip the
248+
// MissingWorkspace branch and fall through to Local with the configured
249+
// workspace id.
250+
const unknownWorkspaceID = WorkspaceID.ascending()
251+
const response = yield* HttpClientRequest.get(`/probe?workspace=${unknownWorkspaceID}`).pipe(
252+
HttpClientRequest.setHeader("x-opencode-directory", dir),
253+
HttpClient.execute,
254+
)
255+
256+
expect(response.status).toBe(200)
257+
expect(yield* response.json).toMatchObject({
258+
directory: dir,
259+
workspaceID: fixedWorkspaceID,
260+
})
261+
}),
262+
)
263+
264+
it.live("keeps configured workspace id on control-plane routes without remote routing", () =>
265+
Effect.gen(function* () {
266+
const fixedWorkspaceID = WorkspaceID.ascending()
267+
yield* withFixedWorkspaceID(fixedWorkspaceID)
268+
269+
const dir = yield* tmpdirScoped({ git: true })
270+
const project = yield* Project.use.fromDirectory(dir)
271+
const workspaceDir = path.join(dir, ".workspace-local")
272+
const workspace = yield* createLocalWorkspace({
273+
projectID: project.project.id,
274+
type: "instance-context-fixed-workspace-control-plane",
275+
directory: workspaceDir,
276+
})
277+
// /session is matched by isLocalWorkspaceRoute, so shouldStayOnControlPlane
278+
// is true. Combined with the env override, the route must stay Local with
279+
// the configured workspace id (not divert to the requested workspace's
280+
// local directory).
281+
yield* serveProbe("/session")
282+
283+
const response = yield* HttpClientRequest.get(`/session?workspace=${workspace.id}`).pipe(
284+
HttpClientRequest.setHeader("x-opencode-directory", dir),
285+
HttpClient.execute,
286+
)
287+
288+
expect(response.status).toBe(200)
289+
expect(yield* response.json).toMatchObject({
290+
directory: dir,
291+
workspaceID: fixedWorkspaceID,
292+
})
293+
}),
294+
)
295+
241296
it.live("preserves selected workspace id on instance disposal events", () =>
242297
Effect.gen(function* () {
243298
const dir = yield* tmpdirScoped({ git: true })

0 commit comments

Comments
 (0)