Skip to content
Open
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
2 changes: 1 addition & 1 deletion src/plugins/github/depends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ async def get_labels_name(labels: LabelsItems = Depends(get_labels)) -> list[str

async def get_issue_title(event: IssuesEvent):
"""获取议题标题"""
return event.payload.issue.title
return event.payload.issue.title # pragma: no cover


async def get_repo_info(event: PullRequestEvent | IssuesEvent) -> RepoInfo:
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/github/plugins/publish/depends.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

def get_type_by_title(title: str = Depends(get_issue_title)) -> PublishType | None:
"""通过标题获取类型"""
return utils.get_type_by_title(title)
return utils.get_type_by_title(title) # pragma: no cover


async def get_pull_requests_by_label(
Expand Down
9 changes: 7 additions & 2 deletions src/plugins/github/plugins/publish/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from githubkit.exception import RequestFailed
from nonebot import logger
from nonebot.adapters.github import ActionFailed

from src.plugins.github import plugin_config
from src.plugins.github.constants import (
Expand Down Expand Up @@ -132,7 +133,11 @@ async def resolve_conflict_pull_requests(
logger.info("拉取请求为草稿,跳过处理")
continue

issue_handler = await handler.to_issue_handler(issue_number)
try:
issue_handler = await handler.to_issue_handler(issue_number)
except (ActionFailed, RequestFailed):
logger.error(f"议题 #{issue_number} 不存在,跳过处理 {pull.title}")
continue

try:
artifact = await get_noneflow_artifact(issue_handler)
Expand Down Expand Up @@ -169,7 +174,7 @@ def update_file(store: StoreModels, handler: GitHandler) -> None:
path = plugin_config.input_config.bot_path
case StorePlugin():
path = plugin_config.input_config.plugin_path
case _:
case _: # pragma: no cover
raise ValueError("暂不支持的发布类型")

logger.info(f"正在更新文件: {path}")
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/github/plugins/remove/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from githubkit.exception import RequestFailed
from nonebot import logger
from nonebot.adapters.github import ActionFailed
from pydantic_core import PydanticCustomError

from src.plugins.github import plugin_config
Expand Down Expand Up @@ -97,7 +98,11 @@ async def resolve_conflict_pull_requests(

# 根据标签获取发布类型
publish_type = get_type_by_labels(pull.labels)
issue_handler = await handler.to_issue_handler(issue_number)
try:
issue_handler = await handler.to_issue_handler(issue_number)
except (ActionFailed, RequestFailed):
logger.error(f"议题 #{issue_number} 不存在,跳过处理 {pull.title}")
continue

if publish_type:
# 验证作者信息
Expand Down
4 changes: 2 additions & 2 deletions src/providers/validation/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ def supported_adapters_validator(
def plugin_test_load_validator(cls, v: bool, info: ValidationInfo) -> bool:
context = info.context
if context is None:
raise PydanticCustomError("validation_context", "未获取到验证上下文")
raise PydanticCustomError("validation_context", "未获取到验证上下文") # pragma: no cover

if v or context.get("skip_test"):
return True
Expand All @@ -318,7 +318,7 @@ def plugin_test_metadata_validator(
) -> bool:
context = info.context
if context is None:
raise PydanticCustomError("validation_context", "未获取到验证上下文")
raise PydanticCustomError("validation_context", "未获取到验证上下文") # pragma: no cover

if not v:
raise PydanticCustomError(
Expand Down
49 changes: 49 additions & 0 deletions tests/plugins/github/config/process/test_config_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,3 +359,52 @@ async def test_process_config_check(
)

assert mocked_api["homepage"].called


async def test_config_issue_state_closed(
app: App,
mocker: MockerFixture,
mocked_api: MockRouter,
mock_installation,
mock_installation_token,
mock_results,
) -> None:
"""测试配置检查时议题已关闭的情况"""
import os

os.chdir(mock_results["plugins"].parent)

mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker)

mock_issue = MockIssue(state="closed").as_mock(mocker)
mock_issues_resp = mocker.MagicMock()
mock_issues_resp.parsed_data = mock_issue

async with app.test_matcher() as ctx:
_adapter, bot = get_github_bot(ctx)
event = get_mock_event(IssuesOpened)
event.payload.issue.labels = get_config_labels()

ctx.should_call_api(
"rest.apps.async_get_repo_installation",
{"owner": "he0119", "repo": "action-test"},
mock_installation,
)
ctx.should_call_api(
"rest.apps.async_create_installation_access_token",
{"installation_id": mock_installation.parsed_data.id},
mock_installation_token,
)
ctx.should_call_api(
"rest.issues.async_get",
{"owner": "he0119", "repo": "action-test", "issue_number": 80},
mock_issues_resp,
)

ctx.receive_event(bot, event)

assert mocked_api.calls == []
assert_subprocess_run_calls(
mock_subprocess_run,
[["git", "config", "--global", "safe.directory", "*"]],
)
218 changes: 218 additions & 0 deletions tests/plugins/github/publish/process/test_publish_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -1466,3 +1466,221 @@ async def test_comment_immediate_after_pull_request_closed(
)

assert mocked_api["homepage"].called


async def test_plugin_issue_state_closed(
app: App,
mocker: MockerFixture,
mocked_api: MockRouter,
mock_installation,
mock_installation_token,
) -> None:
"""测试插件议题已关闭

发布类型为 Plugin,event.issue.state = "closed"
"""
mock_subprocess_run = mocker.patch(
"subprocess.run", side_effect=lambda *args, **kwargs: mocker.MagicMock()
)

mock_issue = MockIssue(state="closed").as_mock(mocker)
mock_issues_resp = mocker.MagicMock()
mock_issues_resp.parsed_data = mock_issue

async with app.test_matcher() as ctx:
_adapter, bot = get_github_bot(ctx)
event = get_mock_event(IssuesOpened)
event.payload.issue.labels = get_issue_labels(["Plugin", "Publish"])

ctx.should_call_api(
"rest.apps.async_get_repo_installation",
{"owner": "he0119", "repo": "action-test"},
mock_installation,
)
ctx.should_call_api(
"rest.apps.async_create_installation_access_token",
{"installation_id": mock_installation.parsed_data.id},
mock_installation_token,
)
ctx.should_call_api(
"rest.issues.async_get",
{"owner": "he0119", "repo": "action-test", "issue_number": 80},
mock_issues_resp,
)

ctx.receive_event(bot, event)

assert mocked_api.calls == []
assert_subprocess_run_calls(
mock_subprocess_run,
[["git", "config", "--global", "safe.directory", "*"]],
)


async def test_adapter_issue_state_closed(
app: App,
mocker: MockerFixture,
mocked_api: MockRouter,
mock_installation,
mock_installation_token,
) -> None:
"""测试适配器议题已关闭

发布类型为 Adapter,event.issue.state = "closed"
"""
mock_subprocess_run = mocker.patch(
"subprocess.run", side_effect=lambda *args, **kwargs: mocker.MagicMock()
)

mock_issue = MockIssue(state="closed").as_mock(mocker)
mock_issues_resp = mocker.MagicMock()
mock_issues_resp.parsed_data = mock_issue

async with app.test_matcher() as ctx:
_adapter, bot = get_github_bot(ctx)
event = get_mock_event(IssuesOpened)
event.payload.issue.labels = get_issue_labels(["Adapter", "Publish"])

ctx.should_call_api(
"rest.apps.async_get_repo_installation",
{"owner": "he0119", "repo": "action-test"},
mock_installation,
)
ctx.should_call_api(
"rest.apps.async_create_installation_access_token",
{"installation_id": mock_installation.parsed_data.id},
mock_installation_token,
)
ctx.should_call_api(
"rest.issues.async_get",
{"owner": "he0119", "repo": "action-test", "issue_number": 80},
mock_issues_resp,
)

ctx.receive_event(bot, event)

assert mocked_api.calls == []
assert_subprocess_run_calls(
mock_subprocess_run,
[["git", "config", "--global", "safe.directory", "*"]],
)


async def test_bot_process_publish_check_with_history(
app: App,
mocker: MockerFixture,
mocked_api: MockRouter,
tmp_path: Path,
mock_installation,
mock_installation_token,
) -> None:
"""测试机器人的发布流程,存在历史检查评论

测试 handle_pull_request_and_update_issue 中历史工作流的代码路径(lines 165-166)
"""
from src.plugins.github import plugin_config
from src.plugins.github.constants import NONEFLOW_MARKER

mock_subprocess_run = mock_subprocess_run_with_side_effect(mocker)

mock_issue = MockIssue(body=MockBody(type="bot", name="test").generate()).as_mock(
mocker
)

mock_issues_resp = mocker.MagicMock()
mock_issues_resp.parsed_data = mock_issue

# 创建包含 NONEFLOW_MARKER 和历史工作流数据的自评论
mock_self_comment = mocker.MagicMock()
mock_self_comment.id = 123
mock_self_comment.body = f"""\
# 📃 商店发布检查结果

> Bot: test

**✅ 所有测试通过,一切准备就绪!**

<details>
<summary>历史测试</summary>
<pre><code><li>✅ <a href="https://github.com/owner/repo/actions/runs/99999">2023-08-22 09:22:14 CST</a></li></code></pre>
</details>

---

💡 如需修改信息,请直接修改 issue,机器人会自动更新检查结果。

💪 Powered by [NoneFlow](https://github.com/nonebot/noneflow)
{NONEFLOW_MARKER}
"""

mock_list_comments_resp = mocker.MagicMock()
mock_list_comments_resp.parsed_data = [mock_self_comment]

mock_pull = mocker.MagicMock()
mock_pull.number = 2
mock_pulls_resp = mocker.MagicMock()
mock_pulls_resp.parsed_data = mock_pull

with open(tmp_path / "bots.json5", "w") as f:
json.dump([], f)

async with app.test_matcher() as ctx:
_adapter, bot = get_github_bot(ctx)
event = get_mock_event(IssuesOpened)

ctx.should_call_api(
"rest.apps.async_get_repo_installation",
{"owner": "he0119", "repo": "action-test"},
mock_installation,
)
ctx.should_call_api(
"rest.apps.async_create_installation_access_token",
{"installation_id": mock_installation.parsed_data.id},
mock_installation_token,
)
ctx.should_call_api(
"rest.issues.async_get",
{"owner": "he0119", "repo": "action-test", "issue_number": 80},
mock_issues_resp,
)
ctx.should_call_api(
"rest.issues.async_list_comments",
{"owner": "he0119", "repo": "action-test", "issue_number": 80},
mock_list_comments_resp,
)
ctx.should_call_api(
"rest.issues.async_update_comment",
{
"owner": "he0119",
"repo": "action-test",
"comment_id": 123,
"body": mocker.ANY,
},
True,
)
ctx.should_call_api(
"rest.pulls.async_create",
{
"owner": "he0119",
"repo": "action-test",
"title": "Bot: test",
"body": "resolve #80",
"base": "master",
"head": "publish/issue80",
},
mock_pulls_resp,
)
ctx.should_call_api(
"rest.issues.async_add_labels",
{
"owner": "he0119",
"repo": "action-test",
"issue_number": 2,
"labels": ["Publish", "Bot"],
},
True,
)

ctx.receive_event(bot, event)

assert mocked_api["homepage"].called
Loading