research: delete Hono backend (do not merge) (#25667)
This commit is contained in:
@@ -22,23 +22,21 @@ import { disposeAllInstances, tmpdir } from "../fixture/fixture"
|
||||
import { it } from "../lib/effect"
|
||||
|
||||
const original = {
|
||||
OPENCODE_EXPERIMENTAL_HTTPAPI: Flag.OPENCODE_EXPERIMENTAL_HTTPAPI,
|
||||
OPENCODE_SERVER_PASSWORD: Flag.OPENCODE_SERVER_PASSWORD,
|
||||
OPENCODE_SERVER_USERNAME: Flag.OPENCODE_SERVER_USERNAME,
|
||||
}
|
||||
|
||||
type Backend = "legacy" | "httpapi"
|
||||
type ServerPath = "default" | "raw"
|
||||
type Sdk = ReturnType<typeof createOpencodeClient>
|
||||
type SdkResult = { response: Response; data?: unknown; error?: unknown }
|
||||
type Captured = { status: number; data?: unknown; error?: unknown }
|
||||
type ProjectFixture = { sdk: Sdk; directory: string }
|
||||
type LlmProjectFixture = ProjectFixture & { llm: TestLLMServer["Service"] }
|
||||
|
||||
function app(backend: Backend, input?: { password?: string; username?: string }) {
|
||||
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = backend === "httpapi"
|
||||
function app(serverPath: ServerPath, input?: { password?: string; username?: string }) {
|
||||
Flag.OPENCODE_SERVER_PASSWORD = input?.password
|
||||
Flag.OPENCODE_SERVER_USERNAME = input?.username
|
||||
if (backend === "legacy") return Server.Legacy().app
|
||||
if (serverPath === "default") return Server.Default().app
|
||||
|
||||
const handler = HttpRouter.toWebHandler(
|
||||
ExperimentalHttpApiServer.routes.pipe(
|
||||
@@ -62,7 +60,7 @@ function app(backend: Backend, input?: { password?: string; username?: string })
|
||||
}
|
||||
|
||||
function client(
|
||||
backend: Backend,
|
||||
serverPath: ServerPath,
|
||||
directory?: string,
|
||||
input?: { password?: string; username?: string; headers?: Record<string, string> },
|
||||
) {
|
||||
@@ -70,12 +68,12 @@ function client(
|
||||
baseUrl: "http://localhost",
|
||||
directory,
|
||||
headers: input?.headers,
|
||||
fetch: serverFetch(backend, input),
|
||||
fetch: serverFetch(serverPath, input),
|
||||
})
|
||||
}
|
||||
|
||||
function serverFetch(backend: Backend, input?: { password?: string; username?: string }) {
|
||||
const serverApp = app(backend, input)
|
||||
function serverFetch(serverPath: ServerPath, input?: { password?: string; username?: string }) {
|
||||
const serverApp = app(serverPath, input)
|
||||
return Object.assign(
|
||||
async (request: RequestInfo | URL, init?: RequestInit) =>
|
||||
await serverApp.fetch(request instanceof Request ? request : new Request(request, init)),
|
||||
@@ -194,20 +192,20 @@ function httpapi<A, E>(name: string, effect: Effect.Effect<A, E, Scope.Scope>) {
|
||||
it.live(name, effect)
|
||||
}
|
||||
|
||||
function parity<A, E>(name: string, scenario: (backend: Backend) => Effect.Effect<A, E, Scope.Scope>) {
|
||||
function serverPathParity<A, E>(name: string, scenario: (serverPath: ServerPath) => Effect.Effect<A, E, Scope.Scope>) {
|
||||
it.live(
|
||||
name,
|
||||
Effect.gen(function* () {
|
||||
const legacy = yield* scenario("legacy")
|
||||
const standard = yield* scenario("default")
|
||||
yield* resetState()
|
||||
const httpapi = yield* scenario("httpapi")
|
||||
expect(httpapi).toEqual(legacy)
|
||||
const raw = yield* scenario("raw")
|
||||
expect(raw).toEqual(standard)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
function withProject<A, E, R>(
|
||||
backend: Backend,
|
||||
serverPath: ServerPath,
|
||||
options: { git?: boolean; config?: Partial<Config.Info>; setup?: (dir: string) => Effect.Effect<void> },
|
||||
run: (input: ProjectFixture) => Effect.Effect<A, E, R>,
|
||||
) {
|
||||
@@ -216,30 +214,30 @@ function withProject<A, E, R>(
|
||||
(tmp) => call(() => tmp[Symbol.asyncDispose]()).pipe(Effect.ignore),
|
||||
).pipe(
|
||||
Effect.tap((tmp) => options.setup?.(tmp.path) ?? Effect.void),
|
||||
Effect.flatMap((tmp) => run({ sdk: client(backend, tmp.path), directory: tmp.path })),
|
||||
Effect.flatMap((tmp) => run({ sdk: client(serverPath, tmp.path), directory: tmp.path })),
|
||||
)
|
||||
}
|
||||
|
||||
function withStandardProject<A, E, R>(backend: Backend, run: (input: ProjectFixture) => Effect.Effect<A, E, R>) {
|
||||
return withProject(backend, { setup: writeStandardFiles }, run)
|
||||
function withStandardProject<A, E, R>(serverPath: ServerPath, run: (input: ProjectFixture) => Effect.Effect<A, E, R>) {
|
||||
return withProject(serverPath, { setup: writeStandardFiles }, run)
|
||||
}
|
||||
|
||||
function withFakeLlm<A, E, R>(backend: Backend, run: (input: LlmProjectFixture) => Effect.Effect<A, E, R>) {
|
||||
function withFakeLlm<A, E, R>(serverPath: ServerPath, run: (input: LlmProjectFixture) => Effect.Effect<A, E, R>) {
|
||||
return Effect.gen(function* () {
|
||||
const llm = yield* TestLLMServer
|
||||
return yield* withProject(backend, { config: providerConfig(llm.url) }, (input) => run({ ...input, llm }))
|
||||
return yield* withProject(serverPath, { config: providerConfig(llm.url) }, (input) => run({ ...input, llm }))
|
||||
}).pipe(Effect.provide(TestLLMServer.layer))
|
||||
}
|
||||
|
||||
function withFakeLlmProject<A, E, R>(
|
||||
backend: Backend,
|
||||
serverPath: ServerPath,
|
||||
options: { setup?: (dir: string) => Effect.Effect<void> },
|
||||
run: (input: LlmProjectFixture) => Effect.Effect<A, E, R>,
|
||||
) {
|
||||
return Effect.gen(function* () {
|
||||
const llm = yield* TestLLMServer
|
||||
return yield* withProject(
|
||||
backend,
|
||||
serverPath,
|
||||
{
|
||||
config: providerConfig(llm.url),
|
||||
setup: options.setup,
|
||||
@@ -306,7 +304,6 @@ function seedMessage(directory: string, sessionID: string) {
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
Flag.OPENCODE_EXPERIMENTAL_HTTPAPI = original.OPENCODE_EXPERIMENTAL_HTTPAPI
|
||||
Flag.OPENCODE_SERVER_PASSWORD = original.OPENCODE_SERVER_PASSWORD
|
||||
Flag.OPENCODE_SERVER_USERNAME = original.OPENCODE_SERVER_USERNAME
|
||||
await disposeAllInstances()
|
||||
@@ -317,7 +314,7 @@ describe("HttpApi SDK", () => {
|
||||
httpapi(
|
||||
"uses the generated SDK for global and control routes",
|
||||
Effect.gen(function* () {
|
||||
const sdk = client("httpapi")
|
||||
const sdk = client("raw")
|
||||
const health = yield* call(() => sdk.global.health())
|
||||
const log = yield* call(() => sdk.app.log({ service: "httpapi-sdk-test", level: "info", message: "hello" }))
|
||||
|
||||
@@ -334,7 +331,7 @@ describe("HttpApi SDK", () => {
|
||||
|
||||
httpapi(
|
||||
"uses the generated SDK for safe instance routes",
|
||||
withProject("httpapi", { git: false, setup: writeStandardFiles }, ({ sdk }) =>
|
||||
withProject("raw", { git: false, setup: writeStandardFiles }, ({ sdk }) =>
|
||||
Effect.gen(function* () {
|
||||
const file = yield* call(() => sdk.file.read({ path: "hello.txt" }))
|
||||
const session = yield* call(() => sdk.session.create({ title: "sdk" }))
|
||||
@@ -357,9 +354,9 @@ describe("HttpApi SDK", () => {
|
||||
),
|
||||
)
|
||||
|
||||
parity("matches generated SDK global and control behavior across backends", (backend) =>
|
||||
serverPathParity("matches generated SDK global and control behavior", (serverPath) =>
|
||||
Effect.gen(function* () {
|
||||
const sdk = client(backend)
|
||||
const sdk = client(serverPath)
|
||||
const health = yield* capture(() => sdk.global.health())
|
||||
const log = yield* capture(() => sdk.app.log({ service: "sdk-parity", level: "info", message: "hello" }))
|
||||
const invalidAuth = yield* capture(() => sdk.auth.set({ providerID: "test" }))
|
||||
@@ -372,22 +369,22 @@ describe("HttpApi SDK", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
parity("matches generated SDK global event stream across backends", (backend) =>
|
||||
firstEvent(() => client(backend).global.event({ signal: AbortSignal.timeout(1_000) })).pipe(
|
||||
serverPathParity("matches generated SDK global event stream", (serverPath) =>
|
||||
firstEvent(() => client(serverPath).global.event({ signal: AbortSignal.timeout(1_000) })).pipe(
|
||||
Effect.map((event) => ({ type: record(record(event).payload).type })),
|
||||
),
|
||||
)
|
||||
|
||||
parity("matches generated SDK instance event stream across backends", (backend) =>
|
||||
withStandardProject(backend, ({ sdk }) =>
|
||||
serverPathParity("matches generated SDK instance event stream", (serverPath) =>
|
||||
withStandardProject(serverPath, ({ sdk }) =>
|
||||
firstEvent(() => sdk.event.subscribe(undefined, { signal: AbortSignal.timeout(1_000) })).pipe(
|
||||
Effect.map((event) => ({ type: record(record(event).payload).type })),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
parity("matches generated SDK missing session errors across backends", (backend) =>
|
||||
withStandardProject(backend, ({ sdk }) =>
|
||||
serverPathParity("matches generated SDK missing session errors", (serverPath) =>
|
||||
withStandardProject(serverPath, ({ sdk }) =>
|
||||
Effect.gen(function* () {
|
||||
const sessionID = "ses_missing"
|
||||
const expected = {
|
||||
@@ -408,8 +405,8 @@ describe("HttpApi SDK", () => {
|
||||
),
|
||||
)
|
||||
|
||||
parity("formats missing session validation errors for -s", (backend) =>
|
||||
withStandardProject(backend, ({ directory }) =>
|
||||
serverPathParity("formats missing session validation errors for -s", (serverPath) =>
|
||||
withStandardProject(serverPath, ({ directory }) =>
|
||||
Effect.gen(function* () {
|
||||
const sessionID = "ses_206f84f18ffeZ6hhD7pFYAiW5T"
|
||||
const thrown = yield* captureThrown(() =>
|
||||
@@ -417,7 +414,7 @@ describe("HttpApi SDK", () => {
|
||||
url: "http://localhost",
|
||||
directory,
|
||||
sessionID,
|
||||
fetch: serverFetch(backend),
|
||||
fetch: serverFetch(serverPath),
|
||||
}),
|
||||
)
|
||||
expect(errorMessage(thrown)).toBe(`Session not found: ${sessionID}`)
|
||||
@@ -426,20 +423,21 @@ describe("HttpApi SDK", () => {
|
||||
),
|
||||
)
|
||||
|
||||
parity("matches generated SDK basic auth behavior across backends", (backend) =>
|
||||
withStandardProject(backend, ({ directory }) =>
|
||||
httpapi(
|
||||
"uses generated SDK basic auth behavior",
|
||||
withStandardProject("raw", ({ directory }) =>
|
||||
Effect.gen(function* () {
|
||||
const missing = yield* capture(() =>
|
||||
client(backend, directory, { password: "secret" }).file.read({ path: "hello.txt" }),
|
||||
client("raw", directory, { password: "secret" }).file.read({ path: "hello.txt" }),
|
||||
)
|
||||
const bad = yield* capture(() =>
|
||||
client(backend, directory, {
|
||||
client("raw", directory, {
|
||||
password: "secret",
|
||||
headers: { authorization: authorization("opencode", "wrong") },
|
||||
}).file.read({ path: "hello.txt" }),
|
||||
)
|
||||
const good = yield* capture(() =>
|
||||
client(backend, directory, {
|
||||
client("raw", directory, {
|
||||
password: "secret",
|
||||
headers: { authorization: authorization("opencode", "secret") },
|
||||
}).file.read({ path: "hello.txt" }),
|
||||
@@ -453,8 +451,8 @@ describe("HttpApi SDK", () => {
|
||||
),
|
||||
)
|
||||
|
||||
parity("matches generated SDK instance read routes across backends", (backend) =>
|
||||
withStandardProject(backend, ({ sdk, directory }) =>
|
||||
serverPathParity("matches generated SDK instance read routes", (serverPath) =>
|
||||
withStandardProject(serverPath, ({ sdk, directory }) =>
|
||||
Effect.gen(function* () {
|
||||
const project = yield* capture(() => sdk.project.current())
|
||||
const projects = yield* capture(() => sdk.project.list())
|
||||
@@ -504,8 +502,8 @@ describe("HttpApi SDK", () => {
|
||||
),
|
||||
)
|
||||
|
||||
parity("matches generated SDK session lifecycle routes across backends", (backend) =>
|
||||
withStandardProject(backend, ({ sdk }) =>
|
||||
serverPathParity("matches generated SDK session lifecycle routes", (serverPath) =>
|
||||
withStandardProject(serverPath, ({ sdk }) =>
|
||||
Effect.gen(function* () {
|
||||
const parent = yield* capture(() => sdk.session.create({ title: "parent" }))
|
||||
const parentID = String(record(parent.data).id)
|
||||
@@ -557,8 +555,8 @@ describe("HttpApi SDK", () => {
|
||||
),
|
||||
)
|
||||
|
||||
parity("matches generated SDK session message and part routes across backends", (backend) =>
|
||||
withStandardProject(backend, ({ sdk, directory }) =>
|
||||
serverPathParity("matches generated SDK session message and part routes", (serverPath) =>
|
||||
withStandardProject(serverPath, ({ sdk, directory }) =>
|
||||
Effect.gen(function* () {
|
||||
const session = yield* capture(() => sdk.session.create({ title: "messages" }))
|
||||
const sessionID = String(record(session.data).id)
|
||||
@@ -609,8 +607,8 @@ describe("HttpApi SDK", () => {
|
||||
),
|
||||
)
|
||||
|
||||
parity("matches generated SDK prompt no-reply routes across backends", (backend) =>
|
||||
withStandardProject(backend, ({ sdk }) =>
|
||||
serverPathParity("matches generated SDK prompt no-reply routes", (serverPath) =>
|
||||
withStandardProject(serverPath, ({ sdk }) =>
|
||||
Effect.gen(function* () {
|
||||
const session = yield* capture(() => sdk.session.create({ title: "prompt" }))
|
||||
const sessionID = String(record(session.data).id)
|
||||
@@ -646,8 +644,8 @@ describe("HttpApi SDK", () => {
|
||||
),
|
||||
)
|
||||
|
||||
parity("matches generated SDK prompt streaming through fake LLM across backends", (backend) =>
|
||||
withFakeLlm(backend, ({ sdk, llm }) =>
|
||||
serverPathParity("matches generated SDK prompt streaming through fake LLM", (serverPath) =>
|
||||
withFakeLlm(serverPath, ({ sdk, llm }) =>
|
||||
Effect.gen(function* () {
|
||||
yield* llm.text("fake world", { usage: { input: 11, output: 7 } })
|
||||
const session = yield* capture(() =>
|
||||
@@ -682,7 +680,7 @@ describe("HttpApi SDK", () => {
|
||||
|
||||
httpapi(
|
||||
"includes project skills in REST API async prompt context",
|
||||
withFakeLlmProject("httpapi", { setup: writeProjectSkill }, ({ sdk, llm }) =>
|
||||
withFakeLlmProject("default", { setup: writeProjectSkill }, ({ sdk, llm }) =>
|
||||
Effect.gen(function* () {
|
||||
yield* llm.text("skill context ok", { usage: { input: 11, output: 7 } })
|
||||
const session = yield* capture(() =>
|
||||
@@ -710,8 +708,8 @@ describe("HttpApi SDK", () => {
|
||||
),
|
||||
)
|
||||
|
||||
parity("matches generated SDK TUI validation and command routes across backends", (backend) =>
|
||||
withStandardProject(backend, ({ sdk }) =>
|
||||
serverPathParity("matches generated SDK TUI validation and command routes", (serverPath) =>
|
||||
withStandardProject(serverPath, ({ sdk }) =>
|
||||
Effect.gen(function* () {
|
||||
const session = yield* capture(() => sdk.session.create({ title: "tui" }))
|
||||
const sessionID = String(record(session.data).id)
|
||||
@@ -761,8 +759,8 @@ describe("HttpApi SDK", () => {
|
||||
),
|
||||
)
|
||||
|
||||
parity("matches generated SDK project git initialization across backends", (backend) =>
|
||||
withProject(backend, { git: false }, ({ sdk, directory }) =>
|
||||
serverPathParity("matches generated SDK project git initialization", (serverPath) =>
|
||||
withProject(serverPath, { git: false }, ({ sdk, directory }) =>
|
||||
Effect.gen(function* () {
|
||||
const before = yield* capture(() => sdk.project.current())
|
||||
const init = yield* capture(() => sdk.project.initGit())
|
||||
|
||||
Reference in New Issue
Block a user