refactor(opencode): use layer nodes in server tests (#34503)

This commit is contained in:
James Long 2026-06-29 16:50:59 -04:00 committed by GitHub
commit 3726052307
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 83 additions and 104 deletions

View file

@ -1,5 +1,4 @@
import { FetchHttpClient } from "effect/unstable/http" import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { Layer } from "effect"
import { LayerNode } from "@opencode-ai/core/effect/layer-node" import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { Database } from "@opencode-ai/core/database/database" import { Database } from "@opencode-ai/core/database/database"
import { FSUtil } from "@opencode-ai/core/fs-util" import { FSUtil } from "@opencode-ai/core/fs-util"
@ -15,16 +14,21 @@ import { SessionPrompt } from "../../src/session/prompt"
import { EventV2Bridge } from "../../src/event-v2-bridge" import { EventV2Bridge } from "../../src/event-v2-bridge"
export const workspaceLayerWithRuntimeFlags = (overrides: Partial<RuntimeFlags.Info>) => export const workspaceLayerWithRuntimeFlags = (overrides: Partial<RuntimeFlags.Info>) =>
Workspace.layer.pipe( AppNodeBuilder.build(
Layer.provide(Auth.defaultLayer), LayerNode.group([
Layer.provide(Session.defaultLayer), Workspace.node,
Layer.provide(SessionPrompt.defaultLayer), Auth.node,
Layer.provide(Project.defaultLayer), Session.node,
Layer.provide(Vcs.defaultLayer), SessionPrompt.node,
Layer.provide(Database.defaultLayer), Project.node,
Layer.provide(EventV2Bridge.defaultLayer), Vcs.node,
Layer.provide(FetchHttpClient.layer), Database.node,
Layer.provide(FSUtil.defaultLayer), EventV2Bridge.node,
Layer.provide(RuntimeFlags.layer(overrides)), FSUtil.node,
Layer.provide(LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, InstanceBootstrap.node]])), InstanceStore.node,
]),
[
[InstanceStore.bootstrapNode, InstanceBootstrap.node],
[RuntimeFlags.node, RuntimeFlags.layer(overrides)],
],
) )

View file

@ -1,7 +1,6 @@
import { NodeHttpServer, NodeServices } from "@effect/platform-node" import { NodeHttpServer, NodeServices } from "@effect/platform-node"
import { describe, expect } from "bun:test" import { describe, expect } from "bun:test"
import { Effect, Fiber, Layer, Schema } from "effect" import { Effect, Fiber, Layer, Schema } from "effect"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { HttpClient, HttpClientRequest, HttpRouter } from "effect/unstable/http" import { HttpClient, HttpClientRequest, HttpRouter } from "effect/unstable/http"
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi" import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi"
import * as Socket from "effect/unstable/socket/Socket" import * as Socket from "effect/unstable/socket/Socket"
@ -9,12 +8,9 @@ import { mkdir } from "node:fs/promises"
import path from "node:path" import path from "node:path"
import { registerAdapter } from "../../src/control-plane/adapters" import { registerAdapter } from "../../src/control-plane/adapters"
import { WorkspaceV2 } from "@opencode-ai/core/workspace" import { WorkspaceV2 } from "@opencode-ai/core/workspace"
import { Ripgrep } from "@opencode-ai/core/ripgrep"
import type { WorkspaceAdapter } from "../../src/control-plane/types" import type { WorkspaceAdapter } from "../../src/control-plane/types"
import { Workspace } from "../../src/control-plane/workspace" import { Workspace } from "../../src/control-plane/workspace"
import { InstanceRef, WorkspaceRef } from "../../src/effect/instance-ref" import { InstanceRef, WorkspaceRef } from "../../src/effect/instance-ref"
import { InstanceBootstrap } from "../../src/project/bootstrap"
import { InstanceStore } from "../../src/project/instance-store"
import { Project } from "../../src/project/project" import { Project } from "../../src/project/project"
import { Session } from "../../src/session/session" import { Session } from "../../src/session/session"
import { disposeMiddleware, markInstanceForDisposal } from "../../src/server/routes/instance/httpapi/lifecycle" import { disposeMiddleware, markInstanceForDisposal } from "../../src/server/routes/instance/httpapi/lifecycle"
@ -53,10 +49,8 @@ const it = testEffect(
testStateLayer, testStateLayer,
NodeHttpServer.layerTest, NodeHttpServer.layerTest,
NodeServices.layer, NodeServices.layer,
LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, InstanceBootstrap.node]]),
Project.defaultLayer,
workspaceLayer, workspaceLayer,
).pipe(Layer.provide(Ripgrep.defaultLayer)), ),
) )
const instanceContextTestLayer = Layer.mergeAll( const instanceContextTestLayer = Layer.mergeAll(

View file

@ -10,19 +10,15 @@
import { NodeHttpServer, NodeServices } from "@effect/platform-node" import { NodeHttpServer, NodeServices } from "@effect/platform-node"
import { describe, expect } from "bun:test" import { describe, expect } from "bun:test"
import { Deferred, Effect, Layer, Schema, Scope } from "effect" import { Deferred, Effect, Layer, Schema, Scope } from "effect"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import * as Stream from "effect/Stream" import * as Stream from "effect/Stream"
import { HttpClient, HttpRouter, HttpServerResponse } from "effect/unstable/http" import { HttpClient, HttpRouter, HttpServerResponse } from "effect/unstable/http"
import * as Socket from "effect/unstable/socket/Socket" import * as Socket from "effect/unstable/socket/Socket"
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/unstable/httpapi" import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/unstable/httpapi"
import { mkdir } from "node:fs/promises" import { mkdir } from "node:fs/promises"
import { registerAdapter } from "../../src/control-plane/adapters" import { registerAdapter } from "../../src/control-plane/adapters"
import { Ripgrep } from "@opencode-ai/core/ripgrep"
import type { WorkspaceAdapter } from "../../src/control-plane/types" import type { WorkspaceAdapter } from "../../src/control-plane/types"
import { Workspace } from "../../src/control-plane/workspace" import { Workspace } from "../../src/control-plane/workspace"
import { InstanceRef, WorkspaceRef } from "../../src/effect/instance-ref" import { InstanceRef, WorkspaceRef } from "../../src/effect/instance-ref"
import { InstanceBootstrap } from "../../src/project/bootstrap"
import { InstanceStore } from "../../src/project/instance-store"
import { Project } from "../../src/project/project" import { Project } from "../../src/project/project"
import { Session } from "../../src/session/session" import { Session } from "../../src/session/session"
import { import {
@ -58,10 +54,8 @@ const it = testEffect(
testStateLayer, testStateLayer,
NodeHttpServer.layerTest, NodeHttpServer.layerTest,
NodeServices.layer, NodeServices.layer,
LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, InstanceBootstrap.node]]),
Project.defaultLayer,
workspaceLayer, workspaceLayer,
).pipe(Layer.provide(Ripgrep.defaultLayer)), ),
) )
const instanceContextTestLayer = Layer.mergeAll( const instanceContextTestLayer = Layer.mergeAll(

View file

@ -5,13 +5,14 @@ import { Deferred, Effect, Layer } from "effect"
import type * as Scope from "effect/Scope" import type * as Scope from "effect/Scope"
import { HttpServer } from "effect/unstable/http" import { HttpServer } from "effect/unstable/http"
import { ChildProcessSpawner } from "effect/unstable/process" import { ChildProcessSpawner } from "effect/unstable/process"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { FSUtil } from "@opencode-ai/core/fs-util" import { FSUtil } from "@opencode-ai/core/fs-util"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { Flag } from "@opencode-ai/core/flag/flag" import { Flag } from "@opencode-ai/core/flag/flag"
import { createOpencodeClient } from "@opencode-ai/sdk/v2" import { createOpencodeClient } from "@opencode-ai/sdk/v2"
import { validateSession } from "../../src/cli/tui/validate-session" import { validateSession } from "../../src/cli/tui/validate-session"
import { InstanceBootstrap } from "../../src/project/bootstrap-service" import { InstanceBootstrap } from "../../src/project/bootstrap"
import { InstanceStore } from "../../src/project/instance-store" import { InstanceStore } from "../../src/project/instance-store"
import { MessageID, PartID, SessionID } from "../../src/session/schema" import { MessageID, PartID, SessionID } from "../../src/session/schema"
import { MessageV2 } from "../../src/session/message-v2" import { MessageV2 } from "../../src/session/message-v2"
@ -30,15 +31,13 @@ import { ModelV2 } from "@opencode-ai/core/model"
import { Database } from "@opencode-ai/core/database/database" import { Database } from "@opencode-ai/core/database/database"
import { httpApiLayer } from "./httpapi-layer" import { httpApiLayer } from "./httpapi-layer"
const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void })) const noopBootstrapLayer = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void }))
const appLayer = AppNodeBuilder.build(
LayerNode.group([FSUtil.node, CrossSpawnSpawner.node, InstanceStore.node, Database.node, SessionNs.node]),
[[InstanceStore.bootstrapNode, noopBootstrapLayer]],
)
const it = testEffect( const it = testEffect(
Layer.mergeAll( Layer.mergeAll(appLayer, httpApiLayer),
FSUtil.defaultLayer,
CrossSpawnSpawner.defaultLayer,
LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, noopBootstrap]]),
Database.defaultLayer,
httpApiLayer,
),
) )
const original = { const original = {
@ -56,6 +55,7 @@ type TestServices =
| FSUtil.Service | FSUtil.Service
| ChildProcessSpawner.ChildProcessSpawner | ChildProcessSpawner.ChildProcessSpawner
| InstanceStore.Service | InstanceStore.Service
| SessionNs.Service
| HttpServer.HttpServer | HttpServer.HttpServer
type TestScope = Scope.Scope | TestServices type TestScope = Scope.Scope | TestServices
@ -324,7 +324,7 @@ function seedMessage(directory: string, sessionID: string) {
}) })
return { message, part } return { message, part }
}), }),
).pipe(Effect.provide(SessionNs.defaultLayer)), ),
), ),
) )
} }

View file

@ -7,15 +7,15 @@ import path from "node:path"
import { Cause, Config, Effect, Exit, Layer } from "effect" import { Cause, Config, Effect, Exit, Layer } from "effect"
import { HttpClient, HttpClientRequest, HttpClientResponse, HttpRouter, HttpServer } from "effect/unstable/http" import { HttpClient, HttpClientRequest, HttpClientResponse, HttpRouter, HttpServer } from "effect/unstable/http"
import { layerWebSocketConstructorGlobal } from "effect/unstable/socket/Socket" import { layerWebSocketConstructorGlobal } from "effect/unstable/socket/Socket"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { Flag } from "@opencode-ai/core/flag/flag" import { Flag } from "@opencode-ai/core/flag/flag"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { Ripgrep } from "@opencode-ai/core/ripgrep" import { Ripgrep } from "@opencode-ai/core/ripgrep"
import { registerAdapter } from "../../src/control-plane/adapters" import { registerAdapter } from "../../src/control-plane/adapters"
import type { WorkspaceAdapter } from "../../src/control-plane/types" import type { WorkspaceAdapter } from "../../src/control-plane/types"
import { Workspace } from "../../src/control-plane/workspace" import { Workspace } from "../../src/control-plane/workspace"
import { InstanceBootstrap } from "../../src/project/bootstrap"
import { InstanceBootstrap as InstanceBootstrapService } from "../../src/project/bootstrap-service" import { InstanceBootstrap as InstanceBootstrapService } from "../../src/project/bootstrap-service"
import { InstanceStore } from "../../src/project/instance-store" import { InstanceStore } from "../../src/project/instance-store"
import { Project } from "../../src/project/project" import { Project } from "../../src/project/project"
@ -38,15 +38,14 @@ import { testProviderConfig } from "../lib/test-provider"
import { pollWithTimeout, testEffect } from "../lib/effect" import { pollWithTimeout, testEffect } from "../lib/effect"
const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
const workspaceLayer = Workspace.defaultLayer.pipe( const noopBootstrapLayer = Layer.succeed(
Layer.provide(LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, InstanceBootstrap.node]])), InstanceBootstrapService.Service,
InstanceBootstrapService.Service.of({ run: Effect.void }),
)
const appLayer = AppNodeBuilder.build(
LayerNode.group([InstanceStore.node, Project.node, Session.node, Workspace.node, Database.node, Ripgrep.node]),
[[InstanceStore.bootstrapNode, noopBootstrapLayer]],
) )
const instanceStoreLayer = LayerNode.compile(InstanceStore.node, [
[
InstanceStore.bootstrapNode,
Layer.succeed(InstanceBootstrapService.Service, InstanceBootstrapService.Service.of({ run: Effect.void })),
],
])
const servedRoutes: Layer.Layer<never, Config.ConfigError, HttpServer.HttpServer> = HttpRouter.serve( const servedRoutes: Layer.Layer<never, Config.ConfigError, HttpServer.HttpServer> = HttpRouter.serve(
HttpApiApp.routes, HttpApiApp.routes,
{ {
@ -60,14 +59,7 @@ const httpApiLayer = servedRoutes.pipe(
Layer.provideMerge(NodeServices.layer), Layer.provideMerge(NodeServices.layer),
) )
const it = testEffect( const it = testEffect(
Layer.mergeAll( Layer.mergeAll(appLayer, httpApiLayer),
instanceStoreLayer,
Project.defaultLayer,
Session.defaultLayer,
workspaceLayer,
Database.defaultLayer,
httpApiLayer,
).pipe(Layer.provide(Ripgrep.defaultLayer)),
) )
function pathFor(path: string, params: Record<string, string>) { function pathFor(path: string, params: Record<string, string>) {
@ -433,7 +425,7 @@ describe("session HttpApi", () => {
cwd: sessionDirectory, cwd: sessionDirectory,
root: sessionDirectory, root: sessionDirectory,
}) })
}).pipe(Effect.provide(TestLLMServer.layer), Effect.provide(CrossSpawnSpawner.defaultLayer)), }).pipe(Effect.provide(TestLLMServer.layer), Effect.provide(AppNodeBuilder.build(CrossSpawnSpawner.node))),
) )
it.instance( it.instance(
@ -869,7 +861,7 @@ describe("session HttpApi", () => {
pathSession: yield* createSession(), pathSession: yield* createSession(),
pathlessSession: yield* createSession(), pathlessSession: yield* createSession(),
} }
}).pipe(Effect.provideService(TestInstance, { directory: currentDir }), Effect.provide(Session.defaultLayer)), }).pipe(Effect.provideService(TestInstance, { directory: currentDir })),
) )
yield* clearSessionPath(pathlessSession.id) yield* clearSessionPath(pathlessSession.id)

View file

@ -1,7 +1,8 @@
import { createHash } from "node:crypto" import { createHash } from "node:crypto"
import { describe, expect } from "bun:test" import { describe, expect } from "bun:test"
import { Flag } from "@opencode-ai/core/flag/flag" import { Flag } from "@opencode-ai/core/flag/flag"
import { ConfigProvider, Effect, Layer } from "effect" import { ConfigProvider, Effect, Layer, Option } from "effect"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { import {
HttpClient, HttpClient,
HttpClientRequest, HttpClientRequest,
@ -39,7 +40,15 @@ const testStateLayer = Layer.effectDiscard(
}), }),
) )
const it = testEffect(Layer.mergeAll(testStateLayer, FSUtil.defaultLayer, RuntimeFlags.layer())) const fsUtilLayer = AppNodeBuilder.build(FSUtil.node)
const it = testEffect(Layer.mergeAll(testStateLayer, fsUtilLayer, RuntimeFlags.layer()))
function authConfigLayer(input?: { password?: string; username?: string }) {
return ServerAuth.Config.layer({
password: input?.password === undefined ? Option.none() : Option.some(input.password),
username: input?.username ?? "opencode",
})
}
function restoreEnv(key: string, value: string | undefined) { function restoreEnv(key: string, value: string | undefined) {
if (value === undefined) { if (value === undefined) {
@ -94,18 +103,12 @@ function uiApp(input?: {
) )
}), }),
).pipe( ).pipe(
Layer.provide(authorizationRouterMiddleware.layer.pipe(Layer.provide(ServerAuth.Config.defaultLayer))), Layer.provide(authorizationRouterMiddleware.layer.pipe(Layer.provide(authConfigLayer(input)))),
Layer.provide([ Layer.provide([
FSUtil.defaultLayer, fsUtilLayer,
input?.client ?? httpClient(new Response("ui")), input?.client ?? httpClient(new Response("ui")),
RuntimeFlags.layer({ disableEmbeddedWebUi: input?.disableEmbeddedWebUi ?? false }), RuntimeFlags.layer({ disableEmbeddedWebUi: input?.disableEmbeddedWebUi ?? false }),
HttpServer.layerServices, HttpServer.layerServices,
ConfigProvider.layer(
ConfigProvider.fromUnknown({
OPENCODE_SERVER_PASSWORD: input?.password,
OPENCODE_SERVER_USERNAME: input?.username,
}),
),
]), ]),
), ),
{ disableLogger: true }, { disableLogger: true },
@ -141,7 +144,7 @@ function routeOrderingApp() {
}), }),
).pipe( ).pipe(
Layer.provide([ Layer.provide([
FSUtil.defaultLayer, fsUtilLayer,
RuntimeFlags.layer({ disableEmbeddedWebUi: true }), RuntimeFlags.layer({ disableEmbeddedWebUi: true }),
httpClient(new Response("ui"), (request) => { httpClient(new Response("ui"), (request) => {
proxiedUrl = request.url proxiedUrl = request.url

View file

@ -21,7 +21,6 @@ import type { WorkspaceAdapter } from "../../src/control-plane/types"
import { Workspace } from "../../src/control-plane/workspace" import { Workspace } from "../../src/control-plane/workspace"
import { WorkspaceTable } from "@opencode-ai/core/control-plane/workspace.sql" import { WorkspaceTable } from "@opencode-ai/core/control-plane/workspace.sql"
import { Database } from "@opencode-ai/core/database/database" import { Database } from "@opencode-ai/core/database/database"
import { Ripgrep } from "@opencode-ai/core/ripgrep"
import { Project } from "../../src/project/project" import { Project } from "../../src/project/project"
import { Session } from "../../src/session/session" import { Session } from "../../src/session/session"
import { WorkspacePaths } from "../../src/server/routes/instance/httpapi/groups/workspace" import { WorkspacePaths } from "../../src/server/routes/instance/httpapi/groups/workspace"
@ -55,11 +54,9 @@ const it = testEffect(
testStateLayer, testStateLayer,
NodeHttpServer.layerTest, NodeHttpServer.layerTest,
NodeServices.layer, NodeServices.layer,
Database.defaultLayer,
Project.defaultLayer,
workspaceLayer, workspaceLayer,
Socket.layerWebSocketConstructorGlobal, Socket.layerWebSocketConstructorGlobal,
).pipe(Layer.provide(Ripgrep.defaultLayer)), ),
) )
type ProxiedRequest = { type ProxiedRequest = {

View file

@ -2,8 +2,9 @@ import { afterEach, describe, expect, mock } from "bun:test"
import { mkdir } from "node:fs/promises" import { mkdir } from "node:fs/promises"
import path from "node:path" import path from "node:path"
import { Effect, Layer, Stream } from "effect" import { Effect, Layer, Stream } from "effect"
import { Flag } from "@opencode-ai/core/flag/flag" import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node" import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { Flag } from "@opencode-ai/core/flag/flag"
import { registerAdapter } from "../../src/control-plane/adapters" import { registerAdapter } from "../../src/control-plane/adapters"
import { WorkspaceV2 } from "@opencode-ai/core/workspace" import { WorkspaceV2 } from "@opencode-ai/core/workspace"
import type { WorkspaceAdapter } from "../../src/control-plane/types" import type { WorkspaceAdapter } from "../../src/control-plane/types"
@ -24,18 +25,19 @@ import { testEffect } from "../lib/effect"
import { httpApiLayer, requestInDirectory } from "./httpapi-layer" import { httpApiLayer, requestInDirectory } from "./httpapi-layer"
const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
const workspaceLayer = Workspace.defaultLayer.pipe( const appLayer = AppNodeBuilder.build(
Layer.provide(LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, InstanceBootstrap.node]])), LayerNode.group([
Project.node,
Session.node,
Workspace.node,
InstanceStore.node,
Database.node,
Ripgrep.node,
]),
[[InstanceStore.bootstrapNode, InstanceBootstrap.node]],
) )
const it = testEffect( const it = testEffect(
Layer.mergeAll( Layer.mergeAll(appLayer, httpApiLayer),
Project.defaultLayer,
Session.defaultLayer,
workspaceLayer,
LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, InstanceBootstrap.node]]),
Database.defaultLayer,
httpApiLayer,
).pipe(Layer.provide(Ripgrep.defaultLayer)),
) )
function request(path: string, directory: string, init: RequestInit = {}) { function request(path: string, directory: string, init: RequestInit = {}) {

View file

@ -2,6 +2,7 @@ import { afterEach, describe, expect } from "bun:test"
import { $ } from "bun" import { $ } from "bun"
import fs from "fs/promises" import fs from "fs/promises"
import path from "path" import path from "path"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node" import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { Effect, Layer } from "effect" import { Effect, Layer } from "effect"
import { HttpClientResponse } from "effect/unstable/http" import { HttpClientResponse } from "effect/unstable/http"
@ -21,10 +22,10 @@ afterEach(async () => {
}) })
const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void })) const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void }))
const testInstanceStore = LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, noopBootstrap]]) const testInstanceStore = AppNodeBuilder.build(InstanceStore.node, [[InstanceStore.bootstrapNode, noopBootstrap]])
const it = testEffect( const it = testEffect(
Layer.mergeAll( Layer.mergeAll(
LayerNode.compile(LayerNode.group([FSUtil.node, Database.node, Snapshot.node])), AppNodeBuilder.build(LayerNode.group([FSUtil.node, Database.node, Snapshot.node])),
testInstanceStore, testInstanceStore,
httpApiLayer, httpApiLayer,
), ),

View file

@ -1,4 +1,5 @@
import { afterEach, describe, expect } from "bun:test" import { afterEach, describe, expect } from "bun:test"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node" import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { FSUtil } from "@opencode-ai/core/fs-util" import { FSUtil } from "@opencode-ai/core/fs-util"
import { Effect, Layer } from "effect" import { Effect, Layer } from "effect"
@ -20,10 +21,10 @@ afterEach(async () => {
}) })
const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void })) const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void }))
const testInstanceStore = LayerNode.compile(InstanceStore.node, [[InstanceStore.bootstrapNode, noopBootstrap]]) const testInstanceStore = AppNodeBuilder.build(InstanceStore.node, [[InstanceStore.bootstrapNode, noopBootstrap]])
const it = testEffect( const it = testEffect(
Layer.mergeAll(LayerNode.compile(LayerNode.group([FSUtil.node, Snapshot.node])), testInstanceStore, httpApiLayer), Layer.mergeAll(AppNodeBuilder.build(LayerNode.group([FSUtil.node, Snapshot.node])), testInstanceStore, httpApiLayer),
) )
function request(directory: string, url: string, init: RequestInit = {}) { function request(directory: string, url: string, init: RequestInit = {}) {

View file

@ -1,5 +1,7 @@
import { afterEach, describe, expect } from "bun:test" import { afterEach, describe, expect } from "bun:test"
import { Effect, Layer } from "effect" import { Effect } from "effect"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { Database } from "@opencode-ai/core/database/database" import { Database } from "@opencode-ai/core/database/database"
import { SessionProjector } from "@opencode-ai/core/session/projector" import { SessionProjector } from "@opencode-ai/core/session/projector"
import { Session as SessionNs } from "@/session/session" import { Session as SessionNs } from "@/session/session"
@ -9,23 +11,12 @@ import path from "path"
import { SessionTable } from "@opencode-ai/core/session/sql" import { SessionTable } from "@opencode-ai/core/session/sql"
import { eq } from "drizzle-orm" import { eq } from "drizzle-orm"
import { testEffect } from "../lib/effect" import { testEffect } from "../lib/effect"
import { EventV2Bridge } from "@/event-v2-bridge"
import { Storage } from "@/storage/storage"
import { RuntimeFlags } from "@/effect/runtime-flags" import { RuntimeFlags } from "@/effect/runtime-flags"
import { BackgroundJob } from "@/background/job"
const layer = (experimentalWorkspaces: boolean) => const layer = (experimentalWorkspaces: boolean) =>
Layer.mergeAll( AppNodeBuilder.build(
Database.defaultLayer, LayerNode.group([Database.node, SessionNs.node, SessionProjector.node]),
SessionNs.layer.pipe( [[RuntimeFlags.node, RuntimeFlags.layer({ experimentalWorkspaces })]],
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(Storage.defaultLayer),
Layer.provide(Database.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(SessionProjector.defaultLayer),
Layer.provide(RuntimeFlags.layer({ experimentalWorkspaces })),
Layer.provide(BackgroundJob.defaultLayer),
),
) )
const it = testEffect(layer(false)) const it = testEffect(layer(false))
const itWorkspaces = testEffect(layer(true)) const itWorkspaces = testEffect(layer(true))