test(opencode): simplify share layer wiring (#31811)

This commit is contained in:
James Long 2026-06-10 22:19:06 -04:00 committed by GitHub
commit 20bf18ffb4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,15 +1,14 @@
import { NodeFileSystem } from "@effect/platform-node"
import { beforeEach, describe, expect } from "bun:test" import { beforeEach, describe, expect } from "bun:test"
import { Effect, Exit, Layer, Option } from "effect" import { Effect, Exit, Layer, Option } from "effect"
import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http" import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { httpClient } from "@opencode-ai/core/effect/layer-node-platform"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { SessionProjector } from "@opencode-ai/core/session/projector"
import { AccessToken, AccountID, OrgID, RefreshToken } from "../../src/account/schema" import { AccessToken, AccountID, OrgID, RefreshToken } from "../../src/account/schema"
import { Account } from "../../src/account/account"
import { AccountRepo } from "../../src/account/repo" import { AccountRepo } from "../../src/account/repo"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { EventV2Bridge } from "../../src/event-v2-bridge" import { EventV2Bridge } from "../../src/event-v2-bridge"
import { Config } from "@/config/config"
import { Provider } from "@/provider/provider"
import { Session } from "@/session/session" import { Session } from "@/session/session"
import type { SessionID } from "../../src/session/schema" import type { SessionID } from "../../src/session/schema"
import { ShareNext } from "@/share/share-next" import { ShareNext } from "@/share/share-next"
@ -20,13 +19,7 @@ import { provideTmpdirInstance } from "../fixture/fixture"
import { resetDatabase } from "../fixture/db" import { resetDatabase } from "../fixture/db"
import { pollWithTimeout, testEffect } from "../lib/effect" import { pollWithTimeout, testEffect } from "../lib/effect"
const env = Layer.mergeAll( const env = LayerNode.buildLayer(CrossSpawnSpawner.node)
Session.defaultLayer,
AccountRepo.defaultLayer,
Database.defaultLayer,
NodeFileSystem.layer,
CrossSpawnSpawner.defaultLayer,
)
const it = testEffect(env) const it = testEffect(env)
const json = (req: Parameters<typeof HttpClientResponse.fromWeb>[0], body: unknown, status = 200) => const json = (req: Parameters<typeof HttpClientResponse.fromWeb>[0], body: unknown, status = 200) =>
@ -40,35 +33,28 @@ const json = (req: Parameters<typeof HttpClientResponse.fromWeb>[0], body: unkno
const none = HttpClient.make(() => Effect.die("unexpected http call")) const none = HttpClient.make(() => Effect.die("unexpected http call"))
function live(client: HttpClient.HttpClient) { function requestLayer(client: HttpClient.HttpClient) {
const http = Layer.succeed(HttpClient.HttpClient, client) return LayerNode.buildLayer(
return ShareNext.layer.pipe( LayerNode.group([ShareNext.node, AccountRepo.node]),
Layer.provide(EventV2Bridge.defaultLayer), {
Layer.provide(Account.layer.pipe(Layer.provide(AccountRepo.defaultLayer), Layer.provide(http))), replacements: [LayerNode.replace(httpClient, Layer.succeed(HttpClient.HttpClient, client))],
Layer.provide(Config.defaultLayer), },
Layer.provide(Database.defaultLayer),
Layer.provide(http),
Layer.provide(Provider.defaultLayer),
Layer.provide(Session.defaultLayer),
) )
} }
function wired(client: HttpClient.HttpClient) { function integrationLayer(client: HttpClient.HttpClient) {
const http = Layer.succeed(HttpClient.HttpClient, client) return LayerNode.buildLayer(
return Layer.mergeAll( LayerNode.group([
EventV2Bridge.defaultLayer, ShareNext.node,
ShareNext.layer, EventV2Bridge.node,
Session.defaultLayer, Session.node,
AccountRepo.defaultLayer, SessionProjector.node,
Database.defaultLayer, AccountRepo.node,
NodeFileSystem.layer, Database.node,
CrossSpawnSpawner.defaultLayer, ]),
).pipe( {
Layer.provide(EventV2Bridge.defaultLayer), replacements: [LayerNode.replace(httpClient, Layer.succeed(HttpClient.HttpClient, client))],
Layer.provide(Account.layer.pipe(Layer.provide(AccountRepo.defaultLayer), Layer.provide(http))), },
Layer.provide(Config.defaultLayer),
Layer.provide(http),
Layer.provide(Provider.defaultLayer),
) )
} }
@ -115,7 +101,7 @@ describe("ShareNext", () => {
expect(req.baseUrl).toBe("https://legacy-share.example.com") expect(req.baseUrl).toBe("https://legacy-share.example.com")
expect(req.headers).toEqual({}) expect(req.headers).toEqual({})
}), }),
).pipe(Effect.provide(live(none))), ).pipe(Effect.provide(requestLayer(none))),
{ config: { enterprise: { url: "https://legacy-share.example.com" } } }, { config: { enterprise: { url: "https://legacy-share.example.com" } } },
), ),
) )
@ -130,7 +116,7 @@ describe("ShareNext", () => {
expect(req.api.create).toBe("/api/share") expect(req.api.create).toBe("/api/share")
expect(req.headers).toEqual({}) expect(req.headers).toEqual({})
}), }),
).pipe(Effect.provide(live(none))), ).pipe(Effect.provide(requestLayer(none))),
), ),
) )
@ -139,7 +125,7 @@ describe("ShareNext", () => {
Effect.gen(function* () { Effect.gen(function* () {
yield* seed("https://control.example.com", "org-1") yield* seed("https://control.example.com", "org-1")
const req = yield* ShareNext.use.request().pipe(Effect.provide(live(none))) const req = yield* ShareNext.use.request()
expect(req.api.create).toBe("/api/shares") expect(req.api.create).toBe("/api/shares")
expect(req.api.sync("shr_123")).toBe("/api/shares/shr_123/sync") expect(req.api.sync("shr_123")).toBe("/api/shares/shr_123/sync")
@ -150,31 +136,31 @@ describe("ShareNext", () => {
authorization: "Bearer st_test_token", authorization: "Bearer st_test_token",
"x-org-id": "org-1", "x-org-id": "org-1",
}) })
}), }).pipe(Effect.provide(requestLayer(none))),
), ),
) )
it.live("create posts share, persists it, and returns the result", () => it.live("create posts share, persists it, and returns the result", () =>
provideTmpdirInstance( provideTmpdirInstance(
() => () => {
Effect.gen(function* () { const seen: HttpClientRequest.HttpClientRequest[] = []
const session = yield* Session.use.create({ title: "test" }) const client = HttpClient.make((req) => {
const seen: HttpClientRequest.HttpClientRequest[] = [] seen.push(req)
const client = HttpClient.make((req) => { if (req.url.endsWith("/api/share")) {
seen.push(req) return Effect.succeed(
if (req.url.endsWith("/api/share")) { json(req, {
return Effect.succeed( id: "shr_abc",
json(req, { url: "https://legacy-share.example.com/share/abc",
id: "shr_abc", secret: "sec_123",
url: "https://legacy-share.example.com/share/abc", }),
secret: "sec_123", )
}), }
) return Effect.succeed(json(req, { ok: true }))
} })
return Effect.succeed(json(req, { ok: true })) return Effect.gen(function* () {
}) const session = yield* (yield* Session.Service).create({ title: "test" })
const result = yield* ShareNext.use.create(session.id).pipe(Effect.provide(live(client))) const result = yield* (yield* ShareNext.Service).create(session.id)
expect(result.id).toBe("shr_abc") expect(result.id).toBe("shr_abc")
expect(result.url).toBe("https://legacy-share.example.com/share/abc") expect(result.url).toBe("https://legacy-share.example.com/share/abc")
@ -188,60 +174,59 @@ describe("ShareNext", () => {
expect(seen).toHaveLength(1) expect(seen).toHaveLength(1)
expect(seen[0].method).toBe("POST") expect(seen[0].method).toBe("POST")
expect(seen[0].url).toBe("https://legacy-share.example.com/api/share") expect(seen[0].url).toBe("https://legacy-share.example.com/api/share")
}), }).pipe(Effect.provide(integrationLayer(client)))
},
{ config: { enterprise: { url: "https://legacy-share.example.com" } } }, { config: { enterprise: { url: "https://legacy-share.example.com" } } },
), ),
) )
it.live("remove deletes the persisted share and calls the delete endpoint", () => it.live("remove deletes the persisted share and calls the delete endpoint", () =>
provideTmpdirInstance( provideTmpdirInstance(
() => () => {
Effect.gen(function* () { const seen: HttpClientRequest.HttpClientRequest[] = []
const session = yield* Session.use.create({ title: "test" }) const client = HttpClient.make((req) => {
const seen: HttpClientRequest.HttpClientRequest[] = [] seen.push(req)
const client = HttpClient.make((req) => { if (req.method === "POST") {
seen.push(req) return Effect.succeed(
if (req.method === "POST") { json(req, {
return Effect.succeed( id: "shr_abc",
json(req, { url: "https://legacy-share.example.com/share/abc",
id: "shr_abc", secret: "sec_123",
url: "https://legacy-share.example.com/share/abc", }),
secret: "sec_123", )
}), }
) return Effect.succeed(HttpClientResponse.fromWeb(req, new Response(null, { status: 200 })))
} })
return Effect.succeed(HttpClientResponse.fromWeb(req, new Response(null, { status: 200 }))) return Effect.gen(function* () {
}) const session = yield* (yield* Session.Service).create({ title: "test" })
const service = yield* ShareNext.Service
yield* Effect.gen(function* () { yield* service.create(session.id)
yield* ShareNext.use.create(session.id) yield* service.remove(session.id)
yield* ShareNext.use.remove(session.id)
}).pipe(Effect.provide(live(client)))
expect(yield* share(session.id)).toBeUndefined() expect(yield* share(session.id)).toBeUndefined()
expect(seen.map((req) => [req.method, req.url])).toEqual([ expect(seen.map((req) => [req.method, req.url])).toEqual([
["POST", "https://legacy-share.example.com/api/share"], ["POST", "https://legacy-share.example.com/api/share"],
["DELETE", "https://legacy-share.example.com/api/share/shr_abc"], ["DELETE", "https://legacy-share.example.com/api/share/shr_abc"],
]) ])
}), }).pipe(Effect.provide(integrationLayer(client)))
},
{ config: { enterprise: { url: "https://legacy-share.example.com" } } }, { config: { enterprise: { url: "https://legacy-share.example.com" } } },
), ),
) )
it.live("create fails on a non-ok response and does not persist a share", () => it.live("create fails on a non-ok response and does not persist a share", () =>
provideTmpdirInstance(() => provideTmpdirInstance(() => {
Effect.gen(function* () { const client = HttpClient.make((req) => Effect.succeed(json(req, { error: "bad" }, 500)))
const session = yield* Session.use.create({ title: "test" }) return Effect.gen(function* () {
const client = HttpClient.make((req) => Effect.succeed(json(req, { error: "bad" }, 500))) const session = yield* (yield* Session.Service).create({ title: "test" })
const exit = yield* ShareNext.Service.use((svc) => Effect.exit(svc.create(session.id))).pipe( const exit = yield* ShareNext.Service.use((svc) => Effect.exit(svc.create(session.id)))
Effect.provide(live(client)),
)
expect(Exit.isFailure(exit)).toBe(true) expect(Exit.isFailure(exit)).toBe(true)
expect(yield* share(session.id)).toBeUndefined() expect(yield* share(session.id)).toBeUndefined()
}), }).pipe(Effect.provide(integrationLayer(client)))
), }),
) )
it.live("ShareNext coalesces rapid diff events into one delayed sync with latest data", () => it.live("ShareNext coalesces rapid diff events into one delayed sync with latest data", () =>
@ -336,7 +321,7 @@ describe("ShareNext", () => {
status: "modified", status: "modified",
}, },
]) ])
}).pipe(Effect.provide(wired(client))) }).pipe(Effect.provide(integrationLayer(client)))
}, },
{ config: { enterprise: { url: "https://legacy-share.example.com" } } }, { config: { enterprise: { url: "https://legacy-share.example.com" } } },
), ),