"""专家委员会:工作人员写入 → 门户公开读取。""" import pytest from fastapi.testclient import TestClient from app.core.config import get_settings from app.core.security import create_access_token @pytest.fixture def client(monkeypatch): monkeypatch.setenv("JWT_SECRET", "test-secret-must-be-32-characters!") monkeypatch.setenv("API_PAYLOAD_ENCRYPT", "false") get_settings.cache_clear() if not get_settings().database_url.strip(): pytest.skip("DATABASE_URL 为空") from app.main import create_app with TestClient(create_app()) as api: yield api get_settings.cache_clear() def _headers(token_type: str) -> dict[str, str]: return {"Authorization": f"Bearer {create_access_token('1', token_type)}"} def _minio_ready() -> bool: try: from app.services.storage import ensure_buckets ensure_buckets() return True except Exception: return False def test_expert_committees_list_detail_delete_and_placeholder(client): if not _minio_ready(): pytest.skip("MinIO 不可达") staff = _headers("staff") ids: list[int] = [] file_id = None try: empty = client.get("/api/site/about/experts") assert empty.status_code == 200, empty.text assert "暂无专家委员会信息" in empty.text pdf = client.post( "/api/admin/files", headers=staff, data={"bucket": "public"}, files={"file": ("专家名录.pdf", b"%PDF-1.4\nexperts", "application/pdf")}, ) assert pdf.status_code == 200, pdf.text file_id = pdf.json()["id"] first = client.post( "/api/admin/about/experts", headers=staff, json={ "name": "房地产估价专家委员会", "bodyHtml": "

估价技术咨询

", "sortOrder": 1, "published": True, "attachmentFileIds": [file_id], }, ) assert first.status_code == 200, first.text ids.append(first.json()["id"]) second = client.post( "/api/admin/about/experts", headers=staff, json={ "name": "行业自律专家委员会", "bodyHtml": "

自律意见

", "sortOrder": 2, "published": True, "attachmentFileIds": [], }, ) assert second.status_code == 200, second.text ids.append(second.json()["id"]) draft = client.post( "/api/admin/about/experts", headers=staff, json={ "name": "内部筹备组", "bodyHtml": "

内部

", "sortOrder": 0, "published": False, "attachmentFileIds": [], }, ) assert draft.status_code == 200, draft.text ids.append(draft.json()["id"]) listing = client.get("/api/site/about/experts") assert listing.status_code == 200, listing.text names = [item["name"] for item in listing.json()["items"]] assert names == ["房地产估价专家委员会", "行业自律专家委员会"] assert "内部筹备组" not in listing.text detail = client.get(f"/api/site/about/experts/{ids[0]}") assert detail.status_code == 200, detail.text assert "估价技术咨询" in detail.json()["bodyHtml"] lowered = detail.json()["bodyHtml"].lower() assert "script" not in lowered assert "alert" not in lowered assert [item["filename"] for item in detail.json()["attachments"]] == ["专家名录.pdf"] hidden = client.get(f"/api/site/about/experts/{ids[2]}") assert hidden.status_code == 404 deleted = client.delete(f"/api/admin/about/experts/{ids[1]}", headers=staff) assert deleted.status_code == 200, deleted.text ids.remove(ids[1]) after = client.get("/api/site/about/experts") assert "行业自律专家委员会" not in after.text assert "房地产估价专家委员会" in after.text rejected = client.post( "/api/admin/about/experts", headers=_headers("member"), json={ "name": "会员委员会", "bodyHtml": "

x

", "sortOrder": 9, "published": True, "attachmentFileIds": [], }, ) assert rejected.status_code in (401, 403) assert "会员委员会" not in client.get("/api/site/about/experts").text finally: for expert_id in list(ids): client.delete(f"/api/admin/about/experts/{expert_id}", headers=staff) leftover = client.get("/api/site/about/experts") # clean any published leftovers from failed runs admin_list = client.get("/api/admin/about/experts", headers=staff) if admin_list.status_code == 200: for item in admin_list.json().get("items", []): client.delete(f"/api/admin/about/experts/{item['id']}", headers=staff) assert "暂无专家委员会信息" in client.get("/api/site/about/experts").text if file_id is not None: client.delete(f"/api/admin/files/{file_id}", headers=staff)