From 55e83630de983905a1a628330093fda00f18e6e0 Mon Sep 17 00:00:00 2001 From: Ji Mao Cai <44921326+jinghero@users.noreply.github.com> Date: Fri, 13 Nov 2026 11:19:03 +0800 Subject: [PATCH] =?UTF-8?q?fix(maafw):=20=E4=BF=AE=E5=A4=8D=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E6=94=B6=E5=B0=BE=E6=8A=A5=E9=94=99=E5=92=8C=E5=8E=86?= =?UTF-8?q?=E5=8F=B2=E8=AE=B0=E5=BD=95=E6=9C=AA=E4=BF=9D=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/task/MaaFW/embedded_manager.py | 2 + .../fix-maafw-history-finalization.fix.md | 2 + tests/task/test_maafw_stop_reservation.py | 75 ++++++++++++++++++- 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 changelog.d/fix-maafw-history-finalization.fix.md diff --git a/app/task/MaaFW/embedded_manager.py b/app/task/MaaFW/embedded_manager.py index 4d002946dd..e65449cd3d 100644 --- a/app/task/MaaFW/embedded_manager.py +++ b/app/task/MaaFW/embedded_manager.py @@ -698,6 +698,8 @@ def _build_inner_task(self, route: Any) -> "MaaFWPluginAutoProxyTask": project_update_logs=self._take_project_update_logs(), signal_tracker=self.signal_tracker, ) + task.history = self.history + task.callbacks = self.callbacks task.maafw_runtime_pool_root = route.root task.maafw_runtime_pool_id = route.pool_id return task diff --git a/changelog.d/fix-maafw-history-finalization.fix.md b/changelog.d/fix-maafw-history-finalization.fix.md new file mode 100644 index 0000000000..64a719cf58 --- /dev/null +++ b/changelog.d/fix-maafw-history-finalization.fix.md @@ -0,0 +1,2 @@ +project: mfw +修复任务结束时报错、账号收尾中断及历史记录未保存的问题 diff --git a/tests/task/test_maafw_stop_reservation.py b/tests/task/test_maafw_stop_reservation.py index 01bf26f83a..07a6c5daad 100644 --- a/tests/task/test_maafw_stop_reservation.py +++ b/tests/task/test_maafw_stop_reservation.py @@ -1,9 +1,12 @@ -"""停止已运行的 MFW 账号后,同一项目仍可再次运行。""" +"""MFW 账号收尾保留历史,停止后同一项目仍可再次运行。""" import asyncio import sys import threading import uuid +from pathlib import Path +from types import SimpleNamespace +from unittest.mock import AsyncMock import pytest @@ -19,6 +22,76 @@ from tests.task_stop_support import StopTask, add_script +@pytest.mark.asyncio +async def test_manager_built_account_finishes_and_saves_history( + isolated_app_paths, monkeypatch, tmp_path +): + from app.core.history import HistoryStore + from app.models.task import TaskItem + from app.task import history_core + from app.task.MaaFW import embedded_manager + from app.task.MaaFW.tools.embedded import runner_task + from app.utils import UTC4 + + config = await activate_config(MaaFWConfig()) + user_id = config.UserData.add( + MaaFWUserConfig, payload={"Notify": {"Enabled": False}} + ) + await config.UserData.commit() + info = TaskItem() + script = await add_script( + info=info, script_id=str(config.uid), user_id=str(user_id) + ) + manager = embedded_manager.MaaFWEmbeddedManager(script) + manager.script_config = config + manager.user_config = config.UserData + manager.history = history_core.TaskHistorySession(info) + storage = HistoryStore() + await storage.initialize(video_limit_bytes=0) + monkeypatch.setattr(history_core, "History", storage) + monkeypatch.setattr(runner_task.Notify, "send_notice", AsyncMock()) + monkeypatch.setattr(runner_task.Notify, "push_statistics_report", AsyncMock()) + account = manager._build_inner_task( + SimpleNamespace(root=tmp_path / "runtime-pool", pool_id="test") + ) + manager.inner_task = account + manager._inner_finalized = False + temporary_history = Path(account._history_temp.name) + try: + # 只隔离游戏执行,使用真实的账号装配、日志、收尾与历史存储。 + await account.prepare() + account.check_result = "Pass" + account.run_complete = True + account._history_statuses[id(account.cur_user_log)] = "success" + account._append_log("completed account log") + await manager.callbacks.drain() + assert "completed account log" in "".join(account.cur_user_log.content) + await manager._finalize_inner_task() + await manager._finalize_inner_task() + assert script.user_list[0].status == "完成" + assert config.UserData[user_id].Data.ProxyTimes == 1 + assert not temporary_history.exists() + day = account.cur_user_log_started_at.astimezone(UTC4).date() + groups = await storage.search(start_date=day, end_date=day, mode="DAILY") + records = [ + record + for users in groups.values() + for bucket in users.values() + for record in bucket.index + ] + assert len(records) == 1 + assert records[0].status == "success" + detail = await storage.get_detail(record_id=records[0].id) + assert "completed account log" in detail.log_content + finally: + await account.callbacks.close() + await manager.callbacks.close() + await manager.history.close() + await storage.close() + await account._release_project_path() + account._history_temp.cleanup() + + @pytest.mark.asyncio async def test_stopped_account_releases_project_after_emulator_close_is_cancelled( isolated_app_paths,