import { describe, expect, test } from "bun:test"; import type { OpenworkCloudMcpFailure, OpenworkCloudMcpHealth, } from "../src/app/lib/openwork-server"; import { createCloudMcpSubmissionCoordinator, clearCloudMcpSubmissionFailure, decideCloudMcpSubmissionGate, ensureCloudMcpSubmissionReadiness, IDLE_CLOUD_MCP_SUBMISSION_GATE_STATE, resolveCloudMcpSubmissionAuth, type CloudMcpSubmissionGateDecision, type CloudMcpSubmissionPreparationResult, } from "../src/react-app/domains/connections/cloud-mcp-submit-readiness"; const PROVIDER_MODEL = { provider: "openwork", model: "gpt-5" }; function failure(input?: Partial): OpenworkCloudMcpFailure { return { code: input?.code ?? "cloud_registration_failed", stage: input?.stage ?? "engine_delivery", retryable: input?.retryable ?? true, recommendedAction: input?.recommendedAction ?? "Retry", message: input?.message ?? "Cloud tools are not ready.", }; } function health(input?: { usable?: boolean; usableByCurrentModel?: boolean | null; firstFailure?: OpenworkCloudMcpFailure | null; projectionSource?: "experimental_tool" | "provider_capability"; projectionChecked?: boolean; modelExists?: boolean; toolCalling?: boolean | null; }): OpenworkCloudMcpHealth { const usable = input?.usable ?? true; const projectionSource = input?.projectionSource ?? "experimental_tool"; const projected = usable ? ["openwork-cloud_search_capabilities", "openwork-cloud_execute_capability"] : []; const projectionPresent = projectionSource === "experimental_tool" ? projected : []; const direct = usable ? ["search_capabilities", "execute_capability"] : []; return { schemaVersion: 1, phase: usable ? "ready" : "registration_failed", usable, usableByCurrentModel: input?.usableByCurrentModel === undefined ? usable : input.usableByCurrentModel, connectCatalogEnabled: true, workspace: { id: "workspace_1", type: "local", directory: "/workspace", path: "/workspace" }, desired: { present: true, name: "openwork-cloud", revision: "rev_1", config: { type: "remote", enabled: true }, token: { present: true, metadata: {} }, }, delivery: { state: usable ? "ready" : "failed", desiredRevision: "rev_1", appliedRevision: usable ? "rev_1" : null, updatedAt: 1, appliedAt: usable ? 1 : null, lastAttemptAt: 1, }, engine: { status: usable ? "connected" : "missing" }, tools: { expected: ["openwork-cloud_search_capabilities", "openwork-cloud_execute_capability"], present: projected, missing: usable ? [] : ["openwork-cloud_search_capabilities", "openwork-cloud_execute_capability"], direct: { checked: true, source: "mcp_tools_list", expected: ["search_capabilities", "execute_capability"], present: direct, missing: usable ? [] : ["search_capabilities", "execute_capability"], }, providerProjection: { checked: input?.projectionChecked ?? true, provider: PROVIDER_MODEL.provider, model: PROVIDER_MODEL.model, source: projectionSource, ...(projectionSource === "provider_capability" ? { limitation: "Only generic model tool-call capability is available.", modelExists: input?.modelExists ?? true, toolCalling: input?.toolCalling === undefined ? true : input.toolCalling, } : {}), present: projectionPresent, missing: projectionPresent.length ? [] : ["openwork-cloud_search_capabilities", "openwork-cloud_execute_capability"], }, }, pluginCanaries: { expected: ["openwork_docs_search"], present: usable ? ["openwork_docs_search"] : [], missing: usable ? [] : ["openwork_docs_search"] }, compatibility: { openwork: { serverVersion: "test", app: null }, opencode: { expectedVersion: "test", actualVersion: "test", probe: "ok" }, pluginFileHashes: [], supportedFeatures: { dynamicMcp: true, directoryScoping: true, toolIds: true, providerToolProjection: projectionSource === "experimental_tool", pluginCanaries: true }, experimentalToolIds: { checked: true, expected: ["openwork-cloud_search_capabilities", "openwork-cloud_execute_capability"], present: projected, missing: usable ? [] : ["openwork-cloud_search_capabilities", "openwork-cloud_execute_capability"], includesMcpTools: usable, }, experimentalProviderTools: { checked: true, provider: PROVIDER_MODEL.provider, model: PROVIDER_MODEL.model, expected: ["openwork-cloud_search_capabilities", "openwork-cloud_execute_capability"], present: projected, missing: usable ? [] : ["openwork-cloud_search_capabilities", "openwork-cloud_execute_capability"], includesMcpTools: projectionSource === "experimental_tool" && usable, }, }, toolDenies: [], firstFailure: usable ? input?.firstFailure ?? null : input?.firstFailure ?? failure(), checkedAt: "2026-07-15T00:00:00.000Z", }; } function requiredDecision(input?: { authStatus?: "checking" | "signed_in" | "unavailable" | "signed_out"; hasSessionToken?: boolean; userState?: "disabled" | "removed" | null; workspaceId?: string; model?: string; }) { return decideCloudMcpSubmissionGate({ cloudAuthStatus: input?.authStatus ?? "signed_in", cloudHasSessionToken: input?.hasSessionToken ?? true, denBaseUrl: "https://app.openwork.test", serverBaseUrl: "https://worker.openwork.test", orgId: "org_1", workspaceId: input?.workspaceId ?? "workspace_1", providerModel: { ...PROVIDER_MODEL, model: input?.model ?? PROVIDER_MODEL.model }, userState: input?.userState ?? null, }); } function preparation(input: { check: () => Promise; repair: () => Promise; }): () => Promise { return async () => { const result = await ensureCloudMcpSubmissionReadiness({ providerModel: PROVIDER_MODEL, check: input.check, repair: input.repair, retryDelaysMs: [0], attemptTimeoutMs: 0, }); if (result.outcome === "ready") return { outcome: "ready" }; if (result.outcome === "bypass") return { outcome: "bypass" }; return { outcome: "failed", issue: result.issue }; }; } describe("Cloud MCP pre-send readiness", () => { test("a successful provider refresh clears only a stale failure", () => { const failed = { status: "failed" as const, issue: failure({ code: "provider_tool_projection_missing" }), attempt: 3, maxAttempts: 3, }; const checking = { ...failed, status: "checking" as const }; expect(clearCloudMcpSubmissionFailure(failed)).toEqual(IDLE_CLOUD_MCP_SUBMISSION_GATE_STATE); expect(clearCloudMcpSubmissionFailure(checking)).toBe(checking); }); test("ready projected tools send immediately", async () => { const coordinator = createCloudMcpSubmissionCoordinator(); const decision = requiredDecision(); let checks = 0; let repairs = 0; let runs = 0; const result = await coordinator.submit({ scopeKey: decision.scopeKey, prepare: preparation({ check: async () => { checks += 1; return health(); }, repair: async () => { repairs += 1; return health(); }, }), send: async () => { runs += 1; }, }); expect(result).toEqual({ outcome: "sent", bypassed: false }); expect({ checks, repairs, runs }).toEqual({ checks: 1, repairs: 0, runs: 1 }); }); test("a transient startup failure repairs and sends exactly once", async () => { const coordinator = createCloudMcpSubmissionCoordinator(); const decision = requiredDecision(); let repairs = 0; let runs = 0; const result = await coordinator.submit({ scopeKey: decision.scopeKey, prepare: preparation({ check: async () => health({ usable: false, firstFailure: failure({ retryable: true }) }), repair: async () => { repairs += 1; return health(); }, }), send: async () => { runs += 1; }, }); expect(result).toEqual({ outcome: "sent", bypassed: false }); expect(repairs).toBe(1); expect(runs).toBe(1); }); test("a cached Cloud session waits for auth restoration and sends exactly once", async () => { const coordinator = createCloudMcpSubmissionCoordinator(); const checking = requiredDecision({ authStatus: "checking", hasSessionToken: true }); const signedIn = requiredDecision(); expect(checking.mode).toBe("waiting_for_auth"); expect(checking.scopeKey).toBe(signedIn.scopeKey); let resolveAuth: ((decision: CloudMcpSubmissionGateDecision) => void) | null = null; const authResolution = new Promise((resolve) => { resolveAuth = resolve; }); let checks = 0; let runs = 0; const input = { scopeKey: checking.scopeKey, prepare: async (): Promise => { const resolution = await resolveCloudMcpSubmissionAuth({ decision: checking, waitForResolution: () => authResolution, timeoutMs: 0, }); if (resolution.outcome === "failed") { return { outcome: "failed", issue: resolution.issue }; } if (resolution.decision.mode === "bypass") return { outcome: "bypass" }; if (resolution.decision.mode !== "required") { return { outcome: "cancelled", reason: "context_changed" }; } return preparation({ check: async () => { checks += 1; return health(); }, repair: async () => health(), })(); }, send: async () => { runs += 1; }, }; const first = coordinator.submit(input); const second = coordinator.submit(input); expect(second).toBe(first); expect({ checks, runs }).toEqual({ checks: 0, runs: 0 }); resolveAuth?.(signedIn); await expect(first).resolves.toEqual({ outcome: "sent", bypassed: false }); await expect(second).resolves.toEqual({ outcome: "sent", bypassed: false }); expect({ checks, runs }).toEqual({ checks: 1, runs: 1 }); }); test("an auth restoration timeout creates no run", async () => { const coordinator = createCloudMcpSubmissionCoordinator(); const checking = requiredDecision({ authStatus: "checking", hasSessionToken: true }); let runs = 0; const result = await coordinator.submit({ scopeKey: checking.scopeKey, prepare: async () => { const resolution = await resolveCloudMcpSubmissionAuth({ decision: checking, waitForResolution: () => new Promise(() => undefined), timeoutMs: 1, }); if (resolution.outcome === "failed") { return { outcome: "failed", issue: resolution.issue }; } return { outcome: "ready" }; }, send: async () => { runs += 1; }, }); expect(result).toMatchObject({ outcome: "blocked", issue: { code: "cloud_mcp_auth_resolution_timeout" }, }); expect(runs).toBe(0); }); test("a permanent injection failure creates no run and preserves the exact draft", async () => { const coordinator = createCloudMcpSubmissionCoordinator(); const decision = requiredDecision(); const originalDraft = { text: "Search my connected services", attachments: [{ id: "attachment_1", name: "brief.pdf" }], }; let storedDraft = originalDraft; let runs = 0; const result = await coordinator.submit({ scopeKey: decision.scopeKey, prepare: preparation({ check: async () => health({ usable: false, firstFailure: failure({ code: "provider_tool_projection_missing", stage: "provider_projection", retryable: false, message: "The selected model is missing an injected Cloud tool.", }), }), repair: async () => health(), }), send: async () => { runs += 1; storedDraft = { text: "", attachments: [] }; }, }); expect(result).toMatchObject({ outcome: "blocked", issue: { code: "provider_tool_projection_missing" } }); expect(runs).toBe(0); expect(storedDraft).toEqual(originalDraft); }); test("provider tool-call capability plus verified direct tools is accepted as projection proof", async () => { const coordinator = createCloudMcpSubmissionCoordinator(); const decision = requiredDecision(); let checks = 0; let repairs = 0; let runs = 0; const result = await coordinator.submit({ scopeKey: decision.scopeKey, prepare: preparation({ check: async () => { checks += 1; return health({ projectionSource: "provider_capability", usableByCurrentModel: true }); }, repair: async () => { repairs += 1; return health(); }, }), send: async () => { runs += 1; }, }); expect(result).toEqual({ outcome: "sent", bypassed: false }); expect({ checks, repairs, runs }).toEqual({ checks: 1, repairs: 0, runs: 1 }); }); test("provider capability without model tool calling is blocked without retry", async () => { const coordinator = createCloudMcpSubmissionCoordinator(); const decision = requiredDecision(); let runs = 0; const result = await coordinator.submit({ scopeKey: decision.scopeKey, prepare: preparation({ check: async () => health({ projectionSource: "provider_capability", usableByCurrentModel: false, modelExists: true, toolCalling: false, }), repair: async () => health(), }), send: async () => { runs += 1; }, }); expect(result).toMatchObject({ outcome: "blocked", issue: { code: "provider_tool_calling_unavailable", retryable: false, message: "The selected model does not support tool calling.", }, }); expect(runs).toBe(0); }); test("an unchecked provider projection is blocked", async () => { const coordinator = createCloudMcpSubmissionCoordinator(); const decision = requiredDecision(); let runs = 0; const unchecked = health({ projectionChecked: false }); const result = await coordinator.submit({ scopeKey: decision.scopeKey, prepare: preparation({ check: async () => unchecked, repair: async () => unchecked, }), send: async () => { runs += 1; }, }); expect(result).toMatchObject({ outcome: "blocked", issue: { code: "provider_tool_projection_unverified", retryable: true }, }); expect(runs).toBe(0); }); test("repeated clicks share one queued submission and cannot duplicate the send", async () => { const coordinator = createCloudMcpSubmissionCoordinator(); const decision = requiredDecision(); let release: ((result: CloudMcpSubmissionPreparationResult) => void) | null = null; const pending = new Promise((resolve) => { release = resolve; }); let runs = 0; const input = { scopeKey: decision.scopeKey, prepare: () => pending, send: async () => { runs += 1; }, }; const first = coordinator.submit(input); const second = coordinator.submit(input); expect(second).toBe(first); release?.({ outcome: "ready" }); await Promise.all([first, second]); expect(runs).toBe(1); }); test("signed-out, tokenless startup, and explicitly disabled or removed Cloud bypass readiness entirely", async () => { const coordinator = createCloudMcpSubmissionCoordinator(); let preparations = 0; let runs = 0; for (const decision of [ requiredDecision({ authStatus: "signed_out", hasSessionToken: false }), requiredDecision({ authStatus: "checking", hasSessionToken: false }), requiredDecision({ userState: "disabled" }), requiredDecision({ userState: "removed" }), ]) { await coordinator.submit({ scopeKey: decision.scopeKey, ...(decision.mode === "required" ? { prepare: async () => { preparations += 1; return { outcome: "ready" }; }, } : {}), send: async () => { runs += 1; }, }); } expect(preparations).toBe(0); expect(runs).toBe(4); }); test("workspace or model changes cannot release an old queued message", async () => { const coordinator = createCloudMcpSubmissionCoordinator(); const original = requiredDecision({ workspaceId: "workspace_a", model: "gpt-5" }); const replacement = requiredDecision({ workspaceId: "workspace_b", model: "gpt-5-mini" }); let releaseOriginal: ((result: CloudMcpSubmissionPreparationResult) => void) | null = null; const originalPreparation = new Promise((resolve) => { releaseOriginal = resolve; }); const sentContexts: string[] = []; const originalResult = coordinator.submit({ scopeKey: original.scopeKey, prepare: () => originalPreparation, send: async () => { sentContexts.push("workspace_a/gpt-5"); }, }); const replacementResult = coordinator.submit({ scopeKey: replacement.scopeKey, prepare: async () => ({ outcome: "ready" }), send: async () => { sentContexts.push("workspace_b/gpt-5-mini"); }, }); releaseOriginal?.({ outcome: "ready" }); await expect(originalResult).resolves.toEqual({ outcome: "cancelled", reason: "context_changed" }); await expect(replacementResult).resolves.toEqual({ outcome: "sent", bypassed: false }); expect(sentContexts).toEqual(["workspace_b/gpt-5-mini"]); }); });