ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

cli-anything-adguardhome 测试体系深度解析:从单元测试到 Docker E2E 的完整验证方案

cli-anything-adguardhome 测试体系深度解析:从单元测试到 Docker E2E 的完整验证方案 cli-anything-adguardhome 测试体系深度解析从单元测试到 Docker E2E 的完整验证方案【免费下载链接】CLI-AnythingCLI-Anything: Making ALL Software Agent-Native -- CLI-Hub: https://clianything.cc/项目地址: https://gitcode.com/GitHub_Trending/cl/CLI-Anything导读本文以 adguardhome 模块的测试计划 TEST.md 为核心系统讲解 cli-anything-adguardhome 这套「把 AdGuardHome 变成 Agent 原生可操控 CLI」的 harness 是如何被分层验证的。读者将掌握其单元测试无真实实例、全 mock、子进程测试针对已安装 CLI 的机制验证与 Docker 端到端测试真实 AdGuardHome v0.107.73三层测试设计理解每个被测模块背后的真实 REST 调用链并可直接照搬运行命令复现 36/36 全部通过的验证结论。一、被测对象cli-anything-adguardhome 是什么cli-anything-adguardhome 是位于 adguardhome/agent-harness/cli_anything/adguardhome 的一个 Python CLI harness目标是把 AdGuardHome 的网页后台操作全部暴露为命令行接口让人或 Agent可以直接执行cli-anything-adguardhome server status、filter add --url ...这类命令来控制广告拦截、DNS 重写、客户端管理等功能。从 模块结构 看业务逻辑被拆分为多个聚焦的 core 模块模块文件职责主要 REST 端点core/project.py连接配置读写、环境变量与默认值配置落盘本地 JSONcore/filtering.py过滤器状态、订阅列表增删、启停、刷新/filtering/status、/filtering/add_url、/filtering/remove_url、/filtering/configcore/blocking.py家长控制、安全浏览、安全搜索、屏蔽服务/parental/*、/safebrowsing/*、/safesearch/*、/blocked_services/*core/clients.py客户端设备增删改查/clients、/clients/add、/clients/deletecore/rewrite.pyDNS 重写规则增删查/rewrite/list、/rewrite/add、/rewrite/deletecore/session.pyREPL 会话状态与历史记录内存态所有 REST 调用统一收敛到 utils/adguardhome_backend.py 的AdGuardHomeClient中测试因此可以针对「调用方」和「HTTP 客户端」分别设计策略前者用 mock 拦截后者用真实 Docker 容器验证。这也是 TEST.md 中整套测试分层的根源。二、测试分层设计为什么拆成两层、三类TEST.md 开篇就给出了总览式的「Test Inventory Plan」test_core.py20 个单元测试不需要真实 AdGuardHometest_full_e2e.py12 个 E2E 子进程测试需要Docker 中的 AdGuardHome端口 3001。而实际执行结果中test_core.py展开为 24 个用例、test_full_e2e.py展开为 12 个用例合计 36 个全部通过。这种「单元 子进程 Docker E2E」的三角结构是一种很典型的 CLI harness 验证策略单元测试快、无依赖验证每个 core 模块发出的 HTTP 请求「路径正确、body 正确、返回解析正确」HTTP 层用unittest.mock打桩任何 CI 环境无需 Docker 即可秒级跑完子进程测试CLI 机制用subprocess真正执行已安装的cli-anything-adguardhome命令验证参数解析、--help、--json输出等「壳」本身的行为即使没有真实服务也能覆盖 CLI 面Docker E2E真实链路拉起官方adguard/adguardhome容器、走完安装向导再驱动命令对真实后端做过滤器/DNS 重写的完整生命周期验证从命令行参数到 REST 再到服务端状态的端到端正确性。三、单元测试计划test_core.py逐模块拆解test_core.py的完整源码位于 tests/test_core.py头部注释明确说明其设计前提No real AdGuardHome instance needed - all HTTP calls are mocked。文件中提供了两个关键 helpermock_response(...)构造带raise_for_status、json()、content的MagicMock响应甚至可模拟「无 JSON 返回」的场景json.side_effect ValueErrormake_client(...)快捷构造带认证的AdGuardHomeClient。3.1 AdGuardHomeClientutils/adguardhome_backend.py对应TestAdGuardHomeClient类的 9 个用例覆盖 HTTP 客户端的全部核心行为测试方法验证点对应源码逻辑test_client_init_default默认 hostlocalhost、port3000、无认证adguardhome_backend.py 构造签名默认值test_client_init_with_auth传入用户名密码后session.auth被设置构造器中if username or password: self.session.auth (username, password)test_client_init_no_auth未传认证时session.auth is None同上分支不进入test_client_url_constructionURL 拼接正确http://host:port/control/statusbase_url固定以/control为前缀端口为 443 时自动切https、为 80/443 时省略端口号test_get_successGET 返回反序列化后的 JSONsession.get(...)_handle_responsetest_get_empty_response空响应体时返回{}_handle_response中if not resp.content: return {}test_post_jsonPOST 发送 JSON bodysession.post(url, jsondata)test_post_empty无 body 的 POSTsession.post(url)第三个分支test_connection_error_raises_runtimeConnectionError被包装为带安装指引的RuntimeError_connection_error方法拼装提示信息需要特别强调的是test_connection_error_raises_runtime背后的可用性设计当无法连上 AdGuardHome 时adguardhome_backend.py 并不是抛一个干巴巴的ConnectionError而是抛出带有安装指引的RuntimeError原生安装 curl 命令与docker run --name adguardhome -p 3000:3000 adguard/adguardhome让初次使用的用户或 Agent能从报错中直接得到修复路径——这是该 harness 面向 Agent 友好性的一个微观体现。3.2 project.py配置加载优先级TestProject的 4 个用例验证配置系统的三层回退与覆盖逻辑对应 core/project.pytest_load_config_defaults无配置文件、无环境变量时返回localhost:3000见源码中DEFAULT_HOST/DEFAULT_PORTtest_load_config_from_file从 JSON 文件读取~/.config/cli-anything-adguardhome.jsontest_load_config_env_override环境变量优先于文件——源码逻辑为「先读文件填入默认 dict再用AGH_HOST/AGH_PORT/AGH_USERNAME/AGH_PASSWORD逐个覆盖」且AGH_PORT会被int()强转test_save_config把 host/port/username/password/https 五元组以indent2写入 JSON父目录自动mkdir(parentsTrue)。这套优先级默认值 文件 环境变量与 模块 README 中「export AGH_* 或config save」两种配置方式一一对应保证了 REPL 与一次性命令、人与 Agent 使用同一套配置语义。3.3 filtering.py过滤器与订阅TestFiltering的 4 个用例直接断言每个函数的 HTTP 调用目标test_get_status→GET /filtering/statustest_add_filter→POST /filtering/add_urlbody 含name、url、whitelisttest_remove_filter→POST /filtering/remove_urltest_set_enabled→POST /filtering/config且从 filtering.py 源码可见其实现细节先get_status读取当前interval默认 24再原样回填避免切换开关时误改刷新周期。此外 filtering.py 还实现了set_filter_url更新某条订阅的name/enabled与refresh强制刷新订阅列表均可在 README 的filter refresh命令中触达。3.4 blocking.py家长控制与安全浏览TestBlocking的 3 个用例覆盖parental_status、parental_enable、safebrowsing_status。结合 blocking.py 全貌可以看到该模块是对称完整的parental、safebrowsing、safesearch三组各自具备*_status/*_enable/*_disable三个函数再加上blocked_services_get/set按服务 id 列表批量屏蔽共同组成「按类别拦截」的防线。3.5 clients.py 与 rewrite.py设备与 DNS 重写TestClients的test_list_clientsGET /clients与test_add_clientPOST /clients/add验证客户端管理。值得关注的是 clients.py 中add_client的默认 body新增设备默认use_global_settingsTrue、继承全局屏蔽服务设置同时显式关闭 parental/safebrowsing/safesearch 三项独立开关含义是「新设备先用全局策略按需再细分」。TestRewrite的test_list_rewritesGET /rewrite/list与test_add_rewritePOST /rewrite/add验证 DNS 重写。对应 README 中的典型场景把内网域名myserver.local重写到192.168.1.50。四、E2E 测试计划test_full_e2e.py真实容器的完整生命周期test_full_e2e.py源码见 tests/test_full_e2e.py把验证推进到「真实 AdGuardHome」层面实现上分为几个关键部件。4.1 CLI 解析器既测安装版又保开发可用_resolve_cli(name)先用shutil.which查找 PATH 中的已安装命令找不到时回退到python -m cli_anything.adguardhome.adguardhome_cli以便开发环境下直接跑测试。若设置了环境变量CLI_ANYTHING_FORCE_INSTALLED1则强制要求已安装 CLI否则抛出带安装指引的RuntimeError。这解释了 README 中CLI_ANYTHING_FORCE_INSTALLED1 python3 -m pytest ...这条命令的用途它专门用于 CI 中校验「安装产物」本身可用。4.2 Docker fixture拉起、等待、配好再测文件顶部固定测试端口为3001、容器名为agh-cli-test避免与开发机上常见的 3000 端口冲突。测试启动后按顺序执行_wait_for_adguardhome(port, timeout30)轮询GET /control/status直到返回 200/401/40330 秒超时期间每秒探测一次解决容器启动慢导致的 flaky_configure_adguardhome(...)调用 AdGuardHome 的安装向导 API/control/install/configurepayload 指定 web/dns 监听地址、账号与密码模拟用户首次打开网页时的初始化流程使后续所有操作都处于已配置状态。setup/teardown 阶段分别在会话开始创建容器、结束后移除保证每次 E2E 都在干净的实例上运行。4.3 子进程用例无需真实服务即可验证 CLI 面TestCLISubprocess先覆盖一组「纯 CLI 机制」用例这部分不依赖真实 AdGuardHometest_helpcli-anything-adguardhome --help退出码为 0test_config_show_json--json config show输出包含 host/port 的合法 JSONtest_config_show_default_host验证默认 host 展示test_help_subcommands_listed--help中列出了全部子命令test_filter_help/test_rewrite_help/test_blocking_help各子命令的帮助信息可正常渲染。4.4 Docker 端到端用例真实功能链路TestDockerE2E在真实容器上执行实际运行记录为 5 个用例命名与计划略有差异但覆盖一致test_server_status_json--json server status拿到真实运行状态test_filter_list_json全新实例上的过滤器列表此时应为空或仅含默认订阅test_rewrite_lifecycle新增 rewrite → 列表中可见 → 删除 → 确认消失覆盖 rewrite.py 中add_rewrite/list_rewrites/delete_rewrite的闭环test_stats_show_json统计信息输出test_config_test配置自检。五、实测结果36/36 全绿TEST.md 末尾记录了 2026-03-13 的一次完整运行输出环境Python 3.13.5、pytest 9.0.2、真实 AdGuardHome v0.107.73汇总如下 36 passed in 6.57s 单元测试24/24子进程测试已安装 CLI7/7Docker E2E 测试真实 AdGuardHome v0.107.735/5值得说明的是计划清单中test_core.py写的是 20 个用例实际展开为 24 个TestAdGuardHomeClient比计划多出test_client_init_no_auth等 4 个用例说明 test_core.py 在计划制定后又有补充测试文档与代码保持同步演进全文 6.57 秒即可完成也印证了「单测快、Docker E2E 可控」的设计意图。六、如何在本仓库复现全部测试前置条件AdGuardHome 正在运行原生安装或docker run --name adguardhome -p 3000:3000 adguard/adguardhomeDocker E2E 部分需要本机可拉取adguard/adguardhome镜像。在仓库的 adguardhome/agent-harness 目录下执行# 仅单元测试无需 Docker最快 python3 -m pytest cli_anything/adguardhome/tests/test_core.py -v # 子进程 Docker E2E需要真实容器测试内部会自动拉起 agh-cli-test 容器端口 3001 python3 -m pytest cli_anything/adguardhome/tests/test_full_e2e.py -v -s # 全量回归 强制使用已安装的 CLI 产物适合 CI CLI_ANYTHING_FORCE_INSTALLED1 python3 -m pytest cli_anything/adguardhome/tests/ -v -s运行前也可先执行pip install -e .在 agent-harness 目录内确保cli-anything-adguardhome命令可用并用 adguardhome_cli.py 对应的server status、filter list做一次冒烟验证。三种测试文件的详细路径分别为 tests/test_core.py、tests/test_full_e2e.py 与本文所依据的 tests/TEST.md可供进一步阅读源码对照。七、这套测试体系给同类 Agent-CLI harness 的启示从本模块的测试组织可以提炼出几条可复用的工程经验供其他 harness仓库内如 freecad、blender、obs-studio 等模块结构类似借鉴按依赖强弱分文件把「可离线单测」与「需要真服务的 E2E」拆成两个测试文件让 CI 可以只跑前者做快速门禁后者放到带 Docker 的流水线互不拖累HTTP 客户端单独成层并重点测试所有 REST 调用收敛到一个AdGuardHomeClient其 URL 拼接、空响应处理、认证注入、连接错误包装都是最高价值的测试点配置优先级是必须锁定的契约默认值、配置文件、环境变量三者的覆盖关系一旦写错Agent 场景下极易出现「以为连的是 A 实例实际连了 B」的隐患因此单测要显式覆盖每一层错误信息即文档连接失败时把安装命令直接拼进RuntimeError让错误本身具备自愈引导能力容器名与端口显式错开E2E 用 3001 端口与agh-cli-test容器名隔离测试环境配合 30 秒健康轮询显著降低测试间的相互污染与启动 flaky。【免费下载链接】CLI-AnythingCLI-Anything: Making ALL Software Agent-Native -- CLI-Hub: https://clianything.cc/项目地址: https://gitcode.com/GitHub_Trending/cl/CLI-Anything创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表