From d48d42dc27f08f29cf1413c15451cc37f4f6ee7b Mon Sep 17 00:00:00 2001 From: uy_sun Date: Thu, 18 Jul 2024 15:59:04 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=B8=BB=E9=A1=B5?= =?UTF-8?q?=E4=B8=BA=E7=A9=BA=E5=AD=97=E7=AC=A6=E4=B8=B2=E4=B9=9F=E8=83=BD?= =?UTF-8?q?=E9=80=9A=E8=BF=87=E6=B5=8B=E8=AF=95=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 +++ src/utils/validation/__init__.py | 1 - src/utils/validation/constants.py | 1 + src/utils/validation/models.py | 13 ++++++++-- .../utils/validation/fields/test_homepage.py | 25 +++++++++++++++++++ 5 files changed, 41 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52fca228..fc8a5c26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/lang/zh-CN/ ## [Unreleased] +### Fixed + +- 修复主页为空字符串也能通过测试的问题 + ## [3.3.2] - 2024-05-01 ### Added diff --git a/src/utils/validation/__init__.py b/src/utils/validation/__init__.py index b90fe29a..4d4c6e16 100644 --- a/src/utils/validation/__init__.py +++ b/src/utils/validation/__init__.py @@ -29,7 +29,6 @@ def validate_info( if publish_type not in validation_model_map: raise ValueError("⚠️ 未知的发布类型。") # pragma: no cover - # 如果升级至 pydantic 2 后,可以使用 validation-context # https://docs.pydantic.dev/latest/usage/validators/#validation-context validation_context = { "previous_data": raw_data.get("previous_data"), diff --git a/src/utils/validation/constants.py b/src/utils/validation/constants.py index 54f2aa05..ba7e5aa8 100644 --- a/src/utils/validation/constants.py +++ b/src/utils/validation/constants.py @@ -27,4 +27,5 @@ "color_error": "颜色格式不正确", "string_too_long": "字符串长度不能超过 {max_length} 个字符", "too_long": "列表长度不能超过 {max_length} 个元素", + "string_pattern_mismatch": "字符串应满足格式 '{pattern}'", } diff --git a/src/utils/validation/models.py b/src/utils/validation/models.py index 23be2ae7..9f9573fd 100644 --- a/src/utils/validation/models.py +++ b/src/utils/validation/models.py @@ -1,11 +1,12 @@ import abc import json from enum import Enum -from typing import TYPE_CHECKING, Any, TypedDict +from typing import TYPE_CHECKING, Annotated, Any, TypedDict from pydantic import ( BaseModel, Field, + StringConstraints, ValidationInfo, ValidatorFunctionWrapHandler, field_validator, @@ -111,7 +112,10 @@ class PublishInfo(abc.ABC, BaseModel): name: str = Field(max_length=NAME_MAX_LENGTH) desc: str author: str - homepage: str + homepage: Annotated[ + str, + StringConstraints(strip_whitespace=True, pattern=r"^https?://.*$"), + ] tags: list[Tag] = Field(max_length=3) is_official: bool = False @@ -120,6 +124,11 @@ class PublishInfo(abc.ABC, BaseModel): def collect_valid_values( cls, v: Any, handler: ValidatorFunctionWrapHandler, info: ValidationInfo ): + """收集验证通过的数据 + + NOTE: 其他所有的验证器都应该在这个验证器之前执行 + 所以不能用 after 模式,只能用 before 模式 + """ context = info.context if context is None: # pragma: no cover raise PydanticCustomError("validation_context", "未获取到验证上下文") diff --git a/tests/utils/validation/fields/test_homepage.py b/tests/utils/validation/fields/test_homepage.py index 01133a22..4d2e2746 100644 --- a/tests/utils/validation/fields/test_homepage.py +++ b/tests/utils/validation/fields/test_homepage.py @@ -24,3 +24,28 @@ async def test_homepage_failed_http_exception(mocked_api: MockRouter) -> None: ] assert mocked_api["exception"].called + + +async def test_homepage_failed_empty_homepage(mocked_api: MockRouter) -> None: + """主页为空字符串的情况""" + from src.utils.validation import PublishType, validate_info + + data = generate_bot_data(homepage="") + + result = validate_info(PublishType.BOT, data) + + assert not result["valid"] + assert "homepage" not in result["data"] + assert result["errors"] == [ + { + "type": "string_pattern_mismatch", + "loc": ("homepage",), + "msg": "字符串应满足格式 '^https?://.*$'", + "input": "", + "ctx": {"pattern": "^https?://.*$"}, + "url": "https://errors.pydantic.dev/2.7/v/string_pattern_mismatch", + } + ] + + assert not mocked_api["homepage"].called + assert not mocked_api["homepage_failed"].called