summaryrefslogtreecommitdiffhomepage
path: root/packages/app/e2e/fixtures.ts
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-01 11:58:11 -0400
committerGitHub <[email protected]>2026-04-01 15:58:11 +0000
commit38d22765920ef5047db4d9f1bdc0bdf602e6906f (patch)
tree378eb3358326fb91bef194fea04aeea64cb75279 /packages/app/e2e/fixtures.ts
parentd58004a864ee04a34a30fbbcdde9336d477fc8fa (diff)
downloadopencode-38d22765920ef5047db4d9f1bdc0bdf602e6906f.tar.gz
opencode-38d22765920ef5047db4d9f1bdc0bdf602e6906f.zip
test(e2e): isolate prompt tests with per-worker backend (#20464)
Diffstat (limited to 'packages/app/e2e/fixtures.ts')
-rw-r--r--packages/app/e2e/fixtures.ts165
1 files changed, 116 insertions, 49 deletions
diff --git a/packages/app/e2e/fixtures.ts b/packages/app/e2e/fixtures.ts
index 7fc4cda05..c94c93992 100644
--- a/packages/app/e2e/fixtures.ts
+++ b/packages/app/e2e/fixtures.ts
@@ -3,6 +3,7 @@ import { ManagedRuntime } from "effect"
import type { E2EWindow } from "../src/testing/terminal"
import type { Item, Reply, Usage } from "../../opencode/test/lib/llm-server"
import { TestLLMServer } from "../../opencode/test/lib/llm-server"
+import { startBackend } from "./backend"
import {
healthPhase,
cleanupSession,
@@ -19,6 +20,20 @@ import { createSdk, dirSlug, getWorktree, sessionPath } from "./utils"
type LLMFixture = {
url: string
push: (...input: (Item | Reply)[]) => Promise<void>
+ pushMatch: (
+ match: (hit: { url: URL; body: Record<string, unknown> }) => boolean,
+ ...input: (Item | Reply)[]
+ ) => Promise<void>
+ textMatch: (
+ match: (hit: { url: URL; body: Record<string, unknown> }) => boolean,
+ value: string,
+ opts?: { usage?: Usage },
+ ) => Promise<void>
+ toolMatch: (
+ match: (hit: { url: URL; body: Record<string, unknown> }) => boolean,
+ name: string,
+ input: unknown,
+ ) => Promise<void>
text: (value: string, opts?: { usage?: Usage }) => Promise<void>
tool: (name: string, input: unknown) => Promise<void>
toolHang: (name: string, input: unknown) => Promise<void>
@@ -46,32 +61,54 @@ const seedModel = (() => {
}
})()
+type ProjectHandle = {
+ directory: string
+ slug: string
+ gotoSession: (sessionID?: string) => Promise<void>
+ trackSession: (sessionID: string, directory?: string) => void
+ trackDirectory: (directory: string) => void
+ sdk: ReturnType<typeof createSdk>
+}
+
+type ProjectOptions = {
+ extra?: string[]
+ model?: { providerID: string; modelID: string }
+ setup?: (directory: string) => Promise<void>
+ beforeGoto?: (project: { directory: string; sdk: ReturnType<typeof createSdk> }) => Promise<void>
+}
+
type TestFixtures = {
llm: LLMFixture
sdk: ReturnType<typeof createSdk>
gotoSession: (sessionID?: string) => Promise<void>
- withProject: <T>(
- callback: (project: {
- directory: string
- slug: string
- gotoSession: (sessionID?: string) => Promise<void>
- trackSession: (sessionID: string, directory?: string) => void
- trackDirectory: (directory: string) => void
- }) => Promise<T>,
- options?: {
- extra?: string[]
- model?: { providerID: string; modelID: string }
- setup?: (directory: string) => Promise<void>
- },
- ) => Promise<T>
+ withProject: <T>(callback: (project: ProjectHandle) => Promise<T>, options?: ProjectOptions) => Promise<T>
+ withBackendProject: <T>(callback: (project: ProjectHandle) => Promise<T>, options?: ProjectOptions) => Promise<T>
}
type WorkerFixtures = {
+ backend: {
+ url: string
+ sdk: (directory?: string) => ReturnType<typeof createSdk>
+ }
directory: string
slug: string
}
export const test = base.extend<TestFixtures, WorkerFixtures>({
+ backend: [
+ async ({}, use, workerInfo) => {
+ const handle = await startBackend(`w${workerInfo.workerIndex}`)
+ try {
+ await use({
+ url: handle.url,
+ sdk: (directory?: string) => createSdk(directory, handle.url),
+ })
+ } finally {
+ await handle.stop()
+ }
+ },
+ { scope: "worker" },
+ ],
llm: async ({}, use) => {
const rt = ManagedRuntime.make(TestLLMServer.layer)
try {
@@ -79,6 +116,9 @@ export const test = base.extend<TestFixtures, WorkerFixtures>({
await use({
url: svc.url,
push: (...input) => rt.runPromise(svc.push(...input)),
+ pushMatch: (match, ...input) => rt.runPromise(svc.pushMatch(match, ...input)),
+ textMatch: (match, value, opts) => rt.runPromise(svc.textMatch(match, value, opts)),
+ toolMatch: (match, name, input) => rt.runPromise(svc.toolMatch(match, name, input)),
text: (value, opts) => rt.runPromise(svc.text(value, opts)),
tool: (name, input) => rt.runPromise(svc.tool(name, input)),
toolHang: (name, input) => rt.runPromise(svc.toolHang(name, input)),
@@ -146,44 +186,70 @@ export const test = base.extend<TestFixtures, WorkerFixtures>({
await use(gotoSession)
},
withProject: async ({ page }, use) => {
- await use(async (callback, options) => {
- const root = await createTestProject()
- const sessions = new Map<string, string>()
- const dirs = new Set<string>()
- await options?.setup?.(root)
- await seedStorage(page, { directory: root, extra: options?.extra, model: options?.model })
-
- const gotoSession = async (sessionID?: string) => {
- await page.goto(sessionPath(root, sessionID))
- await waitSession(page, { directory: root, sessionID })
- const current = sessionIDFromUrl(page.url())
- if (current) trackSession(current)
- }
+ await use((callback, options) =>
+ runProject(page, callback, options),
+ )
+ },
+ withBackendProject: async ({ page, backend }, use) => {
+ await use((callback, options) =>
+ runProject(page, callback, { ...options, serverUrl: backend.url, sdk: backend.sdk }),
+ )
+ },
+})
- const trackSession = (sessionID: string, directory?: string) => {
- sessions.set(sessionID, directory ?? root)
- }
+async function runProject<T>(
+ page: Page,
+ callback: (project: ProjectHandle) => Promise<T>,
+ options?: ProjectOptions & {
+ serverUrl?: string
+ sdk?: (directory?: string) => ReturnType<typeof createSdk>
+ },
+) {
+ const url = options?.serverUrl
+ const root = await createTestProject(url ? { serverUrl: url } : undefined)
+ const sdk = options?.sdk?.(root) ?? createSdk(root, url)
+ const sessions = new Map<string, string>()
+ const dirs = new Set<string>()
+ await options?.setup?.(root)
+ await seedStorage(page, {
+ directory: root,
+ extra: options?.extra,
+ model: options?.model,
+ serverUrl: url,
+ })
- const trackDirectory = (directory: string) => {
- if (directory !== root) dirs.add(directory)
- }
+ const gotoSession = async (sessionID?: string) => {
+ await page.goto(sessionPath(root, sessionID))
+ await waitSession(page, { directory: root, sessionID, serverUrl: url })
+ const current = sessionIDFromUrl(page.url())
+ if (current) trackSession(current)
+ }
- try {
- await gotoSession()
- const slug = await waitSlug(page)
- return await callback({ directory: root, slug, gotoSession, trackSession, trackDirectory })
- } finally {
- setHealthPhase(page, "cleanup")
- await Promise.allSettled(
- Array.from(sessions, ([sessionID, directory]) => cleanupSession({ sessionID, directory })),
- )
- await Promise.allSettled(Array.from(dirs, (directory) => cleanupTestProject(directory)))
- await cleanupTestProject(root)
- setHealthPhase(page, "test")
- }
- })
- },
-})
+ const trackSession = (sessionID: string, directory?: string) => {
+ sessions.set(sessionID, directory ?? root)
+ }
+
+ const trackDirectory = (directory: string) => {
+ if (directory !== root) dirs.add(directory)
+ }
+
+ try {
+ await options?.beforeGoto?.({ directory: root, sdk })
+ await gotoSession()
+ const slug = await waitSlug(page)
+ return await callback({ directory: root, slug, gotoSession, trackSession, trackDirectory, sdk })
+ } finally {
+ setHealthPhase(page, "cleanup")
+ await Promise.allSettled(
+ Array.from(sessions, ([sessionID, directory]) =>
+ cleanupSession({ sessionID, directory, serverUrl: url }),
+ ),
+ )
+ await Promise.allSettled(Array.from(dirs, (directory) => cleanupTestProject(directory)))
+ await cleanupTestProject(root)
+ setHealthPhase(page, "test")
+ }
+}
async function seedStorage(
page: Page,
@@ -191,6 +257,7 @@ async function seedStorage(
directory: string
extra?: string[]
model?: { providerID: string; modelID: string }
+ serverUrl?: string
},
) {
await seedProjects(page, input)