diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index f3c72c7584..ccc63d807d 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -1446,159 +1446,131 @@ test("config parser preserves permission order while rejecting unknown top-level // MCP config merging tests -test("project config can override MCP server enabled status", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - // Simulates a base config (like from remote .well-known) with disabled MCP - await Filesystem.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - mcp: { - jira: { - type: "remote", - url: "https://jira.example.com/mcp", - enabled: false, - }, - wiki: { - type: "remote", - url: "https://wiki.example.com/mcp", - enabled: false, - }, - }, - }), - ) - // Project config enables just jira - await Filesystem.write( - path.join(dir, "opencode.jsonc"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - mcp: { - jira: { - type: "remote", - url: "https://jira.example.com/mcp", - enabled: true, - }, - }, - }), - ) - }, - }) - await withTestInstance({ - directory: tmp.path, - fn: async (ctx) => { - const config = await load(ctx) - // jira should be enabled (overridden by project config) - expect(config.mcp?.jira).toEqual({ - type: "remote", - url: "https://jira.example.com/mcp", - enabled: true, - }) - // wiki should still be disabled (not overridden) - expect(config.mcp?.wiki).toEqual({ - type: "remote", - url: "https://wiki.example.com/mcp", - enabled: false, - }) - }, - }) -}) - -test("MCP config deep merges preserving base config properties", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - // Base config with full MCP definition - await Filesystem.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - mcp: { - myserver: { - type: "remote", - url: "https://myserver.example.com/mcp", - enabled: false, - headers: { - "X-Custom-Header": "value", - }, - }, - }, - }), - ) - // Override just enables it, should preserve other properties - await Filesystem.write( - path.join(dir, "opencode.jsonc"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - mcp: { - myserver: { - type: "remote", - url: "https://myserver.example.com/mcp", - enabled: true, - }, - }, - }), - ) - }, - }) - await withTestInstance({ - directory: tmp.path, - fn: async (ctx) => { - const config = await load(ctx) - expect(config.mcp?.myserver).toEqual({ - type: "remote", - url: "https://myserver.example.com/mcp", - enabled: true, - headers: { - "X-Custom-Header": "value", +it.instance("project config can override MCP server enabled status", () => + Effect.gen(function* () { + const test = yield* TestInstance + // Simulates a base config (like from remote .well-known) with disabled MCP. + yield* writeConfigEffect(test.directory, { + $schema: "https://opencode.ai/config.json", + mcp: { + jira: { + type: "remote", + url: "https://jira.example.com/mcp", + enabled: false, }, - }) - }, - }) -}) + wiki: { + type: "remote", + url: "https://wiki.example.com/mcp", + enabled: false, + }, + }, + }) + // Project config enables just jira. + yield* writeConfigEffect( + test.directory, + { + $schema: "https://opencode.ai/config.json", + mcp: { + jira: { + type: "remote", + url: "https://jira.example.com/mcp", + enabled: true, + }, + }, + }, + "opencode.jsonc", + ) -test("local .opencode config can override MCP from project config", async () => { - await using tmp = await tmpdir({ - init: async (dir) => { - // Project config with disabled MCP - await Filesystem.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - mcp: { - docs: { - type: "remote", - url: "https://docs.example.com/mcp", - enabled: false, - }, + const config = yield* Config.Service.use((svc) => svc.get()) + expect(config.mcp?.jira).toEqual({ + type: "remote", + url: "https://jira.example.com/mcp", + enabled: true, + }) + expect(config.mcp?.wiki).toEqual({ + type: "remote", + url: "https://wiki.example.com/mcp", + enabled: false, + }) + }), +) + +it.instance("MCP config deep merges preserving base config properties", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* writeConfigEffect(test.directory, { + $schema: "https://opencode.ai/config.json", + mcp: { + myserver: { + type: "remote", + url: "https://myserver.example.com/mcp", + enabled: false, + headers: { + "X-Custom-Header": "value", }, - }), - ) - // Local .opencode directory config enables it - const opencodeDir = path.join(dir, ".opencode") - await fs.mkdir(opencodeDir, { recursive: true }) - await Filesystem.write( - path.join(opencodeDir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - mcp: { - docs: { - type: "remote", - url: "https://docs.example.com/mcp", - enabled: true, - }, + }, + }, + }) + yield* writeConfigEffect( + test.directory, + { + $schema: "https://opencode.ai/config.json", + mcp: { + myserver: { + type: "remote", + url: "https://myserver.example.com/mcp", + enabled: true, }, - }), - ) - }, - }) - await withTestInstance({ - directory: tmp.path, - fn: async (ctx) => { - const config = await load(ctx) - expect(config.mcp?.docs?.enabled).toBe(true) - }, - }) -}) + }, + }, + "opencode.jsonc", + ) + + const config = yield* Config.Service.use((svc) => svc.get()) + expect(config.mcp?.myserver).toEqual({ + type: "remote", + url: "https://myserver.example.com/mcp", + enabled: true, + headers: { + "X-Custom-Header": "value", + }, + }) + }), +) + +it.instance("local .opencode config can override MCP from project config", () => + Effect.gen(function* () { + const test = yield* TestInstance + yield* writeConfigEffect(test.directory, { + $schema: "https://opencode.ai/config.json", + mcp: { + docs: { + type: "remote", + url: "https://docs.example.com/mcp", + enabled: false, + }, + }, + }) + yield* mkdirEffect(path.join(test.directory, ".opencode")) + yield* writeConfigEffect( + path.join(test.directory, ".opencode"), + { + $schema: "https://opencode.ai/config.json", + mcp: { + docs: { + type: "remote", + url: "https://docs.example.com/mcp", + enabled: true, + }, + }, + }, + "opencode.json", + ) + + const config = yield* Config.Service.use((svc) => svc.get()) + expect(config.mcp?.docs?.enabled).toBe(true) + }), +) test("project config overrides remote well-known config", async () => { const originalFetch = globalThis.fetch diff --git a/perf/test-suite.md b/perf/test-suite.md index 2516bdc409..b37b868bbd 100644 --- a/perf/test-suite.md +++ b/perf/test-suite.md @@ -80,6 +80,7 @@ Repeated setup work, long sleeps/timeouts, serial integration tests, filesystem/ | Remaining simple config load cases can use Effect-aware instance fixtures | Migrated default config load and legacy TUI-key cases to `it.instance` | 7.78s | 6.39s | keep | Single baseline before edit; after median from three sequential reruns (5.76, 6.39, 6.53). Keep as cleanup with cautious timing. | | Managed settings config cases can use Effect-aware instance fixtures | Migrated managed override and missing-managed-file cases to `it.instance` | 2.40s | 1.76s | keep | Single baseline before edit; after median from three sequential reruns (1.75, 1.76, 1.80). | | Local plugin and subagent config fixtures can use Effect-aware instance fixtures | Migrated scoped npm plugin and custom subagent markdown cases to `it.instance` | 2.37s | 1.67s | keep | Single baseline before edit; after median from three sequential reruns (1.66, 1.67, 1.67). | +| MCP merge config cases can use Effect-aware instance fixtures | Migrated three MCP merge/override cases to `it.instance` | 1.98s | 1.95s | keep | Neutral timing within noise; removes manual `tmpdir` + `withTestInstance` setup from isolated filesystem-only config cases. | ## Profiling Results @@ -135,3 +136,4 @@ Full-suite sanity checks: | Socket reset retry test can shorten its idle-timeout path | Reduced Bun server idle timeout and tried forced server close | 16.46s | failed | discard | Shorter idle timeout changed the error shape; forced close hung. Keep the real socket reset. | | `tool/webfetch` can avoid per-test instance setup | Switched local HTTP tests from `it.instance` to `it.live` | 1.219s | failed | discard | Tool execution reads instance-local agent state, so the temp instance is required. | | LSP client interop tests can shorten coarse request-handling sleeps | Reduced fixed post-notification waits from 100ms to 10ms | 4.270s | 4.740s | discard | First run improved to 3.870s but verification was slower than baseline; not a clear win. | +| Config content env cases can use Effect-aware instance fixtures | Migrated two `OPENCODE_CONFIG_CONTENT` token substitution cases to `it.instance` | 1.95s | 2.06s | discard | Passing but not neutral-or-better in focused reruns; keep existing explicit env cleanup. |