diff --git a/src/app/api/setup-status/route.ts b/src/app/api/setup-status/route.ts index ba34855b8..081568601 100644 --- a/src/app/api/setup-status/route.ts +++ b/src/app/api/setup-status/route.ts @@ -51,6 +51,10 @@ function check(id: SetupCheckId, label: string, status: SetupCheckStatus, detail return { id, label, status, detail }; } +function projectSetupCheckStatus(status: ReturnType["status"]) { + return status === "ready" || status === "warning" ? "ready" : "needs_setup"; +} + async function readSupabaseAvailability(supabase: AdminClient | null) { if (!requiredSupabaseEnvPresent || !supabaseProjectCanBeQueried || !supabase) return null; const now = Date.now(); @@ -296,7 +300,7 @@ async function buildSetupStatusPayload(): Promise { check( "project", "Clinical KB Database target", - supabaseProjectCheck.status === "ready" ? "ready" : "needs_setup", + projectSetupCheckStatus(supabaseProjectCheck.status), formatSupabaseProjectCheck(supabaseProjectCheck), ), check( @@ -353,7 +357,7 @@ async function buildSetupStatusPayload(): Promise { check( "project", "Clinical KB Database target", - supabaseProjectCheck.status === "ready" ? "ready" : "needs_setup", + projectSetupCheckStatus(supabaseProjectCheck.status), formatSupabaseProjectCheck(supabaseProjectCheck), ), schema, diff --git a/tests/setup-status-route.test.ts b/tests/setup-status-route.test.ts index f952b2ef8..aa7014c2f 100644 --- a/tests/setup-status-route.test.ts +++ b/tests/setup-status-route.test.ts @@ -52,4 +52,45 @@ describe("/api/setup-status", () => { expect(JSON.stringify(body)).not.toContain("service-role-key"); expect(JSON.stringify(body)).not.toContain("openai-key"); }); + + it("treats project warning status as ready when the URL ref matches", async () => { + vi.stubEnv("NODE_ENV", "production"); + const from = vi.fn(async () => ({ error: null, data: [], count: 0 })); + const createAdminClient = vi.fn(() => ({ + from, + rpc: vi.fn(), + })); + vi.doMock("@/lib/env", () => ({ + env: { + NEXT_PUBLIC_SUPABASE_URL: "https://sjrfecxgysukkwxsowpy.supabase.co", + SUPABASE_SERVICE_ROLE_KEY: "service-role-key", + OPENAI_API_KEY: "openai-key", + SUPABASE_DOCUMENT_BUCKET: "clinical-documents", + SUPABASE_IMAGE_BUCKET: "clinical-images", + WORKER_POLL_MS: 1500, + }, + isDemoMode: () => false, + isLocalNoAuthMode: () => false, + })); + vi.doMock("@/lib/supabase/admin", () => ({ createAdminClient })); + vi.doMock("@/lib/supabase/health", () => ({ + probeSupabaseHealth: vi.fn(async () => ({ ok: true })), + isSupabaseUnavailableError: () => false, + formatSupabaseUnavailableError: (error: unknown) => String(error), + })); + vi.doMock("@/lib/supabase/project", () => ({ + checkSupabaseProjectConfig: () => ({ + status: "warning", + detail: 'Set SUPABASE_PROJECT_NAME="Clinical KB Database" in .env.local.', + }), + formatSupabaseProjectCheck: () => 'Set SUPABASE_PROJECT_NAME="Clinical KB Database" in .env.local.', + })); + const { GET } = await import("../src/app/api/setup-status/route"); + + const response = await GET(new Request("https://clinical.example/api/setup-status")); + const body = await response.json(); + + expect(response.status).toBe(200); + expect(body.checks).toEqual(expect.arrayContaining([expect.objectContaining({ id: "project", status: "ready" })])); + }); }); diff --git a/tests/ui-smoke.spec.ts b/tests/ui-smoke.spec.ts index 2eb32ebbf..cf0468ad2 100644 --- a/tests/ui-smoke.spec.ts +++ b/tests/ui-smoke.spec.ts @@ -930,26 +930,25 @@ test.describe("Clinical KB UI smoke coverage", () => { test("private mode unauthenticated dashboard gates real-mode search", async ({ page }) => { await page.setViewportSize({ width: 390, height: 820 }); const answerRequests: string[] = []; + const unsafeLocalProjectPayload = { + appName: "Clinical KB", + projectId: "test-project", + identityPath: "/api/local-project-id", + localServer: { + currentUrl: "http://localhost:4298", + currentPort: 4298, + projectPortStart: 4298, + projectPortEnd: 53210, + safeLocalOrigin: false, + requestOrigin: null, + requestReferer: null, + unsafeLocalCaller: "http://localhost:3000", + }, + }; + await mockPrivateUnauthenticatedApi(page); await page.route(/\/api\/local-project-id$/, async (route) => { - await route.fulfill({ - json: { - appName: "Clinical KB", - projectId: "test-project", - identityPath: "/api/local-project-id", - localServer: { - currentUrl: "http://localhost:4298", - currentPort: 4298, - projectPortStart: 4298, - projectPortEnd: 53210, - safeLocalOrigin: false, - requestOrigin: null, - requestReferer: null, - unsafeLocalCaller: "http://localhost:3000", - }, - }, - }); + await route.fulfill({ json: unsafeLocalProjectPayload }); }); - await mockPrivateUnauthenticatedApi(page); await page.route(/\/api\/answer(?:\/stream)?(?:\?.*)?$/, async (route) => { answerRequests.push(route.request().url()); await route.fulfill({ status: 401, json: { error: "Authentication required." } });