mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-08 22:43:18 +00:00
210 lines
6.5 KiB
TypeScript
210 lines
6.5 KiB
TypeScript
import { expect, test } from "bun:test"
|
|
import fs from "node:fs/promises"
|
|
import os from "node:os"
|
|
import path from "node:path"
|
|
import { OPENCODE_VERSION } from "../src/version"
|
|
import { writeExport } from "../src/commands/handlers/export"
|
|
|
|
const info = {
|
|
id: "ses_export_test",
|
|
projectID: "global",
|
|
cost: 0,
|
|
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
time: { created: 1, updated: 2 },
|
|
title: "Exported session",
|
|
location: { directory: "/project" },
|
|
}
|
|
const transfer = {
|
|
info,
|
|
messages: [
|
|
{ id: "msg_first", type: "user", text: "First", time: { created: 1 } },
|
|
{ id: "msg_second", type: "user", text: "Second", time: { created: 2 } },
|
|
],
|
|
}
|
|
const sanitizedTransfer = {
|
|
info: {
|
|
...info,
|
|
title: "[redacted:session-title:ses_export_test]",
|
|
location: { directory: "/[redacted:session-directory:ses_export_test]" },
|
|
},
|
|
messages: [
|
|
{
|
|
id: "msg_first",
|
|
type: "user",
|
|
text: "[redacted:text:msg_first]",
|
|
time: { created: 1 },
|
|
},
|
|
{
|
|
id: "msg_second",
|
|
type: "user",
|
|
text: "[redacted:text:msg_second]",
|
|
time: { created: 2 },
|
|
},
|
|
],
|
|
}
|
|
|
|
const health = () => Response.json({ healthy: true, version: OPENCODE_VERSION, pid: process.pid })
|
|
|
|
function run(args: string[], stdin?: string) {
|
|
const child = Bun.spawn([process.execPath, "run", "src/index.ts", ...args], {
|
|
cwd: path.join(import.meta.dir, ".."),
|
|
stdin: stdin === undefined ? undefined : new Blob([stdin]),
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
})
|
|
return Promise.all([new Response(child.stdout).text(), new Response(child.stderr).text(), child.exited])
|
|
}
|
|
|
|
test("export is raw by default and supports explicit sanitization", async () => {
|
|
const sanitization: string[] = []
|
|
const server = Bun.serve({
|
|
port: 0,
|
|
fetch(request) {
|
|
const url = new URL(request.url)
|
|
if (url.pathname === "/api/health") return health()
|
|
if (url.pathname === `/api/session/${info.id}`) return Response.json({ data: info })
|
|
if (url.pathname === `/api/session/${info.id}/export`) {
|
|
sanitization.push(url.searchParams.get("sanitize") ?? "")
|
|
return Response.json({ data: url.searchParams.get("sanitize") === "true" ? sanitizedTransfer : transfer })
|
|
}
|
|
return new Response("Not found", { status: 404 })
|
|
},
|
|
})
|
|
|
|
try {
|
|
const [stdout, , exitCode] = await run(["export", "-s", info.id, "--server", server.url.toString()])
|
|
const exported = JSON.parse(stdout)
|
|
|
|
expect(exitCode).toBe(0)
|
|
expect(exported).toEqual(transfer)
|
|
|
|
const [sanitized, , sanitizedExitCode] = await run([
|
|
"export",
|
|
"-s",
|
|
info.id,
|
|
"--sanitize",
|
|
"--server",
|
|
server.url.toString(),
|
|
])
|
|
expect(sanitizedExitCode).toBe(0)
|
|
expect(JSON.parse(sanitized)).toEqual(sanitizedTransfer)
|
|
expect(sanitization).toEqual(["false", "true"])
|
|
} finally {
|
|
await server.stop(true)
|
|
}
|
|
}, 15_000)
|
|
|
|
test("export reports an empty session list without a stack trace", async () => {
|
|
const server = Bun.serve({
|
|
port: 0,
|
|
fetch(request) {
|
|
const url = new URL(request.url)
|
|
if (url.pathname === "/api/health") return health()
|
|
if (url.pathname === "/api/location") {
|
|
return Response.json({
|
|
directory: "/project",
|
|
project: { id: "global", directory: "/project", canonical: "/project" },
|
|
})
|
|
}
|
|
if (url.pathname === "/api/session") return Response.json({ data: [], cursor: {} })
|
|
return new Response("Not found", { status: 404 })
|
|
},
|
|
})
|
|
|
|
try {
|
|
const [stdout, stderr, exitCode] = await run(["export", "--server", server.url.toString()])
|
|
|
|
expect(exitCode).toBe(0)
|
|
expect(stdout).toBe("")
|
|
expect(stderr).toBe(`No sessions found${os.EOL}`)
|
|
} finally {
|
|
await server.stop(true)
|
|
}
|
|
})
|
|
|
|
test("interactive export writes a temporary JSON file", async () => {
|
|
const output = await writeExport(transfer, info.id, false)
|
|
const file = output.trim()
|
|
|
|
try {
|
|
expect(path.dirname(file)).toBe(os.tmpdir())
|
|
expect(await Bun.file(file).json()).toEqual(transfer)
|
|
} finally {
|
|
await fs.rm(file, { force: true })
|
|
}
|
|
})
|
|
|
|
test("import validates a file and sends it to the resolved location", async () => {
|
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-import-"))
|
|
const file = path.join(root, "session.json")
|
|
await fs.writeFile(file, JSON.stringify(transfer))
|
|
let imported: unknown
|
|
const server = Bun.serve({
|
|
port: 0,
|
|
async fetch(request) {
|
|
const url = new URL(request.url)
|
|
if (url.pathname === "/api/health") return health()
|
|
if (url.pathname === "/api/location") {
|
|
return Response.json({
|
|
directory: root,
|
|
project: { id: "global", directory: root, canonical: root },
|
|
})
|
|
}
|
|
if (url.pathname === "/api/session/import") {
|
|
imported = await request.json()
|
|
return Response.json({ data: { ...info, location: { directory: root } } })
|
|
}
|
|
return new Response("Not found", { status: 404 })
|
|
},
|
|
})
|
|
|
|
try {
|
|
const [stdout, , exitCode] = await run([
|
|
"import",
|
|
file,
|
|
"--directory",
|
|
root,
|
|
"--server",
|
|
server.url.toString(),
|
|
])
|
|
|
|
expect(exitCode).toBe(0)
|
|
expect(stdout).toBe(`Imported session: ${info.id}${os.EOL}`)
|
|
expect(imported).toEqual({ ...transfer, location: { directory: root } })
|
|
} finally {
|
|
await server.stop(true)
|
|
await fs.rm(root, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
test("import reports an existing session without a stack trace", async () => {
|
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-import-conflict-"))
|
|
const file = path.join(root, "session.json")
|
|
await fs.writeFile(file, JSON.stringify(transfer))
|
|
const server = Bun.serve({
|
|
port: 0,
|
|
fetch(request) {
|
|
const url = new URL(request.url)
|
|
if (url.pathname === "/api/health") return health()
|
|
if (url.pathname === "/api/location") {
|
|
return Response.json({
|
|
directory: root,
|
|
project: { id: "global", directory: root, canonical: root },
|
|
})
|
|
}
|
|
if (url.pathname === "/api/session/import") return new Response("Conflict", { status: 409 })
|
|
return new Response("Not found", { status: 404 })
|
|
},
|
|
})
|
|
|
|
try {
|
|
const [stdout, stderr, exitCode] = await run(["import", file, "--server", server.url.toString()])
|
|
|
|
expect(exitCode).toBe(0)
|
|
expect(stdout).toBe("")
|
|
expect(stderr).toBe(`Session already exists${os.EOL}`)
|
|
} finally {
|
|
await server.stop(true)
|
|
await fs.rm(root, { recursive: true, force: true })
|
|
}
|
|
})
|