From 85115f4537341b6daabd6947ad53408f32bbd8ae Mon Sep 17 00:00:00 2001 From: uy_sun Date: Sat, 14 Dec 2024 08:54:14 +0800 Subject: [PATCH 01/12] =?UTF-8?q?test:=20nonebug=20=E6=96=B0=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E7=9A=84=E6=96=B0=E5=86=99=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/conftest.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 56a8f046..863c6ffe 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,4 @@ from pathlib import Path -from typing import TYPE_CHECKING import httpx import nonebot @@ -7,14 +6,12 @@ from nonebot.adapters.github import Adapter from nonebug import NONEBOT_INIT_KWARGS from nonebug.app import App +from pytest_asyncio import is_async_test from pytest_mock import MockerFixture from respx import MockRouter from src.providers.constants import STORE_ADAPTERS_URL, STORE_PLUGINS_URL -if TYPE_CHECKING: - from nonebot.plugin import Plugin - def pytest_configure(config: pytest.Config) -> None: config.stash[NONEBOT_INIT_KWARGS] = { @@ -35,12 +32,21 @@ def pytest_configure(config: pytest.Config) -> None: } +def pytest_collection_modifyitems(items: list[pytest.Item]): + pytest_asyncio_tests = (item for item in items if is_async_test(item)) + session_scope_marker = pytest.mark.asyncio(loop_scope="session") + for async_test in pytest_asyncio_tests: + async_test.add_marker(session_scope_marker, append=False) + + @pytest.fixture(scope="session", autouse=True) -def load_plugin(nonebug_init: None) -> set["Plugin"]: - nonebot.get_driver().register_adapter(Adapter) - return nonebot.load_plugins( - str(Path(__file__).parent.parent.parent / "src" / "plugins") - ) +async def _after_nonebot_init(after_nonebot_init: None): + # 加载适配器 + driver = nonebot.get_driver() + driver.register_adapter(Adapter) + + # 加载插件 + nonebot.load_plugins(str(Path(__file__).parent.parent.parent / "src" / "plugins")) @pytest.fixture From 3cba3407de1c869d23aab344e64d39b67d31e537 Mon Sep 17 00:00:00 2001 From: uy_sun Date: Sat, 14 Dec 2024 16:51:39 +0800 Subject: [PATCH 02/12] =?UTF-8?q?feat:=20=E9=80=82=E9=85=8D=E5=99=A8?= =?UTF-8?q?=E5=92=8C=E9=A9=B1=E5=8A=A8=E5=99=A8=E6=B7=BB=E5=8A=A0=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=8F=B7=E5=92=8C=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + src/providers/models.py | 11 ++++++++--- src/providers/validation/models.py | 21 +++++++++++++++++---- src/providers/validation/utils.py | 9 +++++++++ 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 394f7216..e7900471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/lang/zh-CN/ ### Added - 插件测试中使用 uv 来控制 Python 版本并与本体共享依赖 +- 适配器和驱动器添加版本号和时间 ## [4.1.4] - 2024-12-08 diff --git a/src/providers/models.py b/src/providers/models.py index 4047da14..2c7512ff 100644 --- a/src/providers/models.py +++ b/src/providers/models.py @@ -177,6 +177,8 @@ class RegistryAdapter(BaseModel): homepage: str tags: list[Tag] is_official: bool + time: str + version: str @property def key(self): @@ -195,6 +197,8 @@ def from_publish_info(cls, publish_info: AdapterPublishInfo) -> Self: homepage=publish_info.homepage, tags=[Tag(label=tag.label, color=tag.color) for tag in publish_info.tags], is_official=publish_info.is_official, + time=publish_info.time, + version=publish_info.version, ) @@ -235,6 +239,8 @@ class RegistryDriver(BaseModel): homepage: str tags: list[Tag] is_official: bool + time: str + version: str @property def key(self): @@ -253,6 +259,8 @@ def from_publish_info(cls, publish_info: DriverPublishInfo) -> Self: homepage=publish_info.homepage, tags=[Tag(label=tag.label, color=tag.color) for tag in publish_info.tags], is_official=publish_info.is_official, + time=publish_info.time, + version=publish_info.version, ) @@ -282,9 +290,6 @@ def key(self): @classmethod def from_publish_info(cls, publish_info: PluginPublishInfo) -> Self: - if publish_info.time is None: - raise ValueError("上传时间不能为空") - return cls( module_name=publish_info.module_name, project_link=publish_info.project_link, diff --git a/src/providers/validation/models.py b/src/providers/validation/models.py index be89a6fc..76e94f60 100644 --- a/src/providers/validation/models.py +++ b/src/providers/validation/models.py @@ -31,6 +31,7 @@ check_url, get_adapters, get_pypi_name, + get_pypi_version, get_upload_time, resolve_adapter_name, ) @@ -80,6 +81,11 @@ class PyPIMixin(BaseModel): 从 PyPI 获取最新版本的上传时间 """ + version: str + """版本号 + + 从 PyPI 获取最新版本号或者由插件测试提供 + """ @field_validator("module_name", mode="before") @classmethod @@ -130,9 +136,18 @@ def prevent_duplication( {"project_link": project_link, "module_name": module_name}, ) - # 如果一切正常才记录上传时间 - if project_link: + # 如果一切正常才记录上传时间和版本号 + if project_link is not None: + if issubclass(cls, DriverPublishInfo) and ( + project_link == "" or project_link.startswith("nonebot2[") + ): + project_link = "nonebot2" + values["time"] = get_upload_time(project_link) + # 只有不是插件测试的情况下才获取版本号 + # 插件测试的时候应该使用从插件测试获取的版本号,也就是传入的版本号 + if not issubclass(cls, PluginPublishInfo): + values["version"] = get_pypi_version(project_link) return values @@ -209,8 +224,6 @@ class PluginPublishInfo(PublishInfo, PyPIMixin): """插件测试元数据""" skip_test: bool """是否跳过插件测试""" - version: str - """插件版本号""" test_config: str = "" """插件测试配置""" test_output: str = "" diff --git a/src/providers/validation/utils.py b/src/providers/validation/utils.py index 723c889f..a0729567 100644 --- a/src/providers/validation/utils.py +++ b/src/providers/validation/utils.py @@ -27,6 +27,15 @@ def get_pypi_name(project_link: str) -> str: return data["info"]["name"] +def get_pypi_version(project_link: str) -> str: + """获取 PyPI 版本""" + url = f"https://pypi.org/pypi/{project_link}/json" + r = get_url(url) + r.raise_for_status() + data = load_json(r.text) + return data["info"]["version"] + + def get_upload_time(project_link: str) -> str | None: """获取插件的上传时间""" url = f"https://pypi.org/pypi/{project_link}/json" From cad7c8df4c17f877050d6acbd33010552ba68d3a Mon Sep 17 00:00:00 2001 From: uy_sun Date: Sat, 14 Dec 2024 22:24:32 +0800 Subject: [PATCH 03/12] =?UTF-8?q?test:=20=E7=BB=9F=E4=B8=80=E5=95=86?= =?UTF-8?q?=E5=BA=97=E7=9B=B8=E5=85=B3=20mock=20=E7=9A=84=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/conftest.py | 196 ++++++++++-------- tests/providers/store_test/conftest.py | 37 ---- .../store_test => }/store/plugin_configs.json | 0 .../store/registry_adapters.json | 4 +- .../store_test => }/store/registry_bots.json | 0 .../store/registry_drivers.json | 8 +- .../store/registry_plugins.json | 8 +- .../store/registry_results.json | 4 +- .../store/store_adapters.json5 | 0 .../store_test => }/store/store_bots.json5 | 0 .../store_test => }/store/store_drivers.json5 | 0 .../store_test => }/store/store_plugins.json5 | 0 12 files changed, 118 insertions(+), 139 deletions(-) rename tests/{providers/store_test => }/store/plugin_configs.json (100%) rename tests/{providers/store_test => }/store/registry_adapters.json (74%) rename tests/{providers/store_test => }/store/registry_bots.json (100%) rename tests/{providers/store_test => }/store/registry_drivers.json (69%) rename tests/{providers/store_test => }/store/registry_plugins.json (85%) rename tests/{providers/store_test => }/store/registry_results.json (97%) rename tests/{providers/store_test => }/store/store_adapters.json5 (100%) rename tests/{providers/store_test => }/store/store_bots.json5 (100%) rename tests/{providers/store_test => }/store/store_drivers.json5 (100%) rename tests/{providers/store_test => }/store/store_plugins.json5 (100%) diff --git a/tests/conftest.py b/tests/conftest.py index 863c6ffe..c714b102 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ from pathlib import Path +from typing import TypedDict import httpx import nonebot @@ -10,7 +11,18 @@ from pytest_mock import MockerFixture from respx import MockRouter -from src.providers.constants import STORE_ADAPTERS_URL, STORE_PLUGINS_URL +from src.providers.constants import ( + REGISTRY_ADAPTERS_URL, + REGISTRY_BOTS_URL, + REGISTRY_DRIVERS_URL, + REGISTRY_PLUGIN_CONFIG_URL, + REGISTRY_PLUGINS_URL, + REGISTRY_RESULTS_URL, + STORE_ADAPTERS_URL, + STORE_BOTS_URL, + STORE_DRIVERS_URL, + STORE_PLUGINS_URL, +) def pytest_configure(config: pytest.Config) -> None: @@ -125,110 +137,110 @@ def _clear_cache(app: App): get_url.cache_clear() +class PyPIProject(TypedDict): + url: str + name: str + version: str + upload_time_iso_8601: str + + @pytest.fixture def mocked_api(respx_mock: MockRouter): + # 主页数据 respx_mock.get("exception", name="exception").mock(side_effect=httpx.ConnectError) - respx_mock.get( - "https://pypi.org/pypi/project_link/json", name="project_link" - ).respond( - json={ - "info": {"name": "project_link", "version": "0.0.1"}, - "urls": [{"upload_time_iso_8601": "2023-09-01T00:00:00+00:00"}], - } + respx_mock.get("https://nonebot.dev/", name="homepage").respond() + respx_mock.get("https://www.baidu.com", name="homepage_failed").respond(404) + # GitHub 数据 + respx_mock.get("https://api.github.com/user/1", name="github_username_1").respond( + json={"login": "he0119"} ) - respx_mock.get( - "https://pypi.org/pypi/project_link//json", name="project_link/" - ).respond( - json={ - "info": {"name": "project_link/", "version": "0.0.1"}, - "urls": [{"upload_time_iso_8601": "2023-10-01T00:00:00+00:00"}], - } + respx_mock.get("https://api.github.com/user/2", name="github_username_2").respond( + json={"login": "BigOrangeQWQ"} ) + # PyPI 数据 + pypi_projects = [ + PyPIProject( + url="project_link", + name="project_link", + version="0.0.1", + upload_time_iso_8601="2023-09-01T00:00:00.000000Z", + ), + PyPIProject( + url="project_link/", + name="project_link/", + version="0.0.1", + upload_time_iso_8601="2023-10-01T00:00:00.000000Z", + ), + PyPIProject( + url="nonebot-plugin-datastore", + name="nonebot-plugin-datastore", + version="1.3.0", + upload_time_iso_8601="2024-06-20T07:53:23.524486Z", + ), + PyPIProject( + url="nonebot-plugin-treehelp", + name="nonebot-plugin-treehelp", + version="0.5.0", + upload_time_iso_8601="2024-07-13T04:41:40.905441Z", + ), + PyPIProject( + url="nonebot-plugin-wordcloud", + name="nonebot-plugin-wordcloud", + version="0.8.0", + upload_time_iso_8601="2024-08-15T13:06:51.084754Z", + ), + PyPIProject( + url="project_link_normalization", + name="project-link-normalization", + version="0.0.1", + upload_time_iso_8601="2023-10-01T00:00:00.000000Z", + ), + ] + for project in pypi_projects: + respx_mock.get( + f"https://pypi.org/pypi/{project['url']}/json", + name=f"pypi_{project['url']}", + ).respond( + json={ + "info": {"name": project["name"], "version": project["version"]}, + "urls": [{"upload_time_iso_8601": project["upload_time_iso_8601"]}], + } + ) respx_mock.get( - "https://pypi.org/pypi/nonebot-plugin-treehelp/json", - name="project_link_treehelp", - ).respond( - json={ - "info": {"name": "nonebot-plugin-treehelp", "version": "0.3.1"}, - "urls": [{"upload_time_iso_8601": "2021-08-01T00:00:00+00:00"}], - } + "https://pypi.org/pypi/project_link_failed/json", + name="pypi_project_link_failed", + ).respond(404) + # 商店数据 + store_path = Path(__file__).parent / "store" + respx_mock.get(STORE_ADAPTERS_URL).respond( + text=(store_path / "store_adapters.json5").read_text(encoding="utf8") ) - respx_mock.get( - "https://pypi.org/pypi/nonebot-plugin-datastore/json", - name="project_link_datastore", - ).respond( - json={ - "info": {"name": "nonebot-plugin-datastore", "version": "1.0.0"}, - } + respx_mock.get(STORE_BOTS_URL).respond( + text=(store_path / "store_bots.json5").read_text(encoding="utf8") ) - respx_mock.get( - "https://pypi.org/pypi/nonebot-plugin-wordcloud/json", - name="project_link_wordcloud", - ).respond( - json={"info": {"name": "nonebot-plugin-wordcloud", "version": "0.5.0"}}, + respx_mock.get(STORE_DRIVERS_URL).respond( + text=(store_path / "store_drivers.json5").read_text(encoding="utf8") ) - respx_mock.get( - "https://pypi.org/pypi/project_link1/json", name="project_link1" - ).respond( - json={ - "info": {"name": "project_link1", "version": "0.5.0"}, - "urls": [{"upload_time_iso_8601": "2023-10-01T00:00:00+00:00"}], - } + respx_mock.get(STORE_PLUGINS_URL).respond( + text=(store_path / "store_plugins.json5").read_text(encoding="utf8") ) - respx_mock.get( - "https://pypi.org/pypi/project_link_failed/json", name="project_link_failed" - ).respond(404) - respx_mock.get( - "https://pypi.org/pypi/project_link_normalization/json", - name="project_link_normalization", - ).respond( - json={ - "info": {"name": "project-link-normalization", "version": "0.0.1"}, - "urls": [{"upload_time_iso_8601": "2023-10-01T00:00:00+00:00"}], - } + respx_mock.get(REGISTRY_ADAPTERS_URL).respond( + text=(store_path / "registry_adapters.json").read_text(encoding="utf8") ) - respx_mock.get("https://www.baidu.com", name="homepage_failed").respond(404) - respx_mock.get("https://nonebot.dev/", name="homepage").respond() - respx_mock.get("https://v2.nonebot.dev", name="homepage_v2").respond() - respx_mock.get(STORE_ADAPTERS_URL, name="store_adapters").respond( - json=[ - { - "module_name": "nonebot.adapters.onebot.v11", - "project_link": "nonebot-adapter-onebot", - "name": "OneBot V11", - "desc": "OneBot V11 协议", - "author_id": 2, - "homepage": "https://onebot.adapters.nonebot.dev/", - "tags": [], - "is_official": True, - }, - { - "module_name": "nonebot.adapters.onebot.v12", - "project_link": "nonebot-adapter-onebot", - "name": "OneBot V12", - "desc": "OneBot V12 协议", - "author_id": 2, - "homepage": "https://onebot.adapters.nonebot.dev/", - "tags": [], - "is_official": True, - }, - ] + respx_mock.get(REGISTRY_BOTS_URL).respond( + text=(store_path / "registry_bots.json").read_text(encoding="utf8") ) - respx_mock.get(STORE_PLUGINS_URL, name="store_plugins").respond( - json=[ - { - "module_name": "nonebot-plugin-treehelp", - "project_link": "nonebot-plugin-treehelp", - "author_id": 1, - "tags": [], - "is_official": True, - }, - ] + respx_mock.get(REGISTRY_DRIVERS_URL).respond( + text=(store_path / "registry_drivers.json").read_text(encoding="utf8") ) - respx_mock.get("https://api.github.com/user/1", name="github_username_1").respond( - json={"login": "he0119"} + respx_mock.get(REGISTRY_PLUGINS_URL).respond( + text=(store_path / "registry_plugins.json").read_text(encoding="utf8") ) - respx_mock.get("https://api.github.com/user/2", name="github_username_2").respond( - json={"login": "BigOrangeQWQ"} + respx_mock.get(REGISTRY_RESULTS_URL).respond( + text=(store_path / "registry_results.json").read_text(encoding="utf8") ) + respx_mock.get(REGISTRY_PLUGIN_CONFIG_URL).respond( + text=(store_path / "plugin_configs.json").read_text(encoding="utf8") + ) + return respx_mock diff --git a/tests/providers/store_test/conftest.py b/tests/providers/store_test/conftest.py index e6753ad6..dbf18205 100644 --- a/tests/providers/store_test/conftest.py +++ b/tests/providers/store_test/conftest.py @@ -1,35 +1,9 @@ from pathlib import Path -import pyjson5 import pytest from pytest_mock import MockerFixture from respx import MockRouter -from src.providers.constants import ( - REGISTRY_ADAPTERS_URL, - REGISTRY_BOTS_URL, - REGISTRY_DRIVERS_URL, - REGISTRY_PLUGIN_CONFIG_URL, - REGISTRY_PLUGINS_URL, - REGISTRY_RESULTS_URL, - STORE_ADAPTERS_URL, - STORE_BOTS_URL, - STORE_DRIVERS_URL, - STORE_PLUGINS_URL, -) - - -def load_json(name: str) -> dict: - # 商店为 json5 格式 - if name.startswith("store_"): - name = f"{name}.json5" - else: - name = f"{name}.json" - - path = Path(__file__).parent / "store" / name - with path.open("r", encoding="utf-8") as f: - return pyjson5.decode_io(f) # type: ignore - @pytest.fixture def mocked_store_data( @@ -56,15 +30,4 @@ def mocked_store_data( "src.providers.store_test.store.PLUGIN_CONFIG_PATH", paths["plugin_configs"] ) - mocked_api.get(STORE_ADAPTERS_URL).respond(json=load_json("store_adapters")) - mocked_api.get(STORE_BOTS_URL).respond(json=load_json("store_bots")) - mocked_api.get(STORE_DRIVERS_URL).respond(json=load_json("store_drivers")) - mocked_api.get(STORE_PLUGINS_URL).respond(json=load_json("store_plugins")) - mocked_api.get(REGISTRY_ADAPTERS_URL).respond(json=load_json("registry_adapters")) - mocked_api.get(REGISTRY_BOTS_URL).respond(json=load_json("registry_bots")) - mocked_api.get(REGISTRY_DRIVERS_URL).respond(json=load_json("registry_drivers")) - mocked_api.get(REGISTRY_PLUGINS_URL).respond(json=load_json("registry_plugins")) - mocked_api.get(REGISTRY_RESULTS_URL).respond(json=load_json("registry_results")) - mocked_api.get(REGISTRY_PLUGIN_CONFIG_URL).respond(json=load_json("plugin_configs")) - return paths diff --git a/tests/providers/store_test/store/plugin_configs.json b/tests/store/plugin_configs.json similarity index 100% rename from tests/providers/store_test/store/plugin_configs.json rename to tests/store/plugin_configs.json diff --git a/tests/providers/store_test/store/registry_adapters.json b/tests/store/registry_adapters.json similarity index 74% rename from tests/providers/store_test/store/registry_adapters.json rename to tests/store/registry_adapters.json index 32264ee6..a0ced331 100644 --- a/tests/providers/store_test/store/registry_adapters.json +++ b/tests/store/registry_adapters.json @@ -7,6 +7,8 @@ "author": "yanyongyu", "homepage": "https://onebot.adapters.nonebot.dev/", "tags": [], - "is_official": true + "is_official": true, + "time": "2024-10-24T07:34:56.115315Z", + "version": "2.4.6" } ] diff --git a/tests/providers/store_test/store/registry_bots.json b/tests/store/registry_bots.json similarity index 100% rename from tests/providers/store_test/store/registry_bots.json rename to tests/store/registry_bots.json diff --git a/tests/providers/store_test/store/registry_drivers.json b/tests/store/registry_drivers.json similarity index 69% rename from tests/providers/store_test/store/registry_drivers.json rename to tests/store/registry_drivers.json index 64337f69..52743f98 100644 --- a/tests/providers/store_test/store/registry_drivers.json +++ b/tests/store/registry_drivers.json @@ -7,7 +7,9 @@ "author": "yanyongyu", "homepage": "/docs/advanced/driver", "tags": [], - "is_official": true + "is_official": true, + "time": "2024-10-31T13:47:14.152851Z", + "version": "2.4.0" }, { "module_name": "~fastapi", @@ -17,6 +19,8 @@ "author": "yanyongyu", "homepage": "/docs/advanced/driver", "tags": [], - "is_official": true + "is_official": true, + "time": "2024-10-31T13:47:14.152851Z", + "version": "2.4.0" } ] diff --git a/tests/providers/store_test/store/registry_plugins.json b/tests/store/registry_plugins.json similarity index 85% rename from tests/providers/store_test/store/registry_plugins.json rename to tests/store/registry_plugins.json index 1bd33e22..c85b58e0 100644 --- a/tests/providers/store_test/store/registry_plugins.json +++ b/tests/store/registry_plugins.json @@ -11,8 +11,8 @@ "type": "library", "supported_adapters": null, "valid": true, - "time": "2023-06-22 11:58:18", - "version": "0.0.1", + "time": "2024-06-20T07:53:23.524486Z", + "version": "1.3.0", "skip_test": false }, { @@ -27,8 +27,8 @@ "type": "application", "supported_adapters": null, "valid": true, - "time": "2023-06-22 12:10:18", - "version": "0.0.1", + "time": "2024-07-13T04:41:40.905441Z", + "version": "0.5.0", "skip_test": false } ] diff --git a/tests/providers/store_test/store/registry_results.json b/tests/store/registry_results.json similarity index 97% rename from tests/providers/store_test/store/registry_results.json rename to tests/store/registry_results.json index c1eb5e1a..774dbbd8 100644 --- a/tests/providers/store_test/store/registry_results.json +++ b/tests/store/registry_results.json @@ -7,9 +7,7 @@ "load": true, "metadata": true }, - "inputs": { - "config": "" - }, + "config": "", "outputs": { "validation": null, "load": "datastore", diff --git a/tests/providers/store_test/store/store_adapters.json5 b/tests/store/store_adapters.json5 similarity index 100% rename from tests/providers/store_test/store/store_adapters.json5 rename to tests/store/store_adapters.json5 diff --git a/tests/providers/store_test/store/store_bots.json5 b/tests/store/store_bots.json5 similarity index 100% rename from tests/providers/store_test/store/store_bots.json5 rename to tests/store/store_bots.json5 diff --git a/tests/providers/store_test/store/store_drivers.json5 b/tests/store/store_drivers.json5 similarity index 100% rename from tests/providers/store_test/store/store_drivers.json5 rename to tests/store/store_drivers.json5 diff --git a/tests/providers/store_test/store/store_plugins.json5 b/tests/store/store_plugins.json5 similarity index 100% rename from tests/providers/store_test/store/store_plugins.json5 rename to tests/store/store_plugins.json5 From ba1e0fc36fa8f9ea7c9ff971ec64fbd7942fb579 Mon Sep 17 00:00:00 2001 From: uy_sun Date: Sun, 15 Dec 2024 14:46:09 +0800 Subject: [PATCH 04/12] =?UTF-8?q?test:=20=E4=BF=AE=E5=A4=8D=20validation?= =?UTF-8?q?=20=E7=9B=B8=E5=85=B3=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/validation/models.py | 10 ++ src/providers/validation/utils.py | 5 +- tests/conftest.py | 8 +- .../validation/fields/test_missing.py | 41 ++++--- .../providers/validation/fields/test_name.py | 5 +- .../fields/test_plugin_supported_adapters.py | 10 +- .../providers/validation/fields/test_pypi.py | 42 ++++---- .../providers/validation/fields/test_tags.py | 28 ++--- tests/providers/validation/test_adapter.py | 10 +- tests/providers/validation/test_driver.py | 102 ++++++++++++++++++ tests/providers/validation/test_plugin.py | 14 +-- tests/providers/validation/utils.py | 24 +++++ 12 files changed, 228 insertions(+), 71 deletions(-) create mode 100644 tests/providers/validation/test_driver.py diff --git a/src/providers/validation/models.py b/src/providers/validation/models.py index 76e94f60..b1c73453 100644 --- a/src/providers/validation/models.py +++ b/src/providers/validation/models.py @@ -90,6 +90,10 @@ class PyPIMixin(BaseModel): @field_validator("module_name", mode="before") @classmethod def module_name_validator(cls, v: str) -> str: + # NoneBot 内置驱动器都是以 ~ 开头的 + if issubclass(cls, DriverPublishInfo) and v.startswith("~"): + return v + if not PYTHON_MODULE_NAME_REGEX.match(v): raise PydanticCustomError("module_name", "包名不符合规范") return v @@ -97,6 +101,12 @@ def module_name_validator(cls, v: str) -> str: @field_validator("project_link", mode="before") @classmethod def project_link_validator(cls, v: str) -> str: + # NoneBot 内置驱动器需要特殊处理 + if issubclass(cls, DriverPublishInfo) and ( + v == "" or v.startswith("nonebot2[") + ): + return v + if not PYPI_PACKAGE_NAME_PATTERN.match(v): raise PydanticCustomError("project_link.name", "PyPI 项目名不符合规范") diff --git a/src/providers/validation/utils.py b/src/providers/validation/utils.py index a0729567..1134dde8 100644 --- a/src/providers/validation/utils.py +++ b/src/providers/validation/utils.py @@ -27,11 +27,12 @@ def get_pypi_name(project_link: str) -> str: return data["info"]["name"] -def get_pypi_version(project_link: str) -> str: +def get_pypi_version(project_link: str) -> str | None: """获取 PyPI 版本""" url = f"https://pypi.org/pypi/{project_link}/json" r = get_url(url) - r.raise_for_status() + if r.status_code != 200: + return None data = load_json(r.text) return data["info"]["version"] diff --git a/tests/conftest.py b/tests/conftest.py index c714b102..9fb2faad 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -147,9 +147,9 @@ class PyPIProject(TypedDict): @pytest.fixture def mocked_api(respx_mock: MockRouter): # 主页数据 - respx_mock.get("exception", name="exception").mock(side_effect=httpx.ConnectError) respx_mock.get("https://nonebot.dev/", name="homepage").respond() respx_mock.get("https://www.baidu.com", name="homepage_failed").respond(404) + respx_mock.get("exception", name="exception").mock(side_effect=httpx.ConnectError) # GitHub 数据 respx_mock.get("https://api.github.com/user/1", name="github_username_1").respond( json={"login": "he0119"} @@ -195,6 +195,12 @@ def mocked_api(respx_mock: MockRouter): version="0.0.1", upload_time_iso_8601="2023-10-01T00:00:00.000000Z", ), + PyPIProject( + url="nonebot2", + name="nonebot2", + version="2.4.0", + upload_time_iso_8601="2024-10-31T13:47:14.152851Z", + ), ] for project in pypi_projects: respx_mock.get( diff --git a/tests/providers/validation/fields/test_missing.py b/tests/providers/validation/fields/test_missing.py index 596db40e..d3b67008 100644 --- a/tests/providers/validation/fields/test_missing.py +++ b/tests/providers/validation/fields/test_missing.py @@ -19,7 +19,6 @@ async def test_fields_missing_plugin(mocked_api: MockRouter) -> None: metadata=False, version=None, ) - data["version"] = None result = validate_info(PublishType.PLUGIN, data, []) @@ -29,7 +28,7 @@ async def test_fields_missing_plugin(mocked_api: MockRouter) -> None: { "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "author": "author", "author_id": 1, "tags": [{"label": "test", "color": "#ffffff"}], @@ -42,6 +41,24 @@ async def test_fields_missing_plugin(mocked_api: MockRouter) -> None: assert result.info is None assert result.errors == snapshot( [ + { + "type": "missing", + "loc": ("version",), + "msg": "字段不存在", + "input": { + "author": "author", + "module_name": "module_name", + "project_link": "project_link", + "tags": '[{"label": "test", "color": "#ffffff"}]', + "supported_adapters": None, + "skip_test": False, + "metadata": False, + "author_id": 1, + "load": False, + "test_output": "error", + "time": "2023-09-01T00:00:00.000000Z", + }, + }, { "type": "missing", "loc": ("name",), @@ -57,8 +74,7 @@ async def test_fields_missing_plugin(mocked_api: MockRouter) -> None: "author_id": 1, "load": False, "test_output": "error", - "version": None, - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", }, }, { @@ -76,8 +92,7 @@ async def test_fields_missing_plugin(mocked_api: MockRouter) -> None: "author_id": 1, "load": False, "test_output": "error", - "version": None, - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", }, }, { @@ -95,8 +110,7 @@ async def test_fields_missing_plugin(mocked_api: MockRouter) -> None: "author_id": 1, "load": False, "test_output": "error", - "version": None, - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", }, }, { @@ -114,8 +128,7 @@ async def test_fields_missing_plugin(mocked_api: MockRouter) -> None: "author_id": 1, "load": False, "test_output": "error", - "version": None, - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", }, }, { @@ -125,14 +138,8 @@ async def test_fields_missing_plugin(mocked_api: MockRouter) -> None: "input": False, "ctx": {"output": "error"}, }, - { - "type": "string_type", - "loc": ("version",), - "msg": "值不是合法的字符串", - "input": None, - }, ] ) - assert mocked_api["project_link"].called + assert mocked_api["pypi_project_link"].called assert not mocked_api["homepage"].called diff --git a/tests/providers/validation/fields/test_name.py b/tests/providers/validation/fields/test_name.py index 06d524d4..b56142b4 100644 --- a/tests/providers/validation/fields/test_name.py +++ b/tests/providers/validation/fields/test_name.py @@ -20,7 +20,8 @@ async def test_name_too_long(mocked_api: MockRouter) -> None: { "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", + "version": "0.0.1", "desc": "desc", "author": "author", "homepage": "https://nonebot.dev", @@ -41,5 +42,5 @@ async def test_name_too_long(mocked_api: MockRouter) -> None: ] ) - assert mocked_api["project_link"].called + assert mocked_api["pypi_project_link"].called assert mocked_api["homepage"].called diff --git a/tests/providers/validation/fields/test_plugin_supported_adapters.py b/tests/providers/validation/fields/test_plugin_supported_adapters.py index 300a1a04..5d3f0ffd 100644 --- a/tests/providers/validation/fields/test_plugin_supported_adapters.py +++ b/tests/providers/validation/fields/test_plugin_supported_adapters.py @@ -34,14 +34,14 @@ async def test_plugin_supported_adapters_none(mocked_api: MockRouter) -> None: "load": True, "version": "0.0.1", "test_output": "test_output", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", } ) assert result.valid_data == snapshot( { "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "author", @@ -75,7 +75,7 @@ async def test_plugin_supported_adapters_set(mocked_api: MockRouter) -> None: { "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "author", @@ -119,7 +119,7 @@ async def test_plugin_supported_adapters_json(mocked_api: MockRouter) -> None: { "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "author", @@ -167,7 +167,7 @@ async def test_plugin_supported_adapters_missing_adapters( { "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "author", diff --git a/tests/providers/validation/fields/test_pypi.py b/tests/providers/validation/fields/test_pypi.py index fa2361fe..523a8c32 100644 --- a/tests/providers/validation/fields/test_pypi.py +++ b/tests/providers/validation/fields/test_pypi.py @@ -17,7 +17,8 @@ async def test_project_link_invalid(mocked_api: MockRouter) -> None: assert result.valid_data == snapshot( { "module_name": "module_name", - "time": "2023-10-01T00:00:00+00:00", + "time": "2023-10-01T00:00:00.000000Z", + "version": "0.0.1", "name": "name", "desc": "desc", "author": "author", @@ -54,7 +55,8 @@ async def test_module_name_invalid(mocked_api: MockRouter) -> None: assert result.valid_data == snapshot( { "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", + "version": "0.0.1", "name": "name", "desc": "desc", "author": "author", @@ -75,7 +77,7 @@ async def test_module_name_invalid(mocked_api: MockRouter) -> None: ) ] - assert mocked_api["project_link"].called + assert mocked_api["pypi_project_link"].called assert mocked_api["homepage"].called @@ -83,14 +85,11 @@ async def test_name_duplication(mocked_api: MockRouter) -> None: """测试名称重复的情况""" from src.providers.validation import PublishType, validate_info - data = generate_adapter_data( - module_name="module_name1", - project_link="project_link1", - ) + data = generate_adapter_data() previous_data = [ { - "module_name": "module_name1", - "project_link": "project_link1", + "module_name": "module_name", + "project_link": "project_link", "author_id": 1, "name": "name", "desc": "desc", @@ -108,23 +107,23 @@ async def test_name_duplication(mocked_api: MockRouter) -> None: { "type": "duplication", "loc": (), - "msg": "PyPI 项目名 project_link1 加包名 module_name1 的值与商店重复", + "msg": "PyPI 项目名 project_link 加包名 module_name 的值与商店重复", "input": { "name": "name", "desc": "desc", "author": "author", - "module_name": "module_name1", - "project_link": "project_link1", + "module_name": "module_name", + "project_link": "project_link", "homepage": "https://nonebot.dev", "tags": '[{"label": "test", "color": "#ffffff"}]', "author_id": 1, }, - "ctx": {"project_link": "project_link1", "module_name": "module_name1"}, + "ctx": {"project_link": "project_link", "module_name": "module_name"}, } ] ) - assert not mocked_api["project_link1"].called + assert not mocked_api["pypi_project_link"].called assert not mocked_api["homepage"].called @@ -135,9 +134,7 @@ async def test_name_duplication_previos_data_missing(mocked_api: MockRouter) -> """ from src.providers.validation import PublishType, validate_info - data = generate_adapter_data( - module_name="module_name1", project_link="project_link1" - ) + data = generate_adapter_data() result = validate_info(PublishType.ADAPTER, data, None) @@ -155,8 +152,8 @@ async def test_name_duplication_previos_data_missing(mocked_api: MockRouter) -> "name": "name", "desc": "desc", "author": "author", - "module_name": "module_name1", - "project_link": "project_link1", + "module_name": "module_name", + "project_link": "project_link", "homepage": "https://nonebot.dev", "tags": '[{"label": "test", "color": "#ffffff"}]', "author_id": 1, @@ -165,7 +162,7 @@ async def test_name_duplication_previos_data_missing(mocked_api: MockRouter) -> ] ) - assert not mocked_api["project_link1"].called + assert not mocked_api["pypi_project_link"].called assert not mocked_api["homepage"].called @@ -183,7 +180,8 @@ async def test_project_link_normalization(mocked_api: MockRouter) -> None: { "module_name": "module_name", "project_link": "project-link-normalization", - "time": "2023-10-01T00:00:00+00:00", + "time": "2023-10-01T00:00:00.000000Z", + "version": "0.0.1", "name": "name", "desc": "desc", "author": "author", @@ -196,4 +194,4 @@ async def test_project_link_normalization(mocked_api: MockRouter) -> None: assert result.errors == [] assert mocked_api["homepage"].called - assert mocked_api["project_link_normalization"].called + assert mocked_api["pypi_project_link_normalization"].called diff --git a/tests/providers/validation/fields/test_tags.py b/tests/providers/validation/fields/test_tags.py index 39a144e0..27238833 100644 --- a/tests/providers/validation/fields/test_tags.py +++ b/tests/providers/validation/fields/test_tags.py @@ -20,7 +20,7 @@ async def test_tags_color_missing(mocked_api: MockRouter) -> None: { "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "author", @@ -47,7 +47,7 @@ async def test_tags_color_missing(mocked_api: MockRouter) -> None: ) ] - assert mocked_api["project_link"].called + assert mocked_api["pypi_project_link"].called assert mocked_api["homepage"].called @@ -65,7 +65,7 @@ async def test_tags_color_invalid(mocked_api: MockRouter) -> None: { "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "author", @@ -92,7 +92,7 @@ async def test_tags_color_invalid(mocked_api: MockRouter) -> None: ) ] - assert mocked_api["project_link"].called + assert mocked_api["pypi_project_link"].called assert mocked_api["homepage"].called @@ -110,7 +110,7 @@ async def test_tags_label_invalid(mocked_api: MockRouter) -> None: { "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "author", @@ -138,7 +138,7 @@ async def test_tags_label_invalid(mocked_api: MockRouter) -> None: ) ] - assert mocked_api["project_link"].called + assert mocked_api["pypi_project_link"].called assert mocked_api["homepage"].called @@ -163,7 +163,7 @@ async def test_tags_number_invalid(mocked_api: MockRouter) -> None: { "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "author", @@ -196,7 +196,7 @@ async def test_tags_number_invalid(mocked_api: MockRouter) -> None: ) ] - assert mocked_api["project_link"].called + assert mocked_api["pypi_project_link"].called assert mocked_api["homepage"].called @@ -215,7 +215,7 @@ async def test_tags_json_invalid(mocked_api: MockRouter) -> None: { "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "author", @@ -242,7 +242,7 @@ async def test_tags_json_invalid(mocked_api: MockRouter) -> None: ) ] - assert mocked_api["project_link"].called + assert mocked_api["pypi_project_link"].called assert mocked_api["homepage"].called @@ -261,7 +261,7 @@ async def test_tags_json_not_list(mocked_api: MockRouter) -> None: { "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "author", @@ -288,7 +288,7 @@ async def test_tags_json_not_list(mocked_api: MockRouter) -> None: ) ] - assert mocked_api["project_link"].called + assert mocked_api["pypi_project_link"].called assert mocked_api["homepage"].called @@ -306,7 +306,7 @@ async def test_tags_json_not_dict(mocked_api: MockRouter) -> None: { "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "author", @@ -334,5 +334,5 @@ async def test_tags_json_not_dict(mocked_api: MockRouter) -> None: ) ] - assert mocked_api["project_link"].called + assert mocked_api["pypi_project_link"].called assert mocked_api["homepage"].called diff --git a/tests/providers/validation/test_adapter.py b/tests/providers/validation/test_adapter.py index 5b095cf9..95a71d3d 100644 --- a/tests/providers/validation/test_adapter.py +++ b/tests/providers/validation/test_adapter.py @@ -24,7 +24,8 @@ async def test_adapter_info_validation_success(mocked_api: MockRouter) -> None: "homepage": "https://nonebot.dev", "tags": '[{"label": "test", "color": "#ffffff"}]', "author_id": 1, - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", + "version": "0.0.1", } ) @@ -72,6 +73,12 @@ async def test_adapter_info_validation_failed(mocked_api: MockRouter) -> None: "msg": "值不是合法的字符串", "input": None, }, + { + "type": "string_type", + "loc": ("version",), + "msg": "值不是合法的字符串", + "input": None, + }, { "type": "homepage", "loc": ("homepage",), @@ -104,6 +111,7 @@ async def test_adapter_info_validation_failed(mocked_api: MockRouter) -> None: "tags": '[{"label": "test", "color": "#ffffff"}, {"label": "testtoolong", "color": "#fffffff"}]', "author_id": 1, "time": None, + "version": None, }, type=PublishType.ADAPTER, valid_data={ diff --git a/tests/providers/validation/test_driver.py b/tests/providers/validation/test_driver.py new file mode 100644 index 00000000..f463db58 --- /dev/null +++ b/tests/providers/validation/test_driver.py @@ -0,0 +1,102 @@ +from inline_snapshot import snapshot +from respx import MockRouter + +from tests.providers.validation.utils import generate_driver_data + + +async def test_driver_info_validation_success(mocked_api: MockRouter) -> None: + """测试验证成功的情况""" + from src.providers.validation import DriverPublishInfo, PublishType, validate_info + + data = generate_driver_data() + + result = validate_info(PublishType.DRIVER, data, []) + + assert result.valid + assert result.type == PublishType.DRIVER + assert result.raw_data == snapshot( + { + "name": "name", + "desc": "desc", + "author": "author", + "module_name": "module_name", + "project_link": "project_link", + "homepage": "https://nonebot.dev", + "tags": '[{"label": "test", "color": "#ffffff"}]', + "author_id": 1, + "time": "2023-09-01T00:00:00.000000Z", + "version": "0.0.1", + } + ) + + assert isinstance(result.info, DriverPublishInfo) + assert result.errors == [] + + assert mocked_api["homepage"].called + assert mocked_api["pypi_project_link"].called + + +async def test_driver_info_validation_none(mocked_api: MockRouter) -> None: + """内置驱动器 none 的情况""" + from src.providers.validation import DriverPublishInfo, PublishType, validate_info + + data = generate_driver_data(project_link="", module_name="~none") + + result = validate_info(PublishType.DRIVER, data, []) + + assert result.valid + assert result.type == PublishType.DRIVER + assert result.raw_data == snapshot( + { + "name": "name", + "desc": "desc", + "author": "author", + "module_name": "~none", + "project_link": "", + "homepage": "https://nonebot.dev", + "tags": '[{"label": "test", "color": "#ffffff"}]', + "author_id": 1, + "time": "2024-10-31T13:47:14.152851Z", + "version": "2.4.0", + } + ) + + assert isinstance(result.info, DriverPublishInfo) + assert result.errors == [] + + assert mocked_api["homepage"].called + assert mocked_api["pypi_nonebot2"].called + + +async def test_driver_info_validation_fastapi(mocked_api: MockRouter) -> None: + """内置驱动器 fastapi 的情况""" + from src.providers.validation import DriverPublishInfo, PublishType, validate_info + + data = generate_driver_data( + project_link="nonebot2[fastapi]", module_name="~fastapi" + ) + + result = validate_info(PublishType.DRIVER, data, []) + + assert result.valid + assert result.type == PublishType.DRIVER + assert result.raw_data == snapshot( + { + "name": "name", + "desc": "desc", + "author": "author", + "module_name": "~fastapi", + "project_link": "nonebot2[fastapi]", + "homepage": "https://nonebot.dev", + "tags": '[{"label": "test", "color": "#ffffff"}]', + "author_id": 1, + "time": "2024-10-31T13:47:14.152851Z", + "version": "2.4.0", + } + ) + + assert isinstance(result.info, DriverPublishInfo) + assert result.errors == [] + + assert mocked_api["homepage"].called + assert mocked_api["pypi_nonebot2"].called diff --git a/tests/providers/validation/test_plugin.py b/tests/providers/validation/test_plugin.py index c16a544c..35def94a 100644 --- a/tests/providers/validation/test_plugin.py +++ b/tests/providers/validation/test_plugin.py @@ -31,7 +31,7 @@ async def test_plugin_info_validation_success(mocked_api: MockRouter) -> None: "load": True, "version": "0.0.1", "test_output": "test_output", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", } ) assert isinstance(result.info, PluginPublishInfo) @@ -113,13 +113,13 @@ async def test_plugin_info_validation_failed(mocked_api: MockRouter) -> None: "load": True, "version": "0.0.1", "test_output": "test_output", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", }, type=PublishType.PLUGIN, valid_data={ "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "author", @@ -174,13 +174,13 @@ async def test_plugin_info_validation_plugin_load_failed( "load": False, "version": "0.0.1", "test_output": "test_output", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", }, type=PublishType.PLUGIN, valid_data={ "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "author", @@ -257,13 +257,13 @@ async def test_plugin_info_validation_plugin_invalid_metadata( "load": True, "version": "0.0.1", "test_output": "test_output", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", }, type=PublishType.PLUGIN, valid_data={ "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "author", diff --git a/tests/providers/validation/utils.py b/tests/providers/validation/utils.py index 70c82b74..ceea23a4 100644 --- a/tests/providers/validation/utils.py +++ b/tests/providers/validation/utils.py @@ -56,6 +56,30 @@ def generate_bot_data( ) +def generate_driver_data( + module_name: str | None = "module_name", + project_link: str | None = "project_link", + name: str | None = "name", + desc: str | None = "desc", + author: str | None = "author", + homepage: str | None = "https://nonebot.dev", + tags: list | None = [{"label": "test", "color": "#ffffff"}], + author_id: int | None = 1, +): + return exclude_none( + { + "module_name": module_name, + "project_link": project_link, + "name": name, + "desc": desc, + "author": author, + "homepage": homepage, + "tags": json.dumps(tags), + "author_id": author_id, + } + ) + + def generate_plugin_data( author: str | None = "author", module_name: str | None = "module_name", From 059d84d5c9dce9b262a60abb206ff610b919b7b6 Mon Sep 17 00:00:00 2001 From: uy_sun Date: Mon, 16 Dec 2024 22:51:16 +0800 Subject: [PATCH 05/12] =?UTF-8?q?test:=20=E4=BF=AE=E5=A4=8D=20store=5Ftest?= =?UTF-8?q?=20=E7=9B=B8=E5=85=B3=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/models.py | 69 +++++++++++++++++++ src/providers/store_test/store.py | 38 ++++------ src/providers/store_test/validation.py | 2 +- src/providers/utils.py | 7 ++ src/providers/validation/models.py | 6 +- src/providers/validation/utils.py | 7 -- tests/conftest.py | 33 ++++++--- tests/providers/store_test/conftest.py | 16 ++--- tests/providers/store_test/test_store_sync.py | 20 ++++-- tests/providers/store_test/test_store_test.py | 52 +++++++------- .../store_test/test_validate_plugin.py | 12 ++-- .../validation/fields/test_homepage.py | 4 +- tests/store/registry_results.json | 2 +- 13 files changed, 175 insertions(+), 93 deletions(-) diff --git a/src/providers/models.py b/src/providers/models.py index 2c7512ff..344c1721 100644 --- a/src/providers/models.py +++ b/src/providers/models.py @@ -8,6 +8,8 @@ from src.providers.constants import BOT_KEY_TEMPLATE, PYPI_KEY_TEMPLATE from src.providers.docker_test import Metadata +from src.providers.utils import get_author_name +from src.providers.validation import validate_info from src.providers.validation.models import ( AdapterPublishInfo, BotPublishInfo, @@ -61,6 +63,21 @@ def from_publish_info(cls, publish_info: AdapterPublishInfo) -> Self: is_official=publish_info.is_official, ) + def to_registry(self) -> "RegistryAdapter": + """将仓库数据转换为注册表数据 + + 将获取 author 信息,重新验证数据 + """ + author = get_author_name(self.author_id) + result = validate_info( + PublishType.ADAPTER, + {**self.model_dump(), "author": author}, + [], + ) + if result.info is None or not isinstance(result.info, AdapterPublishInfo): + raise ValueError(f"数据验证失败: {result.errors}") + return RegistryAdapter.from_publish_info(result.info) + class StoreBot(BaseModel): """NoneBot 仓库中的机器人数据""" @@ -87,6 +104,21 @@ def from_publish_info(cls, publish_info: BotPublishInfo) -> Self: is_official=publish_info.is_official, ) + def to_registry(self) -> "RegistryBot": + """将仓库数据转换为注册表数据 + + 将获取 author 信息,重新验证数据 + """ + author = get_author_name(self.author_id) + result = validate_info( + PublishType.BOT, + {**self.model_dump(), "author": author}, + [], + ) + if result.info is None or not isinstance(result.info, BotPublishInfo): + raise ValueError(f"数据验证失败: {result.errors}") + return RegistryBot.from_publish_info(result.info) + class StoreDriver(BaseModel): """NoneBot 仓库中的驱动数据""" @@ -119,6 +151,21 @@ def from_publish_info(cls, publish_info: DriverPublishInfo) -> Self: is_official=publish_info.is_official, ) + def to_registry(self) -> "RegistryDriver": + """将仓库数据转换为注册表数据 + + 将获取 author 信息,重新验证数据 + """ + author = get_author_name(self.author_id) + result = validate_info( + PublishType.DRIVER, + {**self.model_dump(), "author": author}, + [], + ) + if result.info is None or not isinstance(result.info, DriverPublishInfo): + raise ValueError(f"数据验证失败: {result.errors}") + return RegistryDriver.from_publish_info(result.info) + class StorePlugin(BaseModel): """NoneBot 仓库中的插件数据""" @@ -201,6 +248,12 @@ def from_publish_info(cls, publish_info: AdapterPublishInfo) -> Self: version=publish_info.version, ) + def update(self, store: StoreAdapter) -> "RegistryAdapter": + """根据商店数据更新注册表数据""" + data = self.model_dump() + data.update(store.model_dump()) + return RegistryAdapter(**data) + class RegistryBot(BaseModel): """NoneBot 商店机器人数据""" @@ -227,6 +280,12 @@ def from_publish_info(cls, publish_info: BotPublishInfo) -> Self: is_official=publish_info.is_official, ) + def update(self, store: StoreBot) -> "RegistryBot": + """根据商店数据更新注册表数据""" + data = self.model_dump() + data.update(store.model_dump()) + return RegistryBot(**data) + class RegistryDriver(BaseModel): """NoneBot 商店驱动数据""" @@ -263,6 +322,12 @@ def from_publish_info(cls, publish_info: DriverPublishInfo) -> Self: version=publish_info.version, ) + def update(self, store: StoreDriver) -> "RegistryDriver": + """根据商店数据更新注册表数据""" + data = self.model_dump() + data.update(store.model_dump()) + return RegistryDriver(**data) + class RegistryPlugin(BaseModel): """NoneBot 商店插件数据""" @@ -317,6 +382,10 @@ def metadata(self) -> dict[str, Any]: "supported_adapters": self.supported_adapters, } + def update(self, store: StorePlugin) -> "RegistryPlugin": + """根据商店数据更新注册表数据""" + return RegistryPlugin(**self.model_dump(), **store.model_dump()) + RegistryModels: TypeAlias = ( RegistryAdapter | RegistryBot | RegistryDriver | RegistryPlugin diff --git a/src/providers/store_test/store.py b/src/providers/store_test/store.py index 08ac1216..b1a6262e 100644 --- a/src/providers/store_test/store.py +++ b/src/providers/store_test/store.py @@ -33,7 +33,6 @@ get_latest_version, load_json_from_web, ) -from src.providers.validation.utils import get_author_name from .constants import ( ADAPTERS_PATH, @@ -316,38 +315,26 @@ async def sync_store(self): 以商店数据为准,更新商店数据到仓库中,如果仓库中不存在则获取用户名后存储 """ for key in self._store_adapters: - if key not in self._previous_adapters: - author = get_author_name(self._store_adapters[key].author_id) - self._previous_adapters[key] = RegistryAdapter( - **self._store_adapters[key].model_dump(), author=author + if key in self._previous_adapters: + self._previous_adapters[key] = self._previous_adapters[key].update( + self._store_adapters[key] ) else: - self._previous_adapters[key] = RegistryAdapter( - **self._store_adapters[key].model_dump(), - author=self._previous_adapters[key].author, - ) + self._previous_adapters[key] = self._store_adapters[key].to_registry() for key in self._store_bots: - if key not in self._previous_bots: - author = get_author_name(self._store_bots[key].author_id) - self._previous_bots[key] = RegistryBot( - **self._store_bots[key].model_dump(), author=author + if key in self._previous_bots: + self._previous_bots[key] = self._previous_bots[key].update( + self._store_bots[key] ) else: - self._previous_bots[key] = RegistryBot( - **self._store_bots[key].model_dump(), - author=self._previous_bots[key].author, - ) + self._previous_bots[key] = self._store_bots[key].to_registry() for key in self._store_drivers: - if key not in self._previous_drivers: - author = get_author_name(self._store_drivers[key].author_id) - self._previous_drivers[key] = RegistryDriver( - **self._store_drivers[key].model_dump(), author=author + if key in self._previous_drivers: + self._previous_drivers[key] = self._previous_drivers[key].update( + self._store_drivers[key] ) else: - self._previous_drivers[key] = RegistryDriver( - **self._store_drivers[key].model_dump(), - author=self._previous_drivers[key].author, - ) + self._previous_drivers[key] = self._store_drivers[key].to_registry() for key in self._store_plugins: if key in self._previous_plugins: plugin_data = self._previous_plugins[key].model_dump() @@ -355,6 +342,7 @@ async def sync_store(self): # TODO: 如果 author_id 变化,应该重新获取 author plugin_data.update(self._store_plugins[key].model_dump()) self._previous_plugins[key] = RegistryPlugin(**plugin_data) + # TODO: 如果插件不存在,尝试重新测试获取相关信息验证 def generate_github_summary(self, results: dict[str, StoreTestResult]): """生成 GitHub 摘要""" diff --git a/src/providers/store_test/validation.py b/src/providers/store_test/validation.py index 0491898e..f33d25e8 100644 --- a/src/providers/store_test/validation.py +++ b/src/providers/store_test/validation.py @@ -5,13 +5,13 @@ from src.providers.docker_test import DockerPluginTest from src.providers.logger import logger from src.providers.models import RegistryPlugin, StorePlugin, StoreTestResult +from src.providers.utils import get_author_name, get_upload_time from src.providers.validation import ( PluginPublishInfo, PublishType, ValidationDict, validate_info, ) -from src.providers.validation.utils import get_author_name, get_upload_time async def validate_plugin( diff --git a/src/providers/utils.py b/src/providers/utils.py index 75e1ca9d..77719b56 100644 --- a/src/providers/utils.py +++ b/src/providers/utils.py @@ -105,3 +105,10 @@ def add_step_summary(summary: str): with open(github_step_summary, "a", encoding="utf-8") as f: f.write(summary + "\n") logger.debug(f"已添加作业摘要:{summary}") + + +@cache +def get_author_name(author_id: int) -> str: + """通过作者的ID获取作者名字""" + url = f"https://api.github.com/user/{author_id}" + return load_json_from_web(url)["login"] diff --git a/src/providers/validation/models.py b/src/providers/validation/models.py index b1c73453..6a4198df 100644 --- a/src/providers/validation/models.py +++ b/src/providers/validation/models.py @@ -170,7 +170,8 @@ class PublishInfo(abc.ABC, BaseModel): author: str author_id: int homepage: Annotated[ - str, StringConstraints(strip_whitespace=True, pattern=r"^https?://.*$") + str, + StringConstraints(strip_whitespace=True, pattern=r"^(https?://.*|/docs/.*)$"), ] tags: list[Tag] = Field(max_length=3) is_official: bool = Field(default=False) @@ -199,6 +200,9 @@ def collect_valid_values( @classmethod def homepage_validator(cls, v: str) -> str: if v: + # 内置驱动器的主页可以不是网址 + if issubclass(cls, DriverPublishInfo) and v.startswith("/docs/"): + return v status_code, msg = check_url(v) if status_code != 200: raise PydanticCustomError( diff --git a/src/providers/validation/utils.py b/src/providers/validation/utils.py index 1134dde8..ba5a7415 100644 --- a/src/providers/validation/utils.py +++ b/src/providers/validation/utils.py @@ -69,13 +69,6 @@ def check_url(url: str) -> tuple[int, str]: return -1, str(e) -@cache -def get_author_name(author_id: int) -> str: - """通过作者的ID获取作者名字""" - url = f"https://api.github.com/user/{author_id}" - return load_json_from_web(url)["login"] - - def get_adapters() -> set[str]: """获取适配器列表""" adapters = load_json_from_web(STORE_ADAPTERS_URL) diff --git a/tests/conftest.py b/tests/conftest.py index 9fb2faad..9b65540c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -148,6 +148,12 @@ class PyPIProject(TypedDict): def mocked_api(respx_mock: MockRouter): # 主页数据 respx_mock.get("https://nonebot.dev/", name="homepage").respond() + respx_mock.get( + "https://onebot.adapters.nonebot.dev/", name="homepage_onebot" + ).respond() + respx_mock.get( + "https://github.com/cscs181/QQ-GitHub-Bot", name="homepage_qq_github_bot" + ).respond() respx_mock.get("https://www.baidu.com", name="homepage_failed").respond(404) respx_mock.get("exception", name="exception").mock(side_effect=httpx.ConnectError) # GitHub 数据 @@ -201,6 +207,12 @@ def mocked_api(respx_mock: MockRouter): version="2.4.0", upload_time_iso_8601="2024-10-31T13:47:14.152851Z", ), + PyPIProject( + url="nonebot-adapter-onebot", + name="nonebot-adapter-onebot", + version="2.4.6", + upload_time_iso_8601="2024-10-24T07:34:56.115315Z", + ), ] for project in pypi_projects: respx_mock.get( @@ -218,35 +230,34 @@ def mocked_api(respx_mock: MockRouter): ).respond(404) # 商店数据 store_path = Path(__file__).parent / "store" - respx_mock.get(STORE_ADAPTERS_URL).respond( + respx_mock.get(STORE_ADAPTERS_URL, name="store_adapter").respond( text=(store_path / "store_adapters.json5").read_text(encoding="utf8") ) - respx_mock.get(STORE_BOTS_URL).respond( + respx_mock.get(STORE_BOTS_URL, name="store_bots").respond( text=(store_path / "store_bots.json5").read_text(encoding="utf8") ) - respx_mock.get(STORE_DRIVERS_URL).respond( + respx_mock.get(STORE_DRIVERS_URL, name="store_drivers").respond( text=(store_path / "store_drivers.json5").read_text(encoding="utf8") ) - respx_mock.get(STORE_PLUGINS_URL).respond( + respx_mock.get(STORE_PLUGINS_URL, name="store_plugins").respond( text=(store_path / "store_plugins.json5").read_text(encoding="utf8") ) - respx_mock.get(REGISTRY_ADAPTERS_URL).respond( + respx_mock.get(REGISTRY_ADAPTERS_URL, name="registry_adapters").respond( text=(store_path / "registry_adapters.json").read_text(encoding="utf8") ) - respx_mock.get(REGISTRY_BOTS_URL).respond( + respx_mock.get(REGISTRY_BOTS_URL, name="registry_bots").respond( text=(store_path / "registry_bots.json").read_text(encoding="utf8") ) - respx_mock.get(REGISTRY_DRIVERS_URL).respond( + respx_mock.get(REGISTRY_DRIVERS_URL, name="registry_drivers").respond( text=(store_path / "registry_drivers.json").read_text(encoding="utf8") ) - respx_mock.get(REGISTRY_PLUGINS_URL).respond( + respx_mock.get(REGISTRY_PLUGINS_URL, name="registry_plugins").respond( text=(store_path / "registry_plugins.json").read_text(encoding="utf8") ) - respx_mock.get(REGISTRY_RESULTS_URL).respond( + respx_mock.get(REGISTRY_RESULTS_URL, name="registry_results").respond( text=(store_path / "registry_results.json").read_text(encoding="utf8") ) - respx_mock.get(REGISTRY_PLUGIN_CONFIG_URL).respond( + respx_mock.get(REGISTRY_PLUGIN_CONFIG_URL, name="plugin_configs").respond( text=(store_path / "plugin_configs.json").read_text(encoding="utf8") ) - return respx_mock diff --git a/tests/providers/store_test/conftest.py b/tests/providers/store_test/conftest.py index dbf18205..9815e721 100644 --- a/tests/providers/store_test/conftest.py +++ b/tests/providers/store_test/conftest.py @@ -9,6 +9,8 @@ def mocked_store_data( tmp_path: Path, mocker: MockerFixture, mocked_api: MockRouter ) -> dict[str, Path]: + from src.providers.store_test import store + plugin_test_path = tmp_path / "plugin_test" plugin_test_path.mkdir() @@ -21,13 +23,11 @@ def mocked_store_data( "plugin_configs": plugin_test_path / "plugin_configs.json", } - mocker.patch("src.providers.store_test.store.RESULTS_PATH", paths["results"]) - mocker.patch("src.providers.store_test.store.ADAPTERS_PATH", paths["adapters"]) - mocker.patch("src.providers.store_test.store.BOTS_PATH", paths["bots"]) - mocker.patch("src.providers.store_test.store.DRIVERS_PATH", paths["drivers"]) - mocker.patch("src.providers.store_test.store.PLUGINS_PATH", paths["plugins"]) - mocker.patch( - "src.providers.store_test.store.PLUGIN_CONFIG_PATH", paths["plugin_configs"] - ) + mocker.patch.object(store, "RESULTS_PATH", paths["results"]) + mocker.patch.object(store, "ADAPTERS_PATH", paths["adapters"]) + mocker.patch.object(store, "BOTS_PATH", paths["bots"]) + mocker.patch.object(store, "DRIVERS_PATH", paths["drivers"]) + mocker.patch.object(store, "PLUGINS_PATH", paths["plugins"]) + mocker.patch.object(store, "PLUGIN_CONFIG_PATH", paths["plugin_configs"]) return paths diff --git a/tests/providers/store_test/test_store_sync.py b/tests/providers/store_test/test_store_sync.py index 0c2c91c4..5b4f5be3 100644 --- a/tests/providers/store_test/test_store_sync.py +++ b/tests/providers/store_test/test_store_sync.py @@ -34,6 +34,8 @@ async def test_store_sync( "homepage": "https://onebot.adapters.nonebot.dev/", "tags": [{"label": "sync", "color": "#ffffff"}], "is_official": True, + "time": "2024-10-24T07:34:56.115315Z", + "version": "2.4.6", }, { "module_name": "nonebot.adapters.onebot.v12", @@ -44,6 +46,8 @@ async def test_store_sync( "homepage": "https://onebot.adapters.nonebot.dev/", "tags": [], "is_official": True, + "time": "2024-10-24T07:34:56.115315Z", + "version": "2.4.6", }, ] ) @@ -78,6 +82,8 @@ async def test_store_sync( "homepage": "/docs/advanced/driver", "tags": [{"label": "sync", "color": "#ffffff"}], "is_official": True, + "time": "2024-10-31T13:47:14.152851Z", + "version": "2.4.0", }, { "module_name": "~fastapi", @@ -88,6 +94,8 @@ async def test_store_sync( "homepage": "/docs/advanced/driver", "tags": [], "is_official": True, + "time": "2024-10-31T13:47:14.152851Z", + "version": "2.4.0", }, { "module_name": "~quart", @@ -98,6 +106,8 @@ async def test_store_sync( "homepage": "/docs/advanced/driver", "tags": [], "is_official": True, + "time": "2024-10-31T13:47:14.152851Z", + "version": "2.4.0", }, ] ) @@ -115,8 +125,8 @@ async def test_store_sync( "type": "library", "supported_adapters": None, "valid": True, - "time": "2023-06-22 11:58:18", - "version": "0.0.1", + "time": "2024-06-20T07:53:23.524486Z", + "version": "1.3.0", "skip_test": False, }, { @@ -131,8 +141,8 @@ async def test_store_sync( "type": "application", "supported_adapters": None, "valid": True, - "time": "2023-06-22 12:10:18", - "version": "0.0.1", + "time": "2024-07-13T04:41:40.905441Z", + "version": "0.5.0", "skip_test": False, }, ] @@ -142,7 +152,7 @@ async def test_store_sync( "nonebot-plugin-datastore:nonebot_plugin_datastore": { "time": "2023-06-26T22:08:18.945584+08:00", "config": "", - "version": "1.0.0", + "version": "1.3.0", "test_env": None, "results": {"validation": True, "load": True, "metadata": True}, "outputs": { diff --git a/tests/providers/store_test/test_store_test.py b/tests/providers/store_test/test_store_test.py index c92baef2..a614481f 100644 --- a/tests/providers/store_test/test_store_test.py +++ b/tests/providers/store_test/test_store_test.py @@ -85,29 +85,29 @@ async def test_store_test( type="application", supported_adapters=None, valid=True, - time="2023-06-22 12:10:18", - version="0.0.1", + time="2024-07-13T04:41:40.905441Z", + version="0.5.0", skip_test=False, ), config="TEST_CONFIG=true", ) - assert mocked_api["project_link_treehelp"].called - assert mocked_api["project_link_datastore"].called + assert mocked_api["pypi_nonebot-plugin-treehelp"].called + assert mocked_api["pypi_nonebot-plugin-datastore"].called assert mocked_store_data["adapters"].read_text(encoding="utf-8") == snapshot( - '[{"module_name":"nonebot.adapters.onebot.v11","project_link":"nonebot-adapter-onebot","name":"OneBot V11","desc":"OneBot V11 协议","author":"yanyongyu","homepage":"https://onebot.adapters.nonebot.dev/","tags":[{"label":"sync","color":"#ffffff"}],"is_official":true},{"module_name":"nonebot.adapters.onebot.v12","project_link":"nonebot-adapter-onebot","name":"OneBot V12","desc":"OneBot V12 协议","author":"he0119","homepage":"https://onebot.adapters.nonebot.dev/","tags":[],"is_official":true}]' + '[{"module_name":"nonebot.adapters.onebot.v11","project_link":"nonebot-adapter-onebot","name":"OneBot V11","desc":"OneBot V11 协议","author":"yanyongyu","homepage":"https://onebot.adapters.nonebot.dev/","tags":[{"label":"sync","color":"#ffffff"}],"is_official":true,"time":"2024-10-24T07:34:56.115315Z","version":"2.4.6"},{"module_name":"nonebot.adapters.onebot.v12","project_link":"nonebot-adapter-onebot","name":"OneBot V12","desc":"OneBot V12 协议","author":"he0119","homepage":"https://onebot.adapters.nonebot.dev/","tags":[],"is_official":true,"time":"2024-10-24T07:34:56.115315Z","version":"2.4.6"}]' ) assert mocked_store_data["bots"].read_text(encoding="utf-8") == snapshot( '[{"name":"CoolQBot","desc":"基于 NoneBot2 的聊天机器人","author":"he0119","homepage":"https://github.com/he0119/CoolQBot","tags":[{"label":"sync","color":"#ffffff"}],"is_official":false},{"name":"Github Bot","desc":"在QQ获取/处理Github repo/pr/issue","author":"BigOrangeQWQ","homepage":"https://github.com/cscs181/QQ-GitHub-Bot","tags":[],"is_official":false}]' ) assert mocked_store_data["drivers"].read_text(encoding="utf-8") == snapshot( - '[{"module_name":"~none","project_link":"","name":"None","desc":"None 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[{"label":"sync","color":"#ffffff"}],"is_official":true},{"module_name":"~fastapi","project_link":"nonebot2[fastapi]","name":"FastAPI","desc":"FastAPI 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[],"is_official":true},{"module_name":"~quart","project_link":"nonebot2[quart]","name":"Quart","desc":"Quart 驱动器","author":"he0119","homepage":"/docs/advanced/driver","tags":[],"is_official":true}]' + '[{"module_name":"~none","project_link":"","name":"None","desc":"None 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[{"label":"sync","color":"#ffffff"}],"is_official":true,"time":"2024-10-31T13:47:14.152851Z","version":"2.4.0"},{"module_name":"~fastapi","project_link":"nonebot2[fastapi]","name":"FastAPI","desc":"FastAPI 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[],"is_official":true,"time":"2024-10-31T13:47:14.152851Z","version":"2.4.0"},{"module_name":"~quart","project_link":"nonebot2[quart]","name":"Quart","desc":"Quart 驱动器","author":"he0119","homepage":"/docs/advanced/driver","tags":[],"is_official":true,"time":"2024-10-31T13:47:14.152851Z","version":"2.4.0"}]' ) assert mocked_store_data["plugins"].read_text(encoding="utf-8") == snapshot( - '[{"module_name":"nonebot_plugin_datastore","project_link":"nonebot-plugin-datastore","name":"数据存储","desc":"NoneBot 数据存储插件","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-datastore","tags":[{"label":"good first plugin","color":"#ffffff"}],"is_official":false,"type":"library","supported_adapters":null,"valid":true,"time":"2023-06-22 11:58:18","version":"0.0.1","skip_test":false},{"module_name":"nonebot_plugin_treehelp","project_link":"nonebot-plugin-treehelp","name":"帮助","desc":"获取插件帮助信息","author":"author","homepage":"https://nonebot.dev/","tags":[],"is_official":false,"type":"application","supported_adapters":null,"valid":true,"time":"2023-08-28T00:00:00.000000+08:00","version":"0.3.0","skip_test":false}]' + '[{"module_name":"nonebot_plugin_datastore","project_link":"nonebot-plugin-datastore","name":"数据存储","desc":"NoneBot 数据存储插件","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-datastore","tags":[{"label":"good first plugin","color":"#ffffff"}],"is_official":false,"type":"library","supported_adapters":null,"valid":true,"time":"2024-06-20T07:53:23.524486Z","version":"1.3.0","skip_test":false},{"module_name":"nonebot_plugin_treehelp","project_link":"nonebot-plugin-treehelp","name":"帮助","desc":"获取插件帮助信息","author":"author","homepage":"https://nonebot.dev/","tags":[],"is_official":false,"type":"application","supported_adapters":null,"valid":true,"time":"2023-08-28T00:00:00.000000+08:00","version":"0.3.0","skip_test":false}]' ) assert mocked_store_data["results"].read_text(encoding="utf-8") == snapshot( - '{"nonebot-plugin-datastore:nonebot_plugin_datastore":{"time":"2023-06-26T22:08:18.945584+08:00","config":"","version":"1.0.0","test_env":null,"results":{"validation":true,"load":true,"metadata":true},"outputs":{"validation":null,"load":"datastore","metadata":{"name":"数据存储","description":"NoneBot 数据存储插件","usage":"请参考文档","type":"library","homepage":"https://github.com/he0119/nonebot-plugin-datastore","supported_adapters":null}}},"nonebot-plugin-treehelp:nonebot_plugin_treehelp":{"time":"2023-08-28T00:00:00.000000+08:00","config":"","version":"1.0.0","test_env":null,"results":{"load":true,"metadata":true,"validation":true},"outputs":{"load":"output","metadata":{"name":"帮助","description":"获取插件帮助信息","usage":"获取插件列表\\n/help\\n获取插件树\\n/help -t\\n/help --tree\\n获取某个插件的帮助\\n/help 插件名\\n获取某个插件的树\\n/help --tree 插件名\\n","type":"application","homepage":"https://nonebot.dev/","supported_adapters":null},"validation":null}}}' + '{"nonebot-plugin-datastore:nonebot_plugin_datastore":{"time":"2023-06-26T22:08:18.945584+08:00","config":"","version":"1.3.0","test_env":null,"results":{"validation":true,"load":true,"metadata":true},"outputs":{"validation":null,"load":"datastore","metadata":{"name":"数据存储","description":"NoneBot 数据存储插件","usage":"请参考文档","type":"library","homepage":"https://github.com/he0119/nonebot-plugin-datastore","supported_adapters":null}}},"nonebot-plugin-treehelp:nonebot_plugin_treehelp":{"time":"2023-08-28T00:00:00.000000+08:00","config":"","version":"1.0.0","test_env":null,"results":{"load":true,"metadata":true,"validation":true},"outputs":{"load":"output","metadata":{"name":"帮助","description":"获取插件帮助信息","usage":"获取插件列表\\n/help\\n获取插件树\\n/help -t\\n/help --tree\\n获取某个插件的帮助\\n/help 插件名\\n获取某个插件的树\\n/help --tree 插件名\\n","type":"application","homepage":"https://nonebot.dev/","supported_adapters":null},"validation":null}}}' ) assert mocked_store_data["plugin_configs"].read_text(encoding="utf-8") == snapshot( """\ @@ -153,15 +153,15 @@ async def test_store_test_with_key( type="application", supported_adapters=None, valid=True, - time="2023-06-22 12:10:18", - version="0.0.1", + time="2024-07-13T04:41:40.905441Z", + version="0.5.0", skip_test=False, ), config="TEST_CONFIG=true", ) - assert mocked_api["project_link_treehelp"].called - assert not mocked_api["project_link_datastore"].called + assert mocked_api["pypi_nonebot-plugin-treehelp"].called + assert not mocked_api["pypi_nonebot-plugin-datastore"].called async def test_store_test_with_key_skip( @@ -180,8 +180,8 @@ async def test_store_test_with_key_skip( ) mocked_validate_plugin.assert_not_called() - assert not mocked_api["project_link_treehelp"].called - assert mocked_api["project_link_datastore"].called + assert not mocked_api["pypi_nonebot-plugin-treehelp"].called + assert mocked_api["pypi_nonebot-plugin-datastore"].called async def test_store_test_raise( @@ -202,7 +202,7 @@ async def test_store_test_raise( respx_mock.get( "https://pypi.org/pypi/nonebot-plugin-treehelp/json", - name="project_link_treehelp", + name="pypi_nonebot-plugin-treehelp", ).side_effect = httpx.ConnectTimeout mocked_validate_plugin = mocker.patch( @@ -231,25 +231,25 @@ async def test_store_test_raise( # 数据没有更新,只是被压缩 assert mocked_store_data["adapters"].read_text(encoding="utf-8") == snapshot( - '[{"module_name":"nonebot.adapters.onebot.v11","project_link":"nonebot-adapter-onebot","name":"OneBot V11","desc":"OneBot V11 协议","author":"yanyongyu","homepage":"https://onebot.adapters.nonebot.dev/","tags":[{"label":"sync","color":"#ffffff"}],"is_official":true},{"module_name":"nonebot.adapters.onebot.v12","project_link":"nonebot-adapter-onebot","name":"OneBot V12","desc":"OneBot V12 协议","author":"he0119","homepage":"https://onebot.adapters.nonebot.dev/","tags":[],"is_official":true}]' + '[{"module_name":"nonebot.adapters.onebot.v11","project_link":"nonebot-adapter-onebot","name":"OneBot V11","desc":"OneBot V11 协议","author":"yanyongyu","homepage":"https://onebot.adapters.nonebot.dev/","tags":[{"label":"sync","color":"#ffffff"}],"is_official":true,"time":"2024-10-24T07:34:56.115315Z","version":"2.4.6"},{"module_name":"nonebot.adapters.onebot.v12","project_link":"nonebot-adapter-onebot","name":"OneBot V12","desc":"OneBot V12 协议","author":"he0119","homepage":"https://onebot.adapters.nonebot.dev/","tags":[],"is_official":true,"time":"2024-10-24T07:34:56.115315Z","version":"2.4.6"}]' ) assert mocked_store_data["bots"].read_text(encoding="utf-8") == snapshot( '[{"name":"CoolQBot","desc":"基于 NoneBot2 的聊天机器人","author":"he0119","homepage":"https://github.com/he0119/CoolQBot","tags":[{"label":"sync","color":"#ffffff"}],"is_official":false},{"name":"Github Bot","desc":"在QQ获取/处理Github repo/pr/issue","author":"BigOrangeQWQ","homepage":"https://github.com/cscs181/QQ-GitHub-Bot","tags":[],"is_official":false}]' ) assert mocked_store_data["drivers"].read_text(encoding="utf-8") == snapshot( - '[{"module_name":"~none","project_link":"","name":"None","desc":"None 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[{"label":"sync","color":"#ffffff"}],"is_official":true},{"module_name":"~fastapi","project_link":"nonebot2[fastapi]","name":"FastAPI","desc":"FastAPI 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[],"is_official":true},{"module_name":"~quart","project_link":"nonebot2[quart]","name":"Quart","desc":"Quart 驱动器","author":"he0119","homepage":"/docs/advanced/driver","tags":[],"is_official":true}]' + '[{"module_name":"~none","project_link":"","name":"None","desc":"None 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[{"label":"sync","color":"#ffffff"}],"is_official":true,"time":"2024-10-31T13:47:14.152851Z","version":"2.4.0"},{"module_name":"~fastapi","project_link":"nonebot2[fastapi]","name":"FastAPI","desc":"FastAPI 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[],"is_official":true,"time":"2024-10-31T13:47:14.152851Z","version":"2.4.0"},{"module_name":"~quart","project_link":"nonebot2[quart]","name":"Quart","desc":"Quart 驱动器","author":"he0119","homepage":"/docs/advanced/driver","tags":[],"is_official":true,"time":"2024-10-31T13:47:14.152851Z","version":"2.4.0"}]' ) assert mocked_store_data["plugins"].read_text(encoding="utf-8") == snapshot( - '[{"module_name":"nonebot_plugin_datastore","project_link":"nonebot-plugin-datastore","name":"数据存储","desc":"NoneBot 数据存储插件","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-datastore","tags":[{"label":"good first plugin","color":"#ffffff"}],"is_official":false,"type":"library","supported_adapters":null,"valid":true,"time":"2023-06-22 11:58:18","version":"0.0.1","skip_test":false},{"module_name":"nonebot_plugin_treehelp","project_link":"nonebot-plugin-treehelp","name":"帮助","desc":"获取插件帮助信息","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-treehelp","tags":[],"is_official":false,"type":"application","supported_adapters":null,"valid":true,"time":"2023-06-22 12:10:18","version":"0.0.1","skip_test":false}]' + '[{"module_name":"nonebot_plugin_datastore","project_link":"nonebot-plugin-datastore","name":"数据存储","desc":"NoneBot 数据存储插件","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-datastore","tags":[{"label":"good first plugin","color":"#ffffff"}],"is_official":false,"type":"library","supported_adapters":null,"valid":true,"time":"2024-06-20T07:53:23.524486Z","version":"1.3.0","skip_test":false},{"module_name":"nonebot_plugin_treehelp","project_link":"nonebot-plugin-treehelp","name":"帮助","desc":"获取插件帮助信息","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-treehelp","tags":[],"is_official":false,"type":"application","supported_adapters":null,"valid":true,"time":"2024-07-13T04:41:40.905441Z","version":"0.5.0","skip_test":false}]' ) assert mocked_store_data["results"].read_text(encoding="utf-8") == snapshot( - '{"nonebot-plugin-datastore:nonebot_plugin_datastore":{"time":"2023-06-26T22:08:18.945584+08:00","config":"","version":"1.0.0","test_env":null,"results":{"validation":true,"load":true,"metadata":true},"outputs":{"validation":null,"load":"datastore","metadata":{"name":"数据存储","description":"NoneBot 数据存储插件","usage":"请参考文档","type":"library","homepage":"https://github.com/he0119/nonebot-plugin-datastore","supported_adapters":null}}},"nonebot-plugin-treehelp:nonebot_plugin_treehelp":{"time":"2023-06-26T22:20:41.833311+08:00","config":"","version":"0.3.0","test_env":null,"results":{"validation":true,"load":true,"metadata":true},"outputs":{"validation":null,"load":"treehelp","metadata":{"name":"帮助","description":"获取插件帮助信息","usage":"获取插件列表\\n/help\\n获取插件树\\n/help -t\\n/help --tree\\n获取某个插件的帮助\\n/help 插件名\\n获取某个插件的树\\n/help --tree 插件名\\n","type":"application","homepage":"https://github.com/he0119/nonebot-plugin-treehelp","supported_adapters":null}}}}' + '{"nonebot-plugin-datastore:nonebot_plugin_datastore":{"time":"2023-06-26T22:08:18.945584+08:00","config":"","version":"1.3.0","test_env":null,"results":{"validation":true,"load":true,"metadata":true},"outputs":{"validation":null,"load":"datastore","metadata":{"name":"数据存储","description":"NoneBot 数据存储插件","usage":"请参考文档","type":"library","homepage":"https://github.com/he0119/nonebot-plugin-datastore","supported_adapters":null}}},"nonebot-plugin-treehelp:nonebot_plugin_treehelp":{"time":"2023-06-26T22:20:41.833311+08:00","config":"","version":"0.3.0","test_env":null,"results":{"validation":true,"load":true,"metadata":true},"outputs":{"validation":null,"load":"treehelp","metadata":{"name":"帮助","description":"获取插件帮助信息","usage":"获取插件列表\\n/help\\n获取插件树\\n/help -t\\n/help --tree\\n获取某个插件的帮助\\n/help 插件名\\n获取某个插件的树\\n/help --tree 插件名\\n","type":"application","homepage":"https://github.com/he0119/nonebot-plugin-treehelp","supported_adapters":null}}}}' ) - assert mocked_api["project_link_datastore"].called - assert mocked_api["project_link_treehelp"].called + assert mocked_api["pypi_nonebot-plugin-datastore"].called + assert mocked_api["pypi_nonebot-plugin-treehelp"].called # 因为没有之前测试的结果,所以不需要获取插件版本号,直接开始测试 - assert not mocked_api["project_link_wordcloud"].called + assert not mocked_api["pypi_nonebot-plugin-wordcloud"].called async def test_store_test_with_key_raise( @@ -282,17 +282,17 @@ async def test_store_test_with_key_raise( # 数据没有更新,只是被压缩 assert mocked_store_data["adapters"].read_text(encoding="utf-8") == snapshot( - '[{"module_name":"nonebot.adapters.onebot.v11","project_link":"nonebot-adapter-onebot","name":"OneBot V11","desc":"OneBot V11 协议","author":"yanyongyu","homepage":"https://onebot.adapters.nonebot.dev/","tags":[],"is_official":true}]' + '[{"module_name":"nonebot.adapters.onebot.v11","project_link":"nonebot-adapter-onebot","name":"OneBot V11","desc":"OneBot V11 协议","author":"yanyongyu","homepage":"https://onebot.adapters.nonebot.dev/","tags":[],"is_official":true,"time":"2024-10-24T07:34:56.115315Z","version":"2.4.6"}]' ) assert mocked_store_data["bots"].read_text(encoding="utf-8") == snapshot( '[{"name":"CoolQBot","desc":"基于 NoneBot2 的聊天机器人","author":"he0119","homepage":"https://github.com/he0119/CoolQBot","tags":[],"is_official":false}]' ) assert mocked_store_data["drivers"].read_text(encoding="utf-8") == snapshot( - '[{"module_name":"~none","project_link":"","name":"None","desc":"None 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[],"is_official":true},{"module_name":"~fastapi","project_link":"nonebot2[fastapi]","name":"FastAPI","desc":"FastAPI 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[],"is_official":true}]' + '[{"module_name":"~none","project_link":"","name":"None","desc":"None 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[],"is_official":true,"time":"2024-10-31T13:47:14.152851Z","version":"2.4.0"},{"module_name":"~fastapi","project_link":"nonebot2[fastapi]","name":"FastAPI","desc":"FastAPI 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[],"is_official":true,"time":"2024-10-31T13:47:14.152851Z","version":"2.4.0"}]' ) assert mocked_store_data["plugins"].read_text(encoding="utf-8") == snapshot( - '[{"module_name":"nonebot_plugin_datastore","project_link":"nonebot-plugin-datastore","name":"数据存储","desc":"NoneBot 数据存储插件","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-datastore","tags":[],"is_official":false,"type":"library","supported_adapters":null,"valid":true,"time":"2023-06-22 11:58:18","version":"0.0.1","skip_test":false},{"module_name":"nonebot_plugin_treehelp","project_link":"nonebot-plugin-treehelp","name":"帮助","desc":"获取插件帮助信息","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-treehelp","tags":[],"is_official":false,"type":"application","supported_adapters":null,"valid":true,"time":"2023-06-22 12:10:18","version":"0.0.1","skip_test":false}]' + '[{"module_name":"nonebot_plugin_datastore","project_link":"nonebot-plugin-datastore","name":"数据存储","desc":"NoneBot 数据存储插件","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-datastore","tags":[],"is_official":false,"type":"library","supported_adapters":null,"valid":true,"time":"2024-06-20T07:53:23.524486Z","version":"1.3.0","skip_test":false},{"module_name":"nonebot_plugin_treehelp","project_link":"nonebot-plugin-treehelp","name":"帮助","desc":"获取插件帮助信息","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-treehelp","tags":[],"is_official":false,"type":"application","supported_adapters":null,"valid":true,"time":"2024-07-13T04:41:40.905441Z","version":"0.5.0","skip_test":false}]' ) assert mocked_store_data["results"].read_text(encoding="utf-8") == snapshot( - '{"nonebot-plugin-datastore:nonebot_plugin_datastore":{"time":"2023-06-26T22:08:18.945584+08:00","config":"","version":"1.0.0","test_env":null,"results":{"validation":true,"load":true,"metadata":true},"outputs":{"validation":null,"load":"datastore","metadata":{"name":"数据存储","description":"NoneBot 数据存储插件","usage":"请参考文档","type":"library","homepage":"https://github.com/he0119/nonebot-plugin-datastore","supported_adapters":null}}},"nonebot-plugin-treehelp:nonebot_plugin_treehelp":{"time":"2023-06-26T22:20:41.833311+08:00","config":"","version":"0.3.0","test_env":null,"results":{"validation":true,"load":true,"metadata":true},"outputs":{"validation":null,"load":"treehelp","metadata":{"name":"帮助","description":"获取插件帮助信息","usage":"获取插件列表\\n/help\\n获取插件树\\n/help -t\\n/help --tree\\n获取某个插件的帮助\\n/help 插件名\\n获取某个插件的树\\n/help --tree 插件名\\n","type":"application","homepage":"https://github.com/he0119/nonebot-plugin-treehelp","supported_adapters":null}}}}' + '{"nonebot-plugin-datastore:nonebot_plugin_datastore":{"time":"2023-06-26T22:08:18.945584+08:00","config":"","version":"1.3.0","test_env":null,"results":{"validation":true,"load":true,"metadata":true},"outputs":{"validation":null,"load":"datastore","metadata":{"name":"数据存储","description":"NoneBot 数据存储插件","usage":"请参考文档","type":"library","homepage":"https://github.com/he0119/nonebot-plugin-datastore","supported_adapters":null}}},"nonebot-plugin-treehelp:nonebot_plugin_treehelp":{"time":"2023-06-26T22:20:41.833311+08:00","config":"","version":"0.3.0","test_env":null,"results":{"validation":true,"load":true,"metadata":true},"outputs":{"validation":null,"load":"treehelp","metadata":{"name":"帮助","description":"获取插件帮助信息","usage":"获取插件列表\\n/help\\n获取插件树\\n/help -t\\n/help --tree\\n获取某个插件的帮助\\n/help 插件名\\n获取某个插件的树\\n/help --tree 插件名\\n","type":"application","homepage":"https://github.com/he0119/nonebot-plugin-treehelp","supported_adapters":null}}}}' ) diff --git a/tests/providers/store_test/test_validate_plugin.py b/tests/providers/store_test/test_validate_plugin.py index 79cec7eb..b370cea8 100644 --- a/tests/providers/store_test/test_validate_plugin.py +++ b/tests/providers/store_test/test_validate_plugin.py @@ -86,7 +86,7 @@ async def test_validate_plugin(mocked_api: MockRouter, mocker: MockerFixture) -> skip_test=False, supported_adapters=None, tags=[], - time="2023-09-01T00:00:00+00:00", + time="2023-09-01T00:00:00.000000Z", type="application", valid=True, version="0.2.0", @@ -184,7 +184,7 @@ async def test_validate_plugin_with_previous( "type": "application", "supported_adapters": None, "valid": True, - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "version": "0.2.0", "skip_test": False, } @@ -260,7 +260,7 @@ async def test_validate_plugin_skip_test( skip_test=False, supported_adapters=None, tags=[], - time="2023-09-01T00:00:00+00:00", + time="2023-09-01T00:00:00.000000Z", type="application", valid=True, version="0.2.0", @@ -345,7 +345,7 @@ async def test_validate_plugin_skip_test_plugin_test_failed( skip_test=True, supported_adapters=None, tags=[], - time="2021-08-01T00:00:00+00:00", + time="2024-07-13T04:41:40.905441Z", type="application", valid=True, version="0.3.9", @@ -410,7 +410,7 @@ async def test_validate_plugin_failed_with_previous( "data": { "module_name": "module_name", "project_link": "project_link", - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "name": "name", "desc": "desc", "author": "he0119", @@ -467,7 +467,7 @@ async def test_validate_plugin_failed_with_previous( skip_test=False, supported_adapters=None, tags=[], - time="2023-09-01T00:00:00+00:00", + time="2023-09-01T00:00:00.000000Z", type="application", valid=False, version="0.3.9", diff --git a/tests/providers/validation/fields/test_homepage.py b/tests/providers/validation/fields/test_homepage.py index 3c971604..812053a9 100644 --- a/tests/providers/validation/fields/test_homepage.py +++ b/tests/providers/validation/fields/test_homepage.py @@ -64,9 +64,9 @@ async def test_homepage_failed_empty_homepage(mocked_api: MockRouter) -> None: { "type": "string_pattern_mismatch", "loc": ("homepage",), - "msg": "字符串应满足格式 '^https?://.*$'", + "msg": "字符串应满足格式 '^(https?://.*|/docs/.*)$'", "input": "", - "ctx": {"pattern": "^https?://.*$"}, + "ctx": {"pattern": "^(https?://.*|/docs/.*)$"}, } ] ) diff --git a/tests/store/registry_results.json b/tests/store/registry_results.json index 774dbbd8..0d037c2a 100644 --- a/tests/store/registry_results.json +++ b/tests/store/registry_results.json @@ -1,7 +1,7 @@ { "nonebot-plugin-datastore:nonebot_plugin_datastore": { "time": "2023-06-26T22:08:18.945584+08:00", - "version": "1.0.0", + "version": "1.3.0", "results": { "validation": true, "load": true, From f353358621d90057273f3ca8dbeffdbc0d8a545a Mon Sep 17 00:00:00 2001 From: uy_sun Date: Mon, 16 Dec 2024 23:14:28 +0800 Subject: [PATCH 06/12] =?UTF-8?q?test:=20=E4=BF=AE=E5=A4=8D=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E7=9B=B8=E5=85=B3=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugins/github/config/process/test_config_check.py | 4 ++-- .../github/publish/process/test_publish_check.py | 10 +++++----- .../publish/process/test_publish_pull_request.py | 4 ++-- .../publish/utils/test_trigger_registry_update.py | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/plugins/github/config/process/test_config_check.py b/tests/plugins/github/config/process/test_config_check.py index bb5ba61a..acdf43e9 100644 --- a/tests/plugins/github/config/process/test_config_check.py +++ b/tests/plugins/github/config/process/test_config_check.py @@ -148,7 +148,7 @@ async def test_process_config_check(
详情 -
  • ✅ 项目 nonebot-plugin-treehelp 已发布至 PyPI。
  • ✅ 插件发布时间:2021-08-01 00:00:00。
  • ✅ 项目 主页 返回状态码 200。
  • ✅ 标签: test-#ffffff。
  • ✅ 插件类型: application。
  • ✅ 插件支持的适配器: nonebot.adapters.onebot.v11。
  • ✅ 插件版本号: 1.0.0。
  • ✅ 插件 加载测试 通过。
  • +
  • ✅ 项目 nonebot-plugin-treehelp 已发布至 PyPI。
  • ✅ 插件发布时间:2024-07-13 04:41:40。
  • ✅ 插件版本号: 1.0.0。
  • ✅ 项目 主页 返回状态码 200。
  • ✅ 标签: test-#ffffff。
  • ✅ 插件类型: application。
  • ✅ 插件支持的适配器: nonebot.adapters.onebot.v11。
  • ✅ 插件 加载测试 通过。
  • --- @@ -308,7 +308,7 @@ async def test_process_config_check( "type": "application", "supported_adapters": ["nonebot.adapters.onebot.v11"], "valid": True, - "time": "2021-08-01T00:00:00+00:00", + "time": "2024-07-13T04:41:40.905441Z", "version": "1.0.0", "skip_test": False, } diff --git a/tests/plugins/github/publish/process/test_publish_check.py b/tests/plugins/github/publish/process/test_publish_check.py index 96119ba6..78c4e953 100644 --- a/tests/plugins/github/publish/process/test_publish_check.py +++ b/tests/plugins/github/publish/process/test_publish_check.py @@ -281,7 +281,7 @@ async def test_adapter_process_publish_check(
    详情 -
  • ✅ 项目 project_link 已发布至 PyPI。
  • ✅ 插件发布时间:2023-09-01 00:00:00。
  • ✅ 项目 主页 返回状态码 200。
  • ✅ 标签: test-#ffffff。
  • +
  • ✅ 项目 project_link 已发布至 PyPI。
  • ✅ 插件发布时间:2023-09-01 00:00:00。
  • ✅ 插件版本号: 0.0.1。
  • ✅ 项目 主页 返回状态码 200。
  • ✅ 标签: test-#ffffff。
  • --- @@ -568,7 +568,7 @@ async def test_plugin_process_publish_check(
    详情 -
  • ✅ 项目 project_link 已发布至 PyPI。
  • ✅ 插件发布时间:2023-09-01 00:00:00。
  • ✅ 项目 主页 返回状态码 200。
  • ✅ 标签: test-#ffffff。
  • ✅ 插件类型: application。
  • ✅ 插件支持的适配器: 所有。
  • ✅ 插件版本号: 1.0.0。
  • ✅ 插件 加载测试 通过。
  • +
  • ✅ 项目 project_link 已发布至 PyPI。
  • ✅ 插件发布时间:2023-09-01 00:00:00。
  • ✅ 插件版本号: 1.0.0。
  • ✅ 项目 主页 返回状态码 200。
  • ✅ 标签: test-#ffffff。
  • ✅ 插件类型: application。
  • ✅ 插件支持的适配器: 所有。
  • ✅ 插件 加载测试 通过。
  • --- @@ -860,7 +860,7 @@ async def test_plugin_process_publish_check_re_run(
    详情 -
  • ✅ 项目 project_link 已发布至 PyPI。
  • ✅ 插件发布时间:2023-09-01 00:00:00。
  • ✅ 项目 主页 返回状态码 200。
  • ✅ 标签: test-#ffffff。
  • ✅ 插件类型: application。
  • ✅ 插件支持的适配器: 所有。
  • ✅ 插件版本号: 1.0.0。
  • ✅ 插件 加载测试 通过。
  • +
  • ✅ 项目 project_link 已发布至 PyPI。
  • ✅ 插件发布时间:2023-09-01 00:00:00。
  • ✅ 插件版本号: 1.0.0。
  • ✅ 项目 主页 返回状态码 200。
  • ✅ 标签: test-#ffffff。
  • ✅ 插件类型: application。
  • ✅ 插件支持的适配器: 所有。
  • ✅ 插件 加载测试 通过。
  • --- @@ -1760,7 +1760,7 @@ async def test_skip_plugin_check(
    详情 -
  • ✅ 项目 project_link 已发布至 PyPI。
  • ✅ 插件发布时间:2023-09-01 00:00:00。
  • ✅ 标签: test-#ffffff。
  • ✅ 插件版本号: 0.0.1。
  • ✅ 插件 加载测试 已跳过。
  • +
  • ✅ 项目 project_link 已发布至 PyPI。
  • ✅ 插件发布时间:2023-09-01 00:00:00。
  • ✅ 插件版本号: 0.0.1。
  • ✅ 标签: test-#ffffff。
  • ✅ 插件 加载测试 已跳过。
  • --- @@ -1815,7 +1815,7 @@ async def test_skip_plugin_check( # 检查文件是否正确 check_json_data(plugin_config.input_config.plugin_path, []) - assert mocked_api["project_link"].called + assert mocked_api["pypi_project_link"].called async def test_convert_pull_request_to_draft( diff --git a/tests/plugins/github/publish/process/test_publish_pull_request.py b/tests/plugins/github/publish/process/test_publish_pull_request.py index a6fff421..549a4b41 100644 --- a/tests/plugins/github/publish/process/test_publish_pull_request.py +++ b/tests/plugins/github/publish/process/test_publish_pull_request.py @@ -98,7 +98,7 @@ async def test_process_pull_request( "type": "application", "supported_adapters": ["nonebot.adapters.onebot.v11"], "valid": True, - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "version": "1.0.0", "skip_test": False, }, @@ -295,7 +295,7 @@ async def test_process_pull_request_skip_plugin_test( "type": "application", "supported_adapters": ["nonebot.adapters.onebot.v11"], "valid": True, - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "version": "0.0.1", "skip_test": True, }, diff --git a/tests/plugins/github/publish/utils/test_trigger_registry_update.py b/tests/plugins/github/publish/utils/test_trigger_registry_update.py index ceeab838..5f2573f1 100644 --- a/tests/plugins/github/publish/utils/test_trigger_registry_update.py +++ b/tests/plugins/github/publish/utils/test_trigger_registry_update.py @@ -79,7 +79,7 @@ async def test_trigger_registry_update( "type": "application", "supported_adapters": ["nonebot.adapters.onebot.v11"], "valid": True, - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "version": "1.0.0", "skip_test": False, }, @@ -178,7 +178,7 @@ async def test_trigger_registry_update_skip_test( "type": "application", "supported_adapters": ["nonebot.adapters.onebot.v11"], "valid": True, - "time": "2023-09-01T00:00:00+00:00", + "time": "2023-09-01T00:00:00.000000Z", "version": "0.0.1", "skip_test": True, }, @@ -233,7 +233,7 @@ async def test_trigger_registry_update_bot( from src.providers.validation import PublishType mock_issue = MockIssue( - body=MockBody(type="bot", homepage="https://v2.nonebot.dev").generate(), + body=MockBody(type="bot").generate(), number=1, ).as_mock(mocker) @@ -253,7 +253,7 @@ async def test_trigger_registry_update_bot( "name": "name", "desc": "desc", "author": "test", - "homepage": "https://v2.nonebot.dev", + "homepage": "https://nonebot.dev", "tags": [{"label": "test", "color": "#ffffff"}], "is_official": False, }, @@ -272,7 +272,7 @@ async def test_trigger_registry_update_bot( await trigger_registry_update(handler, PublishType.BOT) - assert mocked_api["homepage_v2"].called + assert mocked_api["homepage"].called async def test_trigger_registry_update_plugins_issue_body_info_missing( From 79bf9ef1e48188ea7f76032708806aac7b712142 Mon Sep 17 00:00:00 2001 From: uy_sun Date: Tue, 17 Dec 2024 09:18:29 +0800 Subject: [PATCH 07/12] =?UTF-8?q?fix:=20=E7=8E=B0=E5=9C=A8=E6=AF=8F?= =?UTF-8?q?=E6=AC=A1=20sync=5Fstore=20=E6=97=B6=E8=83=BD=E6=AD=A3=E5=B8=B8?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=95=86=E5=BA=97=E6=95=B0=E6=8D=AE=E5=92=8C?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC=E5=8F=B7=E5=92=8C=E6=97=B6?= =?UTF-8?q?=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../github/plugins/publish/validation.py | 4 +- src/providers/models.py | 17 +- src/providers/store_test/store.py | 4 +- src/providers/store_test/validation.py | 4 +- src/providers/utils.py | 22 +- src/providers/validation/models.py | 20 +- src/providers/validation/utils.py | 34 +-- tests/providers/store_test/test_store_sync.py | 212 ++++++++++++++++++ 8 files changed, 264 insertions(+), 53 deletions(-) diff --git a/src/plugins/github/plugins/publish/validation.py b/src/plugins/github/plugins/publish/validation.py index e65d5421..339f5b44 100644 --- a/src/plugins/github/plugins/publish/validation.py +++ b/src/plugins/github/plugins/publish/validation.py @@ -9,7 +9,7 @@ from src.plugins.github.models import AuthorInfo from src.plugins.github.utils import extract_issue_info_from_issue from src.providers.docker_test import DockerPluginTest, Metadata -from src.providers.utils import get_latest_version, load_json_from_file +from src.providers.utils import get_pypi_version, load_json_from_file from src.providers.validation import PublishType, ValidationDict, validate_info from .constants import ( @@ -101,7 +101,7 @@ async def validate_plugin_info_from_issue( raw_data.update(metadata) # 跳过测试的时候只能从 pypi 获取版本号 - raw_data["version"] = get_latest_version(project_link) + raw_data["version"] = get_pypi_version(project_link) raw_data["load"] = False raw_data["test_output"] = "插件未进行测试" raw_data["metadata"] = bool(metadata) diff --git a/src/providers/models.py b/src/providers/models.py index 344c1721..e11854b2 100644 --- a/src/providers/models.py +++ b/src/providers/models.py @@ -8,7 +8,7 @@ from src.providers.constants import BOT_KEY_TEMPLATE, PYPI_KEY_TEMPLATE from src.providers.docker_test import Metadata -from src.providers.utils import get_author_name +from src.providers.utils import get_author_name, get_pypi_upload_time, get_pypi_version from src.providers.validation import validate_info from src.providers.validation.models import ( AdapterPublishInfo, @@ -250,8 +250,13 @@ def from_publish_info(cls, publish_info: AdapterPublishInfo) -> Self: def update(self, store: StoreAdapter) -> "RegistryAdapter": """根据商店数据更新注册表数据""" + version = get_pypi_version(self.project_link) + time = get_pypi_upload_time(self.project_link) + data = self.model_dump() data.update(store.model_dump()) + data.update(version=version, time=time) + return RegistryAdapter(**data) @@ -324,8 +329,18 @@ def from_publish_info(cls, publish_info: DriverPublishInfo) -> Self: def update(self, store: StoreDriver) -> "RegistryDriver": """根据商店数据更新注册表数据""" + # ~none 和 ~fastapi 驱动器的项目名一个是空字符串,一个是 nonebot2[fastapi] + # 上传时间和版本号均以 nonebot2 为准 + project_link = self.project_link + if project_link == "" or project_link.startswith("nonebot2["): + project_link = "nonebot2" + version = get_pypi_version(project_link) + time = get_pypi_upload_time(project_link) + data = self.model_dump() data.update(store.model_dump()) + data.update(version=version, time=time) + return RegistryDriver(**data) diff --git a/src/providers/store_test/store.py b/src/providers/store_test/store.py index b1a6262e..949d45b8 100644 --- a/src/providers/store_test/store.py +++ b/src/providers/store_test/store.py @@ -30,7 +30,7 @@ from src.providers.utils import ( add_step_summary, dump_json, - get_latest_version, + get_pypi_version, load_json_from_web, ) @@ -133,7 +133,7 @@ def should_skip(self, key: str, force: bool = False) -> bool: # 如果插件为最新版本,则跳过测试 try: - latest_version = get_latest_version(previous_plugin.project_link) + latest_version = get_pypi_version(previous_plugin.project_link) except ValueError as e: logger.warning(f"插件 {key} 获取最新版本失败:{e},跳过测试") return True diff --git a/src/providers/store_test/validation.py b/src/providers/store_test/validation.py index f33d25e8..2445c2d0 100644 --- a/src/providers/store_test/validation.py +++ b/src/providers/store_test/validation.py @@ -5,7 +5,7 @@ from src.providers.docker_test import DockerPluginTest from src.providers.logger import logger from src.providers.models import RegistryPlugin, StorePlugin, StoreTestResult -from src.providers.utils import get_author_name, get_upload_time +from src.providers.utils import get_author_name, get_pypi_upload_time from src.providers.validation import ( PluginPublishInfo, PublishType, @@ -32,7 +32,7 @@ async def validate_plugin( module_name = store_plugin.module_name # 从 PyPI 获取信息 - pypi_time = get_upload_time(project_link) + pypi_time = get_pypi_upload_time(project_link) # 测试插件 plugin_test_result = await DockerPluginTest(project_link, module_name, config).run( diff --git a/src/providers/utils.py b/src/providers/utils.py index 77719b56..bc86fdc5 100644 --- a/src/providers/utils.py +++ b/src/providers/utils.py @@ -81,18 +81,30 @@ def get_pypi_data(project_link: str) -> dict[str, Any]: raise ValueError(f"获取 PyPI 数据失败:{e}") if r.status_code != 200: raise ValueError(f"获取 PyPI 数据失败:{r.text}") - return load_json(r.text) + return r.json() -def get_latest_version(project_link: str) -> str: - """获取插件的最新版本号""" +def get_pypi_name(project_link: str) -> str: + """获取 PyPI 项目名""" data = get_pypi_data(project_link) + return data["info"]["name"] + + +def get_pypi_version(project_link: str) -> str | None: + """获取插件的最新版本号""" + try: + data = get_pypi_data(project_link) + except ValueError: + return None return data["info"]["version"] -def get_upload_time(project_link: str) -> str: +def get_pypi_upload_time(project_link: str) -> str | None: """获取插件的上传时间""" - data = get_pypi_data(project_link) + try: + data = get_pypi_data(project_link) + except ValueError: + return None return data["urls"][0]["upload_time_iso_8601"] diff --git a/src/providers/validation/models.py b/src/providers/validation/models.py index 6a4198df..98d9b275 100644 --- a/src/providers/validation/models.py +++ b/src/providers/validation/models.py @@ -18,7 +18,12 @@ from pydantic_extra_types.color import Color from pyjson5 import Json5DecoderException -from src.providers.utils import load_json +from src.providers.utils import ( + get_pypi_name, + get_pypi_upload_time, + get_pypi_version, + load_json, +) from .constants import ( NAME_MAX_LENGTH, @@ -30,9 +35,6 @@ check_pypi, check_url, get_adapters, - get_pypi_name, - get_pypi_version, - get_upload_time, resolve_adapter_name, ) @@ -148,15 +150,17 @@ def prevent_duplication( # 如果一切正常才记录上传时间和版本号 if project_link is not None: + # ~none 和 ~fastapi 驱动器的项目名一个是空字符串,一个是 nonebot2[fastapi] + # 上传时间和版本号均以 nonebot2 为准 if issubclass(cls, DriverPublishInfo) and ( project_link == "" or project_link.startswith("nonebot2[") ): project_link = "nonebot2" - values["time"] = get_upload_time(project_link) - # 只有不是插件测试的情况下才获取版本号 - # 插件测试的时候应该使用从插件测试获取的版本号,也就是传入的版本号 - if not issubclass(cls, PluginPublishInfo): + values["time"] = get_pypi_upload_time(project_link) + # 只有不是插件测试并且未提供版本号的情况下才获取版本号 + # 插件测试的时候应该总是使用从插件测试获取的版本号,也就是传入的版本号 + if not issubclass(cls, PluginPublishInfo) and "version" not in values: values["version"] = get_pypi_version(project_link) return values diff --git a/src/providers/validation/utils.py b/src/providers/validation/utils.py index ba5a7415..b997c014 100644 --- a/src/providers/validation/utils.py +++ b/src/providers/validation/utils.py @@ -4,7 +4,7 @@ import httpx from src.providers.constants import STORE_ADAPTERS_URL -from src.providers.utils import load_json, load_json_from_web +from src.providers.utils import load_json_from_web from .constants import MESSAGE_TRANSLATIONS @@ -18,38 +18,6 @@ def get_url(url: str) -> httpx.Response: return httpx.get(url, follow_redirects=True) -def get_pypi_name(project_link: str) -> str: - """获取 PyPI 项目名""" - url = f"https://pypi.org/pypi/{project_link}/json" - r = get_url(url) - r.raise_for_status() - data = load_json(r.text) - return data["info"]["name"] - - -def get_pypi_version(project_link: str) -> str | None: - """获取 PyPI 版本""" - url = f"https://pypi.org/pypi/{project_link}/json" - r = get_url(url) - if r.status_code != 200: - return None - data = load_json(r.text) - return data["info"]["version"] - - -def get_upload_time(project_link: str) -> str | None: - """获取插件的上传时间""" - url = f"https://pypi.org/pypi/{project_link}/json" - r = get_url(url) - if r.status_code != 200: - return None - try: - data = load_json(r.text) - except Exception: - return None - return data["urls"][0]["upload_time_iso_8601"] - - def check_pypi(project_link: str) -> bool: """检查项目是否存在""" url = f"https://pypi.org/pypi/{project_link}/json" diff --git a/tests/providers/store_test/test_store_sync.py b/tests/providers/store_test/test_store_sync.py index 5b4f5be3..a2dc0f02 100644 --- a/tests/providers/store_test/test_store_sync.py +++ b/tests/providers/store_test/test_store_sync.py @@ -5,6 +5,8 @@ from pytest_mock import MockerFixture from respx import MockRouter +from tests.conftest import PyPIProject + def load_json(path: Path) -> dict: with path.open("r", encoding="utf-8") as f: @@ -19,6 +21,7 @@ async def test_store_sync( 均增加一个 tag,以测试数据是否正确同步 """ from src.providers.store_test.store import StoreTest + from src.providers.utils import get_pypi_data test = StoreTest() await test.run(0, 0, False) @@ -190,6 +193,215 @@ async def test_store_sync( /help 插件名 获取某个插件的树 /help --tree 插件名 +""", + "type": "application", + "homepage": "https://github.com/he0119/nonebot-plugin-treehelp", + "supported_adapters": None, + }, + }, + }, + } + ) + + # 当 PyPI 版本更新时,商店数据也应该更新 + pypi_projects = [ + PyPIProject( + url="nonebot2", + name="nonebot2", + version="2.4.1", + upload_time_iso_8601="2024-11-31T13:47:14.152851Z", + ), + PyPIProject( + url="nonebot-adapter-onebot", + name="nonebot-adapter-onebot", + version="2.4.7", + upload_time_iso_8601="2024-11-24T07:34:56.115315Z", + ), + ] + for project in pypi_projects: + mocked_api.get( + f"https://pypi.org/pypi/{project['url']}/json", + name=f"pypi_{project['url']}", + ).respond( + json={ + "info": {"name": project["name"], "version": project["version"]}, + "urls": [{"upload_time_iso_8601": project["upload_time_iso_8601"]}], + } + ) + + # 缓存了 PyPI 数据,需要清除缓存 + get_pypi_data.cache_clear() + + test = StoreTest() + await test.run(0, 0, False) + + assert load_json(mocked_store_data["adapters"]) == snapshot( + [ + { + "module_name": "nonebot.adapters.onebot.v11", + "project_link": "nonebot-adapter-onebot", + "name": "OneBot V11", + "desc": "OneBot V11 协议", + "author": "yanyongyu", + "homepage": "https://onebot.adapters.nonebot.dev/", + "tags": [{"label": "sync", "color": "#ffffff"}], + "is_official": True, + "time": "2024-11-24T07:34:56.115315Z", + "version": "2.4.7", + }, + { + "module_name": "nonebot.adapters.onebot.v12", + "project_link": "nonebot-adapter-onebot", + "name": "OneBot V12", + "desc": "OneBot V12 协议", + "author": "he0119", + "homepage": "https://onebot.adapters.nonebot.dev/", + "tags": [], + "is_official": True, + "time": "2024-11-24T07:34:56.115315Z", + "version": "2.4.7", + }, + ] + ) + assert load_json(mocked_store_data["bots"]) == snapshot( + [ + { + "name": "CoolQBot", + "desc": "基于 NoneBot2 的聊天机器人", + "author": "he0119", + "homepage": "https://github.com/he0119/CoolQBot", + "tags": [{"label": "sync", "color": "#ffffff"}], + "is_official": False, + }, + { + "name": "Github Bot", + "desc": "在QQ获取/处理Github repo/pr/issue", + "author": "BigOrangeQWQ", + "homepage": "https://github.com/cscs181/QQ-GitHub-Bot", + "tags": [], + "is_official": False, + }, + ] + ) + assert load_json(mocked_store_data["drivers"]) == snapshot( + [ + { + "module_name": "~none", + "project_link": "", + "name": "None", + "desc": "None 驱动器", + "author": "yanyongyu", + "homepage": "/docs/advanced/driver", + "tags": [{"label": "sync", "color": "#ffffff"}], + "is_official": True, + "time": "2024-11-31T13:47:14.152851Z", + "version": "2.4.1", + }, + { + "module_name": "~fastapi", + "project_link": "nonebot2[fastapi]", + "name": "FastAPI", + "desc": "FastAPI 驱动器", + "author": "yanyongyu", + "homepage": "/docs/advanced/driver", + "tags": [], + "is_official": True, + "time": "2024-11-31T13:47:14.152851Z", + "version": "2.4.1", + }, + { + "module_name": "~quart", + "project_link": "nonebot2[quart]", + "name": "Quart", + "desc": "Quart 驱动器", + "author": "he0119", + "homepage": "/docs/advanced/driver", + "tags": [], + "is_official": True, + "time": "2024-11-31T13:47:14.152851Z", + "version": "2.4.1", + }, + ] + ) + assert load_json(mocked_store_data["plugins"]) == snapshot( + [ + { + "module_name": "nonebot_plugin_datastore", + "project_link": "nonebot-plugin-datastore", + "name": "数据存储", + "desc": "NoneBot 数据存储插件", + "author": "he0119", + "homepage": "https://github.com/he0119/nonebot-plugin-datastore", + "tags": [{"label": "good first plugin", "color": "#ffffff"}], + "is_official": False, + "type": "library", + "supported_adapters": None, + "valid": True, + "time": "2024-06-20T07:53:23.524486Z", + "version": "1.3.0", + "skip_test": False, + }, + { + "module_name": "nonebot_plugin_treehelp", + "project_link": "nonebot-plugin-treehelp", + "name": "帮助", + "desc": "获取插件帮助信息", + "author": "he0119", + "homepage": "https://github.com/he0119/nonebot-plugin-treehelp", + "tags": [], + "is_official": False, + "type": "application", + "supported_adapters": None, + "valid": True, + "time": "2024-07-13T04:41:40.905441Z", + "version": "0.5.0", + "skip_test": False, + }, + ] + ) + assert load_json(mocked_store_data["results"]) == snapshot( + { + "nonebot-plugin-datastore:nonebot_plugin_datastore": { + "time": "2023-06-26T22:08:18.945584+08:00", + "config": "", + "version": "1.3.0", + "test_env": None, + "results": {"validation": True, "load": True, "metadata": True}, + "outputs": { + "validation": None, + "load": "datastore", + "metadata": { + "name": "数据存储", + "description": "NoneBot 数据存储插件", + "usage": "请参考文档", + "type": "library", + "homepage": "https://github.com/he0119/nonebot-plugin-datastore", + "supported_adapters": None, + }, + }, + }, + "nonebot-plugin-treehelp:nonebot_plugin_treehelp": { + "time": "2023-06-26T22:20:41.833311+08:00", + "config": "", + "version": "0.3.0", + "test_env": None, + "results": {"validation": True, "load": True, "metadata": True}, + "outputs": { + "validation": None, + "load": "treehelp", + "metadata": { + "name": "帮助", + "description": "获取插件帮助信息", + "usage": """\ +获取插件列表 +/help +获取插件树 +/help -t +/help --tree +获取某个插件的帮助 +/help 插件名 +获取某个插件的树 +/help --tree 插件名 """, "type": "application", "homepage": "https://github.com/he0119/nonebot-plugin-treehelp", From 5425c170ca2b1a118e24303d9a686cd7e0c2987d Mon Sep 17 00:00:00 2001 From: uy_sun Date: Tue, 17 Dec 2024 09:39:09 +0800 Subject: [PATCH 08/12] =?UTF-8?q?test:=20=E4=BF=AE=E5=A4=8D=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E9=85=8D=E7=BD=AE=E4=B8=AD=E6=8C=87=E5=90=91=E7=9A=84?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=B2=A1=E7=BB=9F=E4=B8=80=20mock=20?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/utils.py | 12 ++- src/providers/validation/utils.py | 11 +-- tests/conftest.py | 76 +++++-------------- .../publish/process/test_publish_check.py | 22 ++---- .../resolve/test_resolve_pull_request.py | 8 +- tests/providers/store_test/test_store_sync.py | 4 +- 6 files changed, 43 insertions(+), 90 deletions(-) diff --git a/src/providers/utils.py b/src/providers/utils.py index bc86fdc5..6f87c4a3 100644 --- a/src/providers/utils.py +++ b/src/providers/utils.py @@ -69,14 +69,20 @@ def dump_json5(path: Path, data: Any) -> None: @cache -def get_pypi_data(project_link: str) -> dict[str, Any]: - """获取 PyPI 数据""" +def get_url(url: str) -> httpx.Response: + """获取网址""" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" } + return httpx.get(url, follow_redirects=True, headers=headers) + + +def get_pypi_data(project_link: str) -> dict[str, Any]: + """获取 PyPI 数据""" + url = f"https://pypi.org/pypi/{project_link}/json" try: - r = httpx.get(url, headers=headers) + r = get_url(url) except Exception as e: raise ValueError(f"获取 PyPI 数据失败:{e}") if r.status_code != 200: diff --git a/src/providers/validation/utils.py b/src/providers/validation/utils.py index b997c014..ee7b4422 100644 --- a/src/providers/validation/utils.py +++ b/src/providers/validation/utils.py @@ -1,10 +1,7 @@ -from functools import cache from typing import TYPE_CHECKING -import httpx - from src.providers.constants import STORE_ADAPTERS_URL -from src.providers.utils import load_json_from_web +from src.providers.utils import get_url, load_json_from_web from .constants import MESSAGE_TRANSLATIONS @@ -12,12 +9,6 @@ from pydantic_core import ErrorDetails -@cache -def get_url(url: str) -> httpx.Response: - """获取网址""" - return httpx.get(url, follow_redirects=True) - - def check_pypi(project_link: str) -> bool: """检查项目是否存在""" url = f"https://pypi.org/pypi/{project_link}/json" diff --git a/tests/conftest.py b/tests/conftest.py index 9b65540c..28636147 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,4 @@ +import shutil from pathlib import Path from typing import TypedDict @@ -24,6 +25,8 @@ STORE_PLUGINS_URL, ) +STORE_PATH = Path(__file__).parent / "store" + def pytest_configure(config: pytest.Config) -> None: config.stash[NONEBOT_INIT_KWARGS] = { @@ -66,51 +69,13 @@ async def app( app: App, tmp_path: Path, mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch ): from src.plugins.github import plugin_config - from src.providers.utils import dump_json5 adapter_path = tmp_path / "adapters.json5" - dump_json5( - adapter_path, - [ - { - "module_name": "module_name1", - "project_link": "project_link1", - "name": "name", - "desc": "desc", - "author_id": 1, - "homepage": "https://v2.nonebot.dev", - "tags": [], - "is_official": False, - } - ], - ) + shutil.copy(STORE_PATH / "store_adapters.json5", adapter_path) bot_path = tmp_path / "bots.json5" - dump_json5( - bot_path, - [ - { - "name": "name", - "desc": "desc", - "author_id": 1, - "homepage": "https://v2.nonebot.dev", - "tags": [], - "is_official": False, - } - ], - ) + shutil.copy(STORE_PATH / "store_bots.json5", bot_path) plugin_path = tmp_path / "plugins.json5" - dump_json5( - plugin_path, - [ - { - "module_name": "module_name1", - "project_link": "project_link1", - "author_id": 1, - "tags": [], - "is_official": False, - } - ], - ) + shutil.copy(STORE_PATH / "store_plugins.json5", plugin_path) mocker.patch.object(plugin_config.input_config, "adapter_path", adapter_path) mocker.patch.object(plugin_config.input_config, "bot_path", bot_path) @@ -122,17 +87,13 @@ async def app( # 以后需要想想办法,如果能通过 nb-cli 启动就好了。 monkeypatch.setenv("GITHUB_STEP_SUMMARY", str(tmp_path / "step_summary.md")) - yield app - - from src.providers.utils import get_pypi_data - - get_pypi_data.cache_clear() + return app @pytest.fixture(autouse=True) def _clear_cache(app: App): """每次运行前都清除 cache""" - from src.providers.validation.utils import get_url + from src.providers.utils import get_url get_url.cache_clear() @@ -229,35 +190,34 @@ def mocked_api(respx_mock: MockRouter): name="pypi_project_link_failed", ).respond(404) # 商店数据 - store_path = Path(__file__).parent / "store" respx_mock.get(STORE_ADAPTERS_URL, name="store_adapter").respond( - text=(store_path / "store_adapters.json5").read_text(encoding="utf8") + text=(STORE_PATH / "store_adapters.json5").read_text(encoding="utf8") ) respx_mock.get(STORE_BOTS_URL, name="store_bots").respond( - text=(store_path / "store_bots.json5").read_text(encoding="utf8") + text=(STORE_PATH / "store_bots.json5").read_text(encoding="utf8") ) respx_mock.get(STORE_DRIVERS_URL, name="store_drivers").respond( - text=(store_path / "store_drivers.json5").read_text(encoding="utf8") + text=(STORE_PATH / "store_drivers.json5").read_text(encoding="utf8") ) respx_mock.get(STORE_PLUGINS_URL, name="store_plugins").respond( - text=(store_path / "store_plugins.json5").read_text(encoding="utf8") + text=(STORE_PATH / "store_plugins.json5").read_text(encoding="utf8") ) respx_mock.get(REGISTRY_ADAPTERS_URL, name="registry_adapters").respond( - text=(store_path / "registry_adapters.json").read_text(encoding="utf8") + text=(STORE_PATH / "registry_adapters.json").read_text(encoding="utf8") ) respx_mock.get(REGISTRY_BOTS_URL, name="registry_bots").respond( - text=(store_path / "registry_bots.json").read_text(encoding="utf8") + text=(STORE_PATH / "registry_bots.json").read_text(encoding="utf8") ) respx_mock.get(REGISTRY_DRIVERS_URL, name="registry_drivers").respond( - text=(store_path / "registry_drivers.json").read_text(encoding="utf8") + text=(STORE_PATH / "registry_drivers.json").read_text(encoding="utf8") ) respx_mock.get(REGISTRY_PLUGINS_URL, name="registry_plugins").respond( - text=(store_path / "registry_plugins.json").read_text(encoding="utf8") + text=(STORE_PATH / "registry_plugins.json").read_text(encoding="utf8") ) respx_mock.get(REGISTRY_RESULTS_URL, name="registry_results").respond( - text=(store_path / "registry_results.json").read_text(encoding="utf8") + text=(STORE_PATH / "registry_results.json").read_text(encoding="utf8") ) respx_mock.get(REGISTRY_PLUGIN_CONFIG_URL, name="plugin_configs").respond( - text=(store_path / "plugin_configs.json").read_text(encoding="utf8") + text=(STORE_PATH / "plugin_configs.json").read_text(encoding="utf8") ) return respx_mock diff --git a/tests/plugins/github/publish/process/test_publish_check.py b/tests/plugins/github/publish/process/test_publish_check.py index 78c4e953..493817a2 100644 --- a/tests/plugins/github/publish/process/test_publish_check.py +++ b/tests/plugins/github/publish/process/test_publish_check.py @@ -673,17 +673,14 @@ async def test_plugin_process_publish_check( # 检查文件是否正确 check_json_data( - plugin_config.input_config.adapter_path, + plugin_config.input_config.plugin_path, [ snapshot( { - "module_name": "module_name1", - "project_link": "project_link1", - "name": "name", - "desc": "desc", + "module_name": "module_name", + "project_link": "project_link", "author_id": 1, - "homepage": "https://v2.nonebot.dev", - "tags": [], + "tags": [{"label": "test", "color": "#ffffff"}], "is_official": False, } ) @@ -965,17 +962,14 @@ async def test_plugin_process_publish_check_re_run( # 检查文件是否正确 check_json_data( - plugin_config.input_config.adapter_path, + plugin_config.input_config.plugin_path, [ snapshot( { - "module_name": "module_name1", - "project_link": "project_link1", - "name": "name", - "desc": "desc", + "module_name": "module_name", + "project_link": "project_link", "author_id": 1, - "homepage": "https://v2.nonebot.dev", - "tags": [], + "tags": [{"label": "test", "color": "#ffffff"}], "is_official": False, } ) diff --git a/tests/plugins/github/resolve/test_resolve_pull_request.py b/tests/plugins/github/resolve/test_resolve_pull_request.py index fc5c4dd0..c9b80f6f 100644 --- a/tests/plugins/github/resolve/test_resolve_pull_request.py +++ b/tests/plugins/github/resolve/test_resolve_pull_request.py @@ -47,13 +47,15 @@ async def test_resolve_pull_request( mock_publish_pull.head.ref = "publish/issue100" mock_publish_pull.labels = get_pr_labels(["Publish", "Bot"]) mock_remove_issue = MockIssue( - body=generate_issue_body_remove(type="Bot", key="name:https://v2.nonebot.dev"), + body=generate_issue_body_remove( + type="Bot", key="CoolQBot:https://github.com/he0119/CoolQBot" + ), number=101, ).as_mock(mocker) mock_remove_issue_resp = mocker.MagicMock() mock_remove_issue_resp.parsed_data = mock_remove_issue mock_remove_pull = mocker.MagicMock() - mock_remove_pull.title = "Bot: remove test" + mock_remove_pull.title = "Bot: remove CoolQBot" mock_remove_pull.draft = False mock_remove_pull.head.ref = "remove/issue101" mock_remove_pull.labels = get_pr_labels(["Remove", "Bot"]) @@ -149,7 +151,7 @@ async def test_resolve_pull_request( "test@users.noreply.github.com", ], ["git", "add", "-A"], - ["git", "commit", "-m", ":hammer: remove name (#101)"], + ["git", "commit", "-m", ":hammer: remove CoolQBot (#101)"], ["git", "fetch", "origin"], ["git", "diff", "origin/remove/issue101", "remove/issue101"], ["git", "push", "origin", "remove/issue101", "-f"], diff --git a/tests/providers/store_test/test_store_sync.py b/tests/providers/store_test/test_store_sync.py index a2dc0f02..11ea16bd 100644 --- a/tests/providers/store_test/test_store_sync.py +++ b/tests/providers/store_test/test_store_sync.py @@ -21,7 +21,7 @@ async def test_store_sync( 均增加一个 tag,以测试数据是否正确同步 """ from src.providers.store_test.store import StoreTest - from src.providers.utils import get_pypi_data + from src.providers.utils import get_url test = StoreTest() await test.run(0, 0, False) @@ -230,7 +230,7 @@ async def test_store_sync( ) # 缓存了 PyPI 数据,需要清除缓存 - get_pypi_data.cache_clear() + get_url.cache_clear() test = StoreTest() await test.run(0, 0, False) From f5189356415d84553c40adb51f2735dc90bd9867 Mon Sep 17 00:00:00 2001 From: uy/sun Date: Tue, 17 Dec 2024 11:44:16 +0800 Subject: [PATCH 09/12] =?UTF-8?q?fix:=20=E8=A1=A5=E4=B8=8A=E4=BB=8E?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E6=9B=B4=E6=96=B0=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/models.py | 5 ++++- src/providers/store_test/store.py | 12 ++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/providers/models.py b/src/providers/models.py index e11854b2..86a25018 100644 --- a/src/providers/models.py +++ b/src/providers/models.py @@ -399,7 +399,10 @@ def metadata(self) -> dict[str, Any]: def update(self, store: StorePlugin) -> "RegistryPlugin": """根据商店数据更新注册表数据""" - return RegistryPlugin(**self.model_dump(), **store.model_dump()) + # TODO: 如果 author_id 变化,应该重新获取 author + data = self.model_dump() + data.update(store.model_dump()) + return RegistryPlugin(**data) RegistryModels: TypeAlias = ( diff --git a/src/providers/store_test/store.py b/src/providers/store_test/store.py index 949d45b8..27a57ab2 100644 --- a/src/providers/store_test/store.py +++ b/src/providers/store_test/store.py @@ -337,12 +337,12 @@ async def sync_store(self): self._previous_drivers[key] = self._store_drivers[key].to_registry() for key in self._store_plugins: if key in self._previous_plugins: - plugin_data = self._previous_plugins[key].model_dump() - # 更新插件数据,假设商店数据的数据没有问题的 - # TODO: 如果 author_id 变化,应该重新获取 author - plugin_data.update(self._store_plugins[key].model_dump()) - self._previous_plugins[key] = RegistryPlugin(**plugin_data) - # TODO: 如果插件不存在,尝试重新测试获取相关信息验证 + self._previous_plugins[key] = self._previous_plugins[key].update( + self._store_plugins[key] + ) + else: + # TODO: 如果插件不存在,尝试重新测试获取相关信息验证 + pass def generate_github_summary(self, results: dict[str, StoreTestResult]): """生成 GitHub 摘要""" From 4a5eaa9e020b252646a7f57e3e88b77cbe03522b Mon Sep 17 00:00:00 2001 From: uy/sun Date: Tue, 17 Dec 2024 15:00:06 +0800 Subject: [PATCH 10/12] =?UTF-8?q?fix:=20=E5=A4=84=E7=90=86=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E5=87=BA=E9=94=99=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/providers/store_test/store.py | 71 ++++--- tests/providers/store_test/test_store_sync.py | 182 ++++++++++++++++++ 2 files changed, 228 insertions(+), 25 deletions(-) diff --git a/src/providers/store_test/store.py b/src/providers/store_test/store.py index 27a57ab2..b086cded 100644 --- a/src/providers/store_test/store.py +++ b/src/providers/store_test/store.py @@ -315,34 +315,55 @@ async def sync_store(self): 以商店数据为准,更新商店数据到仓库中,如果仓库中不存在则获取用户名后存储 """ for key in self._store_adapters: - if key in self._previous_adapters: - self._previous_adapters[key] = self._previous_adapters[key].update( - self._store_adapters[key] - ) - else: - self._previous_adapters[key] = self._store_adapters[key].to_registry() + try: + if key in self._previous_adapters: + new_adapter = self._previous_adapters[key].update( + self._store_adapters[key] + ) + else: + new_adapter = self._store_adapters[key].to_registry() + except Exception as e: + logger.error(f"适配器 {key} 同步商店数据失败:{e}") + continue + self._previous_adapters[key] = new_adapter for key in self._store_bots: - if key in self._previous_bots: - self._previous_bots[key] = self._previous_bots[key].update( - self._store_bots[key] - ) - else: - self._previous_bots[key] = self._store_bots[key].to_registry() + try: + if key in self._previous_bots: + new_bot = self._previous_bots[key].update(self._store_bots[key]) + else: + new_bot = self._store_bots[key].to_registry() + except Exception as e: + logger.error(f"机器人 {key} 同步商店数据失败:{e}") + continue + + self._previous_bots[key] = new_bot for key in self._store_drivers: - if key in self._previous_drivers: - self._previous_drivers[key] = self._previous_drivers[key].update( - self._store_drivers[key] - ) - else: - self._previous_drivers[key] = self._store_drivers[key].to_registry() + try: + if key in self._previous_drivers: + new_driver = self._previous_drivers[key].update( + self._store_drivers[key] + ) + else: + new_driver = self._store_drivers[key].to_registry() + except Exception as e: + logger.error(f"驱动器 {key} 同步商店数据失败:{e}") + continue + + self._previous_drivers[key] = new_driver for key in self._store_plugins: - if key in self._previous_plugins: - self._previous_plugins[key] = self._previous_plugins[key].update( - self._store_plugins[key] - ) - else: - # TODO: 如果插件不存在,尝试重新测试获取相关信息验证 - pass + try: + if key in self._previous_plugins: + new_plugin = self._previous_plugins[key].update( + self._store_plugins[key] + ) + else: + # TODO: 如果插件不存在,尝试重新测试获取相关信息验证 + raise NotImplementedError("插件需要重新测试") + except Exception as e: + logger.error(f"插件 {key} 同步商店数据失败:{e}") + continue + + self._previous_plugins[key] = new_plugin def generate_github_summary(self, results: dict[str, StoreTestResult]): """生成 GitHub 摘要""" diff --git a/tests/providers/store_test/test_store_sync.py b/tests/providers/store_test/test_store_sync.py index 11ea16bd..9d3fdef0 100644 --- a/tests/providers/store_test/test_store_sync.py +++ b/tests/providers/store_test/test_store_sync.py @@ -411,3 +411,185 @@ async def test_store_sync( }, } ) + + +async def test_store_sync_validation_failed( + mocked_store_data: dict[str, Path], mocked_api: MockRouter, mocker: MockerFixture +) -> None: + """测试同步商店数据出错的情况 + + 均增加一个 tag,以测试数据是否正确同步 + """ + from src.providers.store_test.store import StoreTest + + mocked_api.get("https://pypi.org/pypi/nonebot2/json", name="pypi_nonebot2").respond( + 404 + ) + + test = StoreTest() + await test.run(0, 0, False) + + assert load_json(mocked_store_data["adapters"]) == snapshot( + [ + { + "module_name": "nonebot.adapters.onebot.v11", + "project_link": "nonebot-adapter-onebot", + "name": "OneBot V11", + "desc": "OneBot V11 协议", + "author": "yanyongyu", + "homepage": "https://onebot.adapters.nonebot.dev/", + "tags": [{"label": "sync", "color": "#ffffff"}], + "is_official": True, + "time": "2024-10-24T07:34:56.115315Z", + "version": "2.4.6", + }, + { + "module_name": "nonebot.adapters.onebot.v12", + "project_link": "nonebot-adapter-onebot", + "name": "OneBot V12", + "desc": "OneBot V12 协议", + "author": "he0119", + "homepage": "https://onebot.adapters.nonebot.dev/", + "tags": [], + "is_official": True, + "time": "2024-10-24T07:34:56.115315Z", + "version": "2.4.6", + }, + ] + ) + assert load_json(mocked_store_data["bots"]) == snapshot( + [ + { + "name": "CoolQBot", + "desc": "基于 NoneBot2 的聊天机器人", + "author": "he0119", + "homepage": "https://github.com/he0119/CoolQBot", + "tags": [{"label": "sync", "color": "#ffffff"}], + "is_official": False, + }, + { + "name": "Github Bot", + "desc": "在QQ获取/处理Github repo/pr/issue", + "author": "BigOrangeQWQ", + "homepage": "https://github.com/cscs181/QQ-GitHub-Bot", + "tags": [], + "is_official": False, + }, + ] + ) + assert load_json(mocked_store_data["drivers"]) == snapshot( + [ + { + "module_name": "~none", + "project_link": "", + "name": "None", + "desc": "None 驱动器", + "author": "yanyongyu", + "homepage": "/docs/advanced/driver", + "tags": [], + "is_official": True, + "time": "2024-10-31T13:47:14.152851Z", + "version": "2.4.0", + }, + { + "module_name": "~fastapi", + "project_link": "nonebot2[fastapi]", + "name": "FastAPI", + "desc": "FastAPI 驱动器", + "author": "yanyongyu", + "homepage": "/docs/advanced/driver", + "tags": [], + "is_official": True, + "time": "2024-10-31T13:47:14.152851Z", + "version": "2.4.0", + }, + ] + ) + assert load_json(mocked_store_data["plugins"]) == snapshot( + [ + { + "module_name": "nonebot_plugin_datastore", + "project_link": "nonebot-plugin-datastore", + "name": "数据存储", + "desc": "NoneBot 数据存储插件", + "author": "he0119", + "homepage": "https://github.com/he0119/nonebot-plugin-datastore", + "tags": [{"label": "good first plugin", "color": "#ffffff"}], + "is_official": False, + "type": "library", + "supported_adapters": None, + "valid": True, + "time": "2024-06-20T07:53:23.524486Z", + "version": "1.3.0", + "skip_test": False, + }, + { + "module_name": "nonebot_plugin_treehelp", + "project_link": "nonebot-plugin-treehelp", + "name": "帮助", + "desc": "获取插件帮助信息", + "author": "he0119", + "homepage": "https://github.com/he0119/nonebot-plugin-treehelp", + "tags": [], + "is_official": False, + "type": "application", + "supported_adapters": None, + "valid": True, + "time": "2024-07-13T04:41:40.905441Z", + "version": "0.5.0", + "skip_test": False, + }, + ] + ) + assert load_json(mocked_store_data["results"]) == snapshot( + { + "nonebot-plugin-datastore:nonebot_plugin_datastore": { + "time": "2023-06-26T22:08:18.945584+08:00", + "config": "", + "version": "1.3.0", + "test_env": None, + "results": {"validation": True, "load": True, "metadata": True}, + "outputs": { + "validation": None, + "load": "datastore", + "metadata": { + "name": "数据存储", + "description": "NoneBot 数据存储插件", + "usage": "请参考文档", + "type": "library", + "homepage": "https://github.com/he0119/nonebot-plugin-datastore", + "supported_adapters": None, + }, + }, + }, + "nonebot-plugin-treehelp:nonebot_plugin_treehelp": { + "time": "2023-06-26T22:20:41.833311+08:00", + "config": "", + "version": "0.3.0", + "test_env": None, + "results": {"validation": True, "load": True, "metadata": True}, + "outputs": { + "validation": None, + "load": "treehelp", + "metadata": { + "name": "帮助", + "description": "获取插件帮助信息", + "usage": """\ +获取插件列表 +/help +获取插件树 +/help -t +/help --tree +获取某个插件的帮助 +/help 插件名 +获取某个插件的树 +/help --tree 插件名 +""", + "type": "application", + "homepage": "https://github.com/he0119/nonebot-plugin-treehelp", + "supported_adapters": None, + }, + }, + }, + } + ) From 153cb95093604361918beff84433579cc60eb07b Mon Sep 17 00:00:00 2001 From: uy/sun Date: Tue, 17 Dec 2024 15:05:42 +0800 Subject: [PATCH 11/12] =?UTF-8?q?test:=20=E7=BB=9F=E4=B8=80=E7=94=A8?= =?UTF-8?q?=E6=9D=A5=E6=B5=8B=E8=AF=95=E5=90=8C=E6=AD=A5=E7=9A=84=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/providers/store_test/test_store_sync.py | 6 +++--- tests/providers/store_test/test_store_test.py | 4 ++-- tests/store/store_plugins.json5 | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/providers/store_test/test_store_sync.py b/tests/providers/store_test/test_store_sync.py index 9d3fdef0..0b51ee31 100644 --- a/tests/providers/store_test/test_store_sync.py +++ b/tests/providers/store_test/test_store_sync.py @@ -123,7 +123,7 @@ async def test_store_sync( "desc": "NoneBot 数据存储插件", "author": "he0119", "homepage": "https://github.com/he0119/nonebot-plugin-datastore", - "tags": [{"label": "good first plugin", "color": "#ffffff"}], + "tags": [{"label": "sync", "color": "#ffffff"}], "is_official": False, "type": "library", "supported_adapters": None, @@ -332,7 +332,7 @@ async def test_store_sync( "desc": "NoneBot 数据存储插件", "author": "he0119", "homepage": "https://github.com/he0119/nonebot-plugin-datastore", - "tags": [{"label": "good first plugin", "color": "#ffffff"}], + "tags": [{"label": "sync", "color": "#ffffff"}], "is_official": False, "type": "library", "supported_adapters": None, @@ -514,7 +514,7 @@ async def test_store_sync_validation_failed( "desc": "NoneBot 数据存储插件", "author": "he0119", "homepage": "https://github.com/he0119/nonebot-plugin-datastore", - "tags": [{"label": "good first plugin", "color": "#ffffff"}], + "tags": [{"label": "sync", "color": "#ffffff"}], "is_official": False, "type": "library", "supported_adapters": None, diff --git a/tests/providers/store_test/test_store_test.py b/tests/providers/store_test/test_store_test.py index a614481f..d562fea4 100644 --- a/tests/providers/store_test/test_store_test.py +++ b/tests/providers/store_test/test_store_test.py @@ -104,7 +104,7 @@ async def test_store_test( '[{"module_name":"~none","project_link":"","name":"None","desc":"None 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[{"label":"sync","color":"#ffffff"}],"is_official":true,"time":"2024-10-31T13:47:14.152851Z","version":"2.4.0"},{"module_name":"~fastapi","project_link":"nonebot2[fastapi]","name":"FastAPI","desc":"FastAPI 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[],"is_official":true,"time":"2024-10-31T13:47:14.152851Z","version":"2.4.0"},{"module_name":"~quart","project_link":"nonebot2[quart]","name":"Quart","desc":"Quart 驱动器","author":"he0119","homepage":"/docs/advanced/driver","tags":[],"is_official":true,"time":"2024-10-31T13:47:14.152851Z","version":"2.4.0"}]' ) assert mocked_store_data["plugins"].read_text(encoding="utf-8") == snapshot( - '[{"module_name":"nonebot_plugin_datastore","project_link":"nonebot-plugin-datastore","name":"数据存储","desc":"NoneBot 数据存储插件","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-datastore","tags":[{"label":"good first plugin","color":"#ffffff"}],"is_official":false,"type":"library","supported_adapters":null,"valid":true,"time":"2024-06-20T07:53:23.524486Z","version":"1.3.0","skip_test":false},{"module_name":"nonebot_plugin_treehelp","project_link":"nonebot-plugin-treehelp","name":"帮助","desc":"获取插件帮助信息","author":"author","homepage":"https://nonebot.dev/","tags":[],"is_official":false,"type":"application","supported_adapters":null,"valid":true,"time":"2023-08-28T00:00:00.000000+08:00","version":"0.3.0","skip_test":false}]' + '[{"module_name":"nonebot_plugin_datastore","project_link":"nonebot-plugin-datastore","name":"数据存储","desc":"NoneBot 数据存储插件","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-datastore","tags":[{"label":"sync","color":"#ffffff"}],"is_official":false,"type":"library","supported_adapters":null,"valid":true,"time":"2024-06-20T07:53:23.524486Z","version":"1.3.0","skip_test":false},{"module_name":"nonebot_plugin_treehelp","project_link":"nonebot-plugin-treehelp","name":"帮助","desc":"获取插件帮助信息","author":"author","homepage":"https://nonebot.dev/","tags":[],"is_official":false,"type":"application","supported_adapters":null,"valid":true,"time":"2023-08-28T00:00:00.000000+08:00","version":"0.3.0","skip_test":false}]' ) assert mocked_store_data["results"].read_text(encoding="utf-8") == snapshot( '{"nonebot-plugin-datastore:nonebot_plugin_datastore":{"time":"2023-06-26T22:08:18.945584+08:00","config":"","version":"1.3.0","test_env":null,"results":{"validation":true,"load":true,"metadata":true},"outputs":{"validation":null,"load":"datastore","metadata":{"name":"数据存储","description":"NoneBot 数据存储插件","usage":"请参考文档","type":"library","homepage":"https://github.com/he0119/nonebot-plugin-datastore","supported_adapters":null}}},"nonebot-plugin-treehelp:nonebot_plugin_treehelp":{"time":"2023-08-28T00:00:00.000000+08:00","config":"","version":"1.0.0","test_env":null,"results":{"load":true,"metadata":true,"validation":true},"outputs":{"load":"output","metadata":{"name":"帮助","description":"获取插件帮助信息","usage":"获取插件列表\\n/help\\n获取插件树\\n/help -t\\n/help --tree\\n获取某个插件的帮助\\n/help 插件名\\n获取某个插件的树\\n/help --tree 插件名\\n","type":"application","homepage":"https://nonebot.dev/","supported_adapters":null},"validation":null}}}' @@ -240,7 +240,7 @@ async def test_store_test_raise( '[{"module_name":"~none","project_link":"","name":"None","desc":"None 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[{"label":"sync","color":"#ffffff"}],"is_official":true,"time":"2024-10-31T13:47:14.152851Z","version":"2.4.0"},{"module_name":"~fastapi","project_link":"nonebot2[fastapi]","name":"FastAPI","desc":"FastAPI 驱动器","author":"yanyongyu","homepage":"/docs/advanced/driver","tags":[],"is_official":true,"time":"2024-10-31T13:47:14.152851Z","version":"2.4.0"},{"module_name":"~quart","project_link":"nonebot2[quart]","name":"Quart","desc":"Quart 驱动器","author":"he0119","homepage":"/docs/advanced/driver","tags":[],"is_official":true,"time":"2024-10-31T13:47:14.152851Z","version":"2.4.0"}]' ) assert mocked_store_data["plugins"].read_text(encoding="utf-8") == snapshot( - '[{"module_name":"nonebot_plugin_datastore","project_link":"nonebot-plugin-datastore","name":"数据存储","desc":"NoneBot 数据存储插件","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-datastore","tags":[{"label":"good first plugin","color":"#ffffff"}],"is_official":false,"type":"library","supported_adapters":null,"valid":true,"time":"2024-06-20T07:53:23.524486Z","version":"1.3.0","skip_test":false},{"module_name":"nonebot_plugin_treehelp","project_link":"nonebot-plugin-treehelp","name":"帮助","desc":"获取插件帮助信息","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-treehelp","tags":[],"is_official":false,"type":"application","supported_adapters":null,"valid":true,"time":"2024-07-13T04:41:40.905441Z","version":"0.5.0","skip_test":false}]' + '[{"module_name":"nonebot_plugin_datastore","project_link":"nonebot-plugin-datastore","name":"数据存储","desc":"NoneBot 数据存储插件","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-datastore","tags":[{"label":"sync","color":"#ffffff"}],"is_official":false,"type":"library","supported_adapters":null,"valid":true,"time":"2024-06-20T07:53:23.524486Z","version":"1.3.0","skip_test":false},{"module_name":"nonebot_plugin_treehelp","project_link":"nonebot-plugin-treehelp","name":"帮助","desc":"获取插件帮助信息","author":"he0119","homepage":"https://github.com/he0119/nonebot-plugin-treehelp","tags":[],"is_official":false,"type":"application","supported_adapters":null,"valid":true,"time":"2024-07-13T04:41:40.905441Z","version":"0.5.0","skip_test":false}]' ) assert mocked_store_data["results"].read_text(encoding="utf-8") == snapshot( '{"nonebot-plugin-datastore:nonebot_plugin_datastore":{"time":"2023-06-26T22:08:18.945584+08:00","config":"","version":"1.3.0","test_env":null,"results":{"validation":true,"load":true,"metadata":true},"outputs":{"validation":null,"load":"datastore","metadata":{"name":"数据存储","description":"NoneBot 数据存储插件","usage":"请参考文档","type":"library","homepage":"https://github.com/he0119/nonebot-plugin-datastore","supported_adapters":null}}},"nonebot-plugin-treehelp:nonebot_plugin_treehelp":{"time":"2023-06-26T22:20:41.833311+08:00","config":"","version":"0.3.0","test_env":null,"results":{"validation":true,"load":true,"metadata":true},"outputs":{"validation":null,"load":"treehelp","metadata":{"name":"帮助","description":"获取插件帮助信息","usage":"获取插件列表\\n/help\\n获取插件树\\n/help -t\\n/help --tree\\n获取某个插件的帮助\\n/help 插件名\\n获取某个插件的树\\n/help --tree 插件名\\n","type":"application","homepage":"https://github.com/he0119/nonebot-plugin-treehelp","supported_adapters":null}}}}' diff --git a/tests/store/store_plugins.json5 b/tests/store/store_plugins.json5 index dff05ed0..15439ec5 100644 --- a/tests/store/store_plugins.json5 +++ b/tests/store/store_plugins.json5 @@ -6,7 +6,7 @@ "author_id": 1, "tags": [ { - "label": "good first plugin", + "label": "sync", "color": "#ffffff" } ], From 3c9a516be3cd476cb267de3934f748cde4aabf88 Mon Sep 17 00:00:00 2001 From: uy/sun Date: Tue, 17 Dec 2024 16:15:13 +0800 Subject: [PATCH 12/12] =?UTF-8?q?test:=20=E6=89=80=E6=9C=89=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E5=9D=87=E5=87=BA=E9=94=99=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/providers/store_test/test_store_sync.py | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/tests/providers/store_test/test_store_sync.py b/tests/providers/store_test/test_store_sync.py index 0b51ee31..9daf0c3e 100644 --- a/tests/providers/store_test/test_store_sync.py +++ b/tests/providers/store_test/test_store_sync.py @@ -425,6 +425,13 @@ async def test_store_sync_validation_failed( mocked_api.get("https://pypi.org/pypi/nonebot2/json", name="pypi_nonebot2").respond( 404 ) + mocked_api.get( + "https://pypi.org/pypi/nonebot-adapter-onebot/json", + name="pypi_nonebot-adapter-onebot", + ).respond(404) + mocked_api.get( + "https://github.com/cscs181/QQ-GitHub-Bot", name="homepage_qq_github_bot" + ).respond(404) test = StoreTest() await test.run(0, 0, False) @@ -438,23 +445,11 @@ async def test_store_sync_validation_failed( "desc": "OneBot V11 协议", "author": "yanyongyu", "homepage": "https://onebot.adapters.nonebot.dev/", - "tags": [{"label": "sync", "color": "#ffffff"}], - "is_official": True, - "time": "2024-10-24T07:34:56.115315Z", - "version": "2.4.6", - }, - { - "module_name": "nonebot.adapters.onebot.v12", - "project_link": "nonebot-adapter-onebot", - "name": "OneBot V12", - "desc": "OneBot V12 协议", - "author": "he0119", - "homepage": "https://onebot.adapters.nonebot.dev/", "tags": [], "is_official": True, "time": "2024-10-24T07:34:56.115315Z", "version": "2.4.6", - }, + } ] ) assert load_json(mocked_store_data["bots"]) == snapshot( @@ -466,15 +461,7 @@ async def test_store_sync_validation_failed( "homepage": "https://github.com/he0119/CoolQBot", "tags": [{"label": "sync", "color": "#ffffff"}], "is_official": False, - }, - { - "name": "Github Bot", - "desc": "在QQ获取/处理Github repo/pr/issue", - "author": "BigOrangeQWQ", - "homepage": "https://github.com/cscs181/QQ-GitHub-Bot", - "tags": [], - "is_official": False, - }, + } ] ) assert load_json(mocked_store_data["drivers"]) == snapshot(