Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: 将 datetime 的 mock 统一为一个 fixture,且修改 patch 的方案 #361

Merged
merged 5 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
STORE_DRIVERS_URL,
STORE_PLUGINS_URL,
)
from tests.constant import PROJECT_SOURCE_PATH
from tests.utils import find_datetime_loc

STORE_PATH = Path(__file__).parent / "store"

Expand Down Expand Up @@ -221,3 +223,38 @@ def mocked_api(respx_mock: MockRouter):
text=(STORE_PATH / "plugin_configs.json").read_text(encoding="utf8")
)
return respx_mock


@pytest.fixture(scope="session", autouse=True)
def datetime_modules_loc() -> list[str]:
return find_datetime_loc(PROJECT_SOURCE_PATH)


@pytest.fixture
def mock_datetime(mocker: MockerFixture, datetime_modules_loc: list[str]):
"""
将所有模块中的 datetime.now() 方法返回值固定
"""
from datetime import datetime
from importlib import import_module

from src.providers.constants import TIME_ZONE

fixed_time = datetime(2023, 8, 23, 9, 22, 14, 836035, tzinfo=TIME_ZONE)

for module_path in datetime_modules_loc:
try:
module = import_module(module_path)
original_datetime = module.datetime

class MockDateTime(original_datetime):
@classmethod
def now(cls, tz=None):
return fixed_time

mocker.patch.object(module, "datetime", new=MockDateTime)

except (ImportError, AttributeError):
continue

return fixed_time
3 changes: 3 additions & 0 deletions tests/constant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from pathlib import Path

PROJECT_SOURCE_PATH = Path("./src")
8 changes: 1 addition & 7 deletions tests/plugins/github/config/process/test_config_check.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
from datetime import datetime
from pathlib import Path

from inline_snapshot import snapshot
Expand Down Expand Up @@ -29,19 +28,14 @@ async def test_process_config_check(
tmp_path: Path,
mock_installation,
mock_results: dict[str, Path],
mock_datetime,
) -> None:
"""测试发布检查不通过"""
from src.providers.constants import TIME_ZONE
from src.providers.docker_test import Metadata

# 更改当前工作目录为临时目录
os.chdir(tmp_path)

mock_datetime = mocker.patch("src.providers.models.datetime")
mock_datetime.now.return_value = datetime(
2023, 8, 23, 9, 22, 14, 836035, tzinfo=TIME_ZONE
)

mock_subprocess_run = mocker.patch(
"subprocess.run", side_effect=lambda *args, **kwargs: mocker.MagicMock()
)
Expand Down
8 changes: 1 addition & 7 deletions tests/plugins/github/config/utils/test_config_update_file.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
from datetime import datetime
from pathlib import Path

from inline_snapshot import snapshot
Expand All @@ -14,9 +13,9 @@ async def test_update_file(
mocker: MockerFixture,
tmp_path: Path,
mock_results: dict[str, Path],
mock_datetime,
) -> None:
from src.plugins.github.plugins.config.utils import update_file
from src.providers.constants import TIME_ZONE
from src.providers.validation.models import (
PluginPublishInfo,
PublishType,
Expand All @@ -26,11 +25,6 @@ async def test_update_file(
# 更改当前工作目录为临时目录
os.chdir(tmp_path)

mock_datetime = mocker.patch("src.providers.models.datetime")
mock_datetime.now.return_value = datetime(
2023, 8, 23, 9, 22, 14, 836035, tzinfo=TIME_ZONE
)

raw_data = {
"module_name": "nonebot_plugin_treehelp",
"project_link": "nonebot-plugin-treehelp",
Expand Down
16 changes: 8 additions & 8 deletions tests/plugins/github/remove/process/test_remove_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,8 +513,8 @@ async def test_process_remove_not_found_check(
["git", "config", "--global", "safe.directory", "*"],
check=True,
capture_output=True,
),
] # type: ignore
), # type: ignore
]
)


Expand Down Expand Up @@ -629,8 +629,8 @@ async def test_process_remove_author_info_not_eq(
["git", "config", "--global", "safe.directory", "*"],
check=True,
capture_output=True,
),
] # type: ignore
), # type: ignore
]
)


Expand Down Expand Up @@ -743,8 +743,8 @@ async def test_process_remove_issue_info_not_found(
["git", "config", "--global", "safe.directory", "*"],
check=True,
capture_output=True,
),
] # type: ignore
), # type: ignore
]
)


Expand Down Expand Up @@ -838,8 +838,8 @@ async def test_process_remove_driver(
["git", "config", "--global", "safe.directory", "*"],
check=True,
capture_output=True,
),
] # type: ignore
), # type: ignore
]
)


Expand Down
12 changes: 4 additions & 8 deletions tests/providers/store_test/test_step_summary.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from datetime import datetime
from pathlib import Path

from inline_snapshot import snapshot
Expand All @@ -7,18 +6,15 @@


async def test_step_summary(
mocked_store_data: dict[str, Path], mocked_api: MockRouter, mocker: MockerFixture
mocked_store_data: dict[str, Path],
mocked_api: MockRouter,
mocker: MockerFixture,
mock_datetime,
) -> None:
"""验证插件信息"""
from src.providers.constants import TIME_ZONE
from src.providers.models import StoreTestResult
from src.providers.store_test.store import StoreTest

mock_datetime = mocker.patch("src.providers.store_test.store.datetime")
mock_datetime.now.return_value = datetime(
2023, 8, 23, 9, 22, 14, 836035, tzinfo=TIME_ZONE
)

store_test = {
"NOT_AC": StoreTestResult(
config="",
Expand Down
43 changes: 7 additions & 36 deletions tests/providers/store_test/test_validate_plugin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import json
from datetime import datetime
from pathlib import Path

from inline_snapshot import snapshot
Expand All @@ -23,17 +22,13 @@ def mock_docker_result(path: Path, mocker: MockerFixture):
return mock_plugin_test


async def test_validate_plugin(mocked_api: MockRouter, mocker: MockerFixture) -> None:
async def test_validate_plugin(
mocked_api: MockRouter, mocker: MockerFixture, mock_datetime
) -> None:
"""验证插件信息"""
from src.providers.constants import TIME_ZONE
from src.providers.models import RegistryPlugin, StorePlugin, StoreTestResult
from src.providers.store_test.validation import validate_plugin

mock_datetime = mocker.patch("src.providers.models.datetime")
mock_datetime.now.return_value = datetime(
2023, 8, 23, 9, 22, 14, 836035, tzinfo=TIME_ZONE
)

output_path = Path(__file__).parent / "output.json"
mock_docker_result(output_path, mocker)

Expand Down Expand Up @@ -97,21 +92,15 @@ async def test_validate_plugin(mocked_api: MockRouter, mocker: MockerFixture) ->


async def test_validate_plugin_with_previous(
mocked_api: MockRouter, mocker: MockerFixture
mocked_api: MockRouter, mocker: MockerFixture, mock_datetime
) -> None:
"""插件验证通过,但提供了之前插件信息的情况

需要能够正常更新 author_id, tags 和 is_official 等信息
"""
from src.providers.constants import TIME_ZONE
from src.providers.models import Color, RegistryPlugin, StoreTestResult, Tag
from src.providers.store_test.validation import StorePlugin, validate_plugin

mock_datetime = mocker.patch("src.providers.models.datetime")
mock_datetime.now.return_value = datetime(
2023, 8, 23, 9, 22, 14, 836035, tzinfo=TIME_ZONE
)

output_path = Path(__file__).parent / "output.json"
mock_docker_result(output_path, mocker)

Expand Down Expand Up @@ -195,21 +184,15 @@ async def test_validate_plugin_with_previous(


async def test_validate_plugin_skip_test(
mocked_api: MockRouter, mocker: MockerFixture
mocked_api: MockRouter, mocker: MockerFixture, mock_datetime
) -> None:
"""跳过插件测试的情况

如果插件之前是跳过测试的,如果插件测试成功,应将 skip_test 设置为 False。
"""
from src.providers.constants import TIME_ZONE
from src.providers.models import RegistryPlugin, StoreTestResult
from src.providers.store_test.validation import StorePlugin, validate_plugin

mock_datetime = mocker.patch("src.providers.models.datetime")
mock_datetime.now.return_value = datetime(
2023, 8, 23, 9, 22, 14, 836035, tzinfo=TIME_ZONE
)

output_path = Path(__file__).parent / "output.json"
mock_docker_result(output_path, mocker)

Expand Down Expand Up @@ -273,21 +256,15 @@ async def test_validate_plugin_skip_test(


async def test_validate_plugin_skip_test_plugin_test_failed(
mocked_api: MockRouter, mocker: MockerFixture
mocked_api: MockRouter, mocker: MockerFixture, mock_datetime
) -> None:
"""跳过插件测试的情况

如果插件之前是跳过测试的,如果插件测试失败,应不改变 skip_test 的值。
"""
from src.providers.constants import TIME_ZONE
from src.providers.models import RegistryPlugin, StoreTestResult
from src.providers.store_test.validation import StorePlugin, validate_plugin

mock_datetime = mocker.patch("src.providers.models.datetime")
mock_datetime.now.return_value = datetime(
2023, 8, 23, 9, 22, 14, 836035, tzinfo=TIME_ZONE
)

output_path = Path(__file__).parent / "output_failed.json"
mock_docker_result(output_path, mocker)

Expand Down Expand Up @@ -390,21 +367,15 @@ async def test_validate_plugin_skip_test_plugin_test_failed(


async def test_validate_plugin_failed_with_previous(
mocked_api: MockRouter, mocker: MockerFixture
mocked_api: MockRouter, mocker: MockerFixture, mock_datetime
) -> None:
"""插件验证失败,但提供了之前插件信息的情况

需要能够正常更新 author_id, tags 和 is_official 等信息
"""
from src.providers.constants import TIME_ZONE
from src.providers.models import RegistryPlugin, StoreTestResult
from src.providers.store_test.validation import StorePlugin, validate_plugin

mock_datetime = mocker.patch("src.providers.models.datetime")
mock_datetime.now.return_value = datetime(
2023, 8, 23, 9, 22, 14, 836035, tzinfo=TIME_ZONE
)

output_path = Path(__file__).parent / "output_failed.json"
mock_docker_result(output_path, mocker)

Expand Down
21 changes: 21 additions & 0 deletions tests/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from pathlib import Path


def find_datetime_loc(path: Path) -> list[str]:
"""
找到所有包含 `from datetime import datetime` 的相对导入路径
"""
result: list[str] = []

def to_module_path(file_path: Path) -> str:
relative_path = file_path.relative_to(path.parent)
parts = list(relative_path.with_suffix("").parts)
if parts[-1] == "__init__":
parts = parts[:-1]
return ".".join(parts)

for p in path.rglob("*.py"):
content = p.read_text("utf-8")
if "from datetime import datetime" in content:
result.append(to_module_path(p))
return result
Loading