"""资料库:政策分类、标题与正文检索、案例库、未发布不可见。""" 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 _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_library_publish_kind_search_and_cases(client): if not _minio_ready(): pytest.skip("MinIO 不可达") staff = _headers("staff") article_ids: list[int] = [] file_ids: list[int] = [] try: empty_policy = client.get("/api/site/about/articles/policy_analyses") assert empty_policy.status_code == 200, empty_policy.text assert "暂无政策分析" in empty_policy.text empty_cases = client.get("/api/site/about/articles/case_library") assert empty_cases.status_code == 200, empty_cases.text assert "暂无案例库" in empty_cases.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\npolicy", "application/pdf")}, ) assert pdf.status_code == 200, pdf.text file_ids.append(pdf.json()["id"]) published = client.post( "/api/admin/home/articles", headers=staff, json={ "category": "policy_analyses", "kind": "national", "title": "资料库检索样例-民法典导读", "published": True, "publishedAt": "2025-09-12T08:00:00", "bodyHtml": "

本条标注:现行有效。

", "imageFileId": cover.json()["id"], "attachmentFileIds": [pdf.json()["id"]], }, ) assert published.status_code == 200, published.text article_ids.append(published.json()["id"]) local = client.post( "/api/admin/home/articles", headers=staff, json={ "category": "policy_analyses", "kind": "local", "title": "资料库检索样例-地方配套", "published": True, "publishedAt": "2025-03-08T08:00:00", "bodyHtml": "

仅收录本地区政策。

", "attachmentFileIds": [], }, ) assert local.status_code == 200, local.text article_ids.append(local.json()["id"]) hidden = client.post( "/api/admin/home/articles", headers=staff, json={ "category": "policy_analyses", "kind": "expert", "title": "资料库检索样例-未发布解读", "published": False, "bodyHtml": "

正文里有未发布机密词。

", "attachmentFileIds": [], }, ) assert hidden.status_code == 200, hidden.text article_ids.append(hidden.json()["id"]) bad_kind = client.post( "/api/admin/home/articles", headers=staff, json={ "category": "policy_analyses", "kind": "residential", "title": "资料库检索样例-错误分类", "published": True, "bodyHtml": "

x

", "attachmentFileIds": [], }, ) assert bad_kind.status_code == 400 case_with_kind = client.post( "/api/admin/home/articles", headers=staff, json={ "category": "case_library", "kind": "national", "title": "资料库检索样例-案例不分类", "published": True, "bodyHtml": "

案例正文

", "attachmentFileIds": [], }, ) assert case_with_kind.status_code == 400 case = client.post( "/api/admin/home/articles", headers=staff, json={ "category": "case_library", "title": "资料库检索样例-城关区案例", "published": True, "publishedAt": "2025-08-16T08:00:00", "bodyHtml": "

可比案例说明。

", "imageFileId": cover.json()["id"], "attachmentFileIds": [pdf.json()["id"]], }, ) assert case.status_code == 200, case.text article_ids.append(case.json()["id"]) listed = client.get("/api/site/about/articles/policy_analyses") titles = [item["title"] for item in listed.json()["items"]] assert "资料库检索样例-民法典导读" in titles assert "资料库检索样例-未发布解读" not in titles national = next(item for item in listed.json()["items"] if item["title"] == "资料库检索样例-民法典导读") assert national["kind"] == "national" assert national["kindLabel"] == "国家级" assert national["imageUrl"] only_local = client.get("/api/site/about/articles/policy_analyses?kind=local") assert [item["title"] for item in only_local.json()["items"]] == ["资料库检索样例-地方配套"] by_title = client.get("/api/site/about/articles/policy_analyses?q=民法典") assert [item["title"] for item in by_title.json()["items"]] == ["资料库检索样例-民法典导读"] by_body = client.get("/api/site/about/articles/policy_analyses?q=现行有效") assert [item["title"] for item in by_body.json()["items"]] == ["资料库检索样例-民法典导读"] hidden_body = client.get("/api/site/about/articles/policy_analyses?q=未发布机密词") assert hidden_body.json()["placeholder"] == "未找到相关政策" assert hidden_body.json()["items"] == [] unknown = client.get("/api/site/about/articles/policy_analyses?kind=unknown") assert unknown.status_code == 404 cases = client.get("/api/site/about/articles/case_library") assert cases.json()["items"][0]["title"] == "资料库检索样例-城关区案例" assert "kind" not in cases.json()["items"][0] detail = client.get(f"/api/site/home/articles/{national['id']}") assert detail.status_code == 200, detail.text assert "现行有效" in detail.text assert "政策.pdf" in detail.text home = client.get("/api/admin/home", headers=staff) categories = {item["category"] for item in home.json()["articles"]} assert "policy_analyses" not in categories assert "case_library" not in categories rejected = client.post( "/api/admin/home/articles", headers=_headers("member"), json={ "category": "policy_analyses", "kind": "national", "title": "会员不得发布政策", "published": True, "bodyHtml": "

x

", "attachmentFileIds": [], }, ) assert rejected.status_code in (401, 403) 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)