diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/control.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/control.ts index 33e6a8e4a0..3bac1b6de0 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/control.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/control.ts @@ -2,16 +2,14 @@ import { Auth } from "@/auth" import { ProviderID } from "@/provider/schema" import { Schema } from "effect" import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" +import { withWorkspaceRouting } from "../query" import { described } from "./metadata" const AuthParams = Schema.Struct({ providerID: ProviderID, }) -const LogQuery = Schema.Struct({ - directory: Schema.optional(Schema.String), - workspace: Schema.optional(Schema.String), -}) +const LogQuery = withWorkspaceRouting({}) export const LogInput = Schema.Struct({ service: Schema.String.annotate({ description: "Service name for the log entry" }), diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts index 3488d2616c..4b9a6a0f77 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts @@ -9,6 +9,7 @@ import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "e import { Authorization } from "../middleware/authorization" import { InstanceContextMiddleware } from "../middleware/instance-context" import { WorkspaceRoutingMiddleware } from "../middleware/workspace-routing" +import { withWorkspaceRouting } from "../query" import { described } from "./metadata" const ConsoleStateResponse = Schema.Struct({ @@ -42,7 +43,7 @@ const ToolListItem = Schema.Struct({ parameters: Schema.Unknown, }).annotate({ identifier: "ToolListItem" }) const ToolList = Schema.Array(ToolListItem).annotate({ identifier: "ToolList" }) -export const ToolListQuery = Schema.Struct({ +export const ToolListQuery = withWorkspaceRouting({ provider: ProviderID, model: ModelID, }) @@ -54,7 +55,7 @@ const QueryBoolean = Schema.Literals(["true", "false"]).pipe( }), ) const WorktreeList = Schema.Array(Schema.String) -export const SessionListQuery = Schema.Struct({ +export const SessionListQuery = withWorkspaceRouting({ directory: Schema.optional(Schema.String), roots: Schema.optional(QueryBoolean), start: Schema.optional(Schema.NumberFromString), diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/file.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/file.ts index b950adb383..a4a11b3f95 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/file.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/file.ts @@ -6,17 +6,18 @@ import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable import { Authorization } from "../middleware/authorization" import { InstanceContextMiddleware } from "../middleware/instance-context" import { WorkspaceRoutingMiddleware } from "../middleware/workspace-routing" +import { withWorkspaceRouting } from "../query" import { described } from "./metadata" -export const FileQuery = Schema.Struct({ +export const FileQuery = withWorkspaceRouting({ path: Schema.String, }) -export const FindTextQuery = Schema.Struct({ +export const FindTextQuery = withWorkspaceRouting({ pattern: Schema.String, }) -export const FindFileQuery = Schema.Struct({ +export const FindFileQuery = withWorkspaceRouting({ query: Schema.String, dirs: Schema.optional(Schema.Literals(["true", "false"])), type: Schema.optional(Schema.Literals(["file", "directory"])), @@ -25,7 +26,7 @@ export const FindFileQuery = Schema.Struct({ ), }) -export const FindSymbolQuery = Schema.Struct({ +export const FindSymbolQuery = withWorkspaceRouting({ query: Schema.String, }) diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/instance.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/instance.ts index f2b0504a05..9045747c9c 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/instance.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/instance.ts @@ -9,6 +9,7 @@ import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApiSchema, OpenApi } from " import { Authorization } from "../middleware/authorization" import { InstanceContextMiddleware } from "../middleware/instance-context" import { WorkspaceRoutingMiddleware } from "../middleware/workspace-routing" +import { withWorkspaceRouting } from "../query" import { described } from "./metadata" const PathInfo = Schema.Struct({ @@ -19,7 +20,7 @@ const PathInfo = Schema.Struct({ directory: Schema.String, }).annotate({ identifier: "Path" }) -export const VcsDiffQuery = Schema.Struct({ +export const VcsDiffQuery = withWorkspaceRouting({ mode: Vcs.Mode, }) diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/session.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/session.ts index 967cc80206..43fc0bed3e 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/session.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/session.ts @@ -15,6 +15,7 @@ import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, HttpApiSchema, Op import { Authorization } from "../middleware/authorization" import { InstanceContextMiddleware } from "../middleware/instance-context" import { WorkspaceRoutingMiddleware } from "../middleware/workspace-routing" +import { withWorkspaceRouting } from "../query" import { ApiNotFoundError } from "../errors" import { described } from "./metadata" @@ -25,7 +26,7 @@ const QueryBoolean = Schema.Literals(["true", "false"]).pipe( encode: SchemaGetter.transform((value) => (value ? "true" : "false")), }), ) -export const ListQuery = Schema.Struct({ +export const ListQuery = withWorkspaceRouting({ directory: Schema.optional(Schema.String), scope: Schema.optional(Schema.Literals(["project"])), path: Schema.optional(Schema.String), @@ -34,8 +35,8 @@ export const ListQuery = Schema.Struct({ search: Schema.optional(Schema.String), limit: Schema.optional(Schema.NumberFromString), }) -export const DiffQuery = Schema.Struct(Struct.omit(SessionSummary.DiffInput.fields, ["sessionID"])) -export const MessagesQuery = Schema.Struct({ +export const DiffQuery = withWorkspaceRouting(Struct.omit(SessionSummary.DiffInput.fields, ["sessionID"])) +export const MessagesQuery = withWorkspaceRouting({ limit: Schema.optional(Schema.NumberFromString.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0))), before: Schema.optional(Schema.String), }) diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/message.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/message.ts index 3b0b2fa5b1..a66483d34f 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/message.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/message.ts @@ -3,6 +3,7 @@ import { SessionMessage } from "@/v2/session-message" import { Schema } from "effect" import { HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi" import { Authorization } from "../../middleware/authorization" +import { WorkspaceRoutingQueryFields } from "../../query" export const MessageGroup = HttpApiGroup.make("v2.message") .add( @@ -10,6 +11,7 @@ export const MessageGroup = HttpApiGroup.make("v2.message") params: { sessionID: SessionID }, query: Schema.Union([ Schema.Struct({ + ...WorkspaceRoutingQueryFields, limit: Schema.optional( Schema.NumberFromString.check( Schema.isInt(), @@ -26,6 +28,7 @@ export const MessageGroup = HttpApiGroup.make("v2.message") cursor: Schema.optional(Schema.Never), }), Schema.Struct({ + ...WorkspaceRoutingQueryFields, limit: Schema.optional( Schema.NumberFromString.check( Schema.isInt(), diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/session.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/session.ts index 17ddcaeda3..3041d73ee1 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/v2/session.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/v2/session.ts @@ -6,12 +6,14 @@ import { SessionV2 } from "@/v2/session" import { Schema, SchemaGetter } from "effect" import { HttpApiEndpoint, HttpApiError, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi" import { Authorization } from "../../middleware/authorization" +import { WorkspaceRoutingQueryFields } from "../../query" export const SessionGroup = HttpApiGroup.make("v2.session") .add( HttpApiEndpoint.get("sessions", "/api/session", { query: Schema.Union([ Schema.Struct({ + ...WorkspaceRoutingQueryFields, limit: Schema.optional( Schema.NumberFromString.check( Schema.isInt(), @@ -24,7 +26,6 @@ export const SessionGroup = HttpApiGroup.make("v2.session") order: Schema.optional(Schema.Union([Schema.Literal("asc"), Schema.Literal("desc")])).annotate({ description: "Session order for the first page. Use desc for newest first or asc for oldest first.", }), - directory: Schema.String.pipe(Schema.optional), path: Schema.String.pipe(Schema.optional), workspace: WorkspaceID.pipe(Schema.optional), roots: Schema.Literals(["true", "false"]) @@ -40,6 +41,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session") cursor: Schema.optional(Schema.Never), }), Schema.Struct({ + ...WorkspaceRoutingQueryFields, limit: Schema.optional( Schema.NumberFromString.check( Schema.isInt(), @@ -54,9 +56,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session") "Opaque pagination cursor returned as cursor.previous or cursor.next in the previous response. Do not combine with order.", }), order: Schema.optional(Schema.Never), - directory: Schema.optional(Schema.Never), path: Schema.optional(Schema.Never), - workspace: Schema.optional(Schema.Never), roots: Schema.optional(Schema.Never), start: Schema.optional(Schema.Never), search: Schema.optional(Schema.Never), diff --git a/packages/opencode/src/server/routes/instance/httpapi/query.ts b/packages/opencode/src/server/routes/instance/httpapi/query.ts new file mode 100644 index 0000000000..48432c44ce --- /dev/null +++ b/packages/opencode/src/server/routes/instance/httpapi/query.ts @@ -0,0 +1,13 @@ +import { Schema } from "effect" + +export const WorkspaceRoutingQueryFields = { + directory: Schema.optional(Schema.String), + workspace: Schema.optional(Schema.String), +} + +export function withWorkspaceRouting(fields: T) { + return Schema.Struct({ + ...WorkspaceRoutingQueryFields, + ...fields, + }) +} diff --git a/packages/opencode/test/server/httpapi-query-schema-drift.test.ts b/packages/opencode/test/server/httpapi-query-schema-drift.test.ts new file mode 100644 index 0000000000..2d2d78d2ca --- /dev/null +++ b/packages/opencode/test/server/httpapi-query-schema-drift.test.ts @@ -0,0 +1,205 @@ +import { afterEach, describe, expect } from "bun:test" +import { Effect } from "effect" +import { Flag } from "@opencode-ai/core/flag/flag" +import { WithInstance } from "../../src/project/with-instance" +import { Session } from "@/session/session" +import { MessageID, PartID, SessionID, type SessionID as SessionIDType } from "../../src/session/schema" +import { ModelID, ProviderID } from "../../src/provider/schema" +import { resetDatabase } from "../fixture/db" +import { disposeAllInstances, tmpdir } from "../fixture/fixture" +import { it } from "../lib/effect" +import { SessionPaths } from "../../src/server/routes/instance/httpapi/groups/session" +import { FilePaths } from "../../src/server/routes/instance/httpapi/groups/file" + +void (await import("@opencode-ai/core/util/log")).init({ print: false }) + +const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES + +function runSession(fx: Effect.Effect) { + return Effect.runPromise(fx.pipe(Effect.provide(Session.defaultLayer))) +} + +function pathFor(path: string, params: Record) { + return Object.entries(params).reduce((result, [key, value]) => result.replace(`:${key}`, value), path) +} + +function createSession(directory: string, input?: Session.CreateInput) { + return Effect.promise( + async () => + await WithInstance.provide({ + directory, + fn: () => runSession(Session.Service.use((svc) => svc.create(input))), + }), + ) +} + +function createTextMessage(directory: string, sessionID: SessionIDType, text: string) { + return Effect.promise( + async () => + await WithInstance.provide({ + directory, + fn: () => + runSession( + Effect.gen(function* () { + const svc = yield* Session.Service + const info = yield* svc.updateMessage({ + id: MessageID.ascending(), + role: "user", + sessionID, + agent: "build", + model: { providerID: ProviderID.make("test"), modelID: ModelID.make("test") }, + time: { created: Date.now() }, + }) + const part = yield* svc.updatePart({ + id: PartID.ascending(), + sessionID, + messageID: info.id, + type: "text", + text, + }) + return { info, part } + }), + ), + }), + ) +} + +function request(path: string, init?: RequestInit) { + return Effect.promise(async () => { + const { Server } = await import("../../src/server/server") + return Server.Default().app.request(path, init) + }) +} + +function withTmp( + options: Parameters[0], + fn: (tmp: Awaited>) => Effect.Effect, +) { + return Effect.acquireRelease( + Effect.promise(() => tmpdir(options)), + (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()), + ).pipe(Effect.flatMap(fn)) +} + +afterEach(async () => { + Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = originalWorkspaces + await disposeAllInstances() + await resetDatabase() +}) + +/** + * Reproducer for: runtime HttpApi query schemas must accept directory/workspace + * + * Previously, the OpenAPI spec was manually injected with directory/workspace query + * params (InstanceQueryParameters in public.ts), but the runtime query schemas + * did not include these fields. This caused a drift where: + * 1. Generated SDKs would send requests with ?directory=...&workspace=... + * 2. But runtime validation would reject these as unknown fields + * 3. Resulting in 400 Bad Request errors + * + * The fix adds directory/workspace to all instance route query schemas using the + * extendWithInstanceQuery helper in query.ts. + */ +describe("query schema drift fix", () => { + it.live( + "accepts directory and workspace query params on session.messages route", + withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + Effect.gen(function* () { + const headers = { "x-opencode-directory": tmp.path } + const session = yield* createSession(tmp.path, { title: "drift test" }) + yield* createTextMessage(tmp.path, session.id, "test message") + + // This should NOT return 400 - previously it would fail validation + // because MessagesQuery didn't include directory/workspace fields + const response = yield* request( + `${pathFor(SessionPaths.messages, { sessionID: session.id })}?limit=1&directory=${encodeURIComponent(tmp.path)}`, + { headers }, + ) + + // Should be 200 OK, not 400 Bad Request due to unknown query params + // Note: workspace param is omitted because an invalid workspace ID would cause 500 + expect(response.status).toBe(200) + + const body = yield* Effect.promise(() => response.json()) + expect(Array.isArray(body)).toBe(true) + expect(body.length).toBe(1) + }), + ), + ) + + it.live( + "accepts directory and workspace query params on file.list route", + withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + Effect.gen(function* () { + const headers = { "x-opencode-directory": tmp.path } + + // Create a test file + const testFile = `${tmp.path}/test.txt` + yield* Effect.promise(() => Bun.write(testFile, "test content")) + + // This should NOT return 400 - previously FileQuery didn't include directory/workspace + const response = yield* request( + `${FilePaths.list}?path=${encodeURIComponent(tmp.path)}&directory=${encodeURIComponent(tmp.path)}`, + { headers }, + ) + + // Should be 200 OK, not 400 Bad Request + // Note: workspace param is omitted because an invalid workspace ID would cause 500 + expect(response.status).toBe(200) + + const body = yield* Effect.promise(() => response.json()) + expect(Array.isArray(body)).toBe(true) + }), + ), + ) + + it.live( + "accepts directory and workspace query params on session.list route", + withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + Effect.gen(function* () { + const headers = { "x-opencode-directory": tmp.path } + yield* createSession(tmp.path, { title: "list drift test" }) + + // This should NOT return 400 - ListQuery already had directory but now includes workspace + // Use only directory parameter since invalid workspace ID would cause 500 + const response = yield* request( + `${SessionPaths.list}?directory=${encodeURIComponent(tmp.path)}`, + { headers }, + ) + + // Should be 200 OK, not 400 Bad Request + expect(response.status).toBe(200) + + const body = yield* Effect.promise(() => response.json()) + expect(Array.isArray(body)).toBe(true) + expect(body.length).toBeGreaterThan(0) + }), + ), + ) + + it.live( + "accepts directory and workspace query params on find.file route", + withTmp({ git: true, config: { formatter: false, lsp: false } }, (tmp) => + Effect.gen(function* () { + const headers = { "x-opencode-directory": tmp.path } + + // Create a test file + const testFile = `${tmp.path}/findme.txt` + yield* Effect.promise(() => Bun.write(testFile, "test content")) + + // This should NOT return 400 - FindFileQuery now includes directory/workspace + // Use only directory parameter since invalid workspace ID would cause 500 + const response = yield* request( + `${FilePaths.findFile}?query=findme&directory=${encodeURIComponent(tmp.path)}`, + { headers }, + ) + + // Should be 200 OK, not 400 Bad Request + expect(response.status).toBe(200) + + const body = yield* Effect.promise(() => response.json()) + expect(Array.isArray(body)).toBe(true) + }), + ), + ) +}) diff --git a/packages/opencode/test/server/httpapi-sdk.test.ts b/packages/opencode/test/server/httpapi-sdk.test.ts index 8a179a4dcc..0b03ad00a8 100644 --- a/packages/opencode/test/server/httpapi-sdk.test.ts +++ b/packages/opencode/test/server/httpapi-sdk.test.ts @@ -331,18 +331,36 @@ describe("HttpApi SDK", () => { httpapi( "uses the generated SDK for safe instance routes", - withProject("raw", { git: false, setup: writeStandardFiles }, ({ sdk }) => + withProject("raw", { setup: writeStandardFiles }, ({ sdk }) => Effect.gen(function* () { const file = yield* call(() => sdk.file.read({ path: "hello.txt" })) + const files = yield* call(() => sdk.file.list({ path: "." })) + const status = yield* call(() => sdk.file.status()) + const findText = yield* call(() => sdk.find.text({ pattern: "sdk-parity" })) + const findFiles = yield* call(() => sdk.find.files({ query: "hello", limit: 10 })) + const findSymbols = yield* call(() => sdk.find.symbols({ query: "hello" })) const session = yield* call(() => sdk.session.create({ title: "sdk" })) const listed = yield* call(() => sdk.session.list({ roots: true, limit: 10 })) + const messages = yield* call(() => sdk.session.messages({ sessionID: String(record(session.data).id), limit: 1 })) + const diff = yield* call(() => sdk.session.diff({ sessionID: String(record(session.data).id) })) + const vcsDiff = yield* call(() => sdk.vcs.diff({ mode: "git" })) + const tools = yield* call(() => sdk.tool.list({ provider: "opencode", model: "gpt-5" })) expect(file.response.status).toBe(200) expect(file.data).toMatchObject({ content: "hello" }) + expect(files.response.status).toBe(200) + expect(status.response.status).toBe(200) + expect(findText.response.status).toBe(200) + expect(findFiles.response.status).toBe(200) + expect(findSymbols.response.status).toBe(200) expect(session.response.status).toBe(200) expect(session.data).toMatchObject({ title: "sdk" }) expect(listed.response.status).toBe(200) expect(listed.data?.map((item) => item.id)).toContain(session.data?.id) + expect(messages.response.status).toBe(200) + expect(diff.response.status).toBe(200) + expect(vcsDiff.response.status).toBe(200) + expect(tools.response.status).toBe(200) yield* Effect.all([ expectStatus(() => sdk.project.current(), 200), @@ -464,9 +482,11 @@ describe("HttpApi SDK", () => { const fileStatus = yield* capture(() => sdk.file.status()) const findFiles = yield* capture(() => sdk.find.files({ query: "hello", limit: 10 })) const findText = yield* capture(() => sdk.find.text({ pattern: "sdk-parity" })) + const vcsDiff = yield* capture(() => sdk.vcs.diff({ mode: "git" })) const agents = yield* capture(() => sdk.app.agents()) const skills = yield* capture(() => sdk.app.skills()) const tools = yield* capture(() => sdk.tool.ids()) + const modelTools = yield* capture(() => sdk.tool.list({ provider: "opencode", model: "gpt-5" })) const vcs = yield* capture(() => sdk.vcs.get()) const formatter = yield* capture(() => sdk.formatter.status()) const lsp = yield* capture(() => sdk.lsp.status()) @@ -483,9 +503,11 @@ describe("HttpApi SDK", () => { fileStatus, findFiles, findText, + vcsDiff, agents, skills, tools, + modelTools, vcs, formatter, lsp, @@ -515,6 +537,7 @@ describe("HttpApi SDK", () => { const all = yield* capture(() => sdk.session.list({ roots: false, limit: 10 })) const children = yield* capture(() => sdk.session.children({ sessionID: parentID })) const todo = yield* capture(() => sdk.session.todo({ sessionID: parentID })) + const diff = yield* capture(() => sdk.session.diff({ sessionID: parentID })) const status = yield* capture(() => sdk.session.status()) const messages = yield* capture(() => sdk.session.messages({ sessionID: parentID })) const missingGet = yield* capture(() => sdk.session.get({ sessionID: "ses_missing" })) @@ -535,6 +558,7 @@ describe("HttpApi SDK", () => { all, children, todo, + diff, status, messages, missingGet, diff --git a/packages/opencode/test/server/httpapi-session.test.ts b/packages/opencode/test/server/httpapi-session.test.ts index 96ddf8fcce..61cc5469a5 100644 --- a/packages/opencode/test/server/httpapi-session.test.ts +++ b/packages/opencode/test/server/httpapi-session.test.ts @@ -290,8 +290,10 @@ describe("session HttpApi", () => { ) expect( - (yield* requestJson<{ items: SessionMessage.Message[] }>(`/api/session/${parent.id}/message`, { headers })) - .items, + (yield* requestJson<{ items: SessionMessage.Message[] }>( + `/api/session/${parent.id}/message?directory=${encodeURIComponent(tmp.path)}`, + { headers }, + )).items, ).toMatchObject([{ type: "assistant" }]) }), ), @@ -364,8 +366,12 @@ describe("session HttpApi", () => { headers: { "x-opencode-directory": tmp.path, "content-type": "application/json" }, body: JSON.stringify({ title: "workspace session" }), }) + const messages = yield* request(`${pathFor(SessionPaths.messages, { sessionID: created.id })}?workspace=${workspace.id}`, { + headers: { "x-opencode-directory": tmp.path }, + }) expect(created).toMatchObject({ id: created.id, workspaceID: workspace.id }) + expect(messages.status).toBe(200) expect( yield* Effect.sync(() => Database.use((db) => diff --git a/packages/opencode/test/server/session-messages.test.ts b/packages/opencode/test/server/session-messages.test.ts index e3c5e83136..b81986b066 100644 --- a/packages/opencode/test/server/session-messages.test.ts +++ b/packages/opencode/test/server/session-messages.test.ts @@ -165,4 +165,28 @@ describe("session messages endpoint", () => { }), ) }) + + test("accepts workspace routing query params with paginated message requests", async () => { + await using tmp = await tmpdir({ git: true }) + await withoutWatcher(() => + WithInstance.provide({ + directory: tmp.path, + fn: async () => { + const session = await svc.create({}) + await fill(session.id, 1) + const app = Server.Default().app + + const directory = await app.request( + `/session/${session.id}/message?limit=80&directory=${encodeURIComponent(tmp.path)}`, + ) + const workspace = await app.request(`/session/${session.id}/message?limit=80&workspace=wrk_test`) + + expect(directory.status).toBe(200) + expect(workspace.status).toBe(200) + + await svc.remove(session.id) + }, + }), + ) + }) })