from fastapi.testclient import TestClient from app.main import app def test_health(): client = TestClient(app) response = client.get("/api/v1/health") assert response.status_code == 200 assert response.json()["status"] == "ok" def test_openapi_available(): client = TestClient(app) response = client.get("/openapi.json") assert response.status_code == 200 assert "/api/v1/health" in response.json()["paths"] assert "/api/v1/test-plans" not in response.json()["paths"] def test_openapi_declares_bearer_authorization_for_protected_operations(): schema = TestClient(app).get("/openapi.json").json() bearer = schema["components"]["securitySchemes"]["BearerAuth"] assert bearer == {"type": "http", "scheme": "bearer"} assert schema["paths"]["/api/v1/providers/{provider_id}/check"]["post"]["security"] == [ {"BearerAuth": []} ] assert schema["paths"]["/api/v1/auth/users"]["get"]["security"] == [{"BearerAuth": []}] assert schema["paths"]["/api/v1/auth/users/{user_id}"]["delete"]["security"] == [ {"BearerAuth": []} ] assert schema["paths"]["/api/v1/auth/me"]["get"]["security"] == [{"BearerAuth": []}] assert schema["paths"]["/api/v1/auth/password"]["patch"]["security"] == [{"BearerAuth": []}] assert "security" not in schema["paths"]["/api/v1/health"]["get"] assert "security" not in schema["paths"]["/api/v1/auth/login"]["post"] def test_self_service_auth_openapi_is_documented(): schema = TestClient(app).get("/openapi.json").json() assert "is_admin" in schema["components"]["schemas"]["UserResponse"]["properties"] change = schema["components"]["schemas"]["ChangePasswordRequest"] assert set(change["required"]) == {"current_password", "new_password"} assert "重新登录" in schema["paths"]["/api/v1/auth/password"]["patch"]["description"] def test_provider_check_openapi_is_documented(): schema = TestClient(app).get("/openapi.json").json() operation = schema["paths"]["/api/v1/providers/{provider_id}/check"]["post"] assert "/api/v1/providers/check" not in schema["paths"] assert "模型提供商" in operation["summary"] assert ( operation["responses"]["502"]["content"]["application/json"]["schema"]["$ref"] == "#/components/schemas/ErrorResponse" ) provider = operation["parameters"][0]["schema"] assert provider["enum"] == ["target", "judge"] def test_capability_probe_is_internal_to_runs(): schema = TestClient(app).get("/openapi.json").json() assert "/api/v1/capabilities/probe" not in schema["paths"] assert "能力探测" in schema["paths"]["/api/v1/runs"]["post"]["description"] assert {"get", "post"} <= set(schema["paths"]["/api/v1/runs"]) assert {"get", "patch", "delete"} <= set(schema["paths"]["/api/v1/runs/{run_id}"]) assert "/api/v1/runs/{run_id}/retry-errors" in schema["paths"] assert "/api/v1/runs/{run_id}/results/{execution_id}/retry" in schema["paths"] def test_start_run_openapi_explains_usage(): schema = TestClient(app).get("/openapi.json").json() operation = schema["paths"]["/api/v1/runs"]["post"] response = schema["components"]["schemas"]["RunResponse"] assert '"profile": "smoke"' in operation["description"] assert "单轮、多轮、工具和图片各选取 1 条" in operation["description"] assert ( "单轮、多轮、工具和图片各执行 1 条" in schema["components"]["schemas"]["StartRunRequest"]["properties"]["profile"][ "description" ] ) assert "completed_with_errors" in operation["description"] assert "REQUEST_RETRY_COUNT" in operation["description"] assert ( "completed_count + error_count" in schema["paths"]["/api/v1/runs/{run_id}"]["get"]["description"] ) assert "terminal" in schema["paths"]["/api/v1/runs/{run_id}"]["get"]["description"] assert ( "needs_human_review" in schema["paths"]["/api/v1/runs/{run_id}/results"]["get"]["description"] ) assert "model_input" in schema["components"]["schemas"]["ResultItem"]["properties"] result_properties = schema["components"]["schemas"]["ResultItem"]["properties"] assert {"execution_status", "verdict"} <= set(result_properties) assert "status" not in result_properties assert "/runs/{run_id}/results" in operation["description"] assert response["examples"][0]["run_id"] == 42 assert response["examples"][0]["status"] == "pending" assert "202" in operation["responses"] assert "Idempotency-Key" in operation["description"] assert "409" in operation["responses"] resume = schema["paths"]["/api/v1/runs/{run_id}/resume"]["post"] assert "不会再次请求模型" in resume["description"] assert "skipped_count" in schema["components"]["schemas"]["ResumeRunResponse"]["properties"] def test_reports_openapi_declares_derived_read_only_resource(): schema = TestClient(app).get("/openapi.json").json() assert set(schema["paths"]["/api/v1/reports"]) == {"get"} assert set(schema["paths"]["/api/v1/reports/{run_id}"]) == {"get"} assert "实时计算" in schema["paths"]["/api/v1/reports"]["get"]["description"] assert "404" in schema["paths"]["/api/v1/reports/{run_id}"]["get"]["responses"]