diff --git a/app/chain/_transfer.py b/app/chain/_transfer.py index fe41c91f2..6cdcaad61 100644 --- a/app/chain/_transfer.py +++ b/app/chain/_transfer.py @@ -23,8 +23,8 @@ from app.chain.media import MediaChain from app.chain.storage import StorageChain from app.chain.subscribe import SubscribeChain from app.application.chain.data import ( - DownloadHistoryPortProxy as DownloadHistoryOper, - TransferHistoryPortProxy as TransferHistoryOper, + get_chain_download_history_port, + get_chain_transfer_history_port, ) from app.application.configuration import ( get_chain_runtime_config_snapshot, @@ -417,7 +417,7 @@ class FileFilterMixin: @staticmethod def _is_overwrite_declined(task: TransferTask, transferinfo: TransferInfo, - transferhis: TransferHistoryOper) -> bool: + transferhis: Any) -> bool: """ 判断本次未入库是否为「同路径已有成功记录 + 覆盖模式裁定不覆盖」。 @@ -859,7 +859,7 @@ class HistoryMatchMixin: def _resolve_history_from_download_files( self, - downloadhis: DownloadHistoryOper, + downloadhis: Any, download_files: List[DownloadFiles], file_path: Optional[Path] = None, save_path: Optional[Path] = None, @@ -889,7 +889,7 @@ class HistoryMatchMixin: def _resolve_download_history( self, - downloadhis: DownloadHistoryOper, + downloadhis: Any, file_path: Path, bluray_dir: bool = False, download_hash: Optional[str] = None, @@ -1148,7 +1148,7 @@ class ManualHistoryMixin: def _get_manual_transfer_history( self, fileitem: FileItem, - transfer_history_oper: TransferHistoryOper, + transfer_history_oper: Any, include_move_dest: bool = False, ) -> Optional[TransferHistory]: """查询文件源路径历史,并兼容从成功移动后的目标现址重新整理。""" @@ -1178,7 +1178,7 @@ class ManualHistoryMixin: :param fileitems: 待查询的文件或目录项 :return: 去重后的成功整理记录 """ - transfer_history_oper = TransferHistoryOper() + transfer_history_oper = get_chain_transfer_history_port() histories: Dict[int, TransferHistory] = {} for fileitem in fileitems or []: if not fileitem or not fileitem.path: @@ -1212,7 +1212,7 @@ class ManualHistoryMixin: @staticmethod def _delete_manual_transfer_history( history: TransferHistory, - transfer_history_oper: TransferHistoryOper, + transfer_history_oper: Any, ) -> Tuple[bool, str]: """删除手动重整历史;非成功移动记录同时清理可能存在的旧目标。""" if ( @@ -1385,7 +1385,7 @@ class FailedRetryMixin: ) return - history = TransferHistoryOper().get(history_id) + history = get_chain_transfer_history_port().get(history_id) if not history: self.post_message( Message( @@ -1478,7 +1478,7 @@ class FailedRetryMixin: :param media_id: 数据源原生 ID,必须与 media_source 成对提供 """ # 查询历史记录 - history: TransferHistory = TransferHistoryOper().get(logid) + history: TransferHistory = get_chain_transfer_history_port().get(logid) if not history: logger.error(f"整理记录不存在,ID:{logid}") return False, "整理记录不存在" diff --git a/app/chain/transfer.py b/app/chain/transfer.py index 9aa6f837c..0132d514f 100755 --- a/app/chain/transfer.py +++ b/app/chain/transfer.py @@ -23,9 +23,9 @@ from app.domain.meta.metabase import MetaBase from app.domain.meta.metamusic import MetaMusic from app.domain.metainfo import MetaInfoPath from app.application.chain.data import ( - DownloadHistoryPortProxy as DownloadHistoryOper, - TransferPendingPortProxy as TransferPendingOper, - TransferHistoryPortProxy as TransferHistoryOper, + get_chain_download_history_port, + get_chain_transfer_history_port, + get_chain_transfer_pending_port, ) DownloadHistory = Any from app.application.configuration import get_configured_system_config @@ -142,7 +142,7 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo # 整理失败通知聚合器 self.failure_notification_aggregator = TransferFailureNotificationAggregator() # 待整理文件落盘登记,用于进程重启后回放内存队列里未完成的任务 - self._pendingoper = TransferPendingOper() + self._pendingoper = get_chain_transfer_pending_port() # 转移成功的文件清单 self._success_target_files: Dict[Tuple, List[str]] = {} # 批次级刮削缓冲,避免同一批多文件入库重复触发目录刮削 @@ -419,7 +419,7 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo username=task.username, ) - transferhis = TransferHistoryOper() + transferhis = get_chain_transfer_history_port() target_dir_path = self.__get_transfer_target_dir_path(transferinfo) job_id = self.jobview.get_job_id(task) @@ -1265,7 +1265,7 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo """ try: # 识别 - transferhis = TransferHistoryOper() + transferhis = get_chain_transfer_history_port() # 显式标注联合:下面既会赋回音乐识别结果(MusicInfo),也会赋回影视识别 # 结果(MediaInfo),不标注时会被推断成其中一种,另一种就成了假错误 mediainfo: Optional[Union[MediaInfo, MusicInfo]] = task.mediainfo @@ -1647,7 +1647,7 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo continue # 查询下载记录识别情况 - downloadhis: DownloadHistory = DownloadHistoryOper().get_by_hash( + downloadhis: DownloadHistory = get_chain_download_history_port().get_by_hash( torrent.hash ) # 下载记录中的媒体类型作为整理类型来源,无下载记录时留空由文件后缀兜底 @@ -2265,7 +2265,7 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo def _build_main_meta( main_fileitem: FileItem, main_bluray_dir: bool, - download_history_oper: DownloadHistoryOper, + download_history_oper: Any, ) -> Optional[MetaBase]: """ 构建主视频元数据。 @@ -2357,7 +2357,7 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo if not items: return [], {} - download_history_oper = DownloadHistoryOper() + download_history_oper = get_chain_download_history_port() inherited_map: Dict[Tuple[str, str], MetaBase] = {} main_items_by_dir, extra_items_by_dir = _build_directory_index(items) main_items = [ @@ -2557,7 +2557,7 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo # 成功但源文件已变化放行交 overwrite_mode 决断);手动整理可清理失败记录, # 或按用户确认清理成功记录。 if (not force or reorganize) and not preview: - transfer_history_oper = TransferHistoryOper() + transfer_history_oper = get_chain_transfer_history_port() transferd = self._get_manual_transfer_history( fileitem=file_item, transfer_history_oper=transfer_history_oper, @@ -2627,7 +2627,7 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo continue # 提前获取下载历史,以便获取自定义识别词 - downloadhis = DownloadHistoryOper() + downloadhis = get_chain_download_history_port() download_history = self._resolve_download_history( downloadhis=downloadhis, file_path=file_path, diff --git a/docs/refactor/backend-architecture-next-stage.md b/docs/refactor/backend-architecture-next-stage.md index 3f5acfb75..e76bfb72c 100644 --- a/docs/refactor/backend-architecture-next-stage.md +++ b/docs/refactor/backend-architecture-next-stage.md @@ -6,7 +6,7 @@ > 审计范围:宿主后端;排除 `app/plugins/**` 运行时插件副本 > 规范优先级:`AGENTS.md` 与 `docs/rules/` 高于本文 > 相关文档:`docs/architecture-overview.md`、`docs/refactor/backend-architecture-governance.md`、`docs/refactor/backend-module-refactor-compatibility.md` -> 实施进度:阶段 0~6 的宿主架构能力已完成收口;API/Application 公共复杂度基线已清零,启动组合根的 SystemConfigOper 构造点已由 14 降至 1;API 进程内后台任务已完成首批统一登记,插件仓适配和 Outbox 外围扩展仍按风险切片推进。Model/Base 查询与写装饰器、legacy 隐式会话外壳均已清零,插件 SDK 也不再导出宿主 Model。2026-08-23 的长期整改阶段 0 已恢复宿主、启动性能、官方插件和 SDK 契约门禁的可信基线;阶段 1a 已补齐 TaskRegistry owner 零债务门禁和诚实的关停超时语义;阶段 1b1 已收口整理 worker、pending 回放、失败通知、进程内 AI 重试、插件监控与事件投递的生命周期所有权;2026-08-24 的阶段 2 已将 212 个已观察宿主模块方法的 legacy aggregation 清零,并补齐可执行 fanout 与下载器文件 DTO 边界;阶段 3 已将消息交互和远程命令的订阅删除统一到 Application/UoW/outbox,宿主不再调用裸线程统计入口;阶段 4 已统一七种消息渠道的宿主回环与后台执行边界;阶段 5 已补齐事件窗口聚合任务的生命周期所有权;阶段 6 已统一插件文件操作的取消完成语义;阶段 7 已统一插件协程补偿的终态等待;阶段 8 已统一宿主同步函数的异步线程池入口;阶段 9 已统一工作流运行时的宿主获取路径;阶段 10 已统一模块、插件与调度运行时的显式 getter 调用;阶段 11 已清除系统配置 getter 的 Oper 形别名;阶段 12 已完成工作流域的显式 Chain 数据端口迁移;阶段 13 已收口用户、交互与消息链的数据端口;阶段 14 已收口音乐订阅数据端口;阶段 15 已收口站点数据端口;阶段 16 已收口媒体服务器数据端口;阶段 17 已收口下载数据端口;阶段 18 已收口主订阅数据端口。 +> 实施进度:阶段 0~6 的宿主架构能力已完成收口;API/Application 公共复杂度基线已清零,启动组合根的 SystemConfigOper 构造点已由 14 降至 1;API 进程内后台任务已完成首批统一登记,插件仓适配和 Outbox 外围扩展仍按风险切片推进。Model/Base 查询与写装饰器、legacy 隐式会话外壳均已清零,插件 SDK 也不再导出宿主 Model。2026-08-23 的长期整改阶段 0 已恢复宿主、启动性能、官方插件和 SDK 契约门禁的可信基线;阶段 1a 已补齐 TaskRegistry owner 零债务门禁和诚实的关停超时语义;阶段 1b1 已收口整理 worker、pending 回放、失败通知、进程内 AI 重试、插件监控与事件投递的生命周期所有权;2026-08-24 的阶段 2 已将 212 个已观察宿主模块方法的 legacy aggregation 清零,并补齐可执行 fanout 与下载器文件 DTO 边界;阶段 3 已将消息交互和远程命令的订阅删除统一到 Application/UoW/outbox,宿主不再调用裸线程统计入口;阶段 4 已统一七种消息渠道的宿主回环与后台执行边界;阶段 5 已补齐事件窗口聚合任务的生命周期所有权;阶段 6 已统一插件文件操作的取消完成语义;阶段 7 已统一插件协程补偿的终态等待;阶段 8 已统一宿主同步函数的异步线程池入口;阶段 9 已统一工作流运行时的宿主获取路径;阶段 10 已统一模块、插件与调度运行时的显式 getter 调用;阶段 11 已清除系统配置 getter 的 Oper 形别名;阶段 12 已完成工作流域的显式 Chain 数据端口迁移;阶段 13 已收口用户、交互与消息链的数据端口;阶段 14 已收口音乐订阅数据端口;阶段 15 已收口站点数据端口;阶段 16 已收口媒体服务器数据端口;阶段 17 已收口下载数据端口;阶段 18 已收口主订阅数据端口;阶段 19 已收口整理数据端口。 ## 当前复核结论(2026-08-24) @@ -203,6 +203,15 @@ - 兼容边界不变:三个 DB Oper、Proxy 类、订阅 Chain 公开方法、订阅字段和事件合同、V2/V3 插件调用 方式均未修改。 +### 长期整改阶段 19:整理 Chain 数据端口收口(2026-08-24) + +- `TransferChain` 与 `_transfer` mixin 原先把 pending、下载历史和整理历史三个 `*PortProxy` 别名为 + 数据库 Oper;现在 worker 回放、查重、历史解析、手动重整和失败重试统一调用对应 `get_chain_*_port()`。 +- 整理主流程、同步附属文件、音乐整理和手动历史测试改为替换命名 getter;架构门禁同时覆盖主链和 + mixin,禁止重新引入三个 Proxy。 +- 兼容边界不变:三个 DB Oper、Proxy 类、整理公开入口、队列和生命周期、历史字段、事件 payload 以及 + V2/V3 插件调用方式均未修改。 + ### 总体判断 当前架构总体合理,已经从跨层混合的遗留单体收敛为**边界清晰的模块化单体**: diff --git a/docs/rules/05-architecture.md b/docs/rules/05-architecture.md index cb77c09d4..34654e36c 100644 --- a/docs/rules/05-architecture.md +++ b/docs/rules/05-architecture.md @@ -131,7 +131,7 @@ Session. `app/db/adapters/` is the concrete persistence-adapter layer: it may depend on Application-owned Protocols, UoW/Session and Oper implementations. This deliberate dependency inversion is the only `DB implementation -> Application contract` direction; Application must remain free of DB imports. -Migrated workflow, user, interaction, messaging, music, site, media-server, download and subscribe +Migrated workflow, user, interaction, messaging, music, site, media-server, download, subscribe and transfer Chain consumers use the named `get_chain_*_port()` functions from `app/application/chain/data.py`; they must not alias migration-time `*PortProxy` classes back to database Oper names. Those proxy classes remain compatibility diff --git a/tests/test_architecture_dependencies.py b/tests/test_architecture_dependencies.py index d16369c11..6ed361531 100644 --- a/tests/test_architecture_dependencies.py +++ b/tests/test_architecture_dependencies.py @@ -543,6 +543,31 @@ def test_subscribe_chain_uses_explicit_data_port_getters(): assert violations == [] +def test_transfer_chains_use_explicit_data_port_getters(): + """整理主链与 mixin 不得把迁移期 PortProxy 伪装成数据库 Oper。""" + paths = [APP_ROOT / "chain" / "transfer.py", APP_ROOT / "chain" / "_transfer.py"] + forbidden = { + "DownloadHistoryPortProxy", + "TransferHistoryPortProxy", + "TransferPendingPortProxy", + } + violations: list[str] = [] + for path in paths: + tree = ast.parse(path.read_text(encoding="utf-8-sig"), filename=str(path)) + for node in ast.walk(tree): + if not isinstance(node, ast.ImportFrom): + continue + if node.module != "app.application.chain.data": + continue + for alias in node.names: + if alias.name in forbidden: + violations.append( + f"{path.relative_to(PROJECT_ROOT).as_posix()}:{node.lineno}:{alias.name}" + ) + + assert violations == [] + + def test_plugin_components_do_not_reexport_legacy_abi_names(): """新插件组件只提供 canonical 能力,不得复制旧 Helper、Manager 或 Oper 导出。""" violations: list[str] = [] diff --git a/tests/test_manual_transfer_history.py b/tests/test_manual_transfer_history.py index 9e5d90c27..a97986c8b 100644 --- a/tests/test_manual_transfer_history.py +++ b/tests/test_manual_transfer_history.py @@ -59,12 +59,12 @@ def _patch_transfer_planning(monkeypatch, chain, fileitem, history, planned, del delete=lambda history_id: deleted.append(("history", history_id)), ) monkeypatch.setattr( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", lambda: history_oper, ) - monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: history_oper) + monkeypatch.setattr("app.chain._transfer.get_chain_transfer_history_port", lambda: history_oper) monkeypatch.setattr( - "app.chain.transfer.DownloadHistoryOper", + "app.chain.transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, @@ -72,7 +72,7 @@ def _patch_transfer_planning(monkeypatch, chain, fileitem, history, planned, del get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], diff --git a/tests/test_music_transfer.py b/tests/test_music_transfer.py index d0825c375..521372351 100644 --- a/tests/test_music_transfer.py +++ b/tests/test_music_transfer.py @@ -535,10 +535,10 @@ def test_success_file_aggregation_is_isolated_between_music_jobs_in_same_directo ) monkeypatch.setattr( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(), ) - monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace()) + monkeypatch.setattr("app.chain._transfer.get_chain_transfer_history_port", lambda: SimpleNamespace()) monkeypatch.setattr( "app.chain.transfer.add_transfer_success", lambda **kwargs: SimpleNamespace(id=1), @@ -767,8 +767,8 @@ def test_downloader_process_forwards_music_history_type(tmp_path, monkeypatch): ], ) monkeypatch.setattr( - "app.chain.transfer.DownloadHistoryOper.get_by_hash", - lambda _, download_hash: history, + "app.chain.transfer.get_chain_download_history_port", + lambda: SimpleNamespace(get_by_hash=lambda download_hash: history), ) monkeypatch.setattr( chain, diff --git a/tests/test_transfer_failed_retry_buttons.py b/tests/test_transfer_failed_retry_buttons.py index 41a504421..139bf813d 100644 --- a/tests/test_transfer_failed_retry_buttons.py +++ b/tests/test_transfer_failed_retry_buttons.py @@ -137,7 +137,7 @@ class TestTransferFailedRetryButtons(unittest.TestCase): settings, "AI_AGENT_ENABLE", True ): with patch( - "app.chain._transfer.TransferHistoryOper" + "app.chain._transfer.get_chain_transfer_history_port" ) as history_oper_cls, patch( "app.chain._transfer.build_manual_redo_prompt", return_value="retry transfer prompt", @@ -217,7 +217,7 @@ class TestTransferFailedRetryButtons(unittest.TestCase): settings, "AI_AGENT_ENABLE", True ): with patch( - "app.chain._transfer.TransferHistoryOper" + "app.chain._transfer.get_chain_transfer_history_port" ) as history_oper_cls, patch( "app.chain._transfer.build_manual_redo_prompt", side_effect=build_manual_redo_prompt, diff --git a/tests/test_transfer_job_manager.py b/tests/test_transfer_job_manager.py index 390ec1a8d..2c99a7741 100644 --- a/tests/test_transfer_job_manager.py +++ b/tests/test_transfer_job_manager.py @@ -426,7 +426,7 @@ class TransferJobManagerTest(unittest.TestCase): ) with patch( - "app.chain.transfer.TransferHistoryOper", return_value=SimpleNamespace() + "app.chain.transfer.get_chain_transfer_history_port", return_value=SimpleNamespace() ), patch( "app.chain.transfer.add_transfer_success", lambda **kwargs: SimpleNamespace(id=1), @@ -472,8 +472,8 @@ class TransferJobManagerTest(unittest.TestCase): ) system_config_oper = SimpleNamespace(get=lambda key: None) - with patch("app.chain.transfer.TransferHistoryOper", return_value=transfer_history_oper), \ - patch("app.chain.transfer.DownloadHistoryOper", return_value=download_history_oper), \ + with patch("app.chain.transfer.get_chain_transfer_history_port", return_value=transfer_history_oper), \ + patch("app.chain.transfer.get_chain_download_history_port", return_value=download_history_oper), \ patch("app.chain.transfer.get_configured_system_config", return_value=system_config_oper), \ patch("app.chain.transfer.MetaInfoPath", lambda *args, **kwargs: FakeMeta(14)): state, errmsg = chain.do_transfer( @@ -647,7 +647,7 @@ class TransferJobManagerTest(unittest.TestCase): system_config_oper = SimpleNamespace(get=lambda key: None) with patch( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", return_value=transfer_history_oper, ), patch( "app.chain.transfer.get_configured_system_config", @@ -715,10 +715,10 @@ class TransferJobManagerTest(unittest.TestCase): _reset_failed_retries(fileitem.path, fileitem.storage) try: with patch( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", return_value=transfer_history_oper, ), patch( - "app.chain.transfer.DownloadHistoryOper", + "app.chain.transfer.get_chain_download_history_port", return_value=download_history_oper, ), patch( "app.chain.transfer.get_configured_system_config", @@ -794,10 +794,10 @@ class TransferJobManagerTest(unittest.TestCase): ): record_transfer_failure(fileitem.path, fileitem.storage) with patch( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", return_value=transfer_history_oper, ), patch( - "app.chain.transfer.DownloadHistoryOper", + "app.chain.transfer.get_chain_download_history_port", return_value=download_history_oper, ), patch( "app.chain.transfer.get_configured_system_config", @@ -850,7 +850,7 @@ class TransferJobManagerTest(unittest.TestCase): ) failed_history_oper = SimpleNamespace() with patch( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", return_value=failed_history_oper, ), patch( "app.chain.transfer.add_transfer_fail", @@ -890,7 +890,7 @@ class TransferJobManagerTest(unittest.TestCase): need_notify=False, ) with patch( - "app.chain.transfer.TransferHistoryOper", return_value=SimpleNamespace() + "app.chain.transfer.get_chain_transfer_history_port", return_value=SimpleNamespace() ), patch( "app.chain.transfer.add_transfer_success", lambda **kwargs: SimpleNamespace(id=2), @@ -920,7 +920,7 @@ class TransferJobManagerTest(unittest.TestCase): transfer_history_oper = SimpleNamespace() with patch( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", return_value=transfer_history_oper, ), patch( "app.chain.transfer.add_transfer_fail", @@ -962,7 +962,7 @@ class TransferJobManagerTest(unittest.TestCase): self.assertTrue(chain.jobview.add_task(task)) with patch( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", return_value=SimpleNamespace(), ), patch( "app.chain.transfer.add_transfer_fail", @@ -1005,7 +1005,7 @@ class TransferJobManagerTest(unittest.TestCase): self.assertTrue(chain.jobview.add_task(task)) with patch( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", return_value=SimpleNamespace(), ), patch( "app.chain.transfer.add_transfer_fail", @@ -1081,10 +1081,10 @@ class TransferJobManagerTest(unittest.TestCase): ) with patch( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", return_value=transfer_history_oper, ), patch( - "app.chain.transfer.DownloadHistoryOper", + "app.chain.transfer.get_chain_download_history_port", return_value=download_history_oper, ), patch( "app.chain.transfer.get_configured_system_config", @@ -1180,10 +1180,10 @@ class TransferJobManagerTest(unittest.TestCase): storage_chain = SimpleNamespace(get_item=lambda fileitem: subtitle_fileitem) with patch( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", return_value=transfer_history_oper, ), patch( - "app.chain.transfer.DownloadHistoryOper", + "app.chain.transfer.get_chain_download_history_port", return_value=download_history_oper, ), patch( "app.chain.transfer.get_configured_system_config", @@ -1261,10 +1261,10 @@ class TransferJobManagerTest(unittest.TestCase): ) with patch( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", return_value=transfer_history_oper, ), patch( - "app.chain.transfer.DownloadHistoryOper", + "app.chain.transfer.get_chain_download_history_port", return_value=download_history_oper, ), patch( "app.chain.transfer.get_configured_system_config", @@ -1366,10 +1366,10 @@ class TransferJobManagerTest(unittest.TestCase): ) with patch( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", return_value=transfer_history_oper, ), patch( - "app.chain.transfer.DownloadHistoryOper", + "app.chain.transfer.get_chain_download_history_port", return_value=download_history_oper, ), patch( "app.chain.transfer.get_configured_system_config", @@ -1464,7 +1464,7 @@ class TransferJobManagerTest(unittest.TestCase): ] with patch( - "app.chain.transfer.TransferHistoryOper", return_value=SimpleNamespace() + "app.chain.transfer.get_chain_transfer_history_port", return_value=SimpleNamespace() ), patch( "app.chain.transfer.add_transfer_success", lambda **kwargs: SimpleNamespace(id=1), @@ -1530,7 +1530,7 @@ class TransferJobManagerTest(unittest.TestCase): ) with patch( - "app.chain.transfer.TransferHistoryOper", return_value=SimpleNamespace() + "app.chain.transfer.get_chain_transfer_history_port", return_value=SimpleNamespace() ), patch( "app.chain.transfer.add_transfer_success", lambda **kwargs: SimpleNamespace(id=1), diff --git a/tests/test_transfer_movie_collection.py b/tests/test_transfer_movie_collection.py index f4adcaf16..523cccfa7 100644 --- a/tests/test_transfer_movie_collection.py +++ b/tests/test_transfer_movie_collection.py @@ -104,10 +104,10 @@ def test_conflicting_download_history_recognizes_movie_by_file_meta(monkeypatch) try_remove_job=lambda task: None, ) monkeypatch.setattr( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_type_tmdbid=lambda **kwargs: None), ) - monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_type_tmdbid=lambda **kwargs: None)) + monkeypatch.setattr("app.chain._transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_type_tmdbid=lambda **kwargs: None)) monkeypatch.setattr( "app.chain.transfer.MediaChain", lambda: SimpleNamespace( @@ -187,12 +187,12 @@ def test_movie_collection_conflict_only_drops_automatic_media( chain._TransferChain__handle_transfer = fake_handle_transfer monkeypatch.setattr( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) - monkeypatch.setattr("app.chain.transfer.DownloadHistoryOper", lambda: history_oper) - monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: history_oper) + monkeypatch.setattr("app.chain._transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain.transfer.get_chain_download_history_port", lambda: history_oper) + monkeypatch.setattr("app.chain._transfer.get_chain_download_history_port", lambda: history_oper) monkeypatch.setattr( "app.chain.transfer.get_configured_system_config", lambda: SimpleNamespace(get=lambda key: None), diff --git a/tests/test_transfer_overwrite_declined.py b/tests/test_transfer_overwrite_declined.py index f3114fd2b..553e6c5c2 100644 --- a/tests/test_transfer_overwrite_declined.py +++ b/tests/test_transfer_overwrite_declined.py @@ -153,7 +153,7 @@ def test_default_callback_skips_history_and_notification_when_overwrite_declined ) with patch( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", return_value=transfer_history_oper, ), patch( "app.chain.transfer.add_transfer_fail", @@ -199,7 +199,7 @@ def test_default_callback_keeps_original_failure_semantics_without_success_histo ) with patch( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", return_value=transfer_history_oper, ), patch( "app.chain.transfer.add_transfer_fail", @@ -251,7 +251,7 @@ def test_default_callback_delegates_primary_failure_to_durable_writer(): chain.durable_event_writer.transfer_result.side_effect = durable_transfer_result with patch( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", return_value=transfer_history_oper, ), patch( "app.chain.transfer.add_transfer_fail", diff --git a/tests/test_transfer_sync_extra_files.py b/tests/test_transfer_sync_extra_files.py index 5c48f58da..612733c57 100644 --- a/tests/test_transfer_sync_extra_files.py +++ b/tests/test_transfer_sync_extra_files.py @@ -139,12 +139,12 @@ def test_sync_extra_subtitle_inherits_matching_video_episode(monkeypatch): monkeypatch.setattr(chain, "_TransferChain__handle_transfer", fake_handle_transfer) monkeypatch.setattr( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr( - "app.chain.transfer.DownloadHistoryOper", + "app.chain.transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, @@ -152,7 +152,7 @@ def test_sync_extra_subtitle_inherits_matching_video_episode(monkeypatch): get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], @@ -236,12 +236,12 @@ def test_single_subtitle_transfer_reuses_same_name_video_episode(monkeypatch): monkeypatch.setattr(chain, "_TransferChain__handle_transfer", fake_handle_transfer) monkeypatch.setattr( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr( - "app.chain.transfer.DownloadHistoryOper", + "app.chain.transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, @@ -249,7 +249,7 @@ def test_single_subtitle_transfer_reuses_same_name_video_episode(monkeypatch): get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], @@ -351,12 +351,12 @@ def test_single_video_transfer_lists_parent_once_for_same_name_extra(monkeypatch monkeypatch.setattr(chain, "_TransferChain__handle_transfer", fake_handle_transfer) monkeypatch.setattr( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr( - "app.chain.transfer.DownloadHistoryOper", + "app.chain.transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, @@ -364,7 +364,7 @@ def test_single_video_transfer_lists_parent_once_for_same_name_extra(monkeypatch get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], @@ -449,12 +449,12 @@ def test_episode_format_filters_extra_files_before_sync_planning(monkeypatch): monkeypatch.setattr(chain, "_TransferChain__handle_transfer", fake_handle_transfer) monkeypatch.setattr( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr( - "app.chain.transfer.DownloadHistoryOper", + "app.chain.transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, @@ -462,7 +462,7 @@ def test_episode_format_filters_extra_files_before_sync_planning(monkeypatch): get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], @@ -536,12 +536,12 @@ def test_episode_format_keeps_matching_extra_files_following_main(monkeypatch): monkeypatch.setattr(chain, "_TransferChain__handle_transfer", fake_handle_transfer) monkeypatch.setattr( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr( - "app.chain.transfer.DownloadHistoryOper", + "app.chain.transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, @@ -549,7 +549,7 @@ def test_episode_format_keeps_matching_extra_files_following_main(monkeypatch): get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], @@ -632,12 +632,12 @@ def test_single_matching_subtitle_uses_unmatched_video_only_as_context(monkeypat monkeypatch.setattr(chain, "_TransferChain__handle_transfer", fake_handle_transfer) monkeypatch.setattr( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr( - "app.chain.transfer.DownloadHistoryOper", + "app.chain.transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, @@ -645,7 +645,7 @@ def test_single_matching_subtitle_uses_unmatched_video_only_as_context(monkeypat get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], @@ -728,12 +728,12 @@ def test_cleanup_dest_fileitem_is_deleted_only_after_allowed_items_exist(monkeyp monkeypatch.setattr(chain, "_TransferChain__handle_transfer", fake_handle_transfer) monkeypatch.setattr( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None), ) - monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) + monkeypatch.setattr("app.chain._transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(get_by_src=lambda src, storage=None: None)) monkeypatch.setattr( - "app.chain.transfer.DownloadHistoryOper", + "app.chain.transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, @@ -741,7 +741,7 @@ def test_cleanup_dest_fileitem_is_deleted_only_after_allowed_items_exist(monkeyp get_by_path=lambda path: None, ), ) - monkeypatch.setattr("app.chain._transfer.DownloadHistoryOper", lambda: SimpleNamespace( + monkeypatch.setattr("app.chain._transfer.get_chain_download_history_port", lambda: SimpleNamespace( get_by_hash=lambda download_hash: None, get_file_by_fullpath=lambda fullpath: None, get_files_by_savepath=lambda savepath: [], diff --git a/tests/test_transfer_tmdb_category.py b/tests/test_transfer_tmdb_category.py index 6a7818951..1d74228f0 100644 --- a/tests/test_transfer_tmdb_category.py +++ b/tests/test_transfer_tmdb_category.py @@ -76,10 +76,10 @@ def test_transfer_stops_when_automatic_category_has_no_tmdb_result(monkeypatch) chain = object.__new__(TransferChain) chain.jobview = SimpleNamespace(try_remove_job=lambda _task: None) monkeypatch.setattr( - "app.chain.transfer.TransferHistoryOper", + "app.chain.transfer.get_chain_transfer_history_port", lambda: SimpleNamespace(), ) - monkeypatch.setattr("app.chain._transfer.TransferHistoryOper", lambda: SimpleNamespace()) + monkeypatch.setattr("app.chain._transfer.get_chain_transfer_history_port", lambda: SimpleNamespace()) monkeypatch.setattr( "app.chain.transfer.MediaChain", lambda: SimpleNamespace(