/
Alex_Ural
/
opencode
Обзор
Документация
Войти
/
Alex_Ural
/
opencode
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
packages/core/test/effect/layer-node/node-build.test.ts
125 строк
5 KB
James Long
refactor(core): refine layer node replacements (#34377)
29 июн 2026, 06:48
Не верифицирован
29 июн 2026, 06:48
84336e4
Код
Авторство
О чём код?
import { describe, expect, test } from "bun:test" import { Context, Effect, Layer, LayerMap, Option } from "effect" import { Node } from "@opencode-ai/core/effect/app-node" import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" import { LayerNode } from "@opencode-ai/core/effect/layer-node" import { Location } from "@opencode-ai/core/location" import { LocationServiceMap } from "@opencode-ai/core/location-service-map" import type { LocationError, LocationServices } from "@opencode-ai/core/location-services" import { Project } from "@opencode-ai/core/project" import { AbsolutePath } from "@opencode-ai/core/schema" import { tmpdir } from "../../fixture/tmpdir" class Value extends Context.Service<Value, { readonly value: string }>()("test/TagValue") {} class Result extends Context.Service<Result, { readonly value: string }>()("test/TagResult") {} class CycleA extends Context.Service<CycleA, {}>()("test/NodeBuildA") {} class CycleB extends Context.Service<CycleB, { readonly directory: AbsolutePath }>()("test/NodeBuildB") {} describe("node build", () => { test("does not build a location service map when the graph does not require it", async () => { const result = Node.makeGlobalNode({ service: Result, layer: Layer.succeed(Result, Result.of({ value: "plain" })), deps: [], }) const layer = AppNodeBuilder.build(result) const program = Effect.gen(function* () { expect(Option.isNone(yield* Effect.serviceOption(LocationServiceMap.Service))).toBe(true) return (yield* Result).value }).pipe(Effect.provide(layer)) expect(await Effect.runPromise(program)).toBe("plain") }) test("detects cycles through a replaced location service map", async () => { const a = Node.makeGlobalNode({ service: CycleA, layer: Layer.effect(CycleA, Effect.as(LocationServiceMap.Service, CycleA.of({}))), deps: [LocationServiceMap.node], }) const b = Node.makeGlobalNode({ service: CycleB, layer: Layer.effect( CycleB, Effect.map(CycleA, () => CycleB.of({ directory: AbsolutePath.make(process.cwd()) })), ), deps: [a], }) const mapLayer = Layer.effect( LocationServiceMap.Service, Effect.gen(function* () { const service = yield* CycleB return yield* LayerMap.make( (ref: Location.Ref) => Layer.succeed( Location.Service, Location.Service.of({ directory: ref.directory, workspaceID: ref.workspaceID, project: { id: Project.ID.global, directory: service.directory }, }), ), { idleTimeToLive: "1 minute" }, ) }) as unknown as Effect.Effect<LayerMap.LayerMap<Location.Ref, LocationServices, LocationError>, never, CycleB>, ) const map = Node.makeGlobalNode({ service: LocationServiceMap.Service, layer: mapLayer, deps: [b] }) expect(() => AppNodeBuilder.build(LayerNode.group([a]), [[LocationServiceMap.node, map]])).toThrow( "Cycle detected in layer tree", ) }) test("shares top-level project with location services", async () => { await using tmp = await tmpdir() let acquisitions = 0 const projectLayer = Layer.effect( Project.Service, Effect.sync(() => { acquisitions++ return Project.Service.of({ directories: () => Effect.succeed([]), resolve: (directory) => Effect.succeed({ id: Project.ID.global, directory }), commit: () => Effect.void, }) }), ) const ref = Location.Ref.make({ directory: AbsolutePath.make(tmp.path) }) const layer = AppNodeBuilder.build(LayerNode.group([Project.node, LocationServiceMap.node]), [ [Project.node, projectLayer], ]) const program = Effect.gen(function* () { yield* Project.Service const locations = yield* LocationServiceMap.Service expect(Option.isSome(yield* Effect.serviceOption(LocationServiceMap.Service))).toBe(true) return yield* Location.Service.pipe(Effect.provide(locations.get(ref))) }).pipe(Effect.provide(layer)) expect((await Effect.runPromise(program)).directory).toBe(ref.directory) expect(acquisitions).toBe(1) }) test("returns a composed application layer", async () => { const value = Node.makeGlobalNode({ service: Value, layer: Layer.succeed(Value, Value.of({ value: "value" })), deps: [], }) const result = Node.makeGlobalNode({ service: Result, layer: Layer.effect( Result, Effect.gen(function* () { return Result.of({ value: (yield* Value).value }) }), ), deps: [value], }) const serviceLayer = AppNodeBuilder.build(result) const program = Effect.gen(function* () { expect(Option.isNone(yield* Effect.serviceOption(LocationServiceMap.Service))).toBe(true) return (yield* Result).value }).pipe(Effect.provide(serviceLayer)) expect(await Effect.runPromise(program)).toBe("value") }) })