Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/app/api/setup-status/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ function check(id: SetupCheckId, label: string, status: SetupCheckStatus, detail
return { id, label, status, detail };
}

function projectSetupCheckStatus(status: ReturnType<typeof checkSupabaseProjectConfig>["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();
Expand Down Expand Up @@ -296,7 +300,7 @@ async function buildSetupStatusPayload(): Promise<SetupStatusPayload> {
check(
"project",
"Clinical KB Database target",
supabaseProjectCheck.status === "ready" ? "ready" : "needs_setup",
projectSetupCheckStatus(supabaseProjectCheck.status),
formatSupabaseProjectCheck(supabaseProjectCheck),
),
check(
Expand Down Expand Up @@ -353,7 +357,7 @@ async function buildSetupStatusPayload(): Promise<SetupStatusPayload> {
check(
"project",
"Clinical KB Database target",
supabaseProjectCheck.status === "ready" ? "ready" : "needs_setup",
projectSetupCheckStatus(supabaseProjectCheck.status),
formatSupabaseProjectCheck(supabaseProjectCheck),
),
schema,
Expand Down
41 changes: 41 additions & 0 deletions tests/setup-status-route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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" })]));
});
});
35 changes: 17 additions & 18 deletions tests/ui-smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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." } });
Expand Down
Loading