"""技术交流两类栏目文章:工作人员写入 → 门户公开读取。""" 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 _clear_category(client, staff: dict[str, str], category: str) -> None: listed = client.get(f"/api/admin/about/articles/{category}", headers=staff) assert listed.status_code == 200, listed.text for item in listed.json().get("items") or []: deleted = client.delete(f"/api/admin/home/articles/{item['id']}", headers=staff) assert deleted.status_code in (200, 204), deleted.text def _png() -> bytes: return ( b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01" b"\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDATx\x9cc\xf8\x0f" b"\x00\x00\x01\x01\x00\x05\x18\xd8N\x00\x00\x00\x00IEND\xaeB`\x82" ) def test_tech_exchange_publish_filter_placeholder_cover_and_pagination(client): if not _minio_ready(): pytest.skip("MinIO 不可达") staff = _headers("staff") article_ids: list[int] = [] file_ids: list[int] = [] try: for category in ("tech_seminars", "tech_exchange"): _clear_category(client, staff, category) empty_seminars = client.get("/api/site/about/articles/tech_seminars") assert empty_seminars.status_code == 200, empty_seminars.text assert "暂无技术研讨" in empty_seminars.text assert "暂无技术交流" in client.get("/api/site/about/articles/tech_exchange").text cover = client.post( "/api/admin/files", headers=staff, data={"bucket": "public"}, files={"file": ("cover.png", _png(), "image/png")}, ) assert cover.status_code == 200, cover.text file_ids.append(cover.json()["id"]) pdf = client.post( "/api/admin/files", headers=staff, data={"bucket": "public"}, files={"file": ("研讨通知.pdf", b"%PDF-1.4\nnotice", "application/pdf")}, ) assert pdf.status_code == 200, pdf.text file_ids.append(pdf.json()["id"]) award = client.post( "/api/admin/files", headers=staff, data={"bucket": "public"}, files={"file": ("获奖名单公示.pdf", b"%PDF-1.4\nawards", "application/pdf")}, ) assert award.status_code == 200, award.text file_ids.append(award.json()["id"]) seminar = client.post( "/api/admin/home/articles", headers=staff, json={ "category": "tech_seminars", "title": "城市更新中的房地产估价服务专题研讨", "publishedAt": "2025-11-08T08:00:00", "published": True, "bodyHtml": "

城市更新研讨

", "imageFileId": file_ids[0], "attachmentFileIds": [file_ids[1]], }, ) assert seminar.status_code == 200, seminar.text article_ids.append(seminar.json()["id"]) draft = client.post( "/api/admin/home/articles", headers=staff, json={ "category": "tech_seminars", "title": "内部筹备研讨", "publishedAt": "2025-11-09T08:00:00", "published": False, "bodyHtml": "

内部

", "attachmentFileIds": [], }, ) assert draft.status_code == 200, draft.text article_ids.append(draft.json()["id"]) exchange = client.post( "/api/admin/home/articles", headers=staff, json={ "category": "tech_exchange", "title": "2018年度优秀报告评选表彰与实践分享(公示)", "publishedAt": "2019-04-26T08:00:00", "published": True, "bodyHtml": "

优秀报告公示

", "attachmentFileIds": [file_ids[2]], }, ) assert exchange.status_code == 200, exchange.text article_ids.append(exchange.json()["id"]) seminars = client.get("/api/site/about/articles/tech_seminars") assert seminars.status_code == 200, seminars.text assert "城市更新中的房地产估价服务专题研讨" in seminars.text assert "内部筹备研讨" not in seminars.text assert seminars.json()["items"][0].get("imageUrl") detail = client.get(f"/api/site/home/articles/{article_ids[0]}") assert detail.status_code == 200, detail.text body = detail.json()["bodyHtml"].lower() assert "城市更新研讨" in detail.json()["bodyHtml"] assert "script" not in body assert "alert" not in body assert detail.json().get("imageUrl") assert [item["filename"] for item in detail.json()["attachments"]] == ["研讨通知.pdf"] hidden = client.get(f"/api/site/home/articles/{article_ids[1]}") assert hidden.status_code == 404 exchanges = client.get("/api/site/about/articles/tech_exchange") assert exchanges.status_code == 200, exchanges.text assert "2018年度优秀报告评选表彰与实践分享(公示)" in exchanges.text exchange_detail = client.get(f"/api/site/home/articles/{article_ids[2]}") assert exchange_detail.status_code == 200, exchange_detail.text assert [item["filename"] for item in exchange_detail.json()["attachments"]] == ["获奖名单公示.pdf"] for index in range(10): created = client.post( "/api/admin/home/articles", headers=staff, json={ "category": "tech_seminars", "title": f"研讨分页样例-{index:02d}", "publishedAt": f"2025-01-{index + 1:02d}T08:00:00", "published": True, "bodyHtml": f"

研讨分页样例-{index:02d}

", "attachmentFileIds": [], }, ) assert created.status_code == 200, created.text article_ids.append(created.json()["id"]) page1 = client.get("/api/site/about/articles/tech_seminars?current=1&size=10") assert page1.status_code == 200, page1.text assert page1.json()["total"] == 11 assert page1.json()["size"] == 10 assert len(page1.json()["items"]) == 10 page2 = client.get("/api/site/about/articles/tech_seminars?current=2&size=10") assert page2.status_code == 200, page2.text assert len(page2.json()["items"]) == 1 size20 = client.get("/api/site/about/articles/tech_seminars?current=1&size=20") assert size20.json()["size"] == 20 assert len(size20.json()["items"]) == 11 bad_size = client.get("/api/site/about/articles/tech_seminars?current=1&size=15") assert bad_size.json()["size"] == 10 home = client.get("/api/admin/home", headers=staff) assert home.status_code == 200, home.text categories = {item["category"] for item in home.json()["articles"]} assert "tech_seminars" not in categories assert "tech_exchange" not in categories rejected = client.post( "/api/admin/home/articles", headers=_headers("member"), json={ "category": "tech_seminars", "title": "会员不得发布研讨", "published": True, "bodyHtml": "

x

", "attachmentFileIds": [], }, ) assert rejected.status_code in (401, 403) assert "会员不得发布研讨" not in client.get("/api/site/about/articles/tech_seminars").text finally: for article_id in article_ids: client.delete(f"/api/admin/home/articles/{article_id}", headers=staff) for file_id in file_ids: client.delete(f"/api/admin/files/{file_id}", headers=staff)