| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- import { afterEach, describe, expect, mock } from "bun:test"
- import { LayerNode } from "@kirincode-ai/core/effect/layer-node"
- import { Context, Effect, Layer } from "effect"
- import { Flag } from "@kirincode-ai/core/flag/flag"
- import { SyncPaths } from "../../src/server/routes/instance/httpapi/groups/sync"
- import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server"
- import { Session } from "@/session/session"
- import { resetDatabase } from "../fixture/db"
- import { disposeAllInstances, TestInstance } from "../fixture/fixture"
- import { testEffect } from "../lib/effect"
- import { httpApiLayer, requestInDirectory } from "./httpapi-layer"
- const originalWorkspaces = Flag.KIRINCODE_EXPERIMENTAL_WORKSPACES
- const context = Context.empty() as Context.Context<unknown>
- const it = testEffect(Layer.mergeAll(LayerNode.compile(Session.node), httpApiLayer))
- afterEach(async () => {
- mock.restore()
- Flag.KIRINCODE_EXPERIMENTAL_WORKSPACES = originalWorkspaces
- await disposeAllInstances()
- await resetDatabase()
- })
- describe("sync HttpApi", () => {
- it.instance(
- "serves sync routes",
- () =>
- Effect.gen(function* () {
- Flag.KIRINCODE_EXPERIMENTAL_WORKSPACES = true
- const tmp = yield* TestInstance
- const headers = { "x-opencode-directory": tmp.directory, "content-type": "application/json" }
- const session = yield* Session.use.create({ title: "sync" })
- const started = yield* requestInDirectory(SyncPaths.start, tmp.directory, { method: "POST", headers })
- expect(started.status).toBe(200)
- expect(yield* started.json).toBe(true)
- const history = yield* requestInDirectory(SyncPaths.history, tmp.directory, {
- method: "POST",
- headers,
- body: JSON.stringify({}),
- })
- expect(history.status).toBe(200)
- const rows = (yield* history.json) as Array<{
- id: string
- aggregate_id: string
- seq: number
- type: string
- data: Record<string, unknown>
- }>
- expect(rows.map((row) => row.aggregate_id)).toContain(session.id)
- const replayed = yield* requestInDirectory(SyncPaths.replay, tmp.directory, {
- method: "POST",
- headers,
- body: JSON.stringify({
- directory: tmp.directory,
- events: rows
- .filter((row) => row.aggregate_id === session.id)
- .map((row) => ({
- id: row.id,
- aggregateID: row.aggregate_id,
- seq: row.seq,
- type: row.type,
- data: row.data,
- })),
- }),
- })
- expect(replayed.status).toBe(200)
- expect(yield* replayed.json).toEqual({ sessionID: session.id })
- }),
- { git: true, config: { formatter: false, lsp: false } },
- )
- it.instance(
- "validates seq values",
- () =>
- Effect.gen(function* () {
- const tmp = yield* TestInstance
- const headers = { "x-opencode-directory": tmp.directory, "content-type": "application/json" }
- const cases = [
- {
- path: SyncPaths.history,
- body: { aggregate: -1 },
- },
- {
- path: SyncPaths.history,
- body: { aggregate: 1.5 },
- },
- {
- path: SyncPaths.replay,
- body: {
- directory: tmp.directory,
- events: [{ id: "event", aggregateID: "session", seq: -1, type: "session.created", data: {} }],
- },
- },
- {
- path: SyncPaths.replay,
- body: {
- directory: tmp.directory,
- events: [{ id: "event", aggregateID: "session", seq: 1.5, type: "session.created", data: {} }],
- },
- },
- {
- path: SyncPaths.replay,
- body: {
- directory: tmp.directory,
- events: [{ id: "event", aggregateID: "session", seq: 0, type: "session.created", data: {} }],
- },
- },
- ]
- for (const item of cases) {
- const response = yield* requestInDirectory(item.path, tmp.directory, {
- method: "POST",
- headers,
- body: JSON.stringify(item.body),
- })
- expect(response.status).toBe(400)
- }
- }),
- { git: true, config: { formatter: false, lsp: false } },
- )
- it.instance.skip(
- "returns structured validation errors",
- () =>
- Effect.gen(function* () {
- const tmp = yield* TestInstance
- const response = yield* Effect.promise(() =>
- HttpApiApp.webHandler().handler(
- new Request(`http://localhost${SyncPaths.history}`, {
- method: "POST",
- headers: { "x-opencode-directory": tmp.directory, "content-type": "application/json" },
- body: JSON.stringify({ aggregate: -1 }),
- }),
- context,
- ),
- )
- expect(response.status).toBe(400)
- expect(response.headers.get("content-type") ?? "").toContain("application/json")
- const body = (yield* Effect.promise(() => response.json())) as Record<string, unknown>
- expect(body.success).toBe(false)
- expect(Array.isArray(body.error) || Array.isArray(body.errors)).toBe(true)
- }),
- { git: true, config: { formatter: false, lsp: false } },
- )
- })
|