1016 lines
37 KiB
Python
1016 lines
37 KiB
Python
import httpx
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
from fastapi.testclient import TestClient
|
|
from minio.error import S3Error
|
|
import urllib3
|
|
|
|
from app.core.config import get_settings
|
|
from app.core.security import create_access_token
|
|
|
|
_PLACEHOLDERS = (
|
|
"协会定位整理中",
|
|
"价值主张整理中",
|
|
"轮播内容准备中",
|
|
"新闻动态即将发布",
|
|
"暂无新的通知公告",
|
|
"政策法规整理中",
|
|
"数据看板整理中",
|
|
"估价师、机构与公众入口即将开放",
|
|
"报告查询、报告审核与投诉举报入口即将开放",
|
|
"协会荣誉与资质整理中",
|
|
"会员单位名录整理中",
|
|
"办公地址整理中",
|
|
"联系电话整理中",
|
|
"联系邮箱整理中",
|
|
"地图位置整理中",
|
|
"主管部门链接整理中",
|
|
"相关部门链接整理中",
|
|
"合作协会链接整理中",
|
|
"隐私政策、使用条款与网站声明整理中",
|
|
)
|
|
|
|
|
|
@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 _template_state(api: TestClient, headers: dict[str, str]) -> dict:
|
|
body = api.get("/api/admin/home", headers=headers)
|
|
assert body.status_code == 200, body.text
|
|
data = body.json()
|
|
return {
|
|
"activeTemplate": data["activeTemplate"],
|
|
"templates": [
|
|
{
|
|
"name": item["name"],
|
|
"slogan": item["slogan"],
|
|
"valueProposition": item["valueProposition"],
|
|
}
|
|
for item in data["templates"]
|
|
],
|
|
}
|
|
|
|
|
|
def _restore_templates(api: TestClient, headers: dict[str, str], state: dict) -> None:
|
|
api.put("/api/admin/home", headers=headers, json=state)
|
|
|
|
|
|
def _headers(token_type: str) -> dict[str, str]:
|
|
token = create_access_token("1", token_type)
|
|
return {"Authorization": f"Bearer {token}"}
|
|
|
|
|
|
def _write(api: TestClient, headers: dict[str, str]):
|
|
return api.put(
|
|
"/api/admin/home",
|
|
headers=headers,
|
|
json={
|
|
"activeTemplate": "LOGO蓝",
|
|
"templates": [
|
|
{
|
|
"name": "科技蓝",
|
|
"slogan": "科技蓝模板标语",
|
|
"valueProposition": "科技蓝价值主张",
|
|
},
|
|
{
|
|
"name": "LOGO蓝",
|
|
"slogan": "服务甘肃房地产估价",
|
|
"valueProposition": "公开、专业、可核验",
|
|
},
|
|
],
|
|
},
|
|
)
|
|
|
|
|
|
def test_public_home_returns_only_the_active_template(client):
|
|
headers = _headers("staff")
|
|
previous = _template_state(client, headers)
|
|
saved = _write(client, headers)
|
|
assert saved.status_code == 200, saved.text
|
|
|
|
response = client.get("/api/site/home")
|
|
assert response.status_code == 200, response.text
|
|
body = response.json()
|
|
assert body["templateName"] == "LOGO蓝"
|
|
assert body["slogan"] == "服务甘肃房地产估价"
|
|
assert body["valueProposition"] == "公开、专业、可核验"
|
|
text = response.text
|
|
assert "科技蓝模板标语" not in text
|
|
assert "科技蓝价值主张" not in text
|
|
assert "陇ICP备19001227号" in text
|
|
assert "甘公网安备62010202001984号" in text
|
|
for sentence in _PLACEHOLDERS:
|
|
if sentence in ("协会定位整理中", "价值主张整理中"):
|
|
continue
|
|
assert sentence in text
|
|
|
|
before = client.get("/api/site/home").json()
|
|
rejected = _write(client, _headers("member"))
|
|
assert rejected.status_code == 403
|
|
after = client.get("/api/site/home").json()
|
|
assert after["slogan"] == before["slogan"] == "服务甘肃房地产估价"
|
|
assert "科技蓝模板标语" not in client.get("/api/site/home").text
|
|
|
|
settings = client.get("/api/site/settings")
|
|
assert settings.status_code == 200
|
|
assert "templateName" not in settings.json()
|
|
assert "服务甘肃房地产估价" not in settings.text
|
|
|
|
_restore_templates(client, headers, previous)
|
|
|
|
|
|
def _minio_ready() -> bool:
|
|
from app.services.storage import ensure_buckets
|
|
|
|
try:
|
|
ensure_buckets()
|
|
except (urllib3.exceptions.HTTPError, OSError, TimeoutError, HTTPException, S3Error):
|
|
return False
|
|
return True
|
|
|
|
|
|
def test_published_carousel_detail_is_public_and_draft_is_hidden(client):
|
|
if not _minio_ready():
|
|
pytest.skip("MinIO 不可达")
|
|
headers = _headers("staff")
|
|
previous = _template_state(client, headers)
|
|
saved = _write(client, headers)
|
|
assert saved.status_code == 200, saved.text
|
|
image_bytes = b"\x89PNG\r\n\x1a\ncarousel"
|
|
pdf_bytes = b"%PDF-1.4\nplan"
|
|
image_id = None
|
|
pdf_id = None
|
|
slide_id = None
|
|
draft_id = None
|
|
try:
|
|
image = client.post(
|
|
"/api/admin/files",
|
|
headers=headers,
|
|
data={"bucket": "public"},
|
|
files={"file": ("cover.png", image_bytes, "image/png")},
|
|
)
|
|
assert image.status_code == 200, image.text
|
|
image_id = image.json()["id"]
|
|
attachment = client.post(
|
|
"/api/admin/files",
|
|
headers=headers,
|
|
data={"bucket": "public"},
|
|
files={"file": ("计划.pdf", pdf_bytes, "application/pdf")},
|
|
)
|
|
assert attachment.status_code == 200, attachment.text
|
|
pdf_id = attachment.json()["id"]
|
|
|
|
created = client.post(
|
|
"/api/admin/home/slides",
|
|
headers=headers,
|
|
json={
|
|
"title": "估价师继续教育通知",
|
|
"imageFileId": image_id,
|
|
"sortOrder": 1,
|
|
"enabled": True,
|
|
"intervalSeconds": 6,
|
|
"published": True,
|
|
"bodyHtml": "<p>请按计划参加</p><script>alert(1)</script>",
|
|
"attachmentFileIds": [pdf_id],
|
|
},
|
|
)
|
|
assert created.status_code == 200, created.text
|
|
slide_id = created.json()["id"]
|
|
|
|
draft = client.post(
|
|
"/api/admin/home/slides",
|
|
headers=headers,
|
|
json={
|
|
"title": "内部草稿",
|
|
"imageFileId": image_id,
|
|
"sortOrder": 0,
|
|
"enabled": True,
|
|
"intervalSeconds": 6,
|
|
"published": False,
|
|
"bodyHtml": "<p>内部草稿</p>",
|
|
"attachmentFileIds": [],
|
|
},
|
|
)
|
|
assert draft.status_code == 200, draft.text
|
|
draft_id = draft.json()["id"]
|
|
|
|
home = client.get("/api/site/home")
|
|
assert home.status_code == 200, home.text
|
|
assert "内部草稿" not in home.text
|
|
assert "轮播内容准备中" not in home.text
|
|
items = home.json()["carousel"]["items"]
|
|
assert [item["title"] for item in items] == ["估价师继续教育通知"]
|
|
assert items[0]["intervalSeconds"] == 6
|
|
assert items[0]["id"] == slide_id
|
|
image_download = httpx.get(items[0]["imageUrl"], timeout=5.0, follow_redirects=True)
|
|
assert image_download.status_code == 200
|
|
assert image_download.content == image_bytes
|
|
|
|
detail = client.get(f"/api/site/home/slides/{slide_id}")
|
|
assert detail.status_code == 200, detail.text
|
|
body = detail.json()
|
|
assert "请按计划参加" in body["bodyHtml"]
|
|
lowered = body["bodyHtml"].lower()
|
|
assert "script" not in lowered
|
|
assert "alert" not in lowered
|
|
assert body["attachments"] == [
|
|
{"filename": "计划.pdf", "url": body["attachments"][0]["url"]}
|
|
]
|
|
downloaded = httpx.get(body["attachments"][0]["url"], timeout=5.0, follow_redirects=True)
|
|
assert downloaded.status_code == 200
|
|
assert downloaded.content == pdf_bytes
|
|
|
|
hidden = client.get(f"/api/site/home/slides/{draft_id}")
|
|
assert hidden.status_code == 404
|
|
assert "内部草稿" not in hidden.text
|
|
assert "内部草稿" not in client.get("/api/site/home").text
|
|
finally:
|
|
for slide in (slide_id, draft_id):
|
|
if slide is not None:
|
|
client.delete(f"/api/admin/home/slides/{slide}", headers=headers)
|
|
for file_id in (image_id, pdf_id):
|
|
if file_id is not None:
|
|
client.delete(f"/api/admin/files/{file_id}", headers=headers)
|
|
_restore_templates(client, headers, previous)
|
|
|
|
|
|
def test_slide_body_can_be_revised(client):
|
|
if not _minio_ready():
|
|
pytest.skip("MinIO 不可达")
|
|
headers = _headers("staff")
|
|
image_bytes = b"\x89PNG\r\n\x1a\nrevise"
|
|
image_id = None
|
|
pdf_id = None
|
|
slide_id = None
|
|
try:
|
|
image = client.post(
|
|
"/api/admin/files",
|
|
headers=headers,
|
|
data={"bucket": "public"},
|
|
files={"file": ("revise.png", image_bytes, "image/png")},
|
|
)
|
|
assert image.status_code == 200, image.text
|
|
image_id = image.json()["id"]
|
|
created = client.post(
|
|
"/api/admin/home/slides",
|
|
headers=headers,
|
|
json={
|
|
"title": "待改轮播",
|
|
"imageFileId": image_id,
|
|
"sortOrder": 9,
|
|
"enabled": True,
|
|
"intervalSeconds": 6,
|
|
"published": True,
|
|
"bodyHtml": "<p>原来的正文</p>",
|
|
"attachmentFileIds": [],
|
|
},
|
|
)
|
|
assert created.status_code == 200, created.text
|
|
slide_id = created.json()["id"]
|
|
pdf = client.post(
|
|
"/api/admin/files",
|
|
headers=headers,
|
|
data={"bucket": "public"},
|
|
files={"file": ("计划.pdf", b"%PDF-1.4\nplan", "application/pdf")},
|
|
)
|
|
assert pdf.status_code == 200, pdf.text
|
|
pdf_id = pdf.json()["id"]
|
|
updated = client.put(
|
|
f"/api/admin/home/slides/{slide_id}",
|
|
headers=headers,
|
|
json={
|
|
"title": "已改轮播",
|
|
"imageFileId": image_id,
|
|
"sortOrder": 3,
|
|
"enabled": True,
|
|
"intervalSeconds": 8,
|
|
"published": True,
|
|
"bodyHtml": "<p>报名已延期</p><script>alert(1)</script>",
|
|
"attachmentFileIds": [pdf_id],
|
|
},
|
|
)
|
|
assert updated.status_code == 200, updated.text
|
|
revised = client.get(f"/api/site/home/slides/{slide_id}")
|
|
assert revised.status_code == 200, revised.text
|
|
body = revised.json()["bodyHtml"]
|
|
assert "报名已延期" in body
|
|
assert "原来的正文" not in body
|
|
lowered = body.lower()
|
|
assert "script" not in lowered
|
|
assert "alert" not in lowered
|
|
assert [item["filename"] for item in revised.json()["attachments"]] == ["计划.pdf"]
|
|
home = client.get("/api/site/home").json()
|
|
listed = next(item for item in home["carousel"]["items"] if item["id"] == slide_id)
|
|
assert listed["title"] == "已改轮播"
|
|
assert listed["intervalSeconds"] == 8
|
|
editor = client.get("/api/admin/home", headers=headers)
|
|
saved = next(item for item in editor.json()["slides"] if item["id"] == slide_id)
|
|
assert "报名已延期" in saved["bodyHtml"]
|
|
assert [item["filename"] for item in saved["attachments"]] == ["计划.pdf"]
|
|
missing = client.put(
|
|
"/api/admin/home/slides/99999999",
|
|
headers=headers,
|
|
json={
|
|
"title": "不存在",
|
|
"imageFileId": image_id,
|
|
"sortOrder": 1,
|
|
"enabled": True,
|
|
"intervalSeconds": 6,
|
|
"published": False,
|
|
"bodyHtml": "<p>无</p>",
|
|
"attachmentFileIds": [],
|
|
},
|
|
)
|
|
assert missing.status_code == 404
|
|
finally:
|
|
if slide_id is not None:
|
|
client.delete(f"/api/admin/home/slides/{slide_id}", headers=headers)
|
|
for file_id in (image_id, pdf_id):
|
|
if file_id is not None:
|
|
client.delete(f"/api/admin/files/{file_id}", headers=headers)
|
|
|
|
|
|
def test_article_body_and_attachments_can_be_revised(client):
|
|
if not _minio_ready():
|
|
pytest.skip("MinIO 不可达")
|
|
headers = _headers("staff")
|
|
pdf_id = None
|
|
extra_id = None
|
|
article_id = None
|
|
try:
|
|
pdf = client.post(
|
|
"/api/admin/files",
|
|
headers=headers,
|
|
data={"bucket": "public"},
|
|
files={"file": ("办法.pdf", b"%PDF-1.4\nrule", "application/pdf")},
|
|
)
|
|
assert pdf.status_code == 200, pdf.text
|
|
pdf_id = pdf.json()["id"]
|
|
created = client.post(
|
|
"/api/admin/home/articles",
|
|
headers=headers,
|
|
json={
|
|
"category": "policies",
|
|
"title": "待改办法",
|
|
"publishedAt": "2024-03-01T09:00:00",
|
|
"published": True,
|
|
"bodyHtml": "<p>原办法</p>",
|
|
"imageFileId": None,
|
|
"attachmentFileIds": [pdf_id],
|
|
},
|
|
)
|
|
assert created.status_code == 200, created.text
|
|
article_id = created.json()["id"]
|
|
extra = client.post(
|
|
"/api/admin/files",
|
|
headers=headers,
|
|
data={"bucket": "public"},
|
|
files={"file": ("补充.docx", b"PK\x03\x04extra", "application/octet-stream")},
|
|
)
|
|
assert extra.status_code == 200, extra.text
|
|
extra_id = extra.json()["id"]
|
|
updated = client.put(
|
|
f"/api/admin/home/articles/{article_id}",
|
|
headers=headers,
|
|
json={
|
|
"category": "notices",
|
|
"title": "已改通知",
|
|
"publishedAt": "2024-04-02T10:00:00",
|
|
"published": True,
|
|
"bodyHtml": "<p>请下载附件</p><script>alert(1)</script>",
|
|
"imageFileId": None,
|
|
"attachmentFileIds": [pdf_id, extra_id],
|
|
},
|
|
)
|
|
assert updated.status_code == 200, updated.text
|
|
detail = client.get(f"/api/site/home/articles/{article_id}")
|
|
assert detail.status_code == 200, detail.text
|
|
body = detail.json()
|
|
assert body["title"] == "已改通知"
|
|
assert body["category"] == "notices"
|
|
assert "请下载附件" in body["bodyHtml"]
|
|
assert "原办法" not in body["bodyHtml"]
|
|
lowered = body["bodyHtml"].lower()
|
|
assert "script" not in lowered
|
|
assert "alert" not in lowered
|
|
assert [item["filename"] for item in body["attachments"]] == ["办法.pdf", "补充.docx"]
|
|
editor = client.get("/api/admin/home", headers=headers)
|
|
saved = next(item for item in editor.json()["articles"] if item["id"] == article_id)
|
|
assert [item["filename"] for item in saved["attachments"]] == ["办法.pdf", "补充.docx"]
|
|
missing = client.put(
|
|
"/api/admin/home/articles/99999999",
|
|
headers=headers,
|
|
json={
|
|
"category": "news",
|
|
"title": "不存在",
|
|
"published": False,
|
|
"bodyHtml": "<p>无</p>",
|
|
"attachmentFileIds": [],
|
|
},
|
|
)
|
|
assert missing.status_code == 404
|
|
finally:
|
|
if article_id is not None:
|
|
client.delete(f"/api/admin/home/articles/{article_id}", headers=headers)
|
|
for file_id in (pdf_id, extra_id):
|
|
if file_id is not None:
|
|
client.delete(f"/api/admin/files/{file_id}", headers=headers)
|
|
|
|
|
|
def _reset_home(client, headers, previous: dict | None = None) -> None:
|
|
if previous is not None:
|
|
_restore_templates(client, headers, previous)
|
|
|
|
|
|
def test_home_article_lists_keep_latest_published_only(client):
|
|
if not _minio_ready():
|
|
pytest.skip("MinIO 不可达")
|
|
headers = _headers("staff")
|
|
empty = client.get("/api/site/home")
|
|
assert empty.status_code == 200, empty.text
|
|
assert "新闻动态即将发布" in empty.text
|
|
assert "数据看板整理中" in empty.text
|
|
image_bytes = b"\x89PNG\r\n\x1a\nchart"
|
|
image_id = None
|
|
article_ids: list[int] = []
|
|
try:
|
|
limited = client.put(
|
|
"/api/admin/home/articles/display",
|
|
headers=headers,
|
|
json={"news": 2},
|
|
)
|
|
assert limited.status_code == 200, limited.text
|
|
image = client.post(
|
|
"/api/admin/files",
|
|
headers=headers,
|
|
data={"bucket": "public"},
|
|
files={"file": ("chart.png", image_bytes, "image/png")},
|
|
)
|
|
assert image.status_code == 200, image.text
|
|
image_id = image.json()["id"]
|
|
image_url = image.json()["url"]
|
|
|
|
def add(category: str, title: str, published_at: str, published: bool, body: str, image_file_id):
|
|
created = client.post(
|
|
"/api/admin/home/articles",
|
|
headers=headers,
|
|
json={
|
|
"category": category,
|
|
"title": title,
|
|
"publishedAt": published_at,
|
|
"published": published,
|
|
"bodyHtml": body,
|
|
"imageFileId": image_file_id,
|
|
"attachmentFileIds": [],
|
|
},
|
|
)
|
|
assert created.status_code == 200, created.text
|
|
article_ids.append(created.json()["id"])
|
|
return created.json()["id"]
|
|
|
|
news_id = add("news", "新闻", "2020-01-03T00:00:00", True, "<p>新闻</p>", None)
|
|
add("news", "中闻", "2020-01-02T00:00:00", True, "<p>中闻</p>", None)
|
|
add("news", "旧闻", "2020-01-01T00:00:00", True, "<p>旧闻</p>", None)
|
|
unpublished_news = add("news", "未发新闻", "2020-01-04T00:00:00", False, "<p>未发新闻</p>", None)
|
|
dashboard_id = add(
|
|
"dashboard",
|
|
"区域价格指数示意说明",
|
|
"2020-02-01T00:00:00",
|
|
True,
|
|
f'<p>本期示意,非实时采集</p><img src="{image_url}" alt="示意">',
|
|
image_id,
|
|
)
|
|
hidden_dashboard = add(
|
|
"dashboard",
|
|
"内部测算稿",
|
|
"2020-02-02T00:00:00",
|
|
False,
|
|
"<p>内部测算稿</p>",
|
|
image_id,
|
|
)
|
|
|
|
home = client.get("/api/site/home")
|
|
assert home.status_code == 200, home.text
|
|
text = home.text
|
|
assert "旧闻" not in text
|
|
assert "未发新闻" not in text
|
|
assert "内部测算稿" not in text
|
|
assert "新闻动态即将发布" not in text
|
|
assert "数据看板整理中" not in text
|
|
assert "暂无新的通知公告" in text
|
|
assert "政策法规整理中" in text
|
|
news_items = home.json()["news"]["items"]
|
|
assert [item["title"] for item in news_items] == ["新闻", "中闻"]
|
|
assert "imageUrl" not in news_items[0]
|
|
assert news_items[0]["id"] == news_id
|
|
dashboard_items = home.json()["dashboard"]["items"]
|
|
assert [item["title"] for item in dashboard_items] == ["区域价格指数示意说明"]
|
|
chart = httpx.get(dashboard_items[0]["imageUrl"], timeout=5.0, follow_redirects=True)
|
|
assert chart.status_code == 200
|
|
assert chart.content == image_bytes
|
|
|
|
detail = client.get(f"/api/site/home/articles/{dashboard_id}")
|
|
assert detail.status_code == 200, detail.text
|
|
assert "本期示意,非实时采集" in detail.json()["bodyHtml"]
|
|
assert image_url in detail.json()["bodyHtml"]
|
|
hidden = client.get(f"/api/site/home/articles/{unpublished_news}")
|
|
assert hidden.status_code == 404
|
|
assert "未发新闻" not in hidden.text
|
|
draft = client.get(f"/api/site/home/articles/{hidden_dashboard}")
|
|
assert draft.status_code == 404
|
|
assert "内部测算稿" not in draft.text
|
|
finally:
|
|
for article_id in article_ids:
|
|
client.delete(f"/api/admin/home/articles/{article_id}", headers=headers)
|
|
if image_id is not None:
|
|
client.delete(f"/api/admin/files/{image_id}", headers=headers)
|
|
client.put(
|
|
"/api/admin/home/articles/display",
|
|
headers=headers,
|
|
json={"news": 8},
|
|
)
|
|
|
|
|
|
def _clear_entries(client, headers) -> None:
|
|
client.put(
|
|
"/api/admin/home/audiences",
|
|
headers=headers,
|
|
json={
|
|
"categories": [
|
|
{"name": "估价师", "links": []},
|
|
{"name": "估价机构", "links": []},
|
|
{"name": "公众", "links": []},
|
|
]
|
|
},
|
|
)
|
|
client.put(
|
|
"/api/admin/home/hot-links",
|
|
headers=headers,
|
|
json={
|
|
"links": [
|
|
{"key": "reportQuery", "label": "报告查询", "url": "/reports/query", "visible": False, "sortOrder": 0},
|
|
{"key": "reportReview", "label": "报告审核", "url": "/reports/review", "visible": False, "sortOrder": 1},
|
|
{"key": "complaint", "label": "投诉举报", "url": "", "visible": False, "sortOrder": 2},
|
|
]
|
|
},
|
|
)
|
|
client.put(
|
|
"/api/admin/home/menu",
|
|
headers=headers,
|
|
json={
|
|
"items": [
|
|
{"key": "member", "label": "会员服务", "url": "", "visible": False, "sortOrder": 1},
|
|
]
|
|
},
|
|
)
|
|
|
|
|
|
def test_public_home_exposes_audience_hot_links_and_shortcut_menu(client):
|
|
headers = _headers("staff")
|
|
_clear_entries(client, headers)
|
|
empty = client.get("/api/site/home")
|
|
assert empty.status_code == 200, empty.text
|
|
assert "估价师、机构与公众入口即将开放" in empty.text
|
|
assert "报告查询、报告审核与投诉举报入口即将开放" in empty.text
|
|
assert empty.json()["shortcutMenu"]["items"] == [
|
|
{"label": "首页", "url": "/", "navigation": "sameWindow"}
|
|
]
|
|
try:
|
|
audiences = client.put(
|
|
"/api/admin/home/audiences",
|
|
headers=headers,
|
|
json={
|
|
"categories": [
|
|
{
|
|
"name": "估价师",
|
|
"links": [
|
|
{
|
|
"label": "估价师查询",
|
|
"url": "https://example.com/appraiser",
|
|
"visible": True,
|
|
"sortOrder": 1,
|
|
},
|
|
{
|
|
"label": "不展示的机构入口",
|
|
"url": "https://example.com/hidden",
|
|
"visible": False,
|
|
"sortOrder": 2,
|
|
},
|
|
],
|
|
},
|
|
{"name": "估价机构", "links": []},
|
|
{
|
|
"name": "公众",
|
|
"links": [
|
|
{
|
|
"label": "联系我们",
|
|
"url": "",
|
|
"visible": True,
|
|
"sortOrder": 1,
|
|
}
|
|
],
|
|
},
|
|
]
|
|
},
|
|
)
|
|
assert audiences.status_code == 200, audiences.text
|
|
hot = client.put(
|
|
"/api/admin/home/hot-links",
|
|
headers=headers,
|
|
json={
|
|
"links": [
|
|
{
|
|
"key": "reportQuery",
|
|
"label": "报告查询",
|
|
"url": "https://example.com/report-check",
|
|
"visible": True,
|
|
"sortOrder": 1,
|
|
},
|
|
{
|
|
"key": "reportReview",
|
|
"label": "报告审核",
|
|
"url": "https://example.com/review",
|
|
"visible": False,
|
|
"sortOrder": 2,
|
|
},
|
|
{
|
|
"key": "complaint",
|
|
"label": "投诉举报",
|
|
"url": "",
|
|
"visible": False,
|
|
"sortOrder": 3,
|
|
},
|
|
]
|
|
},
|
|
)
|
|
assert hot.status_code == 200, hot.text
|
|
menu = client.put(
|
|
"/api/admin/home/menu",
|
|
headers=headers,
|
|
json={
|
|
"items": [
|
|
{
|
|
"key": "member",
|
|
"label": "会员服务",
|
|
"url": "https://example.com/member",
|
|
"visible": True,
|
|
"sortOrder": 1,
|
|
}
|
|
]
|
|
},
|
|
)
|
|
assert menu.status_code == 200, menu.text
|
|
|
|
home = client.get("/api/site/home")
|
|
assert home.status_code == 200, home.text
|
|
body = home.json()
|
|
text = home.text
|
|
assert "估价师、机构与公众入口即将开放" not in text
|
|
assert "报告查询、报告审核与投诉举报入口即将开放" not in text
|
|
assert "不展示的机构入口" not in text
|
|
assert "真伪" not in text
|
|
assert "authenticity" not in text
|
|
categories = body["audiences"]["categories"]
|
|
assert [item["name"] for item in categories] == ["估价师", "估价机构", "公众"]
|
|
for category in categories:
|
|
assert "url" not in category
|
|
assert categories[0]["links"] == [
|
|
{
|
|
"label": "估价师查询",
|
|
"url": "https://example.com/appraiser",
|
|
"navigation": "newWindow",
|
|
}
|
|
]
|
|
assert categories[2]["links"] == [
|
|
{"label": "联系我们", "url": "", "navigation": "none"}
|
|
]
|
|
assert "报告查询" not in str(body["audiences"])
|
|
assert body["hotLinks"]["links"] == [
|
|
{
|
|
"label": "报告查询",
|
|
"url": "/reports/query",
|
|
"navigation": "sameWindow",
|
|
}
|
|
]
|
|
assert "https://example.com/report-check" not in text
|
|
assert [item["label"] for item in body["shortcutMenu"]["items"]] == ["首页", "会员服务"]
|
|
assert body["shortcutMenu"]["items"][1] == {
|
|
"label": "会员服务",
|
|
"url": "https://example.com/member",
|
|
"navigation": "newWindow",
|
|
}
|
|
assert "继续教育" not in [item["label"] for item in body["shortcutMenu"]["items"]]
|
|
|
|
hidden = client.put(
|
|
"/api/admin/home/menu",
|
|
headers=headers,
|
|
json={
|
|
"items": [
|
|
{
|
|
"key": "member",
|
|
"label": "会员服务",
|
|
"url": "https://example.com/member",
|
|
"visible": False,
|
|
"sortOrder": 1,
|
|
},
|
|
]
|
|
},
|
|
)
|
|
assert hidden.status_code == 200, hidden.text
|
|
menu_only = client.get("/api/site/home").json()["shortcutMenu"]["items"]
|
|
assert menu_only == [{"label": "首页", "url": "/", "navigation": "sameWindow"}]
|
|
assert "https://example.com/member" not in client.get("/api/site/home").text
|
|
finally:
|
|
_clear_entries(client, headers)
|
|
|
|
|
|
def test_public_home_lists_members_without_links_and_keeps_empty_honors(client):
|
|
if not _minio_ready():
|
|
pytest.skip("MinIO 不可达")
|
|
headers = _headers("staff")
|
|
empty = client.get("/api/site/home")
|
|
assert empty.status_code == 200, empty.text
|
|
assert "协会荣誉与资质整理中" in empty.text
|
|
assert "会员单位名录整理中" in empty.text
|
|
logo_bytes = b"\x89PNG\r\n\x1a\nmember"
|
|
honor_bytes = b"\x89PNG\r\n\x1a\nhonor"
|
|
logo_id = None
|
|
honor_id = None
|
|
try:
|
|
logo = client.post(
|
|
"/api/admin/files",
|
|
headers=headers,
|
|
data={"bucket": "public"},
|
|
files={"file": ("member.png", logo_bytes, "image/png")},
|
|
)
|
|
assert logo.status_code == 200, logo.text
|
|
logo_id = logo.json()["id"]
|
|
honor = client.post(
|
|
"/api/admin/files",
|
|
headers=headers,
|
|
data={"bucket": "public"},
|
|
files={"file": ("honor.png", honor_bytes, "image/png")},
|
|
)
|
|
assert honor.status_code == 200, honor.text
|
|
honor_id = honor.json()["id"]
|
|
|
|
shown = client.put(
|
|
"/api/admin/home/honors",
|
|
headers=headers,
|
|
json={
|
|
"intervalSeconds": 5,
|
|
"items": [{"imageFileId": honor_id, "sortOrder": 1}],
|
|
},
|
|
)
|
|
assert shown.status_code == 200, shown.text
|
|
with_honor = client.get("/api/site/home")
|
|
assert with_honor.status_code == 200, with_honor.text
|
|
assert "协会荣誉与资质整理中" not in with_honor.text
|
|
honor_items = with_honor.json()["honors"]["items"]
|
|
assert with_honor.json()["honors"]["intervalSeconds"] == 5
|
|
downloaded = httpx.get(honor_items[0]["imageUrl"], timeout=5.0, follow_redirects=True)
|
|
assert downloaded.status_code == 200
|
|
assert downloaded.content == honor_bytes
|
|
|
|
cleared = client.put(
|
|
"/api/admin/home/honors",
|
|
headers=headers,
|
|
json={"intervalSeconds": 5, "items": []},
|
|
)
|
|
assert cleared.status_code == 200, cleared.text
|
|
saved = client.put(
|
|
"/api/admin/home/members",
|
|
headers=headers,
|
|
json={
|
|
"intervalSeconds": 8,
|
|
"items": [
|
|
{
|
|
"name": "甘肃示例估价公司",
|
|
"url": "",
|
|
"imageFileId": logo_id,
|
|
"sortOrder": 1,
|
|
}
|
|
],
|
|
},
|
|
)
|
|
assert saved.status_code == 200, saved.text
|
|
|
|
home = client.get("/api/site/home")
|
|
assert home.status_code == 200, home.text
|
|
assert "协会荣誉与资质整理中" in home.text
|
|
assert "会员单位名录整理中" not in home.text
|
|
members = home.json()["members"]
|
|
assert members["intervalSeconds"] == 8
|
|
assert members["items"] == [
|
|
{
|
|
"name": "甘肃示例估价公司",
|
|
"url": "",
|
|
"navigation": "none",
|
|
"imageUrl": members["items"][0]["imageUrl"],
|
|
}
|
|
]
|
|
logo_download = httpx.get(members["items"][0]["imageUrl"], timeout=5.0, follow_redirects=True)
|
|
assert logo_download.status_code == 200
|
|
assert logo_download.content == logo_bytes
|
|
finally:
|
|
client.put(
|
|
"/api/admin/home/honors",
|
|
headers=headers,
|
|
json={"intervalSeconds": 5, "items": []},
|
|
)
|
|
client.put(
|
|
"/api/admin/home/members",
|
|
headers=headers,
|
|
json={"intervalSeconds": 8, "items": []},
|
|
)
|
|
for file_id in (logo_id, honor_id):
|
|
if file_id is not None:
|
|
client.delete(f"/api/admin/files/{file_id}", headers=headers)
|
|
|
|
|
|
def _empty_contact(name: str, address: str = "") -> dict:
|
|
return {
|
|
"name": name,
|
|
"address": address,
|
|
"phone": "",
|
|
"email": "",
|
|
"latitude": None,
|
|
"longitude": None,
|
|
}
|
|
|
|
|
|
def _related(template_name: str, supervisor: list[dict]) -> dict:
|
|
return {
|
|
"templateName": template_name,
|
|
"categories": [
|
|
{"name": "主管部门", "links": supervisor},
|
|
{"name": "相关部门", "links": []},
|
|
{"name": "合作协会", "links": []},
|
|
],
|
|
}
|
|
|
|
|
|
def test_staff_home_includes_map_picker_credentials(client, monkeypatch):
|
|
headers = _headers("staff")
|
|
monkeypatch.setenv("GAODE_KEY", "test-gaode-key")
|
|
monkeypatch.setenv("GAODE_SECURITY_JS_CODE", "test-gaode-secret")
|
|
get_settings.cache_clear()
|
|
try:
|
|
body = client.get("/api/admin/home", headers=headers)
|
|
assert body.status_code == 200, body.text
|
|
assert body.json()["mapPicker"] == {
|
|
"key": "test-gaode-key",
|
|
"securityJsCode": "test-gaode-secret",
|
|
}
|
|
monkeypatch.setenv("GAODE_SECURITY_JS_CODE", "")
|
|
get_settings.cache_clear()
|
|
hidden = client.get("/api/admin/home", headers=headers)
|
|
assert hidden.status_code == 200, hidden.text
|
|
assert hidden.json()["mapPicker"] is None
|
|
guest = client.get("/api/admin/home")
|
|
assert guest.status_code == 401
|
|
finally:
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def test_public_footer_uses_the_active_template_and_hides_an_unplaced_map(client, monkeypatch):
|
|
headers = _headers("staff")
|
|
previous = _template_state(client, headers)
|
|
monkeypatch.setenv("GAODE_KEY", "test-gaode-key")
|
|
monkeypatch.setenv("GAODE_SECURITY_JS_CODE", "test-gaode-secret")
|
|
get_settings.cache_clear()
|
|
try:
|
|
activated = _write(client, headers)
|
|
assert activated.status_code == 200, activated.text
|
|
contact = client.put(
|
|
"/api/admin/home/contact",
|
|
headers=headers,
|
|
json={
|
|
"templates": [
|
|
_empty_contact("科技蓝", "科技蓝办公地址"),
|
|
_empty_contact("LOGO蓝"),
|
|
]
|
|
},
|
|
)
|
|
assert contact.status_code == 200, contact.text
|
|
related = client.put(
|
|
"/api/admin/home/related-links",
|
|
headers=headers,
|
|
json=_related(
|
|
"LOGO蓝",
|
|
[
|
|
{
|
|
"label": "甘肃省住建厅",
|
|
"url": "https://example.com/zjt",
|
|
"visible": True,
|
|
"sortOrder": 1,
|
|
}
|
|
],
|
|
),
|
|
)
|
|
assert related.status_code == 200, related.text
|
|
other = client.put(
|
|
"/api/admin/home/related-links",
|
|
headers=headers,
|
|
json=_related(
|
|
"科技蓝",
|
|
[
|
|
{
|
|
"label": "科技蓝主管部门",
|
|
"url": "https://example.com/tech",
|
|
"visible": True,
|
|
"sortOrder": 1,
|
|
}
|
|
],
|
|
),
|
|
)
|
|
assert other.status_code == 200, other.text
|
|
compliance = client.put(
|
|
"/api/admin/home/compliance",
|
|
headers=headers,
|
|
json={"templateName": "LOGO蓝", "links": []},
|
|
)
|
|
assert compliance.status_code == 200, compliance.text
|
|
|
|
home = client.get("/api/site/home")
|
|
assert home.status_code == 200, home.text
|
|
text = home.text
|
|
assert "科技蓝办公地址" not in text
|
|
assert "科技蓝主管部门" not in text
|
|
assert "办公地址整理中" in text
|
|
assert "联系电话整理中" in text
|
|
assert "联系邮箱整理中" in text
|
|
assert "地图位置整理中" in text
|
|
assert "主管部门链接整理中" not in text
|
|
assert "相关部门链接整理中" in text
|
|
assert "合作协会链接整理中" in text
|
|
assert "隐私政策" in text
|
|
assert "使用条款" in text
|
|
assert "网站声明" in text
|
|
assert home.json()["compliance"]["links"][0]["url"] == "/legal/privacy"
|
|
assert home.json()["relatedLinks"]["主管部门"]["links"] == [
|
|
{
|
|
"label": "甘肃省住建厅",
|
|
"url": "https://example.com/zjt",
|
|
"navigation": "newWindow",
|
|
}
|
|
]
|
|
|
|
placed = client.put(
|
|
"/api/admin/home/contact",
|
|
headers=headers,
|
|
json={
|
|
"templates": [
|
|
_empty_contact("科技蓝", "科技蓝办公地址"),
|
|
{
|
|
"name": "LOGO蓝",
|
|
"address": "",
|
|
"phone": "",
|
|
"email": "",
|
|
"latitude": 36.5,
|
|
"longitude": 103.5,
|
|
},
|
|
]
|
|
},
|
|
)
|
|
assert placed.status_code == 200, placed.text
|
|
mapped = client.get("/api/site/home")
|
|
assert mapped.status_code == 200, mapped.text
|
|
assert "地图位置整理中" not in mapped.text
|
|
assert mapped.json()["contact"]["map"] == {
|
|
"latitude": 36.5,
|
|
"longitude": 103.5,
|
|
"key": "test-gaode-key",
|
|
"securityJsCode": "test-gaode-secret",
|
|
}
|
|
finally:
|
|
client.put(
|
|
"/api/admin/home/contact",
|
|
headers=headers,
|
|
json={"templates": [_empty_contact("科技蓝"), _empty_contact("LOGO蓝")]},
|
|
)
|
|
for name in ("科技蓝", "LOGO蓝"):
|
|
client.put(
|
|
"/api/admin/home/related-links",
|
|
headers=headers,
|
|
json=_related(name, []),
|
|
)
|
|
client.put(
|
|
"/api/admin/home/compliance",
|
|
headers=headers,
|
|
json={"templateName": name, "links": []},
|
|
)
|
|
_reset_home(client, headers, previous)
|
|
get_settings.cache_clear()
|