231 lines
8.8 KiB
Python
231 lines
8.8 KiB
Python
"""专项估价指南:分类筛选、占位、分页、未发布不可见。"""
|
|
|
|
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_guides(client, staff: dict[str, str]) -> None:
|
|
listed = client.get("/api/admin/about/articles/appraisal_guides", 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_appraisal_guides_kind_filter_placeholder_and_pagination(client):
|
|
if not _minio_ready():
|
|
pytest.skip("MinIO 不可达")
|
|
staff = _headers("staff")
|
|
article_ids: list[int] = []
|
|
file_ids: list[int] = []
|
|
try:
|
|
_clear_guides(client, staff)
|
|
|
|
empty = client.get("/api/site/about/articles/appraisal_guides")
|
|
assert empty.status_code == 200, empty.text
|
|
assert "暂无估价指南" in empty.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\nguide", "application/pdf")},
|
|
)
|
|
assert pdf.status_code == 200, pdf.text
|
|
file_ids.append(pdf.json()["id"])
|
|
|
|
missing_kind = client.post(
|
|
"/api/admin/home/articles",
|
|
headers=staff,
|
|
json={
|
|
"category": "appraisal_guides",
|
|
"title": "缺分类指南",
|
|
"published": True,
|
|
"bodyHtml": "<p>x</p>",
|
|
"attachmentFileIds": [],
|
|
},
|
|
)
|
|
assert missing_kind.status_code == 400
|
|
|
|
residential = client.post(
|
|
"/api/admin/home/articles",
|
|
headers=staff,
|
|
json={
|
|
"category": "appraisal_guides",
|
|
"kind": "residential",
|
|
"title": "住宅估价服务指南",
|
|
"publishedAt": "2025-06-01T08:00:00",
|
|
"published": True,
|
|
"bodyHtml": "<p>住宅流程</p><script>alert(1)</script>",
|
|
"imageFileId": file_ids[0],
|
|
"attachmentFileIds": [file_ids[1]],
|
|
},
|
|
)
|
|
assert residential.status_code == 200, residential.text
|
|
article_ids.append(residential.json()["id"])
|
|
|
|
commercial = client.post(
|
|
"/api/admin/home/articles",
|
|
headers=staff,
|
|
json={
|
|
"category": "appraisal_guides",
|
|
"kind": "commercial",
|
|
"title": "商业估价服务指南",
|
|
"publishedAt": "2025-06-02T08:00:00",
|
|
"published": True,
|
|
"bodyHtml": "<p>商业流程</p>",
|
|
"attachmentFileIds": [],
|
|
},
|
|
)
|
|
assert commercial.status_code == 200, commercial.text
|
|
article_ids.append(commercial.json()["id"])
|
|
|
|
draft = client.post(
|
|
"/api/admin/home/articles",
|
|
headers=staff,
|
|
json={
|
|
"category": "appraisal_guides",
|
|
"kind": "industrial",
|
|
"title": "工业估价内部稿",
|
|
"publishedAt": "2025-06-03T08:00:00",
|
|
"published": False,
|
|
"bodyHtml": "<p>内部</p>",
|
|
"attachmentFileIds": [],
|
|
},
|
|
)
|
|
assert draft.status_code == 200, draft.text
|
|
article_ids.append(draft.json()["id"])
|
|
|
|
all_list = client.get("/api/site/about/articles/appraisal_guides")
|
|
assert all_list.status_code == 200, all_list.text
|
|
titles = [item["title"] for item in all_list.json()["items"]]
|
|
assert "住宅估价服务指南" in titles
|
|
assert "商业估价服务指南" in titles
|
|
assert "工业估价内部稿" not in titles
|
|
residential_item = next(item for item in all_list.json()["items"] if "住宅" in item["title"])
|
|
assert residential_item["kind"] == "residential"
|
|
assert residential_item["kindLabel"] == "住宅"
|
|
assert residential_item.get("imageUrl")
|
|
|
|
only_residential = client.get("/api/site/about/articles/appraisal_guides?kind=residential")
|
|
assert only_residential.status_code == 200, only_residential.text
|
|
assert [item["title"] for item in only_residential.json()["items"]] == ["住宅估价服务指南"]
|
|
|
|
only_commercial = client.get("/api/site/about/articles/appraisal_guides?kind=commercial")
|
|
assert [item["title"] for item in only_commercial.json()["items"]] == ["商业估价服务指南"]
|
|
|
|
empty_specialty = client.get("/api/site/about/articles/appraisal_guides?kind=specialty")
|
|
assert empty_specialty.status_code == 200, empty_specialty.text
|
|
assert "暂无估价指南" in empty_specialty.text
|
|
|
|
bad_kind = client.get("/api/site/about/articles/appraisal_guides?kind=unknown")
|
|
assert bad_kind.status_code == 404
|
|
|
|
detail = client.get(f"/api/site/home/articles/{article_ids[0]}")
|
|
assert detail.status_code == 200, detail.text
|
|
assert detail.json()["kind"] == "residential"
|
|
assert detail.json()["kindLabel"] == "住宅"
|
|
assert "住宅流程" in detail.json()["bodyHtml"]
|
|
assert "script" not in detail.json()["bodyHtml"].lower()
|
|
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[2]}")
|
|
assert hidden.status_code == 404
|
|
|
|
for index in range(10):
|
|
created = client.post(
|
|
"/api/admin/home/articles",
|
|
headers=staff,
|
|
json={
|
|
"category": "appraisal_guides",
|
|
"kind": "specialty",
|
|
"title": f"特色地产指南-{index:02d}",
|
|
"publishedAt": f"2025-01-{index + 1:02d}T08:00:00",
|
|
"published": True,
|
|
"bodyHtml": f"<p>特色-{index:02d}</p>",
|
|
"attachmentFileIds": [],
|
|
},
|
|
)
|
|
assert created.status_code == 200, created.text
|
|
article_ids.append(created.json()["id"])
|
|
|
|
page1 = client.get("/api/site/about/articles/appraisal_guides?kind=specialty¤t=1&size=10")
|
|
assert page1.status_code == 200, page1.text
|
|
assert page1.json()["total"] == 10
|
|
assert page1.json()["size"] == 10
|
|
assert len(page1.json()["items"]) == 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 "appraisal_guides" not in categories
|
|
|
|
rejected = client.post(
|
|
"/api/admin/home/articles",
|
|
headers=_headers("member"),
|
|
json={
|
|
"category": "appraisal_guides",
|
|
"kind": "residential",
|
|
"title": "会员不得发布指南",
|
|
"published": True,
|
|
"bodyHtml": "<p>x</p>",
|
|
"attachmentFileIds": [],
|
|
},
|
|
)
|
|
assert rejected.status_code in (401, 403)
|
|
assert "会员不得发布指南" not in client.get("/api/site/about/articles/appraisal_guides").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)
|