From 485ecbde18f5b164273cf086057a062be9efe3e6 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 12 May 2026 22:52:59 -0400 Subject: [PATCH] test(server): migrate global session list to effect runner (#27233) --- .../test/server/global-session-list.test.ts | 154 +++++++++--------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/packages/opencode/test/server/global-session-list.test.ts b/packages/opencode/test/server/global-session-list.test.ts index 04348e5c0d..0fdba1f663 100644 --- a/packages/opencode/test/server/global-session-list.test.ts +++ b/packages/opencode/test/server/global-session-list.test.ts @@ -1,104 +1,104 @@ -import { describe, expect, test } from "bun:test" -import { Effect } from "effect" -import { WithInstance } from "../../src/project/with-instance" +import { describe, expect } from "bun:test" +import { Deferred, Effect, Layer } from "effect" import { Project } from "@/project/project" import { Session as SessionNs } from "@/session/session" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import * as Log from "@opencode-ai/core/util/log" -import { tmpdir } from "../fixture/fixture" +import { provideInstance, TestInstance, tmpdirScoped } from "../fixture/fixture" +import { testEffect } from "../lib/effect" void Log.init({ print: false }) -function run(fx: Effect.Effect) { - return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer))) -} +const it = testEffect(Layer.mergeAll(SessionNs.defaultLayer, Project.defaultLayer, CrossSpawnSpawner.defaultLayer)) -const svc = { - ...SessionNs, - create(input?: SessionNs.CreateInput) { - return run(SessionNs.Service.use((svc) => svc.create(input))) - }, - setArchived(input: typeof SessionNs.SetArchivedInput.Type) { - return run(SessionNs.Service.use((svc) => svc.setArchived(input))) - }, -} +const withSession = (input?: Parameters[0]) => + Effect.acquireRelease( + SessionNs.Service.use((session) => session.create(input)), + (created) => SessionNs.Service.use((session) => session.remove(created.id).pipe(Effect.ignore)), + ) describe("session.listGlobal", () => { - test("lists sessions across projects with project metadata", async () => { - await using first = await tmpdir({ git: true }) - await using second = await tmpdir({ git: true }) + it.instance( + "lists sessions across projects with project metadata", + () => + Effect.gen(function* () { + const first = yield* TestInstance + const second = yield* tmpdirScoped({ git: true }) - const firstSession = await WithInstance.provide({ - directory: first.path, - fn: async () => svc.create({ title: "first-session" }), - }) - const secondSession = await WithInstance.provide({ - directory: second.path, - fn: async () => svc.create({ title: "second-session" }), - }) + const firstSession = yield* withSession({ title: "first-session" }) + const secondSession = yield* withSession({ title: "second-session" }).pipe(provideInstance(second)) - const sessions = [...svc.listGlobal({ limit: 200 })] - const ids = sessions.map((session) => session.id) + const sessions = yield* Effect.sync(() => [...SessionNs.listGlobal({ limit: 200 })]) + const ids = sessions.map((session) => session.id) - expect(ids).toContain(firstSession.id) - expect(ids).toContain(secondSession.id) + expect(ids).toContain(firstSession.id) + expect(ids).toContain(secondSession.id) - const firstProject = Project.get(firstSession.projectID) - const secondProject = Project.get(secondSession.projectID) + const firstProject = yield* Project.Service.use((project) => project.get(firstSession.projectID)) + const secondProject = yield* Project.Service.use((project) => project.get(secondSession.projectID)) - const firstItem = sessions.find((session) => session.id === firstSession.id) - const secondItem = sessions.find((session) => session.id === secondSession.id) + const firstItem = sessions.find((session) => session.id === firstSession.id) + const secondItem = sessions.find((session) => session.id === secondSession.id) - expect(firstItem?.project?.id).toBe(firstProject?.id) - expect(firstItem?.project?.worktree).toBe(firstProject?.worktree) - expect(secondItem?.project?.id).toBe(secondProject?.id) - expect(secondItem?.project?.worktree).toBe(secondProject?.worktree) - }) + expect(firstItem?.project?.id).toBe(firstProject?.id) + expect(firstItem?.project?.worktree).toBe(firstProject?.worktree) + expect(secondItem?.project?.id).toBe(secondProject?.id) + expect(secondItem?.project?.worktree).toBe(secondProject?.worktree) + expect(first.directory).not.toBe(second) + }), + { git: true }, + ) - test("excludes archived sessions by default", async () => { - await using tmp = await tmpdir({ git: true }) + it.instance( + "excludes archived sessions by default", + () => + Effect.gen(function* () { + const archived = yield* withSession({ title: "archived-session" }) - const archived = await WithInstance.provide({ - directory: tmp.path, - fn: async () => svc.create({ title: "archived-session" }), - }) + yield* SessionNs.Service.use((session) => session.setArchived({ sessionID: archived.id, time: Date.now() })) - await WithInstance.provide({ - directory: tmp.path, - fn: async () => svc.setArchived({ sessionID: archived.id, time: Date.now() }), - }) + const sessions = yield* Effect.sync(() => [...SessionNs.listGlobal({ limit: 200 })]) + const ids = sessions.map((session) => session.id) - const sessions = [...svc.listGlobal({ limit: 200 })] - const ids = sessions.map((session) => session.id) + expect(ids).not.toContain(archived.id) - expect(ids).not.toContain(archived.id) + const allSessions = yield* Effect.sync(() => [...SessionNs.listGlobal({ limit: 200, archived: true })]) + const allIds = allSessions.map((session) => session.id) - const allSessions = [...svc.listGlobal({ limit: 200, archived: true })] - const allIds = allSessions.map((session) => session.id) + expect(allIds).toContain(archived.id) + }), + { git: true }, + ) - expect(allIds).toContain(archived.id) - }) + it.instance( + "supports cursor pagination", + () => + Effect.gen(function* () { + const test = yield* TestInstance - test("supports cursor pagination", async () => { - await using tmp = await tmpdir({ git: true }) + const first = yield* withSession({ title: "page-one" }) + const ready = yield* Deferred.make() + yield* Deferred.succeed(ready, undefined).pipe(Effect.delay("5 millis"), Effect.forkScoped) + yield* Deferred.await(ready).pipe( + Effect.timeoutOrElse({ + duration: "1 second", + orElse: () => Effect.fail(new Error("timed out waiting between session creates")), + }), + ) + const second = yield* withSession({ title: "page-two" }) - const first = await WithInstance.provide({ - directory: tmp.path, - fn: async () => svc.create({ title: "page-one" }), - }) - await new Promise((resolve) => setTimeout(resolve, 5)) - const second = await WithInstance.provide({ - directory: tmp.path, - fn: async () => svc.create({ title: "page-two" }), - }) + const page = yield* Effect.sync(() => [...SessionNs.listGlobal({ directory: test.directory, limit: 1 })]) + expect(page.length).toBe(1) + expect(page[0].id).toBe(second.id) - const page = [...svc.listGlobal({ directory: tmp.path, limit: 1 })] - expect(page.length).toBe(1) - expect(page[0].id).toBe(second.id) + const next = yield* Effect.sync(() => [ + ...SessionNs.listGlobal({ directory: test.directory, limit: 10, cursor: page[0].time.updated }), + ]) + const ids = next.map((session) => session.id) - const next = [...svc.listGlobal({ directory: tmp.path, limit: 10, cursor: page[0].time.updated })] - const ids = next.map((session) => session.id) - - expect(ids).toContain(first.id) - expect(ids).not.toContain(second.id) - }) + expect(ids).toContain(first.id) + expect(ids).not.toContain(second.id) + }), + { git: true }, + ) })