global-session-list.test.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import { describe, expect } from "bun:test"
  2. import { LayerNode } from "@kirincode-ai/core/effect/layer-node"
  3. import { SessionProjector } from "@kirincode-ai/core/session/projector"
  4. import { Deferred, Effect, Layer } from "effect"
  5. import { Project } from "@/project/project"
  6. import { Session as SessionNs } from "@/session/session"
  7. import { CrossSpawnSpawner } from "@kirincode-ai/core/cross-spawn-spawner"
  8. import { provideInstance, TestInstance, tmpdirScoped } from "../fixture/fixture"
  9. import { testEffect } from "../lib/effect"
  10. const it = testEffect(
  11. LayerNode.compile(LayerNode.group([SessionNs.node, SessionProjector.node, Project.node, CrossSpawnSpawner.node])),
  12. )
  13. const withSession = (input?: Parameters<SessionNs.Interface["create"]>[0]) =>
  14. Effect.acquireRelease(SessionNs.use.create(input), (created) =>
  15. SessionNs.Service.use((session) => session.remove(created.id).pipe(Effect.ignore)),
  16. )
  17. describe("session.listGlobal", () => {
  18. it.instance(
  19. "lists sessions across projects with project metadata",
  20. () =>
  21. Effect.gen(function* () {
  22. const first = yield* TestInstance
  23. const second = yield* tmpdirScoped({ git: true })
  24. const firstSession = yield* withSession({ title: "first-session" })
  25. const secondSession = yield* withSession({ title: "second-session" }).pipe(provideInstance(second))
  26. const sessions = yield* SessionNs.Service.use((session) => session.listGlobal({ limit: 200 }))
  27. const ids = sessions.map((session) => session.id)
  28. expect(ids).toContain(firstSession.id)
  29. expect(ids).toContain(secondSession.id)
  30. const firstProject = yield* Project.use.get(firstSession.projectID)
  31. const secondProject = yield* Project.use.get(secondSession.projectID)
  32. const firstItem = sessions.find((session) => session.id === firstSession.id)
  33. const secondItem = sessions.find((session) => session.id === secondSession.id)
  34. expect(firstItem?.project?.id).toBe(firstProject?.id)
  35. expect(firstItem?.project?.worktree).toBe(firstProject?.worktree)
  36. expect(secondItem?.project?.id).toBe(secondProject?.id)
  37. expect(secondItem?.project?.worktree).toBe(secondProject?.worktree)
  38. expect(first.directory).not.toBe(second)
  39. }),
  40. { git: true },
  41. )
  42. it.instance(
  43. "excludes archived sessions by default",
  44. () =>
  45. Effect.gen(function* () {
  46. const archived = yield* withSession({ title: "archived-session" })
  47. yield* SessionNs.Service.use((session) => session.setArchived({ sessionID: archived.id, time: Date.now() }))
  48. const sessions = yield* SessionNs.Service.use((session) => session.listGlobal({ limit: 200 }))
  49. const ids = sessions.map((session) => session.id)
  50. expect(ids).not.toContain(archived.id)
  51. const allSessions = yield* SessionNs.Service.use((session) =>
  52. session.listGlobal({ limit: 200, archived: true }),
  53. )
  54. const allIds = allSessions.map((session) => session.id)
  55. expect(allIds).toContain(archived.id)
  56. }),
  57. { git: true },
  58. )
  59. it.instance(
  60. "supports cursor pagination",
  61. () =>
  62. Effect.gen(function* () {
  63. const test = yield* TestInstance
  64. const first = yield* withSession({ title: "page-one" })
  65. const ready = yield* Deferred.make<void>()
  66. yield* Deferred.succeed(ready, undefined).pipe(Effect.delay("5 millis"), Effect.forkScoped)
  67. yield* Deferred.await(ready).pipe(
  68. Effect.timeoutOrElse({
  69. duration: "1 second",
  70. orElse: () => Effect.fail(new Error("timed out waiting between session creates")),
  71. }),
  72. )
  73. const second = yield* withSession({ title: "page-two" })
  74. const page = yield* SessionNs.Service.use((session) =>
  75. session.listGlobal({ directory: test.directory, limit: 1 }),
  76. )
  77. expect(page.length).toBe(1)
  78. expect(page[0].id).toBe(second.id)
  79. const next = yield* SessionNs.Service.use((session) =>
  80. session.listGlobal({ directory: test.directory, limit: 10, cursor: page[0].time.updated }),
  81. )
  82. const ids = next.map((session) => session.id)
  83. expect(ids).toContain(first.id)
  84. expect(ids).not.toContain(second.id)
  85. }),
  86. { git: true },
  87. )
  88. })