补充测试覆盖率 + 新增部署引导命令 bootstrap_deployment

- 新增 apps/blog、apps/solutions、apps/home、apps/api 测试文件,覆盖标签/排序/
  草稿过滤、HomePage.subpage_types 回归测试、page_preview 预览接口
- apps/core/tests.py 新增健康检查端点、发布 webhook 信号、bootstrap_deployment
  命令的测试用例,pytest 用例数从 40 增至 65,全部通过
- 修复 apps/api/urls.py 中 PagePreviewAPIViewSet.get_object() 未捕获
  DoesNotExist 导致 500 而非文档承诺 404 的 bug
- 新增 apps/core/management/commands/bootstrap_deployment.py:部署时按环境变量
  幂等创建首个超级管理员账号并联动初始化 RBAC 权限组,.env.example 补充对应
  环境变量说明
- 新增 OpenSearch + 中文分词本地 PoC 脚手架(docker-compose.opensearch.yml +
  docker/opensearch/Dockerfile),调研结论详见设计文档 §2.10
- 更新设计文档 §2.16 Phase 1 进度清单
This commit is contained in:
2026-08-11 14:47:10 +08:00
parent f37571f395
commit 147107501b
11 changed files with 537 additions and 9 deletions
+47
View File
@@ -0,0 +1,47 @@
"""apps.api 单元测试:预览专用接口 `page_preview`apps/api/urls.py 的
PagePreviewAPIViewSet),覆盖 §2.7/§2.15 描述的"仅凭有效 content_type+token
才能读取草稿快照"行为。"""
import pytest
from rest_framework.test import APIClient
pytestmark = pytest.mark.django_db
@pytest.fixture
def preview_token(home_page):
"""为 home_page 生成一条预览快照记录,返回可用于查询接口的 token。"""
preview = home_page.create_page_preview()
preview.save()
return preview.token
def test_page_preview_returns_draft_snapshot_for_valid_token(home_page, preview_token):
client = APIClient()
response = client.get(
"/api/v2/page_preview/",
{"content_type": "home.homepage", "token": preview_token},
)
assert response.status_code == 200
assert response.data["title"] == home_page.title
def test_page_preview_returns_404_for_unknown_token(home_page):
client = APIClient()
response = client.get(
"/api/v2/page_preview/",
{"content_type": "home.homepage", "token": "does-not-exist"},
)
assert response.status_code == 404
def test_page_preview_returns_404_for_mismatched_content_type(home_page, preview_token):
"""token 是为 home.homepage 生成的,用错误的 content_type 查询应查不到记录。"""
client = APIClient()
response = client.get(
"/api/v2/page_preview/",
{"content_type": "blog.blogpage", "token": preview_token},
)
assert response.status_code == 404
+11 -5
View File
@@ -3,6 +3,7 @@ Wagtail API v2 路由配置。
详见 documents/设计方案分析与完善版.md §2.7 Headless API 设计规范。
"""
from django.contrib.contenttypes.models import ContentType
from django.http import Http404
from rest_framework.response import Response
from wagtail.api.v2.views import PagesAPIViewSet
from wagtail.api.v2.router import WagtailAPIRouter
@@ -40,11 +41,16 @@ class PagePreviewAPIViewSet(PagesAPIViewSet):
return Response(serializer.data)
def get_object(self):
app_label, model = self.request.GET["content_type"].split(".")
content_type = ContentType.objects.get(app_label=app_label, model=model)
page_preview = PagePreview.objects.get(
content_type=content_type, token=self.request.GET["token"]
)
try:
app_label, model = self.request.GET["content_type"].split(".")
content_type = ContentType.objects.get(app_label=app_label, model=model)
page_preview = PagePreview.objects.get(
content_type=content_type, token=self.request.GET["token"]
)
except (KeyError, ValueError, ContentType.DoesNotExist, PagePreview.DoesNotExist):
# 缺少参数/content_type 格式错误/未知或已过期的 token,统一返回 404,
# 不暴露内部异常细节(详见类文档字符串)。
raise Http404("No matching page preview found.")
page = page_preview.as_page()
if not page.pk:
# 新建(尚未保存)页面的预览没有真实主键,填充占位值避免 API 路由生成 URL 时报错