mirror of
https://github.com/anomalyco/opencode.git
synced 2026-06-01 19:05:38 +00:00
test(server): migrate global session list to effect runner (#27233)
This commit is contained in:
@@ -1,104 +1,104 @@
|
|||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect } from "bun:test"
|
||||||
import { Effect } from "effect"
|
import { Deferred, Effect, Layer } from "effect"
|
||||||
import { WithInstance } from "../../src/project/with-instance"
|
|
||||||
import { Project } from "@/project/project"
|
import { Project } from "@/project/project"
|
||||||
import { Session as SessionNs } from "@/session/session"
|
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 * 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 })
|
void Log.init({ print: false })
|
||||||
|
|
||||||
function run<A, E>(fx: Effect.Effect<A, E, SessionNs.Service>) {
|
const it = testEffect(Layer.mergeAll(SessionNs.defaultLayer, Project.defaultLayer, CrossSpawnSpawner.defaultLayer))
|
||||||
return Effect.runPromise(fx.pipe(Effect.provide(SessionNs.defaultLayer)))
|
|
||||||
}
|
|
||||||
|
|
||||||
const svc = {
|
const withSession = (input?: Parameters<SessionNs.Interface["create"]>[0]) =>
|
||||||
...SessionNs,
|
Effect.acquireRelease(
|
||||||
create(input?: SessionNs.CreateInput) {
|
SessionNs.Service.use((session) => session.create(input)),
|
||||||
return run(SessionNs.Service.use((svc) => svc.create(input)))
|
(created) => SessionNs.Service.use((session) => session.remove(created.id).pipe(Effect.ignore)),
|
||||||
},
|
)
|
||||||
setArchived(input: typeof SessionNs.SetArchivedInput.Type) {
|
|
||||||
return run(SessionNs.Service.use((svc) => svc.setArchived(input)))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("session.listGlobal", () => {
|
describe("session.listGlobal", () => {
|
||||||
test("lists sessions across projects with project metadata", async () => {
|
it.instance(
|
||||||
await using first = await tmpdir({ git: true })
|
"lists sessions across projects with project metadata",
|
||||||
await using second = await tmpdir({ git: true })
|
() =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const first = yield* TestInstance
|
||||||
|
const second = yield* tmpdirScoped({ git: true })
|
||||||
|
|
||||||
const firstSession = await WithInstance.provide({
|
const firstSession = yield* withSession({ title: "first-session" })
|
||||||
directory: first.path,
|
const secondSession = yield* withSession({ title: "second-session" }).pipe(provideInstance(second))
|
||||||
fn: async () => svc.create({ title: "first-session" }),
|
|
||||||
})
|
|
||||||
const secondSession = await WithInstance.provide({
|
|
||||||
directory: second.path,
|
|
||||||
fn: async () => svc.create({ title: "second-session" }),
|
|
||||||
})
|
|
||||||
|
|
||||||
const sessions = [...svc.listGlobal({ limit: 200 })]
|
const sessions = yield* Effect.sync(() => [...SessionNs.listGlobal({ limit: 200 })])
|
||||||
const ids = sessions.map((session) => session.id)
|
const ids = sessions.map((session) => session.id)
|
||||||
|
|
||||||
expect(ids).toContain(firstSession.id)
|
expect(ids).toContain(firstSession.id)
|
||||||
expect(ids).toContain(secondSession.id)
|
expect(ids).toContain(secondSession.id)
|
||||||
|
|
||||||
const firstProject = Project.get(firstSession.projectID)
|
const firstProject = yield* Project.Service.use((project) => project.get(firstSession.projectID))
|
||||||
const secondProject = Project.get(secondSession.projectID)
|
const secondProject = yield* Project.Service.use((project) => project.get(secondSession.projectID))
|
||||||
|
|
||||||
const firstItem = sessions.find((session) => session.id === firstSession.id)
|
const firstItem = sessions.find((session) => session.id === firstSession.id)
|
||||||
const secondItem = sessions.find((session) => session.id === secondSession.id)
|
const secondItem = sessions.find((session) => session.id === secondSession.id)
|
||||||
|
|
||||||
expect(firstItem?.project?.id).toBe(firstProject?.id)
|
expect(firstItem?.project?.id).toBe(firstProject?.id)
|
||||||
expect(firstItem?.project?.worktree).toBe(firstProject?.worktree)
|
expect(firstItem?.project?.worktree).toBe(firstProject?.worktree)
|
||||||
expect(secondItem?.project?.id).toBe(secondProject?.id)
|
expect(secondItem?.project?.id).toBe(secondProject?.id)
|
||||||
expect(secondItem?.project?.worktree).toBe(secondProject?.worktree)
|
expect(secondItem?.project?.worktree).toBe(secondProject?.worktree)
|
||||||
})
|
expect(first.directory).not.toBe(second)
|
||||||
|
}),
|
||||||
|
{ git: true },
|
||||||
|
)
|
||||||
|
|
||||||
test("excludes archived sessions by default", async () => {
|
it.instance(
|
||||||
await using tmp = await tmpdir({ git: true })
|
"excludes archived sessions by default",
|
||||||
|
() =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const archived = yield* withSession({ title: "archived-session" })
|
||||||
|
|
||||||
const archived = await WithInstance.provide({
|
yield* SessionNs.Service.use((session) => session.setArchived({ sessionID: archived.id, time: Date.now() }))
|
||||||
directory: tmp.path,
|
|
||||||
fn: async () => svc.create({ title: "archived-session" }),
|
|
||||||
})
|
|
||||||
|
|
||||||
await WithInstance.provide({
|
const sessions = yield* Effect.sync(() => [...SessionNs.listGlobal({ limit: 200 })])
|
||||||
directory: tmp.path,
|
const ids = sessions.map((session) => session.id)
|
||||||
fn: async () => svc.setArchived({ sessionID: archived.id, time: Date.now() }),
|
|
||||||
})
|
|
||||||
|
|
||||||
const sessions = [...svc.listGlobal({ limit: 200 })]
|
expect(ids).not.toContain(archived.id)
|
||||||
const ids = sessions.map((session) => session.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 })]
|
expect(allIds).toContain(archived.id)
|
||||||
const allIds = allSessions.map((session) => session.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 () => {
|
const first = yield* withSession({ title: "page-one" })
|
||||||
await using tmp = await tmpdir({ git: true })
|
const ready = yield* Deferred.make<void>()
|
||||||
|
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({
|
const page = yield* Effect.sync(() => [...SessionNs.listGlobal({ directory: test.directory, limit: 1 })])
|
||||||
directory: tmp.path,
|
expect(page.length).toBe(1)
|
||||||
fn: async () => svc.create({ title: "page-one" }),
|
expect(page[0].id).toBe(second.id)
|
||||||
})
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 5))
|
|
||||||
const second = await WithInstance.provide({
|
|
||||||
directory: tmp.path,
|
|
||||||
fn: async () => svc.create({ title: "page-two" }),
|
|
||||||
})
|
|
||||||
|
|
||||||
const page = [...svc.listGlobal({ directory: tmp.path, limit: 1 })]
|
const next = yield* Effect.sync(() => [
|
||||||
expect(page.length).toBe(1)
|
...SessionNs.listGlobal({ directory: test.directory, limit: 10, cursor: page[0].time.updated }),
|
||||||
expect(page[0].id).toBe(second.id)
|
])
|
||||||
|
const ids = next.map((session) => session.id)
|
||||||
|
|
||||||
const next = [...svc.listGlobal({ directory: tmp.path, limit: 10, cursor: page[0].time.updated })]
|
expect(ids).toContain(first.id)
|
||||||
const ids = next.map((session) => session.id)
|
expect(ids).not.toContain(second.id)
|
||||||
|
}),
|
||||||
expect(ids).toContain(first.id)
|
{ git: true },
|
||||||
expect(ids).not.toContain(second.id)
|
)
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user