test(server): migrate provider httpapi test to effect runner (#27216)
This commit is contained in:
@@ -1,18 +1,23 @@
|
|||||||
import { afterEach, describe, expect } from "bun:test"
|
import { describe, expect } from "bun:test"
|
||||||
import { Effect, FileSystem, Layer, Path } from "effect"
|
import { Effect, FileSystem, Layer, Path } from "effect"
|
||||||
import { NodeFileSystem, NodePath } from "@effect/platform-node"
|
import { NodeFileSystem, NodePath } from "@effect/platform-node"
|
||||||
import { Instance } from "../../src/project/instance"
|
|
||||||
import { WithInstance } from "../../src/project/with-instance"
|
|
||||||
import { InstanceRuntime } from "../../src/project/instance-runtime"
|
|
||||||
import { Server } from "../../src/server/server"
|
import { Server } from "../../src/server/server"
|
||||||
import * as Log from "@opencode-ai/core/util/log"
|
import * as Log from "@opencode-ai/core/util/log"
|
||||||
import { resetDatabase } from "../fixture/db"
|
import { resetDatabase } from "../fixture/db"
|
||||||
import { disposeAllInstances, provideInstance } from "../fixture/fixture"
|
import { TestInstance } from "../fixture/fixture"
|
||||||
import { testEffect } from "../lib/effect"
|
import { testEffect } from "../lib/effect"
|
||||||
|
|
||||||
void Log.init({ print: false })
|
void Log.init({ print: false })
|
||||||
|
|
||||||
const it = testEffect(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer))
|
const testStateLayer = Layer.effectDiscard(
|
||||||
|
Effect.acquireRelease(
|
||||||
|
Effect.promise(() => resetDatabase()),
|
||||||
|
() => Effect.promise(() => resetDatabase()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
const it = testEffect(Layer.mergeAll(testStateLayer, NodeFileSystem.layer, NodePath.layer))
|
||||||
|
const projectOptions = { config: { formatter: false, lsp: false } }
|
||||||
const providerID = "test-oauth-parity"
|
const providerID = "test-oauth-parity"
|
||||||
const oauthURL = "https://example.com/oauth"
|
const oauthURL = "https://example.com/oauth"
|
||||||
const oauthInstructions = "Finish OAuth"
|
const oauthInstructions = "Finish OAuth"
|
||||||
@@ -191,91 +196,69 @@ function writeProviderModelsMutationPlugin(dir: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function withProviderProject<A, E, R>(self: (dir: string) => Effect.Effect<A, E, R>) {
|
function setEnvScoped(key: string, value: string) {
|
||||||
return Effect.gen(function* () {
|
return Effect.acquireRelease(
|
||||||
const fs = yield* FileSystem.FileSystem
|
Effect.sync(() => {
|
||||||
const path = yield* Path.Path
|
const previous = process.env[key]
|
||||||
const dir = yield* fs.makeTempDirectoryScoped({ prefix: "opencode-test-" })
|
process.env[key] = value
|
||||||
|
return previous
|
||||||
yield* fs.writeFileString(
|
}),
|
||||||
path.join(dir, "opencode.json"),
|
(previous) =>
|
||||||
JSON.stringify({ $schema: "https://opencode.ai/config.json", formatter: false, lsp: false }),
|
Effect.sync(() => {
|
||||||
)
|
if (previous === undefined) delete process.env[key]
|
||||||
yield* writeProviderAuthPlugin(dir)
|
else process.env[key] = previous
|
||||||
yield* Effect.addFinalizer(() =>
|
}),
|
||||||
Effect.promise(() =>
|
)
|
||||||
WithInstance.provide({ directory: dir, fn: () => InstanceRuntime.disposeInstance(Instance.current) }),
|
|
||||||
).pipe(Effect.ignore),
|
|
||||||
)
|
|
||||||
|
|
||||||
return yield* self(dir).pipe(provideInstance(dir))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
await disposeAllInstances()
|
|
||||||
await resetDatabase()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("provider HttpApi", () => {
|
describe("provider HttpApi", () => {
|
||||||
it.live(
|
it.instance(
|
||||||
"serves OAuth authorize response shapes",
|
"serves OAuth authorize response shapes",
|
||||||
withProviderProject((dir) =>
|
Effect.gen(function* () {
|
||||||
Effect.gen(function* () {
|
const instance = yield* TestInstance
|
||||||
const headers = { "x-opencode-directory": dir, "content-type": "application/json" }
|
yield* writeProviderAuthPlugin(instance.directory)
|
||||||
const server = app()
|
const headers = { "x-opencode-directory": instance.directory, "content-type": "application/json" }
|
||||||
|
const server = app()
|
||||||
|
|
||||||
const api = yield* requestAuthorize({
|
const api = yield* requestAuthorize({
|
||||||
app: server,
|
app: server,
|
||||||
providerID,
|
providerID,
|
||||||
method: 0,
|
method: 0,
|
||||||
headers,
|
headers,
|
||||||
})
|
})
|
||||||
// method 0 (api-key style) — authorize() resolves with no further
|
// method 0 (api-key style) — authorize() resolves with no further
|
||||||
// redirect; #26474 changed the wire format to JSON `null` so clients
|
// redirect; #26474 changed the wire format to JSON `null` so clients
|
||||||
// can `.json()` parse uniformly instead of getting an empty body
|
// can `.json()` parse uniformly instead of getting an empty body
|
||||||
// that throws.
|
// that throws.
|
||||||
expect(api).toEqual({ status: 200, body: "null" })
|
expect(api).toEqual({ status: 200, body: "null" })
|
||||||
|
|
||||||
const oauth = yield* requestAuthorize({
|
const oauth = yield* requestAuthorize({
|
||||||
app: server,
|
app: server,
|
||||||
providerID,
|
providerID,
|
||||||
method: 1,
|
method: 1,
|
||||||
headers,
|
headers,
|
||||||
})
|
})
|
||||||
expect(JSON.parse(oauth.body)).toEqual({
|
expect(JSON.parse(oauth.body)).toEqual({
|
||||||
url: oauthURL,
|
url: oauthURL,
|
||||||
method: "code",
|
method: "code",
|
||||||
instructions: oauthInstructions,
|
instructions: oauthInstructions,
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
),
|
projectOptions,
|
||||||
)
|
)
|
||||||
|
|
||||||
it.live("serves provider lists when auth loaders add runtime fetch options", () =>
|
it.instance(
|
||||||
|
"serves provider lists when auth loaders add runtime fetch options",
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const fs = yield* FileSystem.FileSystem
|
const instance = yield* TestInstance
|
||||||
const path = yield* Path.Path
|
yield* writeFunctionOptionsPlugin(instance.directory)
|
||||||
const dir = yield* fs.makeTempDirectoryScoped({ prefix: "opencode-test-" })
|
yield* setEnvScoped(
|
||||||
const previous = process.env.OPENCODE_AUTH_CONTENT
|
"OPENCODE_AUTH_CONTENT",
|
||||||
|
JSON.stringify({
|
||||||
yield* fs.writeFileString(
|
|
||||||
path.join(dir, "opencode.json"),
|
|
||||||
JSON.stringify({ $schema: "https://opencode.ai/config.json", formatter: false, lsp: false }),
|
|
||||||
)
|
|
||||||
yield* writeFunctionOptionsPlugin(dir)
|
|
||||||
yield* Effect.sync(() => {
|
|
||||||
process.env.OPENCODE_AUTH_CONTENT = JSON.stringify({
|
|
||||||
google: { type: "oauth", refresh: "dummy", access: "dummy", expires: 9999999999999 },
|
google: { type: "oauth", refresh: "dummy", access: "dummy", expires: 9999999999999 },
|
||||||
})
|
|
||||||
})
|
|
||||||
yield* Effect.addFinalizer(() =>
|
|
||||||
Effect.sync(() => {
|
|
||||||
if (previous === undefined) delete process.env.OPENCODE_AUTH_CONTENT
|
|
||||||
if (previous !== undefined) process.env.OPENCODE_AUTH_CONTENT = previous
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
const headers = { "x-opencode-directory": dir }
|
const headers = { "x-opencode-directory": instance.directory }
|
||||||
const providerResponse = yield* Effect.promise(() => Promise.resolve(app().request("/provider", { headers })))
|
const providerResponse = yield* Effect.promise(() => Promise.resolve(app().request("/provider", { headers })))
|
||||||
const configResponse = yield* Effect.promise(() =>
|
const configResponse = yield* Effect.promise(() =>
|
||||||
Promise.resolve(app().request("/config/providers", { headers })),
|
Promise.resolve(app().request("/config/providers", { headers })),
|
||||||
@@ -291,21 +274,16 @@ describe("provider HttpApi", () => {
|
|||||||
expect(hasNonZeroModelCost(providerBody, "all", "google")).toBe(true)
|
expect(hasNonZeroModelCost(providerBody, "all", "google")).toBe(true)
|
||||||
expect(hasNonZeroModelCost(configBody, "providers", "google")).toBe(true)
|
expect(hasNonZeroModelCost(configBody, "providers", "google")).toBe(true)
|
||||||
}),
|
}),
|
||||||
|
projectOptions,
|
||||||
)
|
)
|
||||||
|
|
||||||
it.live("keeps provider.models hook input mutations out of provider state", () =>
|
it.instance(
|
||||||
|
"keeps provider.models hook input mutations out of provider state",
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const fs = yield* FileSystem.FileSystem
|
const instance = yield* TestInstance
|
||||||
const path = yield* Path.Path
|
yield* writeProviderModelsMutationPlugin(instance.directory)
|
||||||
const dir = yield* fs.makeTempDirectoryScoped({ prefix: "opencode-test-" })
|
|
||||||
|
|
||||||
yield* fs.writeFileString(
|
const headers = { "x-opencode-directory": instance.directory }
|
||||||
path.join(dir, "opencode.json"),
|
|
||||||
JSON.stringify({ $schema: "https://opencode.ai/config.json", formatter: false, lsp: false }),
|
|
||||||
)
|
|
||||||
yield* writeProviderModelsMutationPlugin(dir)
|
|
||||||
|
|
||||||
const headers = { "x-opencode-directory": dir }
|
|
||||||
const providerResponse = yield* Effect.promise(() => Promise.resolve(app().request("/provider", { headers })))
|
const providerResponse = yield* Effect.promise(() => Promise.resolve(app().request("/provider", { headers })))
|
||||||
const configResponse = yield* Effect.promise(() =>
|
const configResponse = yield* Effect.promise(() =>
|
||||||
Promise.resolve(app().request("/config/providers", { headers })),
|
Promise.resolve(app().request("/config/providers", { headers })),
|
||||||
@@ -320,5 +298,6 @@ describe("provider HttpApi", () => {
|
|||||||
expect(hasProviderMutationMarker(configBody, "providers", "google")).toBe(false)
|
expect(hasProviderMutationMarker(configBody, "providers", "google")).toBe(false)
|
||||||
expect(hasNonZeroModelCost(providerBody, "all", "google")).toBe(true)
|
expect(hasNonZeroModelCost(providerBody, "all", "google")).toBe(true)
|
||||||
}),
|
}),
|
||||||
|
projectOptions,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user