Codex 系统手册

配置、策略和环境测试

stage-23.6.243 个文件

这一阶段主要是在幕后做“体检”,不是用户真正干活的主循环。它反复检查配置这条链路:本地、系统、企业云端和项目配置怎么读取、排序、合并,写错时能不能报清楚;再检查权限、网络、沙箱、hooks 小脚本、功能开关和提示文字有没有按安全规则生效。还有一批测试专门造假环境、假云端、假文件,像试车场一样模拟升级、缓存损坏、路径差异和额度不足,防止改代码时把用户设置或安全边界弄坏。

本阶段的文件43

配置测试夹具与云层

这些测试建立共享测试夹具构建器,并验证托管 cloud-config 片段和需求层在更广泛的加载器与解析器覆盖使用前如何构建和命名。

config/src/test_support.rs源码 ↗
testtest setup

这个文件是测试工具箱,不给正式运行的代码用。它解决的问题很简单:很多集成测试都需要一份假的企业云配置,比如企业强制要求、企业下发设置。如果每个测试都手写这些结构,会很啰嗦,也容易写错。这里的 CloudConfigBundleFixture 就像一个“样品制作器”:可以往一个空的配置包里加企业要求片段,也可以加企业配置片段。它会自动给这些片段起 id 和名字,第一份叫 Base requirements 或 Base config,后面的按编号递增。最后,测试可以把它变成 CloudConfigBundle,也可以变成 CloudConfigBundleLoader。后者像一个假的取货员:别人问它要云配置时,它直接返回这份预先做好的包,而不是去访问真实服务。

函数细节8
CloudConfigBundleFixture::enterprise_requirement16–18 ↗
fn enterprise_requirement(contents: impl Into<String>) -> Self

作用:快速做出一个只包含一条企业要求的测试夹具。测试想模拟“企业下发了一条要求”时,可以直接用它,不用自己一步步搭结构。

数据流:输入是一段文字内容 → 它先从一个默认的空 CloudConfigBundleFixture 开始 → 再把这段内容作为企业要求加进去 → 输出一个已经带有这条要求的 CloudConfigBundleFixture。

调用关系:它是 add_enterprise_requirement 的便捷入口。测试 adds_enterprise_requirements_in_order 会用它来从第一条企业要求开始搭测试数据;内部先调用默认构造,再把真正添加要求的活儿交给 add_enterprise_requirement。

调用图:被 1 处调用(adds_enterprise_requirements_in_order);外部调用 1 个(default)。

CloudConfigBundleFixture::enterprise_config20–22 ↗
fn enterprise_config(contents: impl Into<String>) -> Self

作用:快速做出一个只包含一条企业配置的测试夹具。测试想模拟“企业下发了一段配置”时,可以直接调用它。

数据流:输入是一段配置文字 → 它创建一个默认的空夹具 → 把这段文字作为企业配置片段放进去 → 输出一个带有这条配置的 CloudConfigBundleFixture。

调用关系:它是 add_enterprise_config 的便捷入口。别的代码不用关心内部配置包怎么摆放,只要给内容;这个函数负责从空包起步,并把添加配置的细节交给 add_enterprise_config。

调用图:外部调用 1 个(default)。

CloudConfigBundleFixture::loader_with_enterprise_requirement24–28 ↗
fn loader_with_enterprise_requirement(
        contents: impl Into<String>,
    ) -> CloudConfigBundleLoader

作用:快速做出一个假的云配置加载器,里面预装了一条企业要求。很多测试用它来模拟“加载配置时,从云端拿到了企业要求”。

数据流:输入是一段企业要求内容 → 它先做出带这条要求的 CloudConfigBundleFixture → 再把这个夹具转换成 CloudConfigBundleLoader → 输出一个测试用加载器;之后别人调用加载器时,会拿到这份假的配置包。

调用关系:它把 enterprise_requirement 和 into_loader 串起来,是很多配置加载测试的常用入口。调用它的测试包括检查冲突拒绝、云配置是否插入、企业要求优先级、是否忽略托管要求等场景;它让这些测试专注于结果,而不是搭建假云端数据。

调用图:被 36 处调用(write_value_rejects_feature_requirement_conflict, load_config_layers_applies_matching_remote_sandbox_config, load_config_layers_can_ignore_managed_requirements, load_config_layers_includes_cloud_config_bundle, load_config_layers_includes_cloud_hook_requirements, load_config_layers_resolves_relative_bundle_requirements_paths_against_codex_home, mdm_requirements_take_precedence_over_cloud_config_bundle, active_profile_is_cleared_when_requirements_force_fallback, approvals_reviewer_preserves_valid_user_choice_when_allowed_by_requirements, browser_feature_requirements_are_valid (+15 more));外部调用 1 个(enterprise_requirement)。

CloudConfigBundleFixture::loader_with_enterprise_config30–32 ↗
fn loader_with_enterprise_config(contents: impl Into<String>) -> CloudConfigBundleLoader

作用:快速做出一个假的云配置加载器,里面预装了一条企业配置。测试可以用它检查系统拿到企业配置后会不会正确合并、校验或拒绝。

数据流:输入是一段企业配置内容 → 它先做出带这条配置的 CloudConfigBundleFixture → 再转换成 CloudConfigBundleLoader → 输出一个会返回该配置包的测试加载器。

调用关系:它把 enterprise_config 和 into_loader 组合成一步。配置分层相关测试会调用它,比如检查云配置是否插在系统配置和用户配置之间,或者严格配置模式是否拒绝未知的云配置键。

调用图:被 2 处调用(load_config_layers_inserts_cloud_config_between_system_and_user, strict_config_rejects_unknown_cloud_config_key);外部调用 1 个(enterprise_config)。

CloudConfigBundleFixture::add_enterprise_requirement34–49 ↗
fn add_enterprise_requirement(mut self, contents: impl Into<String>) -> Self

作用:往现有测试夹具里再加一条企业要求。它适合用来连续添加多条要求,并自动保持编号和名字有顺序。

数据流:输入是已有的 CloudConfigBundleFixture 和一段要求内容 → 它查看当前已经有几条企业要求,算出下一条编号 → 生成类似 req_1、req_2 的 id,以及 Base requirements 或 Requirements 2 这样的名字 → 把新片段塞进 bundle.requirements_toml.enterprise_managed → 输出更新后的夹具。

调用关系:它是企业要求测试数据真正落到配置包里的地方。enterprise_requirement 会调用它来添加第一条;测试也可以链式调用它来添加多条,从而检查系统是否按顺序处理这些要求。

调用图:外部调用 2 个(into, format!)。

CloudConfigBundleFixture::add_enterprise_config51–66 ↗
fn add_enterprise_config(mut self, contents: impl Into<String>) -> Self

作用:往现有测试夹具里再加一条企业配置。它让测试可以很方便地构造一组按顺序排列的企业配置片段。

数据流:输入是已有的 CloudConfigBundleFixture 和一段配置内容 → 它数一数当前已有多少条企业配置,得到下一条编号 → 生成类似 cfg_1、cfg_2 的 id,以及 Base config 或 Config 2 这样的名字 → 把新片段放进 bundle.config_toml.enterprise_managed → 输出更新后的夹具。

调用关系:它是企业配置片段真正被加入配置包的函数。enterprise_config 会通过它添加第一条配置;需要多条配置的测试也可以反复调用它,构造更复杂的云端配置样本。

调用图:外部调用 2 个(into, format!)。

CloudConfigBundleFixture::into_bundle68–70 ↗
fn into_bundle(self) -> CloudConfigBundle

作用:把测试夹具拆开,取出里面真正的 CloudConfigBundle。测试如果只需要配置包本身,而不需要加载器,就会用这一步。

数据流:输入是一个 CloudConfigBundleFixture → 它取出内部保存的 bundle → 输出 CloudConfigBundle;原来的夹具被消耗掉,不能再继续用。

调用关系:它是从“制作器”到“成品配置包”的出口。into_loader 会先调用它拿到配置包,再把配置包包装成一个测试用加载器。

调用图:被 1 处调用(into_loader)。

CloudConfigBundleFixture::into_loader72–75 ↗
fn into_loader(self) -> CloudConfigBundleLoader

作用:把测试夹具变成一个假的 CloudConfigBundleLoader。这个加载器不会真的去网络或云端取东西,而是固定返回夹具里的配置包。

数据流:输入是一个 CloudConfigBundleFixture → 它先通过 into_bundle 取出 CloudConfigBundle → 再创建一个异步返回结果的加载器:被调用时返回 Ok(Some(bundle)) → 输出 CloudConfigBundleLoader。

调用关系:它是测试接入正式配置加载流程的桥。loader_with_enterprise_requirement 和 loader_with_enterprise_config 最终都会走到这里;它再调用 CloudConfigBundleLoader::new,把假数据包装成系统平时期待的加载器形状。

调用图:调用 2 个内部函数(new, into_bundle)。

config/src/test_support_tests.rs源码 ↗
testtest run

这个文件不参与正式运行,而是在跑测试时用来“验货”。它测试的是 CloudConfigBundleFixture 这类测试辅助工具:先创建一份带有第一条企业要求的云配置包,再追加第二条要求,最后把它变成真正的配置对象。测试会检查结果里 enterprise_managed 这个列表是不是刚好有两项,而且第一项内容是“first”、编号是 req_1、名字是 Base requirements;第二项内容是“second”、编号是 req_2、名字是 Requirements 2。可以把它理解成检查一个表单生成器:先填第一行,再加第二行,最后确认表格里的行没有颠倒,也没有自动命名错。

函数细节1
adds_enterprise_requirements_in_order5–25 ↗
fn adds_enterprise_requirements_in_order()

作用:这个测试函数确认:用测试夹具创建和追加企业要求时,生成出来的要求列表会保持添加顺序,并且自动生成的编号和名称符合预期。有人修改测试辅助代码后,跑这个测试就能发现是否破坏了这个约定。

数据流:进入时没有外部输入;它先调用 enterprise_requirement,用字符串“first”做出一份基础配置,再追加字符串“second”作为第二条要求,然后转成 bundle。最后它读取 bundle.requirements_toml.enterprise_managed,把实际生成的列表和手写的期望列表比较;如果完全一样,测试通过;如果顺序、编号、名称或内容有任何不同,测试失败。

调用关系:这个函数由 Rust 的测试运行器在执行测试时自动调用。它把创建测试配置的工作交给 enterprise_requirement 和后续链式方法完成,再用 assert_eq! 这个断言工具做最终核对;它本身就是一道保险,确保配置测试工具生成的数据稳定可靠。

调用图:调用 1 个内部函数(enterprise_requirement);外部调用 1 个(assert_eq!)。

config/src/cloud_config_bundle_tests.rs源码 ↗
testtest

这个文件不负责真正加载配置,而是专门检查相关代码有没有按预期工作。可以把云端配置包想成公司总部发来的几张规则纸,本地程序要把它们变成可用的配置层。这里测了三件关键事:第一,同一个异步加载器被同时问两次时,真正的加载动作只做一次,避免重复请求或重复计算;第二,企业管理的配置片段会按正确优先级排列,要求文件也能合并出最终规则;第三,在“严格校验”模式下,如果云端配置里写了程序不认识的字段,就要立刻报错,而不是假装没看见。这样可以保证企业策略既稳定,又不会因为格式写错而静默失效。

函数细节3
shared_future_runs_once13–24 ↗
async fn shared_future_runs_once()

作用:这个测试确认共享的云配置加载任务即使被同时请求两次,也只真正执行一次。这样可以避免两个地方同时要配置时,程序重复去加载同一份东西。

数据流:测试先准备一个计数器,加载动作每执行一次就把计数器加一;然后创建一个 CloudConfigBundleLoader,并用 tokio::join! 同时调用两次 get;最后检查两次拿到的结果一样,而且计数器只变成 1。也就是说,进去的是两个并发请求,经过共享加载器合并后,出来的是同一份结果,底层加载只发生一次。

调用关系:它直接测试 CloudConfigBundleLoader::new 创建出来的加载器行为。测试里两个 get 调用同时发生,用来模拟真实运行中多个地方一起等待云配置的情况;如果共享机制坏了,这个测试会通过计数器立刻发现。

调用图:调用 1 个内部函数(new);外部调用 6 个(clone, new, new, assert_eq!, default, join!)。

bundle_layers_preserve_enterprise_managed_bucket_order27–92 ↗
fn bundle_layers_preserve_enterprise_managed_bucket_order()

作用:这个测试确认企业管理的云端配置片段转换成本地配置层时,优先级顺序是对的。顺序很重要,因为后面的规则可能覆盖前面的规则,弄反了就可能让低优先级配置压过高优先级配置。

数据流:测试先创建一个临时目录,作为配置转换时需要的基础路径;再手写一个云端配置包,里面有两段配置和两段要求,名字分别代表高优先级和低优先级;然后调用 CloudConfigBundleLayers::from_bundle 把云端包变成本地层;最后检查配置层的排列顺序,以及要求合并后的结果。最终期望是配置层按正确顺序保存,而要求合并后只允许 on-request 这种审批策略。

调用关系:它主要覆盖 CloudConfigBundleLayers::from_bundle 的转换行为,并进一步用 compose_requirements 检查要求文件合并后的效果。这个测试站在“云配置进入本地配置系统”的入口处,确认转换和合并这两个环节能配合正确。

调用图:调用 2 个内部函数(from_bundle, from_absolute_path);外部调用 3 个(assert_eq!, tempdir, vec!)。

bundle_layers_can_strict_validate_enterprise_managed_config95–125 ↗
fn bundle_layers_can_strict_validate_enterprise_managed_config()

作用:这个测试确认严格模式会拒绝云端配置里的未知字段。这样配置写错时会直接暴露问题,而不是让错误配置悄悄被忽略。

数据流:测试先创建临时目录和基础路径;再构造一个云端配置包,其中配置内容故意写入 unknown_key = true 这个程序不认识的字段;接着调用 CloudConfigBundleLayers::from_bundle_strict_config 做严格转换;最后确认它没有成功,而是返回一个明确错误,错误里包含是哪段配置出错,以及提示 unknown_key 是未知配置字段。

调用关系:它专门测试 CloudConfigBundleLayers::from_bundle_strict_config 这条更严格的转换路径。和普通转换相比,这个测试关心的是校验失败时错误是否准确,方便上层在加载企业配置时给出清楚的诊断信息。

调用图:调用 2 个内部函数(from_bundle_strict_config, from_absolute_path);外部调用 4 个(new, assert_eq!, tempdir, vec!)。

config/src/cloud_config_layers_tests.rs源码 ↗
testtest

这个文件专门测试云端配置片段,也就是企业从云端下发的一小段 TOML 配置文本。TOML 可以理解成一种简单的配置文件格式,像“名字 = 值”这样的写法。项目会把这些片段变成一层层配置,再和系统配置、用户配置合并。这里重点检查几件事:云端层的先后顺序对不对;严格模式下遇到不认识的配置项会不会报错;企业配置是不是压过系统配置、但又会被用户配置压过;配置里的相对路径会不会按指定基础目录变成绝对路径;如果原始 TOML 内容有类型错误,诊断信息会不会显示企业配置的名字和编号。它像一套验收清单,保证配置叠加这件事不会悄悄变味。

函数细节9
fragment15–21 ↗
fn fragment(id: &str, name: &str, contents: &str) -> CloudConfigFragment

作用:这个小工具用来快速造一个“云端配置片段”。测试里不用每次手写完整结构,只要给编号、名字和配置文本就行。

数据流:输入一个片段编号、一个人能看懂的名字、一段配置文本 → 它把这三样东西分别转成字符串,塞进 CloudConfigFragment 这个测试用数据包里 → 输出一个可以交给配置转换函数使用的云端配置片段,不改动别的东西。

调用关系:它是这些测试的造数据帮手。各个测试先用它拼出不同的云端配置场景,再把结果交给 cloud_config_layers_from_fragments 或严格版本去验证后续行为。

toml23–25 ↗
fn toml(contents: &str) -> TomlValue

作用:这个小工具把一段 TOML 配置文字变成程序里可比较的配置值。测试要比较最终配置时,用它来写出期望结果。

数据流:输入一段 TOML 字符串 → 它调用 toml::from_str 解析文本,如果测试里写的 TOML 本身不合法就直接失败 → 输出一个 TomlValue,也就是程序内部表示的配置值。

调用关系:它被 enterprise_layers_precede_user_and_override_system 使用。那个测试需要同时造系统配置、用户配置和最终期望配置,所以把文字形式的 TOML 先交给这个函数转换,之后再用 assert_eq! 比较。

调用图:被 1 处调用(enterprise_layers_precede_user_and_override_system);外部调用 1 个(from_str)。

base_dir27–29 ↗
fn base_dir() -> AbsolutePathBuf

作用:这个小工具提供一个固定的测试基础目录:/var/lib/codex。测试相对路径解析时,需要一个稳定的参照点。

数据流:它不接收外部输入 → 调用 test_path_buf 造出测试路径,再转成 AbsolutePathBuf,也就是程序认为已经是绝对路径的路径类型 → 输出这个固定基础目录。

调用关系:它被多个测试调用。凡是云端配置需要知道“相对路径从哪里算起”,或者只是要传入配置片段转换函数时,测试都会先向它要同一个基础目录,避免每个测试各写一份路径。

调用图:被 6 处调用(enterprise_layers_precede_user_and_override_system, home_relative_path_fields_are_allowed_and_resolved, layers_are_returned_in_stack_order, raw_toml_diagnostics_use_enterprise_layer_name, relative_absolute_path_fields_resolve_against_base_dir, strict_layers_reject_unknown_config_fields);外部调用 1 个(test_path_buf)。

layers_are_returned_in_stack_order32–59 ↗
fn layers_are_returned_in_stack_order()

作用:这个测试确认云端配置片段变成配置层以后,返回顺序是配置栈真正使用的顺序,而不是简单照输入顺序来。这样后续合并配置时,谁先谁后才不会乱。

数据流:它先取得固定基础目录,再造两个云端片段:一个叫 high,一个叫 low → 把它们交给 cloud_config_layers_from_fragments 转成配置层 → 取出每层的来源名字来比较,确认结果顺序是 low 在前、high 在后。

调用关系:它使用 base_dir 准备路径参照,用 fragment 准备测试片段,最后用 assert_eq! 检查结果。它站在配置层生成流程的出口,专门盯住“层的顺序”这个容易出错的约定。

调用图:调用 1 个内部函数(base_dir);外部调用 2 个(assert_eq!, vec!)。

strict_layers_reject_unknown_config_fields62–80 ↗
fn strict_layers_reject_unknown_config_fields()

作用:这个测试确认严格模式下,云端配置里出现不认识的字段会被拒绝,而不是被悄悄忽略。这样企业下发错配置时能尽早发现。

数据流:它先取得固定基础目录,再造一个包含 unknown_key 的云端片段 → 调用 cloud_config_layers_from_fragments_strict,期待它返回错误 → 把实际错误和预期错误比较,确认错误里带着片段编号、片段名字和清楚的提示信息。

调用关系:它使用 base_dir 和 fragment 准备输入,然后把输入交给严格版配置转换函数。这个测试保护的是“严格校验”这道门,防止未知配置项漏进系统。

调用图:调用 1 个内部函数(base_dir);外部调用 2 个(assert_eq!, vec!)。

enterprise_layers_precede_user_and_override_system83–159 ↗
fn enterprise_layers_precede_user_and_override_system()

作用:这个测试确认系统配置、企业云端配置、用户配置三者叠在一起时,优先级符合预期:企业配置能盖过系统配置,但用户配置还能盖过企业配置。没有这个保证,最终生效配置可能和管理员或用户想的不一样。

数据流:它先造一层系统配置,再追加两层云端企业配置,最后追加一层用户配置 → 用 ConfigLayerStack::new 建成配置栈 → 一方面检查层顺序是系统、企业 low、企业 high、用户;另一方面检查最终合并结果里 model 来自用户,model_provider 来自 high 企业层,review_model 来自 low 企业层。

调用关系:它使用 base_dir 提供云端配置的路径参照,使用 toml 把文字配置转成可比较的值,也使用 ConfigLayerEntry::new 和 ConfigLayerStack::new 搭出完整配置栈。它是本文件里最像真实运行场景的测试,把系统、企业、用户三类配置放在一起验收。

调用图:调用 4 个内部函数(base_dir, toml, new, new);外部调用 5 个(default, assert_eq!, test_path_buf, default, vec!)。

relative_absolute_path_fields_resolve_against_base_dir162–182 ↗
fn relative_absolute_path_fields_resolve_against_base_dir()

作用:这个测试确认云端配置里的相对路径,比如 instructions.md,会按照云端配置的基础目录补成绝对路径。这样程序后面打开文件时,不会因为当前工作目录不同而找错地方。

数据流:它取得固定基础目录,再造一个包含 model_instructions_file = "instructions.md" 的云端片段 → 转成配置层后,从结果里取出这个路径字段 → 用 resolve_path_against_base 算出预期路径,并和实际路径比较。

调用关系:它使用 base_dir 和 fragment 准备场景,把片段交给 cloud_config_layers_from_fragments,再借助 AbsolutePathBuf::resolve_path_against_base 得到标准答案。它专门守住路径解析规则,保证相对路径不是随便按运行位置解释。

调用图:调用 2 个内部函数(base_dir, resolve_path_against_base);外部调用 2 个(assert_eq!, vec!)。

home_relative_path_fields_are_allowed_and_resolved185–205 ↗
fn home_relative_path_fields_are_allowed_and_resolved()

作用:这个测试确认以 ~/ 开头的路径也能被云端配置接受并解析。~/ 通常表示“用户家目录”风格的路径,测试确保这种写法不会被误判为非法。

数据流:它取得固定基础目录,再造一个包含 model_instructions_file = "~/instructions.md" 的云端片段 → 转成配置层后,读取解析后的路径字符串 → 用 resolve_path_against_base 按同样规则算出期望值,再比较两者是否一致。

调用关系:它和 relative_absolute_path_fields_resolve_against_base_dir 很像,但专门覆盖 ~/ 这种路径写法。它使用 base_dir 准备参照点,并验证云端配置转换函数对家目录相对路径的行为。

调用图:调用 2 个内部函数(base_dir, resolve_path_against_base);外部调用 2 个(assert_eq!, vec!)。

raw_toml_diagnostics_use_enterprise_layer_name208–231 ↗
async fn raw_toml_diagnostics_use_enterprise_layer_name()

作用:这个异步测试确认当云端配置的原始 TOML 内容有类型错误时,报错信息会指向“企业管理配置”的名字和编号,而不是显示一个含糊的文件名。这样用户或管理员才能知道是哪条云端策略出问题。

数据流:它先造一个云端片段,里面有一个路径字段和一个错误的 model = 1,因为 model 应该不是整数 → 片段先能被转成配置层,然后交给 first_layer_config_error_from_entries 做更深的配置解析诊断 → 输出一个错误诊断,测试检查它的路径名、行号、列号和错误文字都符合预期。

调用关系:它使用 base_dir 准备基础目录,先通过 cloud_config_layers_from_fragments 得到配置层,再把这些层交给 first_layer_config_error_from_entries。这个测试站在错误报告流程上,确保底层解析错误最终能用企业配置的可读名称呈现出来。

调用图:调用 1 个内部函数(base_dir);外部调用 3 个(assert!, assert_eq!, vec!)。

config/src/requirements_layers/stack_tests.rs源码 ↗
testtest

项目里可以有好几层要求配置,比如系统文件、企业下发策略、不同主机专用规则。这个文件专门测试这些层叠在一起时,最终结果是不是正确。它先用小工具把一段 TOML(一种常见的配置文件格式)包装成“配置层”,再调用真正的合并函数,然后和预期结果比较。测试覆盖了很多容易出错的地方:普通字段谁优先、表格能不能递归合并、规则列表是不是按优先级追加、远程沙箱配置是否只在主机名匹配时生效、读取主机名是否避免重复、钩子目录冲突是否要直接失败、文件读取禁止列表是否取并集。它的重要性在于:这些配置通常和安全权限有关,一旦合并错,可能把本该禁止的操作放开,或者把合法配置误判成错误。

函数细节24
layer17–25 ↗
fn layer(id: &str, name: &str, contents: &str) -> RequirementsLayerEntry

作用:把测试里写的一小段 TOML 配置,包装成一个带来源信息的配置层。这样测试不用每次都手写完整结构,只要给一个编号、名字和内容就行。

数据流:输入是配置层的 id、显示名称和 TOML 文本;它把 id 和名称做成企业管理来源,再调用 from_toml 解析内容;输出是一个 RequirementsLayerEntry,后面的合并测试会把它当作一层配置使用。

调用关系:这是很多测试的准备工具。测试函数先用它造出低优先级层和高优先级层,再交给 compose 或底层合并函数检查结果。

调用图:调用 1 个内部函数(from_toml)。

compose27–34 ↗
fn compose(
    layers: Vec<RequirementsLayerEntry>,
) -> Result<Option<ConfigRequirementsToml>, RequirementsCompositionError>

作用:这是测试用的简化合并入口。它把多层配置交给真正的合并逻辑,并把带来源信息的结果转成普通 TOML 结果,方便做相等比较。

数据流:输入是一组 RequirementsLayerEntry;它调用 compose_requirements_for_hostname,并且这里不指定主机名;如果合并成功,就把 ConfigRequirementsWithSources 转成 ConfigRequirementsToml;输出是可能为空的最终配置,或者合并错误。

调用关系:很多测试都通过它走默认合并路径,比如空层、普通 TOML 合并、网络映射、权限、解析错误等。它本身不决定规则,只是把测试数据送进真正的 compose_requirements_for_hostname。

调用图:被 10 处调用(deny_read_only_layers_do_not_leave_empty_permissions_tables, empty_layers_compose_to_none, mcp_requirements_use_regular_toml_merge, network_maps_use_regular_toml_merge, parse_error_names_layer, permissions_deny_read_unions_while_profiles_use_regular_toml_merge, regular_toml_merge_recurses_into_tables, rules_are_appended_in_priority_order, top_level_values_use_toml_priority, windows_requirements_use_regular_toml_merge);外部调用 1 个(compose_requirements_for_hostname)。

compose_with_hook_directory_field36–46 ↗
fn compose_with_hook_directory_field(
    layers: Vec<RequirementsLayerEntry>,
    hook_directory_field: HookDirectoryField,
) -> Result<Option<ConfigRequirementsToml>, RequirementsCompositionError>

作用:这是专门测试钩子目录合并规则的辅助入口。钩子可以理解为“某些动作发生前后自动执行的小脚本”,这个函数允许测试指定当前平台要看哪个钩子目录字段。

数据流:输入是多层配置和一个 HookDirectoryField;它调用 compose_requirements_for_hostname_and_hook_directory,同样不指定主机名;成功后转成普通 TOML,失败就返回错误;输出是最终配置或冲突错误。

调用关系:钩子相关测试会调用它,尤其是检查 managed_dir 和 windows_managed_dir 哪个算“当前生效字段”。真正判断冲突的工作交给 compose_requirements_for_hostname_and_hook_directory。

调用图:被 3 处调用(active_windows_managed_dir_conflicts_fail_closed, hooks_append_groups_and_reject_conflicting_managed_dirs, inactive_hook_dir_conflicts_do_not_fail_composition);外部调用 1 个(compose_requirements_for_hostname_and_hook_directory)。

expected_requirements48–50 ↗
fn expected_requirements(contents: impl AsRef<str>) -> ConfigRequirementsToml

作用:把测试里写的预期 TOML 文本解析成配置对象。这样断言时比较的是结构化配置,而不是容易受空格和顺序影响的纯文本。

数据流:输入是一段可转成字符串的 TOML;它读取字符串内容,用 toml::from_str 解析;输出是 ConfigRequirementsToml,如果预期文本本身写错就直接让测试失败。

调用关系:各个测试在 assert_eq! 前常用它生成“标准答案”。它不参与产品逻辑,只是让测试对比更可靠。

调用图:外部调用 2 个(as_ref, from_str)。

empty_layers_compose_to_none53–56 ↗
fn empty_layers_compose_to_none()

作用:确认没有任何配置层时,合并结果就是没有配置,而不是生成一个空壳配置。这样调用方能清楚地区分“真的没有要求”和“有一份空要求”。

数据流:输入是空的配置层列表;它通过 compose 合并;得到的结果应为 None;测试用 assert_eq! 验证这一点。

调用关系:这个测试走 compose 辅助函数,间接检查 compose_requirements_for_hostname 对空输入的处理。

调用图:调用 1 个内部函数(compose);外部调用 2 个(new, assert_eq!)。

top_level_values_use_toml_priority59–109 ↗
fn top_level_values_use_toml_priority()

作用:确认顶层字段冲突时,高优先级配置会覆盖低优先级配置。同时也检查没有被高层覆盖的条目能保留下来。

数据流:输入是低、高两层配置,里面都有审批策略、沙箱模式、默认权限、远程控制开关和权限档案;合并后,高层字段取胜,低层独有的只读档案仍保留;输出结果和预期 TOML 做比较。

调用关系:它调用 compose 来走默认合并流程,再用 expected_requirements 给出标准答案,验证普通字段的优先级规则。

调用图:调用 1 个内部函数(compose);外部调用 2 个(assert_eq!, vec!)。

composition_strategy_applies_to_non_cloud_layers112–218 ↗
fn composition_strategy_applies_to_non_cloud_layers()

作用:确认同一套合并策略不只适用于企业云下发的配置,也适用于系统文件、MDM 等本地管理来源。MDM 可以理解为公司设备管理系统下发的设置。

数据流:输入是系统配置层和 MDM 配置层;系统层给一些低优先级规则,MDM 层给高优先级规则;合并后,高层普通字段覆盖低层,功能表递归合并,规则按优先级排列,deny_read 路径合并成并集;同时检查字段来源记录来自正确来源。

调用关系:这个测试直接调用 compose_requirements_for_hostname,因为它还要检查带来源的结果,而不是只看普通 TOML。它也用 from_absolute_path 构造系统文件来源。

调用图:调用 1 个内部函数(from_absolute_path);外部调用 4 个(assert_eq!, cfg!, compose_requirements_for_hostname, vec!)。

single_regular_layer_keeps_enterprise_managed_source221–245 ↗
fn single_regular_layer_keeps_enterprise_managed_source()

作用:确认只有一层企业管理配置时,合并结果仍然记得这条设置来自哪一个企业策略。这个来源信息对报错、审计和解释配置很重要。

数据流:输入是一层包含 allow_managed_hooks_only 的企业配置;合并后得到带来源的配置;测试检查这个字段的值是 true,并且来源里的 id 和名称没有丢。

调用关系:它直接调用 compose_requirements_for_hostname,以便检查 Sourced 这种“值加来源”的结果。

调用图:外部调用 3 个(assert_eq!, compose_requirements_for_hostname, vec!)。

regular_toml_merge_recurses_into_tables248–307 ↗
fn regular_toml_merge_recurses_into_tables()

作用:确认普通 TOML 表格会一层层往里合并,而不是遇到高层表格就把低层整个表格丢掉。这样低层里没冲突的子配置还能继续生效。

数据流:输入是两层 features 和 apps 配置;高层改 shared、connector enabled 和 search 工具,低层提供 beta 和 list 工具;合并后既有高层覆盖项,也有低层保留项;输出和预期结构比较。

调用关系:它通过 compose 调用默认合并逻辑,用来守住“递归合并表格”这条基础规则。

调用图:调用 1 个内部函数(compose);外部调用 2 个(assert_eq!, vec!)。

merged_table_source_is_composite_in_priority_order310–350 ↗
fn merged_table_source_is_composite_in_priority_order()

作用:确认一个表格由多层共同合成时,来源信息会记录成组合来源,并且顺序体现优先级。这样以后解释配置时能说清楚它是由哪些策略拼出来的。

数据流:输入是高、低两层各自给 features 表的一部分;合并后 features 里同时有 alpha 和 beta;来源不是单一来源,而是按高优先级到低优先级组合起来的来源。

调用关系:它直接调用 compose_requirements_for_hostname,因为需要看带来源的 feature_requirements,而不是只看最终 TOML。

调用图:外部调用 3 个(assert_eq!, compose_requirements_for_hostname, vec!)。

mcp_requirements_use_regular_toml_merge353–390 ↗
fn mcp_requirements_use_regular_toml_merge()

作用:确认 MCP 服务器配置也按普通 TOML 表格规则合并。MCP 是一种让程序连接外部工具或服务的协议,这里关注的是它的配置别被整块覆盖错。

数据流:输入是低层定义 low 服务器和 shared 服务器,高层改 shared 服务器的命令;合并后 low 服务器保留,shared 服务器采用高层命令;结果和预期比较。

调用关系:它调用 compose,借默认合并路径验证 mcp_servers 这类嵌套表没有特殊误处理。

调用图:调用 1 个内部函数(compose);外部调用 2 个(assert_eq!, vec!)。

network_maps_use_regular_toml_merge393–447 ↗
fn network_maps_use_regular_toml_merge()

作用:确认网络域名和 Unix 套接字的允许/拒绝表会按普通映射规则合并。Unix 套接字可以理解为同一台机器上程序互相通信的本地“插口”。

数据流:输入是两层网络配置,部分键相同、部分键不同;相同键由高层覆盖,不同键都保留;输出包含合并后的域名规则和套接字规则。

调用关系:它通过 compose 检查 experimental_network 下的映射表,防止安全网络规则被错误丢弃。

调用图:调用 1 个内部函数(compose);外部调用 2 个(assert_eq!, vec!)。

windows_requirements_use_regular_toml_merge450–481 ↗
fn windows_requirements_use_regular_toml_merge()

作用:确认 Windows 专用配置也按普通 TOML 合并规则走。这里检查允许使用哪种 Windows 沙箱实现时,高优先级值会覆盖低优先级值。

数据流:输入是低层允许 unelevated,高层允许 elevated;合并后 windows 表中的 allowed_sandbox_implementations 采用高层的 elevated;结果和预期比较。

调用关系:它调用 compose,覆盖 Windows 配置分支的基础合并行为。

调用图:调用 1 个内部函数(compose);外部调用 2 个(assert_eq!, vec!)。

remote_sandbox_config_is_applied_per_layer484–518 ↗
fn remote_sandbox_config_is_applied_per_layer()

作用:确认远程沙箱配置会根据当前主机名逐层生效。沙箱就是限制程序能做什么的隔离环境,远程沙箱配置则允许不同机器用不同限制。

数据流:输入是低层默认只读沙箱,以及高层只对 build-*.example.com 生效的工作区写入沙箱;测试传入匹配的主机名 BUILD-01.EXAMPLE.COM.;合并后采用高层远程配置里的 workspace-write。

调用关系:它直接调用 compose_requirements_for_hostname,因为要传入指定主机名来测试主机名匹配逻辑。

调用图:外部调用 3 个(assert_eq!, compose_requirements_for_hostname, vec!)。

unmatched_remote_sandbox_config_does_not_shadow_lower_layers521–555 ↗
fn unmatched_remote_sandbox_config_does_not_shadow_lower_layers()

作用:确认高优先级的远程沙箱配置如果主机名不匹配,不会挡住低优先级的普通配置。也就是说,不适用的规则不应该产生影响。

数据流:输入是低层 read-only,以及高层只匹配 mac-*.example.com 的远程配置;测试传入 linux-01.example.com;高层远程配置被跳过,最终保留低层 read-only。

调用关系:它直接调用 compose_requirements_for_hostname,用明确主机名验证“未匹配就不覆盖”的规则。

调用图:外部调用 3 个(assert_eq!, compose_requirements_for_hostname, vec!)。

hostname_resolver_is_not_called_without_remote_sandbox_config558–586 ↗
fn hostname_resolver_is_not_called_without_remote_sandbox_config()

作用:确认如果配置里根本没有远程沙箱选择规则,就不会去获取主机名。这样可以避免不必要的系统调用,也减少测试和运行时的不确定性。

数据流:输入是一层普通沙箱配置和一个会记录调用次数的主机名获取函数;合并时没有远程沙箱配置,所以获取函数不应被调用;输出仍是 read-only,调用次数是 0。

调用关系:它调用 compose_requirements_with_hostname_resolver,把一个计数闭包交进去,专门检查合并逻辑是否懒得刚刚好:需要时才问主机名。

调用图:外部调用 4 个(default, assert_eq!, compose_requirements_with_hostname_resolver, vec!)。

hostname_resolver_is_called_once_for_multiple_remote_sandbox_layers589–630 ↗
fn hostname_resolver_is_called_once_for_multiple_remote_sandbox_layers()

作用:确认即使多层配置都需要按主机名选择,也只获取一次主机名。这样避免重复工作,也保证所有层用的是同一个判断依据。

数据流:输入是两层都带远程沙箱规则的配置,以及一个计数的主机名获取函数;合并时主机名函数被调用一次,两个层都用这个结果匹配;最终高层 workspace-write 覆盖低层 read-only。

调用关系:它调用 compose_requirements_with_hostname_resolver,测试主机名解析在多层远程配置中的缓存或复用行为。

调用图:外部调用 4 个(default, assert_eq!, compose_requirements_with_hostname_resolver, vec!)。

rules_are_appended_in_priority_order633–671 ↗
fn rules_are_appended_in_priority_order()

作用:确认命令规则列表不是简单覆盖,而是按优先级顺序追加。这样高优先级规则会先被看到,低优先级规则仍作为后备存在。

数据流:输入是低层 npm 规则和高层 git 规则;合并后列表中 git 规则排在前面,npm 规则排在后面;输出和预期 TOML 比较。

调用关系:它通过 compose 验证 rules.prefix_rules 的特殊合并策略,不同于普通字段覆盖。

调用图:调用 1 个内部函数(compose);外部调用 2 个(assert_eq!, vec!)。

hooks_append_groups_and_reject_conflicting_managed_dirs674–762 ↗
fn hooks_append_groups_and_reject_conflicting_managed_dirs()

作用:确认钩子分组会按优先级追加,同时同一个生效的托管钩子目录如果冲突就必须失败。失败是更安全的做法,叫“fail closed”,意思是不确定时宁可拒绝也不放行。

数据流:第一部分输入两层使用相同 managed_dir、不同钩子组的配置;合并后目录保留,钩子组高层在前低层在后。第二部分输入两个不同 managed_dir;合并返回错误,错误信息应指出字段名和两个冲突来源。

调用关系:它调用 compose_with_hook_directory_field,并指定 ManagedDir 是当前要检查的目录字段,用来验证钩子合并和冲突报错。

调用图:调用 1 个内部函数(compose_with_hook_directory_field);外部调用 3 个(assert!, assert_eq!, vec!)。

active_windows_managed_dir_conflicts_fail_closed765–792 ↗
fn active_windows_managed_dir_conflicts_fail_closed()

作用:确认 Windows 平台生效的钩子目录字段如果冲突,也会直接合并失败。这避免程序在两个不同托管目录之间猜测,造成安全漏洞。

数据流:输入是两层 windows_managed_dir 不同的钩子配置,并指定当前检查 WindowsManagedDir;合并结果应为错误;测试检查错误文字包含字段名和两个来源。

调用关系:它调用 compose_with_hook_directory_field,把焦点切到 WindowsManagedDir,补上 Windows 钩子目录冲突场景。

调用图:调用 1 个内部函数(compose_with_hook_directory_field);外部调用 2 个(assert!, vec!)。

inactive_hook_dir_conflicts_do_not_fail_composition795–925 ↗
fn inactive_hook_dir_conflicts_do_not_fail_composition()

作用:确认不是当前生效的钩子目录字段即使冲突,也不会让合并失败。换句话说,只对当前平台真正会用到的目录做严格冲突检查。

数据流:第一组输入 managed_dir 相同但 windows_managed_dir 不同,并指定检查 ManagedDir,合并成功;第二组输入 windows_managed_dir 相同但 managed_dir 不同,并指定检查 WindowsManagedDir,也成功;两次结果都保留高层目录值并追加钩子组。

调用关系:它两次调用 compose_with_hook_directory_field,分别模拟非 Windows 和 Windows 关注的目录字段,确保只检查活跃字段的冲突。

调用图:调用 1 个内部函数(compose_with_hook_directory_field);外部调用 2 个(assert_eq!, vec!)。

permissions_deny_read_unions_while_profiles_use_regular_toml_merge928–984 ↗
fn permissions_deny_read_unions_while_profiles_use_regular_toml_merge()

作用:确认文件系统的 deny_read 列表会取并集,而权限档案表仍按普通 TOML 规则合并。deny_read 是“禁止读取这些路径”,安全上不能因为高层少写一个路径就把低层禁令取消。

数据流:输入是低层禁止两个路径并定义权限档案,高层禁止其中一个路径并修改档案描述;合并后 deny_read 保留两个路径,档案 description 用高层值,extends 保留低层值;输出和预期比较。

调用关系:它调用 compose,专门覆盖权限合并中“列表取并集”和“表格递归合并”同时存在的情况。

调用图:调用 1 个内部函数(compose);外部调用 3 个(assert_eq!, cfg!, vec!)。

deny_read_only_layers_do_not_leave_empty_permissions_tables987–1015 ↗
fn deny_read_only_layers_do_not_leave_empty_permissions_tables()

作用:确认只有 deny_read 的配置不会额外留下空的权限表。这样最终配置干净,不会出现让人误解的空结构。

数据流:输入是一层只包含 permissions.filesystem.deny_read 的配置;合并后输出也只包含这个禁止读取路径;测试确认没有多余空表被生成。

调用关系:它通过 compose 检查权限合并后的清理行为,避免特殊处理 deny_read 时留下无用配置。

调用图:调用 1 个内部函数(compose);外部调用 3 个(assert_eq!, cfg!, vec!)。

parse_error_names_layer1018–1028 ↗
fn parse_error_names_layer()

作用:确认某一层 TOML 内容写错时,错误信息会点名是哪一层出问题。这样管理员排查配置时不用在多层策略里盲找。

数据流:输入是一层故意写错类型的配置,比如审批策略数组里放了数字;compose 尝试解析并失败;测试检查错误文字包含层名称、层 id 和出错字段名。

调用关系:它调用 compose 触发解析路径,验证 from_toml 或后续解析错误会被包装成带来源的可读错误。

调用图:调用 1 个内部函数(compose);外部调用 2 个(assert!, vec!)。

配置解析与编辑语义

本组涵盖核心配置数据模型、严格解析、合并行为、hooks 和 MCP 等专用区段,以及将配置往返写回 TOML 的持久化和编辑路径。

config/src/types_tests.rs源码 ↗
testtest

这个文件不参与正式运行,而是在开发者跑测试时起作用。它像给配置系统做体检:先把一小段 TOML 配置文本(TOML 是一种常见的配置文件格式,类似“字段 = 值”)喂给程序,看它能不能正确变成 Rust 里的配置对象。前两个测试检查技能配置可以用两种方式指定:一种是用名字,比如“github:yeet”;另一种是用本地文件路径。后两个测试检查记忆功能的配置有没有“兜底保护”:如果用户把数量限制写成 0,程序会自动改成至少 1;如果百分比写到 101 或 -1,程序会夹到合理范围 0 到 100。这样做的意义是防止坏配置让系统进入奇怪状态,就像电梯按钮不会允许你输入不存在的楼层。

函数细节4
deserialize_skill_config_with_name_selector5–17 ↗
fn deserialize_skill_config_with_name_selector()

作用:这个测试确认:当技能配置里写的是技能名字时,程序能正确读出这个名字,并且不会误以为用户提供了路径。它还检查 enabled=false 这种开关值会被正确保留下来。

数据流:进去的是一段 TOML 文本,里面有 name = "github:yeet" 和 enabled = false。测试用 toml::from_str 把文本转换成 SkillConfig,然后检查转换后的结果:name 是指定的字符串,path 是空的,enabled 是 false。出来的不是给业务代码用的结果,而是测试通过或失败;如果读错字段,断言就会失败。

调用关系:它是测试框架在跑单元测试时调用的独立检查项。它把解析工作交给外部的 toml::from_str,再用 assert_eq! 和 assert! 对结果做核对,证明 SkillConfig 的“按名字选择技能”这条路是通的。

调用图:外部调用 3 个(assert!, assert_eq!, from_str)。

deserialize_skill_config_with_path_selector20–43 ↗
fn deserialize_skill_config_with_path_selector()

作用:这个测试确认:当技能配置里写的是本地文件路径时,程序能把它读成一个绝对路径,并且不会同时填入技能名字。它保证“按路径选择技能”的配置方式可用。

数据流:进去的是测试临时创建的目录位置,以及拼出来的 skills/demo/SKILL.md 路径。测试把这个路径塞进一段 TOML 文本,用 toml::from_str 转成 SkillConfig,然后和手工构造的期望配置比较:path 有值,name 是空的,enabled 是 false。它还通过 AbsolutePathBuf::from_absolute_path 表达“这里应该是绝对路径”的要求。

调用关系:它由测试框架运行,先借助 tempfile::tempdir 创建一个安全的临时目录,再用 format! 拼出配置文本,随后把解析交给 toml::from_str。最后用 assert_eq! 把实际结果和期望结果对齐,验证路径选择器没有被解析坏。

调用图:外部调用 4 个(assert_eq!, format!, tempdir, from_str)。

memories_config_clamps_count_limits_to_nonzero_values46–61 ↗
fn memories_config_clamps_count_limits_to_nonzero_values()

作用:这个测试确认:记忆配置里的某些数量上限不能被设置成 0。哪怕用户写了 0,程序也会自动改成 1,避免后续流程因为“允许处理 0 条”而卡住或失效。

数据流:进去的是一个 MemoriesToml 原始配置,其中 max_raw_memories_for_consolidation 和 max_rollouts_per_startup 都被故意设成 Some(0)。测试调用 MemoriesConfig::from,把原始配置转换成正式配置;转换后,这两个值应该都变成 1,其它字段保持默认值。出来的是一次比较结果:实际配置必须等于期望配置,否则测试失败。

调用关系:它在测试阶段直接检查 MemoriesToml 到 MemoriesConfig 的转换规则。它调用 from 执行真正的配置整理,再用 default(ext) 取得默认配置作为参照,最后通过 assert_eq! 确认“最小值保护”确实生效。

调用图:调用 1 个内部函数(from);外部调用 2 个(default, assert_eq!)。

memories_config_clamps_rate_limit_remaining_threshold64–88 ↗
fn memories_config_clamps_rate_limit_remaining_threshold()

作用:这个测试确认:记忆配置里的剩余额度百分比会被限制在 0 到 100 之间。用户写 101 会变成 100,写 -1 会变成 0,避免出现不可能的百分比。

数据流:第一次进去的是 min_rate_limit_remaining_percent = Some(101) 的原始配置,转换成 MemoriesConfig 后应该得到 100。第二次进去的是 min_rate_limit_remaining_percent = Some(-1),转换后应该得到 0。每次都会把转换结果和带默认值的期望配置比较;如果没有正确夹到范围内,测试就会失败。

调用关系:它由测试框架运行,专门盯住 MemoriesConfig::from 里的百分比清洗规则。它不自己实现清洗,只提供两个越界例子,把转换交给 from,再用 default(ext) 和 assert_eq! 检查结果,确保配置系统不会接受荒唐的百分比。

调用图:调用 1 个内部函数(from);外部调用 2 个(default, assert_eq!)。

config/src/merge_tests.rs源码 ↗
testtest

这个文件专门测试 TOML 配置合并。TOML 是一种常见的配置文件格式,长得像一组分段的键值表。这里重点检查两件事:第一,旧名字 no_memories_if_mcp_or_web_search 会被整理成新名字 disable_on_external_context,而且如果新旧名字同时出现,会优先相信新名字;第二,网络权限里的域名会先统一大小写,比如 EXAMPLE.COM 和 example.com 被当成同一个网站。可以把它理解成“把几张配置纸叠在一起前,先把旧叫法和大小写整理干净”,这样后盖上来的配置才能正确覆盖前面的配置。这些测试不会参与正式运行,但它们守住了配置兼容性,避免改代码时悄悄破坏老用户的配置。

函数细节5
parse_toml6–8 ↗
fn parse_toml(value: &str) -> TomlValue

作用:这是测试里用的小帮手,把一段 TOML 配置文字变成程序能比较和合并的数据。这样每个测试不用重复写解析代码。

数据流:进去的是一段配置文本 → 它调用 TOML 解析器把文字读成结构化的 TomlValue → 出来的是可供 merge_toml_values 合并、也可供 assert_eq! 比较的配置值;如果文字本身写错了,测试会直接失败。

调用关系:四个测试都会先用它准备基础配置、覆盖配置或期望结果。它把“人能读的配置文字”转换成“测试能操作的数据”,后面的测试函数再拿这些数据去调用合并逻辑并检查结果。

调用图:被 4 处调用(merge_toml_values_normalizes_legacy_key_from_base_layer, merge_toml_values_normalizes_legacy_key_from_overlay_layer, merge_toml_values_normalizes_permission_network_domains_before_overlaying, merge_toml_values_prefers_canonical_key_when_one_layer_has_both_names);外部调用 1 个(from_str)。

merge_toml_values_normalizes_legacy_key_from_base_layer11–43 ↗
fn merge_toml_values_normalizes_legacy_key_from_base_layer()

作用:这个测试确认:如果底层配置还在用旧配置名,而上层配置用了新配置名,合并后只留下新配置名,并且值是上层配置的值。

数据流:进去的是两层配置:base 里有旧名字 no_memories_if_mcp_or_web_search=false,overlay 里有新名字 disable_on_external_context=true → 测试调用 merge_toml_values 把 overlay 盖到 base 上 → 出来的 base 应该只剩 disable_on_external_context=true;随后它还把结果转成 ConfigToml,确认真实配置结构也能读出这个值。

调用关系:它用 parse_toml 准备输入和期望结果,然后检查 merge_toml_values 的行为。这个场景模拟老用户的旧配置作为底层配置、新配置作为覆盖层时,系统应该如何兼容。

调用图:调用 1 个内部函数(parse_toml);外部调用 1 个(assert_eq!)。

merge_toml_values_normalizes_legacy_key_from_overlay_layer46–78 ↗
fn merge_toml_values_normalizes_legacy_key_from_overlay_layer()

作用:这个测试确认:如果覆盖层配置还在用旧配置名,程序也会把它当成新配置名处理,并正确覆盖底层配置。

数据流:进去的是 base 中的新名字 disable_on_external_context=false,以及 overlay 中的旧名字 no_memories_if_mcp_or_web_search=true → 合并时旧名字先被归一成新名字,再覆盖 base → 出来的配置应该是 disable_on_external_context=true;最后同样转成 ConfigToml,确认正式配置读取也没问题。

调用关系:它依赖 parse_toml 构造两层配置,然后通过断言检查 merge_toml_values 的兼容逻辑。这个测试覆盖的是“用户把旧写法放在更高优先级配置里”的情况。

调用图:调用 1 个内部函数(parse_toml);外部调用 1 个(assert_eq!)。

merge_toml_values_prefers_canonical_key_when_one_layer_has_both_names81–100 ↗
fn merge_toml_values_prefers_canonical_key_when_one_layer_has_both_names()

作用:这个测试确认:同一层配置里如果新旧两个名字同时出现,程序会优先采用标准的新名字,而不是被旧名字反过来覆盖。

数据流:进去的是一个空的 base,以及一个 overlay;overlay 的 memories 段里同时写了 disable_on_external_context=true 和旧名字 no_memories_if_mcp_or_web_search=false → 合并时程序清理旧名字,并保留标准名字的值 → 出来的结果只应有 disable_on_external_context=true。

调用关系:它先创建一个空 TOML 表作为 base,再用 parse_toml 准备带冲突键名的 overlay。它测试的是 merge_toml_values 在同一层内部遇到新旧名字冲突时的取舍规则。

调用图:调用 1 个内部函数(parse_toml);外部调用 3 个(Table, assert_eq!, new)。

merge_toml_values_normalizes_permission_network_domains_before_overlaying103–126 ↗
fn merge_toml_values_normalizes_permission_network_domains_before_overlaying()

作用:这个测试确认:网络权限里的域名在合并前会统一成小写,所以同一个域名不会因为大小写不同而变成两条规则。

数据流:进去的是 base 中 example.com=deny,以及 overlay 中 EXAMPLE.COM=allow → 合并前域名键会被统一成 example.com → overlay 的 allow 覆盖 base 的 deny,最后结果只有 example.com=allow。

调用关系:它用 parse_toml 准备基础权限和覆盖权限,然后检查 merge_toml_values 是否先规范域名再覆盖。这个测试保护的是权限配置的安全和一致性,避免同一网站因为大小写不同出现两份互相打架的规则。

调用图:调用 1 个内部函数(parse_toml);外部调用 1 个(assert_eq!)。

config/src/strict_config_tests.rs源码 ↗
testtest run

这个测试文件像是在给配置系统做“错别字检查”。项目的配置用 TOML(一种常见的配置文件格式)来写,严格模式下,不认识的字段应该报错,而且要告诉用户错在第几行第几列。这里测试了几种容易出问题的情况:来源名字不一定是真实文件路径,也要能报错;如果同一份配置里既有类型写错,又有多余字段,要优先报告类型错误;features 这类功能开关里不能随便写未知名字;但 desktop 这一块允许放一些桌面端自己理解的键,不该被拦住。每个测试都构造一小段配置文本,调用配置检查函数,然后用断言确认得到的错误位置和错误信息完全符合预期。

函数细节5
ignored_toml_field_errors_accept_non_file_source_names9–37 ↗
fn ignored_toml_field_errors_accept_non_file_source_names()

作用:这个测试确认:配置来源不一定是普通文件路径,也可以是像“com.openai.codex:...”这样的名字;即便如此,发现未知配置项时也要正常报错。这样可以支持从特殊渠道加载配置,比如内嵌文本或编码后的配置。

数据流:进去的是一个来源名字、一段 TOML 配置文本,以及把文本解析出来的 TOML 值;测试把这些交给“检查未知字段”的函数。函数发现 unknown_key 不是合法配置项后,返回一个配置错误;测试再拿这个错误和预期错误比较,确认路径名、行列范围、错误文字都对。

调用关系:它在测试阶段单独运行,重点盯住“非文件来源名”这个边角场景。最后通过 assert_eq! 做精确比较,确保报错结果没有偏差。

调用图:外部调用 1 个(assert_eq!)。

type_errors_take_precedence_over_ignored_fields40–66 ↗
fn type_errors_take_precedence_over_ignored_fields()

作用:这个测试确认:如果配置里同时有“字段类型写错”和“多写了未知字段”,系统应该先报告类型错误。这样用户会先看到真正阻止配置被理解的关键问题。

数据流:进去的是一个假配置文件路径和一段配置文本,其中 model_context_window 本该是数字,却写成了字符串 wide,后面还多了 unknown_key。检查函数读取文本后先撞上类型错误,于是输出这个类型错误;测试用预期的 ConfigError 和实际结果比较,确认没有先报未知字段。

调用关系:它验证严格配置检查的错误优先级。函数里会构造预期错误对象,并用 assert_eq! 检查实际返回值,保证上层以后展示给用户的第一条错误是最有用的那条。

调用图:外部调用 2 个(new, assert_eq!)。

strict_config_rejects_unknown_feature_key69–89 ↗
fn strict_config_rejects_unknown_feature_key()

作用:这个测试确认:在全局 features 功能开关区域里,不能写系统不认识的开关名。否则用户拼错一个功能名时,系统必须及时指出来。

数据流:进去的是路径和一段包含 [features] 的 TOML 文本,里面写了 foo = true。检查函数发现 features.foo 不是合法字段,就返回一个错误,位置指向 foo 这个键;测试把它和预期错误做完全一致的比较。

调用关系:它覆盖的是普通 features 区块的严格校验。测试通过 ConfigError::new 准备标准答案,再用 assert_eq! 确认配置检查函数真的拒绝未知功能键。

调用图:外部调用 2 个(new, assert_eq!)。

strict_config_rejects_unknown_profile_feature_key92–112 ↗
fn strict_config_rejects_unknown_profile_feature_key()

作用:这个测试确认:不仅全局功能开关要严格,某个 profile(配置档,比如 work 工作档)里的 features 也要严格。这样不同配置档里拼错的功能名也不会被静默放过。

数据流:进去的是路径和一段 [profiles.work.features] 配置文本,其中写了 foo = true。检查函数沿着 profiles.work.features 这条层级去看,发现 foo 不认识,于是返回带完整字段名 profiles.work.features.foo 的错误;测试确认错误位置和信息符合预期。

调用关系:它补上了“配置档内部功能开关”的测试场景。它构造预期错误对象,并用 assert_eq! 对比结果,保证嵌套配置里的未知字段也能被准确定位。

调用图:外部调用 2 个(new, assert_eq!)。

strict_config_accepts_opaque_desktop_keys115–127 ↗
fn strict_config_accepts_opaque_desktop_keys()

作用:这个测试确认:desktop 这一段配置可以放一些主程序不认识、但桌面端可能需要的键,不应该被严格配置检查当成错误。也就是说,这里是一个有意保留的“自由区域”。

数据流:进去的是路径和一段包含 [desktop]、[desktop.workspace] 的 TOML 文本,里面有 appearanceTheme 和 collapsed。检查函数读取后没有把这些 desktop 下的键当成未知字段,所以返回 None;测试确认结果确实是没有错误。

调用关系:它保护 desktop 配置的特殊规则,避免以后有人把严格检查改得太死,导致桌面端专用配置被误杀。最后用 assert_eq! 确认检查结果为空。

调用图:外部调用 2 个(new, assert_eq!)。

config/src/hooks_tests.rs源码 ↗
testtest

这个文件不是产品运行时直接用的代码,而是一组自动检查。它像验收表一样,拿几段示例配置喂给解析器,看解析出来的结果是不是程序内部真正想要的样子。它重点保护几件事:老版本 JSON 结构还能继续用;事件必须放在正确的 hooks 外壳下面,不能随便写在最外层;TOML 里用数组表写 hook 时能正常变成内部结构;状态信息 state 能和事件配置一起读进来;企业托管的 hook 配置可以把事件字段“摊平”写;Windows 专用命令既支持 command_windows,也支持 commandWindows。没有这些测试,配置格式一改就可能悄悄破坏兼容性,用户写好的自动化脚本可能突然失效。

函数细节7
hooks_file_deserializes_existing_json_shape13–53 ↗
fn hooks_file_deserializes_existing_json_shape()

作用:这个测试确认旧有的 hooks.json 写法仍然能被读懂。它防止项目升级后,把用户已经存在的 JSON 配置文件弄坏。

数据流:进去的是一段 JSON 字符串,里面的事件放在最外层的 hooks 对象里,并包含一个命令型 hook。测试用 JSON 解析函数把它变成 HooksFile 结构,然后拿这个结果和手写出来的期望结构做比较。出来的结果不是业务输出,而是测试通过或失败;如果字段名、默认值或层级解析错了,断言就会失败。

调用关系:它在测试运行时由 Rust 测试框架调用。函数自己把解析工作交给 serde_json::from_str,最后用 assert_eq! 比较实际结果和预期结果,证明 HooksFile 这套反序列化规则没有破坏旧格式。

调用图:外部调用 2 个(assert_eq!, from_str)。

hooks_file_rejects_events_outside_hooks_object56–77 ↗
fn hooks_file_rejects_events_outside_hooks_object()

作用:这个测试确认配置不能把事件直接写在 JSON 最外层。这样做是为了避免用户写错位置时程序还假装接受,最后产生难以排查的行为。

数据流:进去的是一段错误形状的 JSON:SessionStart 事件直接写在根部,没有包在 hooks 对象里。测试尝试把它解析成 HooksFile,并期待解析失败。随后检查错误信息里确实提到了未知字段 SessionStart。出来的结果是测试通过或失败;它验证程序会明确拒绝这种错误格式。

调用关系:它由测试框架调用,用解析失败这件事来检查配置入口的边界。它主要依赖 serde_json 的解析错误,再用 assert! 确认错误原因符合预期。

调用图:外部调用 1 个(assert!)。

hook_events_deserialize_from_toml_arrays_of_tables80–111 ↗
fn hook_events_deserialize_from_toml_arrays_of_tables()

作用:这个测试确认 TOML 里常见的“数组表”写法可以正确表达 hook 事件。数组表可以理解成 TOML 里用双中括号写多条同类配置的方式。

数据流:进去的是一段 TOML 字符串,包含一个 PreToolUse 事件、一个匹配器 matcher,以及一个命令型 hook。测试用 TOML 解析函数把它变成 HookEventsToml,再和预期结构比较。解析后,timeout、statusMessage 等字段应落到正确位置,没写的字段应使用默认值。

调用关系:它在测试阶段运行,专门检查 HookEventsToml 这层事件集合的解析。它把读取 TOML 的工作交给 toml::from_str,再用 assert_eq! 确认解析器、字段重命名规则和默认值一起配合正确。

调用图:外部调用 2 个(assert_eq!, from_str)。

hooks_toml_deserializes_inline_events_and_state_map114–156 ↗
fn hooks_toml_deserializes_inline_events_and_state_map()

作用:这个测试确认完整的 hooks TOML 文件既能写事件配置,也能写每个 hook 的状态记录。状态记录比如是否启用、信任过的哈希值,用来记住用户或系统对某个 hook 的选择。

数据流:进去的是一段 TOML:上半部分是 state 映射,记录某个 hook 的 enabled 和 trusted_hash;下半部分是 PreToolUse 事件及其命令。测试把它解析成 HooksToml,并比较 events 和 state 两块内容是否都正确。出来的是测试结果;如果事件和状态不能同时读,或者 key、布尔值、哈希字符串解析错了,就会失败。

调用关系:它由测试框架触发,覆盖比单独 HookEventsToml 更完整的配置文件形态。它调用 toml::from_str 完成文本到结构体的转换,再用 assert_eq! 证明 HooksToml 能把事件和状态两套信息放到各自正确的位置。

调用图:外部调用 2 个(assert_eq!, from_str)。

managed_hooks_requirements_flatten_hook_events159–194 ↗
fn managed_hooks_requirements_flatten_hook_events()

作用:这个测试确认“托管 hooks 要求”文件可以把管理目录和 hook 事件写在同一层。托管 hooks 通常是公司或团队统一下发的脚本位置和规则。

数据流:进去的是一段 TOML,包含 managed_dir,也包含 PreToolUse 事件和命令型 hook。测试把它解析成 ManagedHooksRequirementsToml,期望 managed_dir 变成路径对象,事件配置则进入 hooks 字段。出来的是测试是否通过;它保证这种比较扁平、好写的配置格式能被程序正确收纳进内部结构。

调用关系:它在自动测试中运行,验证企业托管配置的解析入口。它把解析交给 toml::from_str,再用 assert_eq! 检查 managed_dir、windows_managed_dir 默认值,以及 hooks 事件内容是否都符合预期。

调用图:外部调用 2 个(assert_eq!, from_str)。

hook_events_deserialize_windows_override_from_toml197–229 ↗
fn hook_events_deserialize_windows_override_from_toml()

作用:这个测试确认 TOML 里可以为 Windows 单独指定一条命令。这样同一个 hook 在 Linux/macOS 上用 shell 脚本,在 Windows 上用 PowerShell 脚本,互不冲突。

数据流:进去的是一段 TOML,其中 command 是默认命令,command_windows 是 Windows 专用命令。测试解析成 HookEventsToml 后,检查默认命令和 Windows 命令都被放进同一个 HookHandlerConfig::Command 里。出来的是测试结果;它保护跨平台配置不会丢掉 Windows 覆盖项。

调用关系:它由测试框架调用,专门覆盖 command_windows 这种下划线字段名。它依赖 toml::from_str 完成解析,再用 assert_eq! 确认字段别名、路径反斜杠和默认字段值都处理正确。

调用图:外部调用 2 个(assert_eq!, from_str)。

hook_events_deserialize_camel_case_windows_override_from_toml232–264 ↗
fn hook_events_deserialize_camel_case_windows_override_from_toml()

作用:这个测试确认 Windows 专用命令也支持 commandWindows 这种驼峰写法。驼峰写法就是中间不用下划线、后一个单词首字母大写,很多 JSON 或前端配置里常见。

数据流:进去的是一段 TOML,内容和 Windows 覆盖命令测试类似,但字段名写成 commandWindows。测试把它解析成 HookEventsToml,并检查内部仍然得到 command_windows 这个统一字段。出来的是测试通过或失败;它保证用户用另一种常见命名风格时也不会配置失效。

调用关系:它在测试运行时由测试框架执行,是前一个 Windows 覆盖测试的兼容性补充。它把文本解析交给 toml::from_str,再用 assert_eq! 验证驼峰字段名能被识别并转成同一个内部配置位置。

调用图:外部调用 2 个(assert_eq!, from_str)。

config/src/mcp_types_tests.rs源码 ↗
testtest run

MCP 可以理解成让应用连接外部工具服务的一套接口;这个文件专门测试这些服务在 TOML 配置文件里的写法。它检查两大连接方式:一种是 stdio,也就是启动一个本地命令,通过标准输入输出跟它说话;另一种是 HTTP,也就是连到一个网址。测试会把一小段 TOML 文本读成 McpServerConfig,然后确认默认值、命令参数、环境变量、工作目录、请求头、OAuth 登录信息、工具开关等都落到了正确位置。它也故意喂一些危险或矛盾的配置,比如同时写 command 和 url、HTTP 里写本地环境变量、stdio 里写 HTTP 请求头,确认程序会报错。没有这些测试,配置格式一旦被改坏,用户可能要到运行时才发现服务起不来,问题会更难查。

函数细节25
deserialize_stdio_command_server_config7–29 ↗
fn deserialize_stdio_command_server_config()

作用:测试最简单的 stdio MCP 配置:只写一个 command 命令时,程序能不能把它读成“启动这个命令”的配置。它还确认一些默认开关是合理的。

数据流:输入是一段只包含 command = "echo" 的 TOML 文本;测试把它解析成 McpServerConfig;结果应该是 stdio 传输方式,命令为 echo,参数、环境变量、工作目录为空,并且默认启用、不强制、没有工具过滤。

调用关系:这是 stdio 配置测试的基础样例。它直接调用 TOML 解析,然后用断言检查结果,为后面带参数、带环境变量、带目录的测试提供基准。

调用图:外部调用 3 个(assert!, assert_eq!, from_str)。

deserialize_stdio_command_server_config_with_args32–52 ↗
fn deserialize_stdio_command_server_config_with_args()

作用:测试 stdio 配置里带 args 参数列表时,参数会不会被原样读进去。这样用户写的命令行参数才不会丢。

数据流:输入是一段包含 command 和 args 的 TOML;测试解析后检查传输配置;输出结果应是命令 echo,参数列表变成 hello 和 world,并且服务仍然默认启用。

调用关系:它接在最简单 command 测试之后,验证同一条 stdio 配置通道能继续承载命令参数。它不把工作交给项目内其他函数,而是通过 TOML 反序列化触发配置代码。

调用图:外部调用 3 个(assert!, assert_eq!, from_str)。

deserialize_remote_stdio_server_requires_absolute_cwd55–82 ↗
fn deserialize_remote_stdio_server_requires_absolute_cwd()

作用:测试远程环境里的 stdio 服务必须写绝对工作目录。绝对路径就是从根目录开始的完整地址,避免远程机器不知道相对路径该从哪里算。

数据流:它先输入一个有 environment_id = "remote" 但没有 cwd 的配置,应该得到错误;再输入 cwd = "relative" 这种相对路径,也应该得到错误;最后检查错误信息里说明了原因。

调用关系:这是配置安全边界测试。它验证解析阶段就拦住不明确的远程启动目录,避免后面的启动流程拿到一个无法可靠执行的配置。

调用图:外部调用 1 个(assert!)。

deserialize_remote_stdio_server_accepts_absolute_cwd85–108 ↗
fn deserialize_remote_stdio_server_accepts_absolute_cwd()

作用:测试远程 stdio 服务在提供绝对 cwd 时可以通过。它证明上一个测试里的限制不是“一律禁止”,而是只禁止不明确的目录。

数据流:输入先来自系统临时目录,也就是一个绝对路径;测试把这个路径写进 TOML,再解析成配置;结果应该包含同一个 cwd,并且其它 stdio 默认项保持为空或默认值。

调用关系:它和“必须是绝对 cwd”的拒绝测试成对出现。一个测坏情况,一个测好情况,保证规则既严格又不会误伤合法配置。

调用图:外部调用 5 个(assert_eq!, format!, panic!, temp_dir, from_str)。

deserialize_stdio_command_server_config_with_arg_with_args_and_env111–132 ↗
fn deserialize_stdio_command_server_config_with_arg_with_args_and_env()

作用:测试 stdio 配置同时带命令参数和固定环境变量时能正确读取。环境变量可以理解成启动程序时附带的小纸条,比如 FOO=BAR。

数据流:输入包含 command、args 和 env 表;解析后应该得到 stdio 配置,命令是 echo,参数是 hello/world,env 里有 FOO 到 BAR 的映射,服务默认启用。

调用关系:它扩展了 stdio 的参数测试,把环境变量也放进同一次解析中,确认多个字段组合起来不会互相干扰。

调用图:外部调用 3 个(assert!, assert_eq!, from_str)。

deserialize_stdio_command_server_config_with_env_vars135–154 ↗
fn deserialize_stdio_command_server_config_with_env_vars()

作用:测试 env_vars 这种“从外部继承环境变量名”的写法能被读取。它和 env 不同,env 是直接写值,env_vars 是说运行时去拿这些名字对应的值。

数据流:输入是一段 command 加 env_vars = ["FOO", "BAR"] 的 TOML;解析后 env 仍为空,但 env_vars 列表里应该有 FOO 和 BAR。

调用关系:它验证 stdio 启动配置里第二种环境变量来源。配置解析代码需要同时支持直接给值和只给变量名,这个测试覆盖后者。

调用图:外部调用 2 个(assert_eq!, from_str)。

deserialize_stdio_command_server_config_with_env_var_sources157–190 ↗
fn deserialize_stdio_command_server_config_with_env_var_sources()

作用:测试 env_vars 里既能写老式字符串,也能写带 source 来源的对象。source 表示这个环境变量应该从本地还是远程环境取。

数据流:输入里有一个普通字符串 LEGACY_TOKEN,还有两个带 name/source 的配置;解析后列表应保留三项:一个简单名字,以及两个带 local、remote 来源的结构。

调用关系:它保证新旧两种 env_vars 写法可以共存。这样老用户的配置不会坏,新用户又能明确指定变量来源。

调用图:外部调用 2 个(assert_eq!, from_str)。

deserialize_stdio_command_server_config_rejects_unknown_env_var_source193–207 ↗
fn deserialize_stdio_command_server_config_rejects_unknown_env_var_source()

作用:测试 env_vars 的 source 如果写成不支持的值,会被拒绝。这样拼错来源时不会悄悄变成错误行为。

数据流:输入是 source = "elsewhere" 的配置;解析应该失败;测试再检查错误信息里明确提到 unsupported env_vars source elsewhere

调用关系:它是 env_vars 来源测试的反面案例。前一个测试确认 local/remote 能用,这个测试确认未知来源不能混进去。

调用图:外部调用 1 个(assert!)。

deserialize_stdio_command_server_config_with_cwd210–229 ↗
fn deserialize_stdio_command_server_config_with_cwd()

作用:测试 stdio 配置可以指定 cwd,也就是启动命令前先切到哪个工作目录。很多命令依赖当前目录,读错会导致找不到文件。

数据流:输入包含 command = "echo" 和 cwd = "/tmp";解析后 stdio 配置里的 cwd 应该是 /tmp,其它字段保持默认。

调用关系:它覆盖本地 stdio 的工作目录字段,并与远程 cwd 的绝对路径规则测试互相补充。

调用图:外部调用 2 个(assert_eq!, from_str)。

deserialize_disabled_server_config232–243 ↗
fn deserialize_disabled_server_config()

作用:测试用户可以通过 enabled = false 关闭某个 MCP 服务器配置。这样配置可以保留在文件里,但暂时不启动。

数据流:输入包含 command 和 enabled = false;解析后 cfg.enabled 应该是 false,同时 required 默认仍是 false。

调用关系:它验证服务器级别的开关字段。后续真正启动服务的代码会看这个值决定是否跳过该服务器。

调用图:外部调用 2 个(assert!, from_str)。

deserialize_required_server_config246–256 ↗
fn deserialize_required_server_config()

作用:测试 required = true 能正确读取。required 表示这个服务很重要,启动失败时不能当作可有可无。

数据流:输入包含 command 和 required = true;解析后 cfg.required 应该为 true。

调用关系:它覆盖另一个服务器级别的控制开关。启动流程之后可以根据 required 决定失败时是继续还是报错停下。

调用图:外部调用 2 个(assert!, from_str)。

deserialize_streamable_http_server_config259–277 ↗
fn deserialize_streamable_http_server_config()

作用:测试最简单的 HTTP MCP 配置:只写 url 时,程序能把它识别成通过网络地址连接的服务。

数据流:输入是一段 url = "https://example.com/mcp" 的 TOML;解析后 transport 应该是 StreamableHttp,里面有这个网址,令牌环境变量和请求头都为空,并且服务默认启用。

调用关系:这是 HTTP 配置测试的基础样例,对应 stdio 的最小 command 测试。它确认 url 字段会选择 HTTP 传输方式。

调用图:外部调用 3 个(assert!, assert_eq!, from_str)。

deserialize_streamable_http_server_config_with_env_var280–299 ↗
fn deserialize_streamable_http_server_config_with_env_var()

作用:测试 HTTP 配置可以指定 bearer_token_env_var。它表示认证令牌不直接写在配置里,而是从某个环境变量读取,更安全。

数据流:输入包含 url 和 bearer_token_env_var = "GITHUB_TOKEN";解析后 HTTP 配置里应记录这个环境变量名,其它 HTTP 附加字段为空,服务默认启用。

调用关系:它扩展最简单的 HTTP 测试,覆盖认证令牌的安全写法。后续发 HTTP 请求的代码会按这个名字去取真实令牌。

调用图:外部调用 3 个(assert!, assert_eq!, from_str)。

deserialize_streamable_http_server_config_with_headers302–324 ↗
fn deserialize_streamable_http_server_config_with_headers()

作用:测试 HTTP 配置可以携带请求头。请求头可以理解成每次发网络请求时附带的标签或凭证。

数据流:输入包含 url、http_headers 和 env_http_headers;解析后固定请求头 X-Foo 应该等于 bar,另一个请求头 X-Token 的值则应该标记为从 TOKEN_ENV 环境变量取得。

调用关系:它验证 HTTP 专属字段能正常进入配置。之后网络请求层会用这些字段组装真正发出去的 HTTP 请求。

调用图:外部调用 2 个(assert_eq!, from_str)。

deserialize_streamable_http_server_config_with_oauth_resource327–340 ↗
fn deserialize_streamable_http_server_config_with_oauth_resource()

作用:测试 HTTP MCP 配置可以读取 oauth_resource。OAuth 是一种常见登录授权方式,resource 用来说明要访问哪个受保护资源。

数据流:输入包含 url 和 oauth_resource;解析后 cfg.oauth_resource 应该保存为 https://api.example.com。

调用关系:它覆盖 HTTP 授权相关的资源字段。这个字段只对 HTTP 有意义,后面的拒绝测试会确认 stdio 不能乱用它。

调用图:外部调用 2 个(assert_eq!, from_str)。

deserialize_streamable_http_server_config_with_oauth_client_id343–360 ↗
fn deserialize_streamable_http_server_config_with_oauth_client_id()

作用:测试 HTTP 配置里的 [oauth] 小节可以读取 client_id。client_id 是 OAuth 登录时用来表明“我是哪个客户端”的公开标识。

数据流:输入包含 url 和 oauth.client_id;解析后 cfg.oauth 应该变成一个带 client_id 的 McpServerOAuthConfig。

调用关系:它测试 OAuth 配置块的读取,和 oauth_resource 测试一起覆盖 HTTP 授权配置的主要入口。

调用图:外部调用 2 个(assert_eq!, from_str)。

deserialize_server_config_with_tool_filters363–375 ↗
fn deserialize_server_config_with_tool_filters()

作用:测试可以给某个 MCP 服务器设置工具白名单和黑名单。白名单是只允许哪些工具,黑名单是禁止哪些工具。

数据流:输入包含 command、enabled_tools = ["allowed"] 和 disabled_tools = ["blocked"];解析后两个字段应该分别保存对应的工具名列表。

调用关系:它覆盖服务内工具过滤配置。后续调用工具前,运行逻辑可以根据这些列表判断某个工具能不能用。

调用图:外部调用 2 个(assert_eq!, from_str)。

deserialize_server_config_with_parallel_tool_calls378–388 ↗
fn deserialize_server_config_with_parallel_tool_calls()

作用:测试 supports_parallel_tool_calls = true 能被读取。这个开关表示该服务器是否支持同时跑多个工具调用。

数据流:输入包含 command 和 supports_parallel_tool_calls = true;解析后 cfg.supports_parallel_tool_calls 应该为 true。

调用关系:它为并发工具调用能力做读取测试。调度工具调用的代码之后会依赖这个值决定能不能并行发送请求。

调用图:外部调用 2 个(assert!, from_str)。

deserialize_server_config_with_default_tool_approval_mode391–420 ↗
fn deserialize_server_config_with_default_tool_approval_mode()

作用:测试默认工具审批模式和单个工具的审批模式都能读写。审批模式就是工具执行前要不要自动同意、还是先问用户。

数据流:输入包含 default_tools_approval_mode = "approve",并给 tools.search 单独设置 approval_mode = "prompt";解析后默认值应是 Approve,search 工具应是 Prompt;接着再序列化成 TOML 并读回来,结果必须和原配置相同。

调用关系:它不只测读取,还测写回再读取的往返过程。这样可以保证配置编辑、保存、再加载时不会丢掉审批设置。

调用图:外部调用 4 个(assert!, assert_eq!, from_str, to_string)。

serialize_round_trips_server_config_with_parallel_tool_calls423–439 ↗
fn serialize_round_trips_server_config_with_parallel_tool_calls()

作用:测试支持并行工具调用和工具超时设置在写回 TOML 后不会丢。往返测试能发现“读得进但写不出”的隐藏问题。

数据流:输入包含 command、supports_parallel_tool_calls = true 和 tool_timeout_sec = 2.0;解析后再序列化成字符串;测试确认字符串里有并行开关,再读回 McpServerConfig,最终应与原配置完全相等。

调用关系:它和审批模式的往返测试一样,验证配置的保存能力。配置管理界面或自动修改配置的代码会依赖这种稳定的序列化行为。

调用图:外部调用 4 个(assert!, assert_eq!, from_str, to_string)。

deserialize_ignores_unknown_server_fields442–477 ↗
fn deserialize_ignores_unknown_server_fields()

作用:测试配置里出现未知字段时会被忽略,而不是直接失败。这样旧版本或新版本之间多出来的小字段不一定会把整个配置弄坏。

数据流:输入包含 command 和未知字段 trust_level;解析后应该得到一个标准默认的 stdio 配置,trust_level 不进入结果,其它默认值都明确保持为预期状态。

调用关系:它验证配置格式的兼容性策略。用户配置里有程序暂时不认识的字段时,这条规则能让基本功能继续工作。

调用图:外部调用 2 个(assert_eq!, from_str)。

deserialize_rejects_command_and_url480–488 ↗
fn deserialize_rejects_command_and_url()

作用:测试同一个 MCP 服务器不能同时写 command 和 url。因为这两个字段分别代表本地命令和 HTTP 地址,二选一才说得清怎么连接。

数据流:输入同时包含 command = "echo" 和 url = "https://example.com";解析应该失败,不应该生成一个含糊的配置。

调用关系:它守住传输方式选择的边界。前面的 stdio 和 HTTP 测试分别证明单独写能用,这里证明混着写会被拦下。

deserialize_rejects_env_for_http_transport491–499 ↗
fn deserialize_rejects_env_for_http_transport()

作用:测试 HTTP 传输不能使用 stdio 专用的 env 字段。env 是启动本地命令时给进程的环境变量,HTTP 网址没有这个启动过程。

数据流:输入包含 url 和 env;解析应该失败,避免把本地进程字段错误套到网络服务上。

调用关系:它是传输方式字段隔离测试的一部分。HTTP 能用请求头和令牌环境变量,但不能用 stdio 的进程环境变量配置。

deserialize_rejects_headers_for_stdio502–545 ↗
fn deserialize_rejects_headers_for_stdio()

作用:测试 stdio 传输不能使用 HTTP 专用字段,比如请求头、环境请求头、OAuth 配置和 OAuth 资源。这样配置不会把两种连接方式混在一起。

数据流:它依次输入几段 command 配置,并分别附加 http_headers、env_http_headers、oauth、oauth_resource;每段都应该解析失败;对 oauth 和 oauth_resource,还检查错误文字明确说明 stdio 不支持。

调用关系:它和 HTTP 拒绝 env 的测试互为镜像。两者一起保证 stdio 字段和 HTTP 字段各归各位,解析阶段就发现写错位置的问题。

调用图:外部调用 1 个(assert!)。

deserialize_rejects_inline_bearer_token_field548–561 ↗
fn deserialize_rejects_inline_bearer_token_field()

作用:测试配置里不能直接写 bearer_token = "secret"。这是为了避免把敏感令牌明文放进配置文件,降低泄露风险。

数据流:输入包含 url 和 bearer_token 明文值;解析应该失败;测试检查错误信息里包含 bearer_token is not supported,提示用户这种写法不被支持。

调用关系:它补充 HTTP 认证配置的安全规则。前面允许 bearer_token_env_var,是因为只保存环境变量名;这里禁止直接保存真正密钥。

调用图:外部调用 1 个(assert!)。

config/src/mcp_edit_tests.rs源码 ↗
testtest

这个文件不是正式功能代码,而是两组自动测试。MCP 服务器可以理解成外部工具服务,配置里会记录怎么连接它、哪些工具能用、工具调用要不要人工批准,以及 OAuth(一种常见的网页登录授权方式)相关信息。测试的做法很像临时搭一个空白用户目录:先造一份服务器配置,交给 ConfigEditsBuilder 写入配置文件;再直接读取生成的 TOML 文件,检查文字内容是不是完全符合预期;最后再用正式的加载函数把文件读回来,确认读出的配置和一开始那份一模一样。这样同时检查了“写出去的格式”和“还能不能读回来”两件事。每个测试都会用当前时间和进程号生成独立临时目录,避免不同测试互相踩到,结束后再删除。

函数细节2
replace_mcp_servers_serializes_per_tool_approval_overrides10–86 ↗
async fn replace_mcp_servers_serializes_per_tool_approval_overrides() -> anyhow::Result<()>

作用:这个测试确认:当某个 MCP 服务器下面的不同工具有各自的审批规则时,程序会把这些规则正确写进配置文件。也就是说,“默认自动批准”和“某个工具必须询问/批准”不会在保存时丢掉。

数据流:进去的是测试临时创建的一个配置目录,以及一份名叫 docs 的 MCP 服务器配置;这份配置里有启动命令、并行调用开关、默认工具审批模式,还有 search 和 read 两个工具各自的审批模式。测试把这些配置交给 ConfigEditsBuilder 写入 config.toml,然后读取磁盘上的文本,和预先写好的标准答案逐字比较。接着它再调用加载函数把配置读回内存,确认读回来的结构和原始配置完全一致。最后,它删除临时目录,测试成功时返回 Ok。

调用关系:它是 Tokio 异步测试运行器在测试阶段直接调用的用例。流程上,它先通过 ConfigEditsBuilder::new 创建一个配置编辑器,再让编辑器替换 MCP 服务器配置并写入文件;随后用标准文件读取函数检查落盘内容,并用 load_global_mcp_servers 验证正式读取路径也能理解这些内容。pretty_assertions 的 assert_eq! 用来在失败时给出更清楚的差异。

调用图:调用 1 个内部函数(new);外部调用 9 个(from, from, now, new, assert_eq!, format!, temp_dir, read_to_string, remove_dir_all)。

replace_mcp_servers_serializes_oauth_client_id89–146 ↗
async fn replace_mcp_servers_serializes_oauth_client_id() -> anyhow::Result<()>

作用:这个测试确认:带 OAuth 客户端编号的 MCP 服务器配置,在保存成配置文件时会写到正确的位置。它保护的是登录授权相关配置,避免用户授权信息因为保存格式错误而失效。

数据流:进去的是一个新建的临时配置目录,以及一份名叫 maas_outlook 的 MCP 服务器配置;这个服务器通过 HTTP 地址连接,并带有 oauth.client_id。测试让 ConfigEditsBuilder 把它写进 config.toml,然后读取生成的文件,检查里面是否有服务器 URL 和单独的 oauth 小节,并且 client_id 完全正确。之后再用 load_global_mcp_servers 从磁盘读回配置,和最初的配置做整体比较。最后清理临时目录并返回 Ok。

调用关系:它同样由 Tokio 的异步测试框架在跑测试时触发。它把主要工作交给 ConfigEditsBuilder::new 创建的编辑器来写配置,再用文件读取和 assert_eq! 做结果核对;最后用 load_global_mcp_servers 走一遍真实加载流程,确保写入方和读取方对 OAuth 配置的格式理解一致。

调用图:调用 1 个内部函数(new);外部调用 8 个(from, new, now, assert_eq!, format!, temp_dir, read_to_string, remove_dir_all)。

config/src/state_tests.rs源码 ↗
testtest run

这个文件不负责真正加载配置,而是专门验证配置系统的几个关键承诺。第一,老配置键名要能自动换成新的标准键名,这样老用户的配置不会突然失效,系统记录配置来源时也只记新名字。第二,当同时有基础用户配置和某个 profile(配置档,比如“工作模式”)的配置时,系统要把 profile 文件当成当前最重要的用户配置文件。第三,如果只更新基础配置文件,不能顺手把当前激活的 profile 文件替换掉;更新后还要把基础配置和 profile 配置正确合在一起。测试里用临时目录造出假的配置文件路径,用 TOML(一种常见的配置文件格式)文本造出配置内容,然后检查最后看到的结果是否符合预期。

函数细节4
test_user_config_path5–8 ↗
fn test_user_config_path(temp_dir: &TempDir, file_name: &str) -> AbsolutePathBuf

作用:这个小工具函数给测试用的临时目录拼出一个“绝对路径”的配置文件名。这样测试不用碰真实用户的配置文件,也能模拟真实配置文件路径。

数据流:进去的是一个临时目录和一个文件名 → 它把两者拼成完整路径,并确认这个路径是绝对路径 → 出来的是一个 AbsolutePathBuf,也就是被系统认可的绝对配置文件路径;它不改动文件内容,只准备测试用路径。

调用关系:它是两个用户配置相关测试的辅助零件。active_user_layer_is_highest_precedence_user_layer 和 with_user_config_updates_matching_user_layer_without_replacing_active_profile 会先调用它造出 base 配置和 profile 配置的假路径,再用这些路径搭建配置层。

调用图:调用 1 个内部函数(from_absolute_path);被 2 处调用(active_user_layer_is_highest_precedence_user_layer, with_user_config_updates_matching_user_layer_without_replacing_active_profile);外部调用 1 个(path)。

origins_use_canonical_key_aliases11–40 ↗
fn origins_use_canonical_key_aliases()

作用:这个测试确认:如果配置里用了旧名字,系统记录“这个配置从哪里来”时,会先换成新的标准名字。这样之后查来源时不会被新旧两个名字弄乱。

数据流:进去的是一段 TOML 配置文本,里面写了旧键名 memories.no_memories_if_mcp_or_web_search → 测试把它放进一个配置层,再创建配置层栈,并取出来源记录 origins → 最后检查来源里有新键名 memories.disable_on_external_context,没有旧键名;如果结果不对,测试失败。

调用关系:这个测试直接使用 ConfigLayerEntry::new 创建配置层,再用 ConfigLayerStack::new 触发配置标准化和来源记录。它主要保护“旧配置键名兼容新配置键名”这条规则,避免后续改配置合并逻辑时破坏兼容性。

调用图:调用 2 个内部函数(new, new);外部调用 6 个(default, assert!, assert_eq!, default, from_str, vec!)。

active_user_layer_is_highest_precedence_user_layer43–91 ↗
fn active_user_layer_is_highest_precedence_user_layer()

作用:这个测试确认:当有多个用户配置层时,当前活跃的用户配置文件应该是优先级最高的那个,比如某个 profile 的配置文件。它还确认合并后的用户配置既包含 profile 覆盖的值,也保留基础配置里的值。

数据流:进去的是测试临时目录中模拟出来的两个配置文件路径,以及两段 TOML 配置:基础配置写 model 和 approval_policy,profile 配置只写 model → 测试把它们做成两个用户配置层并组成配置层栈 → 出来要看到当前用户配置文件是 profile 文件,合并后的 model 来自 profile,approval_policy 仍来自基础配置。

调用关系:它调用 test_user_config_path 准备假文件路径,用 ConfigLayerEntry::new 包装每一层配置,再交给 ConfigLayerStack::new 合并。这个测试位于用户配置选择流程的验证端,专门防止系统误把基础配置文件当成当前活跃文件。

调用图:调用 3 个内部函数(new, new, test_user_config_path);外部调用 6 个(new, default, assert_eq!, default, from_str, vec!)。

with_user_config_updates_matching_user_layer_without_replacing_active_profile94–141 ↗
fn with_user_config_updates_matching_user_layer_without_replacing_active_profile()

作用:这个测试确认:更新某个指定的用户配置文件时,只改匹配的那一层,不会把当前激活的 profile 配置文件换掉。它保护的是“改一个文件,不要牵连另一个文件”的行为。

数据流:进去的是基础配置文件路径、profile 配置文件路径、原始两层配置,以及一段新的基础配置 TOML → 测试先建好配置层栈,再调用 with_user_config 用新内容替换基础配置那一层 → 出来要看到当前用户配置文件仍然是 profile 文件,同时合并结果里的 model 变成 updated-base,profile 里的 approval_policy 仍然保留。

调用关系:它和前一个测试一样先借助 test_user_config_path 造路径,再创建配置层栈;不同的是它接着调用 stack.with_user_config 模拟“用户保存了某个配置文件”。这个测试保护更新流程,确保更新基础配置不会破坏 profile 的激活状态。

调用图:调用 3 个内部函数(new, new, test_user_config_path);外部调用 6 个(new, default, assert_eq!, default, from_str, vec!)。

config/src/loader/tests.rs源码 ↗
testtest time

这个测试文件像一套“安检题”。它先做了一个很小的测试用文件系统,只支持把路径变成真实路径、读取文件,其他写入、删除、复制等操作都故意不实现,因为这些测试只需要读配置。然后每个测试都会建一个临时目录,往里面写不同内容的 config.toml 和 work.config.toml,再调用配置加载流程。重点检查一件事:当用户用新版方式选择 work 配置文件时,基础 config.toml 里如果也出现旧版的 work profile,加载器必须报错;如果只是别的名字,比如 dev,就应该放行。这样可以避免“同一个 profile 到底该听谁的”这种混乱,就像同一张工单不能同时有两套互相冲突的编号规则。

函数细节12
TestFileSystem::canonicalize17–27 ↗
fn canonicalize(
        &'a self,
        path: &'a PathUri,
        _sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, PathUri>

作用:这个函数把测试里传进来的路径变成操作系统认可的真实绝对路径。配置加载器需要确认文件位置时,会用它来模拟真实文件系统的行为。

数据流:输入是一个 PathUri 路径和一个这里不用的沙箱参数 → 它先把 PathUri 转成普通绝对路径,再调用系统的路径规范化能力,处理掉符号链接、相对片段等 → 输出一个新的 PathUri,指向规范化后的真实路径;它不改动文件内容。

调用关系:它是 TestFileSystem 给配置加载器提供的读文件环境的一部分。加载流程需要确认配置文件位置时会走到这里;它内部把活交给路径转换和系统 canonicalize,再把结果包装回 PathUri。

调用图:调用 2 个内部函数(from_abs_path, to_abs_path);外部调用 2 个(pin, canonicalize)。

TestFileSystem::read_file29–38 ↗
fn read_file(
        &'a self,
        path: &'a PathUri,
        _sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, Vec<u8>>

作用:这个函数负责在测试中读取配置文件内容。测试写好的 config.toml 和 work.config.toml,最后就是靠它被配置加载器读进去。

数据流:输入是一个 PathUri 路径和一个这里不用的沙箱参数 → 它把路径转成普通绝对路径,再交给 tokio 的异步读文件功能读取字节 → 输出文件的原始字节内容;如果文件不存在或读不了,就返回系统错误。

调用关系:它是测试文件系统里真正被大量需要的能力。配置加载流程要读用户配置时会通过这个接口拿内容;它自己不解析配置,只负责把磁盘上的字节交出去。

调用图:调用 1 个内部函数(to_abs_path);外部调用 3 个(pin, read, as_path)。

TestFileSystem::read_file_stream40–51 ↗
fn read_file_stream(
        &'a self,
        _path: &'a PathUri,
        _sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, FileSystemReadStream>

作用:这个函数表示“流式读取文件”在这个测试文件系统里不支持。流式读取就是一点一点读大文件,但这些测试只需要一次性读小配置文件。

数据流:输入是路径和沙箱参数,但都不会真正使用 → 它直接构造一个“不支持”的输入输出错误 → 输出失败结果,不返回文件流,也不改动任何东西。

调用关系:它只是为了满足 ExecutorFileSystem 这个接口必须提供的函数。当前这些配置加载测试不应该依赖流式读取;如果误用了它,测试会立刻得到明确错误。

调用图:外部调用 2 个(pin, new)。

TestFileSystem::write_file53–60 ↗
fn write_file(
        &'a self,
        _path: &'a PathUri,
        _contents: Vec<u8>,
        _sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, ()>

作用:这个函数故意不提供写文件能力。测试里的文件是直接用标准库先写好的,配置加载器在这里不应该写文件。

数据流:输入是目标路径、要写入的字节和沙箱参数 → 函数进入后直接触发 unimplemented,也就是“这里不该被调用”的占位错误 → 没有正常输出,也不会写任何文件。

调用关系:它是为了凑齐文件系统接口而存在的防护栏。配置加载测试只应该读取配置;如果加载流程意外想写文件,就会在这里暴露问题。

调用图:外部调用 2 个(pin, unimplemented!)。

TestFileSystem::create_directory62–69 ↗
fn create_directory(
        &'a self,
        _path: &'a PathUri,
        _create_directory_options: CreateDirectoryOptions,
        _sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorF

作用:这个函数故意不支持创建目录。因为这些测试只验证读取和解析配置,不需要配置加载器帮忙建目录。

数据流:输入是目录路径、创建选项和沙箱参数 → 它不做实际创建,直接触发“未实现”的错误 → 没有正常结果,磁盘目录不会被改动。

调用关系:它和其他未实现函数一样,是测试替身的一部分。若配置加载器在这个测试场景里错误地尝试创建目录,测试会马上失败,提醒开发者行为跑偏了。

调用图:外部调用 2 个(pin, unimplemented!)。

TestFileSystem::get_metadata71–77 ↗
fn get_metadata(
        &'a self,
        _path: &'a PathUri,
        _sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, FileMetadata>

作用:这个函数故意不支持读取文件元数据。元数据就是文件大小、类型、修改时间这类信息;本测试不需要这些。

数据流:输入是路径和沙箱参数 → 它不访问磁盘,也不返回文件大小或类型 → 直接以未实现方式失败。

调用关系:它只是实现文件系统接口所需的占位。当前配置加载测试应该只关心文件内容;如果以后流程开始依赖元数据,这里会让测试显式失败,促使开发者更新测试文件系统。

调用图:外部调用 2 个(pin, unimplemented!)。

TestFileSystem::read_directory79–85 ↗
fn read_directory(
        &'a self,
        _path: &'a PathUri,
        _sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, Vec<ReadDirectoryEntry>>

作用:这个函数故意不支持列目录。列目录就是看看某个文件夹里有哪些文件;这些测试已经明确指定了配置文件路径,不需要扫描文件夹。

数据流:输入是目录路径和沙箱参数 → 它不读取目录项 → 直接触发未实现错误,没有正常输出。

调用关系:它是测试文件系统的接口占位。配置加载流程在这些测试中不该去枚举目录;如果发生了,测试会失败并暴露这个多余依赖。

调用图:外部调用 2 个(pin, unimplemented!)。

TestFileSystem::remove87–94 ↗
fn remove(
        &'a self,
        _path: &'a PathUri,
        _remove_options: RemoveOptions,
        _sandbox: Option<&'a FileSystemSandboxContext>,
    ) -> ExecutorFileSystemFuture<'a, ()>

作用:这个函数故意不支持删除文件或目录。配置加载器在读取配置时不应该清理用户文件。

数据流:输入是要删除的路径、删除选项和沙箱参数 → 它不碰磁盘 → 直接以未实现方式失败,不会删除任何东西。

调用关系:它是接口要求下的安全占位。这个测试场景只允许读,如果加载流程意外做删除操作,会在这里被拦住。

调用图:外部调用 2 个(pin, unimplemented!)。

TestFileSystem::copy96–104 ↗
fn copy(
        &'a self,
        _source_path: &'a PathUri,
        _destination_path: &'a PathUri,
        _copy_options: CopyOptions,
        _sandbox: Option<&'a FileSystemSandboxContext>,
    )

作用:这个函数故意不支持复制文件。配置读取测试不需要把配置文件从一个地方复制到另一个地方。

数据流:输入是来源路径、目标路径、复制选项和沙箱参数 → 它不执行复制 → 直接触发未实现错误,没有正常输出,也不改变文件系统。

调用关系:它补齐了 ExecutorFileSystem 接口,但不参与正常测试路径。它的存在像一个报警器:如果配置加载过程突然开始复制文件,测试会立刻告诉开发者。

调用图:外部调用 2 个(pin, unimplemented!)。

profile_v2_rejects_matching_legacy_profile_in_base_user_config108–165 ↗
async fn profile_v2_rejects_matching_legacy_profile_in_base_user_config()

作用:这个测试确认:如果用户用新版 profile 文件 work.config.toml,同时基础 config.toml 里又有旧版 [profiles.work],加载器必须报错。这样可以避免同名配置有两套来源,导致结果说不清。

数据流:开始时它创建临时目录 → 写入一个基础 config.toml,里面有 model 和旧版 [profiles.work],再写入一个新版 work.config.toml → 设置覆盖参数,让加载器选择 work 这个新版 profile → 调用配置加载后期待失败 → 最后检查错误类型和错误文字,确认提示里讲清了 --profile work 不能这样用、问题来自 config.toml 和 [profiles.work],并给出文档链接。

调用关系:它是直接驱动配置加载器的测试用例。它用 TestFileSystem 提供读取能力,用 without_managed_config_for_tests 关掉托管配置干扰,用 resolve_path_against_base 指定测试配置文件位置,然后把结果交给断言检查。

调用图:调用 2 个内部函数(without_managed_config_for_tests, resolve_path_against_base);外部调用 4 个(assert!, assert_eq!, write, tempdir)。

profile_v2_rejects_matching_legacy_profile_selector_in_base_user_config168–219 ↗
async fn profile_v2_rejects_matching_legacy_profile_selector_in_base_user_config()

作用:这个测试确认:如果基础 config.toml 里写了旧版的 profile = "work",而用户又用新版方式选择 work.config.toml,也必须报错。原因同样是避免新旧两套选择规则指向同一个名字。

数据流:它先创建临时目录 → 写入基础 config.toml,其中包含 profile = "work" 和默认 model,再写入 work.config.toml → 设置加载覆盖项,让当前用户配置路径指向 work.config.toml,并指定 profile 名为 work → 调用配置加载并期待错误 → 最后检查错误类型是 InvalidData,错误信息里包含 --profile work、profile = "work" 和 work.config.toml。

调用关系:它覆盖的是另一种旧版冲突写法:不是 [profiles.work] 小节,而是 profile 字段。它和上一个测试一起保证配置加载器不会在新版 profile-v2 场景下接受同名旧规则。

调用图:调用 2 个内部函数(without_managed_config_for_tests, resolve_path_against_base);外部调用 4 个(assert!, assert_eq!, write, tempdir)。

profile_v2_allows_unrelated_legacy_profiles_in_base_user_config222–256 ↗
async fn profile_v2_allows_unrelated_legacy_profiles_in_base_user_config()

作用:这个测试确认:基础 config.toml 里有旧版 profile 也不一定错,只要名字和当前新版 profile 不冲突就可以。比如当前选 work,而旧配置里只有 dev,就应该正常加载。

数据流:它创建临时目录 → 写入基础 config.toml,其中有默认 model 和旧版 [profiles.dev],再写入 work.config.toml → 设置覆盖项选择新版 work 配置 → 调用配置加载 → 期待成功,不做额外错误检查。

调用关系:它是前两个拒绝测试的平衡项,防止规则做得太严。它告诉配置加载器:真正要拦的是“同名冲突”,不是看到任何旧版 profile 都报错。

调用图:调用 2 个内部函数(without_managed_config_for_tests, resolve_path_against_base);外部调用 2 个(write, tempdir)。

core/src/config/edit_tests.rs源码 ↗
testtest

这个文件像一套“配置文件改写器的验收清单”。测试会先建一个临时目录,当成假的 Codex 用户目录;有些测试还会先写入一份旧配置。然后它调用配置编辑功能,比如设置模型、服务档位、快捷键、提示隐藏状态、MCP 服务器配置、实时语音设备等。最后再把 config.toml 读出来,和期望的文字或解析后的 TOML 值对比。TOML 是一种常见配置文件格式,长得像 key = value。这里特别在意两件事:第一,只改根配置,不要误改旧的 profile(个人配置方案);第二,尽量保留用户手写的注释和表结构。Unix 系统下还测了符号链接,也就是“文件快捷方式”:正常链接要写到真正目标,死循环链接则要安全替换。

函数细节44
blocking_set_model_top_level17–35 ↗
fn blocking_set_model_top_level()

作用:检查直接设置顶层模型名和推理力度时,配置文件会写成最基本、正确的两行。

数据流:进去的是一个临时用户目录,以及要设置的模型 gpt-5.4 和 high 力度;测试执行配置写入;出来的是 config.toml 文件,内容必须正好包含 model 和 model_reasoning_effort 两项。

调用关系:这是配置编辑器最基础的用例之一,由测试运行器启动;它通过临时目录隔离真实用户文件,再用读取文件和断言确认写入结果。

调用图:外部调用 3 个(assert_eq!, read_to_string, tempdir)。

set_service_tier_saves_default_as_default38–49 ↗
fn set_service_tier_saves_default_as_default()

作用:检查把服务档位设成默认请求值时,文件里保存的是清楚的 default。

数据流:进去的是临时目录和默认服务档位字符串;构建器把它变成一次配置修改并落盘;出来的 config.toml 应该只有 service_tier = "default"。

调用关系:测试运行器调用它;它走 ConfigEditsBuilder 这条更接近日常使用的入口,确认服务档位写入没有绕错格式。

调用图:调用 1 个内部函数(new);外部调用 3 个(assert_eq!, read_to_string, tempdir)。

set_service_tier_saves_priority_as_fast52–63 ↗
fn set_service_tier_saves_priority_as_fast()

作用:检查“快速/优先”服务档位会按系统约定写成 fast。

数据流:进去的是临时目录和 ServiceTier::Fast 对应的请求值;配置构建器写入文件;出来的文本必须是 service_tier = "fast"。

调用关系:它验证服务档位枚举值和配置文件文字之间的转换,由测试运行器在测试阶段自动执行。

调用图:调用 1 个内部函数(new);外部调用 3 个(assert_eq!, read_to_string, tempdir)。

set_service_tier_preserves_unknown_service_tier66–77 ↗
fn set_service_tier_preserves_unknown_service_tier()

作用:检查遇到程序暂时不认识的服务档位时,不会自作主张改掉它。

数据流:进去的是 experimental-tier-id 这样的未知字符串;配置编辑器原样保存;出来的 config.toml 也必须原样写着这个值。

调用关系:这个测试保护向前兼容性:如果服务端以后新增档位,旧客户端也不应该把用户选择弄丢。

调用图:调用 1 个内部函数(new);外部调用 3 个(assert_eq!, read_to_string, tempdir)。

builder_with_edits_applies_custom_paths80–94 ↗
fn builder_with_edits_applies_custom_paths()

作用:检查构建器能接受一组自定义配置修改,并按指定路径写入。

数据流:进去的是路径 enabled 和布尔值 true;构建器收下这条 SetPath 修改并保存;出来的配置文件包含 enabled = true。

调用关系:它覆盖 ConfigEditsBuilder 的通用入口 with_edits,说明除了专门方法,也能直接塞入底层编辑动作。

调用图:调用 1 个内部函数(new);外部调用 4 个(assert_eq!, read_to_string, tempdir, vec!)。

session_picker_view_edit_writes_root_tui_setting97–111 ↗
fn session_picker_view_edit_writes_root_tui_setting()

作用:检查会话选择器的显示模式会写到根部的 tui 表里,而不是别的配置分支。

数据流:进去的是 Dense 显示模式;辅助函数生成一次配置编辑;出来的文件有 [tui] 表和 session_picker_view = "dense"。

调用关系:测试运行器触发它;它验证 UI 设置相关的辅助编辑函数和实际落盘格式能对上。

调用图:调用 1 个内部函数(new);外部调用 3 个(assert_eq!, read_to_string, tempdir)。

keymap_binding_edit_writes_root_action_binding114–128 ↗
fn keymap_binding_edit_writes_root_action_binding()

作用:检查单个快捷键绑定会写到正确的 TUI 键位表下面。

数据流:进去的是区域 composer、动作 submit、按键 ctrl-enter;编辑器写入嵌套表;出来是 [tui.keymap.composer] 里的 submit = "ctrl-enter"。

调用关系:它测试快捷键编辑辅助函数,确保根配置里的键位改动不会走偏。

调用图:调用 1 个内部函数(new);外部调用 3 个(assert_eq!, read_to_string, tempdir)。

keymap_bindings_edit_writes_single_binding_as_string131–149 ↗
fn keymap_bindings_edit_writes_single_binding_as_string()

作用:检查只有一个快捷键时,配置保存成简单字符串,而不是数组。

数据流:进去的是只包含 ctrl-enter 的按键列表;编辑器判断只有一项并写成字符串;出来的 TOML 更简洁,submit 等于一个字符串。

调用关系:它和多快捷键测试配套,保证同一个接口在不同数量输入下写出符合约定的格式。

调用图:调用 1 个内部函数(new);外部调用 3 个(assert_eq!, read_to_string, tempdir)。

keymap_bindings_edit_writes_multiple_bindings_as_array152–183 ↗
fn keymap_bindings_edit_writes_multiple_bindings_as_array()

作用:检查一个动作有多个快捷键时,会保存成数组,也就是一串值的列表。

数据流:进去的是 enter 和 ctrl-enter 两个按键;编辑器写入配置;测试再把 TOML 解析回来,确认 submit 是包含这两个字符串的数组。

调用关系:它补齐快捷键列表场景,调用 TOML 解析来避免只靠文本格式判断。

调用图:调用 1 个内部函数(new);外部调用 4 个(assert_eq!, read_to_string, tempdir, from_str)。

keymap_binding_edit_replaces_existing_binding_without_touching_profile186–230 ↗
fn keymap_binding_edit_replaces_existing_binding_without_touching_profile()

作用:检查替换根快捷键时,不会误改某个 profile 里的快捷键。

数据流:进去的是一份已有根快捷键和 team profile 快捷键的配置;编辑器只把根 submit 改成 ctrl-enter;出来时 profile 里的 shift-enter 仍然保留。

调用关系:它保护“根配置”和“个人配置方案”之间的边界,防止一次普通设置影响团队或个人专用配置。

调用图:调用 1 个内部函数(new);外部调用 5 个(assert_eq!, read_to_string, write, tempdir, from_str)。

keymap_binding_clear_edit_removes_root_action_binding_without_touching_profile233–276 ↗
fn keymap_binding_clear_edit_removes_root_action_binding_without_touching_profile()

作用:检查清除根快捷键时,只删根部那一项,不碰 profile 里的同名项。

数据流:进去的是同时有根快捷键和 profile 快捷键的配置;清除动作移除根 submit;出来时根部查不到 submit,但 profile 里的 submit 仍是 shift-enter。

调用关系:它和替换快捷键测试互相补充,确认删除操作也遵守同样的边界。

调用图:调用 1 个内部函数(new);外部调用 5 个(assert_eq!, read_to_string, write, tempdir, from_str)。

set_model_availability_nux_count_writes_shown_count279–294 ↗
fn set_model_availability_nux_count_writes_shown_count()

作用:检查模型可用性提示已经展示过几次,会写进 TUI 配置。

数据流:进去的是一个映射表,记录 gpt-foo 显示次数为 4;构建器写入配置;出来是 [tui.model_availability_nux] 下 gpt-foo = 4。

调用关系:它验证新手提示类状态可以被保存,避免界面重复打扰用户。

调用图:调用 1 个内部函数(new);外部调用 4 个(from, assert_eq!, read_to_string, tempdir)。

set_skill_config_writes_disabled_entry297–315 ↗
fn set_skill_config_writes_disabled_entry()

作用:检查按文件路径禁用某个 skill 时,会写入一条 disabled 配置。

数据流:进去的是 SKILL.md 的路径和 enabled = false;编辑器写入 skills.config 数组表;出来的配置记录这个路径被禁用。

调用关系:它测试技能开关的持久化格式,让后续加载配置时能知道某个技能被用户关掉了。

调用图:调用 1 个内部函数(new);外部调用 4 个(from, assert_eq!, read_to_string, tempdir)。

set_skill_config_removes_entry_when_enabled318–340 ↗
fn set_skill_config_removes_entry_when_enabled()

作用:检查把某个按路径禁用的 skill 重新启用时,会删掉那条特殊配置。

数据流:进去的是一份已禁用某 skill 的配置,再传入 enabled = true;编辑器移除这条覆盖记录;出来的 config.toml 为空。

调用关系:它说明默认状态是启用,所以启用时不需要留下多余记录;测试保护配置文件不被无意义内容堆满。

调用图:调用 1 个内部函数(new);外部调用 5 个(from, assert_eq!, read_to_string, write, tempdir)。

set_skill_config_writes_name_selector_entry343–361 ↗
fn set_skill_config_writes_name_selector_entry()

作用:检查也可以按 skill 名称而不是文件路径来写禁用配置。

数据流:进去的是名称 github:yeet 和 enabled = false;编辑器写入 skills.config 数组表;出来的配置用 name 字段标识被禁用的技能。

调用关系:它覆盖另一种选择 skill 的方式,确保路径选择和名称选择都能保存。

调用图:调用 1 个内部函数(new);外部调用 3 个(assert_eq!, read_to_string, tempdir)。

blocking_set_model_ignores_inline_legacy_profile_contents364–412 ↗
fn blocking_set_model_ignores_inline_legacy_profile_contents()

作用:检查设置根模型时,不会钻进旧式内联 profile 里改内容。

数据流:进去的是带 profile = fast 和内联 profiles 表的旧配置;编辑器只在根部写 model = o4-mini;出来时 profiles.fast 里的旧 model 和 sandbox_mode 都没变。

调用关系:它保护旧配置兼容性,防止用户历史 profile 被新的根级设置意外覆盖。

调用图:外部调用 5 个(assert_eq!, read_to_string, write, tempdir, from_str)。

batch_write_table_upsert_preserves_inline_comments478–535 ↗
fn batch_write_table_upsert_preserves_inline_comments()

作用:检查一次批量更新多个表里的值时,原来的注释不会丢。

数据流:进去的是带注释的 MCP 服务器和沙箱配置;编辑器更新 url 和 network_access;出来的文本值变了,但 # ok 这类注释仍在原处。

调用关系:它验证批量写入不是粗暴重写整个文件,而是尽量尊重用户手写配置。

调用图:外部调用 5 个(assert_eq!, read_to_string, write, tempdir, vec!)。

blocking_clear_model_does_not_follow_legacy_active_profile538–567 ↗
fn blocking_clear_model_does_not_follow_legacy_active_profile()

作用:检查清除根模型时,不会因为当前 profile 指向 fast 就去改 fast 的旧配置。

数据流:进去的是旧式 profile 配置和一次 model = None、effort = high 的修改;编辑器不动 profile,只在根部增加 model_reasoning_effort;出来的旧 profiles 内容保持原样。

调用关系:它继续保护根设置和旧 profile 的隔离,尤其覆盖“清除模型”这种容易误判的情况。

调用图:外部调用 4 个(assert_eq!, read_to_string, write, tempdir)。

blocking_set_model_does_not_follow_legacy_active_profile570–601 ↗
fn blocking_set_model_does_not_follow_legacy_active_profile()

作用:检查设置模型和推理力度时,只写根部,不跟随当前激活的 legacy profile。

数据流:进去的是 profile = team 且 team 里已有 low 力度的配置;编辑器在根部写 o5-preview 和 minimal;出来时 profile 里的 low 不变。

调用关系:它和前一个测试一起说明:无论设置还是清除,根级编辑都不能偷偷改 profile。

调用图:外部调用 4 个(assert_eq!, read_to_string, write, tempdir)。

blocking_set_hide_full_access_warning_preserves_table604–633 ↗
fn blocking_set_hide_full_access_warning_preserves_table()

作用:检查隐藏“完全访问权限警告”的标记会加进 notice 表,同时保留原表内容和注释。

数据流:进去的是已有 [notice] 表、注释和 existing 值的配置;编辑器加入 hide_full_access_warning = true;出来时旧内容仍在,新标记追加进去。

调用关系:它测试通知类设置的写入,重点是不要为了加一个开关而重建整张表。

调用图:外部调用 5 个(SetNoticeHideFullAccessWarning, assert_eq!, read_to_string, write, tempdir)。

blocking_set_hide_rate_limit_model_nudge_preserves_table636–659 ↗
fn blocking_set_hide_rate_limit_model_nudge_preserves_table()

作用:检查隐藏“速率限制模型提示”的开关会正确追加到 notice 表。

数据流:进去的是已有 notice.existing 的配置;编辑器写入 hide_rate_limit_model_nudge = true;出来时 existing 还在,新开关也在。

调用关系:它覆盖 notice 表里的另一个布尔开关,保证同类提示状态都能安全保存。

调用图:外部调用 5 个(SetNoticeHideRateLimitModelNudge, assert_eq!, read_to_string, write, tempdir)。

blocking_set_hide_gpt5_1_migration_prompt_preserves_table662–687 ↗
fn blocking_set_hide_gpt5_1_migration_prompt_preserves_table()

作用:检查隐藏某个模型迁移提示的开关会写入 notice 表,普通键名不用加引号。

数据流:进去的是已有 notice 表和键名 hide_gpt5_1_migration_prompt;编辑器把它设为 true;出来时 notice 表保留旧值并新增这个布尔项。

调用关系:它验证模型迁移提示的记录方式,确保简单键名输出成常规 TOML 写法。

调用图:外部调用 5 个(SetNoticeHideModelMigrationPrompt, assert_eq!, read_to_string, write, tempdir)。

blocking_set_hide_gpt_5_1_codex_max_migration_prompt_preserves_table690–715 ↗
fn blocking_set_hide_gpt_5_1_codex_max_migration_prompt_preserves_table()

作用:检查键名里带点号和横线时,写 TOML 会自动加引号,避免被误解成多层路径。

数据流:进去的是特殊键名 hide_gpt-5.1-codex-max_migration_prompt 和 true;编辑器写入 notice 表;出来时键名被引号包住,表示它是一个完整名字。

调用关系:它保护 TOML 语法细节:带特殊字符的提示开关不能被拆错层级。

调用图:外部调用 5 个(SetNoticeHideModelMigrationPrompt, assert_eq!, read_to_string, write, tempdir)。

blocking_record_model_migration_seen_preserves_table718–745 ↗
fn blocking_record_model_migration_seen_preserves_table()

作用:检查记录“用户已经看过从某模型迁移到某模型”的信息时,会放进 notice.model_migrations 表。

数据流:进去的是 from = gpt-5.2、to = gpt-5.4 和已有 notice 表;编辑器新增子表记录;出来时 existing 保留,并多出 "gpt-5.2" = "gpt-5.4"。

调用关系:它服务于迁移提示去重,避免用户反复看到同一条模型迁移提醒。

调用图:外部调用 4 个(assert_eq!, read_to_string, write, tempdir)。

blocking_set_hide_external_config_migration_prompt_home_preserves_table748–774 ↗
fn blocking_set_hide_external_config_migration_prompt_home_preserves_table()

作用:检查隐藏“主目录外部配置迁移提示”的状态会写到专门的 notice 子表。

数据流:进去的是已有 notice 表和 home = true 的意图;编辑器创建 notice.external_config_migration_prompts;出来时 home 标记在这个子表里。

调用关系:它测试外部配置迁移提示的首页级别状态,确保不污染 notice 表的其他字段。

调用图:外部调用 5 个(SetNoticeHideExternalConfigMigrationPromptHome, assert_eq!, read_to_string, write, tempdir)。

blocking_set_hide_external_config_migration_prompt_project_preserves_table777–806 ↗
fn blocking_set_hide_external_config_migration_prompt_project_preserves_table()

作用:检查按项目路径隐藏外部配置迁移提示时,路径会作为键保存。

数据流:进去的是一个项目路径和 true;编辑器写入 notice.external_config_migration_prompts.projects;出来时该路径对应 true。

调用关系:它覆盖项目级提示状态,让不同项目可以分别记住是否已经不再提醒。

调用图:外部调用 5 个(SetNoticeHideExternalConfigMigrationPromptProject, assert_eq!, read_to_string, write, tempdir)。

blocking_set_external_config_migration_prompt_home_last_prompted_at_preserves_table809–833 ↗
fn blocking_set_external_config_migration_prompt_home_last_prompted_at_preserves_table()

作用:检查记录主目录迁移提示上次弹出的时间戳时,会写到正确位置。

数据流:进去的是时间戳 1760000000;编辑器写入 notice.external_config_migration_prompts.home_last_prompted_at;出来时旧 notice 内容还在,新时间也在。

调用关系:它用于控制提醒频率,避免同一个提示太频繁出现。

调用图:外部调用 5 个(SetNoticeExternalConfigMigrationPromptHomeLastPromptedAt, assert_eq!, read_to_string, write, tempdir)。

blocking_set_external_config_migration_prompt_project_last_prompted_at_preserves_table836–865 ↗
fn blocking_set_external_config_migration_prompt_project_last_prompted_at_preserves_table()

作用:检查每个项目的外部配置迁移提示上次弹出时间可以单独保存。

数据流:进去的是项目路径和时间戳;编辑器写入 project_last_prompted_at 子表;出来时该路径对应这个时间值。

调用关系:它配合项目级隐藏状态使用,让提醒节奏可以按项目分别计算。

调用图:外部调用 5 个(SetNoticeExternalConfigMigrationPromptProjectLastPromptedAt, assert_eq!, read_to_string, write, tempdir)。

blocking_replace_mcp_servers_round_trips868–973 ↗
fn blocking_replace_mcp_servers_round_trips()

作用:检查整批替换 MCP 服务器配置时,各种字段都能按约定写成 TOML。

数据流:进去的是两个服务器配置:一个 stdio 类型、一个 HTTP 类型,包含环境变量、请求头、超时、工具白名单/黑名单、OAuth 等;编辑器写入文件;出来的文本必须和预期完全一致。

调用关系:它是 MCP 配置序列化的主测试,覆盖字段最多,保证复杂服务器设置保存后不会变形。

调用图:外部调用 8 个(new, new, ReplaceMcpServers, assert_eq!, read_to_string, from_secs, tempdir, vec!)。

blocking_replace_mcp_servers_serializes_tool_approval_overrides976–1025 ↗
fn blocking_replace_mcp_servers_serializes_tool_approval_overrides()

作用:检查 MCP 服务器的工具审批规则能保存,包括默认规则和单个工具覆盖规则。

数据流:进去的是 docs 服务器配置,默认工具审批为 prompt,search 工具审批为 approve;编辑器写入 TOML;出来时对应字段位于服务器表和 tools.search 子表中。

调用关系:它补充 MCP 的权限控制场景,保证细粒度工具审批不会在保存时丢失。

调用图:外部调用 7 个(new, from, new, ReplaceMcpServers, assert_eq!, read_to_string, tempdir)。

blocking_replace_mcp_servers_preserves_inline_comments1028–1076 ↗
fn blocking_replace_mcp_servers_preserves_inline_comments()

作用:检查替换 MCP 服务器时,如果配置内容本来等价,表里的注释会被保留下来。

数据流:进去的是带 # keep me 注释和内联 foo 服务器的配置;新服务器配置内容相同;出来的文件应保持原样,包括注释。

调用关系:它防止配置编辑器为了重写 MCP 而破坏用户注释,是“少动无关内容”的测试。

调用图:外部调用 8 个(new, new, new, ReplaceMcpServers, assert_eq!, read_to_string, write, tempdir)。

blocking_replace_mcp_servers_preserves_inline_comment_suffix1079–1125 ↗
fn blocking_replace_mcp_servers_preserves_inline_comment_suffix()

作用:检查 MCP 服务器内联表行尾注释在更新字段后仍然保留。

数据流:进去的是 foo = { command = "cmd" } # keep me;新配置把 enabled 改成 false;出来时内联表增加 enabled = false,行尾 # keep me 还在。

调用关系:它覆盖注释在同一行末尾的情况,确保更新值时不会截掉后面的说明文字。

调用图:外部调用 8 个(new, new, new, ReplaceMcpServers, assert_eq!, read_to_string, write, tempdir)。

blocking_replace_mcp_servers_preserves_inline_comment_after_removing_keys1128–1174 ↗
fn blocking_replace_mcp_servers_preserves_inline_comment_after_removing_keys()

作用:检查从 MCP 内联表删除字段时,行尾注释仍然不会丢。

数据流:进去的是带 args 字段和行尾注释的内联服务器配置;新配置没有 args;编辑器移除 args;出来时注释 # keep me 仍跟在这一行后面。

调用关系:它测试更难的编辑场景:不仅加字段,还要删字段,同时保住用户注释。

调用图:外部调用 8 个(new, new, new, ReplaceMcpServers, assert_eq!, read_to_string, write, tempdir)。

blocking_replace_mcp_servers_preserves_inline_comment_prefix_on_update1177–1225 ↗
fn blocking_replace_mcp_servers_preserves_inline_comment_prefix_on_update()

作用:检查 MCP 内联表前一行的注释,在更新服务器字段后还能留在原处。

数据流:进去的是 # keep me 后面跟 foo 内联服务器;新配置把 enabled 设为 false;出来时注释仍在 foo 前面,foo 行被正确更新。

调用关系:它和行尾注释测试一起,覆盖注释在字段前后的两种常见写法。

调用图:外部调用 8 个(new, new, new, ReplaceMcpServers, assert_eq!, read_to_string, write, tempdir)。

blocking_clear_path_noop_when_missing1228–1244 ↗
fn blocking_clear_path_noop_when_missing()

作用:检查清除一个不存在的配置路径时,什么都不做,也不凭空创建 config.toml。

数据流:进去的是空临时目录和要清除的 missing 路径;编辑器发现没有可删内容;出来时 config.toml 文件不存在。

调用关系:它保护无操作场景,避免用户只是清一个不存在的设置,却多出一个空配置文件。

调用图:外部调用 3 个(assert!, tempdir, vec!)。

blocking_set_path_updates_notifications1247–1269 ↗
fn blocking_set_path_updates_notifications()

作用:检查通用 SetPath 能写入 tui.notifications 这样的嵌套布尔值。

数据流:进去的是路径 tui -> notifications 和 false;编辑器创建或更新对应表;出来的 TOML 解析后能读到 notifications 为 false。

调用关系:它验证通用路径写入能力,说明不只是专门接口能改配置。

调用图:外部调用 5 个(assert_eq!, read_to_string, tempdir, from_str, vec!)。

async_builder_set_model_persists1272–1287 ↗
async fn async_builder_set_model_persists()

作用:检查异步版本的构建器也能正确保存模型设置。异步就是任务可以等待磁盘操作,不一定堵住当前执行线程。

数据流:进去的是模型 gpt-5.4 和 high 力度;调用 apply().await 等待保存完成;出来的配置文件包含对应两行。

调用关系:它由 Tokio 异步测试运行器执行,确认异步入口和阻塞入口写出的结果一致。

调用图:调用 1 个内部函数(new);外部调用 3 个(assert_eq!, read_to_string, tempdir)。

blocking_builder_set_model_round_trips_back_and_forth1290–1321 ↗
fn blocking_builder_set_model_round_trips_back_and_forth()

作用:检查阻塞式构建器反复切换模型设置时,每次都能写成当前最新值。

数据流:先写 o4-mini/low,再改成 gpt-5.4/high,最后改回 o4-mini/low;每一步都读取文件核对;出来的文件总是匹配最后一次设置。

调用关系:它测试连续编辑的稳定性,模拟用户来回切换设置的真实操作。

调用图:调用 1 个内部函数(new);外部调用 3 个(assert_eq!, read_to_string, tempdir)。

blocking_set_asynchronous_helpers_available1324–1342 ↗
async fn blocking_set_asynchronous_helpers_available()

作用:检查那些辅助设置方法在异步 apply 流程里也能使用。

数据流:进去的是 hide_full_access_warning = true;构建器用异步保存;出来的 TOML 解析后,notice.hide_full_access_warning 为 true。

调用关系:它连接“便捷方法”和“异步落盘”两条路径,确保组合使用时不会漏掉编辑。

调用图:调用 1 个内部函数(new);外部调用 3 个(assert_eq!, read_to_string, tempdir)。

blocking_builder_set_realtime_audio_persists_and_clears1345–1386 ↗
fn blocking_builder_set_realtime_audio_persists_and_clears()

作用:检查实时音频的麦克风和扬声器设置能保存,也能单独清除其中一项。

数据流:先输入麦克风 USB Mic 和扬声器 Desk Speakers;编辑器写入 audio 表;再把麦克风设为 None;出来时 microphone 被删掉,speaker 仍保留。

调用关系:它测试实时音频设备偏好的保存和清理,尤其确认清一个字段不会误删同表里的另一个字段。

调用图:调用 1 个内部函数(new);外部调用 4 个(assert_eq!, read_to_string, tempdir, from_str)。

blocking_builder_set_realtime_voice_persists_and_clears1389–1421 ↗
fn blocking_builder_set_realtime_voice_persists_and_clears()

作用:检查实时语音的 voice 设置能保存,也能清空。

数据流:先输入 voice = cedar;编辑器写入 realtime 表;再把 voice 设为 None;出来时 realtime 表里不再有 voice。

调用关系:它覆盖实时会话相关配置的另一个字段,确认设置和清除路径都正常。

调用图:调用 1 个内部函数(new);外部调用 4 个(assert_eq!, read_to_string, tempdir, from_str)。

replace_mcp_servers_blocking_clears_table_when_empty1424–1441 ↗
fn replace_mcp_servers_blocking_clears_table_when_empty()

作用:检查用空服务器列表替换 MCP 配置时,旧的 mcp_servers 表会被删掉。

数据流:进去的是已有 foo MCP 服务器的配置,以及一个空的服务器映射;编辑器执行替换;出来的文件内容不再包含 mcp_servers。

调用关系:它验证“清空全部 MCP 服务器”的行为,防止旧服务器配置残留导致程序以后还会加载它们。

调用图:外部调用 6 个(new, ReplaceMcpServers, assert!, read_to_string, write, tempdir)。

core/src/config/schema_tests.rs源码 ↗
testtest run

这个文件测试的是配置文件的 JSON Schema。JSON Schema 可以理解成一份“配置文件填写规则说明书”:哪些字段能写、字段是什么类型、哪些写法不允许。这里主要做两件事。第一,把当前代码生成出来的 schema 和仓库里保存的 fixture(固定参考样本)对比,确认两者内容一致;如果不一致,就打印出差异,并提示开发者运行命令更新文件。第二,它还会真的把 schema 写到临时目录,再和仓库里的文件逐字比较,防止格式、换行这类细节出问题。为了照顾 Windows 和其他系统换行符不同的问题,它会统一处理换行。另一个测试专门检查 MCP 服务器配置里不能直接写 bearer_token,只能写 bearer_token_env_var,也就是让用户用环境变量传密钥,避免把敏感令牌明晃晃写进配置文件说明里。

函数细节3
trim_single_trailing_newline9–11 ↗
fn trim_single_trailing_newline(contents: &str) -> &str

作用:这个小工具函数用来去掉字符串末尾一个多余的换行符。它的作用是让测试比较文件内容时,不会因为最后一行有没有换行这种小差别而误报失败。

数据流:进去的是一段文字 → 它检查最后是不是正好有一个换行符 → 如果有就去掉这个换行符,如果没有就原样返回;它不修改原文字,只返回一个可比较的文字片段。

调用关系:它被 config_schema_matches_fixture 用在最后的精确内容比较里。这个测试已经检查了主要结构是否一致,最后再借它把末尾换行这种无关紧要的差异排除掉。

config_schema_matches_fixture14–55 ↗
fn config_schema_matches_fixture()

作用:这个测试确认当前代码生成的配置 schema 和仓库里保存的标准版本一致。它像是在检查“说明书模板”和“现场重新打印出来的说明书”是不是同一本。

数据流:进去的是仓库里的 config.schema.json 文件,以及 config_schema_json 现场生成的新 schema → 它把两边都读成 JSON 数据,先做规范化处理,再比较结构是否一样;如果不一样,就生成一份清楚的差异文本并让测试失败 → 接着它还把 schema 写到临时文件,再读回来和仓库文件做接近逐字的比较,最后确认生成文件和提交在仓库里的文件完全对得上。

调用关系:这是配置 schema 的主验收测试。它会调用 find_resource! 找到仓库里的参考文件,调用 config_schema_json 生成当前 schema,调用 canonicalize 消除 JSON 顺序等无关差异,发现不同时用 TextDiff 做人能看懂的差异报告;后半段再调用 write_config_schema,验证真正写文件的结果也和仓库内容一致。

调用图:外部调用 12 个(new, from_lines, assert_eq!, find_resource!, panic!, from_slice, from_str, to_string_pretty, read_to_string, canonicalize (+2 more))。

config_schema_hides_unsupported_inline_mcp_bearer_token58–75 ↗
fn config_schema_hides_unsupported_inline_mcp_bearer_token()

作用:这个测试确认 schema 不会告诉用户可以直接在配置里写 bearer_token。这样做是为了避免把访问令牌这类敏感信息写进普通配置文件,推荐改用环境变量名 bearer_token_env_var。

数据流:进去的是 config_schema_json 生成的 schema JSON → 它把 JSON 解码成可查询的数据结构,找到 RawMcpServerConfig 的 properties 字段 → 然后检查里面没有 bearer_token,但有 bearer_token_env_var;如果结果不符合,测试就失败。

调用关系:它是一个针对安全和用户暴露面的专门测试。它依赖 config_schema_json 生成完整 schema,然后只检查 MCP 服务器配置这一小块,保证主 schema 生成逻辑以后改动时,不会意外把暂不支持或不安全的内联令牌字段公开出去。

调用图:外部调用 3 个(assert_eq!, from_slice, config_schema_json)。

core/src/config/config_tests.rs源码 ↗
testtest

这段测试像一套“配置体检表”。它会造出临时目录和小段 TOML 配置文本,TOML 是一种常见的配置文件格式,然后让真正的配置加载代码去解析。测试会确认默认值是否合理,比如界面颜色默认开启;旧写法是否还能兼容,比如旧的 memories 开关;危险组合是否会被拒绝,比如同一个模型供应商同时用环境变量密钥和自定义认证命令。它还重点检查权限档案:文件系统哪里能读写、网络是否开启、代理是否启动、MITM(中间人检查/改写网络请求的规则)是否保留顺序。整体上,这个文件不是产品运行时逻辑,而是给配置系统加护栏,确保改代码时不会悄悄破坏用户配置或安全边界。 这一段测试主要围绕“配置文件进来以后,运行时到底会得到什么规则”。它会临时造一些项目目录和配置内容,然后调用配置加载器,检查结果是不是符合预期。重点有三类:第一是权限档案,也就是一套给文件系统和网络放行或禁止的规则;第二是旧版沙盒配置和新版权限档案之间怎么衔接,避免老配置突然失效或意外放权;第三是 TUI(终端用户界面)里的主题、宠物、会话列表样式等小选项能不能从 TOML 配置里正确读出来。可以把这些测试想成“安全门禁演练”:每种配置都是一张门禁卡,测试确认它只能开该开的门,不能偷偷打开仓库、元数据目录或网络权限。 这段测试覆盖的是配置系统里最容易出事故的地方。比如旧版 sandbox_mode 要能正确映射到新版权限模型;MCP(Model Context Protocol,一种让模型连接外部工具/服务的协议)服务器要按企业或托管配置的名单启用或禁用;插件带来的 MCP 服务器不能绕过限制;用户配置、项目配置、会话临时配置、托管配置之间的优先级要稳定。它还检查认证凭据保存位置、网页搜索模式、功能开关、桌面配置透传等细节。整体上,这些测试不是业务入口,而是在开发和持续集成时运行,用临时目录和假配置模拟真实用户环境,确保配置合并后的结果安全、兼容、可预测。 这一段测试主要围绕两类事情。第一类是 MCP 服务器配置。MCP 可以理解成让 Codex 连接外部工具服务的一套接口,这些测试会把服务器配置写进临时的 config.toml,再读回来确认内容没丢、格式没乱,比如环境变量、工作目录、HTTP 请求头、OAuth 信息、启用禁用标记等。第二类是普通配置和 Agent 角色配置。Agent 角色可以理解成给不同“助手身份”准备的说明书。测试会检查模型设置写到正确文件,策略文本会去掉前后空格,托管配置优先级更高,角色文件路径能正确解析,坏的角色文件会被跳过并给出警告。整体像给配置系统做“存取款验钞”:写进去是什么,读出来也必须是什么;不合法的东西不能悄悄混进去。 这段测试主要围绕 Codex 的配置加载过程。它会临时造出假的用户目录、项目目录和 config.toml,再调用真正的配置加载器,看最后得到的配置是不是符合预期。测试覆盖了很多容易出错的边界:同一个 agent 角色同时来自旧配置和项目文件时谁优先;昵称列表要不要去空格、能不能重复;模型目录 JSON 能不能为空;旧的 profile 写法是否被拒绝;OpenTelemetry(遥测,用来记录日志、指标、链路信息)的默认值和过滤规则;企业要求如何强制把危险权限降到只读;Windows 沙箱不支持时怎么降级;以及写回 config.toml 时是否保留原有内容。没有这些测试,用户配置一旦稍微复杂,可能就会出现权限过大、旧配置静默生效、错误提示不清楚,或者项目信任状态写坏的问题。 这一段测试主要围绕“配置怎么被读进来、怎么被修正、怎么被拒绝”展开。测试会临时造一个假的 Codex 主目录,写入不同的 TOML 配置文件(TOML 是一种人能看懂的配置格式),然后用 ConfigBuilder 或 Config::load_from_base_config_with_overrides 把配置加载成程序真正使用的 Config。它像给配置系统做体检:企业要求能不能覆盖用户设置,审批人选择不合法时会不会退回安全默认值,多智能体功能的并发数和等待时间会不会校验,工具推荐列表会不会去掉空值和重复项,实时语音相关配置能不能正确落地。这里很多测试还会检查错误消息和启动警告,因为这些文字是用户遇到配置问题时最直接的提示。

函数细节276
stdio_mcp114–138 ↗
fn stdio_mcp(command: &str) -> McpServerConfig

作用:这是测试里用的小帮手,用一个命令名快速造出一个“标准输入输出型 MCP 服务器配置”。MCP 可以理解成让程序连接外部工具服务器的一套接口,这里是为了少写重复的测试配置。

数据流:进去一个命令字符串 → 它把这个命令放进 Stdio 传输配置里,并填好一堆测试用默认值,比如启用、非必需、无超时、无工具限制 → 出来一个完整的 McpServerConfig,不改外部状态。

调用关系:它被多个 MCP allowlist 测试调用,用来快速准备被过滤的服务器样本。它只负责造数据,不做过滤;真正的检查发生在调用它的测试里。

调用图:被 5 处调用(filter_mcp_servers_by_allowlist_allows_all_when_unset, filter_mcp_servers_by_allowlist_blocks_all_when_empty, filter_mcp_servers_by_allowlist_enforces_identity_rules, filter_plugin_mcp_servers_by_allowlist_blocks_unlisted_plugin, filter_plugin_mcp_servers_by_allowlist_enforces_plugin_and_identity_rules);外部调用 2 个(new, new)。

http_mcp140–163 ↗
fn http_mcp(url: &str) -> McpServerConfig

作用:这是测试里另一个 MCP 配置小帮手,用一个网址快速造出一个 HTTP 型 MCP 服务器配置。它让测试能专心验证规则,而不是反复手写配置结构。

数据流:进去一个 URL 字符串 → 它把 URL 放进 StreamableHttp 传输配置,并补齐默认环境、启用状态、工具列表等字段 → 出来一个 McpServerConfig。

调用关系:它被 MCP 服务器 allowlist 和插件选择相关测试使用。它只提供测试样本,后续是否允许、阻止或覆盖,由调用它的测试流程去判断。

调用图:被 5 处调用(filter_mcp_servers_by_allowlist_allows_all_when_unset, filter_mcp_servers_by_allowlist_blocks_all_when_empty, filter_mcp_servers_by_allowlist_enforces_identity_rules, filter_plugin_mcp_servers_by_allowlist_enforces_plugin_and_identity_rules, selected_plugin_wins_after_discovered_plugin_requirements);外部调用 1 个(new)。

derive_legacy_sandbox_policy_for_test165–189 ↗
async fn derive_legacy_sandbox_policy_for_test(
    cfg: &ConfigToml,
    sandbox_mode_override: Option<SandboxMode>,
    windows_sandbox_level: WindowsSandboxLevel,
    active_project: Option<&Projec

作用:这个测试帮手把新的权限档案推导成旧版沙箱策略。沙箱策略就是限制程序能访问哪些文件、能不能联网的一组规则。

数据流:进去基础配置、沙箱模式覆盖、Windows 沙箱级别、当前项目和权限约束 → 它先调用配置里的权限推导逻辑,得到 PermissionProfile,再尝试转成旧版 SandboxPolicy → 成功就返回旧策略;失败就记录警告并退回只读策略。

调用关系:它被多个沙箱兼容性测试调用,用来验证新权限系统和旧沙箱表示之间的衔接。它把核心工作交给 derive_permission_profile 和 to_legacy_sandbox_policy。

调用图:调用 1 个内部函数(derive_permission_profile);被 4 处调用(derive_sandbox_policy_falls_back_to_read_only_for_implicit_defaults, derive_sandbox_policy_preserves_windows_downgrade_for_unsupported_fallback, test_sandbox_config_parsing, test_untrusted_project_gets_workspace_write_sandbox);外部调用 1 个(new)。

load_config_normalizes_relative_cwd_override192–207 ↗
async fn load_config_normalizes_relative_cwd_override() -> std::io::Result<()>

作用:这个测试确认:如果命令行传进来的当前工作目录是相对路径,配置加载后会变成绝对路径。这样后面代码不会因为路径基准不同而找错地方。

数据流:进去一个相对 cwd 覆盖值 nested 和临时 codex_home → 配置加载器把 nested 按当前目录转换成绝对路径 → 测试比较 config.cwd 是否等于预期绝对路径。

调用关系:它直接调用 Config::load_from_base_config_with_overrides,验证配置加载阶段的路径整理行为。

调用图:调用 1 个内部函数(relative_to_current_dir);外部调用 6 个(default, from, load_from_base_config_with_overrides, assert_eq!, default, tempdir)。

test_toml_parsing210–311 ↗
async fn test_toml_parsing()

作用:这个测试检查几类 TOML 配置能不能正确读进内存,特别是 history 和 memories 设置。它也验证旧字段还能按新含义工作。

数据流:进去多段 TOML 字符串 → toml 解析器把它们变成 ConfigToml,再通过配置加载器变成运行时 Config → 测试逐项比较解析结果和最终运行时 memories 配置。

调用关系:它覆盖从文本解析到运行时配置成型的完整小流程,主要依赖 toml::from_str 和 Config::load_from_base_config_with_overrides。

调用图:外部调用 5 个(load_from_base_config_with_overrides, assert!, assert_eq!, default, tempdir)。

parses_bundled_skills_config314–334 ↗
fn parses_bundled_skills_config()

作用:这个测试确认 skills 配置里的 bundled 开关和 include_instructions 开关能被正确读出来。skills 可以理解成内置能力包的配置。

数据流:进去一段包含 [skills] 和 [skills.bundled] 的 TOML → 解析成 ConfigToml → 测试确认 bundled.enabled 是 false,include_instructions 也是 false。

调用关系:它只检查 TOML 反序列化这一层,不启动完整配置加载流程。

调用图:外部调用 2 个(assert_eq!, from_str)。

tools_web_search_true_deserializes_to_none337–353 ↗
fn tools_web_search_true_deserializes_to_none()

作用:这个测试确认旧的 tools.web_search = true 写法会被解析成 None。这里的 None 表示这个旧开关不再直接形成新配置值。

数据流:进去一段 web_search = true 的 TOML → 解析成 ConfigToml → 测试确认 tools.web_search 没有实际值,其他工具配置也为空。

调用关系:它验证兼容旧配置的解析规则,避免旧字段误触发新的工具配置。

调用图:外部调用 2 个(assert_eq!, from_str)。

tools_web_search_false_deserializes_to_none356–372 ↗
fn tools_web_search_false_deserializes_to_none()

作用:这个测试确认旧的 tools.web_search = false 也会被解析成 None。也就是说,无论旧值真假,都不会变成新的 web_search 配置。

数据流:进去一段 web_search = false 的 TOML → 解析成 ConfigToml → 出来时 tools.web_search 为 None。

调用关系:它和 true 版本的测试成对存在,保证旧 web_search 字段的两种写法行为一致。

调用图:外部调用 2 个(assert_eq!, from_str)。

tools_experimental_request_user_input_defaults_to_enabled375–390 ↗
fn tools_experimental_request_user_input_defaults_to_enabled()

作用:这个测试确认只写出 experimental_request_user_input 配置表但不写 enabled 时,默认是开启。这个功能名表示实验性的“请求用户输入”工具。

数据流:进去一个空的 [tools.experimental_request_user_input] TOML 表 → 解析后自动填 enabled = true → 测试比较结果。

调用关系:它只验证解析默认值,后续运行时是否启用由加载配置的测试继续检查。

调用图:外部调用 2 个(assert_eq!, from_str)。

tools_experimental_request_user_input_can_be_disabled393–409 ↗
fn tools_experimental_request_user_input_can_be_disabled()

作用:这个测试确认用户可以明确关掉 experimental_request_user_input。这样默认开启不代表不能关闭。

数据流:进去 enabled = false 的 TOML → 解析成 ConfigToml → 测试确认 experimental_request_user_input.enabled 是 false。

调用关系:它和默认开启测试配套,覆盖该工具配置的两种关键状态。

调用图:外部调用 2 个(assert_eq!, from_str)。

load_config_resolves_experimental_request_user_input_enabled412–431 ↗
async fn load_config_resolves_experimental_request_user_input_enabled() -> std::io::Result<()>

作用:这个测试确认解析到的 experimental_request_user_input 开关会真正影响运行时 Config。也就是不只会读,还会生效。

数据流:进去一个把该功能 enabled 设为 false 的 ConfigToml → 配置加载器合并默认值和覆盖项 → 出来的 config.experimental_request_user_input_enabled 为 false。

调用关系:它接在 TOML 解析测试之后,检查 Config::load_from_base_config_with_overrides 是否把解析结果落到运行时字段。

调用图:外部调用 5 个(load_from_base_config_with_overrides, assert!, default, default, tempdir)。

load_config_resolves_code_mode_config434–457 ↗
async fn load_config_resolves_code_mode_config() -> std::io::Result<()>

作用:这个测试确认 code_mode 功能开关和排除的工具命名空间会进入运行时配置。命名空间可以理解成一组工具的分类名。

数据流:进去带 [features.code_mode] 的 TOML → 加载成 Config → 测试确认功能已启用,并且 excluded_tool_namespaces 保存了两个指定名字。

调用关系:它验证功能开关系统和 code_mode 专属配置的衔接。

调用图:外部调用 6 个(load_from_base_config_with_overrides, assert!, assert_eq!, default, tempdir, from_str)。

rejects_provider_auth_with_env_key460–477 ↗
fn rejects_provider_auth_with_env_key()

作用:这个测试确认模型供应商配置不能同时写 env_key 和 auth 命令。因为两套认证方式同时存在会让凭据来源变得含糊。

数据流:进去一段冲突的模型供应商 TOML → 解析失败并返回错误 → 测试确认错误信息明确指出 provider auth 不能和 env_key 一起用。

调用关系:它只走 TOML 解析层,验证配置校验能尽早挡住不安全或不清楚的写法。

调用图:外部调用 1 个(assert!)。

rejects_provider_aws_for_custom_provider480–497 ↗
fn rejects_provider_aws_for_custom_provider()

作用:这个测试确认 AWS 配置只能用于 amazon-bedrock 这个供应商,不能随便挂到自定义供应商上。AWS 是亚马逊云服务。

数据流:进去一个 custom provider 却带 [aws] 的 TOML → 解析失败 → 测试确认错误说明 AWS 只支持 amazon-bedrock。

调用关系:它验证模型供应商配置的边界,避免用户写出看似有效但实际不支持的组合。

调用图:外部调用 1 个(assert!)。

accepts_amazon_bedrock_aws_profile_override500–524 ↗
fn accepts_amazon_bedrock_aws_profile_override()

作用:这个测试确认 amazon-bedrock 允许覆盖 AWS profile 和 region。profile 是本机 AWS 凭据配置名,region 是云服务区域。

数据流:进去包含 profile 和 region 的 Bedrock TOML → 解析成 ConfigToml → 测试从 model_providers 里取出 AWS 配置并确认值正确。

调用关系:它验证 Bedrock 的特例:普通供应商不能乱配 AWS,但 Bedrock 可以配置这两个字段。

调用图:外部调用 1 个(assert_eq!)。

load_config_applies_amazon_bedrock_aws_profile_override527–564 ↗
async fn load_config_applies_amazon_bedrock_aws_profile_override()

作用:这个测试确认 Bedrock 的 AWS profile 和 region 不只会解析,还会出现在最终运行时模型供应商配置里。

数据流:进去选择 amazon-bedrock 并设置 AWS profile、region 的 TOML → 配置加载器生成运行时 Config → 测试确认当前 provider 是 amazon-bedrock,且 AWS 字段保留下来。

调用关系:它把上一条解析测试推进到完整加载流程,检查最终 Config.model_provider。

调用图:外部调用 4 个(load_from_base_config_with_overrides, assert_eq!, default, tempdir)。

load_config_rejects_unsupported_amazon_bedrock_overrides567–597 ↗
async fn load_config_rejects_unsupported_amazon_bedrock_overrides()

作用:这个测试确认 amazon-bedrock 只能改 AWS profile 和 region,不能改名字、base_url 等其他供应商字段。这样可以避免内置供应商被半改坏。

数据流:进去一段带多个不支持覆盖字段的 Bedrock TOML → 加载配置时失败 → 测试确认错误类型是 InvalidData,并且错误信息说明只支持改 aws.profile 和 aws.region。

调用关系:它检查完整配置加载阶段的校验,因为有些限制需要在合并默认供应商后才能判断。

调用图:外部调用 5 个(load_from_base_config_with_overrides, assert!, assert_eq!, default, tempdir)。

config_toml_deserializes_model_availability_nux600–635 ↗
fn config_toml_deserializes_model_availability_nux()

作用:这个测试确认 TUI 里“模型可用性新手提示”的显示次数表能被读出来。TUI 是终端用户界面,就是命令行里的交互界面。

数据流:进去 [tui.model_availability_nux] 的 TOML 映射 → 解析成 ConfigToml → 测试确认不同模型名对应的 shown_count 数字正确,其他 TUI 字段使用默认值。

调用关系:它只验证 TUI 配置的文本解析和默认值填充。

调用图:外部调用 2 个(assert_eq!, from_str)。

config_toml_status_line_use_colors_defaults_to_enabled638–650 ↗
fn config_toml_status_line_use_colors_defaults_to_enabled()

作用:这个测试确认状态栏颜色默认开启。状态栏就是终端界面底部或顶部显示状态信息的区域。

数据流:进去一个空的 [tui] 表 → 解析时填入默认值 → 测试确认 status_line_use_colors 为 true。

调用关系:它验证 TUI 默认体验,不涉及完整配置加载。

调用图:外部调用 2 个(assert!, from_str)。

config_toml_deserializes_status_line_use_colors_disabled653–666 ↗
fn config_toml_deserializes_status_line_use_colors_disabled()

作用:这个测试确认用户可以关闭状态栏颜色。这样在不支持颜色或不喜欢颜色的终端里能按配置生效。

数据流:进去 status_line_use_colors = false 的 TOML → 解析成 ConfigToml → 测试确认该字段为 false。

调用关系:它和默认开启测试配对,覆盖颜色开关的可配置性。

调用图:外部调用 2 个(assert!, from_str)。

config_toml_deserializes_terminal_resize_reflow_config669–683 ↗
fn config_toml_deserializes_terminal_resize_reflow_config()

作用:这个测试确认终端尺寸变化时重新排版的最大行数能被配置。重新排版就是窗口变宽变窄后重新整理显示文本。

数据流:进去 terminal_resize_reflow_max_rows = 9000 的 TOML → 解析成 ConfigToml → 测试确认 TUI 配置里保存了 Some(9000)。

调用关系:它验证一个具体 TUI 数值选项的解析。

调用图:外部调用 2 个(assert_eq!, from_str)。

runtime_config_defaults_model_availability_nux686–699 ↗
async fn runtime_config_defaults_model_availability_nux()

作用:这个测试确认如果用户没写模型可用性提示配置,运行时会使用默认值。这样程序不会因为缺配置而出现空洞状态。

数据流:进去默认 ConfigToml 和默认覆盖项 → 配置加载器生成运行时 Config → 测试确认 model_availability_nux 等于默认配置。

调用关系:它检查完整加载后的默认值,而不是单纯 TOML 解析。

调用图:外部调用 5 个(load_from_base_config_with_overrides, assert_eq!, default, default, tempdir)。

test_tui_vim_mode_default_defaults_to_false702–713 ↗
fn test_tui_vim_mode_default_defaults_to_false()

作用:这个测试确认 TUI 的 Vim 模式默认关闭。Vim 模式是类似 Vim 编辑器的一套键盘操作习惯。

数据流:进去空 [tui] TOML → 解析后填默认值 → 测试确认 vim_mode_default 是 false。

调用关系:它验证界面键位习惯不会在用户没要求时自动启用。

调用图:外部调用 2 个(assert!, from_str)。

test_tui_vim_mode_default_true716–728 ↗
fn test_tui_vim_mode_default_true()

作用:这个测试确认用户可以把 TUI 的 Vim 模式默认打开。

数据流:进去 vim_mode_default = true 的 TOML → 解析成 ConfigToml → 测试确认字段为 true。

调用关系:它和默认关闭测试一起覆盖 Vim 模式开关。

调用图:外部调用 2 个(assert!, from_str)。

test_tui_raw_output_mode_defaults_to_false731–742 ↗
fn test_tui_raw_output_mode_defaults_to_false()

作用:这个测试确认原始输出模式默认关闭。原始输出模式通常表示少做界面美化或加工,更接近原始文本。

数据流:进去空 [tui] TOML → 解析后填默认值 → 测试确认 raw_output_mode 是 false。

调用关系:它验证默认界面行为保持正常加工显示。

调用图:外部调用 2 个(assert!, from_str)。

test_tui_raw_output_mode_true745–757 ↗
fn test_tui_raw_output_mode_true()

作用:这个测试确认用户可以打开 TUI 原始输出模式。

数据流:进去 raw_output_mode = true 的 TOML → 解析成 ConfigToml → 测试确认字段为 true。

调用关系:它检查 TOML 层的开关解析,后面还有运行时加载测试确认它会生效。

调用图:外部调用 2 个(assert!, from_str)。

runtime_config_uses_tui_raw_output_mode760–775 ↗
async fn runtime_config_uses_tui_raw_output_mode()

作用:这个测试确认 raw_output_mode 解析后会进入最终运行时配置。也就是说界面真正会看到这个开关。

数据流:进去 raw_output_mode = true 的 ConfigToml → 配置加载器生成 Config → 测试确认 cfg.tui_raw_output_mode 为 true。

调用关系:它承接解析测试,验证 Config::load_from_base_config_with_overrides 的合并和落地。

调用图:外部调用 5 个(load_from_base_config_with_overrides, assert!, default, tempdir, from_str)。

config_toml_deserializes_permission_profiles778–898 ↗
fn config_toml_deserializes_permission_profiles()

作用:这个测试确认复杂的权限档案能被 TOML 正确读出来。权限档案是一套预设规则,说明文件哪里能读写、网络哪里能连。

数据流:进去一大段 permissions TOML,里面有默认档案、工作区根目录、文件权限、网络代理、域名规则和 MITM 规则 → 解析成 ConfigToml → 测试逐项比较所有嵌套结构和值。

调用关系:它是权限配置解析的基础测试,后续运行时权限测试都建立在这些结构能正确解析之上。

调用图:外部调用 2 个(assert_eq!, from_str)。

config_toml_rejects_empty_mitm_action_reference_list901–923 ↗
fn config_toml_rejects_empty_mitm_action_reference_list()

作用:这个测试确认 MITM hook 的 action 列表不能为空。MITM hook 是匹配某些网络请求后执行改写动作的规则,空动作容易造成误解。

数据流:进去 action = [] 的 TOML → 解析失败 → 测试确认错误提示 action must not be empty。

调用关系:它验证网络改写规则采用“失败关闭”的安全思路:写错就拒绝,而不是默默放行。

调用图:外部调用 1 个(assert!)。

config_toml_rejects_empty_mitm_action_definition926–947 ↗
fn config_toml_rejects_empty_mitm_action_definition()

作用:这个测试确认 MITM action 本身必须至少定义一个操作,比如删请求头或加请求头。空定义没有实际意义,也可能隐藏配置错误。

数据流:进去一个空的 actions.strip_auth TOML 表 → 解析失败 → 测试确认错误说明必须至少定义一个 operation。

调用关系:它和空 action 引用列表测试一起,守住 MITM 配置的安全边界。

调用图:外部调用 1 个(assert!)。

permissions_profile_network_to_proxy_config_preserves_mitm_hooks950–989 ↗
fn permissions_profile_network_to_proxy_config_preserves_mitm_hooks()

作用:这个测试确认权限档案里的网络 MITM 规则转换成代理配置时不会丢失。代理配置就是真正控制网络代理如何工作的运行时设置。

数据流:进去手工构造的 NetworkToml,里面有一个 hook 和一个 action → 调用 to_network_proxy_config → 测试确认网络模式、MITM 开关、hook 主机、方法和删请求头动作都还在。

调用关系:它检查从配置模型到代理运行配置的转换函数,确保安全检查规则能被带到实际代理里。

调用图:外部调用 7 个(from, new, assert!, assert_eq!, default, default, vec!)。

permissions_profile_network_to_proxy_config_preserves_mitm_hook_declaration_order992–1032 ↗
fn permissions_profile_network_to_proxy_config_preserves_mitm_hook_declaration_order()

作用:这个测试确认多个 MITM hook 转成代理配置后仍按用户声明顺序排列。顺序重要,因为更具体的规则可能需要先匹配。

数据流:进去两个 hook 的 TOML,名字顺序不是字母排序意图 → 解析后取出 network,再转换成 proxy config → 测试确认第一个 hook 仍是更具体的 /repos/openai/,第二个是 /repos/。

调用关系:它验证配置解析使用能保序的数据结构,并且 to_network_proxy_config 转换时不打乱顺序。

调用图:外部调用 2 个(assert_eq!, from_str)。

permissions_profiles_proxy_policy_does_not_start_managed_network_proxy_without_feature1035–1083 ↗
async fn permissions_profiles_proxy_policy_does_not_start_managed_network_proxy_without_feature() -> std::io::Result<()>

作用:这个测试确认只在权限档案里写 network.enabled,不会自动启动托管网络代理。托管代理是程序自己启动的本地网络管道。

数据流:进去一个启用网络但没开启 network_proxy 功能的权限档案 → 加载配置 → 测试确认网络沙箱策略是 Enabled,但 config.permissions.network 仍是 None。

调用关系:它验证“允许联网”和“启动代理”是两件事,启动代理还需要额外功能开关。

调用图:外部调用 8 个(from, default, new, load_from_base_config_with_overrides, assert!, assert_eq!, Access, write)。

permissions_profiles_proxy_policy_starts_managed_network_proxy1086–1135 ↗
async fn permissions_profiles_proxy_policy_starts_managed_network_proxy() -> std::io::Result<()>

作用:这个测试名字看似说会启动代理,但当前断言确认:没有 network_proxy 功能时,即使配置了 proxy_url,也不会启动托管代理。它守住功能开关的边界。

数据流:进去启用网络并写了 proxy_url、enable_socks5=false 的权限档案,但没开功能开关 → 加载配置 → 网络沙箱为 Enabled,实际托管代理配置仍为空。

调用关系:它和上一条测试一起说明:权限档案可表达代理策略,但是否真正启动代理由 feature 控制。

调用图:外部调用 8 个(from, default, new, load_from_base_config_with_overrides, assert!, assert_eq!, Access, write)。

network_proxy_feature_is_no_op_without_sandbox_network1138–1163 ↗
async fn network_proxy_feature_is_no_op_without_sandbox_network() -> std::io::Result<()>

作用:这个测试确认只打开 network_proxy 功能,但沙箱网络本身没放开时,不会启动代理。因为没有联网权限,代理也不该绕过限制。

数据流:进去 features.network_proxy = true,但没有开启网络访问 → 加载配置 → 测试确认网络策略仍是 Restricted,托管代理仍为空。

调用关系:它验证 network_proxy 功能不会单独改变网络权限,只在网络已允许时才可能工作。

调用图:外部调用 6 个(default, new, load_from_base_config_with_overrides, assert!, assert_eq!, from_str)。

network_proxy_feature_matrix_preserves_sandbox_network_semantics1166–1313 ↗
async fn network_proxy_feature_matrix_preserves_sandbox_network_semantics() -> std::io::Result<()>

作用:这个测试用一张小矩阵确认:无论用新权限档案还是旧 workspace-write 配置,network_proxy 开关都不能改变“网络是否允许”的语义。

数据流:进去多组组合:配置来源、新旧写法、network_enabled 真或假、proxy_enabled 真或假 → 每组加载一次 Config → 测试确认网络沙箱策略只跟 network_enabled 有关,而托管代理只在 network_enabled 和 proxy_enabled 都为真时出现。

调用关系:它集中覆盖新旧两套配置入口,确保功能开关只控制代理启动,不偷偷放宽沙箱。

调用图:外部调用 7 个(from, default, new, load_from_base_config_with_overrides, assert_eq!, Access, write)。

network_proxy_cli_overrides_merge_toggle_with_proxy_config1316–1362 ↗
async fn network_proxy_cli_overrides_merge_toggle_with_proxy_config() -> std::io::Result<()>

作用:这个测试确认命令行覆盖项能同时打开 network_proxy,并覆盖代理细节,比如关闭 SOCKS5。SOCKS5 是一种代理协议。

数据流:先在临时配置文件里写 workspace-write 且允许网络 → 再通过 CLI 覆盖打开 features.network_proxy.enabled,并设置 enable_socks5=false → 构建 Config → 测试确认网络允许、代理地址为默认 127.0.0.1:3128、SOCKS5 关闭。

调用关系:它使用 ConfigBuilder 而不是直接加载基础配置,专门验证文件配置和命令行覆盖的合并行为。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 6 个(default, new, assert!, assert_eq!, write, vec!)。

experimental_network_requirements_enable_proxy_without_feature1365–1392 ↗
async fn experimental_network_requirements_enable_proxy_without_feature() -> std::io::Result<()>

作用:这个测试确认企业下发的 experimental_network 要求可以配置托管代理,即使普通 network_proxy 功能开关没开。企业要求可以理解成管理员强制配置。

数据流:进去一个带 experimental_network.enabled = true 的云端配置包 → ConfigBuilder 加载 → 测试确认 Feature::NetworkProxy 没开,但 managed_network_requirements_enabled 为真,而且 permissions.network 里代理已启用。

调用关系:它验证企业/云端要求走的是独立通道,不依赖普通用户功能开关。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 2 个(new, assert!)。

network_proxy_feature_uses_profile_network_proxy_settings1395–1447 ↗
async fn network_proxy_feature_uses_profile_network_proxy_settings() -> std::io::Result<()>

作用:这个测试确认当 network_proxy 功能开启时,会使用权限档案里写的代理地址和 SOCKS5 设置。

数据流:进去开启 network_proxy、默认权限档案启用网络、proxy_url 为 127.0.0.1:43128、enable_socks5=false 的配置 → 加载 Config → 测试确认网络允许,托管代理存在,地址和 SOCKS5 状态都按档案设置。

调用关系:它验证功能开关打开后,权限档案里的代理细节会被转成运行时网络代理配置。

调用图:外部调用 8 个(from, default, new, load_from_base_config_with_overrides, assert!, assert_eq!, Access, from_str)。

disabled_network_proxy_feature_does_not_start_profile_proxy_policy1450–1505 ↗
async fn disabled_network_proxy_feature_does_not_start_profile_proxy_policy() -> std::io::Result<()>

作用:这个测试确认显式关闭 network_proxy 时,即使权限档案里写了代理配置,也不会启动托管代理。

数据流:进去 features.network_proxy.enabled=false,同时权限档案启用网络并配置 proxy_url → 加载 Config → 测试确认功能未启用,permissions.network 为空。

调用关系:它和开启功能的测试成对,证明功能开关优先控制代理是否启动。

调用图:外部调用 7 个(from, default, new, load_from_base_config_with_overrides, assert!, Access, from_str)。

permissions_profiles_network_disabled_by_default_does_not_start_proxy1508–1555 ↗
async fn permissions_profiles_network_disabled_by_default_does_not_start_proxy() -> std::io::Result<()>

作用:这个测试确认权限档案只写域名规则但没显式启用网络时,默认不会启动代理。默认关闭更安全。

数据流:进去一个网络部分只包含 openai.com allow 规则、没有 enabled=true 的权限档案 → 加载 Config → 测试确认 permissions.network 是 None。

调用关系:它验证网络配置里出现规则不等于打开网络或代理,必须有明确启用信号。

调用图:外部调用 7 个(from, default, new, load_from_base_config_with_overrides, assert!, Access, write)。

default_permissions_profile_populates_runtime_sandbox_policy1558–1658 ↗
async fn default_permissions_profile_populates_runtime_sandbox_policy() -> std::io::Result<()>

作用:这个测试确认默认权限档案会生成运行时文件系统和网络沙箱规则。它重点检查哪些路径能读写,以及旧版沙箱投影是否合理。

数据流:进去一个 dev 权限档案:最小路径只读、工作区根可写、docs 只读 → 加载 Config → 测试确认运行时文件系统策略包含这些规则,旧版策略是 workspace-write 但没有额外 writable_roots,.git 不能写,网络受限,活动档案是 dev。

调用关系:它是权限档案从配置变成实际沙箱政策的核心测试,连接 Config 加载、文件系统权限判断和旧版 SandboxPolicy 投影。

调用图:外部调用 10 个(from, default, new, load_from_base_config_with_overrides, assert!, assert_eq!, Access, Scoped, create_dir_all, write)。

default_permissions_extended_profile_preserves_parent_metadata1661–1717 ↗
async fn default_permissions_extended_profile_preserves_parent_metadata() -> std::io::Result<()>

作用:这个测试确认权限档案继承时,运行时仍记得当前档案继承自哪个父档案。继承就是 dev 可以复用 base 的规则。

数据流:进去两个权限档案:base 定义文件权限,dev extends base → 加载 Config → 测试确认 active_permission_profile 的 id 是 dev,extends 是 base。

调用关系:它验证合并父档案规则时不会丢掉元信息,方便界面或诊断显示当前权限来源。

调用图:外部调用 7 个(from, default, new, load_from_base_config_with_overrides, assert_eq!, Access, write)。

permission_profile_override_populates_runtime_permissions1720–1746 ↗
async fn permission_profile_override_populates_runtime_permissions() -> std::io::Result<()>

作用:这个测试确认测试/命令行覆盖的 PermissionProfile 会直接成为运行时有效权限。这里使用 Disabled,表示不启用沙箱限制。

数据流:进去默认配置,但覆盖项里指定 permission_profile = Disabled → 加载 Config → 测试确认有效权限就是 Disabled,没有活动命名档案,旧版沙箱是 DangerFullAccess。

调用关系:它验证 ConfigOverrides.permission_profile 的优先级高于普通配置档案。

调用图:外部调用 5 个(default, new, load_from_base_config_with_overrides, assert_eq!, default)。

permission_snapshot_setter_preserves_permission_constraints1749–1770 ↗
fn permission_snapshot_setter_preserves_permission_constraints()

作用:这个测试确认从会话快照恢复权限时,不能突破已有约束。约束就像上级规定的边界,比如只能使用只读权限。

数据流:先创建一个只能使用 read_only 档案的 Permissions → 尝试从会话快照设置成 workspace_write → 设置失败,返回 ConstraintError,原来的只读权限和活动档案状态保持不变。

调用关系:它直接测试 Permissions 的快照恢复入口,确保会话恢复不会把受管限制绕过去。

调用图:调用 7 个内部函数(new, allow_any, allow_only, from_approval_and_profile, active, read_only, workspace_write);外部调用 2 个(assert!, assert_eq!)。

permission_profile_override_preserves_managed_unrestricted_filesystem1773–1804 ↗
async fn permission_profile_override_preserves_managed_unrestricted_filesystem() -> std::io::Result<()>

作用:这个测试确认托管权限档案里的“不限制文件系统,但限制网络”能保留下来。托管表示这些权限可能来自外部管理策略。

数据流:进去一个 Managed 权限:文件系统 Unrestricted,网络 Restricted → 加载 Config → 测试确认有效权限没变,旧版沙箱投影成 ExternalSandbox 且网络受限。

调用关系:它验证新权限模型里较细的表达,转换到旧沙箱时仍尽量保留关键安全含义。

调用图:外部调用 5 个(default, new, load_from_base_config_with_overrides, assert_eq!, default)。

managed_unrestricted_permission_profile_still_enables_network_requirements1807–1859 ↗
async fn managed_unrestricted_permission_profile_still_enables_network_requirements() -> std::io::Result<()>

作用:这个测试确认即使托管权限在旧版投影里看起来像完全开放,网络管理要求仍能被识别。它防止旧表示丢失新策略的重要信息。

数据流:先加载一个 Managed 权限:文件系统不限制、网络允许 → 旧版沙箱显示 DangerFullAccess → 测试再手工把配置层里的网络要求设为 enabled → 重建 ConfigLayerStack → 确认 managed_network_requirements_enabled 为真。

调用关系:它测试配置层栈和托管网络要求的交互,说明不能只看 legacy_sandbox_policy 来判断全部安全策略。

调用图:调用 2 个内部函数(new, new);外部调用 6 个(default, new, load_from_base_config_with_overrides, assert!, assert_eq!, default)。

permission_profile_override_keeps_memories_root_out_of_legacy_projection1862–1912 ↗
async fn permission_profile_override_keeps_memories_root_out_of_legacy_projection() -> std::io::Result<()>

作用:这个测试确认覆盖权限档案时,不会把 memories 目录错误地加入旧版可写路径。memories 目录是程序保存记忆数据的地方。

数据流:进去一个运行时权限:根目录只读、项目根可写、网络受限 → 加载 Config → 测试确认 codex_home/memories 不能写,旧版沙箱投影仍是 workspace-write 且 writable_roots 为空。

调用关系:它防止新权限到旧策略转换时意外扩大可写范围,尤其是用户数据目录。

调用图:调用 2 个内部函数(from_runtime_permissions, restricted);外部调用 7 个(default, new, load_from_base_config_with_overrides, assert!, assert_eq!, default, vec!)。

permission_profile_override_preserves_configured_network_policy_without_starting_proxy1915–1973 ↗
async fn permission_profile_override_preserves_configured_network_policy_without_starting_proxy() -> std::io::Result<()>

作用:这个测试确认当权限被覆盖成 Disabled 时,配置里的网络代理策略不会因此自动启动代理。也就是说覆盖权限不该意外激活另一套网络组件。

数据流:进去一个权限档案,里面启用网络、写了 proxy_url、域名规则等;同时覆盖 permission_profile 为 Disabled → 加载 Config → 测试确认托管网络代理为空,有效权限是 Disabled。

调用关系:它验证权限覆盖和网络代理启动之间的边界,避免因为配置里有代理字段就越过覆盖后的权限选择。

调用图:外部调用 7 个(from, default, new, load_from_base_config_with_overrides, assert!, assert_eq!, Access)。

workspace_root_glob_none_compiles_to_filesystem_pattern_entry1976–2057 ↗
async fn workspace_root_glob_none_compiles_to_filesystem_pattern_entry() -> std::io::Result<()>

作用:测试在工作区根目录规则里写通配符时,系统会把它当成真正的文件匹配规则,而不是普通的字面路径。这样像 **/*.env 这种“所有 env 文件”的规则才会真的生效。

数据流:输入是一个临时配置:默认权限档案叫 dev,工作区根目录里允许写当前目录,但拒绝 **/*.env。测试还提供当前目录和额外工作区目录 → 配置加载器把这些规则展开到每个工作区根目录,并生成文件系统沙盒策略 → 输出是配置对象;测试确认扫描深度保留为 2,两个根目录下都出现了拒绝 env 文件的通配符规则,并且没有被误存成普通的特殊路径子项。

调用关系:测试运行器调用它;它把主要工作交给 Config::load_from_base_config_with_overrides 来加载配置,再用路径解析和断言检查权限策略是否按预期生成。

调用图:调用 1 个内部函数(resolve_path_against_base);外部调用 9 个(from, default, new, load_from_base_config_with_overrides, assert!, assert_eq!, Scoped, write, vec!)。

permissions_profiles_require_default_permissions2060–2102 ↗
async fn permissions_profiles_require_default_permissions() -> std::io::Result<()>

作用:测试如果配置里声明了权限档案,就必须同时指定默认使用哪一个。否则系统不知道该拿哪套门禁规则上岗,应该直接报错。

数据流:输入是一个有 [permissions] 档案但没有 default_permissions 的配置 → 配置加载器尝试解析时发现缺少默认档案 → 输出是一个 InvalidInput 错误;测试确认错误信息明确说明定义了权限档案却没有设置默认权限。

调用关系:测试运行器调用它;它专门验证 Config::load_from_base_config_with_overrides 的错误路径,防止配置加载器默默选择错误默认值。

调用图:外部调用 7 个(from, default, new, load_from_base_config_with_overrides, assert_eq!, Access, write)。

default_permissions_can_select_builtin_profile_without_permissions_table2105–2143 ↗
async fn default_permissions_can_select_builtin_profile_without_permissions_table() -> std::io::Result<()>

作用:测试不用自定义权限表,也可以直接用内置权限档案。这里检查内置的工作区权限档案能让项目根目录可写,同时保护 .git 这类项目元数据。

数据流:输入是只设置 default_permissions 为内置 :workspace 的配置 → 配置加载器选择内置档案,生成文件系统沙盒策略 → 输出是配置对象;测试确认这是显式选择的权限档案、没有自定义档案,并且当前项目根目录可写但 .git 不可写。

调用关系:测试运行器调用它;它通过配置加载器进入真实的权限解析流程,再询问生成的策略能不能写指定路径。

调用图:外部调用 5 个(default, new, load_from_base_config_with_overrides, assert!, assert_eq!)。

default_permissions_read_only_keeps_add_dir_read_only2146–2178 ↗
async fn default_permissions_read_only_keeps_add_dir_read_only() -> std::io::Result<()>

作用:测试选择内置只读档案时,即使运行时额外传入“可写根目录”,也不能变成可写。只读就是只读,不能被附加目录悄悄放宽。

数据流:输入是默认权限为内置 :read-only,并带一个额外可写目录的覆盖参数 → 配置加载器生成只读策略 → 输出是配置对象;测试确认额外目录仍然不可写,并且当前激活的权限档案确实是只读档案。

调用关系:测试运行器调用它;它用配置加载器合并配置和运行时覆盖项,然后检查权限策略没有被覆盖项错误放宽。

调用图:外部调用 6 个(default, new, load_from_base_config_with_overrides, assert!, assert_eq!, vec!)。

workspace_profile_applies_rules_to_runtime_and_profile_workspace_roots2181–2278 ↗
async fn workspace_profile_applies_rules_to_runtime_and_profile_workspace_roots() -> std::io::Result<()>

作用:测试工作区规则会同时作用在运行时传入的工作区根目录,以及权限档案自己声明的工作区根目录上。这样多目录项目不会只有一部分目录被正确保护。

数据流:输入是三个临时目录:当前目录、运行时额外根目录、档案里声明的根目录;配置要求工作区根可写,但 .git.codex 只读 → 配置加载器合并这些根目录并生成沙盒规则 → 输出是配置对象;测试确认运行时根目录列表、有效根目录列表、档案根目录列表分别正确,并且每个根目录本身可写,元数据目录不可写。

调用关系:测试运行器调用它;它依赖配置加载器把配置档案和运行时参数拼在一起,再用策略查询函数检查每个目录的最终权限。

调用图:外部调用 9 个(from, default, new, load_from_base_config_with_overrides, assert!, assert_eq!, Scoped, create_dir_all, vec!)。

explicit_builtin_workspace_profile_ignores_legacy_workspace_write_settings2281–2320 ↗
async fn explicit_builtin_workspace_profile_ignores_legacy_workspace_write_settings() -> std::io::Result<()>

作用:测试当用户明确选择新版内置 :workspace 权限档案时,旧版 sandbox_workspace_write 设置不会再混进来。这样新旧配置不会叠加出意外的额外写权限或网络权限。

数据流:输入是显式默认权限 :workspace,同时旧版沙盒配置里写了额外可写目录和网络访问 → 配置加载器优先采用新版权限档案 → 输出是配置对象;测试确认网络仍是受限的,旧版额外目录没有变成直接放行项。

调用关系:测试运行器调用它;它检查配置加载器在新版显式权限档案和旧版沙盒字段冲突时,选择新版规则而不是混用。

调用图:外部调用 6 个(default, new, load_from_base_config_with_overrides, assert!, assert_eq!, vec!)。

default_permissions_profile_can_extend_builtin_workspace2323–2418 ↗
async fn default_permissions_profile_can_extend_builtin_workspace() -> std::io::Result<()>

作用:测试自定义权限档案可以继承内置工作区档案,并在此基础上改一部分规则,比如打开网络、把临时目录从可写改成只读。

数据流:输入是自定义档案 workspace-with-network,它继承 :workspace,额外设置 :tmpdir 只读并启用网络 → 配置加载器先拿到父档案规则,再用子档案规则覆盖对应项 → 输出是配置对象;测试确认项目根目录仍可写、.git 仍受保护、继承的 /tmp 写权限保留、tmpdir 写权限被只读替换、网络已启用,并记录了激活档案及其父档案。

调用关系:测试运行器调用它;它验证配置加载器的“继承加覆盖”逻辑,尤其是子档案能替换父档案同一路径规则。

调用图:外部调用 7 个(from, default, new, load_from_base_config_with_overrides, assert!, assert_eq!, Access)。

default_permissions_profile_can_extend_builtin_read_only2421–2474 ↗
async fn default_permissions_profile_can_extend_builtin_read_only() -> std::io::Result<()>

作用:测试自定义权限档案可以继承内置只读档案,同时只打开网络,不会顺手获得写文件能力。

数据流:输入是自定义档案 read-only-with-network,继承 :read-only 并启用网络 → 配置加载器生成继承后的权限策略 → 输出是配置对象;测试确认当前目录可读但不可写,网络策略为启用,并记录了激活档案和继承来源。

调用关系:测试运行器调用它;它通过配置加载器检查只读父档案的文件限制会保留,网络设置可以单独被子档案改变。

调用图:外部调用 6 个(from, default, new, load_from_base_config_with_overrides, assert!, assert_eq!)。

empty_config_defaults_to_builtin_profile_for_trusted_project2477–2529 ↗
async fn empty_config_defaults_to_builtin_profile_for_trusted_project() -> std::io::Result<()>

作用:测试没有显式权限配置时,受信任项目会自动落到合理的内置权限档案。非 Windows 下通常是工作区可写;Windows 因沙盒支持限制会保持只读。

数据流:输入是一个只标记当前项目为 Trusted 的配置 → 配置加载器根据项目信任等级和操作系统选择默认权限 → 输出是配置对象;测试确认激活档案名符合平台预期,并检查非 Windows 下项目根可写且 .codex 受保护,Windows 下保持不可写。

调用关系:测试运行器调用它;它验证配置加载器在“用户没写权限档案”时的自动兜底选择。

调用图:外部调用 7 个(default, from, new, load_from_base_config_with_overrides, assert!, assert_eq!, cfg!)。

empty_config_defaults_to_builtin_profile_for_untrusted_project2532–2588 ↗
async fn empty_config_defaults_to_builtin_profile_for_untrusted_project() -> std::io::Result<()>

作用:测试没有显式权限配置时,未受信任项目也会得到安全的默认权限。它至少允许读取项目,但平台不同会影响是否允许工作区写入。

数据流:输入是一个只标记当前项目为 Untrusted 的配置 → 配置加载器根据默认策略和操作系统生成权限 → 输出是配置对象;测试确认档案名符合平台预期、当前目录可读,并按平台检查是否可写及元数据保护。

调用关系:测试运行器调用它;它覆盖未受信任项目的默认权限路径,确保默认值不会变成完全开放。

调用图:外部调用 7 个(default, from, new, load_from_base_config_with_overrides, assert!, assert_eq!, cfg!)。

implicit_builtin_workspace_profile_preserves_sandbox_workspace_write_settings2591–2657 ↗
async fn implicit_builtin_workspace_profile_preserves_sandbox_workspace_write_settings() -> std::io::Result<()>

作用:测试当系统隐式选择工作区权限时,旧版沙盒里的额外可写目录和网络设置仍会被保留下来。这样老用户不显式迁移配置,也不会突然丢权限。

数据流:输入是受信任项目配置、旧版 sandbox_workspace_write 设置、Windows 沙盒提升配置和一个额外可写目录 → 配置加载器走隐式工作区兼容路径 → 输出是配置对象;测试确认额外目录可写、网络启用、不能简单标记为某个纯内置激活档案,并且旧版沙盒投影里保留了原来的可写目录和排除选项。

调用关系:测试运行器调用它;它检查新权限系统对旧版沙盒配置的兼容桥接,失败时会直接 panic! 暴露结果类型不对。

调用图:外部调用 8 个(default, from, new, load_from_base_config_with_overrides, assert!, assert_eq!, panic!, vec!)。

implicit_builtin_workspace_profile_preserves_add_dir_metadata_carveouts2660–2707 ↗
async fn implicit_builtin_workspace_profile_preserves_add_dir_metadata_carveouts() -> std::io::Result<()>

作用:测试隐式工作区权限处理额外可写目录时,仍会保护 .git.agents.codex 这些元数据目录。也就是说目录可以写,但里面的关键控制区不能随便改。

数据流:输入是受信任项目和运行时额外可写根目录,额外目录里事先建好几个元数据子目录 → 配置加载器生成兼容的工作区写策略 → 输出是配置对象;测试确认额外根目录本身可写,但这些元数据子目录都不可写。

调用关系:测试运行器调用它;它验证配置加载器在处理运行时追加目录时,会继续应用旧版元数据保护规则。

调用图:外部调用 7 个(default, from, new, load_from_base_config_with_overrides, assert!, create_dir_all, vec!)。

empty_config_defaults_to_builtin_read_only_without_trust_decision2710–2735 ↗
async fn empty_config_defaults_to_builtin_read_only_without_trust_decision() -> std::io::Result<()>

作用:测试完全没有项目信任判断时,默认应该是只读。这样系统在不知道项目是否安全时,不会贸然给写权限。

数据流:输入是空配置和当前目录 → 配置加载器选择默认只读权限 → 输出是配置对象;测试确认当前目录可读但不可写。

调用关系:测试运行器调用它;它覆盖最保守的默认配置路径,确保没有信任信息时不会放宽权限。

调用图:外部调用 5 个(default, new, load_from_base_config_with_overrides, assert!, default)。

default_permissions_can_select_builtin_full_access_profile2738–2768 ↗
async fn default_permissions_can_select_builtin_full_access_profile() -> std::io::Result<()>

作用:测试可以通过新版内置权限名选择完全访问档案。这个档案相当于关闭沙盒,所以必须确认名字和结果都明确对应。

数据流:输入是 default_permissions 设为内置危险全访问档案 → 配置加载器生成禁用权限限制的档案 → 输出是配置对象;测试确认有效权限档案是 Disabled,并且激活档案名是危险全访问档案。

调用关系:测试运行器调用它;它检查配置加载器能识别当前支持的全访问内置档案名。

调用图:外部调用 4 个(default, new, load_from_base_config_with_overrides, assert_eq!)。

legacy_danger_no_sandbox_is_rejected2771–2794 ↗
async fn legacy_danger_no_sandbox_is_rejected() -> std::io::Result<()>

作用:测试旧的 :danger-no-sandbox 名字不再被接受。这样用户不会继续使用已经废弃或语义不清的危险别名。

数据流:输入是 default_permissions = ":danger-no-sandbox" → 配置加载器查找内置档案名失败 → 输出是错误;测试确认错误信息说这是未知内置档案。

调用关系:测试运行器调用它;它验证配置加载器拒绝旧别名,而不是悄悄映射到全访问。

调用图:外部调用 4 个(default, new, load_from_base_config_with_overrides, assert_eq!)。

user_defined_permission_profile_names_cannot_use_builtin_prefix2797–2827 ↗
async fn user_defined_permission_profile_names_cannot_use_builtin_prefix() -> std::io::Result<()>

作用:测试用户自定义权限档案不能用冒号开头的内置前缀。这样自定义名字不会和系统保留名字混在一起。

数据流:输入是名为 :custom 的用户档案,并把它设为默认 → 配置加载器发现名字用了保留前缀 → 输出是 InvalidInput 错误;测试确认错误信息指出这个档案名占用了内置前缀。

调用关系:测试运行器调用它;它检查配置加载器在读取自定义权限表时会先做命名规则校验。

调用图:外部调用 6 个(from, default, new, load_from_base_config_with_overrides, assert_eq!, default)。

unknown_builtin_permission_profile_name_is_rejected2830–2854 ↗
async fn unknown_builtin_permission_profile_name_is_rejected() -> std::io::Result<()>

作用:测试冒号开头但系统不认识的内置权限名会被拒绝。这样拼错的权限名不会被默默当成别的配置。

数据流:输入是 default_permissions = ":unknown" → 配置加载器尝试按内置档案解析但找不到 → 输出是 InvalidInput 错误;测试确认错误信息包含未知档案名。

调用关系:测试运行器调用它;它验证配置加载器对内置档案名的白名单检查。

调用图:外部调用 4 个(default, new, load_from_base_config_with_overrides, assert_eq!)。

permissions_profiles_allow_direct_write_roots_outside_workspace_root2857–2920 ↗
async fn permissions_profiles_allow_direct_write_roots_outside_workspace_root() -> std::io::Result<()>

作用:测试权限档案可以直接给工作区外的绝对目录写权限。这个能力用于明确允许某些外部目录,但必须只在用户写明时发生。

数据流:输入是自定义 dev 档案,文件系统规则里直接写了一个工作区外的绝对路径并给写权限 → 配置加载器把这个路径加入沙盒策略 → 输出是配置对象;测试确认自定义档案摘要正确、外部目录可写,并且旧版沙盒投影也包含这个可写根目录且网络关闭。

调用关系:测试运行器调用它;它通过配置加载器验证显式绝对路径授权能同时反映到新版权限策略和旧版沙盒表示。

调用图:调用 1 个内部函数(from_absolute_path);外部调用 9 个(from, default, new, load_from_base_config_with_overrides, assert!, assert_eq!, Access, canonicalize, write)。

permissions_profiles_reject_nested_entries_for_non_workspace_roots2923–2970 ↗
async fn permissions_profiles_reject_nested_entries_for_non_workspace_roots() -> std::io::Result<()>

作用:测试只有支持“子规则”的特殊路径才能写嵌套规则。像 :minimal 这种不支持子项的路径,如果再写 docs 子规则,应该报错。

数据流:输入是 dev 权限档案,给 :minimal 配了一个嵌套的 docs 只读规则 → 配置加载器发现这个特殊路径不支持嵌套项 → 输出是 InvalidInput 错误;测试确认错误信息准确指出 :minimal 不支持嵌套。

调用关系:测试运行器调用它;它验证配置加载器对权限配置结构的合法性检查,避免用户写出看似生效其实无法解释的规则。

调用图:外部调用 7 个(from, default, new, load_from_base_config_with_overrides, assert_eq!, Scoped, write)。

load_workspace_permission_profile2972–2994 ↗
async fn load_workspace_permission_profile(
    profile: PermissionProfileToml,
) -> std::io::Result<Config>

作用:这是本测试文件里的小帮手,用来快速加载一个名为 dev 的权限档案。它减少重复代码,让后面的几个测试只关心档案内容本身。

数据流:输入是一个 PermissionProfileToml 权限档案对象 → 函数创建临时 Codex 主目录和当前目录,并在当前目录放一个 .git 标记,然后把这个档案包装成完整配置 → 输出是 Config::load_from_base_config_with_overrides 返回的配置加载结果,可能成功也可能报错。

调用关系:它被 permissions_profiles_allow_empty_filesystem_with_warningpermissions_profiles_allow_missing_filesystem_with_warningpermissions_profiles_allow_unknown_special_pathspermissions_profiles_allow_unknown_special_paths_with_nested_entries 调用;这些测试把不同档案交给它,再检查加载后的策略和警告。

调用图:被 4 处调用(permissions_profiles_allow_empty_filesystem_with_warning, permissions_profiles_allow_missing_filesystem_with_warning, permissions_profiles_allow_unknown_special_paths, permissions_profiles_allow_unknown_special_paths_with_nested_entries);外部调用 5 个(from, default, new, load_from_base_config_with_overrides, write)。

permissions_profiles_allow_unknown_special_paths2997–3039 ↗
async fn permissions_profiles_allow_unknown_special_paths() -> std::io::Result<()>

作用:测试遇到当前版本不认识的特殊路径时,配置加载不会崩溃,而是保留为未知项并给出启动警告。这样新版本写出的配置在旧版本里至少能被温和处理。

数据流:输入是一个包含 :future_special_path 只读规则的 dev 档案 → helper 加载配置,配置加载器把它记为未知特殊路径 → 输出是配置对象;测试确认沙盒策略里有未知特殊路径项、旧版沙盒投影退成只读,并且启动警告提示这个路径当前版本不认识会被忽略。

调用关系:测试运行器调用它;它把配置准备工作交给 load_workspace_permission_profile,重点检查未知特殊路径的兼容行为。

调用图:调用 1 个内部函数(load_workspace_permission_profile);外部调用 4 个(from, assert!, assert_eq!, Access)。

permissions_profiles_allow_unknown_special_paths_with_nested_entries3042–3079 ↗
async fn permissions_profiles_allow_unknown_special_paths_with_nested_entries() -> std::io::Result<()>

作用:测试未知特殊路径即使带了子路径规则,也不会让配置加载失败,而是带着子路径一起标成未知并警告。这样未来配置格式更容易向前兼容。

数据流:输入是 :future_special_path 下嵌套 docs 只读的 dev 档案 → helper 加载配置,配置加载器把未知特殊路径和子路径一起记录 → 输出是配置对象;测试确认沙盒策略里有带 docs 子路径的未知项,并且启动警告说明这个组合当前版本不认识。

调用关系:测试运行器调用它;它依赖 load_workspace_permission_profile 建好标准测试环境,再验证未知嵌套路径的处理方式。

调用图:调用 1 个内部函数(load_workspace_permission_profile);外部调用 4 个(from, assert!, assert_eq!, Scoped)。

permissions_profiles_allow_missing_filesystem_with_warning3082–3110 ↗
async fn permissions_profiles_allow_missing_filesystem_with_warning() -> std::io::Result<()>

作用:测试权限档案完全没写文件系统规则时,加载仍然成功,但会警告用户这套档案没有任何当前版本认识的文件规则。

数据流:输入是 dev 档案,filesystem 字段为空 → helper 加载配置,配置加载器生成空的受限文件系统策略 → 输出是配置对象;测试确认策略为空、旧版沙盒投影为只读且网络关闭,并出现缺少有效文件系统条目的警告。

调用关系:测试运行器调用它;它通过 load_workspace_permission_profile 走正常加载流程,验证“空文件权限”是可接受但会提醒的状态。

调用图:调用 1 个内部函数(load_workspace_permission_profile);外部调用 2 个(assert!, assert_eq!)。

permissions_profiles_allow_empty_filesystem_with_warning3113–3138 ↗
async fn permissions_profiles_allow_empty_filesystem_with_warning() -> std::io::Result<()>

作用:测试文件系统规则表存在但为空时,行为和没写规则类似:不报错,但给出警告。这样用户能发现自己写了一个实际上没有放行任何路径的档案。

数据流:输入是 dev 档案,filesystem.entries 是空表 → helper 加载配置,配置加载器生成空的受限策略 → 输出是配置对象;测试确认策略为空,并且启动警告提示 dev 档案没有当前版本认识的文件系统条目。

调用关系:测试运行器调用它;它复用 load_workspace_permission_profile,专门覆盖“空表”和“字段缺失”之间的细微差别。

调用图:调用 1 个内部函数(load_workspace_permission_profile);外部调用 3 个(new, assert!, assert_eq!)。

permissions_profiles_reject_workspace_root_parent_traversal3141–3187 ↗
async fn permissions_profiles_reject_workspace_root_parent_traversal() -> std::io::Result<()>

作用:测试工作区根目录的子路径规则不能用 .. 跳到父目录或兄弟目录。这样配置不能借“工作区子路径”的名义越界访问外部位置。

数据流:输入是 dev 权限档案,在 :workspace_roots 下写了 ../sibling 只读 → 配置加载器检查子路径时发现包含父目录跳转 → 输出是 InvalidInput 错误;测试确认错误信息要求子路径必须是干净的后代路径,不能含 ...

调用关系:测试运行器调用它;它验证配置加载器的路径安全检查,防止权限规则通过相对路径逃出工作区。

调用图:外部调用 7 个(from, default, new, load_from_base_config_with_overrides, assert_eq!, Scoped, write)。

permissions_profiles_allow_network_enablement3190–3235 ↗
async fn permissions_profiles_allow_network_enablement() -> std::io::Result<()>

作用:测试自定义权限档案可以明确打开网络访问。网络权限是高风险开关,所以需要确认只有配置写明时才会启用。

数据流:输入是 dev 档案,文件系统只有最小只读规则,网络字段 enabled = true → 配置加载器生成权限策略 → 输出是配置对象;测试确认网络沙盒策略处于启用状态,旧版沙盒投影也显示有完整网络访问。

调用关系:测试运行器调用它;它通过配置加载器验证新版网络权限会正确同步到旧版沙盒表示。

调用图:外部调用 7 个(from, default, new, load_from_base_config_with_overrides, assert!, Access, write)。

tui_theme_deserializes_from_toml3238–3248 ↗
fn tui_theme_deserializes_from_toml()

作用:测试 TUI 的主题字段能从 TOML 配置里读出来。TUI 是终端用户界面,这里确保用户写的主题名不会被丢掉。

数据流:输入是一段 TOML:[tui] theme = "dracula" → TOML 解析器把文本反序列化成 ConfigToml 结构 → 输出是解析后的配置;测试确认主题值是 dracula

调用关系:测试运行器调用它;它直接测试 TOML 反序列化,不经过完整运行时配置加载流程。

调用图:外部调用 1 个(assert_eq!)。

tui_theme_defaults_to_none3251–3257 ↗
fn tui_theme_defaults_to_none()

作用:测试 TUI 配置块存在但没写主题时,主题保持为空。这样系统不会凭空套用用户没选择的主题。

数据流:输入是一段只有 [tui] 的 TOML → TOML 解析器生成配置对象 → 输出是解析后的配置;测试确认主题字段是 None,也就是没有设置。

调用关系:测试运行器调用它;它只验证配置文本到数据结构的默认值行为。

调用图:外部调用 1 个(assert_eq!)。

tui_session_picker_view_deserializes_from_toml3260–3270 ↗
fn tui_session_picker_view_deserializes_from_toml()

作用:测试会话选择器视图模式能从 TOML 里的字符串读成内部枚举值。这里的枚举值可以理解为“只能在几个固定选项里选一个”。

数据流:输入是 [tui] session_picker_view = "dense" → TOML 解析器把字符串转换为 SessionPickerViewMode::Dense → 输出是解析后的配置;测试确认字段值是 Dense。

调用关系:测试运行器调用它;它检查 TUI 子配置的反序列化映射是否正确。

调用图:外部调用 1 个(assert_eq!)。

tui_pet_deserializes_from_toml3273–3283 ↗
fn tui_pet_deserializes_from_toml()

作用:测试 TUI 的 pet 字段能从配置里读出来。这个字段表示终端界面里选择的“小宠物”样式或名字。

数据流:输入是 [tui] pet = "chefito" → TOML 解析器生成配置对象 → 输出是解析后的配置;测试确认 pet 字段保存为 chefito

调用关系:测试运行器调用它;它直接覆盖 TUI pet 字段的配置解析。

调用图:外部调用 1 个(assert_eq!)。

tui_session_picker_view_defaults_to_none3286–3295 ↗
fn tui_session_picker_view_defaults_to_none()

作用:测试 TUI 配置块没写会话选择器视图时,原始配置结构里这个字段是空的。后续运行时默认值会在别处决定。

数据流:输入是只有 [tui] 的 TOML → TOML 解析器生成 ConfigToml → 输出是解析后的配置;测试确认 session_picker_view 没有值。

调用关系:测试运行器调用它;它验证反序列化层不会提前填入运行时默认视图。

调用图:外部调用 1 个(assert_eq!)。

tui_pet_defaults_to_none3298–3304 ↗
fn tui_pet_defaults_to_none()

作用:测试 TUI 配置块没写 pet 时,不会默认选择某个宠物。用户没设置就保持为空。

数据流:输入是只有 [tui] 的 TOML → TOML 解析器生成配置对象 → 输出是解析后的配置;测试确认 pet 字段是 None。

调用关系:测试运行器调用它;它专注检查 pet 字段的默认空值。

调用图:外部调用 1 个(assert_eq!)。

tui_pet_anchor_deserializes_from_toml3307–3317 ↗
fn tui_pet_anchor_deserializes_from_toml()

作用:测试 pet 的显示位置字段能从 TOML 读出来。比如 screen-bottom 表示把它固定在屏幕底部。

数据流:输入是 [tui] pet_anchor = "screen-bottom" → TOML 解析器把字符串转换为 TuiPetAnchor::ScreenBottom → 输出是解析后的配置;测试确认位置枚举值正确。

调用关系:测试运行器调用它;它验证 TUI pet 位置字符串和内部固定选项之间的对应关系。

调用图:外部调用 1 个(assert_eq!)。

tui_pet_anchor_defaults_to_composer3320–3329 ↗
fn tui_pet_anchor_defaults_to_composer()

作用:测试 pet 位置没写时,默认挂在 composer,也就是输入区附近。这样界面行为有一个稳定默认值。

数据流:输入是只有 [tui] 的 TOML → TOML 解析器生成配置对象,并给 pet_anchor 填默认值 → 输出是解析后的配置;测试确认默认值是 TuiPetAnchor::Composer

调用关系:测试运行器调用它;它检查反序列化默认值,而不是完整运行时加载。

调用图:外部调用 1 个(assert_eq!)。

tui_pet_anchor_rejects_unknown_value3332–3345 ↗
fn tui_pet_anchor_rejects_unknown_value()

作用:测试 pet 位置如果写了不支持的值,会被拒绝,并在错误里提示可用选项。这样用户拼错配置时能马上知道问题在哪。

数据流:输入是 [tui] pet_anchor = "bottom",这是未知值 → TOML 解析器尝试转换枚举失败 → 输出是解析错误;测试确认错误信息包含未知值 bottom,也包含允许的 composerscreen-bottom

调用关系:测试运行器调用它;它直接检查 TOML 解析层对非法枚举值的报错质量。

调用图:外部调用 1 个(assert!)。

tui_config_missing_notifications_field_defaults_to_enabled3348–3378 ↗
fn tui_config_missing_notifications_field_defaults_to_enabled()

作用:测试 TUI 配置里没写通知字段时,通知相关设置会使用默认启用配置,同时其他 TUI 字段也有稳定默认值。

数据流:输入是只有 [tui] 的 TOML → TOML 解析器生成完整的 Tui 子配置并填默认值 → 输出是解析后的 TUI 对象;测试把它和预期的默认 TUI 配置逐项比较,包括动画、提示、备用屏幕、颜色、pet 位置、按键映射等。

调用关系:测试运行器调用它;它是 TUI 默认值的整体快照测试,能在默认值被意外改动时及时失败。

调用图:外部调用 1 个(assert_eq!)。

runtime_config_resolves_terminal_resize_reflow_defaults_and_overrides3381–3436 ↗
async fn runtime_config_resolves_terminal_resize_reflow_defaults_and_overrides()

作用:测试运行时配置会正确决定终端 resize 后重排内容时最多处理多少行。默认是自动,正数表示限制行数,0 表示关闭这个限制。

数据流:输入先是空配置 → 配置加载器生成默认运行时配置,最大行数为 Auto;再输入 TUI 里设置 9000 → 输出变为 Limit(9000);最后输入 0 → 输出变为 Disabled。测试逐次确认这三种结果。

调用关系:测试运行器调用它;它通过 Config::load_from_base_config_with_overrides 检查原始 TOML 字段如何被提升成真正运行时使用的 terminal_resize_reflow 配置。

调用图:外部调用 6 个(default, load_from_base_config_with_overrides, assert_eq!, default, default, tempdir)。

forced_chatgpt_workspace_id_empty_values_disable_runtime_restriction3439–3485 ↗
async fn forced_chatgpt_workspace_id_empty_values_disable_runtime_restriction() -> std::io::Result<()>

作用:测试强制 ChatGPT 工作区 ID 的配置在空值、空字符串、空列表时会被视为未启用限制;只有真正非空的 ID 才会留下来。

数据流:输入是一组 TOML 案例:未设置、空字符串、空白字符串、空列表、空白列表项、混合有效 ID → 每个案例先解析成 ConfigToml,再走配置加载器清洗字段 → 输出是运行时配置里的 forced_chatgpt_workspace_id;测试确认空值都变成 None,混合列表只保留去掉空白后的有效 ID。

调用关系:测试运行器调用它;它把多个小场景放在循环里,验证配置加载器对这个运行时限制字段的清理逻辑。

调用图:外部调用 6 个(load_from_base_config_with_overrides, assert_eq!, default, tempdir, from_str, vec!)。

legacy_remote_thread_store_endpoint_is_rejected3488–3506 ↗
async fn legacy_remote_thread_store_endpoint_is_rejected()

作用:测试旧的远程 thread-store endpoint 配置虽然还能被 TOML 解析出来,但在加载阶段会被拒绝。这样旧字段不会继续悄悄生效。

数据流:输入是 experimental_thread_store_endpoint = "https://example.com" → TOML 解析成功,说明旧字段仍可识别;随后配置加载器检查到它已不受支持 → 输出是加载错误;测试确认错误信息提到该字段并说明不再支持。

调用关系:测试运行器调用它;它区分“能解析旧字段”和“运行时拒绝旧字段”这两个阶段。

调用图:外部调用 5 个(load_from_base_config_with_overrides, assert!, default, tempdir, from_str)。

profile_tui_rejects_unsupported_settings3509–3521 ↗
fn profile_tui_rejects_unsupported_settings()

作用:测试 profile 里的 TUI 配置不能随便写根级 TUI 字段。比如在某个 profile 下设置 theme 应该被拒绝,避免用户以为它会按 profile 生效。

数据流:输入是一段 TOML,选择 profile work,并在 [profiles.work.tui] 下写 theme = "dark" → TOML 解析器按 profile TUI 支持字段校验时失败 → 输出是解析错误;测试确认错误里有 unknown field 和 theme。

调用关系:测试运行器调用它;它直接验证配置 schema,也就是配置格式规则,对 profile 作用域里的 TUI 字段有限制。

调用图:外部调用 1 个(assert!)。

runtime_config_resolves_session_picker_view_default_and_override3524–3553 ↗
async fn runtime_config_resolves_session_picker_view_default_and_override()

作用:测试运行时会话选择器视图的默认值和用户覆盖值。原始配置没写时默认是 dense,用户写 comfortable 时就改成 comfortable。

数据流:输入先是空配置 → 配置加载器生成运行时配置,tui_session_picker_view 为 Dense;再输入 TUI 覆盖为 Comfortable → 输出变为 Comfortable。测试分别确认两个结果。

调用关系:测试运行器调用它;它检查从原始 TUI 配置到运行时字段的默认填充和覆盖流程。

调用图:外部调用 6 个(default, load_from_base_config_with_overrides, assert_eq!, default, default, tempdir)。

test_sandbox_config_parsing3556–3677 ↗
async fn test_sandbox_config_parsing()

作用:测试旧版沙盒配置会被解析成正确的沙盒策略。它覆盖全访问、只读、工作区可写,以及 Windows 平台下的特殊降级行为。

数据流:输入是多段 TOML:危险全访问、只读、带可写根目录的工作区写入配置,还分别测试有无受信任项目记录 → 每段先解析成 ConfigToml,再交给测试用的沙盒策略推导函数 → 输出是 SandboxPolicy;测试确认全访问会忽略工作区网络设置,只读会忽略工作区网络设置,工作区写入在非 Windows 下保留可写根目录和排除选项,在 Windows 下退为只读。

调用关系:测试运行器调用它;它把具体推导工作交给 derive_legacy_sandbox_policy_for_test,用于守住旧版沙盒配置到内部策略之间的兼容规则。

调用图:调用 1 个内部函数(derive_legacy_sandbox_policy_for_test);外部调用 4 个(assert_eq!, cfg!, test_absolute_path, format!)。

legacy_sandbox_mode_builds_profiles_with_compatible_projection3680–3816 ↗
async fn legacy_sandbox_mode_builds_profiles_with_compatible_projection() -> std::io::Result<()>

作用:验证旧的 sandbox_mode 配置还能被正确转换成新的权限配置。这样老用户升级后,原来的读写限制和网络限制不会突然变味。

数据流:输入是几种旧格式的 TOML 配置、临时的 Codex 主目录和当前工作目录;测试把它们加载成 Config,再取出旧沙箱策略、新文件系统策略和网络策略互相比对;结果是确认它们能来回转换,并且 workspace-write 会把工作目录和额外目录正确放进可写范围,Windows 上则保持降级为只读的旧行为。

调用关系:测试运行器调用它;它主要把配置交给 Config::load_from_base_config_with_overrides,再检查 legacy_sandbox_policy、permissions 这些配置转换结果,防止新旧权限模型之间的兼容层出错。

调用图:外部调用 9 个(default, new, load_from_base_config_with_overrides, assert!, assert_eq!, cfg!, test_absolute_path, unreachable!, vec!)。

filter_mcp_servers_by_allowlist_enforces_identity_rules3819–3899 ↗
fn filter_mcp_servers_by_allowlist_enforces_identity_rules()

作用:验证普通 MCP 服务器必须同时“名字在名单里”和“身份匹配”才会被允许。身份可以是启动命令,也可以是 URL。

数据流:输入是一组服务器和一份带来源的要求名单;测试调用 filter_mcp_servers_by_requirements 过滤;输出是匹配命令或 URL 的服务器仍启用,不匹配或没有列名的服务器被禁用,并记录禁用原因。

调用关系:测试运行器调用它;它直接检查 MCP 允许名单过滤函数,确保企业或托管配置不能只靠同名服务器被冒充。

调用图:调用 3 个内部函数(new, http_mcp, stdio_mcp);外部调用 3 个(from, from, assert_eq!)。

filter_mcp_servers_by_allowlist_allows_all_when_unset3902–3923 ↗
fn filter_mcp_servers_by_allowlist_allows_all_when_unset()

作用:验证没有设置 MCP 要求名单时,现有服务器不会被莫名禁用。也就是说,限制没开就不该默认拦截。

数据流:输入是两个 MCP 服务器和 None 表示没有要求名单;过滤函数执行后;输出是两个服务器都保持启用,禁用原因为空。

调用关系:测试运行器调用它;它补足 filter_mcp_servers_by_requirements 的默认分支,说明只有明确给了名单才会开始限制。

调用图:调用 2 个内部函数(http_mcp, stdio_mcp);外部调用 2 个(from, assert_eq!)。

filter_mcp_servers_by_allowlist_blocks_all_when_empty3926–3950 ↗
fn filter_mcp_servers_by_allowlist_blocks_all_when_empty()

作用:验证一份“空的要求名单”不是等于没限制,而是表示一个都不允许。这个区别对企业管控很重要。

数据流:输入是两个服务器和一份空的要求表;过滤函数看到名单存在但没有任何允许项;输出是所有服务器都被禁用,并带上来自该要求来源的禁用原因。

调用关系:测试运行器调用它;它和“名单未设置时全放行”的测试配对,确认 None 和空表的含义不会混淆。

调用图:调用 3 个内部函数(new, http_mcp, stdio_mcp);外部调用 3 个(new, from, assert_eq!)。

filter_plugin_mcp_servers_by_allowlist_enforces_plugin_and_identity_rules3953–4012 ↗
fn filter_plugin_mcp_servers_by_allowlist_enforces_plugin_and_identity_rules()

作用:验证插件提供的 MCP 服务器也必须符合对应插件下的允许名单。不能因为服务器来自插件,就绕过命令或 URL 身份检查。

数据流:输入是某个插件的三个服务器和一份只给 sample@test 插件写的要求;调用 filter_plugin_mcp_servers_by_requirements 后;输出是名字和命令都匹配的服务器保留,命令不匹配或没在名单里的服务器禁用。

调用关系:测试运行器调用它;它检查插件专用过滤路径,配合普通 MCP 过滤规则,保证插件导入的工具同样受管控。

调用图:调用 3 个内部函数(new, http_mcp, stdio_mcp);外部调用 3 个(from, from, assert_eq!)。

filter_plugin_mcp_servers_by_allowlist_blocks_unlisted_plugin4015–4053 ↗
fn filter_plugin_mcp_servers_by_allowlist_blocks_unlisted_plugin()

作用:验证如果要求名单里根本没有当前插件,这个插件的 MCP 服务器会被全部禁用。这样未获批准的插件不能偷偷带工具进来。

数据流:输入是 sample@test 插件的服务器,但要求名单只写了 other@test;过滤后;输出是 sample@test 的服务器被禁用,并记录要求来源。

调用关系:测试运行器调用它;它覆盖 filter_plugin_mcp_servers_by_requirements 的“插件没被列出”场景。

调用图:调用 2 个内部函数(new, stdio_mcp);外部调用 3 个(from, from, assert_eq!)。

rebuild_preserving_session_layers_refreshes_requirements4056–4262 ↗
async fn rebuild_preserving_session_layers_refreshes_requirements() -> std::io::Result<()>

作用:验证重建配置时,会保留会话里的临时设置,同时刷新最新的 MCP 要求名单。它防止“旧会话配置”把新的企业限制盖掉。

数据流:输入是两套配置层:一套代表刷新后的用户/项目/托管配置和要求名单,另一套代表当前线程里带会话层的旧配置;测试分别加载成 Config,再调用 rebuild_preserving_session_layers;输出是会话层该保留的仍保留,托管要求该覆盖的会覆盖,未满足新要求的会话服务器被禁用。

调用关系:测试运行器调用它;它通过 ConfigLayerStack 和 Config::load_config_with_layer_stack 搭环境,最后检查 Config::rebuild_preserving_session_layers 的合并结果。

调用图:调用 3 个内部函数(new, new, resolve_path_against_base);外部调用 6 个(from, default, new, load_config_with_layer_stack, assert_eq!, vec!)。

rebuild_preserving_session_layers_refreshes_plugin_derived_mcp_config4265–4370 ↗
async fn rebuild_preserving_session_layers_refreshes_plugin_derived_mcp_config() -> anyhow::Result<()>

作用:验证重建配置后,插件派生出来的 MCP 配置也会按最新功能开关刷新。尤其是原会话里插件关闭,刷新后插件开启时,插件服务器要能出现。

数据流:输入是一个临时插件目录和两套配置层:刷新配置开启 plugins,线程旧配置关闭 plugins;重建后再转换成 MCP 配置;输出是插件 .mcp.json 里的 sample 服务器被加载,并带有插件归属信息。

调用关系:测试运行器调用它;它串起 Config::rebuild_preserving_session_layers、PluginsManager 和 Config::to_mcp_config,确认插件发现结果不会停留在旧配置状态。

调用图:调用 3 个内部函数(new, new, resolve_path_against_base);外部调用 7 个(default, new, load_config_with_layer_stack, assert_eq!, create_dir_all, write, vec!)。

to_mcp_config_omits_plugin_id_when_user_server_shadows_plugin_mcp4373–4429 ↗
async fn to_mcp_config_omits_plugin_id_when_user_server_shadows_plugin_mcp() -> anyhow::Result<()>

作用:验证用户自己配置的 MCP 服务器如果和插件服务器同名,会覆盖插件版本,并且不会被标记成插件提供。这样归属不会误导后续逻辑。

数据流:输入是一个同名插件 MCP 服务器和用户 config.toml 里的同名服务器;加载配置后转换成 MCP 配置;输出是最终服务器使用用户写的 URL,插件归属表为空。

调用关系:测试运行器调用它;它检查 ConfigBuilder、PluginsManager 和 to_mcp_config 合成服务器目录时的“用户配置优先”规则。

调用图:调用 1 个内部函数(new);外部调用 6 个(new, assert!, assert_eq!, default, create_dir_all, write)。

selected_plugin_wins_after_discovered_plugin_requirements4432–4536 ↗
async fn selected_plugin_wins_after_discovered_plugin_requirements() -> anyhow::Result<()>

作用:验证手动选中的插件 MCP 注册项可以覆盖被发现插件的要求限制。也就是说,用户明确选中的来源在最终目录里有更高优先级。

数据流:输入是一个插件自带的两个服务器、企业要求只允许其中一个,以及一个后来传入的 selected plugin 注册;第一次生成 MCP 配置时,未列入要求的服务器被禁用;第二次带 selected 注册生成时,该服务器以选中插件来源启用,并使用选中配置。

调用关系:测试运行器调用它;它先检查 to_mcp_config 的企业要求过滤,再检查 to_mcp_config_with_plugin_registrations 能让显式选择的插件条目胜出。

调用图:调用 5 个内部函数(new, from_selected_plugin, loader_with_enterprise_requirement, new, http_mcp);外部调用 5 个(new, assert_eq!, default, create_dir_all, write)。

to_mcp_config_empty_mcp_requirements_disable_plugin_mcps4539–4602 ↗
async fn to_mcp_config_empty_mcp_requirements_disable_plugin_mcps() -> anyhow::Result<()>

作用:验证企业配置里写了空的 MCP 要求表时,插件带来的 MCP 服务器会被禁用。空表在这里表示“没有任何服务器被批准”。

数据流:输入是一个启用的插件和企业要求中的空 [mcp_servers] 表;配置加载后转换成 MCP 配置;输出是插件里的 sample 服务器存在但 disabled,并带企业要求来源作为原因。

调用关系:测试运行器调用它;它覆盖 ConfigBuilder 加载云端企业要求后,to_mcp_config 对插件 MCP 做限制的路径。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, new);外部调用 5 个(new, assert_eq!, default, create_dir_all, write)。

add_dir_override_extends_workspace_writable_roots4605–4650 ↗
async fn add_dir_override_extends_workspace_writable_roots() -> std::io::Result<()>

作用:验证命令行或运行时传入的额外可写目录,会加入 workspace-write 沙箱的可写范围,而且重复路径不会重复出现。

数据流:输入是 frontend 作为当前目录、backend 作为额外可写目录,包含相对路径和绝对路径两种写法;加载配置后查看旧沙箱策略;输出是在非 Windows 上 backend 只出现一次,Windows 上保持只读降级。

调用关系:测试运行器调用它;它检查 ConfigOverrides.additional_writable_roots 如何影响 Config::load_from_base_config_with_overrides 生成的 sandbox policy。

调用图:外部调用 9 个(default, new, load_from_base_config_with_overrides, assert_eq!, cfg!, default, panic!, create_dir_all, vec!)。

default_zsh_path_sets_runtime_zsh_path4653–4669 ↗
async fn default_zsh_path_sets_runtime_zsh_path() -> std::io::Result<()>

作用:验证外部传入的默认 zsh 路径会进入运行时配置。zsh 是一种命令行 shell,这里通常用于指定打包自带的 shell。

数据流:输入是 ConfigOverrides 里的 default_zsh_path;配置加载后;输出是 config.zsh_path 等于这个路径。

调用关系:测试运行器调用它;它检查 Config::load_from_base_config_with_overrides 是否把覆盖项正确落到最终 Config 字段。

调用图:外部调用 5 个(default, new, load_from_base_config_with_overrides, assert_eq!, default)。

sqlite_home_defaults_to_codex_home_for_workspace_write4672–4687 ↗
async fn sqlite_home_defaults_to_codex_home_for_workspace_write() -> std::io::Result<()>

作用:验证在 workspace-write 沙箱模式下,SQLite 数据库默认放在 Codex 主目录。SQLite 是一个本地小数据库文件。

数据流:输入是开启 workspace-write 的覆盖配置和临时 Codex 主目录;加载配置后;输出是 config.sqlite_home 指向该 Codex 主目录。

调用关系:测试运行器调用它;它检查配置默认值选择,避免数据库路径跑到不该写的位置。

调用图:外部调用 5 个(default, new, load_from_base_config_with_overrides, assert_eq!, default)。

workspace_write_includes_configured_writable_root_once_without_memories_root4690–4741 ↗
async fn workspace_write_includes_configured_writable_root_once_without_memories_root() -> std::io::Result<()>

作用:验证 workspace-write 会接收配置里的可写目录,但不会因为加载配置就创建 memories 目录,也不会把 memories 目录自动加入可写范围。

数据流:输入是重复写了两次的 writable_root 和 workspace-write 模式;配置加载后;输出是在非 Windows 上该目录只出现一次,memories 目录不存在且不可写,Windows 上仍是只读降级。

调用关系:测试运行器调用它;它检查沙箱可写目录去重,以及 memory 相关目录不会被无意扩大权限。

调用图:外部调用 8 个(default, new, load_from_base_config_with_overrides, assert!, assert_eq!, cfg!, panic!, vec!)。

memory_tool_makes_memories_root_readable_without_creating_or_widening_writes4744–4797 ↗
async fn memory_tool_makes_memories_root_readable_without_creating_or_widening_writes() -> std::io::Result<()>

作用:验证开启 memories 功能后,memories 目录只变成可读,不会被创建,也不会变成可写。这样记忆工具能读取需要的位置,但不会扩大写权限。

数据流:输入是启用 memories 功能的配置、当前工作目录和 workspace-write 模式;加载后取文件系统权限策略;输出是 memories 路径可读、不可写,目录本身没有被创建,旧沙箱可写根里也不包含它。

调用关系:测试运行器调用它;它检查 features、permissions 和 legacy_sandbox_policy 三者对 memory 功能的权限处理是否一致。

调用图:调用 1 个内部函数(from);外部调用 7 个(from, default, new, load_from_base_config_with_overrides, assert!, cfg!, panic!)。

config_defaults_to_file_cli_auth_store_mode4800–4817 ↗
async fn config_defaults_to_file_cli_auth_store_mode() -> std::io::Result<()>

作用:验证 CLI 登录凭据默认保存在文件里。凭据就是登录令牌一类的认证信息。

数据流:输入是空配置;加载后;输出是 config.cli_auth_credentials_store_mode 为 File。

调用关系:测试运行器调用它;它检查 Config::load_from_base_config_with_overrides 的默认认证存储策略。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, default)。

config_resolves_explicit_keyring_auth_store_mode4820–4843 ↗
async fn config_resolves_explicit_keyring_auth_store_mode() -> std::io::Result<()>

作用:验证用户明确选择 keyring 时,会按当前版本规则解析最终的 CLI 凭据存储方式。keyring 是系统提供的安全凭据库。

数据流:输入是 cli_auth_credentials_store = Keyring;配置加载后;输出等于 resolve_cli_auth_credentials_store_mode 根据包版本算出的结果。

调用关系:测试运行器调用它;它把 Config 加载结果和解析函数对齐,确保配置字段没有绕过版本兼容逻辑。

调用图:外部调用 5 个(default, new, load_from_base_config_with_overrides, assert_eq!, default)。

config_resolves_default_oauth_store_mode4846–4866 ↗
async fn config_resolves_default_oauth_store_mode() -> std::io::Result<()>

作用:验证 MCP OAuth 凭据默认使用 Auto,并会按版本解析成真正的存储方式。OAuth 是常见的网页登录授权机制。

数据流:输入是空配置;加载后;输出是 config.mcp_oauth_credentials_store_mode 等于 resolve_mcp_oauth_credentials_store_mode 的结果。

调用关系:测试运行器调用它;它检查 MCP OAuth 的默认值不是硬编码错的,而是走统一解析函数。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, default)。

local_dev_builds_force_file_cli_auth_store_modes4869–4895 ↗
fn local_dev_builds_force_file_cli_auth_store_modes()

作用:验证本地开发版本会强制把 CLI 凭据存储改为文件,除非用户选择临时模式。这样开发环境不会依赖系统 keyring。

数据流:输入是不同的 CLI 凭据存储选项和 LOCAL_DEV_BUILD_VERSION;解析函数处理后;输出是 Keyring 和 Auto 都变 File,Ephemeral 仍保持临时,正式版本仍可用 Keyring。

调用关系:测试运行器调用它;它直接测试 resolve_cli_auth_credentials_store_mode 的版本分支。

调用图:外部调用 1 个(assert_eq!)。

local_dev_builds_force_file_mcp_oauth_store_modes4898–4917 ↗
fn local_dev_builds_force_file_mcp_oauth_store_modes()

作用:验证本地开发版本会强制 MCP OAuth 凭据走文件存储。这样开发构建在没有系统凭据库时也能稳定运行。

数据流:输入是 OAuth 存储模式和版本号;解析函数处理后;输出是本地开发版本的 Keyring 和 Auto 变成 File,正式版本的 Keyring 保持 Keyring。

调用关系:测试运行器调用它;它直接测试 resolve_mcp_oauth_credentials_store_mode 的本地开发兼容规则。

调用图:外部调用 1 个(assert_eq!)。

feedback_enabled_defaults_to_true4920–4937 ↗
async fn feedback_enabled_defaults_to_true() -> std::io::Result<()>

作用:验证 feedback 配置表存在但没写 enabled 时,反馈功能默认开启。这样空表不会被误解成关闭。

数据流:输入是 feedback 默认表;加载配置后;输出是 config.feedback_enabled 为 true。

调用关系:测试运行器调用它;它检查 FeedbackConfigToml 的默认值如何进入最终 Config。

调用图:外部调用 6 个(default, new, load_from_base_config_with_overrides, assert_eq!, default, default)。

web_search_mode_defaults_to_none_if_unset4940–4945 ↗
fn web_search_mode_defaults_to_none_if_unset()

作用:验证没有任何网页搜索配置时,解析结果为空。也就是系统不会凭空选择一种搜索模式。

数据流:输入是默认 ConfigToml 和默认 Features;resolve_web_search_mode 处理后;输出是 None。

调用关系:测试运行器调用它;它检查网页搜索配置解析的最基础默认分支。

调用图:调用 1 个内部函数(with_defaults);外部调用 2 个(assert_eq!, default)。

web_search_mode_prefers_config_over_legacy_flags4948–4960 ↗
fn web_search_mode_prefers_config_over_legacy_flags()

作用:验证新的 web_search 配置优先于旧功能开关。这样用户明确写的新配置不会被老开关盖掉。

数据流:输入是 web_search = Live,同时旧的 cached 功能开关也打开;解析后;输出是 Live。

调用关系:测试运行器调用它;它检查 resolve_web_search_mode 在新旧配置同时存在时的优先级。

调用图:调用 1 个内部函数(with_defaults);外部调用 2 个(default, assert_eq!)。

web_search_mode_disabled_overrides_legacy_request4963–4975 ↗
fn web_search_mode_disabled_overrides_legacy_request()

作用:验证用户明确禁用网页搜索时,即使旧开关请求搜索,也会保持禁用。

数据流:输入是 web_search = Disabled 和旧的 request 功能开关;解析后;输出是 Disabled。

调用关系:测试运行器调用它;它保护 resolve_web_search_mode 的安全优先级,避免旧开关意外打开搜索。

调用图:调用 1 个内部函数(with_defaults);外部调用 2 个(default, assert_eq!)。

web_search_mode_for_turn_uses_preference_for_read_only4978–4984 ↗
fn web_search_mode_for_turn_uses_preference_for_read_only()

作用:验证在只读权限配置下,每一轮对话会使用用户偏好的网页搜索模式。只读权限不需要强行改成 live。

数据流:输入是偏好 Cached 的受约束值和只读权限档案;解析当前轮次模式;输出是 Cached。

调用关系:测试运行器调用它;它检查 resolve_web_search_mode_for_turn 面对 PermissionProfile::read_only 的行为。

调用图:调用 2 个内部函数(allow_any, read_only);外部调用 1 个(assert_eq!)。

web_search_mode_for_turn_prefers_live_for_disabled_permissions4987–4992 ↗
fn web_search_mode_for_turn_prefers_live_for_disabled_permissions()

作用:验证当权限档案是 Disabled 时,如果允许 live,当前轮次会偏向 Live 搜索。这里 Disabled 指工具权限关闭,不是网页搜索关闭。

数据流:输入是偏好 Cached 但允许任意值的网页搜索设置,以及 Disabled 权限档案;解析后;输出是 Live。

调用关系:测试运行器调用它;它检查 resolve_web_search_mode_for_turn 在无工具权限时的默认选择。

调用图:调用 1 个内部函数(allow_any);外部调用 1 个(assert_eq!)。

web_search_mode_for_turn_respects_disabled_for_disabled_permissions4995–5000 ↗
fn web_search_mode_for_turn_respects_disabled_for_disabled_permissions()

作用:验证用户明确把网页搜索设为 Disabled 时,权限档案不会把它改成 Live。

数据流:输入是允许任意值但当前值为 Disabled 的设置,以及 Disabled 权限档案;解析后;输出仍是 Disabled。

调用关系:测试运行器调用它;它和偏向 Live 的测试配对,确认显式禁用具有最高优先级。

调用图:调用 1 个内部函数(allow_any);外部调用 1 个(assert_eq!)。

web_search_mode_for_turn_falls_back_when_live_is_disallowed5003–5021 ↗
fn web_search_mode_for_turn_falls_back_when_live_is_disallowed() -> anyhow::Result<()>

作用:验证如果策略不允许 Live 搜索,系统会退回到允许的偏好值,而不是硬切到 Live。

数据流:输入是一个受约束的网页搜索设置,只允许 Disabled 和 Cached;当前权限档案会尝试偏向 Live;解析后;输出回到 Cached。

调用关系:测试运行器调用它;它通过 Constrained 约束对象检查 resolve_web_search_mode_for_turn 是否尊重外部要求。

调用图:调用 1 个内部函数(new);外部调用 1 个(assert_eq!)。

project_profiles_are_ignored5024–5072 ↗
async fn project_profiles_are_ignored() -> std::io::Result<()>

作用:验证项目本地 .codex/config.toml 里的 profile 和 profiles 设置会被忽略,并给出启动警告。这样项目仓库不能偷偷切换用户模型配置。

数据流:输入是用户配置里信任某工作区,项目配置里写 profile 和 profiles;ConfigBuilder 加载后;输出是 config.model 没有被项目 profile 改掉,并且 startup_warnings 里有提示用户把这些设置放到用户级配置。

调用关系:测试运行器调用它;它通过 ConfigBuilder::without_managed_config_for_tests 模拟真实加载流程,检查项目配置的安全边界。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 7 个(default, new, assert!, assert_eq!, format!, create_dir_all, write)。

feature_table_overrides_legacy_flags5075–5094 ↗
async fn feature_table_overrides_legacy_flags() -> std::io::Result<()>

作用:验证新的 [features] 表可以覆盖旧功能开关的默认行为。这样功能开关迁移后,用户写的新表是准的。

数据流:输入是 features.apply_patch_freeform = false;加载配置后;输出是 Feature::ApplyPatchFreeform 未启用。

调用关系:测试运行器调用它;它检查 FeaturesToml 进入最终 config.features 的优先级。

调用图:调用 1 个内部函数(from);外部调用 6 个(new, default, new, load_from_base_config_with_overrides, assert!, default)。

legacy_toggles_map_to_features5097–5116 ↗
async fn legacy_toggles_map_to_features() -> std::io::Result<()>

作用:验证旧的 experimental_use_unified_exec_tool 开关仍会映射到新的 Feature::UnifiedExec。这样老配置不会失效。

数据流:输入是旧字段 experimental_use_unified_exec_tool = true;加载后;输出是新 features 里 UnifiedExec 启用,同时旧兼容字段也为 true。

调用关系:测试运行器调用它;它保护旧配置字段到新功能系统的兼容映射。

调用图:外部调用 5 个(default, new, load_from_base_config_with_overrides, assert!, default)。

responses_websocket_features_do_not_change_wire_api5119–5140 ↗
async fn responses_websocket_features_do_not_change_wire_api() -> std::io::Result<()>

作用:验证 responses_websockets 相关功能开关不会改变底层 wire API。wire API 可以理解为客户端和模型服务说话的协议格式。

数据流:输入是分别开启 responses_websockets 和 responses_websockets_v2 的配置;加载后;输出是 model_provider.wire_api 仍为 Responses。

调用关系:测试运行器调用它;它防止实验性 websocket 开关误改模型供应商协议类型。

调用图:调用 1 个内部函数(from);外部调用 6 个(new, default, new, load_from_base_config_with_overrides, assert_eq!, default)。

config_honors_explicit_file_oauth_store_mode5143–5163 ↗
async fn config_honors_explicit_file_oauth_store_mode() -> std::io::Result<()>

作用:验证用户明确把 MCP OAuth 凭据存储设为 file 时,最终配置会照做。

数据流:输入是 mcp_oauth_credentials_store = File;加载配置后;输出是 config.mcp_oauth_credentials_store_mode 为 File。

调用关系:测试运行器调用它;它检查显式配置不会被默认解析逻辑覆盖。

调用图:外部调用 5 个(default, new, load_from_base_config_with_overrides, assert_eq!, default)。

managed_config_overrides_oauth_store_mode5166–5212 ↗
async fn managed_config_overrides_oauth_store_mode() -> anyhow::Result<()>

作用:验证托管配置可以覆盖用户配置里的 OAuth 凭据存储方式。托管配置通常来自企业管理,优先级更高。

数据流:输入是用户 config.toml 写 file、托管配置写 keyring;加载配置层并反序列化后;输出是中间 TOML 和最终 Config 都采用 keyring,并按版本解析最终模式。

调用关系:测试运行器调用它;它使用 load_config_layers_state、deserialize_config_toml_with_base 和 Config 加载,检查配置层优先级链条。

调用图:调用 1 个内部函数(with_managed_config_path_for_tests);外部调用 6 个(new, new, load_from_base_config_with_overrides, assert_eq!, default, write)。

load_global_mcp_servers_returns_empty_if_missing5215–5222 ↗
async fn load_global_mcp_servers_returns_empty_if_missing() -> anyhow::Result<()>

作用:验证没有全局配置文件时,读取全局 MCP 服务器会返回空表,而不是报错。

数据流:输入是一个没有 config.toml 的临时 Codex 主目录;调用 load_global_mcp_servers;输出是空集合。

调用关系:测试运行器调用它;它检查读取全局 MCP 配置的容错默认行为。

调用图:外部调用 2 个(new, assert!)。

replace_mcp_servers_round_trips_entries5225–5294 ↗
async fn replace_mcp_servers_round_trips_entries() -> anyhow::Result<()>

作用:验证用配置编辑操作替换 MCP 服务器后,再读回来内容不丢不变。这个测试保证写配置和读配置是一套兼容格式。

数据流:输入是一个完整的 docs MCP 服务器配置,包括命令、参数、工作目录、超时、环境等;apply_blocking 写入后 load_global_mcp_servers 读回;输出是所有关键字段匹配。随后写入空表,再读回确认服务器被清空。

调用关系:测试运行器调用它;它串起 ConfigEdit::ReplaceMcpServers、apply_blocking 和 load_global_mcp_servers,检查配置文件的读写闭环。

调用图:调用 1 个内部函数(apply_blocking);外部调用 10 个(new, from_secs, new, new, new, ReplaceMcpServers, assert!, assert_eq!, panic!, vec!)。

managed_config_wins_over_cli_overrides5297–5329 ↗
async fn managed_config_wins_over_cli_overrides() -> anyhow::Result<()>

作用:验证托管配置的优先级高于命令行覆盖项。企业指定的模型不应被用户启动参数轻易改掉。

数据流:输入是用户配置 model=base、CLI 覆盖 model=cli、托管配置 model=managed_config;加载配置层后反序列化;输出是最终 cfg.model 为 managed_config。

调用关系:测试运行器调用它;它检查 load_config_layers_state 合并配置层时的优先级顺序。

调用图:调用 1 个内部函数(with_managed_config_path_for_tests);外部调用 4 个(new, String, assert_eq!, write)。

load_global_mcp_servers_accepts_legacy_ms_field5332–5351 ↗
async fn load_global_mcp_servers_accepts_legacy_ms_field() -> anyhow::Result<()>

作用:验证读取 MCP 服务器时还能接受旧字段 startup_timeout_ms。这样旧配置里的毫秒单位超时不会坏掉。

数据流:输入是 config.toml 里写 command 和 startup_timeout_ms = 2500;读取全局 MCP 服务器后;输出是 startup_timeout_sec 等于 2500 毫秒对应的 Duration。

调用关系:测试运行器调用它;它检查 load_global_mcp_servers 的旧字段兼容解析。

调用图:外部调用 3 个(new, assert_eq!, write)。

mcp_servers_toml_parses_per_tool_approval_overrides5354–5383 ↗
fn mcp_servers_toml_parses_per_tool_approval_overrides()

作用:验证 MCP 服务器可以给单个工具单独设置审批模式。比如服务器默认要询问,但某个工具可以直接批准。

数据流:输入是一段 TOML,包含服务器默认审批模式和 docs.tools.search 的审批模式;反序列化成 ConfigToml;输出是服务器默认值为 Prompt,search 工具配置为 Approve。

调用关系:测试运行器调用它;它直接测试 ConfigToml 的 TOML 解析结构,确保 per-tool 配置能进内存模型。

调用图:外部调用 1 个(assert_eq!)。

mcp_servers_toml_ignores_unknown_server_fields5386–5400 ↗
fn mcp_servers_toml_ignores_unknown_server_fields()

作用:验证 MCP 服务器配置里出现未知字段时会被忽略,而不是导致整个配置失败。这样向前兼容性更好。

数据流:输入是带 trust_level 这个未知字段的 MCP 服务器 TOML;反序列化后;输出仍得到正常的 stdio MCP 配置。

调用关系:测试运行器调用它;它检查 ConfigToml 的反序列化策略,防止旧客户端遇到新字段就崩。

调用图:外部调用 1 个(assert_eq!)。

mcp_servers_toml_parses_tool_approval_override_for_reserved_name5403–5426 ↗
fn mcp_servers_toml_parses_tool_approval_override_for_reserved_name()

作用:验证即使工具名叫 command 这种容易和服务器字段混淆的名字,也能作为工具配置被正确解析。

数据流:输入是 mcp_servers.docs.tools.command.approval_mode;反序列化后查找 docs 服务器的 command 工具;输出是该工具审批模式为 Approve。

调用关系:测试运行器调用它;它保护 TOML 层级解析,避免保留字段名影响工具名。

调用图:外部调用 1 个(assert_eq!)。

desktop_toml_round_trips_opaque_nested_values5429–5477 ↗
fn desktop_toml_round_trips_opaque_nested_values() -> anyhow::Result<()>

作用:验证 desktop 配置可以保存前端需要的任意嵌套值,并且序列化再读回来不变。这里 opaque 表示后端不理解内容,只负责原样保存。

数据流:输入是包含字符串、数组、嵌套表和内联对象的 desktop TOML;解析后检查它们变成对应 JSON 值;再转回 TOML 并重新解析;输出是 reparsed.desktop 与原值一致。

调用关系:测试运行器调用它;它检查 ConfigToml.desktop 作为“透明盒子”的读写往返能力。

调用图:外部调用 2 个(assert_eq!, to_string)。

to_mcp_config_preserves_apps_feature_from_config5480–5504 ↗
async fn to_mcp_config_preserves_apps_feature_from_config() -> std::io::Result<()>

作用:验证转换成 MCP 配置时,会保留 Apps 功能开关和产品 SKU。SKU 可以理解为产品套餐或版本标识。

数据流:输入是一个 Config,并手动设置 apps_mcp_product_sku;转换成 MCP 配置后;输出 apps_enabled 随 Feature::Apps 的启用或禁用变化,SKU 仍传过去。

调用关系:测试运行器调用它;它检查 Config::to_mcp_config 从通用配置传递 Apps 相关字段到 MCP 运行配置。

调用图:调用 1 个内部函数(new);外部调用 6 个(new, load_from_base_config_with_overrides, assert!, assert_eq!, default, default)。

to_mcp_config_flows_mcp_tool_prefix_from_feature5507–5525 ↗
async fn to_mcp_config_flows_mcp_tool_prefix_from_feature() -> std::io::Result<()>

作用:验证 MCP 工具名前缀策略会从功能开关流入 MCP 配置。默认加前缀,开启 NonPrefixedMcpToolNames 后不加。

数据流:输入是默认 Config;第一次转换输出 prefix_mcp_tool_names 为 true;启用 Feature::NonPrefixedMcpToolNames 后再转换;输出变为 false。

调用关系:测试运行器调用它;它检查 Config::to_mcp_config 是否正确读取 features 来决定工具命名方式。

调用图:调用 1 个内部函数(new);外部调用 5 个(new, load_from_base_config_with_overrides, assert!, default, default)。

to_mcp_config_preserves_auth_elicitation_feature_from_config5528–5555 ↗
async fn to_mcp_config_preserves_auth_elicitation_feature_from_config() -> std::io::Result<()>

作用:验证开启 AuthElicitation 功能后,MCP 客户端会声明自己支持认证引导能力。认证引导就是服务端可以要求客户端弹表单或打开链接让用户授权。

数据流:输入是默认 Config;第一次转换得到默认 elicitation 能力;启用 Feature::AuthElicitation 后再转换;输出包含表单和 URL 两种引导能力。

调用关系:测试运行器调用它;它检查 Config::to_mcp_config 是否把认证交互能力从功能开关传给 MCP 客户端配置。

调用图:调用 1 个内部函数(new);外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, default)。

load_global_mcp_servers_rejects_inline_bearer_token5558–5580 ↗
async fn load_global_mcp_servers_rejects_inline_bearer_token() -> anyhow::Result<()>

作用:验证全局 MCP 配置不允许直接写 bearer_token 明文密钥,而是要求使用 bearer_token_env_var 这类环境变量方式。这样可以避免把秘密写进配置文件。

数据流:输入是一个 HTTP MCP 服务器配置,里面直接写 bearer_token = secret;调用 load_global_mcp_servers;输出是 InvalidData 错误,错误信息同时提到 bearer_token 和 bearer_token_env_var。

调用关系:测试运行器调用它;它检查全局 MCP 配置读取时的安全校验,防止敏感令牌被明文接受。

调用图:外部调用 4 个(new, assert!, assert_eq!, write)。

replace_mcp_servers_serializes_env_sorted5583–5659 ↗
async fn replace_mcp_servers_serializes_env_sorted() -> anyhow::Result<()>

作用:测试写入 MCP 标准输入输出服务器时,普通环境变量会被保存下来,而且在文件里按固定顺序排列。这样配置文件每次生成都稳定,不会因为哈希表顺序不同而忽左忽右。

数据流:它先创建一个临时 Codex 主目录,准备一个名叫 docs 的 MCP 服务器配置,里面有命令、参数和两个环境变量。然后调用 apply_blocking 把配置写进 config.toml,读取文件确认 ALPHA_VAR 排在 ZIG_VAR 前面。最后再用 load_global_mcp_servers 读回来,确认命令、参数、环境变量都还在,env_vars 为空,cwd 没有被误填。

调用关系:这个测试由测试运行器触发,核心是在模拟用户替换 MCP 服务器配置时走一遍真实写入流程。它把工作交给 apply_blocking 写文件,再交给 load_global_mcp_servers 验证读取结果,确保写和读两头能对上。

调用图:调用 1 个内部函数(apply_blocking);外部调用 11 个(from, from, new, new, new, ReplaceMcpServers, assert!, assert_eq!, panic!, read_to_string (+1 more))。

replace_mcp_servers_serializes_env_vars5662–5714 ↗
async fn replace_mcp_servers_serializes_env_vars() -> anyhow::Result<()>

作用:测试 MCP 服务器可以保存 env_vars 这种“从外部环境继承哪些变量名”的配置。它防止这些变量名写入后丢失。

数据流:它创建临时目录和 docs 服务器配置,env 为空,但 env_vars 里有 ALPHA 和 BETA。写入 config.toml 后,它检查文件里出现 env_vars = ["ALPHA", "BETA"]。再读回配置,确认 docs 的 env_vars 仍然是这两个名字。

调用关系:测试运行器执行它时,它会通过 apply_blocking 走配置编辑写入路径,再通过 load_global_mcp_servers 走读取路径,用来保护 env_vars 的序列化和反序列化。

调用图:调用 1 个内部函数(apply_blocking);外部调用 10 个(from, new, new, new, ReplaceMcpServers, assert!, assert_eq!, panic!, read_to_string, vec!)。

replace_mcp_servers_serializes_sourced_env_vars5717–5770 ↗
async fn replace_mcp_servers_serializes_sourced_env_vars() -> anyhow::Result<()>

作用:测试 env_vars 不只支持简单变量名,也支持带来源说明的变量。比如 REMOTE_TOKEN 可以标明来自 remote。

数据流:它准备一个 MCP 服务器,其中 env_vars 同时包含旧式字符串 LEGACY 和带 name、source 的结构化变量 REMOTE_TOKEN。写入文件后,它检查 TOML 里既有字符串也有内联表。随后读回全部 MCP 服务器配置,并直接确认读回结果和原始 servers 完全一致。

调用关系:它覆盖的是 MCP 环境变量格式的兼容性:旧写法和新写法要能混用。apply_blocking 负责写,load_global_mcp_servers 负责读,测试把两者连起来验证。

调用图:调用 1 个内部函数(apply_blocking);外部调用 9 个(from, new, new, new, ReplaceMcpServers, assert!, assert_eq!, read_to_string, vec!)。

replace_mcp_servers_serializes_cwd5773–5826 ↗
async fn replace_mcp_servers_serializes_cwd() -> anyhow::Result<()>

作用:测试 MCP 标准输入输出服务器的工作目录 cwd 会被正确保存和读回。cwd 就是启动这个外部服务器时所在的文件夹。

数据流:它创建 docs 服务器配置,并把 cwd 设为 /tmp/codex-mcp。写入 config.toml 后,它检查文件包含 cwd = "/tmp/codex-mcp"。再读回配置,确认 docs 的 cwd 确实是这个路径。

调用关系:这个测试保护的是启动外部 MCP 进程时的重要路径设置。测试流程仍然是 apply_blocking 写入,再由 load_global_mcp_servers 读取确认。

调用图:调用 1 个内部函数(apply_blocking);外部调用 10 个(from, new, from, new, new, ReplaceMcpServers, assert!, assert_eq!, panic!, read_to_string)。

replace_mcp_servers_streamable_http_serializes_bearer_token5829–5893 ↗
async fn replace_mcp_servers_streamable_http_serializes_bearer_token() -> anyhow::Result<()>

作用:测试使用 Streamable HTTP 连接方式的 MCP 服务器,可以保存 bearer_token_env_var。它表示认证令牌不直接写进配置,而是从某个环境变量里取,更安全。

数据流:它创建一个 HTTP 型 MCP 服务器,设置 url、bearer_token_env_var 和启动超时时间。写入后,它要求 config.toml 精确等于预期文本。再读回配置,确认 URL、令牌环境变量名、空的请求头配置,以及 startup_timeout_sec 都正确。

调用关系:它测试的是 HTTP 型 MCP 服务器的写入格式。apply_blocking 负责把结构体变成 TOML,load_global_mcp_servers 再把 TOML 变回结构体。

调用图:调用 1 个内部函数(apply_blocking);外部调用 9 个(from, from_secs, new, new, ReplaceMcpServers, assert!, assert_eq!, panic!, read_to_string)。

replace_mcp_servers_streamable_http_serializes_custom_headers5896–5973 ↗
async fn replace_mcp_servers_streamable_http_serializes_custom_headers() -> anyhow::Result<()>

作用:测试 HTTP 型 MCP 服务器可以保存自定义请求头。请求头可以理解成发 HTTP 请求时附带的小标签,用来传认证或额外信息。

数据流:它准备 docs 服务器,里面有 URL、令牌环境变量、自定义 http_headers,以及从环境变量取值的 env_http_headers。写入后,它要求文件内容精确包含两个独立的小节。再读回来,确认 X-Doc 和 X-Auth 两类头都没有丢。

调用关系:这个测试覆盖 HTTP MCP 的额外认证信息保存。它通过 apply_blocking 写出嵌套 TOML 小节,再通过 load_global_mcp_servers 检查这些小节能回到正确字段。

调用图:调用 1 个内部函数(apply_blocking);外部调用 9 个(from, from_secs, from, new, new, ReplaceMcpServers, assert_eq!, panic!, read_to_string)。

replace_mcp_servers_streamable_http_removes_optional_sections5976–6077 ↗
async fn replace_mcp_servers_streamable_http_removes_optional_sections() -> anyhow::Result<()>

作用:测试当 HTTP MCP 服务器的可选字段被清空时,旧的配置小节会从文件里删除。这样不会留下已经不用的令牌或请求头。

数据流:它先写入一个带 bearer token、普通请求头、环境请求头和超时的 docs 配置,并确认这些内容存在。然后把同一个服务器改成只剩 URL,再次写入。最后检查 config.toml 只剩 URL,并确认读回时令牌、请求头和启动超时都为空。

调用关系:这是防止“旧配置残留”的测试。它连续调用两次 apply_blocking,模拟用户先设置再删除可选项,然后用 load_global_mcp_servers 证明删除真的生效。

调用图:调用 1 个内部函数(apply_blocking);外部调用 10 个(from, from_secs, from, new, new, ReplaceMcpServers, assert!, assert_eq!, panic!, read_to_string)。

replace_mcp_servers_streamable_http_isolates_headers_between_servers6080–6196 ↗
async fn replace_mcp_servers_streamable_http_isolates_headers_between_servers() -> anyhow::Result<()>

作用:测试一个 MCP 服务器的 HTTP 请求头不会误写到另一个服务器身上。多服务器配置里,这一点很重要。

数据流:它准备两个服务器:docs 是 HTTP 型并带请求头,logs 是标准输入输出型且不带请求头。写入文件后,它检查 docs 有 headers 小节,但 logs 没有 headers、env_headers 或 bearer token。再读回配置,确认 docs 的头还在,logs 没有被污染。

调用关系:它保护的是多服务器配置之间的边界。apply_blocking 写出整个服务器列表,load_global_mcp_servers 读回后,测试确认每个服务器只拿到属于自己的字段。

调用图:调用 1 个内部函数(apply_blocking);外部调用 12 个(from, from_secs, from, new, new, new, ReplaceMcpServers, assert!, assert_eq!, panic! (+2 more))。

replace_mcp_servers_serializes_disabled_flag6199–6246 ↗
async fn replace_mcp_servers_serializes_disabled_flag() -> anyhow::Result<()>

作用:测试 MCP 服务器被禁用时,enabled = false 会写进配置文件,并且读回来仍然是禁用状态。

数据流:它创建一个 enabled 为 false 的 docs 服务器,写入 config.toml。然后读取文件确认包含 enabled = false,再加载 MCP 服务器配置,确认 docs.enabled 是 false。

调用关系:它验证用户关闭某个 MCP 服务器的选择不会丢失。写入由 apply_blocking 完成,读取由 load_global_mcp_servers 完成。

调用图:调用 1 个内部函数(apply_blocking);外部调用 7 个(from, new, new, new, ReplaceMcpServers, assert!, read_to_string)。

replace_mcp_servers_serializes_required_flag6249–6296 ↗
async fn replace_mcp_servers_serializes_required_flag() -> anyhow::Result<()>

作用:测试 MCP 服务器标记为 required 时会被保存。required 可以理解成“这个服务器是必须的”。

数据流:它创建一个 required 为 true 的 docs 服务器,写入配置文件。随后检查文件包含 required = true,再读回配置,确认 docs.required 为 true。

调用关系:它覆盖 MCP 服务器的必需标记。测试运行时通过真实写读流程,防止这个布尔开关在保存时被忽略。

调用图:调用 1 个内部函数(apply_blocking);外部调用 7 个(from, new, new, new, ReplaceMcpServers, assert!, read_to_string)。

replace_mcp_servers_serializes_tool_filters6299–6351 ↗
async fn replace_mcp_servers_serializes_tool_filters() -> anyhow::Result<()>

作用:测试 MCP 服务器的工具白名单和黑名单会保存下来。enabled_tools 是允许的工具,disabled_tools 是禁用的工具。

数据流:它创建 docs 服务器,设置 enabled_tools 为 allowed,disabled_tools 为 blocked。写入后检查 TOML 中包含这两个数组。再读回来,确认配置对象里两个列表也保持原样。

调用关系:它验证工具过滤规则能穿过配置写入和读取两道关。apply_blocking 写入,load_global_mcp_servers 读取,断言负责比较结果。

调用图:调用 1 个内部函数(apply_blocking);外部调用 9 个(from, new, new, new, ReplaceMcpServers, assert!, assert_eq!, read_to_string, vec!)。

replace_mcp_servers_streamable_http_serializes_oauth_resource6354–6405 ↗
async fn replace_mcp_servers_streamable_http_serializes_oauth_resource() -> anyhow::Result<()>

作用:测试 HTTP MCP 服务器的 OAuth 配置能保存。OAuth 是一种常见登录授权机制,这里要保存客户端编号和资源地址。

数据流:它准备一个 HTTP 型 docs 服务器,带 oauth.client_id 和 oauth_resource。写入文件后,确认有 oauth 小节、client_id 和 oauth_resource。再读回配置,确认资源地址和客户端编号都能取到。

调用关系:它保护 MCP 服务器认证配置的读写。apply_blocking 负责落盘,load_global_mcp_servers 负责加载,oauth_client_id 方法负责从读回的配置中取客户端编号。

调用图:调用 1 个内部函数(apply_blocking);外部调用 7 个(from, new, new, ReplaceMcpServers, assert!, assert_eq!, read_to_string)。

set_model_updates_defaults6408–6423 ↗
async fn set_model_updates_defaults() -> anyhow::Result<()>

作用:测试通过配置编辑器设置默认模型时,模型名和推理强度都会写进默认配置。推理强度可以理解成模型回答前“思考得多不多”的档位。

数据流:它创建临时 Codex 主目录,用 ConfigEditsBuilder 设置模型为 gpt-5.4、推理强度为 High,并应用修改。然后读取 config.toml,解析成 ConfigToml,确认 model 和 model_reasoning_effort 都是新值。

调用关系:它测试 ConfigEditsBuilder 的普通写配置路径。测试运行器调用它,它再通过 builder 的 set_model 和 apply 完成实际修改。

调用图:调用 1 个内部函数(new);外部调用 4 个(new, assert_eq!, read_to_string, from_str)。

for_config_writes_selected_user_config_file6426–6458 ↗
async fn for_config_writes_selected_user_config_file() -> anyhow::Result<()>

作用:测试当用户选择了某个单独的配置文件时,修改会写到这个文件,而不是默认 config.toml。这样多套配置不会互相覆盖。

数据流:它先写一个基础 config.toml 和一个 work.config.toml。然后构造 Config,把 user_config_path 指向 work.config.toml。接着用 ConfigEditsBuilder::for_config 设置新模型。最后确认 selected_config 被改成新模型和 High 推理强度,而 base_config 仍保持原文。

调用关系:这个测试模拟用户使用指定 profile 配置文件的场景。ConfigBuilder 先加载带覆盖项的配置,ConfigEditsBuilder::for_config 再根据这个配置决定要写哪个文件。

调用图:调用 2 个内部函数(without_managed_config_for_tests, without_managed_config_for_tests);外部调用 6 个(new, assert_eq!, for_config, read_to_string, write, from_str)。

profile_v2_config_path_resolves_validated_names6461–6469 ↗
fn profile_v2_config_path_resolves_validated_names() -> anyhow::Result<()>

作用:测试新版 profile 名称会解析成正确的配置文件路径。比如 work 会对应 work.config.toml。

数据流:它创建临时 Codex 主目录,把字符串 work 解析成经过校验的 ProfileV2Name。然后调用 resolve_profile_v2_config_path,确认结果是临时目录下的 work.config.toml 绝对路径。

调用关系:它专门验证 profile 文件命名规则。其他加载和编辑配置的流程会依赖这个规则找到正确文件。

调用图:外部调用 2 个(new, assert_eq!)。

set_model_overwrites_existing_model6472–6507 ↗
async fn set_model_overwrites_existing_model() -> anyhow::Result<()>

作用:测试设置默认模型会覆盖已有默认模型,但不会改动某个 profile 里的模型。profile 可以理解成一套单独的使用场景配置。

数据流:它先写入一个已有 model、model_reasoning_effort 和 profiles.dev.model 的配置文件。然后用 ConfigEditsBuilder 把默认模型改成 o4-mini,推理强度改成 High。读回后确认顶层默认值变了,但 profiles.dev.model 仍是 gpt-4.1。

调用关系:它验证配置编辑器只改该改的层级。set_model 面向顶层默认配置,不能误伤 profiles 下的局部配置。

调用图:调用 1 个内部函数(new);外部调用 5 个(new, assert_eq!, read_to_string, write, from_str)。

PrecedenceTestFixture::cwd_path6516–6518 ↗
fn cwd_path(&self) -> PathBuf

作用:这个小工具函数返回测试夹具里的当前工作目录路径。它让测试代码不用直接碰内部字段。

数据流:它读取 self.cwd 这个临时目录对象,取出路径,并转换成 PathBuf 返回。它不修改任何状态。

调用关系:它属于测试夹具的辅助方法,其他优先级相关测试可以用它拿到模拟的当前目录。

调用图:外部调用 1 个(path)。

PrecedenceTestFixture::codex_home6520–6522 ↗
fn codex_home(&self) -> AbsolutePathBuf

作用:这个小工具函数返回测试夹具里的 Codex 主目录绝对路径。Codex 主目录就是放用户配置的地方。

数据流:它读取 self.codex_home,并调用 abs 把路径变成绝对路径返回。它只提供路径,不改文件。

调用关系:它为测试中的配置加载流程提供 codex_home 参数,避免每个测试重复写路径转换。

调用图:外部调用 1 个(abs)。

cli_override_sets_compact_prompt6526–6546 ↗
async fn cli_override_sets_compact_prompt() -> std::io::Result<()>

作用:测试命令行覆盖项可以设置 compact_prompt。compact_prompt 是用于“压缩/总结上下文”时的提示词。

数据流:它创建临时 Codex 主目录,构造 ConfigOverrides,把 compact_prompt 设置成一段文字。然后用默认 ConfigToml 加这些覆盖项加载 Config,最后确认 config.compact_prompt 就是覆盖值。

调用关系:它验证命令行参数的优先级能进入最终配置。Config::load_from_base_config_with_overrides 是被测试的主要入口。

调用图:外部调用 5 个(default, new, load_from_base_config_with_overrides, assert_eq!, default)。

loads_compact_prompt_from_file6549–6576 ↗
async fn loads_compact_prompt_from_file() -> std::io::Result<()>

作用:测试 compact_prompt 可以从文件读取,并且会去掉前后空白。这样长提示词可以放在单独文本文件里。

数据流:它创建 workspace 和 compact_prompt.txt,文件内容前后有空格。ConfigToml 指向这个文件,ConfigOverrides 指定当前工作目录。加载配置后,确认 compact_prompt 是去掉空格后的 summarize differently。

调用关系:它覆盖配置加载时读取外部提示词文件的路径。Config::load_from_base_config_with_overrides 负责把 TOML 和覆盖项合成最终 Config。

调用图:外部调用 6 个(default, new, load_from_base_config_with_overrides, assert_eq!, create_dir_all, write)。

load_config_uses_requirements_guardian_policy_config6579–6611 ↗
async fn load_config_uses_requirements_guardian_policy_config() -> std::io::Result<()>

作用:测试托管的 requirements 配置里可以提供 guardian_policy_config。guardian policy 可以理解成自动审查或安全规则说明。

数据流:它创建 ConfigLayerStack,并在 requirements 层放入带前后空格的 guardian_policy_config。然后调用 Config::load_config_with_layer_stack 加载配置。最终确认 config.guardian_policy_config 被设置,并且前后空格被去掉。

调用关系:它验证配置层堆叠中的托管要求能影响最终配置。ConfigLayerStack 提供上层规则,load_config_with_layer_stack 合并这些规则。

调用图:调用 1 个内部函数(new);外部调用 6 个(default, new, new, load_config_with_layer_stack, assert_eq!, default)。

config_toml_deserializes_auto_review_policy6614–6629 ↗
fn config_toml_deserializes_auto_review_policy()

作用:测试 config.toml 里的 [auto_review] policy 字段可以被正确解析。也就是文件文本能变成程序里的 ConfigToml 结构。

数据流:它把一段 TOML 字符串交给 toml::from_str 解析成 ConfigToml。然后检查 auto_review.policy 的值等于用户写入的策略文本。

调用关系:这是配置格式本身的解析测试。它不走完整加载流程,只验证 TOML 字段名和结构体字段能对应上。

调用图:外部调用 1 个(assert_eq!)。

load_config_uses_auto_review_guardian_policy_config6632–6657 ↗
async fn load_config_uses_auto_review_guardian_policy_config() -> std::io::Result<()>

作用:测试用户配置里的 auto_review.policy 会变成最终的 guardian_policy_config,并且会去掉前后空白。

数据流:它构造一个 ConfigToml,其中 auto_review.policy 带前后空格。加载配置后,确认 config.guardian_policy_config 是修剪后的文本。

调用关系:它验证旧一点或用户侧的 auto_review 配置能接到最终配置字段上。主要被测入口是 Config::load_from_base_config_with_overrides。

调用图:外部调用 4 个(default, new, load_from_base_config_with_overrides, assert_eq!)。

requirements_guardian_policy_beats_auto_review6660–6696 ↗
async fn requirements_guardian_policy_beats_auto_review() -> std::io::Result<()>

作用:测试当托管 requirements 和用户 auto_review 都提供策略时,托管 requirements 的策略优先。这样组织或工作区强制规则不会被个人配置覆盖。

数据流:它创建一个带托管 guardian_policy_config 的 ConfigLayerStack,同时基础 ConfigToml 里也有 auto_review.policy。加载后,确认最终 guardian_policy_config 使用托管文本,而不是用户文本。

调用关系:它验证配置优先级。Config::load_config_with_layer_stack 合并各层时,requirements 层应该压过普通用户配置。

调用图:调用 1 个内部函数(new);外部调用 5 个(default, new, new, load_config_with_layer_stack, assert_eq!)。

load_config_ignores_empty_auto_review_guardian_policy_config6699–6721 ↗
async fn load_config_ignores_empty_auto_review_guardian_policy_config() -> std::io::Result<()>

作用:测试 auto_review.policy 如果只有空格,就会被当成没配置。这样不会把一段空白当成有效规则。

数据流:它构造 auto_review.policy 为几个空格的 ConfigToml。加载配置后,确认 config.guardian_policy_config 是 None,也就是没有策略。

调用关系:它保护配置清洗逻辑。Config::load_from_base_config_with_overrides 在合成最终配置时应忽略空白字符串。

调用图:外部调用 4 个(default, new, load_from_base_config_with_overrides, assert_eq!)。

load_config_ignores_empty_requirements_guardian_policy_config6724–6751 ↗
async fn load_config_ignores_empty_requirements_guardian_policy_config() -> std::io::Result<()>

作用:测试托管 requirements 里的 guardian_policy_config 如果只有空格,也会被忽略。即使来源更高优先级,空内容也不算有效规则。

数据流:它创建一个 requirements 层,其中 guardian_policy_config 是空白字符串。加载配置后,确认最终 config.guardian_policy_config 为 None。

调用关系:它和用户 auto_review 的空白测试对应,验证高优先级配置也要经过同样的有效性检查。

调用图:调用 1 个内部函数(new);外部调用 6 个(default, new, new, load_config_with_layer_stack, assert_eq!, default)。

load_config_rejects_missing_agent_role_config_file6754–6788 ↗
async fn load_config_rejects_missing_agent_role_config_file() -> std::io::Result<()>

作用:测试 Agent 角色引用的 config_file 不存在时,配置加载会报错。这样用户不会以为某个角色已经配置好,实际却缺文件。

数据流:它构造一个 agents.researcher 配置,config_file 指向不存在的 researcher.toml。调用配置加载后期待失败,并检查错误类型是 InvalidInput,错误信息提到 agents.researcher.config_file 和必须指向已有文件。

调用关系:它验证 Config::load_from_base_config_with_overrides 的输入校验。角色配置文件路径必须真实存在,否则加载阶段就阻止继续。

调用图:外部调用 7 个(from, default, new, load_from_base_config_with_overrides, assert!, assert_eq!, default)。

agent_role_relative_config_file_resolves_against_config_toml6791–6837 ↗
async fn agent_role_relative_config_file_resolves_against_config_toml() -> std::io::Result<()>

作用:测试 Agent 角色里的相对 config_file 路径,会相对于 config.toml 所在目录解析。这样用户写 ./agents/researcher.toml 会符合直觉。

数据流:它在临时 Codex 主目录下创建 agents/researcher.toml,并在 config.toml 里写相对路径和昵称候选。加载配置后,确认 researcher 角色的 config_file 是实际绝对路径,nickname_candidates 也读出来。

调用关系:它通过 ConfigBuilder 加载真实文件布局,验证角色文件路径解析规则和角色元数据读取。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 4 个(new, assert_eq!, create_dir_all, write)。

agent_role_relative_config_file_resolves_from_config_layer6840–6895 ↗
async fn agent_role_relative_config_file_resolves_from_config_layer() -> std::io::Result<()>

作用:测试如果 Agent 角色来自某个配置层,相对 config_file 也会按该层来源文件的位置解析。

数据流:它创建 researcher.toml 文件,再构造一个 ConfigLayerEntry,来源标记为用户 config.toml,内容里写 ./agents/researcher.toml。加载配置层堆叠后,确认最终角色的 config_file 指向真实文件。

调用关系:它验证不只是磁盘上的 config.toml,分层配置来源也带有“基准目录”。ConfigLayerStack 提供来源信息,load_config_with_layer_stack 负责解析。

调用图:调用 1 个内部函数(new);外部调用 10 个(default, new, load_config_with_layer_stack, assert_eq!, default, default, create_dir_all, write, from_str, vec!)。

agent_role_file_metadata_overrides_config_toml_metadata6898–6946 ↗
async fn agent_role_file_metadata_overrides_config_toml_metadata() -> std::io::Result<()>

作用:测试 Agent 角色文件里的元数据会覆盖 config.toml 里的同名元数据。角色自己的说明文件更具体,所以优先。

数据流:它创建 researcher.toml,里面写 description、nickname_candidates、developer_instructions 和 model;config.toml 里也给 researcher 写了不同的 description 和 nickname_candidates。加载后,确认最终 description 和昵称来自角色文件,config_file 路径正确。

调用关系:它验证角色配置合并顺序。ConfigBuilder 加载 config.toml 后还会读取角色文件,文件内元数据应覆盖外层声明。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 4 个(new, assert_eq!, create_dir_all, write)。

agent_role_file_without_developer_instructions_is_dropped_with_warning6949–7014 ↗
async fn agent_role_file_without_developer_instructions_is_dropped_with_warning() -> std::io::Result<()>

作用:测试自动发现的独立 Agent 角色文件如果没有 developer_instructions,会被丢弃并给出警告。developer_instructions 是告诉这个角色该怎么工作的核心说明。

数据流:它创建一个模拟 Git 仓库和受信任项目配置,再放两个独立角色文件:researcher 缺 developer_instructions,reviewer 有完整说明。加载配置后,确认 researcher 不在 agent_roles,reviewer 保留,并且 startup_warnings 里有必须定义 developer_instructions 的提示。

调用关系:它覆盖工作区 .codex/agents 自动发现角色的校验。ConfigBuilder 会根据 cwd 找仓库和角色文件,并把不合格文件转成启动警告。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 8 个(default, new, assert!, assert_eq!, format!, create_dir_all, create_dir_all, write)。

legacy_agent_role_config_file_allows_missing_developer_instructions7017–7065 ↗
async fn legacy_agent_role_config_file_allows_missing_developer_instructions() -> std::io::Result<()>

作用:测试旧式通过 config.toml 明确引用的 Agent 角色文件,可以没有 developer_instructions。这样旧用户配置不会突然坏掉。

数据流:它创建一个 researcher.toml,里面只有模型相关配置,没有 developer_instructions。config.toml 里用 [agents.researcher] 明确引用这个文件并提供 description。加载后,确认 researcher 角色仍然存在,description 和 config_file 正确。

调用关系:它体现兼容旧配置的规则:自动发现的角色要求更严格,旧式显式引用的角色更宽松。ConfigBuilder 负责区分这两种来源。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 4 个(new, assert_eq!, create_dir_all, write)。

agent_role_without_description_after_merge_is_dropped_with_warning7068–7118 ↗
async fn agent_role_without_description_after_merge_is_dropped_with_warning() -> std::io::Result<()>

作用:测试 Agent 角色合并完后如果仍然没有 description,会被丢弃并警告。description 是给用户看的角色说明,没有它就不该展示。

数据流:它创建 researcher.toml,里面有 developer_instructions 但没有 description;config.toml 里 researcher 也没有 description,同时还有一个 reviewer 带 description。加载后,确认 researcher 被移除,reviewer 保留,并且启动警告提到 researcher 必须定义 description。

调用关系:它验证角色配置合并后的最终校验。ConfigBuilder 先合并文件和 config.toml,再决定哪些角色可用。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 5 个(new, assert!, assert_eq!, create_dir_all, write)。

discovered_agent_role_file_without_name_is_dropped_with_warning7121–7183 ↗
async fn discovered_agent_role_file_without_name_is_dropped_with_warning() -> std::io::Result<()>

作用:测试自动发现的 Agent 角色文件如果没有非空 name,会被丢弃并警告。name 是角色在系统里的标识。

数据流:它创建受信任仓库和 .codex/agents 目录,放一个缺 name 的 researcher 文件和一个完整 reviewer 文件。加载后,确认 researcher 不存在,reviewer 存在,并且警告里提到必须定义非空 name。

调用关系:它覆盖自动发现角色文件的必填字段检查。ConfigBuilder 扫描工作区角色文件时,会过滤掉缺少 name 的文件。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 8 个(default, new, assert!, assert_eq!, format!, create_dir_all, create_dir_all, write)。

agent_role_file_name_takes_precedence_over_config_key7186–7228 ↗
async fn agent_role_file_name_takes_precedence_over_config_key() -> std::io::Result<()>

作用:测试角色文件里的 name 会优先于 config.toml 里的配置键。也就是说文件说自己叫 archivist,就不会按 researcher 存。

数据流:它让 config.toml 以 [agents.researcher] 引用 researcher.toml,但文件内部写 name = "archivist"。加载后,确认 agent_roles 里没有 researcher,而有 archivist,并且 description 和 config_file 来自文件。

调用关系:它验证角色身份命名的优先级。ConfigBuilder 读取角色文件后,会用文件内部 name 作为最终键。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 4 个(new, assert_eq!, create_dir_all, write)。

loads_legacy_split_agent_roles_from_config_toml7231–7318 ↗
async fn loads_legacy_split_agent_roles_from_config_toml() -> std::io::Result<()>

作用:测试旧式“config.toml 里列角色、每个角色再指向单独文件”的写法还能正常加载多个角色。

数据流:它创建 researcher.toml 和 reviewer.toml,再在 config.toml 里分别声明两个角色的 description、config_file 和昵称候选。加载后,逐一确认两个角色的说明、配置文件路径和昵称候选都正确。

调用关系:它保护老用户的分离式 Agent 角色配置。ConfigBuilder 要同时读主配置和每个角色文件,并把结果合成 agent_roles。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 4 个(new, assert_eq!, create_dir_all, write)。

discovers_multiple_standalone_agent_role_files7321–7448 ↗
async fn discovers_multiple_standalone_agent_role_files() -> std::io::Result<()>

作用:测试系统能在工作区里发现多个独立 Agent 角色文件,包括仓库根目录和更深层目录中的文件。

数据流:它创建一个受信任的模拟 Git 仓库,当前目录在 packages/app。然后分别在根目录 .codex/agents、packages/.codex/agents 的不同位置放 researcher、reviewer、writer 三个角色文件。加载配置后,确认三个角色都被发现,description 和昵称候选都正确。

调用关系:它验证自动发现 Agent 角色的扫描范围。ConfigBuilder 根据 cwd 和仓库结构查找 .codex/agents,把合格的角色文件加入最终配置。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 6 个(default, new, assert_eq!, format!, create_dir_all, write)。

mixed_legacy_and_standalone_agent_role_sources_merge_with_precedence7451–7594 ↗
async fn mixed_legacy_and_standalone_agent_role_sources_merge_with_precedence() -> std::io::Result<()>

作用:测试 agent 角色同时写在旧式全局配置和项目里的独立角色文件时,系统会正确合并,并且项目里的文件优先。这样可以避免用户以为项目配置生效了,实际却被旧配置覆盖。

数据流:输入是临时的 codex_home、一个假的 Git 项目、全局 config.toml、全局 agents 文件夹和项目 .codex/agents 文件夹。测试把 researcher、critic、writer 三种角色分别放在不同来源里,再加载配置。输出是断言:researcher 采用项目文件里的说明和昵称,critic 保留全局配置,writer 从项目文件新增进来。

调用关系:它通过 ConfigBuilder::without_managed_config_for_tests 构造测试用配置加载器,并把目录和当前工作目录交给加载流程。最后不再把活交给别的测试,而是用断言验证 agent_roles 的最终结果。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 7 个(default, new, assert_eq!, format!, create_dir_all, create_dir_all, write)。

higher_precedence_agent_role_can_inherit_description_from_lower_layer7597–7677 ↗
async fn higher_precedence_agent_role_can_inherit_description_from_lower_layer() -> std::io::Result<()>

作用:测试高优先级的 agent 角色文件如果没有写 description,能不能从低优先级配置里继承说明文字。这样用户只改少数字段时,不会把原本有用的描述弄丢。

数据流:输入是一个全局配置里的 researcher 描述和一个项目独立 researcher 文件,项目文件没有 description 但有昵称和模型。加载后,结果应该是 config_file 指向项目文件,昵称来自项目文件,description 则沿用全局配置。

调用关系:它同样使用 ConfigBuilder::without_managed_config_for_tests 走完整配置加载流程,专门验证“优先级覆盖”和“字段继承”这两个规则能一起工作。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 7 个(default, new, assert_eq!, format!, create_dir_all, create_dir_all, write)。

load_config_resolves_agent_interrupt_message7680–7700 ↗
async fn load_config_resolves_agent_interrupt_message() -> std::io::Result<()>

作用:测试 agents.interrupt_message 这个开关能从配置里正确读出来。这个开关决定 agent 被打断时是否显示提示消息。

数据流:输入是一个内存里的 ConfigToml,其中 interrupt_message 设置为 false。配置加载后,输出的 config.agent_interrupt_message_enabled 应该也是 false。

调用关系:它直接调用 Config::load_from_base_config_with_overrides,不经过真实 config.toml 文件,专注检查基础配置字段到运行时 Config 字段的转换。

调用图:外部调用 5 个(default, new, load_from_base_config_with_overrides, assert!, default)。

load_config_normalizes_agent_role_nickname_candidates7703–7743 ↗
async fn load_config_normalizes_agent_role_nickname_candidates() -> std::io::Result<()>

作用:测试 agent 昵称候选列表会被清理前后空格。这样用户写成 " Hypatia " 时,系统实际使用的是干净的 "Hypatia"。

数据流:输入是一个 researcher 角色,nickname_candidates 里有带空格和不带空格的名字。加载配置后,输出的昵称列表应变成 ["Hypatia", "Noether"]。

调用关系:它调用 Config::load_from_base_config_with_overrides 来走正式的配置解析和校验流程,验证昵称规范化发生在加载阶段。

调用图:外部调用 7 个(from, default, new, load_from_base_config_with_overrides, assert_eq!, default, vec!)。

load_config_rejects_empty_agent_role_nickname_candidates7746–7780 ↗
async fn load_config_rejects_empty_agent_role_nickname_candidates() -> std::io::Result<()>

作用:测试空的 agent 昵称候选列表会被拒绝。空列表没有实际意义,允许它会让后续选择昵称的逻辑变得含糊。

数据流:输入是 nickname_candidates = [] 的 researcher 配置。加载配置不会成功,而是返回 InvalidInput 错误,并且错误文字指出 agents.researcher.nickname_candidates 有问题。

调用关系:它调用 Config::load_from_base_config_with_overrides 并期待失败,用来保护配置校验层:坏配置应该尽早报错,而不是等运行时才出问题。

调用图:外部调用 8 个(from, default, new, new, load_from_base_config_with_overrides, assert!, assert_eq!, default)。

load_config_rejects_duplicate_agent_role_nickname_candidates7783–7817 ↗
async fn load_config_rejects_duplicate_agent_role_nickname_candidates() -> std::io::Result<()>

作用:测试重复昵称会被拒绝,即使只是前后多了空格也算重复。这样可以避免同一个候选名在列表里出现两次,造成选择或展示混乱。

数据流:输入是 ["Hypatia", " Hypatia "]。加载时先会按规则去空格,然后发现重复,输出 InvalidInput 错误,并说明 nickname_candidates cannot contain duplicates。

调用关系:它依赖 Config::load_from_base_config_with_overrides 的真实校验流程,和昵称清理测试一起覆盖“先清理、再查重”的行为。

调用图:外部调用 8 个(from, default, new, load_from_base_config_with_overrides, assert!, assert_eq!, default, vec!)。

load_config_rejects_unsafe_agent_role_nickname_candidates7820–7853 ↗
async fn load_config_rejects_unsafe_agent_role_nickname_candidates() -> std::io::Result<()>

作用:测试昵称里有不安全字符时会被拒绝。这里的“不安全”指尖括号这类可能影响显示或命令拼接的字符。

数据流:输入是昵称 "Agent <One>"。加载配置后得到 InvalidInput 错误,错误说明昵称只能包含 ASCII 字母、数字、空格、连字符和下划线。

调用关系:它调用正式配置加载函数,确认安全字符限制不是只写在文档里,而是真的会阻止配置生效。

调用图:外部调用 8 个(from, default, new, load_from_base_config_with_overrides, assert!, assert_eq!, default, vec!)。

model_catalog_json_loads_from_path7856–7881 ↗
async fn model_catalog_json_loads_from_path() -> std::io::Result<()>

作用:测试用户指定的模型目录 JSON 文件可以被读取。模型目录就是一份“有哪些模型可用、模型信息是什么”的清单。

数据流:输入是一个临时 catalog.json,内容来自内置模型目录但只保留一个模型。配置里指定 model_catalog_json 指向这个文件。加载后,config.model_catalog 应该等于这个 JSON 里的目录对象。

调用关系:它先用 bundled_models_response 拿到合法样例,再通过 Config::load_from_base_config_with_overrides 验证自定义路径的加载逻辑。

调用图:外部调用 8 个(default, new, load_from_base_config_with_overrides, assert_eq!, bundled_models_response, default, to_string, write)。

model_catalog_json_rejects_empty_catalog7884–7908 ↗
async fn model_catalog_json_rejects_empty_catalog() -> std::io::Result<()>

作用:测试自定义模型目录不能为空。空目录会让系统不知道能用哪个模型,所以应该在加载配置时直接报错。

数据流:输入是一个内容为 {"models":[]} 的 catalog.json。配置加载失败,输出 InvalidData 错误,并提示目录至少要包含一个模型。

调用关系:它调用 Config::load_from_base_config_with_overrides,验证模型目录读取后还有内容校验,而不是只要 JSON 格式正确就接受。

调用图:外部调用 7 个(default, new, load_from_base_config_with_overrides, assert!, assert_eq!, default, write)。

create_test_fixture7910–7973 ↗
fn create_test_fixture() -> std::io::Result<PrecedenceTestFixture>

作用:创建一套很多测试共用的假配置环境。它像测试里的“样板房”,里面有基础 TOML、临时当前目录和临时 codex_home。

数据流:输入为空。函数把一段固定 TOML 解析成 ConfigToml,创建临时工作目录,并写一个假的 .git 标记防止向父目录查找 AGENTS.md。输出是 PrecedenceTestFixture,里面装着 cwd、codex_home 和 cfg。

调用关系:它被多个服务等级、profile、遥测配置相关测试调用,让这些测试不用重复搭建基础环境,只需要改自己关心的字段。

调用图:被 13 处调用(config_toml_legacy_fast_service_tier_uses_priority_request_value, config_toml_priority_service_tier_uses_priority_request_value, config_toml_service_tier_accepts_arbitrary_string, default_service_tier_override_uses_default_request_value, explicit_null_service_tier_override_maps_to_default_service_tier, fast_default_opt_out_notice_config_is_respected, legacy_fast_service_tier_override_uses_priority_request_value, legacy_profile_selection_is_rejected, load_config_applies_otel_trace_metadata, load_config_drops_invalid_otel_trace_metadata_entries (+3 more));外部调用 3 个(new, write, from_str)。

legacy_profile_selection_is_rejected7976–7998 ↗
async fn legacy_profile_selection_is_rejected() -> std::io::Result<()>

作用:测试旧式的 profile = "gpt3" 顶层写法会被拒绝。这样可以避免已经废弃的配置方式继续悄悄影响运行结果。

数据流:输入来自 create_test_fixture,然后把 fixture.cfg.profile 设为 gpt3。加载配置后应失败,输出 InvalidData 错误,并提示 legacy profile selection 不再支持。

调用关系:它先借 create_test_fixture 得到基础配置,再调用 Config::load_from_base_config_with_overrides,专门检查旧配置入口被拦住。

调用图:调用 1 个内部函数(create_test_fixture);外部调用 4 个(default, load_from_base_config_with_overrides, assert!, assert_eq!)。

metrics_exporter_defaults_to_statsig_when_missing8001–8016 ↗
async fn metrics_exporter_defaults_to_statsig_when_missing() -> std::io::Result<()>

作用:测试没有明确配置指标导出器时,默认使用 Statsig。指标导出器就是把运行指标送到哪里去的设置。

数据流:输入是 create_test_fixture 生成的基础配置,没有额外设置 metrics_exporter。加载后,config.otel.metrics_exporter 应等于 Statsig。

调用关系:它使用通用 fixture 和 Config::load_from_base_config_with_overrides,验证遥测配置的默认值规则。

调用图:调用 1 个内部函数(create_test_fixture);外部调用 3 个(default, load_from_base_config_with_overrides, assert_eq!)。

trace_exporter_defaults_to_none_when_log_exporter_is_set8019–8049 ↗
async fn trace_exporter_defaults_to_none_when_log_exporter_is_set() -> std::io::Result<()>

作用:测试只设置日志导出器时,链路追踪导出器默认是 None。链路追踪是记录一次请求经过哪些步骤的遥测数据。

数据流:输入是一个 otel.exporter,指向 OTLP HTTP 日志端点,同时 metrics_exporter 显式为 None。加载后,otel.exporter 保持 HTTP 导出器,otel.trace_exporter 自动为 None。

调用关系:它在 create_test_fixture 的基础上改 otel 字段,再通过配置加载器验证日志导出和追踪导出不会被错误混用。

调用图:调用 1 个内部函数(create_test_fixture);外部调用 5 个(default, new, load_from_base_config_with_overrides, assert!, assert_eq!)。

load_config_applies_otel_trace_metadata8052–8091 ↗
async fn load_config_applies_otel_trace_metadata() -> std::io::Result<()>

作用:测试配置里的 OpenTelemetry span_attributes 和 tracestate 能被正确读入。span_attributes 是给追踪片段附加的键值标签,tracestate 是跨系统传递的追踪状态。

数据流:输入是一段 TOML,包含 otel.span_attributes 和 otel.tracestate.example。加载后,config.otel.span_attributes 和 config.otel.tracestate 应精确等于配置里的键值。

调用关系:它借 create_test_fixture 提供目录环境,再调用 Config::load_from_base_config_with_overrides 验证遥测元数据的正常路径。

调用图:调用 1 个内部函数(create_test_fixture);外部调用 4 个(default, load_from_base_config_with_overrides, assert_eq!, from_str)。

load_config_drops_invalid_otel_trace_metadata_entries8094–8162 ↗
async fn load_config_drops_invalid_otel_trace_metadata_entries() -> std::io::Result<()>

作用:测试无效的遥测追踪元数据会被丢弃,并留下启动警告。这样坏条目不会污染遥测数据,用户也能知道哪里写错了。

数据流:输入包含一个空的 span attribute 键、几个带换行的 tracestate 值,以及一些合法条目。加载后,合法条目留下,无效条目被去掉,startup_warnings 里出现对应警告。

调用关系:它调用 Config::load_from_base_config_with_overrides,覆盖遥测配置的“宽容但提醒”策略:不是整份配置失败,而是忽略坏项并警告。

调用图:调用 1 个内部函数(create_test_fixture);外部调用 5 个(default, load_from_base_config_with_overrides, assert!, assert_eq!, from_str)。

explicit_null_service_tier_override_maps_to_default_service_tier8165–8185 ↗
async fn explicit_null_service_tier_override_maps_to_default_service_tier() -> std::io::Result<()>

作用:测试命令行或测试覆盖项里显式传入空的 service_tier,会被当成默认服务等级请求值。service_tier 可以理解为请求走哪种服务档位。

数据流:输入是 ConfigOverrides.service_tier = Some(None)。加载后,config.service_tier 变成 SERVICE_TIER_DEFAULT_REQUEST_VALUE,同时 fast_default_opt_out 通知为空。

调用关系:它用 create_test_fixture 建环境,再调用配置加载器,验证覆盖参数里的特殊空值如何映射。

调用图:调用 1 个内部函数(create_test_fixture);外部调用 3 个(default, load_from_base_config_with_overrides, assert_eq!)。

default_service_tier_override_uses_default_request_value8188–8207 ↗
async fn default_service_tier_override_uses_default_request_value() -> std::io::Result<()>

作用:测试 service_tier 覆盖为字符串 "default" 时,会转换成系统真正发送请求用的默认值。这样用户写人类可读的 default,也能得到协议需要的值。

数据流:输入是 ConfigOverrides.service_tier = Some(Some("default"))。加载后,输出的 config.service_tier 是 SERVICE_TIER_DEFAULT_REQUEST_VALUE。

调用关系:它基于 create_test_fixture,检查覆盖参数优先级中的 service_tier 归一化规则。

调用图:调用 1 个内部函数(create_test_fixture);外部调用 3 个(default, load_from_base_config_with_overrides, assert_eq!)。

legacy_fast_service_tier_override_uses_priority_request_value8210–8229 ↗
async fn legacy_fast_service_tier_override_uses_priority_request_value() -> std::io::Result<()>

作用:测试旧名字 "fast" 仍会映射到新的 priority/fast 请求值。这样老用户的命令行参数不会突然失效。

数据流:输入是 service_tier 覆盖值 "fast"。加载后,config.service_tier 等于 ServiceTier::Fast.request_value()。

调用关系:它通过 create_test_fixture 和 Config::load_from_base_config_with_overrides 验证兼容旧写法的转换层。

调用图:调用 1 个内部函数(create_test_fixture);外部调用 3 个(default, load_from_base_config_with_overrides, assert_eq!)。

config_toml_priority_service_tier_uses_priority_request_value8232–8253 ↗
async fn config_toml_priority_service_tier_uses_priority_request_value() -> std::io::Result<()>

作用:测试 config.toml 里写入新的 fast/priority 请求值时,会原样作为服务等级生效。

数据流:输入是 fixture.cfg.service_tier 设为 ServiceTier::Fast.request_value()。加载后,config.service_tier 保持同一个请求值。

调用关系:它使用 create_test_fixture 搭环境,验证来自配置文件的 service_tier 路径,而不是覆盖参数路径。

调用图:调用 1 个内部函数(create_test_fixture);外部调用 3 个(default, load_from_base_config_with_overrides, assert_eq!)。

config_toml_service_tier_accepts_arbitrary_string8256–8277 ↗
async fn config_toml_service_tier_accepts_arbitrary_string() -> std::io::Result<()>

作用:测试 config.toml 允许未知的服务等级字符串。这样实验性服务档位可以先通过配置传下去,不必代码提前枚举所有名字。

数据流:输入是 service_tier = "experimental-tier-id"。加载后,config.service_tier 仍是这个字符串。

调用关系:它用基础 fixture 调用配置加载器,确认 service_tier 对配置文件值是开放字符串,而不是严格枚举。

调用图:调用 1 个内部函数(create_test_fixture);外部调用 3 个(default, load_from_base_config_with_overrides, assert_eq!)。

config_toml_legacy_fast_service_tier_uses_priority_request_value8280–8301 ↗
async fn config_toml_legacy_fast_service_tier_uses_priority_request_value() -> std::io::Result<()>

作用:测试 config.toml 里的旧值 "fast" 会转换成新的 fast 请求值。这样旧配置文件继续可用。

数据流:输入是 fixture.cfg.service_tier = "fast"。加载后,config.service_tier 变成 ServiceTier::Fast.request_value()。

调用关系:它和命令行覆盖的 fast 测试相互补充,确保旧 service_tier 在两个来源里都兼容。

调用图:调用 1 个内部函数(create_test_fixture);外部调用 3 个(default, load_from_base_config_with_overrides, assert_eq!)。

fast_default_opt_out_notice_config_is_respected8304–8325 ↗
async fn fast_default_opt_out_notice_config_is_respected() -> std::io::Result<()>

作用:测试用户设置 fast_default_opt_out 通知开关时,配置加载后会保留这个设置,并且不会自动设置 service_tier。

数据流:输入是在基础配置上加 notice.fast_default_opt_out = true。加载后,config.service_tier 为 None,config.notices.fast_default_opt_out 为 Some(true)。

调用关系:它借 create_test_fixture 并调用配置加载器,验证 notice 配置不会被服务等级默认值逻辑覆盖。

调用图:调用 1 个内部函数(create_test_fixture);外部调用 3 个(default, load_from_base_config_with_overrides, assert_eq!)。

test_requirements_web_search_mode_allowlist_does_not_warn_when_unset8328–8404 ↗
async fn test_requirements_web_search_mode_allowlist_does_not_warn_when_unset() -> anyhow::Result<()>

作用:测试当企业要求允许 cached 网页搜索,但用户没有显式配置冲突值时,不应该产生警告。这样正常默认值不会吓到用户。

数据流:输入是一个要求层:允许 Disabled 和 Cached,默认 constrained 值为 Cached;用户配置本身没有 web_search_mode。加载后,startup_warnings 中不应出现 web_search_mode 被要求限制的警告。

调用关系:它先手动构造 ConfigLayerStack,再调用 Config::load_config_with_layer_stack,验证“要求层”和“用户配置层”合并时的警告逻辑。

调用图:调用 4 个内部函数(new, new, new, create_test_fixture);外部调用 5 个(default, new, load_config_with_layer_stack, assert!, vec!)。

test_set_project_trusted_writes_explicit_tables8407–8429 ↗
fn test_set_project_trusted_writes_explicit_tables() -> anyhow::Result<()>

作用:测试把项目标记为 trusted 时,会用清晰的 TOML 显式表写法。显式表就是 [projects."路径"] 这种更容易追加字段的格式。

数据流:输入是空的 DocumentMut 和项目路径 /some/path。调用 set_project_trust_level_inner 后,输出文本应正好是一个 projects 子表,trust_level = "trusted"。

调用关系:它直接测试写配置的底层函数 set_project_trust_level_inner,关注写出来的 TOML 形状是否稳定。

调用图:外部调用 4 个(new, new, assert_eq!, format!)。

test_set_project_trusted_converts_inline_to_explicit8432–8466 ↗
fn test_set_project_trusted_converts_inline_to_explicit() -> anyhow::Result<()>

作用:测试已有内联项目配置会被转换成显式表。内联配置像一行里的小对象,显式表更适合后续维护。

数据流:输入 TOML 是 [projects] 下的一行内联项目,原本 trust_level 是 untrusted。调用后,输出变成 [projects."路径"] 显式表,并把 trust_level 改成 trusted。

调用关系:它直接调用 set_project_trust_level_inner,验证迁移旧写法时不会只改值,还会改成新的推荐结构。

调用图:外部调用 3 个(new, assert_eq!, format!)。

test_set_project_trusted_migrates_top_level_inline_projects_preserving_entries8469–8503 ↗
fn test_set_project_trusted_migrates_top_level_inline_projects_preserving_entries() -> anyhow::Result<()>

作用:测试顶层 inline 的 projects 大对象会被迁移成多个显式项目表,并保留已有字段。这样旧配置升级时不会丢用户额外写的内容。

数据流:输入 TOML 里有 toplevel、model 和顶层 projects = { ... },其中一个项目还有 foo = "bar"。调用后,输出保留 toplevel 和 model,把原项目拆成表,并追加新项目的 trusted 条目。

调用关系:它调用 set_project_trust_level_inner,并用 project_trust_key 生成期望的新项目 key,专门覆盖最复杂的写回迁移场景。

调用图:调用 1 个内部函数(project_trust_key);外部调用 3 个(new, assert_eq!, format!)。

active_project_does_not_match_configured_alias_for_canonical_cwd8507–8530 ↗
async fn active_project_does_not_match_configured_alias_for_canonical_cwd() -> anyhow::Result<()>

作用:测试当前项目路径不会因为符号链接别名而误匹配。符号链接可以理解为文件夹的“快捷方式”,但这里不能把快捷方式当成真实路径混用。

数据流:输入是真实 project_root 和指向它的 alias_root,配置只信任 alias_root。调用 get_active_project 时传入真实 project_root,结果应为 None。

调用关系:它直接测试 ConfigToml::get_active_project 的路径匹配规则,防止通过别名路径绕过项目信任配置。

调用图:外部调用 6 个(default, from, assert_eq!, create_dir_all, symlink, tempdir)。

test_set_default_oss_provider8533–8565 ↗
fn test_set_default_oss_provider() -> std::io::Result<()>

作用:测试默认 OSS 提供方的写入、更新、覆盖和错误处理。OSS 提供方这里指本地或开源模型服务来源,比如 ollama、lmstudio。

数据流:输入是临时 codex_home。函数先在空配置里写 ollama,再在已有 model 的配置里写 lmstudio,再覆盖回 ollama,最后传入 invalid_provider。输出是文件内容符合预期,非法 provider 返回 InvalidInput。

调用关系:它直接调用 set_default_oss_provider,验证这个写配置工具既能保留已有字段,也能拒绝未知提供方。

调用图:外部调用 5 个(new, assert!, assert_eq!, read_to_string, write)。

test_set_default_oss_provider_rejects_legacy_ollama_chat_provider8568–8583 ↗
fn test_set_default_oss_provider_rejects_legacy_ollama_chat_provider() -> std::io::Result<()>

作用:测试已经移除的旧 ollama-chat 提供方不能再被设置。这样用户会得到明确迁移提示,而不是写入一个以后无法使用的配置。

数据流:输入是 LEGACY_OLLAMA_CHAT_PROVIDER_ID。set_default_oss_provider 返回错误,错误类型是 InvalidInput,文字包含 OLLAMA_CHAT_PROVIDER_REMOVED_ERROR。

调用关系:它直接测试 set_default_oss_provider 的特殊拒绝分支,补充普通非法 provider 的测试。

调用图:外部调用 3 个(new, assert!, assert_eq!)。

test_load_config_rejects_legacy_ollama_chat_provider_with_helpful_error8586–8610 ↗
async fn test_load_config_rejects_legacy_ollama_chat_provider_with_helpful_error() -> std::io::Result<()>

作用:测试配置加载时遇到旧 ollama-chat provider 会失败,并给出有帮助的错误。这样不是只有写入工具会拦,已有旧配置也会被发现。

数据流:输入是 ConfigToml.model_provider = legacy ollama-chat。加载配置返回 NotFound 错误,错误文字包含移除说明。

调用关系:它调用 Config::load_from_base_config_with_overrides,验证读取已有配置时也复用 provider 校验规则。

调用图:外部调用 6 个(default, new, load_from_base_config_with_overrides, assert!, assert_eq!, default)。

test_untrusted_project_gets_workspace_write_sandbox8613–8648 ↗
async fn test_untrusted_project_gets_workspace_write_sandbox() -> anyhow::Result<()>

作用:测试不受信任项目默认得到有限写入沙箱。沙箱就是给程序圈出活动范围,防止它随便改系统文件。

数据流:输入是一个 trust_level = untrusted 的项目配置和 active_project。derive_legacy_sandbox_policy_for_test 返回的策略在非 Windows 上应是 WorkspaceWrite,在 Windows 上因为能力限制降为 ReadOnly。

调用关系:它调用 derive_legacy_sandbox_policy_for_test,专门验证旧沙箱策略推导里“不受信任项目”的默认安全边界。

调用图:调用 1 个内部函数(derive_legacy_sandbox_policy_for_test);外部调用 2 个(assert!, cfg!)。

derive_sandbox_policy_falls_back_to_read_only_for_implicit_defaults8651–8692 ↗
async fn derive_sandbox_policy_falls_back_to_read_only_for_implicit_defaults() -> anyhow::Result<()>

作用:测试默认沙箱模式如果不符合要求,会回退到要求允许的只读模式。只读表示可以看文件,但不能写文件。

数据流:输入是一个受信任项目和一个只允许 PermissionProfile::read_only 的约束。推导沙箱策略后,输出应是 SandboxPolicy::new_read_only_policy()。

调用关系:它手动构造 Constrained 约束,再调用 derive_legacy_sandbox_policy_for_test,验证企业或管理要求能压过隐式默认值。

调用图:调用 3 个内部函数(new, derive_legacy_sandbox_policy_for_test, read_only);外部调用 4 个(default, from, new, assert_eq!)。

derive_sandbox_policy_preserves_windows_downgrade_for_unsupported_fallback8695–8748 ↗
async fn derive_sandbox_policy_preserves_windows_downgrade_for_unsupported_fallback() -> anyhow::Result<()>

作用:测试要求允许 workspace-write 时,Windows 上仍会保留降级到只读的行为。因为某些沙箱能力在 Windows 上不一定支持。

数据流:输入是受信任项目和一个允许带写权限的 workspace-write 约束。非 Windows 输出 WorkspaceWrite;Windows 输出 ReadOnly。

调用关系:它通过 derive_legacy_sandbox_policy_for_test 验证两套规则能叠加:先按要求选 fallback,再按平台能力降级。

调用图:调用 3 个内部函数(new, derive_legacy_sandbox_policy_for_test, workspace_write);外部调用 5 个(default, from, new, assert_eq!, cfg!)。

test_resolve_oss_provider_explicit_override8751–8755 ↗
fn test_resolve_oss_provider_explicit_override()

作用:测试显式传入的 OSS provider 会被直接采用。显式参数通常代表用户这次运行的明确选择。

数据流:输入是 explicit_provider = "custom-provider" 和默认 ConfigToml。resolve_oss_provider 输出 Some("custom-provider")。

调用关系:它直接测试 resolve_oss_provider 的最高优先级分支,不涉及完整配置加载。

调用图:外部调用 2 个(assert_eq!, default)。

test_resolve_oss_provider_from_global_config8758–8766 ↗
fn test_resolve_oss_provider_from_global_config()

作用:测试没有显式 provider 时,会从全局配置 oss_provider 读取。这样用户写在 config.toml 里的默认选择能生效。

数据流:输入是 ConfigToml.oss_provider = "global-provider",显式参数为空。输出 Some("global-provider")。

调用关系:它直接调用 resolve_oss_provider,验证显式参数缺席时的第二优先级来源。

调用图:外部调用 2 个(default, assert_eq!)。

test_resolve_oss_provider_none_when_not_configured8769–8773 ↗
fn test_resolve_oss_provider_none_when_not_configured()

作用:测试既没有显式 provider,也没有全局配置时,结果就是 None。这样系统不会凭空猜一个提供方。

数据流:输入是默认 ConfigToml 和 None 显式参数。resolve_oss_provider 输出 None。

调用关系:它直接覆盖 resolve_oss_provider 的空配置分支,和另外几个 OSS provider 优先级测试组成完整规则。

调用图:外部调用 2 个(assert_eq!, default)。

test_resolve_oss_provider_explicit_overrides_global8776–8784 ↗
fn test_resolve_oss_provider_explicit_overrides_global()

作用:测试显式 provider 会覆盖全局 provider。这样一次性命令参数可以临时改掉默认设置。

数据流:输入是全局 oss_provider = "global-provider",同时 explicit_provider = "explicit-provider"。输出是 explicit-provider。

调用关系:它直接调用 resolve_oss_provider,确认 OSS provider 的优先级顺序是显式参数高于配置文件。

调用图:外部调用 2 个(default, assert_eq!)。

config_toml_deserializes_mcp_oauth_callback_port8787–8792 ↗
fn config_toml_deserializes_mcp_oauth_callback_port()

作用:测试 TOML 里的 mcp_oauth_callback_port 能被解析到 ConfigToml。MCP OAuth 回调端口是登录授权完成后本地接收回调用的端口。

数据流:输入是一行 TOML:mcp_oauth_callback_port = 4321。toml::from_str 解析后,cfg.mcp_oauth_callback_port 应为 Some(4321)。

调用关系:它只测试反序列化,也就是从文本配置到 ConfigToml 结构的第一步。

调用图:外部调用 2 个(assert_eq!, from_str)。

config_toml_deserializes_mcp_oauth_callback_url8795–8803 ↗
fn config_toml_deserializes_mcp_oauth_callback_url()

作用:测试 TOML 里的 mcp_oauth_callback_url 能被解析。这个 URL 是 OAuth 登录完成后跳回来的地址。

数据流:输入是一行 TOML,值为 https://example.com/callback。解析后,cfg.mcp_oauth_callback_url 应保存这个字符串。

调用关系:它直接用 toml::from_str 测试 ConfigToml 字段映射,不走完整加载流程。

调用图:外部调用 2 个(assert_eq!, from_str)。

config_loads_mcp_oauth_callback_port_from_toml8806–8824 ↗
async fn config_loads_mcp_oauth_callback_port_from_toml() -> std::io::Result<()>

作用:测试 MCP OAuth 回调端口不仅能解析,还能进入最终运行时 Config。最终 Config 才是程序运行时真正使用的配置。

数据流:输入是带 model 和 mcp_oauth_callback_port = 5678 的 TOML。解析成 ConfigToml 后加载,输出 config.mcp_oauth_callback_port = Some(5678)。

调用关系:它先 toml::from_str,再调用 Config::load_from_base_config_with_overrides,覆盖从文本到最终配置的完整路径。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, from_str)。

config_loads_allow_login_shell_from_toml8827–8846 ↗
async fn config_loads_allow_login_shell_from_toml() -> std::io::Result<()>

作用:测试 allow_login_shell = false 会进入权限配置。login shell 是带登录环境的命令行外壳,是否允许会影响命令执行环境。

数据流:输入是 TOML 中 allow_login_shell = false。加载后,config.permissions.allow_login_shell 应为 false。

调用关系:它通过完整配置加载器检查权限字段是否从 TOML 正确搬到运行时 permissions。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert!, default, from_str)。

config_loads_apps_mcp_product_sku_from_toml8849–8867 ↗
async fn config_loads_apps_mcp_product_sku_from_toml() -> std::io::Result<()>

作用:测试 apps_mcp_product_sku 字段能从 TOML 进入最终配置。SKU 可以理解为产品或套餐标识。

数据流:输入 TOML 里 apps_mcp_product_sku = "tpp"。加载后,config.apps_mcp_product_sku 应为 Some("tpp")。

调用关系:它使用 Config::load_from_base_config_with_overrides 验证这个应用/MCP 相关字段没有在加载过程中丢失。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, from_str)。

config_loads_mcp_oauth_callback_url_from_toml8870–8891 ↗
async fn config_loads_mcp_oauth_callback_url_from_toml() -> std::io::Result<()>

作用:测试 MCP OAuth 回调 URL 能进入最终运行配置。这样授权流程可以使用用户指定的回调地址。

数据流:输入是带 mcp_oauth_callback_url 的 TOML。解析并加载后,config.mcp_oauth_callback_url 应等于该 URL。

调用关系:它和端口加载测试对应,覆盖 OAuth 回调配置的 URL 分支。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, from_str)。

test_untrusted_project_gets_unless_trusted_approval_policy8894–8944 ↗
async fn test_untrusted_project_gets_unless_trusted_approval_policy() -> anyhow::Result<()>

作用:测试不受信任项目会得到 UnlessTrusted 审批策略,并保持安全沙箱。审批策略决定执行敏感操作前是否要问用户。

数据流:输入是把当前临时项目标记为 untrusted 的 ConfigToml。加载后,approval_policy 应为 UnlessTrusted;沙箱在非 Windows 上是 WorkspaceWrite,在 Windows 上是 ReadOnly。

调用关系:它调用完整配置加载器,然后检查 permissions 和 legacy_sandbox_policy,验证项目信任级别同时影响审批和沙箱。

调用图:外部调用 7 个(default, from, new, load_from_base_config_with_overrides, assert!, assert_eq!, cfg!)。

requirements_disallowing_default_sandbox_falls_back_to_required_default8947–8965 ↗
async fn requirements_disallowing_default_sandbox_falls_back_to_required_default() -> std::io::Result<()>

作用:测试企业要求只允许 read-only 沙箱时,默认沙箱会回退到只读。企业要求就是外部管理策略,对用户配置有约束力。

数据流:输入是一个 cloud_config_bundle,内容为 allowed_sandbox_modes = ["read-only"]。ConfigBuilder 构建后,legacy_sandbox_policy 应是只读策略。

调用关系:它使用 ConfigBuilder::without_managed_config_for_tests 加载带企业要求的配置,验证没有用户显式配置时也要遵守要求。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 2 个(new, assert_eq!)。

explicit_sandbox_mode_falls_back_when_disallowed_by_requirements8968–8991 ↗
async fn explicit_sandbox_mode_falls_back_when_disallowed_by_requirements() -> std::io::Result<()>

作用:测试用户显式写了 danger-full-access,但企业只允许 read-only 时,会回退到只读。danger-full-access 是几乎不设限制的危险模式。

数据流:输入是 config.toml 中 sandbox_mode = "danger-full-access",以及企业要求只允许 read-only。构建配置后,legacy_sandbox_policy 是只读。

调用关系:它写真实临时 config.toml,再通过 ConfigBuilder 加载,验证企业要求会压过用户文件配置。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 3 个(new, assert_eq!, write)。

windows_sandbox_mode_falls_back_when_disallowed_by_requirements8994–9027 ↗
async fn windows_sandbox_mode_falls_back_when_disallowed_by_requirements() -> std::io::Result<()>

作用:测试 Windows 沙箱实现如果被企业要求禁止,会回退到允许的实现并产生警告。

数据流:输入是用户配置 windows.sandbox = "unelevated",企业要求只允许 elevated。加载后,permissions.windows_sandbox_mode 变成 Elevated,并在 startup_warnings 里说明原配置被要求拒绝。

调用关系:它通过 ConfigBuilder 和 CloudConfigBundleFixture 组合用户配置与企业要求,验证 Windows 专属沙箱字段的 fallback 和警告。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 4 个(new, assert!, assert_eq!, write)。

danger_full_access_with_never_is_rejected_when_requirements_force_read_only9030–9058 ↗
async fn danger_full_access_with_never_is_rejected_when_requirements_force_read_only() -> std::io::Result<()>

作用:测试当用户同时设置 approval_policy = never 和 danger-full-access,但企业强制只读时,配置会被拒绝。never 表示不再询问用户,这和权限被降级后的行为组合起来很危险。

数据流:输入是 config.toml 里的 approval_policy = "never" 和 sandbox_mode = "danger-full-access",企业只允许 read-only。构建配置失败,输出 InvalidInput,并给出完整解释。

调用关系:它用 ConfigBuilder 走真实加载流程,验证不是所有被要求拦下的危险配置都能静默 fallback;有些组合必须让用户改配置。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 3 个(new, assert_eq!, write)。

named_full_access_profile_with_never_is_rejected_when_requirements_force_read_only9061–9092 ↗
async fn named_full_access_profile_with_never_is_rejected_when_requirements_force_read_only() -> std::io::Result<()>

作用:测试通过命名权限 profile 获得全写权限、同时审批策略为 never 时,在企业强制只读下也会被拒绝。

数据流:输入是 approval_policy = "never"、default_permissions = "dev",并定义 dev.filesystem 的 root 写权限;企业只允许 read-only。构建失败,错误和 danger-full-access 场景一致。

调用关系:它验证同一安全规则不只检查 sandbox_mode 字段,也会识别命名权限 profile 带来的全访问效果。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 3 个(new, assert_eq!, write)。

permission_profile_override_falls_back_when_disallowed_by_requirements9095–9120 ↗
async fn permission_profile_override_falls_back_when_disallowed_by_requirements() -> std::io::Result<()>

作用:测试通过覆盖参数传入禁用沙箱的权限 profile 时,如果企业只允许只读,会回退到只读。覆盖参数通常来自命令行或测试 harness。

数据流:输入是 ConfigOverrides.permission_profile = PermissionProfile::Disabled,企业要求 allowed_sandbox_modes = ["read-only"]。加载后,legacy_sandbox_policy 和 effective_permission_profile 都是只读。

调用关系:它使用 ConfigBuilder 的 harness_overrides 和 cloud_config_bundle,验证企业要求也能约束运行时覆盖项。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 4 个(default, new, new_read_only_policy, assert_eq!)。

active_profile_is_cleared_when_requirements_force_fallback9123–9152 ↗
async fn active_profile_is_cleared_when_requirements_force_fallback() -> std::io::Result<()>

作用:测试当默认权限 profile 被企业要求强制回退后,active profile 名称会被清空。这样界面不会误报还在使用原来的危险 profile。

数据流:输入是 default_permissions 覆盖为内置 danger-full-access,企业只允许 read-only。加载后,有效权限是 read_only,active_permission_profile 为 None,并产生对应警告。

调用关系:它用 ConfigBuilder 组合覆盖参数和企业要求,验证 fallback 后状态展示也要同步更新。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 4 个(default, new, assert!, assert_eq!)。

bypass_hook_trust_adds_startup_warning9155–9175 ↗
async fn bypass_hook_trust_adds_startup_warning() -> std::io::Result<()>

作用:测试启用危险的绕过 hook 信任检查参数时,会添加启动警告。hook 是自动运行的小脚本,绕过审查可能带来风险。

数据流:输入是 ConfigOverrides.bypass_hook_trust = true。加载后,startup_warnings 中应包含精确警告:启用的 hook 这次运行可能未经审查就执行。

调用关系:它通过 ConfigBuilder 的 harness_overrides 测试一次性危险开关,确保用户启动时会被提醒。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 3 个(default, new, assert!)。

permission_profile_override_preserves_split_write_roots9178–9231 ↗
async fn permission_profile_override_preserves_split_write_roots() -> std::io::Result<()>

作用:测试自定义权限 profile 里“根目录只读、某个外部目录可写”的拆分写权限不会丢失。

数据流:输入是一个 FileSystemSandboxPolicy:Root 为 Read,outside_root 为 Write,网络为 Restricted。加载后,系统确认 outside_root 可写,旧沙箱视图是 WorkspaceWrite,网络沙箱仍是 Restricted。

调用关系:它先用 PermissionProfile::from_runtime_permissions_with_enforcement 构造复杂权限,再交给 ConfigBuilder,验证新权限模型转成旧沙箱视图时不会把额外写目录抹掉。

调用图:调用 4 个内部函数(without_managed_config_for_tests, from_runtime_permissions_with_enforcement, restricted, from_absolute_path);外部调用 6 个(default, new, assert!, assert_eq!, create_dir_all, vec!)。

requirements_web_search_mode_overrides_danger_full_access_default9234–9263 ↗
async fn requirements_web_search_mode_overrides_danger_full_access_default() -> std::io::Result<()>

作用:测试企业要求 cached 网页搜索时,即使用户配置了 danger-full-access,最终网页搜索模式也会是 cached。

数据流:输入是 config.toml 中 sandbox_mode = "danger-full-access",企业要求 allowed_web_search_modes = ["cached"]。加载后,config.web_search_mode 和每轮实际解析值都是 Cached。

调用关系:它通过 ConfigBuilder 加载企业要求,并调用 resolve_web_search_mode_for_turn 检查运行一轮对话时实际会用的搜索模式。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 3 个(new, assert_eq!, write)。

requirements_disallowing_default_approval_falls_back_to_required_default9266–9297 ↗
async fn requirements_disallowing_default_approval_falls_back_to_required_default() -> std::io::Result<()>

作用:测试企业要求只允许 on-request 审批时,默认审批策略会回退到 on-request。on-request 表示需要时向用户请求批准。

数据流:输入是一个 untrusted 项目配置,以及企业 allowed_approval_policies = ["on-request"]。加载后,config.permissions.approval_policy.value() 是 OnRequest。

调用关系:它用 ConfigBuilder 加载真实临时配置文件和企业要求,验证项目默认审批策略也受要求约束。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 4 个(new, assert_eq!, format!, write)。

explicit_approval_policy_falls_back_when_disallowed_by_requirements9300–9324 ↗
async fn explicit_approval_policy_falls_back_when_disallowed_by_requirements() -> std::io::Result<()>

作用:测试用户显式写了不被允许的 approval_policy 时,会回退到企业允许的 on-request。

数据流:输入是 config.toml 中 approval_policy = "untrusted",企业只允许 on-request。加载后,审批策略值为 OnRequest。

调用关系:它通过 ConfigBuilder 检查审批策略字段的要求约束逻辑,和沙箱模式 fallback 测试相对应。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 3 个(new, assert_eq!, write)。

feature_requirements_normalize_effective_feature_values9327–9356 ↗
async fn feature_requirements_normalize_effective_feature_values() -> std::io::Result<()>

作用:测试企业 feature 要求会正确变成最终功能开关。feature 是程序里的功能开关,比如 personality、shell_tool。

数据流:输入是企业要求 personality = true、shell_tool = false。加载后,Personality 开启,ShellTool 关闭,并且没有“features 被要求覆盖”的无意义警告。

调用关系:它通过 ConfigBuilder 加载 feature_requirements,验证要求层能直接规范最终 feature 状态。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 2 个(new, assert!)。

feature_requirements_auto_review_disables_guardian_approval9359–9378 ↗
async fn feature_requirements_auto_review_disables_guardian_approval() -> std::io::Result<()>

作用:测试企业关闭 auto_review 时,也会关闭 GuardianApproval。这里 GuardianApproval 是依赖自动审查能力的相关功能。

数据流:输入是企业要求 auto_review = false。加载后,config.features.enabled(Feature::GuardianApproval) 为 false。

调用关系:它通过 ConfigBuilder 验证 feature 之间的联动规则:一个上游功能被关,下游依赖功能也要关。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 2 个(new, assert!)。

browser_feature_requirements_are_valid9381–9402 ↗
async fn browser_feature_requirements_are_valid() -> std::io::Result<()>

作用:测试浏览器相关 feature 要求可以被正常加载并关闭。这里包括内置浏览器和浏览器使用能力。

数据流:输入是企业要求 in_app_browser = false、browser_use = false。加载后,这两个 Feature 都是关闭状态。

调用关系:它通过 ConfigBuilder 覆盖浏览器功能开关,确认这些 feature 名称和要求解析都是合法的。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 2 个(new, assert!)。

debug_config_lockfile_export_settings_load_from_nested_table9405–9433 ↗
async fn debug_config_lockfile_export_settings_load_from_nested_table() -> std::io::Result<()>

作用:测试 debug.config_lockfile 这个嵌套表里的导出设置能被读取。config lockfile 是把配置锁定下来方便复现问题的调试文件。

数据流:输入是 config.toml 里的 [debug.config_lockfile],包含 export_dir、allow_codex_version_mismatch、save_fields_resolved_from_model_catalog。加载后,导出目录被解析成绝对路径,两个布尔开关按配置生效。

调用关系:它用 ConfigBuilder 加载真实临时 config.toml,验证调试锁文件配置从嵌套表进入最终 Config。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 4 个(new, assert!, assert_eq!, write)。

debug_config_lockfile_load_path_loads_lock_from_nested_table9436–9473 ↗
async fn debug_config_lockfile_load_path_loads_lock_from_nested_table() -> std::io::Result<()>

作用:测试 debug.config_lockfile.load_path 能从指定路径加载配置锁文件。这样调试时可以强制使用某份锁定配置。

数据流:输入是一份临时 session.config.lock.toml,以及 config.toml 里指向它的 load_path 和相关开关。加载后,config.config_lock_toml 有值,并且版本不匹配允许、保存模型目录解析字段关闭。

调用关系:它先写锁文件,再让 ConfigBuilder 读取嵌套 debug 配置,验证加载路径和锁文件读取流程能串起来。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 4 个(new, assert!, format!, write)。

explicit_feature_config_is_normalized_by_requirements9476–9514 ↗
async fn explicit_feature_config_is_normalized_by_requirements() -> std::io::Result<()>

作用:确认企业下发的功能要求会压过用户自己写的功能开关,而且不会把这种正常覆盖误报成警告。

数据流:进去的是一个临时配置文件,用户写着 personality 关、shell_tool 开;同时还有一份企业要求,写着 personality 开、shell_tool 关。测试加载配置后,检查出来的结果变成企业要求的样子,并确认启动警告里没有把 features 配置说成有问题。

调用关系:测试运行器执行它;它先用 CloudConfigBundleFixture::loader_with_enterprise_requirement 准备企业要求,再交给 ConfigBuilder::without_managed_config_for_tests 构造配置,最后用断言检查加载结果。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 3 个(new, assert!, write)。

approvals_reviewer_defaults_to_manual_only_without_guardian_feature9517–9529 ↗
async fn approvals_reviewer_defaults_to_manual_only_without_guardian_feature() -> std::io::Result<()>

作用:确认没有开启 guardian approval 相关功能时,审批审核者默认还是用户本人手动处理。

数据流:进去的是一个空的临时配置目录。测试构建配置后读取 approvals_reviewer,结果应该是 ApprovalsReviewer::User,也就是由用户自己确认。

调用关系:测试运行器调用它;它只走最普通的 ConfigBuilder 加载流程,用来守住默认行为不被别的功能改掉。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 2 个(new, assert_eq!)。

prompt_instruction_blocks_can_be_disabled_from_config9532–9558 ↗
async fn prompt_instruction_blocks_can_be_disabled_from_config() -> std::io::Result<()>

作用:确认用户可以通过配置关掉提示词里的几类说明文字,例如权限说明、应用说明、协作模式说明、技能说明和环境上下文。

数据流:进去的是一个写了多个 include_xxx = false 的配置文件。配置加载后,测试逐个检查对应布尔开关都变成 false,说明这些说明块真的会被排除。

调用关系:测试运行器执行它;它用 ConfigBuilder::default 读取用户配置,服务于提示词组装逻辑的前置配置验证。

调用图:外部调用 4 个(new, assert!, default, write)。

approvals_reviewer_stays_manual_only_when_guardian_feature_is_enabled9561–9579 ↗
async fn approvals_reviewer_stays_manual_only_when_guardian_feature_is_enabled() -> std::io::Result<()>

作用:确认即使打开 guardian_approval 功能,审批审核者也不会自动从用户手动切到自动审核。

数据流:进去的是一个只开启 features.guardian_approval 的配置文件。加载后读取 approvals_reviewer,结果仍然是 User。

调用关系:测试运行器执行它;它用 ConfigBuilder 加载配置,检查功能开关和审批人默认值之间不会产生意外联动。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 3 个(new, assert_eq!, write)。

approvals_reviewer_can_be_set_in_config_without_guardian_approval9582–9599 ↗
async fn approvals_reviewer_can_be_set_in_config_without_guardian_approval() -> std::io::Result<()>

作用:确认用户可以在配置里显式写 approvals_reviewer = "user",即使没有打开 guardian approval 功能也能被接受。

数据流:进去的是一个写了 approvals_reviewer 为 user 的配置文件。加载后配置对象里的 approvals_reviewer 应该还是 User。

调用关系:测试运行器执行它;它通过 ConfigBuilder 走完整读取流程,验证根配置里的审批人字段能被正常解析。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 3 个(new, assert_eq!, write)。

requirements_disallowing_default_approvals_reviewer_falls_back_to_required_default9602–9618 ↗
async fn requirements_disallowing_default_approvals_reviewer_falls_back_to_required_default() -> std::io::Result<()>

作用:确认企业要求如果不允许默认的用户手动审批,系统会自动退到企业允许的默认审批方式。

数据流:进去的是空用户配置和一份企业要求,只允许 guardian_subagent。加载后 approvals_reviewer 变成 AutoReview,也就是使用守护子代理自动审核。

调用关系:测试运行器执行它;它把企业限制交给 ConfigBuilder,验证配置归一化阶段会把不允许的默认值改成可用值。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 2 个(new, assert_eq!)。

root_approvals_reviewer_falls_back_when_disallowed_by_requirements9621–9651 ↗
async fn root_approvals_reviewer_falls_back_when_disallowed_by_requirements() -> std::io::Result<()>

作用:确认用户在主配置里选了企业不允许的审批人时,系统会退回允许值,并给出启动警告。

数据流:进去的是用户配置 approvals_reviewer = user,以及企业要求只允许 guardian_subagent。加载后结果变成 AutoReview,并且 startup_warnings 里出现“这个配置被要求禁止”的提示。

调用关系:测试运行器执行它;它用 ConfigBuilder 同时读取本地配置和企业要求,重点检查冲突处理和警告输出。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 4 个(new, assert!, assert_eq!, write)。

profile_approvals_reviewer_falls_back_when_disallowed_by_requirements9654–9682 ↗
async fn profile_approvals_reviewer_falls_back_when_disallowed_by_requirements() -> std::io::Result<()>

作用:确认审批人设置即使来自某个配置 profile,也同样会被企业要求约束。

数据流:进去的是一个单独的 default.config.toml profile 文件,里面写 approvals_reviewer = user;企业要求只允许 guardian_subagent。加载指定 profile 后,结果被改成 AutoReview。

调用关系:测试运行器执行它;它通过 LoaderOverrides 指定 profile 配置文件,再交给 ConfigBuilder,验证 profile 层和普通配置层遵守同一套限制。

调用图:调用 3 个内部函数(without_managed_config_for_tests, loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 3 个(new, assert_eq!, write)。

approvals_reviewer_preserves_valid_user_choice_when_allowed_by_requirements9685–9715 ↗
async fn approvals_reviewer_preserves_valid_user_choice_when_allowed_by_requirements() -> std::io::Result<()>

作用:确认如果用户选择的审批人本来就在企业允许列表里,系统不会乱改,也不会乱报警。

数据流:进去的是用户配置 guardian_subagent,以及企业要求允许 user 和 guardian_subagent。加载后 approvals_reviewer 是 AutoReview,并且警告里不应该出现 approvals_reviewer 相关内容。

调用关系:测试运行器执行它;它用企业要求包裹 ConfigBuilder 的加载流程,验证“合法用户选择”会被保留。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 4 个(new, assert!, assert_eq!, write)。

smart_approvals_alias_is_ignored9718–9742 ↗
async fn smart_approvals_alias_is_ignored() -> std::io::Result<()>

作用:确认旧名字 smart_approvals 仍能让 guardian approval 功能生效,但不会把配置文件自动改写成新名字或补写审批人字段。

数据流:进去的是配置里的 features.smart_approvals = true。加载后功能集合里 GuardianApproval 是开启的,approvals_reviewer 仍是 User;再读回原配置文件,确认里面仍是 smart_approvals,没有被写成 guardian_approval 或 approvals_reviewer。

调用关系:测试运行器执行它;它先用 ConfigBuilder 加载,再用 tokio::fs::read_to_string 检查磁盘文件,验证兼容旧配置但不擅自修改用户文件。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 5 个(new, assert!, assert_eq!, write, read_to_string)。

multi_agent_v2_config_from_feature_table9745–9804 ↗
async fn multi_agent_v2_config_from_feature_table() -> std::io::Result<()>

作用:确认 multi_agent_v2 的一整组配置项可以从 [features.multi_agent_v2] 表里读出来。

数据流:进去的是开启 multi_agent_v2 并设置并发数、等待时间、提示文字、工具命名空间等字段的配置文件。加载后逐项检查 Config 里的 multi_agent_v2 字段,确认每个值都照配置生效;并确认总线程上限会扣掉根代理自己占用的一个位置。

调用关系:测试运行器执行它;它通过 ConfigBuilder 读取配置,覆盖多智能体 v2 子系统后续运行会依赖的核心参数。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 4 个(new, assert!, assert_eq!, write)。

multi_agent_v2_default_session_thread_cap_counts_root9807–9832 ↗
async fn multi_agent_v2_default_session_thread_cap_counts_root() -> std::io::Result<()>

作用:确认 multi_agent_v2 使用默认并发上限时,会把当前根代理也算进总数里。

数据流:进去的是只开启 multi_agent_v2 的配置。加载后 multi_agent_v2 配置等于默认值,有效可派生子代理线程数是 3,表示默认总槽位里有一个被根代理占用。

调用关系:测试运行器执行它;它通过 ConfigBuilder 验证默认值和 effective_agent_max_threads 的计算约定。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 3 个(new, assert_eq!, write)。

multi_agent_v2_default_usage_hints_use_configured_thread_cap9835–9857 ↗
fn multi_agent_v2_default_usage_hints_use_configured_thread_cap()

作用:确认默认的多智能体使用提示文字会根据配置里的并发槽位数动态改写。

数据流:进去的是一段 TOML 字符串,设置 max_concurrent_threads_per_session = 17。测试解析后调用 resolve_multi_agent_v2_config,检查根代理和子代理的默认提示文字末尾都包含“共有 17 个并发槽位”的说明。

调用关系:测试运行器执行它;它不走完整 ConfigBuilder,而是直接测试 resolve_multi_agent_v2_config 这段解析和默认文案生成逻辑。

调用图:外部调用 3 个(assert!, format!, from_str)。

multi_agent_v2_empty_usage_hint_overrides_clear_default_hints9860–9881 ↗
async fn multi_agent_v2_empty_usage_hint_overrides_clear_default_hints() -> std::io::Result<()>

作用:确认用户把多智能体提示文字配置成空字符串时,意思是明确清空默认提示。

数据流:进去的是 root_agent_usage_hint_text 和 subagent_usage_hint_text 都等于空字符串的配置。加载后这两个字段变成 None,表示没有提示文字。

调用关系:测试运行器执行它;它通过 ConfigBuilder 检查配置归一化规则,避免空字符串被误当成一段有效提示。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 3 个(new, assert_eq!, write)。

multi_agent_v2_feature_rejects_agents_max_threads9884–9916 ↗
async fn multi_agent_v2_feature_rejects_agents_max_threads() -> std::io::Result<()>

作用:确认开启 multi_agent_v2 时,旧的 [agents].max_threads 配置会被判定为冲突。

数据流:进去的是同时开启 features.multi_agent_v2 和设置 agents.max_threads = 3 的配置。加载能完成,但调用 validate_multi_agent_v2_config 后得到 InvalidInput 错误,错误文字说明这两个配置不能同时用;同时有效线程数仍能按旧字段算出 Some(3)。

调用关系:测试运行器执行它;它先用 ConfigBuilder 得到 Config,再专门调用 validate_multi_agent_v2_config,验证启动前的配置合法性检查。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 3 个(new, assert_eq!, write)。

catalog_v2_allows_agents_max_threads_when_feature_disabled9919–9944 ↗
async fn catalog_v2_allows_agents_max_threads_when_feature_disabled() -> std::io::Result<()>

作用:确认 multi_agent_v2 关闭时,旧的 agents.max_threads 仍然可以使用。

数据流:进去的是 features.multi_agent_v2.enabled = false,同时设置 agents.max_threads = 3。加载后校验通过,有效线程数是 Some(3)。

调用关系:测试运行器执行它;它和前一个冲突测试配套,说明限制只在 multi_agent_v2 开启时生效。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 3 个(new, assert_eq!, write)。

multi_agent_v2_rejects_invalid_wait_timeouts9947–10143 ↗
async fn multi_agent_v2_rejects_invalid_wait_timeouts() -> std::io::Result<()>

作用:确认 multi_agent_v2 的等待时间配置会拒绝负数、过大值,以及最小值/默认值/最大值之间不合理的组合。

数据流:测试反复改写同一个临时配置文件。先确认 0 毫秒这种边界值能加载;然后分别写入负数、超过 3600000 的值、最小值大于最大值、默认值小于最小值、默认值大于最大值。每次加载都应该返回 InvalidInput,并带有清楚的错误消息。

调用关系:测试运行器执行它;它多次调用 ConfigBuilder,覆盖等待时间校验器的各种边界情况。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 3 个(new, assert_eq!, write)。

multi_agent_v2_rejects_invalid_tool_namespace10146–10179 ↗
async fn multi_agent_v2_rejects_invalid_tool_namespace() -> std::io::Result<()>

作用:确认 multi_agent_v2 的工具命名空间不能包含空格,也不能使用系统保留的名字。

数据流:进去的是两组坏命名空间:bad namespace 和 functions。每组都会写进临时配置并尝试加载;加载失败,错误分别说明格式必须匹配字母数字下划线横线,或 functions 是保留命名空间。

调用关系:测试运行器执行它;它循环构造坏配置,通过 ConfigBuilder 检查工具命名空间校验逻辑。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 4 个(new, assert_eq!, format!, write)。

multi_agent_v2_session_thread_cap_one_disallows_subagents10182–10208 ↗
async fn multi_agent_v2_session_thread_cap_one_disallows_subagents() -> std::io::Result<()>

作用:确认多智能体总并发槽位设为 1 时,只够根代理自己用,不能再派生子代理。

数据流:进去的是 max_concurrent_threads_per_session = 1 的配置。加载后总上限是 1,而 effective_agent_max_threads 返回 Some(0),表示可用子代理数量为零。

调用关系:测试运行器执行它;它通过 ConfigBuilder 和 effective_agent_max_threads 检查并发数计算里的“根代理占一个槽位”规则。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 3 个(new, assert_eq!, write)。

feature_requirements_normalize_runtime_feature_mutations10211–10242 ↗
async fn feature_requirements_normalize_runtime_feature_mutations() -> std::io::Result<()>

作用:确认企业功能要求不仅在启动加载时有效,运行中尝试改功能开关时也会被自动纠正。

数据流:进去的是企业要求 personality 开、shell_tool 关。加载后测试复制当前功能集,故意把 personality 关掉、shell_tool 打开,再调用 features.set。结果设置操作成功,但最终功能仍被归一化成 personality 开、shell_tool 关。

调用关系:测试运行器执行它;它先用 ConfigBuilder 建立带企业要求的配置,再直接调用 features.can_set 和 features.set,验证运行时修改也受同一套规则约束。

调用图:调用 1 个内部函数(loader_with_enterprise_requirement);外部调用 3 个(new, assert!, default)。

feature_requirements_warn_on_collab_legacy_alias10245–10272 ↗
async fn feature_requirements_warn_on_collab_legacy_alias() -> std::io::Result<()>

作用:确认企业要求里如果使用旧功能名 collab,系统会兼容它,但会提醒应该改用新名字 multi_agent。

数据流:进去的是企业要求 [features] collab = true。加载后 Feature::Collab 被开启,同时 startup_warnings 里包含使用 legacy 名字以及建议 canonical key 的提示。

调用关系:测试运行器执行它;它通过企业要求加载器和 ConfigBuilder 检查旧配置名兼容路径。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 2 个(new, assert!)。

feature_requirements_warn_and_ignore_unknown_feature10275–10302 ↗
async fn feature_requirements_warn_and_ignore_unknown_feature() -> std::io::Result<()>

作用:确认企业要求里出现系统不认识的功能名时,不会让配置加载崩掉,而是忽略并警告。

数据流:进去的是企业要求 made_up_feature = true。加载后测试检查 startup_warnings,里面应该说明这个未知 features requirement 被忽略了。

调用关系:测试运行器执行它;它用 ConfigBuilder 走企业要求解析流程,验证未知字段的容错方式。

调用图:调用 2 个内部函数(loader_with_enterprise_requirement, without_managed_config_for_tests);外部调用 2 个(new, assert!)。

tool_suggest_discoverables_load_from_config_toml10305–10364 ↗
async fn tool_suggest_discoverables_load_from_config_toml() -> std::io::Result<()>

作用:确认工具推荐的 discoverables 列表能从配置里读取,并且空白 id 会在最终配置里被过滤掉。

数据流:进去的是 TOML 里的 tool_suggest.discoverables,包含 connector、plugin 和一个 id 只有空格的项。先检查原始 ConfigToml 能保留这些项;再加载成 Config 后,空白 id 被去掉,只留下有效的 connector 和 plugin。

调用关系:测试运行器执行它;它先用 toml::from_str 测试反序列化,再用 Config::load_from_base_config_with_overrides 测试最终归一化。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, from_str)。

tool_suggest_disabled_tools_load_from_config_toml10367–10413 ↗
async fn tool_suggest_disabled_tools_load_from_config_toml() -> std::io::Result<()>

作用:确认工具推荐里的禁用工具列表会修剪空格、去掉空 id,并合并重复项。

数据流:进去的是 disabled_tools,里面有带前后空格的 connector、重复 connector、空白 id 和一个 plugin。原始 TOML 解析阶段会保留原样;加载成最终 Config 后,只剩去空格后的 connector_calendar 和 slack plugin。

调用关系:测试运行器执行它;它连接 TOML 解析和 Config 归一化两步,验证用户写得不太规整时系统能整理干净。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, from_str)。

tool_suggest_disabled_tools_merge_across_config_layers10416–10470 ↗
async fn tool_suggest_disabled_tools_merge_across_config_layers() -> std::io::Result<()>

作用:确认用户全局配置和项目配置里的禁用工具会合并,并保持去重后的顺序。

数据流:进去的是两层配置:用户主目录里的 tool_suggest.disabled_tools,以及项目 .codex/config.toml 里的 disabled_tools。加载工作区配置后,结果包含 user_connector、shared_plugin、project_connector、project_plugin,重复项只出现一次,id 前后空格被去掉。

调用关系:测试运行器执行它;它创建全局配置和项目配置目录,再通过 ConfigBuilder 指定 cwd,验证配置分层合并逻辑。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 6 个(default, new, assert_eq!, format!, create_dir_all, write)。

experimental_realtime_start_instructions_load_from_config_toml10473–10499 ↗
async fn experimental_realtime_start_instructions_load_from_config_toml() -> std::io::Result<()>

作用:确认实验性的实时启动说明文字能从配置文件读到最终 Config 里。

数据流:进去的是 experimental_realtime_start_instructions 字符串。先检查 ConfigToml 解析出来有这个值,再加载成 Config,最终字段仍是同一段文字。

调用关系:测试运行器执行它;它用 toml::from_str 和 Config::load_from_base_config_with_overrides 验证这个实验字段的完整传递路径。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, from_str)。

experimental_thread_config_endpoint_loads_from_config_toml10502–10528 ↗
async fn experimental_thread_config_endpoint_loads_from_config_toml() -> std::io::Result<()>

作用:确认实验性的线程配置服务地址可以从配置里读取。

数据流:进去的是 experimental_thread_config_endpoint = http://127.0.0.1:8061。解析成 ConfigToml 后有这个地址,加载成 Config 后仍保留这个地址。

调用关系:测试运行器执行它;它覆盖实验接口地址字段从文本配置到运行配置的路径。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, from_str)。

experimental_realtime_ws_base_url_loads_from_config_toml10531–10564 ↗
async fn experimental_realtime_ws_base_url_loads_from_config_toml() -> std::io::Result<()>

作用:确认实验性的实时 WebSocket 地址和 WebRTC 呼叫地址都能从配置里读取。

数据流:进去的是 experimental_realtime_ws_base_url 和 experimental_realtime_webrtc_call_base_url 两个 URL。解析阶段和最终 Config 阶段都应该得到同样的两个字符串。

调用关系:测试运行器执行它;它通过 Config::load_from_base_config_with_overrides 检查实时通信相关地址不会在加载中丢失。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, from_str)。

experimental_realtime_ws_backend_prompt_loads_from_config_toml10567–10593 ↗
async fn experimental_realtime_ws_backend_prompt_loads_from_config_toml() -> std::io::Result<()>

作用:确认实验性的实时 WebSocket 后端提示词可以从配置里传到最终配置对象。

数据流:进去的是 experimental_realtime_ws_backend_prompt 字符串。TOML 解析后字段存在,加载成 Config 后也存在且内容不变。

调用关系:测试运行器执行它;它验证实时后端提示词这个实验字段的解析和落地。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, from_str)。

experimental_realtime_ws_startup_context_loads_from_config_toml10596–10622 ↗
async fn experimental_realtime_ws_startup_context_loads_from_config_toml() -> std::io::Result<()>

作用:确认实验性的实时 WebSocket 启动上下文可以从配置里读取。

数据流:进去的是 experimental_realtime_ws_startup_context。测试先确认 ConfigToml 中有值,再确认最终 Config 中同样有值。

调用关系:测试运行器执行它;它覆盖实时会话启动上下文字段的配置加载链路。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, from_str)。

experimental_realtime_ws_model_loads_from_config_toml10625–10651 ↗
async fn experimental_realtime_ws_model_loads_from_config_toml() -> std::io::Result<()>

作用:确认实验性的实时 WebSocket 模型名可以通过配置指定。

数据流:进去的是 experimental_realtime_ws_model = realtime-test-model。解析和最终加载后,模型名都保持为 realtime-test-model。

调用关系:测试运行器执行它;它保证实时功能选择模型时能拿到用户配置的模型名。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, from_str)。

realtime_config_partial_table_uses_realtime_defaults10654–10679 ↗
async fn realtime_config_partial_table_uses_realtime_defaults() -> std::io::Result<()>

作用:确认 [realtime] 表只写一部分字段时,没写的字段会使用实时功能的默认值。

数据流:进去的是只设置 voice = marin 的 realtime 配置。加载后 voice 是 Marin,其它 architecture、version、session_type、transport 等字段来自 RealtimeConfig::default。

调用关系:测试运行器执行它;它通过 Config::load_from_base_config_with_overrides 验证部分配置和默认值合并的行为。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, from_str)。

realtime_loads_from_config_toml10682–10725 ↗
async fn realtime_loads_from_config_toml() -> std::io::Result<()>

作用:确认 realtime 表里的架构、版本、会话类型、传输方式和声音选择都能正确解析。

数据流:进去的是 [realtime],包含 architecture = avas、version = v2、type = transcription、transport = webrtc、voice = cedar。先检查 ConfigToml 解析成对应枚举值,再检查最终 Config.realtime 也等于这些值。

调用关系:测试运行器执行它;它覆盖实时功能主要配置项从 TOML 到运行配置的完整链路。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, from_str)。

realtime_audio_loads_from_config_toml10728–10759 ↗
async fn realtime_audio_loads_from_config_toml() -> std::io::Result<()>

作用:确认音频输入输出设备名能从 [audio] 配置读出来。

数据流:进去的是 microphone = USB Mic 和 speaker = Desk Speakers。解析后 cfg.audio 中有这两个字符串,最终 Config.realtime_audio 中也保留这两个设备名。

调用关系:测试运行器执行它;它验证实时音频设置会被配置系统传给后续音频子系统。

调用图:外部调用 5 个(new, load_from_base_config_with_overrides, assert_eq!, default, from_str)。

test_tui_notifications_true10773–10783 ↗
fn test_tui_notifications_true()

作用:确认文字界面 TUI 的 notifications = true 会被解析成“启用通知”。TUI 是终端里的图形化文字界面。

数据流:进去的是一个 [tui] 配置片段,notifications 写成 true。toml::from_str 解析后,notifications 字段应是 Notifications::Enabled(true)。

调用关系:测试运行器执行它;它直接测试 RootTomlTest 的反序列化规则,不走完整 ConfigBuilder。

调用图:外部调用 2 个(assert_matches!, from_str)。

test_tui_notifications_custom_array10786–10796 ↗
fn test_tui_notifications_custom_array()

作用:确认 TUI 通知也可以写成自定义字符串数组,而不只是 true/false。

数据流:进去的是 notifications = ["foo"]。解析后 notifications 字段应是 Notifications::Custom,并且数组内容就是 foo。

调用关系:测试运行器执行它;它直接检查 TOML 反序列化对多种写法的支持。

调用图:外部调用 2 个(assert_matches!, from_str)。

test_tui_notification_method10799–10807 ↗
fn test_tui_notification_method()

作用:确认 TUI 的通知方式 notification_method = "bel" 会解析成响铃方式。

数据流:进去的是 notification_method = bel。解析后 method 字段变成 NotificationMethod::Bel。

调用关系:测试运行器执行它;它只测试 TUI 通知方法字段的解析,不涉及磁盘配置合并。

调用图:外部调用 2 个(assert_eq!, from_str)。

test_tui_notification_condition_defaults_to_unfocused10810–10820 ↗
fn test_tui_notification_condition_defaults_to_unfocused()

作用:确认用户没写通知触发条件时,默认只在窗口未聚焦时通知。

数据流:进去的是空的 [tui] 表。解析后 notification condition 是 NotificationCondition::Unfocused。

调用关系:测试运行器执行它;它验证默认值,避免通知在用户没配置时变得过于打扰。

调用图:外部调用 2 个(assert_eq!, from_str)。

test_tui_notification_condition_always10823–10834 ↗
fn test_tui_notification_condition_always()

作用:确认 notification_condition = "always" 会被解析成总是通知。

数据流:进去的是 [tui] 下的 notification_condition = always。解析后 condition 字段是 NotificationCondition::Always。

调用关系:测试运行器执行它;它和默认条件测试配套,检查用户显式选择会生效。

调用图:外部调用 2 个(assert_eq!, from_str)。

test_tui_notification_condition_rejects_unknown_value10837–10850 ↗
fn test_tui_notification_condition_rejects_unknown_value()

作用:确认 TUI 通知条件写成不认识的值时会报错,并告诉用户允许哪些值。

数据流:进去的是 notification_condition = background。解析 RootTomlTest 会失败;测试把错误转成文字,检查里面包含 unknown variant background,并列出 unfocused 和 always。

调用关系:测试运行器执行它;它直接测试 TOML 反序列化错误,保证配置写错时提示足够清楚。

调用图:外部调用 1 个(assert!)。

配置加载与托管策略解析

这些测试从端到端配置加载,推进到由分层配置、托管需求、启动迁移或警告派生出的更高层策略和功能决策。

core/src/config/config_loader_tests.rs源码 ↗
testtest

这个文件像配置系统的“验收清单”。Codex 的配置不是只从一个文件来,而是可能来自用户家目录、项目里的 .codex 文件夹、命令行覆盖项、系统托管配置、macOS 管理策略、云端企业配置、会话线程配置等。层多了就容易出错:谁覆盖谁、坏 TOML 怎么报错、相对路径按哪里算、不可信项目能不能生效、企业限制能不能被用户绕过。这里的测试会现场创建临时目录和配置文件,调用真正的加载函数,再检查合并后的配置、错误信息、权限约束、启动警告和执行规则是否符合预期。它不提供线上功能,但非常重要:没有这些测试,配置优先级或安全边界一改坏,可能直到用户机器上才暴露。

函数细节86
config_error_from_io46–51 ↗
fn config_error_from_io(err: &std::io::Error) -> &ConfigError

作用:从一个普通的输入输出错误里取出真正的配置错误,方便测试比较错误内容。它把外层包装拆开,看里面是不是配置加载失败。

数据流:进去的是一个 std::io::Error → 它查看错误内部附带的原始错误,并向下转换成 ConfigLoadError → 出来的是里面的 ConfigError 引用;如果不是预期类型,测试直接失败。

调用关系:多个错误类测试在拿到加载失败结果后都会调用它,然后再和预期的 TOML 解析错误或字段错误做精确比较。

调用图:被 5 处调用(returns_config_error_for_invalid_managed_config_toml, returns_config_error_for_invalid_user_config_toml, returns_config_error_for_schema_error_in_user_config, strict_config_rejects_unknown_feature_user_config_key, strict_config_rejects_unknown_user_config_key);外部调用 1 个(get_ref)。

cloud_config_bundle_requirement_source53–58 ↗
fn cloud_config_bundle_requirement_source() -> RequirementSource

作用:生成一个固定的“云端企业要求”来源标记。测试用它确认某条限制确实来自云端配置包。

数据流:没有输入 → 构造一个带 id 和名称的 RequirementSource::EnterpriseManaged → 返回这个来源对象。

调用关系:云端要求相关测试用它当标准答案,尤其在检查冲突时确认错误报告里写的是正确来源。

调用图:被 1 处调用(system_remote_sandbox_config_keeps_cloud_sandbox_modes)。

load_single_requirements_toml60–67 ↗
async fn load_single_requirements_toml(
    requirements_file: &AbsolutePathBuf,
) -> anyhow::Result<ConfigRequirementsWithSources>

作用:读取一个 requirements.toml,并把它整理成测试更容易检查的要求对象。requirements.toml 可以理解成管理员写的“哪些设置允许、哪些不允许”的规则表。

数据流:进去的是 requirements.toml 的绝对路径 → 它调用真实加载器读取文件,再把单层要求组合成最终要求 → 返回带来源信息的配置要求。

调用关系:路径解析和约束解析测试会用它,避免每个测试重复写加载和合并步骤。

调用图:调用 1 个内部函数(load_requirements_toml);被 3 处调用(load_requirements_toml_produces_expected_constraints, load_requirements_toml_resolves_deny_read_against_parent, load_requirements_toml_resolves_deny_read_glob_against_parent);外部调用 2 个(compose_requirements, vec!)。

make_config_for_test69–90 ↗
async fn make_config_for_test(
    codex_home: &Path,
    project_path: &Path,
    trust_level: TrustLevel,
    project_root_markers: Option<Vec<String>>,
) -> std::io::Result<()>

作用:快速写一个测试用的用户 config.toml,里面记录某个项目是否可信。很多项目配置测试都需要先声明“这个项目可信/不可信”。

数据流:进去的是 Codex home、项目路径、信任等级和可选项目根标记 → 它组装 ConfigToml 并写到 config.toml → 磁盘上多出一份可被真实加载器读取的用户配置。

调用关系:项目层、工作树、信任判断、相对路径等测试都会先调用它准备用户配置,再交给 load_config_layers_state 或 ConfigBuilder。

调用图:被 15 处调用(cli_override_can_update_project_local_mcp_server_when_project_is_trusted, cli_overrides_with_relative_paths_do_not_break_trust_check, codex_home_within_project_tree_is_not_double_loaded, invalid_project_config_ignored_when_untrusted_or_unknown, linked_worktree_project_layers_keep_worktree_config_but_use_root_repo_hooks, linked_worktree_project_layers_use_root_repo_hooks_without_worktree_config_toml, nested_project_root_markers_do_not_redirect_regular_repo_hooks, project_layer_ignores_unsupported_config_keys, project_layer_is_added_when_dot_codex_exists_without_config_toml, project_layer_without_config_toml_is_disabled_when_untrusted_or_unknown (+5 more));外部调用 6 个(default, from, join, to_string_lossy, write, to_string)。

write_linked_worktree_pointer92–103 ↗
async fn write_linked_worktree_pointer(
    repo_root: &Path,
    worktree_root: &Path,
) -> std::io::Result<()>

作用:伪造一个 Git linked worktree 的 .git 指针文件。linked worktree 可以理解成同一个仓库的另一个工作目录。

数据流:进去的是主仓库根目录和工作树根目录 → 它创建 .git/worktrees/feature-x 目录,并在工作树写入 gitdir 指针 → 测试目录看起来就像一个真实的 linked worktree。

调用关系:linked worktree 相关测试用它搭建场景,然后检查配置来自工作树,而 hooks 是否能回到主仓库查找。

调用图:被 2 处调用(linked_worktree_project_layers_keep_worktree_config_but_use_root_repo_hooks, linked_worktree_project_layers_use_root_repo_hooks_without_worktree_config_toml);外部调用 4 个(join, format!, create_dir_all, write)。

write_project_hook_config105–129 ↗
async fn write_project_hook_config(
    dot_codex_folder: &Path,
    foo: Option<&str>,
    command: &str,
) -> std::io::Result<()>

作用:写一个项目级 hooks 配置。hook 是在某些工具使用前后自动执行的小命令,这里主要用来测试 hooks 配置从哪里加载。

数据流:进去的是 .codex 文件夹、可选 foo 值和命令字符串 → 它创建文件夹并写入 config.toml,里面包含 PreToolUse hook → 磁盘上出现可被项目配置加载器读到的 hook 配置。

调用关系:工作树和项目根标记测试用它准备多个位置的 hooks,再用 project_hook_command 检查最终选中了哪一个。

调用图:被 3 处调用(linked_worktree_project_layers_keep_worktree_config_but_use_root_repo_hooks, linked_worktree_project_layers_use_root_repo_hooks_without_worktree_config_toml, nested_project_root_markers_do_not_redirect_regular_repo_hooks);外部调用 4 个(join, format!, create_dir_all, write)。

cli_overrides_resolve_relative_paths_against_cwd132–153 ↗
async fn cli_overrides_resolve_relative_paths_against_cwd() -> std::io::Result<()>

作用:确认命令行传入的相对路径,是按当前工作目录来解释的。否则日志目录这类路径可能会落到用户意想不到的位置。

数据流:它创建临时 Codex home 和临时 cwd → 用命令行覆盖项设置 log_dir = run-logs → 构建配置后检查 log_dir 是否变成 cwd/run-logs。

调用关系:这个测试直接走 ConfigBuilder,验证命令行覆盖项进入最终配置前的路径解析规则。

调用图:调用 1 个内部函数(resolve_path_against_base);外部调用 5 个(default, assert_eq!, default, tempdir, vec!)。

returns_config_error_for_invalid_user_config_toml156–179 ↗
async fn returns_config_error_for_invalid_user_config_toml()

作用:确认用户 config.toml 语法坏掉时,会返回清楚的配置错误,而不是模糊的普通失败。

数据流:它写入一份破损 TOML → 调用 load_config_layers_state → 得到错误后抽出 ConfigError,并和 TOML 解析器生成的预期错误比较。

调用关系:它依赖 config_error_from_io 拆出内部配置错误,验证用户配置层的错误传播。

调用图:调用 3 个内部函数(load_config_layers_state, config_error_from_io, try_from);外部调用 5 个(assert_eq!, config_error_from_toml, default, write, tempdir)。

ignore_user_config_keeps_empty_user_layer182–215 ↗
async fn ignore_user_config_keeps_empty_user_layer() -> std::io::Result<()>

作用:确认选择忽略用户配置时,系统仍保留一个空的用户层记录。这样界面或后续逻辑仍知道“用户层存在,只是被忽略”。

数据流:它写入一份坏的用户配置 → 以 ignore_user_config 开关加载 → 返回的层栈里用户层为空表,最终配置里没有用户文件里的 model。

调用关系:它直接测试 load_config_layers_state 的忽略开关,保证忽略不是简单把整层元数据删掉。

调用图:调用 2 个内部函数(load_config_layers_state, try_from);外部调用 4 个(default, assert_eq!, write, tempdir)。

ignore_rules_marks_config_stack_for_exec_policy_rule_skip218–237 ↗
async fn ignore_rules_marks_config_stack_for_exec_policy_rule_skip() -> std::io::Result<()>

作用:确认忽略用户和项目执行策略规则的开关,会被记录在配置层栈上。执行策略规则就是决定命令能否运行、是否要询问的规则。

数据流:它用 ignore_user_and_project_exec_policy_rules 加载空配置 → 读取层栈标记 → 结果应显示后续执行策略加载时要跳过这些规则。

调用关系:它为执行策略加载流程打前站,检查配置加载器是否把这个控制信号传下去。

调用图:调用 2 个内部函数(load_config_layers_state, try_from);外部调用 3 个(default, assert!, tempdir)。

returns_config_error_for_invalid_managed_config_toml240–266 ↗
async fn returns_config_error_for_invalid_managed_config_toml()

作用:确认托管配置文件语法坏掉时,也会给出准确的配置错误。托管配置通常来自管理员,不能静默吞掉错误。

数据流:它写一份破损 managed_config.toml → 指定为测试托管配置路径并加载 → 抽出 ConfigError,与预期 TOML 错误比较。

调用关系:它和用户配置坏语法测试类似,但覆盖的是管理员/系统配置入口。

调用图:调用 4 个内部函数(load_config_layers_state, with_managed_config_path_for_tests, config_error_from_io, try_from);外部调用 4 个(assert_eq!, config_error_from_toml, write, tempdir)。

returns_config_error_for_schema_error_in_user_config269–288 ↗
async fn returns_config_error_for_schema_error_in_user_config()

作用:确认用户配置字段类型不对时,会指出结构错误。比如本该是数字却写成字符串。

数据流:它写入 model_context_window = "not_a_number" → 通过 ConfigBuilder 构建配置 → 构建失败后抽出 ConfigError,并和按 ConfigToml 类型校验得到的错误比较。

调用关系:它使用 config_error_from_io,测试的是 TOML 语法正确但不符合配置结构的情况。

调用图:调用 2 个内部函数(config_error_from_io, new);外部调用 4 个(assert_eq!, default, write, tempdir)。

top_level_allow_managed_hooks_only_in_user_config_does_not_enable_requirements_policy291–315 ↗
async fn top_level_allow_managed_hooks_only_in_user_config_does_not_enable_requirements_policy() -> std::io::Result<()>

作用:确认用户配置顶层写 allow_managed_hooks_only,不会偷偷变成管理员要求。也就是说用户不能自己伪装成企业策略。

数据流:它在用户 config.toml 顶层写 allow_managed_hooks_only = true → 加载层栈 → requirements_toml 和最终 requirements 中该限制都应为空。

调用关系:它检查普通用户配置和管理员 requirements 之间的边界。

调用图:调用 2 个内部函数(load_config_layers_state, try_from);外部调用 5 个(assert!, assert_eq!, default, write, tempdir)。

hooks_allow_managed_hooks_only_in_user_config_does_not_enable_requirements_policy318–356 ↗
async fn hooks_allow_managed_hooks_only_in_user_config_does_not_enable_requirements_policy() -> std::io::Result<()>

作用:确认用户在 hooks 区域写 allow_managed_hooks_only,也不会启用管理员级 hook 限制。

数据流:它写入带 hooks 的用户配置 → 加载后确认 hooks 仍作为普通配置读到 → 但 requirements 里的 allow_managed_hooks_only 仍为空。

调用关系:它补充上一条测试,覆盖字段放在 hooks 表里的写法。

调用图:调用 2 个内部函数(load_config_layers_state, try_from);外部调用 5 个(assert!, assert_eq!, default, write, tempdir)。

strict_config_rejects_unknown_user_config_key359–380 ↗
async fn strict_config_rejects_unknown_user_config_key()

作用:确认严格配置模式会拒绝用户 config.toml 里的未知字段。严格模式就是“不认识的配置一律报错”。

数据流:它写入 unknown_key → 用 strict_config 构建配置 → 构建失败,错误内容应等于未知字段检测生成的 ConfigError。

调用关系:它通过 ConfigBuilder 走完整构建流程,并用 config_error_from_io 检查错误细节。

调用图:调用 2 个内部函数(without_managed_config_for_tests, config_error_from_io);外部调用 4 个(assert_eq!, default, write, tempdir)。

strict_config_rejects_unknown_cli_override_key383–403 ↗
async fn strict_config_rejects_unknown_cli_override_key()

作用:确认严格配置模式也会拒绝命令行 -c/--config 里未知的键。

数据流:它传入 cli_overrides: foo = "bar" → 开启 strict_config 构建 → 返回错误字符串,明确说 foo 是未知配置字段。

调用关系:它覆盖命令行覆盖项这一入口,防止用户拼错字段却以为生效。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 4 个(assert_eq!, default, tempdir, vec!)。

strict_config_rejects_unknown_cli_override_key_with_relative_path_override406–431 ↗
async fn strict_config_rejects_unknown_cli_override_key_with_relative_path_override()

作用:确认即使命令行里同时有合法的相对路径字段,未知字段仍会被严格拒绝。

数据流:它准备 instructions.md,再传入合法 model_instructions_file 和非法 foo → 构建配置 → 结果仍报 foo 未知。

调用关系:它防止路径预处理流程把未知字段检查绕过去。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 5 个(assert_eq!, default, write, tempdir, vec!)。

strict_config_rejects_unknown_feature_cli_override_key434–451 ↗
async fn strict_config_rejects_unknown_feature_cli_override_key()

作用:确认 features 下面的未知命令行覆盖项也会被拒绝。features 是功能开关集合。

数据流:它传入 features.foo = true → 严格构建 → 返回错误,指出 features.foo 未知。

调用关系:它专门覆盖嵌套字段的命令行检查。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 4 个(assert_eq!, default, tempdir, vec!)。

strict_config_rejects_unknown_feature_user_config_key454–477 ↗
async fn strict_config_rejects_unknown_feature_user_config_key()

作用:确认用户配置里 features 表下的未知键,会在严格模式下报错并定位到正确行列。

数据流:它写入 [features] foo = true → 严格构建失败 → 抽出 ConfigError,检查消息和错误位置。

调用关系:它使用 config_error_from_io,补充测试嵌套用户配置字段的定位能力。

调用图:调用 2 个内部函数(without_managed_config_for_tests, config_error_from_io);外部调用 4 个(assert_eq!, default, write, tempdir)。

strict_config_points_to_unknown_nested_key480–497 ↗
fn strict_config_points_to_unknown_nested_key()

作用:确认未知的深层嵌套键能报出完整路径。比如 mcp_servers.local.unknown_key,而不是只说 unknown_key。

数据流:它写入带未知字段的 mcp_servers.local 配置 → 调用未知字段检测 → 检查错误消息和行列。

调用关系:它直接测试配置错误生成工具,不走完整 ConfigBuilder。

调用图:外部调用 3 个(assert_eq!, write, tempdir)。

schema_error_points_to_feature_value499–514 ↗
fn schema_error_points_to_feature_value()

作用:确认字段类型错误时,错误位置指向具体错误值,而不是只指向表头或文件开头。

数据流:它写入 features.collaboration_modes = "true" 这种类型不匹配的值 → 进行类型校验 → 检查错误行列正好落在 "true" 上。

调用关系:它直接测试类型化 TOML 校验的定位质量。

调用图:调用 1 个内部函数(new);外部调用 3 个(assert_eq!, write, tempdir)。

merges_managed_config_layer_on_top517–567 ↗
async fn merges_managed_config_layer_on_top()

作用:确认托管配置层会覆盖普通用户配置层。管理员配置优先级更高。

数据流:它分别写用户配置和 managed_config.toml → 加载层栈 → 最终 foo 和 nested.value 来自托管配置,nested.extra 也被合并进来。

调用关系:它调用 load_config_layers_state,测试配置层合并规则中的管理员优先级。

调用图:调用 3 个内部函数(load_config_layers_state, with_managed_config_path_for_tests, try_from);外部调用 3 个(assert_eq!, write, tempdir)。

returns_empty_when_all_layers_missing570–629 ↗
async fn returns_empty_when_all_layers_missing()

作用:确认所有可选配置文件都不存在时,加载结果是空配置而不是失败。

数据流:它指定不存在的托管配置和空用户目录 → 加载层栈 → 得到空用户层、空有效配置,并确认系统层仍然存在。

调用关系:它测试配置加载器的默认启动场景:没有任何配置也要能正常运行。

调用图:调用 5 个内部函数(load_config_layers_state, new, with_managed_config_path_for_tests, resolve_path_against_base, try_from);外部调用 5 个(Table, assert!, assert_eq!, tempdir, new)。

selected_user_config_file_layers_over_base_user_config632–700 ↗
async fn selected_user_config_file_layers_over_base_user_config()

作用:确认用户选择了额外的配置文件时,它会叠在默认用户 config.toml 上方。

数据流:它写默认用户配置和 work.config.toml → 设置 user_config_path 和 profile → 加载后 model 来自选中文件,而 approval_policy 仍继承默认文件。

调用关系:它检查用户配置的多层结构和 active user layer 的来源信息。

调用图:调用 4 个内部函数(load_config_layers_state, with_managed_config_path_for_tests, from_absolute_path, try_from);外部调用 3 个(assert_eq!, write, tempdir)。

includes_thread_config_layers_in_stack703–757 ↗
async fn includes_thread_config_layers_in_stack() -> anyhow::Result<()>

作用:确认会话线程配置也会进入配置层栈,并且能覆盖命令行会话标志。线程配置是一次会话内部带来的配置。

数据流:它传入命令行 features.plugins=true,并用 StaticThreadConfigLoader 提供 plugins=false → 加载层栈 → 层来源包含两个 SessionFlags,最终 plugins 为 false。

调用关系:它测试 load_config_layers_state 和线程配置加载器的配合。

调用图:调用 4 个内部函数(load_config_layers_state, without_managed_config_for_tests, new, from_absolute_path);外部调用 5 个(Boolean, assert_eq!, tempdir, create_dir_all, vec!)。

managed_preferences_take_highest_precedence761–827 ↗
async fn managed_preferences_take_highest_precedence()

作用:在 macOS 上,确认 MDM 管理偏好优先级最高。MDM 是企业设备管理下发策略的一种方式。

数据流:它写用户配置、托管文件和 base64 编码的管理偏好 → 加载后 nested 值来自管理偏好,并保留原始 TOML 文本 → 返回的层栈包含 MDM 层。

调用关系:它只在 macOS 测试,覆盖系统管理策略比本地文件更高的优先级。

调用图:调用 3 个内部函数(load_config_layers_state, with_managed_config_path_for_tests, try_from);外部调用 4 个(assert!, assert_eq!, write, tempdir)。

managed_preferences_expand_home_directory_in_workspace_write_roots831–876 ↗
async fn managed_preferences_expand_home_directory_in_workspace_write_roots() -> anyhow::Result<()>

作用:在 macOS 上,确认管理偏好里的 ~/code 会展开成真实家目录路径。否则沙箱可写目录会指错位置。

数据流:它把 sandbox_workspace_write.writable_roots 写成 ~/code 并 base64 注入管理偏好 → 构建配置 → 检查沙箱策略里的可写路径是 home/code。

调用关系:它通过 ConfigBuilder 走完整配置到沙箱策略的转换流程。

调用图:调用 2 个内部函数(with_managed_config_path_for_tests, from_absolute_path);外部调用 5 个(assert_eq!, default, home_dir, panic!, tempdir)。

managed_preferences_requirements_are_applied880–931 ↗
async fn managed_preferences_requirements_are_applied() -> anyhow::Result<()>

作用:在 macOS 上,确认 MDM 下发的 requirements 会真正限制审批策略和沙箱模式。

数据流:它注入只允许 never 审批和 read-only 沙箱的管理要求 → 加载层栈 → 最终要求值被设为 Never 和只读,尝试设置其他值会失败。

调用关系:它测试 macOS 管理要求入口和 ConfigRequirements 的约束逻辑。

调用图:调用 3 个内部函数(load_config_layers_state, with_managed_config_path_for_tests, try_from);外部调用 3 个(assert!, assert_eq!, tempdir)。

managed_preferences_requirements_take_precedence935–981 ↗
async fn managed_preferences_requirements_take_precedence() -> anyhow::Result<()>

作用:在 macOS 上,确认 MDM requirements 比托管配置文件里的普通设置更强。

数据流:它让托管配置写 approval_policy=on-request,同时 MDM 要求只允许 never → 加载后最终要求是 Never,OnRequest 被拒绝。

调用关系:它覆盖 MDM requirements 与 managed_config.toml 冲突时的优先级。

调用图:调用 3 个内部函数(load_config_layers_state, with_managed_config_path_for_tests, try_from);外部调用 4 个(assert!, assert_eq!, tempdir, write)。

load_requirements_toml_produces_expected_constraints984–1073 ↗
async fn load_requirements_toml_produces_expected_constraints() -> anyhow::Result<()>

作用:确认 requirements.toml 能被解析成实际可执行的限制,比如审批、联网搜索、数据驻留和功能开关。

数据流:它写 requirements.toml → 用 load_single_requirements_toml 读取 → 检查 TOML 层字段,再转换成 ConfigRequirements 并测试哪些值能设、哪些不能设。

调用关系:它是 requirements 解析和约束对象转换的基础覆盖。

调用图:调用 2 个内部函数(load_single_requirements_toml, try_from);外部调用 4 个(assert!, assert_eq!, tempdir, write)。

mdm_requirements_take_precedence_over_cloud_config_bundle1077–1127 ↗
async fn mdm_requirements_take_precedence_over_cloud_config_bundle() -> anyhow::Result<()>

作用:在 macOS 上,确认 MDM 要求会压过云端企业配置包里的要求。

数据流:它设置 MDM 允许 on-request,同时云端要求 never → 加载层栈 → 最终审批值是 OnRequest,尝试 Never 会返回带 MDM 来源的错误。

调用关系:它同时使用 MDM 注入和 CloudConfigBundleFixture,测试企业来源之间的优先级。

调用图:调用 4 个内部函数(load_config_layers_state, without_managed_config_for_tests, loader_with_enterprise_requirement, try_from);外部调用 3 个(default, assert_eq!, tempdir)。

cloud_config_bundle_are_not_overwritten_by_system_requirements1130–1172 ↗
async fn cloud_config_bundle_are_not_overwritten_by_system_requirements() -> anyhow::Result<()>

作用:确认云端企业要求不会被本地系统 requirements 覆盖。云端在这里应更高优先。

数据流:它写系统 requirements 允许 on-request,再加入云端要求 never → compose_requirements 合并 → 最终 allowed_approval_policies 来自云端。

调用关系:它直接测试 compose_requirements 的合并顺序,不走完整层加载。

调用图:调用 2 个内部函数(load_requirements_toml, try_from);外部调用 5 个(assert_eq!, compose_requirements, tempdir, write, vec!)。

system_remote_sandbox_config_keeps_cloud_sandbox_modes1175–1218 ↗
async fn system_remote_sandbox_config_keeps_cloud_sandbox_modes() -> anyhow::Result<()>

作用:确认系统里的远程沙箱例外,不会放宽云端已经收紧的沙箱限制。

数据流:它让系统 remote_sandbox_config 允许 read-only 和 workspace-write,云端只允许 read-only → 合并并转换成 ConfigRequirements → workspace-write 被拒绝,错误来源是云端。

调用关系:它用 cloud_config_bundle_requirement_source 检查拒绝原因来自正确的企业要求。

调用图:调用 3 个内部函数(load_requirements_toml, cloud_config_bundle_requirement_source, try_from);外部调用 5 个(assert_eq!, compose_requirements, tempdir, write, vec!)。

load_requirements_toml_resolves_deny_read_against_parent1221–1266 ↗
async fn load_requirements_toml_resolves_deny_read_against_parent() -> anyhow::Result<()>

作用:确认 requirements.toml 里的禁止读取路径,会按该文件所在目录来解析。

数据流:它把 deny_read 写成 ./sensitive 和 ../shared/secret.txt → 加载要求 → 得到的路径变成对应的绝对路径,并带系统 requirements 来源。

调用关系:它通过 load_single_requirements_toml 测试文件权限限制的相对路径规则。

调用图:调用 2 个内部函数(load_single_requirements_toml, try_from);外部调用 4 个(assert_eq!, tempdir, create_dir_all, write)。

load_requirements_toml_resolves_deny_read_glob_against_parent1269–1315 ↗
async fn load_requirements_toml_resolves_deny_read_glob_against_parent() -> anyhow::Result<()>

作用:确认禁止读取的通配符路径也按 requirements.toml 所在目录解析。通配符路径就是带 ** 或 * 的匹配模式。

数据流:它写 deny_read = ./sensitive/**/*.txt → 加载要求 → 得到规范化后的绝对通配符模式。

调用关系:它补充上一条测试,覆盖 glob 模式而不是普通路径。

调用图:调用 2 个内部函数(load_single_requirements_toml, try_from);外部调用 4 个(assert_eq!, tempdir, create_dir_all, write)。

load_config_layers_includes_cloud_config_bundle1318–1360 ↗
async fn load_config_layers_includes_cloud_config_bundle() -> anyhow::Result<()>

作用:确认完整配置加载流程会把云端企业 requirements 纳入最终要求。

数据流:它创建 Codex home 和云端要求 allowed_approval_policies=[never] → 加载层栈 → requirements_toml 匹配云端内容,设置 OnRequest 会被拒绝。

调用关系:它走 load_config_layers_state,验证云端配置包入口接入了主加载流程。

调用图:调用 3 个内部函数(load_config_layers_state, loader_with_enterprise_requirement, from_absolute_path);外部调用 5 个(default, assert_eq!, tempdir, create_dir_all, from_str)。

system_requirements_define_managed_permission_profiles1363–1414 ↗
async fn system_requirements_define_managed_permission_profiles() -> anyhow::Result<()>

作用:确认系统 requirements 可以定义管理员提供的权限配置档,并让用户选择它。权限配置档就是一组文件/命令访问权限的模板。

数据流:它在用户配置里选 managed-standard,并在 requirements 里定义这个 profile → 构建配置 → 最终 active_permission_profile 是 managed-standard。

调用关系:它通过 ConfigBuilder 测试 requirements、用户默认权限和权限系统的联动。

调用图:调用 2 个内部函数(without_managed_config_for_tests, from_absolute_path);外部调用 5 个(assert_eq!, default, tempdir, create_dir_all, write)。

system_allowed_permission_profiles_select_managed_default_without_local_default1417–1477 ↗
async fn system_allowed_permission_profiles_select_managed_default_without_local_default() -> anyhow::Result<()>

作用:确认当系统限制可用权限配置档并给出默认值时,即使用户没本地默认值,也会选中管理员默认值。

数据流:它分别测试可信、不可信、未知项目 → requirements 给 default_permissions=managed-standard 和允许列表 → 构建后 active profile 都是 managed-standard 且没有不该出现的警告。

调用关系:它循环调用 make_config_for_test 准备不同信任状态,验证默认权限选择不被项目信任干扰。

调用图:调用 3 个内部函数(without_managed_config_for_tests, make_config_for_test, from_absolute_path);外部调用 6 个(assert!, assert_eq!, default, tempdir, create_dir_all, write)。

system_allowed_permission_profiles_require_managed_default1480–1514 ↗
async fn system_allowed_permission_profiles_require_managed_default() -> anyhow::Result<()>

作用:确认如果系统只允许某些自定义权限配置档,就必须设置默认权限,除非允许列表足够覆盖标准组合。

数据流:它写 requirements 只声明 allowed_permission_profiles 和 permissions,但没有 default_permissions → 构建配置 → 应失败并提示必须设置 default_permissions。

调用关系:它验证管理员 requirements 本身的完整性检查。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 5 个(assert!, default, tempdir, create_dir_all, write)。

system_allowed_permission_profiles_standard_pair_defaults_to_workspace1517–1550 ↗
async fn system_allowed_permission_profiles_standard_pair_defaults_to_workspace() -> anyhow::Result<()>

作用:确认当允许列表只包含内置 read-only 和 workspace 两个标准档时,默认会选 workspace。

数据流:它写 allowed_permission_profiles 允许 :read-only 和 :workspace → 构建配置 → active profile 是内置 workspace。

调用关系:它覆盖一个特殊默认规则,避免管理员必须重复写默认值。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 5 个(assert_eq!, default, tempdir, create_dir_all, write)。

system_managed_default_must_be_allowed1553–1592 ↗
async fn system_managed_default_must_be_allowed() -> anyhow::Result<()>

作用:确认 requirements 里的默认权限配置档必须也在允许列表中。

数据流:它把 default_permissions 设为 managed-build,但 allowed_permission_profiles 只允许 managed-standard → 构建失败 → 错误提示默认值必须被允许。

调用关系:它防止管理员配置内部自相矛盾。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 5 个(assert!, default, tempdir, create_dir_all, write)。

system_managed_default_requires_allowed_permission_profiles1595–1624 ↗
async fn system_managed_default_requires_allowed_permission_profiles() -> anyhow::Result<()>

作用:确认只写 default_permissions 但不写 allowed_permission_profiles 是不合法的。

数据流:它在 requirements 里只写 default_permissions=:read-only → 构建失败 → 错误说明 default_permissions 需要 allowed_permission_profiles 配套。

调用关系:它测试权限限制配置的必填关系。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 5 个(assert!, default, tempdir, create_dir_all, write)。

system_allowed_permission_profiles_fall_back_from_disallowed_danger_full_access1627–1680 ↗
async fn system_allowed_permission_profiles_fall_back_from_disallowed_danger_full_access() -> anyhow::Result<()>

作用:确认用户配置了危险的全访问权限但管理员不允许时,会回退到管理员默认值并给警告。

数据流:它让用户默认权限为 danger-full-access,requirements 只允许 managed-standard → 构建后 active profile 是 managed-standard,并包含“被 requirements 禁止”的启动警告。

调用关系:它检查安全回退逻辑,避免危险配置绕过企业限制。

调用图:调用 2 个内部函数(without_managed_config_for_tests, from_absolute_path);外部调用 7 个(assert!, assert_eq!, default, format!, tempdir, create_dir_all, write)。

system_allowed_permission_profiles_fall_back_from_disallowed_workspace1683–1734 ↗
async fn system_allowed_permission_profiles_fall_back_from_disallowed_workspace() -> anyhow::Result<()>

作用:确认即使用户配置的是普通 workspace 权限,只要管理员不允许,也会回退到管理员默认值。

数据流:它让用户默认权限为 :workspace,requirements 只允许 managed-standard → 构建后使用 managed-standard,并产生警告。

调用关系:它覆盖非危险但仍不在允许列表中的用户配置。

调用图:调用 2 个内部函数(without_managed_config_for_tests, from_absolute_path);外部调用 6 个(assert!, assert_eq!, default, tempdir, create_dir_all, write)。

system_requirements_preserve_allowed_configured_permission_default1737–1786 ↗
async fn system_requirements_preserve_allowed_configured_permission_default() -> anyhow::Result<()>

作用:确认如果用户选择的权限配置档在管理员允许列表里,就保留用户选择。

数据流:它让用户选 managed-build,requirements 默认 managed-standard 但允许 managed-build 和 managed-standard → 构建后 active profile 仍是 managed-build。

调用关系:它证明系统默认值不是无条件覆盖用户,而是先看用户值是否被允许。

调用图:调用 2 个内部函数(without_managed_config_for_tests, from_absolute_path);外部调用 5 个(assert_eq!, default, tempdir, create_dir_all, write)。

system_requirements_warn_for_disallowed_explicit_permission_override1789–1837 ↗
async fn system_requirements_warn_for_disallowed_explicit_permission_override() -> anyhow::Result<()>

作用:确认测试环境显式覆盖了一个不允许的权限配置档时,会回退并给警告。

数据流:它通过 harness_overrides 设置 default_permissions=managed-build,requirements 只允许 managed-standard → 构建后使用 managed-standard,并记录警告。

调用关系:它覆盖非文件来源的覆盖项,确保同样受 requirements 限制。

调用图:调用 2 个内部函数(without_managed_config_for_tests, from_absolute_path);外部调用 7 个(assert!, assert_eq!, default, default, tempdir, create_dir_all, write)。

load_config_layers_inserts_cloud_config_between_system_and_user1840–1919 ↗
async fn load_config_layers_inserts_cloud_config_between_system_and_user() -> anyhow::Result<()>

作用:确认云端企业配置层插在系统配置和用户配置之间。也就是用户仍可覆盖云端普通配置,但云端可覆盖系统默认配置。

数据流:它写系统配置、用户配置和云端配置 → 加载层栈 → model 来自用户,model_provider 来自云端,review_model 来自系统,并检查层顺序。

调用关系:它测试云端配置层的准确位置,不是 requirements,而是普通配置项。

调用图:调用 4 个内部函数(load_config_layers_state, without_managed_config_for_tests, loader_with_enterprise_config, from_absolute_path);外部调用 5 个(default, assert_eq!, tempdir, create_dir_all, write)。

load_config_layers_can_ignore_managed_requirements1922–1974 ↗
async fn load_config_layers_can_ignore_managed_requirements() -> anyhow::Result<()>

作用:确认测试/特殊模式可以忽略托管 requirements。这样某些场景可以只读配置,不执行企业限制。

数据流:它准备托管配置、系统 requirements 和云端 requirements,但设置 ignore_managed_requirements=true → 构建后 OnRequest 审批仍可设置。

调用关系:它通过 ConfigBuilder 验证 ignore 开关会影响系统和云端 requirements。

调用图:调用 3 个内部函数(with_managed_config_path_for_tests, loader_with_enterprise_requirement, from_absolute_path);外部调用 5 个(assert!, default, tempdir, create_dir_all, write)。

load_config_layers_includes_cloud_hook_requirements1977–2030 ↗
async fn load_config_layers_includes_cloud_hook_requirements() -> anyhow::Result<()>

作用:确认云端 requirements 里的托管 hooks 会进入最终要求,并保留来源。

数据流:它创建 managed-hooks 目录,把 hooks 配置写进云端 requirements → 加载层栈 → requirements_toml.hooks 匹配预期,managed_hooks 来源是云端。

调用关系:它覆盖云端企业 hook 管理策略进入主加载流程。

调用图:调用 3 个内部函数(load_config_layers_state, loader_with_enterprise_requirement, from_absolute_path);外部调用 6 个(default, assert_eq!, format!, tempdir, create_dir_all, from_str)。

load_config_layers_resolves_relative_bundle_requirements_paths_against_codex_home2033–2079 ↗
async fn load_config_layers_resolves_relative_bundle_requirements_paths_against_codex_home() -> anyhow::Result<()>

作用:确认云端 requirements 包里的相对 deny_read 路径按 Codex home 解析。

数据流:它在云端 requirements 写 deny_read=["secrets/"] → 加载层栈 → 最终路径变成 codex_home/secrets/

调用关系:它测试云端配置包没有本地文件目录时的路径基准。

调用图:调用 4 个内部函数(load_config_layers_state, without_managed_config_for_tests, loader_with_enterprise_requirement, from_absolute_path);外部调用 4 个(default, assert_eq!, tempdir, create_dir_all)。

strict_config_rejects_unknown_cloud_config_key2082–2112 ↗
async fn strict_config_rejects_unknown_cloud_config_key()

作用:确认严格配置模式也会拒绝云端配置里的未知字段。

数据流:它用云端企业配置 unknown_key=true,并开启 strict_config → load_config_layers_state 返回错误 → 错误文本包含 unknown_key。

调用关系:它覆盖云端普通配置的字段校验入口。

调用图:调用 4 个内部函数(load_config_layers_state, without_managed_config_for_tests, loader_with_enterprise_config, from_absolute_path);外部调用 3 个(assert!, tempdir, create_dir_all)。

load_config_layers_applies_matching_remote_sandbox_config2115–2159 ↗
async fn load_config_layers_applies_matching_remote_sandbox_config() -> anyhow::Result<()>

作用:确认匹配当前机器的远程沙箱配置会应用,放宽允许的沙箱模式。

数据流:它设置基础只允许 read-only,同时 remote_sandbox_config 对任意主机允许 read-only 和 workspace-write → 加载后 allowed_sandbox_modes 包含两者,workspace-write 可设置。

调用关系:它测试云端 requirements 里的远程环境例外规则。

调用图:调用 3 个内部函数(load_config_layers_state, loader_with_enterprise_requirement, from_absolute_path);外部调用 5 个(default, assert!, assert_eq!, tempdir, create_dir_all)。

load_config_layers_fails_when_cloud_config_bundle_loader_fails2162–2192 ↗
async fn load_config_layers_fails_when_cloud_config_bundle_loader_fails() -> anyhow::Result<()>

作用:确认云端配置包加载失败时,系统会失败关闭,而不是忽略后继续。失败关闭就是出错时选择更安全的停止。

数据流:它提供一个总是返回 RequestFailed 的 CloudConfigBundleLoader → 加载层栈 → 返回 io::ErrorKind::Other,错误文本包含云端失败信息。

调用关系:它测试云端配置入口的错误策略。

调用图:调用 4 个内部函数(new, new, load_config_layers_state, from_absolute_path);外部调用 5 个(default, assert!, assert_eq!, tempdir, create_dir_all)。

project_layers_prefer_closest_cwd2195–2258 ↗
async fn project_layers_prefer_closest_cwd() -> std::io::Result<()>

作用:确认多个项目 .codex 配置同时存在时,离当前目录最近的优先。

数据流:它在项目根和子目录各写 .codex/config.toml → 标记项目可信并从子目录加载 → 层顺序先子目录后根目录,最终 foo 是 child。

调用关系:它调用 make_config_for_test 和 load_config_layers_state,测试项目层发现和覆盖顺序。

调用图:调用 3 个内部函数(load_config_layers_state, make_config_for_test, from_absolute_path);外部调用 5 个(assert_eq!, default, tempdir, create_dir_all, write)。

linked_worktree_project_layers_keep_worktree_config_but_use_root_repo_hooks2261–2360 ↗
async fn linked_worktree_project_layers_keep_worktree_config_but_use_root_repo_hooks() -> std::io::Result<()>

作用:确认 Git linked worktree 场景下,普通项目配置用工作树自己的 .codex,但 hooks 可以对应到主仓库位置。

数据流:它伪造主仓库和工作树,并在四个位置写 hook 配置 → 从工作树子目录加载 → 配置值来自 worktree,hook 命令来自 repo 对应目录。

调用关系:它结合 write_linked_worktree_pointer、write_project_hook_config 和 project_hook_command,测试复杂仓库结构下的 hooks 解析。

调用图:调用 5 个内部函数(load_config_layers_state, make_config_for_test, write_linked_worktree_pointer, write_project_hook_config, from_absolute_path);外部调用 4 个(assert_eq!, default, tempdir, create_dir_all)。

linked_worktree_project_layers_use_root_repo_hooks_without_worktree_config_toml2363–2417 ↗
async fn linked_worktree_project_layers_use_root_repo_hooks_without_worktree_config_toml() -> std::io::Result<()>

作用:确认 linked worktree 没有自己的 config.toml 时,仍能使用主仓库 hooks。

数据流:它创建工作树 .codex 但只在主仓库写 hook 配置 → 加载项目层 → hooks_config_folder 指向主仓库 .codex,命令也来自主仓库。

调用关系:它是 linked worktree hook 规则的缺省情况测试。

调用图:调用 5 个内部函数(load_config_layers_state, make_config_for_test, write_linked_worktree_pointer, write_project_hook_config, from_absolute_path);外部调用 4 个(assert_eq!, default, tempdir, create_dir_all)。

nested_project_root_markers_do_not_redirect_regular_repo_hooks2420–2495 ↗
async fn nested_project_root_markers_do_not_redirect_regular_repo_hooks() -> std::io::Result<()>

作用:确认普通仓库里使用备用项目根标记时,不会错误地把 hooks 重定向到外层 Git 仓库。

数据流:它创建 repo、project、nested 三层,并用 .hg 作为项目根标记 → 加载后项目层只对应 nested 和 project,hooks 来自各自 .codex。

调用关系:它调用 write_project_hook_config 和 make_config_for_test,测试自定义项目根标记和 hooks 路径的边界。

调用图:调用 4 个内部函数(load_config_layers_state, make_config_for_test, write_project_hook_config, from_absolute_path);外部调用 6 个(assert_eq!, default, tempdir, create_dir_all, write, vec!)。

project_hook_command2497–2509 ↗
fn project_hook_command(layer: &ConfigLayerEntry) -> Option<&str>

作用:从一个项目配置层里取出第一个 PreToolUse hook 的 command 字符串。它是测试用的小工具。

数据流:进去的是 ConfigLayerEntry → 它沿着 config.hooks.PreToolUse[0].hooks[0].command 逐级读取 → 找到就返回命令字符串,否则返回 None。

调用关系:linked worktree 和项目根标记测试用它检查最终 hook 命令来自哪个配置文件。

project_paths_resolve_relative_to_dot_codex_and_override_in_order2512–2565 ↗
async fn project_paths_resolve_relative_to_dot_codex_and_override_in_order() -> std::io::Result<()>

作用:确认项目配置里的相对路径按各自 .codex 文件夹解析,并且更近的项目层能覆盖更远的。

数据流:它在根和子目录 .codex 各写 model_instructions_file 和对应文件 → 从子目录构建配置 → base_instructions 读到 child instructions。

调用关系:它通过 ConfigBuilder 测试项目层路径解析和覆盖顺序的最终效果。

调用图:调用 1 个内部函数(make_config_for_test);外部调用 6 个(assert_eq!, default, default, tempdir, create_dir_all, write)。

cli_override_model_instructions_file_sets_base_instructions2568–2601 ↗
async fn cli_override_model_instructions_file_sets_base_instructions() -> std::io::Result<()>

作用:确认命令行指定说明文件时,最终 base_instructions 会读取该文件内容。

数据流:它写一个 instr.md,并通过 cli_overrides 设置 model_instructions_file 为该路径 → 构建配置 → base_instructions 等于文件内容。

调用关系:它测试命令行覆盖项到最终模型说明文本的路径。

调用图:外部调用 7 个(assert_eq!, default, default, tempdir, create_dir_all, write, vec!)。

inline_instructions_set_base_instructions2604–2625 ↗
async fn inline_instructions_set_base_instructions() -> std::io::Result<()>

作用:确认用户配置里直接写 instructions 文本时,会成为基础说明。

数据流:它在 config.toml 写 instructions="snapshot instructions" → 构建配置 → base_instructions 就是这段文本。

调用关系:它覆盖无需额外说明文件的内联说明配置。

调用图:调用 1 个内部函数(without_managed_config_for_tests);外部调用 4 个(assert_eq!, tempdir, create_dir_all, write)。

project_layer_is_added_when_dot_codex_exists_without_config_toml2628–2670 ↗
async fn project_layer_is_added_when_dot_codex_exists_without_config_toml() -> std::io::Result<()>

作用:确认项目里只要有 .codex 文件夹,即使没有 config.toml,也会加入一个空项目层。

数据流:它创建项目 .codex 但不写配置文件 → 标记项目可信并加载 → 得到一个空表项目层。

调用关系:它保证项目层不仅代表配置文件,也代表项目局部能力入口,比如 hooks 文件夹。

调用图:调用 4 个内部函数(load_config_layers_state, new, make_config_for_test, from_absolute_path);外部调用 7 个(Table, assert_eq!, default, tempdir, create_dir_all, write, new)。

codex_home_is_not_loaded_as_project_layer_from_home_dir2673–2712 ↗
async fn codex_home_is_not_loaded_as_project_layer_from_home_dir() -> std::io::Result<()>

作用:确认用户家目录里的 Codex home 不会被误当成项目 .codex 层加载。

数据流:它把 cwd 设为 home_dir,codex_home 设为 home_dir/.codex,并写用户配置 foo=user → 加载后没有项目层,foo 仍来自用户层。

调用关系:它防止用户配置目录被重复解释成项目配置。

调用图:调用 2 个内部函数(load_config_layers_state, from_absolute_path);外部调用 6 个(new, assert_eq!, default, tempdir, create_dir_all, write)。

codex_home_within_project_tree_is_not_double_loaded2715–2788 ↗
async fn codex_home_within_project_tree_is_not_double_loaded() -> std::io::Result<()>

作用:确认当 Codex home 本身位于项目树里的 .codex 时,不会同时作为用户层和项目层重复加载。

数据流:它让项目根 .codex 作为 codex_home,并在子目录另有 .codex/config.toml → 加载后项目层只有子目录那一层,最终 foo 来自 child。

调用关系:它测试用户配置目录和项目配置目录重叠时的去重逻辑。

调用图:调用 4 个内部函数(load_config_layers_state, new, make_config_for_test, from_absolute_path);外部调用 8 个(assert_eq!, default, format!, tempdir, create_dir_all, read_to_string, write, from_str)。

project_layers_disabled_when_untrusted_or_unknown2791–2909 ↗
async fn project_layers_disabled_when_untrusted_or_unknown() -> std::io::Result<()>

作用:确认项目不可信或信任状态未知时,项目配置层会被禁用,不会影响最终配置。

数据流:它创建项目配置 foo=child 和 profile=ignored,再分别用不可信与未知配置加载 → 项目层存在但有 disabled_reason,最终 foo 仍是用户值,unsupported key 被剔除。

调用关系:它用 make_config_for_test 准备不可信场景,验证安全边界和警告行为。

调用图:调用 3 个内部函数(load_config_layers_state, make_config_for_test, from_absolute_path);外部调用 8 个(assert!, assert_eq!, default, format!, tempdir, create_dir_all, read_to_string, write)。

project_layer_ignores_unsupported_config_keys2912–3026 ↗
async fn project_layer_ignores_unsupported_config_keys() -> std::io::Result<()>

作用:确认项目本地配置只能设置被允许的安全字段,敏感字段会被忽略并产生警告。

数据流:它在项目 config.toml 写 model、说明文件和一堆不支持的字段 → 加载可信项目 → model 和说明路径保留,provider、profile、otel 等被移除,并生成启动警告。

调用关系:它测试项目配置的白名单清洗,防止项目仓库偷偷改全局敏感设置。

调用图:调用 3 个内部函数(load_config_layers_state, make_config_for_test, from_absolute_path);外部调用 7 个(assert!, assert_eq!, default, tempdir, create_dir_all, write, vec!)。

project_trust_does_not_match_configured_alias_for_canonical_cwd3030–3087 ↗
async fn project_trust_does_not_match_configured_alias_for_canonical_cwd() -> std::io::Result<()>

作用:在 Unix 上,确认通过符号链接配置的可信别名,不会自动信任真实路径。符号链接像快捷方式,但安全判断不能随便合并。

数据流:它创建 project 和指向它的 project_alias,并只把 alias 标为可信 → 从真实 project 路径加载 → 项目层被禁用,foo 不生效。

调用关系:它测试项目信任键必须精确匹配,避免路径别名带来的信任绕过。

调用图:调用 2 个内部函数(load_config_layers_state, from_absolute_path);外部调用 10 个(default, from, assert!, assert_eq!, default, symlink, tempdir, create_dir_all, write, to_string)。

cli_override_can_update_project_local_mcp_server_when_project_is_trusted3090–3136 ↗
async fn cli_override_can_update_project_local_mcp_server_when_project_is_trusted() -> std::io::Result<()>

作用:确认可信项目里的本地 MCP server 可以被命令行覆盖项启用。MCP server 是给模型接工具/服务的配置。

数据流:它在可信项目 .codex 配置一个 disabled 的 sentry MCP server → 命令行覆盖 enabled=true → 构建后该 server 存在且 enabled 为 true。

调用关系:它测试可信项目层、MCP 配置和命令行覆盖项三者合并。

调用图:调用 1 个内部函数(make_config_for_test);外部调用 6 个(assert!, default, tempdir, create_dir_all, write, vec!)。

cli_override_for_disabled_project_local_mcp_server_returns_invalid_transport3139–3178 ↗
async fn cli_override_for_disabled_project_local_mcp_server_returns_invalid_transport() -> std::io::Result<()>

作用:确认未可信项目里的 MCP server 不会因为命令行覆盖 enabled=true 就变可用。

数据流:它写同样的项目 MCP server,但不标记项目可信 → 命令行尝试启用 → 构建失败,错误包含 invalid transport 和服务器名。

调用关系:它补充上一条测试,验证不可信项目配置不能提供 MCP 连接信息。

调用图:外部调用 6 个(assert!, default, tempdir, create_dir_all, write, vec!)。

invalid_project_config_ignored_when_untrusted_or_unknown3181–3263 ↗
async fn invalid_project_config_ignored_when_untrusted_or_unknown() -> std::io::Result<()>

作用:确认不可信或未知项目里的坏 TOML 不会导致整体配置加载失败。

数据流:它写一个语法坏掉的项目 config.toml → 分别用不可信和未知场景加载 → 项目层为空且被禁用,最终用户配置仍生效。

调用关系:它测试安全策略:不可信项目配置即使坏了,也只忽略,不阻断用户启动。

调用图:调用 3 个内部函数(load_config_layers_state, make_config_for_test, from_absolute_path);外部调用 8 个(assert!, assert_eq!, default, format!, tempdir, create_dir_all, read_to_string, write)。

project_layer_without_config_toml_is_disabled_when_untrusted_or_unknown3266–3328 ↗
async fn project_layer_without_config_toml_is_disabled_when_untrusted_or_unknown() -> std::io::Result<()>

作用:确认没有 config.toml 的项目 .codex 层,也会根据项目信任状态启用或禁用。

数据流:它创建只有 .codex 文件夹的项目 → 分别测试不可信、未知、可信 → 层都存在但前两者 disabled,可信时不 disabled。

调用关系:它覆盖空项目层的信任规则。

调用图:调用 3 个内部函数(load_config_layers_state, make_config_for_test, from_absolute_path);外部调用 6 个(assert_eq!, default, format!, tempdir, create_dir_all, write)。

cli_overrides_with_relative_paths_do_not_break_trust_check3331–3365 ↗
async fn cli_overrides_with_relative_paths_do_not_break_trust_check() -> std::io::Result<()>

作用:确认命令行相对路径覆盖项不会干扰项目信任检查。

数据流:它创建可信项目,并传入 model_instructions_file="relative.md" → 调用 load_config_layers_state → 加载应成功,不因相对路径解析影响信任判断。

调用关系:它把路径解析和项目信任两个流程放在一起做回归测试。

调用图:调用 3 个内部函数(load_config_layers_state, make_config_for_test, from_absolute_path);外部调用 5 个(default, tempdir, create_dir_all, write, vec!)。

project_root_markers_supports_alternate_markers3368–3432 ↗
async fn project_root_markers_supports_alternate_markers() -> std::io::Result<()>

作用:确认用户可以用 .hg 这类备用标记识别项目根,而不只支持 .git。

数据流:它在项目根放 .hg,并在根和子目录各写 .codex/config.toml → 用户配置声明 project_root_markers=[".hg"] → 加载后两层项目配置都生效,子目录优先。

调用关系:它测试 make_config_for_test 写入的 project_root_markers 会被项目发现逻辑使用。

调用图:调用 3 个内部函数(load_config_layers_state, make_config_for_test, from_absolute_path);外部调用 6 个(assert_eq!, default, tempdir, create_dir_all, write, vec!)。

requirements_exec_policy_tests::tokens3458–3460 ↗
fn tokens(cmd: &[&str]) -> Vec<String>

作用:把字符串切片变成 String 列表,方便测试执行策略匹配命令。命令在策略里通常按 token,也就是一个个词来比较。

数据流:进去的是 &[&str] → 它逐个转成 String → 返回 Vec<String>。

调用关系:执行策略测试用它快速构造命令 token,供 policy.check 使用。

requirements_exec_policy_tests::panic_if_called3462–3464 ↗
fn panic_if_called(_: &[String]) -> Decision

作用:这是一个测试用的兜底函数:如果规则匹配正确,它就不应该被调用;一旦被调用就立刻失败。

数据流:进去的是命令 token 列表但不会使用 → 直接 panic → 没有正常返回。

调用关系:规则匹配测试把它传给策略检查,证明结果来自显式规则而不是后备启发式判断。

调用图:外部调用 1 个(panic!)。

requirements_exec_policy_tests::config_stack_for_dot_codex_folder_with_requirements3466–3478 ↗
fn config_stack_for_dot_codex_folder_with_requirements(
        dot_codex_folder: &Path,
        requirements: ConfigRequirements,
    ) -> ConfigLayerStack

作用:构造一个最小配置层栈,里面有项目 .codex 层和给定 requirements。这样执行策略测试不用启动完整配置加载器。

数据流:进去的是 .codex 文件夹路径和 ConfigRequirements → 它创建 Project 配置层和 ConfigLayerStack → 返回可交给 load_exec_policy 的层栈。

调用关系:执行策略加载测试用它准备输入,再调用 load_exec_policy。

调用图:调用 3 个内部函数(new, new, from_absolute_path);外部调用 4 个(default, Table, default, vec!)。

requirements_exec_policy_tests::requirements_from_toml3480–3485 ↗
fn requirements_from_toml(toml_str: &str) -> ConfigRequirements

作用:把一段 requirements TOML 字符串转成 ConfigRequirements 对象。

数据流:进去的是 TOML 文本 → 先解析成 ConfigRequirementsToml,再合并到带来源的结构里,最后转换成 ConfigRequirements → 返回可执行的要求对象。

调用关系:执行策略测试用它从短字符串快速生成 rules requirements。

调用图:外部调用 3 个(try_from, default, from_str)。

requirements_exec_policy_tests::parses_single_prefix_rule_from_raw_toml3488–3512 ↗
fn parses_single_prefix_rule_from_raw_toml() -> anyhow::Result<()>

作用:确认一条前缀规则能从 TOML 正确解析出来。前缀规则就是“命令开头长这样,就做某个决定”。

数据流:它给出 prefix_rules=[rm forbidden] 的 TOML → 解析成 RequirementsExecPolicyToml → 检查结构体内容完全一致。

调用关系:它测试 requirements 执行策略 TOML 的最简单格式。

调用图:外部调用 2 个(assert_eq!, from_str)。

requirements_exec_policy_tests::parses_multiple_prefix_rules_from_raw_toml3515–3556 ↗
fn parses_multiple_prefix_rules_from_raw_toml() -> anyhow::Result<()>

作用:确认多条前缀规则、any_of 选择和说明文字都能正确解析。

数据流:它给出 rm forbidden 和 git push/commit prompt 两条规则 → 解析 TOML → 检查 token、any_of、decision、justification 都符合预期。

调用关系:它扩展单规则解析测试,覆盖更复杂的 TOML 写法。

调用图:外部调用 2 个(assert_eq!, from_str)。

requirements_exec_policy_tests::converts_rules_toml_into_internal_policy_representation3559–3583 ↗
fn converts_rules_toml_into_internal_policy_representation() -> anyhow::Result<()>

作用:确认解析出来的规则能转换成真正会拦截命令的内部策略。

数据流:它解析 rm forbidden 规则 → 调用 to_policy → 用 rm -rf /tmp 检查 → 得到 Forbidden 和匹配到 rm 前缀的记录。

调用关系:它把 TOML 解析和 codex_execpolicy 的实际匹配连接起来测试。

调用图:外部调用 2 个(assert_eq!, from_str)。

requirements_exec_policy_tests::head_any_of_expands_into_multiple_program_rules3586–3621 ↗
fn head_any_of_expands_into_multiple_program_rules() -> anyhow::Result<()>

作用:确认规则开头的 any_of 会展开成多个程序匹配,比如 git status 和 hg status 都能匹配。

数据流:它解析 any_of=[git,hg] + status 的规则 → 转成 policy → 分别检查 git status、hg status → 都返回 Prompt。

调用关系:它测试 to_policy 对 any_of 在命令头部的特殊展开逻辑。

调用图:外部调用 2 个(assert_eq!, from_str)。

requirements_exec_policy_tests::missing_decision_is_rejected3624–3639 ↗
fn missing_decision_is_rejected() -> anyhow::Result<()>

作用:确认规则缺少 decision 时会被拒绝。decision 是这条规则要禁止、提示还是别的决定。

数据流:它解析只有 pattern 没有 decision 的规则 → 调用 to_policy → 得到 MissingDecision 错误。

调用关系:它测试执行策略规则的必填字段校验。

调用图:外部调用 2 个(assert!, from_str)。

requirements_exec_policy_tests::allow_decision_is_rejected3642–3657 ↗
fn allow_decision_is_rejected() -> anyhow::Result<()>

作用:确认 requirements 里的执行策略不允许写 allow 决定。这里的要求规则只能收紧,不应该放宽。

数据流:它解析 decision="allow" 的规则 → 调用 to_policy → 得到 AllowDecisionNotAllowed 错误。

调用关系:它防止企业 requirements 被写成允许规则从而绕过其他判断。

调用图:外部调用 2 个(assert!, from_str)。

requirements_exec_policy_tests::empty_prefix_rules_is_rejected3660–3673 ↗
fn empty_prefix_rules_is_rejected() -> anyhow::Result<()>

作用:确认空的 prefix_rules 会被拒绝,避免写了规则区但实际没有任何规则。

数据流:它解析 prefix_rules=[] → 调用 to_policy → 得到 EmptyPrefixRules 错误。

调用关系:它测试执行策略 TOML 的基本有效性检查。

调用图:外部调用 2 个(assert!, from_str)。

requirements_exec_policy_tests::loads_requirements_exec_policy_without_rules_files3676–3705 ↗
async fn loads_requirements_exec_policy_without_rules_files() -> anyhow::Result<()>

作用:确认没有外部规则文件时,requirements 里的内联执行规则也能被加载并生效。

数据流:它从 TOML 生成含 rm forbidden 的 requirements → 构造配置层栈 → 调用 load_exec_policy → 检查 rm 命令被 Forbidden。

调用关系:它用 requirements_from_toml 和 config_stack_for_dot_codex_folder_with_requirements 准备输入,测试 load_exec_policy 的内联规则路径。

调用图:调用 1 个内部函数(load_exec_policy);外部调用 4 个(assert_eq!, config_stack_for_dot_codex_folder_with_requirements, requirements_from_toml, tempdir)。

requirements_exec_policy_tests::merges_requirements_exec_policy_with_file_rules3708–3759 ↗
async fn merges_requirements_exec_policy_with_file_rules() -> anyhow::Result<()>

作用:确认 requirements 里的内联执行规则会和项目 rules 文件夹里的规则合并。

数据流:它写 rules/deny.rules 禁止 rm,同时 requirements 里写 git push 需要提示 → 加载执行策略 → rm 返回 Forbidden,git push 返回 Prompt。

调用关系:它测试 load_exec_policy 同时读取文件规则和 requirements 内联规则,并把两边结果一起用于命令判断。

调用图:调用 1 个内部函数(load_exec_policy);外部调用 6 个(assert_eq!, config_stack_for_dot_codex_folder_with_requirements, requirements_from_toml, create_dir_all, write, tempdir)。

core/src/config/auth_keyring_tests.rs源码 ↗
testtest run

这个测试文件关心一个很实际的问题:用户的认证信息,比如登录令牌,到底应该放进系统的安全密钥库,还是用普通方式直接保存。这里测试的开关叫 secret_auth_storage,可以理解成“把秘密交给保险箱保管”。文件先模拟配置里明确打开这个功能,检查结果是不是选择 Secrets;再模拟明确关闭,检查是不是选择 Direct;最后模拟配置里关了,但更高层的“功能要求”又强制打开,确认强制要求会生效。辅助函数 config_toml_load_result 负责搭一个假的配置加载结果,让测试不用真的读磁盘文件。这样做的价值是防止以后改配置代码时,不小心把认证信息存储方式判断错,造成安全行为和用户预期不一致。

函数细节2
resolve_bootstrap_auth_keyring_backend_kind_uses_secret_auth_storage_feature14–61 ↗
fn resolve_bootstrap_auth_keyring_backend_kind_uses_secret_auth_storage_feature() -> std::io::Result<()>

作用:这个测试函数检查 secret_auth_storage 这个功能开关是否真的会影响认证密钥的存放方式。它用几组假配置验证:打开时走安全密钥库,关闭时走直接保存,被要求强制打开时也走安全密钥库。

数据流:进去的是测试代码临时造出来的配置对象:有时把 secret_auth_storage 设为 true,有时设为 false,有时再加上一层“必须启用”的要求。函数把这些配置包装成程序平时看到的配置加载结果,然后交给真正的判断函数 resolve_bootstrap_auth_keyring_backend_kind。出来的是一个选择结果:Secrets 表示用系统秘密存储,Direct 表示直接存储;测试用断言确认结果和预期一样。

调用关系:它是测试入口,由 Rust 测试框架在跑测试时自动调用。它自己不直接实现判断规则,而是搭好不同场景,然后调用 config_toml_load_result 准备假配置,再调用被测的 resolve_bootstrap_auth_keyring_backend_kind,看真实代码会怎么选。

调用图:调用 2 个内部函数(new, from);外部调用 3 个(from, default, assert_eq!)。

config_toml_load_result63–79 ↗
fn config_toml_load_result(
    config_toml: ConfigToml,
    feature_requirements: Option<Sourced<FeatureRequirementsToml>>,
) -> std::io::Result<ConfigTomlLoadResult>

作用:这个辅助函数用来快速做出一个“看起来像已经加载好的配置”的对象。测试需要这种对象,才能像正式程序一样调用配置判断逻辑,但又不用真的去读配置文件。

数据流:进去的是一个 ConfigToml 配置内容,以及可选的功能要求 feature_requirements。函数先把功能要求放进 ConfigRequirements,再创建 ConfigLayerStack,也就是一叠配置层的记录,最后把这些和原始配置一起装进 ConfigTomlLoadResult 返回。它不改外部状态,只生成一个测试用的配置加载结果;如果创建配置层失败,就把错误返回出去。

调用关系:它服务于上面的测试函数 resolve_bootstrap_auth_keyring_backend_kind_uses_secret_auth_storage_feature。测试函数每次要模拟一种配置场景时,就调用它把零散的配置材料打包成被测函数能接受的形状。

调用图:调用 1 个内部函数(new);外部调用 3 个(default, new, default)。

features/src/tests.rs源码 ↗
testtest time

项目里很多能力不是直接写死开关,而是通过功能开关控制:有些默认开启,有些还在开发中,有些已经移除但旧配置里可能还会出现。这个测试文件逐条确认这些规则没有被改坏。它会检查每个功能的阶段,比如稳定、开发中、已废弃、已移除;也会检查默认值、旧名字是否还能识别、移除的配置是否会被忽略。它还测试从 TOML 配置文件读功能开关时,简单布尔值和带细节的表格写法都能正确解析。最后,它确认开发中功能被打开时会生成提醒,避免用户以为这些功能已经完全可靠。简单说,这个文件不是给程序运行时用的,而是给开发者改功能开关时用的“防呆护栏”。

函数细节52
under_development_features_are_disabled_by_default18–28 ↗
fn under_development_features_are_disabled_by_default()

作用:检查所有“开发中”的功能默认都必须是关闭的。这样可以避免半成品功能在用户没主动选择时跑出来。

数据流:它读取项目里的全部功能清单 → 找出阶段是 UnderDevelopment 的项目 → 逐个确认 default_enabled 是 false;如果有一个默认开启,测试就失败并指出功能名。

调用关系:这是整套功能开关规则的总检查之一。它不创建配置,只直接扫全局功能表,用断言把“开发中不能默认开”这条规矩固定下来。

调用图:外部调用 2 个(assert_eq!, matches!)。

default_enabled_features_are_stable31–42 ↗
fn default_enabled_features_are_stable()

作用:检查所有默认开启的功能,必须是稳定功能或已移除但为了兼容保留默认值的功能。它防止实验性或开发中的东西悄悄默认上线。

数据流:它遍历全部功能说明 → 看到 default_enabled 为 true 的功能 → 检查它的阶段只能是 Stable 或 Removed;不符合就报错。

调用关系:它和 under_development_features_are_disabled_by_default 一起守住默认开关的底线,主要依赖全局功能清单和断言。

调用图:外部调用 1 个(assert!)。

use_legacy_landlock_is_deprecated_and_disabled_by_default45–48 ↗
fn use_legacy_landlock_is_deprecated_and_disabled_by_default()

作用:确认旧版 Landlock 沙箱开关已经标成“废弃”,并且默认关闭。废弃表示还能认,但不鼓励继续用。

数据流:它读取 UseLegacyLandlock 这个功能的阶段和默认值 → 比对阶段是否为 Deprecated、默认是否为 false → 输出测试通过或失败。

调用关系:这是针对一个历史功能的定点检查,避免有人无意中把它改回默认启用或改错生命周期状态。

调用图:外部调用 1 个(assert_eq!)。

use_linux_sandbox_bwrap_is_removed_and_disabled_by_default51–54 ↗
fn use_linux_sandbox_bwrap_is_removed_and_disabled_by_default()

作用:确认 bwrap Linux 沙箱开关已经是“移除”状态,并且默认关闭。这样旧功能不会再被实际启用。

数据流:它读取 UseLinuxSandboxBwrap 的阶段和默认值 → 检查阶段是 Removed、默认值是 false → 不一致就失败。

调用关系:它是移除功能兼容测试的一部分,专门锁定这个旧沙箱开关的状态。

调用图:外部调用 1 个(assert_eq!)。

undo_is_removed_and_disabled_by_default57–60 ↗
fn undo_is_removed_and_disabled_by_default()

作用:确认原来的 undo 功能对应的 GhostCommit 已移除且默认关闭。它防止旧功能被重新误开。

数据流:它查看 GhostCommit 的阶段和默认开关 → 要求阶段为 Removed、默认值为 false → 用断言给出结果。

调用关系:这是单个已移除功能的回归测试,和后面的配置忽略测试一起保证旧 undo 配置不会影响新行为。

调用图:外部调用 1 个(assert_eq!)。

image_detail_original_is_removed_and_disabled_by_default63–66 ↗
fn image_detail_original_is_removed_and_disabled_by_default()

作用:确认 image_detail_original 这个旧图片细节功能已经移除,并且默认不开。

数据流:它读取 ImageDetailOriginal 的状态 → 检查阶段是 Removed、默认值是 false → 若被改动就让测试失败。

调用关系:它为旧图片相关功能保留一条明确规则,后续还有测试确认配置里写这个键也会被忽略。

调用图:外部调用 1 个(assert_eq!)。

apply_patch_freeform_is_removed_and_disabled_by_default69–76 ↗
fn apply_patch_freeform_is_removed_and_disabled_by_default()

作用:确认 apply_patch_freeform 已移除、默认关闭,但旧配置键名仍能被识别到对应功能。这样既不启用旧功能,也能知道用户写了什么。

数据流:它读取 ApplyPatchFreeform 的阶段和默认值 → 再把字符串 apply_patch_freeform 查成内部功能枚举 → 确认都符合预期。

调用关系:它连接了功能状态检查和键名解析检查,给后面的“移除配置会被忽略”测试打基础。

调用图:外部调用 1 个(assert_eq!)。

plugin_hooks_is_removed_and_disabled_by_default79–83 ↗
fn plugin_hooks_is_removed_and_disabled_by_default()

作用:确认 plugin_hooks 旧功能已移除、默认关闭,并且旧键名还能映射到对应功能。

数据流:它查看 PluginHooks 的阶段和默认值 → 用 feature_for_key 查 plugin_hooks → 确认查到的是 PluginHooks。

调用关系:它保证旧名字仍被识别,但功能本身不会默认打开;后面还有测试检查配置里写它会被忽略。

调用图:外部调用 1 个(assert_eq!)。

external_migration_is_removed_and_disabled_by_default86–93 ↗
fn external_migration_is_removed_and_disabled_by_default()

作用:确认 external_migration 已移除、默认关闭,同时旧配置键还能被解析。这样旧配置不会让程序崩,也不会启用废功能。

数据流:它读取 ExternalMigration 的阶段、默认值 → 查字符串 external_migration → 期待得到同一个功能枚举。

调用关系:这是已移除功能兼容性检查的一项,主要保护旧配置迁移路径。

调用图:外部调用 1 个(assert_eq!)。

removed_apps_mcp_path_override_shapes_are_ignored96–114 ↗
fn removed_apps_mcp_path_override_shapes_are_ignored()

作用:确认已经移除的 apps_mcp_path_override 配置,不管写成简单 true,还是写成带 path 的表格,都会被读入但最终忽略。

数据流:它构造两段 TOML 配置文本 → 反序列化成 FeaturesToml → 查看 entries 结果 → 两种写法都应该变成空表。

调用关系:它测试配置读取层的兼容行为:旧用户配置不会导致解析失败,但也不会产生有效功能开关。

调用图:外部调用 1 个(assert_eq!)。

code_mode_only_requires_code_mode117–124 ↗
fn code_mode_only_requires_code_mode()

作用:确认打开“只能代码模式”时,会自动要求基础的“代码模式”也打开。因为前者离不开后者,就像只开二楼灯却没通一楼电是不行的。

数据流:它从默认功能状态开始 → 手动打开 CodeModeOnly → 执行依赖归一化 → 检查 CodeModeOnly 和 CodeMode 都是开启。

调用关系:它调用默认配置创建功能集合,再测试 normalize_dependencies 这类依赖补全规则。

调用图:调用 1 个内部函数(with_defaults);外部调用 1 个(assert_eq!)。

guardian_approval_is_stable_and_enabled_by_default127–132 ↗
fn guardian_approval_is_stable_and_enabled_by_default()

作用:确认 GuardianApproval 是稳定功能,并且默认开启。它记录了这个功能应当对普通用户可用。

数据流:它读取 GuardianApproval 的功能说明 → 检查阶段是 Stable → 再检查默认值为 true。

调用关系:这是一个功能发布状态的钉子测试,防止未来改动误把它降级或关掉。

调用图:外部调用 1 个(assert_eq!)。

request_permissions_is_under_development135–141 ↗
fn request_permissions_is_under_development()

作用:确认 ExecPermissionApprovals 仍属于开发中功能,并且默认关闭。它避免权限请求功能在未成熟时自动启用。

数据流:它读取 ExecPermissionApprovals 的阶段和默认值 → 要求阶段是 UnderDevelopment、默认值是 false。

调用关系:这是开发中权限能力的状态测试,和其它 UnderDevelopment 测试共同保证默认安全。

调用图:外部调用 1 个(assert_eq!)。

request_permissions_tool_is_under_development144–150 ↗
fn request_permissions_tool_is_under_development()

作用:确认 RequestPermissionsTool 这个工具还在开发中,并且默认关闭。

数据流:它读取 RequestPermissionsTool 的阶段和默认开关 → 检查阶段为 UnderDevelopment、默认关闭。

调用关系:它固定这个工具型功能的发布状态,避免开发中的工具被默认暴露给用户。

调用图:外部调用 1 个(assert_eq!)。

terminal_resize_reflow_is_removed_and_enabled_by_default153–160 ↗
fn terminal_resize_reflow_is_removed_and_enabled_by_default()

作用:确认 terminal_resize_reflow 已经标记为移除,但默认值仍是开启。这里的重点是兼容:旧行为可能已经并入主流程,所以保留默认真值。

数据流:它先把字符串 terminal_resize_reflow 映射到内部功能 → 再检查阶段是 Removed、默认值是 true。

调用关系:它为一个特殊的已移除功能写明例外规则,并由后面的 from_sources 测试确认旧配置不会改变默认结果。

调用图:外部调用 1 个(assert_eq!)。

from_sources_ignores_removed_terminal_resize_reflow_feature_key163–180 ↗
fn from_sources_ignores_removed_terminal_resize_reflow_feature_key()

作用:确认配置文件里即使写 terminal_resize_reflow = false,也不会改变当前默认功能状态。因为这个键已经移除,旧配置应被忽略。

数据流:它创建一个包含 terminal_resize_reflow=false 的 FeaturesToml → 通过 from_sources 合并配置 → 得到最终 Features → 确认整体等于默认值,且 TerminalResizeReflow 仍为 true。

调用关系:它测试 Features::from_sources 的过滤规则:读取配置时要忽略已移除键,而不是按旧配置关闭功能。

调用图:调用 2 个内部函数(from_sources, from);外部调用 5 个(from, default, assert_eq!, default, default)。

tool_suggest_is_stable_and_enabled_by_default183–186 ↗
fn tool_suggest_is_stable_and_enabled_by_default()

作用:确认 ToolSuggest 是稳定功能,并且默认开启。

数据流:它读取 ToolSuggest 的阶段和默认值 → 检查阶段为 Stable、默认值为 true。

调用关系:这是单个稳定功能的状态锁定测试,防止默认体验被意外改掉。

调用图:外部调用 1 个(assert_eq!)。

network_proxy_is_experimental_and_disabled_by_default189–199 ↗
fn network_proxy_is_experimental_and_disabled_by_default()

作用:确认 network_proxy 是实验性功能,并且默认关闭。实验性表示可以试用,但还不该自动给所有人打开。

数据流:它把 network_proxy 字符串解析为功能 → 读取阶段 → 确认阶段属于 Experimental → 再确认默认值为 false。

调用关系:它同时覆盖键名解析和实验功能默认值规则,是网络代理功能的安全护栏。

调用图:外部调用 2 个(assert!, assert_eq!)。

tool_search_is_removed_and_disabled_by_default202–206 ↗
fn tool_search_is_removed_and_disabled_by_default()

作用:确认 ToolSearch 已移除、默认关闭,并且旧键名 tool_search 还能被识别。

数据流:它读取 ToolSearch 的阶段和默认值 → 查 tool_search 字符串 → 确认结果对应 ToolSearch。

调用关系:它保证旧配置名不会变成未知项,但也不会让已移除功能重新生效。

调用图:外部调用 1 个(assert_eq!)。

secret_auth_storage_defaults_to_windows_only209–216 ↗
fn secret_auth_storage_defaults_to_windows_only()

作用:确认 SecretAuthStorage 是稳定功能,但默认是否开启取决于系统:只在 Windows 上默认开启。

数据流:它读取 SecretAuthStorage 的阶段和默认值 → 用当前编译平台是否为 Windows 来判断预期值 → 再确认 secret_auth_storage 键能解析到该功能。

调用关系:它覆盖一个和操作系统有关的默认规则,避免跨平台行为被改错。

调用图:外部调用 1 个(assert_eq!)。

browser_controls_are_stable_and_enabled_by_default219–241 ↗
fn browser_controls_are_stable_and_enabled_by_default()

作用:确认一组浏览器和电脑控制相关功能都是稳定且默认开启,并且它们的配置键名都能正确识别。

数据流:它依次检查 InAppBrowser、BrowserUse、BrowserUseExternal、ComputerUse → 对每个功能检查阶段、默认值和字符串键名映射。

调用关系:这是浏览器控制能力的一组发布状态测试,把多个紧密相关的开关放在一起检查。

调用图:外部调用 1 个(assert_eq!)。

use_linux_sandbox_bwrap_is_a_removed_feature_key244–253 ↗
fn use_linux_sandbox_bwrap_is_a_removed_feature_key()

作用:确认两个旧沙箱相关键名 use_legacy_landlock 和 use_linux_sandbox_bwrap 仍能被解析。这样旧配置可以被识别并按兼容规则处理。

数据流:它分别把两个字符串交给 feature_for_key → 检查返回的内部功能是否是对应的旧沙箱功能。

调用关系:它补充前面的状态测试,专门确认旧键名入口没有丢失。

调用图:外部调用 1 个(assert_eq!)。

image_generation_is_stable_and_enabled_by_default256–259 ↗
fn image_generation_is_stable_and_enabled_by_default()

作用:确认图片生成功能已经稳定,并且默认开启。

数据流:它读取 ImageGeneration 的阶段和默认值 → 期待 Stable 和 true。

调用关系:这是图片生成能力的发布状态检查,防止默认可用性被误改。

调用图:外部调用 1 个(assert_eq!)。

image_generation_extension_is_under_development_and_disabled_by_default262–266 ↗
fn image_generation_extension_is_under_development_and_disabled_by_default()

作用:确认图片生成扩展功能 ImageGenExt 还在开发中,默认关闭,并且 imagegenext 键能识别。

数据流:它读取 ImageGenExt 的阶段和默认值 → 查字符串 imagegenext → 确认是开发中、默认关、键名映射正确。

调用关系:它把扩展功能和主图片生成功能区分开,避免扩展能力跟着稳定主功能一起默认打开。

调用图:外部调用 1 个(assert_eq!)。

use_legacy_landlock_config_records_deprecation_notice269–288 ↗
fn use_legacy_landlock_config_records_deprecation_notice()

作用:确认用户配置里写了废弃的 use_legacy_landlock 时,系统会记录一条废弃提醒。这样用户能知道这个设置快不能用了。

数据流:它创建一张配置表,写入 use_legacy_landlock=true → 从默认 Features 开始应用这张表 → 读取 legacy_feature_usages → 检查提醒数量、别名、功能、摘要和详细说明。

调用关系:它测试 Features::apply_map 之后留下的“旧功能使用记录”,为后续向用户显示迁移提示提供保障。

调用图:调用 1 个内部函数(with_defaults);外部调用 2 个(new, assert_eq!)。

image_detail_original_is_a_removed_feature_key291–296 ↗
fn image_detail_original_is_a_removed_feature_key()

作用:确认 image_detail_original 这个旧键名仍能映射到已移除的功能。这样旧配置不会变成完全无法识别的垃圾项。

数据流:它把 image_detail_original 传给 feature_for_key → 期待得到 ImageDetailOriginal。

调用关系:它补充已移除图片功能的兼容检查,和默认关闭测试、配置忽略测试配套。

调用图:外部调用 1 个(assert_eq!)。

js_repl_features_are_removed_feature_keys299–310 ↗
fn js_repl_features_are_removed_feature_keys()

作用:确认 js_repl 和 js_repl_tools_only 两个 JavaScript REPL 相关旧功能都已移除、默认关闭,但旧键名仍能识别。

数据流:它分别检查 JsRepl 和 JsReplToolsOnly 的阶段、默认值 → 再检查 js_repl、js_repl_tools_only 两个字符串映射。

调用关系:它为一组已下线的 JavaScript 交互功能保留兼容入口,同时确保不会被启用。

调用图:外部调用 1 个(assert_eq!)。

tool_call_mcp_elicitation_is_stable_and_enabled_by_default313–316 ↗
fn tool_call_mcp_elicitation_is_stable_and_enabled_by_default()

作用:确认 ToolCallMcpElicitation 是稳定功能,并且默认开启。

数据流:它读取该功能的阶段和默认值 → 检查分别是 Stable 和 true。

调用关系:这是 MCP 工具调用相关能力的状态确认测试。

调用图:外部调用 1 个(assert_eq!)。

auth_elicitation_is_under_development319–326 ↗
fn auth_elicitation_is_under_development()

作用:确认 AuthElicitation 认证交互功能还在开发中,默认关闭,并且配置键 auth_elicitation 可识别。

数据流:它读取功能阶段和默认值 → 用 feature_for_key 查 auth_elicitation → 确认三项都符合预期。

调用关系:它保护认证交互功能不会在未完成前默认暴露,同时保证配置解析知道这个键。

调用图:外部调用 1 个(assert_eq!)。

mentions_v2_is_stable_and_enabled_by_default329–333 ↗
fn mentions_v2_is_stable_and_enabled_by_default()

作用:确认 mentions_v2 是稳定功能,并且默认开启。

数据流:它读取 MentionsV2 的阶段和默认值 → 查 mentions_v2 键名 → 期待稳定、默认开、映射正确。

调用关系:这是 mentions 第二版功能的发布状态测试。

调用图:外部调用 1 个(assert_eq!)。

remote_control_is_removed_and_disabled_by_default336–343 ↗
fn remote_control_is_removed_and_disabled_by_default()

作用:确认 remote_control 远程控制功能已经移除、默认关闭,但旧键名还能识别。

数据流:它读取 RemoteControl 的阶段和默认值 → 查 remote_control 键名 → 确认是 Removed、false、映射正确。

调用关系:它给远程控制旧功能设置边界,后面的测试会确认配置试图开启它也无效。

调用图:外部调用 1 个(assert_eq!)。

remote_control_config_is_ignored346–354 ↗
fn remote_control_config_is_ignored()

作用:确认配置里写 remote_control=true 也不会真的打开远程控制。已移除功能必须被忽略。

数据流:它创建包含 remote_control=true 的配置表 → 从默认 Features 开始应用 → 检查 RemoteControl 仍然是关闭。

调用关系:它测试 Features::apply_map 对移除功能的处理,配合 remote_control 的状态测试形成完整保护。

调用图:调用 1 个内部函数(with_defaults);外部调用 2 个(new, assert_eq!)。

workspace_dependencies_is_stable_and_enabled_by_default357–364 ↗
fn workspace_dependencies_is_stable_and_enabled_by_default()

作用:确认 workspace_dependencies 是稳定功能,并且默认开启。

数据流:它读取 WorkspaceDependencies 的阶段和默认值 → 查 workspace_dependencies 键名 → 确认稳定、默认开、映射正确。

调用关系:这是工作区依赖功能的发布状态和键名解析测试。

调用图:外部调用 1 个(assert_eq!)。

telepathy_is_legacy_alias_for_chronicle367–372 ↗
fn telepathy_is_legacy_alias_for_chronicle()

作用:确认旧名字 telepathy 现在是 Chronicle 的别名,同时 Chronicle 仍是开发中、默认关闭。

数据流:它检查 Chronicle 的阶段和默认值 → 分别查 chronicle 和 telepathy 两个键 → 都应指向 Chronicle。

调用关系:它测试“旧名字继续可用”的兼容规则,避免用户旧配置突然失效。

调用图:外部调用 1 个(assert_eq!)。

collab_is_legacy_alias_for_multi_agent375–378 ↗
fn collab_is_legacy_alias_for_multi_agent()

作用:确认 multi_agent 和旧名字 collab 都指向同一个 Collab 功能。

数据流:它把 multi_agent 和 collab 两个字符串分别解析 → 都应得到 Feature::Collab。

调用关系:它保障多智能体协作功能改名后的兼容性。

调用图:外部调用 1 个(assert_eq!)。

codex_hooks_is_legacy_alias_for_hooks381–384 ↗
fn codex_hooks_is_legacy_alias_for_hooks()

作用:确认 hooks 和旧名字 codex_hooks 都会解析到 CodexHooks 功能。

数据流:它分别查询 hooks、codex_hooks → 检查二者都返回 CodexHooks。

调用关系:它保护 hooks 功能的旧配置名,避免改名影响老用户。

调用图:外部调用 1 个(assert_eq!)。

multi_agent_is_stable_and_enabled_by_default387–390 ↗
fn multi_agent_is_stable_and_enabled_by_default()

作用:确认多智能体协作功能 Collab 已经稳定,并且默认开启。

数据流:它读取 Collab 的阶段和默认值 → 检查为 Stable 和 true。

调用关系:它和别名测试一起,说明这个功能既默认可用,也兼容旧名字。

调用图:外部调用 1 个(assert_eq!)。

enable_fanout_is_under_development393–396 ↗
fn enable_fanout_is_under_development()

作用:确认 SpawnCsv,也就是 fanout 相关能力,还在开发中并且默认关闭。

数据流:它读取 SpawnCsv 的阶段和默认值 → 要求 UnderDevelopment 和 false。

调用关系:它给 fanout 功能定下默认安全状态,后面的依赖测试会检查它打开时需要什么。

调用图:外部调用 1 个(assert_eq!)。

enable_fanout_normalization_enables_multi_agent_one_way399–411 ↗
fn enable_fanout_normalization_enables_multi_agent_one_way()

作用:确认打开 fanout 会自动打开多智能体协作,但反过来打开多智能体不会自动打开 fanout。这是单向依赖。

数据流:第一段从默认功能开始 → 打开 SpawnCsv → 归一化依赖 → 检查 SpawnCsv 和 Collab 都开;第二段从默认开始 → 只开 Collab → 归一化后确认 SpawnCsv 仍关。

调用关系:它直接测试 normalize_dependencies 的依赖补全逻辑,说明 SpawnCsv 依赖 Collab,但 Collab 不依赖 SpawnCsv。

调用图:调用 1 个内部函数(with_defaults);外部调用 1 个(assert_eq!)。

apps_require_feature_flag_and_chatgpt_auth414–421 ↗
fn apps_require_feature_flag_and_chatgpt_auth()

作用:确认 Apps 功能不光要功能开关打开,还要求用户有 ChatGPT 认证。两个条件缺一不可。

数据流:它从默认功能开始 → 先检查没有认证时 apps 不可用 → 再打开 Apps 功能 → 分别用无认证和有认证检查结果 → 只有有认证时才为 true。

调用关系:它测试 Features::apps_enabled_for_auth 的组合判断,把功能开关和登录身份联系起来。

调用图:调用 1 个内部函数(with_defaults);外部调用 1 个(assert!)。

from_sources_applies_base_profile_and_overrides424–458 ↗
fn from_sources_applies_base_profile_and_overrides()

作用:确认最终功能状态会按顺序合并:基础配置、个人配置,再加命令或运行时覆盖项。它保证多来源配置不会互相丢失。

数据流:它构造基础配置 plugins=true → 构造 profile 配置 code_mode_only=true → 构造覆盖项 web_search_request=false → 调用 from_sources 合成最终 Features → 检查 Plugins、CodeModeOnly、CodeMode 打开,ApplyPatchFreeform 仍关,WebSearchRequest 被覆盖为关。

调用关系:这是 Features::from_sources 的核心流程测试,覆盖配置合并、依赖归一化和显式覆盖三件事。

调用图:调用 1 个内部函数(from_sources);外部调用 3 个(new, default, assert_eq!)。

from_sources_ignores_removed_image_detail_original_feature_key461–477 ↗
fn from_sources_ignores_removed_image_detail_original_feature_key()

作用:确认配置里写 image_detail_original=true 不会改变最终功能状态,因为这个功能已经移除。

数据流:它创建包含 image_detail_original=true 的 FeaturesToml → 调用 from_sources → 比较结果是否完全等于默认 Features。

调用关系:它测试 from_sources 对已移除图片功能键的忽略规则。

调用图:调用 2 个内部函数(from_sources, from);外部调用 5 个(from, default, assert_eq!, default, default)。

from_sources_ignores_removed_undo_feature_key480–493 ↗
fn from_sources_ignores_removed_undo_feature_key()

作用:确认配置里写 undo=true 会被忽略,不会让旧的 undo/GhostCommit 功能复活。

数据流:它创建包含 undo=true 的 FeaturesToml → 通过 from_sources 合成最终功能 → 检查结果和默认值完全一样。

调用关系:它连接旧 undo 键和配置合并逻辑,确保已移除功能不会被配置重新打开。

调用图:调用 2 个内部函数(from_sources, from);外部调用 5 个(from, default, assert_eq!, default, default)。

from_sources_ignores_removed_js_repl_feature_keys496–512 ↗
fn from_sources_ignores_removed_js_repl_feature_keys()

作用:确认 js_repl 和 js_repl_tools_only 这两个已移除配置键都会被忽略。

数据流:它创建同时包含两个旧 JavaScript REPL 键的 FeaturesToml → 调用 from_sources → 检查最终 Features 等于默认值。

调用关系:它测试一组已移除键在配置合并时的过滤行为。

调用图:调用 2 个内部函数(from_sources, from);外部调用 5 个(from, default, assert_eq!, default, default)。

from_sources_ignores_removed_apply_patch_freeform_feature_key515–529 ↗
fn from_sources_ignores_removed_apply_patch_freeform_feature_key()

作用:确认配置里写 apply_patch_freeform=true 也不会启用这个已移除功能。

数据流:它把 apply_patch_freeform=true 放进 FeaturesToml → 调用 from_sources → 检查最终功能状态没有偏离默认值。

调用关系:它是 ApplyPatchFreeform 状态测试的配置层补充。

调用图:调用 2 个内部函数(from_sources, from);外部调用 5 个(from, default, assert_eq!, default, default)。

from_sources_ignores_removed_plugin_hooks_feature_key532–545 ↗
fn from_sources_ignores_removed_plugin_hooks_feature_key()

作用:确认配置里写 plugin_hooks=true 会被忽略,不会打开旧的插件钩子功能。

数据流:它创建包含 plugin_hooks=true 的 FeaturesToml → 通过 from_sources 合成最终状态 → 断言结果等于默认 Features。

调用关系:它测试 PluginHooks 旧键在配置合并入口处被安全丢弃。

调用图:调用 2 个内部函数(from_sources, from);外部调用 5 个(from, default, assert_eq!, default, default)。

multi_agent_v2_feature_config_deserializes_boolean_toggle548–561 ↗
fn multi_agent_v2_feature_config_deserializes_boolean_toggle()

作用:确认 multi_agent_v2 可以用最简单的 TOML 写法 multi_agent_v2 = true 来配置。

数据流:它把一段 TOML 文本解析成 FeaturesToml → 检查 entries 里有 multi_agent_v2=true → 再检查原始字段保存为 FeatureToml::Enabled(true)。

调用关系:它测试配置反序列化,也就是把文本配置读成程序结构的过程,确保简单开关写法可用。

调用图:外部调用 2 个(assert_eq!, from_str)。

multi_agent_v2_feature_config_deserializes_table564–605 ↗
fn multi_agent_v2_feature_config_deserializes_table()

作用:确认 multi_agent_v2 也可以写成带很多细节选项的 TOML 表格,而不只是 true/false。

数据流:它解析一段 multi_agent_v2 表格配置 → 检查 entries 里把 enabled=true 当作有效开关 → 再逐项确认并发数、等待时间、提示文本、工具命名空间等细节都被读进配置结构。

调用关系:它测试复杂配置格式的读入能力,保证高级配置不会在解析时丢字段。

调用图:外部调用 2 个(assert_eq!, from_str)。

multi_agent_v2_feature_config_usage_hint_enabled_does_not_enable_feature608–644 ↗
fn multi_agent_v2_feature_config_usage_hint_enabled_does_not_enable_feature()

作用:确认只设置 multi_agent_v2 的 usage_hint_enabled 这类附加选项,不等于打开功能本身。只有 enabled 才是开关。

数据流:它解析只包含 usage_hint_enabled=false 的配置 → 调用 from_sources 得到最终 Features → 检查 MultiAgentV2 仍关闭,entries 为空,同时原始配置结构保留 usage_hint_enabled 字段。

调用关系:它保护配置语义不被误解:细节设置只是细节,不能替代 enabled 开关。

调用图:调用 1 个内部函数(from_sources);外部调用 5 个(default, assert_eq!, default, default, from_str)。

materialize_resolved_enabled_writes_all_features_and_preserves_custom_config647–704 ↗
fn materialize_resolved_enabled_writes_all_features_and_preserves_custom_config()

作用:确认把“已经算好的功能开关结果”写回配置时,会写入所有功能的最终开关值,同时保留用户写的其它自定义细节。

数据流:它从默认 Features 开始并手动打开 CodeMode、MultiAgentV2、NetworkProxy → 准备一个带自定义 multi_agent_v2 和 network_proxy 细节的 FeaturesToml → 调用 materialize_resolved_enabled → 检查每个功能键都写成最终 enabled 值,并确认等待时间、代理地址等自定义字段还在 → 再用 from_sources 回放一次,确认已移除功能仍不会被打开。

调用关系:它测试配置落盘或展开前的关键步骤:既要把最终开关写完整,又不能覆盖用户的高级配置。

调用图:调用 2 个内部函数(from_sources, with_defaults);外部调用 6 个(new, default, assert_eq!, default, default, Config)。

unstable_warning_event_only_mentions_enabled_under_development_features707–730 ↗
fn unstable_warning_event_only_mentions_enabled_under_development_features()

作用:确认开发中功能警告只提到“用户配置了、并且最终确实开启了”的开发中功能,不会乱报未知项或未开启项。

数据流:它构造一张 TOML 表,包含 child_agents_md=true、personality=true 和 unknown=true → 从默认 Features 开始只打开 ChildAgentsMd → 调用 unstable_features_warning_event → 拿到警告消息 → 检查消息包含 child_agents_md 和配置路径,不包含 personality。

调用关系:它测试警告生成函数的筛选逻辑,确保提醒准确,不吓唬用户。

调用图:调用 1 个内部函数(with_defaults);外部调用 5 个(new, Boolean, assert!, unstable_features_warning_event, panic!)。

unstable_warning_event_mentions_enabled_structured_under_development_feature733–761 ↗
fn unstable_warning_event_mentions_enabled_structured_under_development_feature()

作用:确认开发中功能即使用表格形式配置,只要 enabled=true,也会出现在警告里;同时警告文字要完整、顺序明确。

数据流:它解析一段包含 multi_agent_v2 表格和 code_mode=true 的 TOML → 手动打开 MultiAgentV2 和 CodeMode → 调用 unstable_features_warning_event → 取出 Warning 消息 → 精确比对整段提示文字。

调用关系:它覆盖 structured TOML 配置的警告路径,确保 unstable_features_warning_event 能识别复杂写法并生成用户看得懂的提醒。

调用图:调用 1 个内部函数(with_defaults);外部调用 4 个(assert_eq!, unstable_features_warning_event, panic!, from_str)。

tools/src/tool_config_tests.rs源码 ↗
testtest run

这份文件不像正式功能代码,更像一张安全检查清单。项目里有好几种让工具执行命令的方式:老的 ShellCommand、新的 UnifiedExec,以及是否走 zsh fork。这里的“feature”可以理解成一个开关,打开或关闭某个实验功能。测试会先造一个假的模型,再组合不同开关,确认系统最后选出的 shell 类型、后端模式、用户可选模式都符合预期。它还特别检查了一个容易踩坑的点:即使开了 zsh fork,也只有在系统、用户 shell、路径等条件都匹配时,才真的使用 zsh fork,否则要退回直接执行。这样可以避免配置看起来开启了,但运行时选错执行通道。

函数细节7
model_with_shell_type12–53 ↗
fn model_with_shell_type(shell_type: ConfigShellToolType) -> ModelInfo

作用:这个辅助函数用来快速造一个“测试模型”对象,并把它的 shell 工具类型设置成测试想要的值。这样测试不用每次手写一大堆模型字段。

数据流:进去的是一个 shell 类型,比如 UnifiedExec;函数把它塞进一个完整的 ModelInfo 结构里,同时填上测试用的名字、可见性、截断策略、输入类型等默认值;出来的是一个可以交给配置判断函数使用的假模型。

调用关系:它被 shell_type_is_derived_from_model_and_feature_gates 使用,用来准备测试素材。内部会调用 tokens 设置上下文截断上限,也会取默认输入模态;这些只是为了把模型对象补完整,让后面的判断能正常跑。

调用图:调用 2 个内部函数(tokens, default_input_modalities);被 1 处调用(shell_type_is_derived_from_model_and_feature_gates);外部调用 3 个(default, new, new)。

shell_features55–62 ↗
fn shell_features() -> Features

作用:这个辅助函数准备一套适合 shell 测试的功能开关初始状态。它默认打开基础 shell 工具,但先关掉 zsh fork 和 UnifiedExec 相关实验开关。

数据流:它不接收外部参数;先从系统默认功能开关开始,然后打开 ShellTool,关闭 ShellZshFork、UnifiedExec、UnifiedExecZshFork;最后返回这组可继续修改的 Features。

调用关系:多个测试都会先调用它拿到一个干净、可预测的起点,然后逐步打开或关闭某个开关,观察配置结果如何变化。它让 shell_command_backend_requires_both_shell_tool_and_zsh_fork、shell_type_is_derived_from_model_and_feature_gates 和 unified_exec_feature_mode_follows_composition_dependencies 不必重复写同样的准备代码。

调用图:调用 1 个内部函数(with_defaults);被 3 处调用(shell_command_backend_requires_both_shell_tool_and_zsh_fork, shell_type_is_derived_from_model_and_feature_gates, unified_exec_feature_mode_follows_composition_dependencies)。

shell_type_is_derived_from_model_and_feature_gates65–101 ↗
fn shell_type_is_derived_from_model_and_feature_gates()

作用:这个测试确认最终使用哪种 shell 工具,不只看模型说支持什么,还要看功能开关和当前系统能力。也就是说,模型想用新工具,不代表一定能用。

数据流:它先造一个声明使用 UnifiedExec 的测试模型,再准备一组 shell 功能开关;随后一步步打开 UnifiedExec、ShellZshFork、UnifiedExecZshFork,最后关闭 ShellTool;每一步都检查 shell_type_for_model_and_features 返回的结果是不是预期值。

调用关系:测试框架运行它时,它会调用 model_with_shell_type 和 shell_features 准备输入,再用断言检查被测配置函数。它还会调用 conpty_supported 判断当前环境是否支持对应终端能力,因为在不支持的系统上必须退回 ShellCommand。

调用图:调用 2 个内部函数(model_with_shell_type, shell_features);外部调用 2 个(assert_eq!, conpty_supported)。

shell_command_backend_requires_both_shell_tool_and_zsh_fork104–122 ↗
fn shell_command_backend_requires_both_shell_tool_and_zsh_fork()

作用:这个测试确认老式 ShellCommand 后端要切到 zsh fork,必须同时满足两个开关:基础 shell 工具开着,zsh fork 也开着。少一个都不能切。

数据流:它从 shell_features 的初始开关开始,先检查默认是 Classic;然后打开 ShellZshFork,期望变成 ZshFork;最后关闭 ShellTool,确认即使 zsh fork 开着,也会回到 Classic。

调用关系:测试框架运行它时,它把不同开关组合交给 shell_command_backend_for_features。这个被测函数负责根据开关选后端,本测试负责守住“必须两个条件都满足”的规则。

调用图:调用 1 个内部函数(shell_features);外部调用 1 个(assert_eq!)。

unified_exec_feature_mode_follows_composition_dependencies125–162 ↗
fn unified_exec_feature_mode_follows_composition_dependencies()

作用:这个测试检查 UnifiedExec 的模式选择是否遵守依赖关系。简单说,新执行方式不是开一个总开关就万事大吉,和 zsh fork 组合时还要看配套开关是否也打开。

数据流:它先从默认 shell 功能状态开始,确认 UnifiedExec 是 Disabled;打开 UnifiedExec 后应变成 Direct;再打开或关闭 zsh fork 相关开关,检查结果在 Direct、Disabled、ZshFork 之间正确切换;最后关闭 ShellTool,确认整体禁用。

调用关系:测试框架运行它时,它用 shell_features 建立起点,再不断改变 Features,交给 unified_exec_feature_mode_for_features 判断。它验证的是这些开关之间的“组合规则”,防止只看单个开关导致误判。

调用图:调用 1 个内部函数(shell_features);外部调用 1 个(assert_eq!)。

request_user_input_modes_follow_default_mode_feature165–178 ↗
fn request_user_input_modes_follow_default_mode_feature()

作用:这个测试确认“请求用户输入”时,默认模式能不能出现,取决于一个专门的功能开关。开关关掉时只允许 Plan,打开时才同时允许 Default 和 Plan。

数据流:它先创建默认功能开关,然后关闭 DefaultModeRequestUserInput,检查可用模式列表只有 Plan;接着打开这个开关,再检查列表变成 Default 加 Plan。

调用关系:测试框架运行它时,它直接调用 request_user_input_available_modes。这个测试不走 shell_features,因为它关心的是用户输入模式,不是 shell 命令执行功能。

调用图:调用 1 个内部函数(with_defaults);外部调用 1 个(assert_eq!)。

unified_exec_shell_mode_uses_zsh_fork_only_when_all_inputs_match181–206 ↗
fn unified_exec_shell_mode_uses_zsh_fork_only_when_all_inputs_match()

作用:这个测试确认 UnifiedExec 真正启动会话时,只有所有条件都合适才用 zsh fork。否则要安全地退回 Direct,避免因为环境不匹配而启动失败。

数据流:它先拿当前测试程序自己的路径,当作 shell 路径和可执行文件路径;然后用 ZshFork 模式、用户 shell 是 Zsh、路径都存在这些输入调用 for_session。在 Unix 系统上期望得到 ZshFork,在非 Unix 系统上期望 Direct;最后再用 Direct 模式确认无论其他条件如何都保持 Direct。

调用关系:测试框架运行它时,它调用 UnifiedExecShellMode::for_session,让真正的会话模式选择逻辑做判断。这里还用 current_exe 获取一个真实存在的路径,用 cfg! 区分操作系统,再用断言确认不同平台下的退路是正确的。

调用图:调用 1 个内部函数(for_session);外部调用 4 个(assert!, assert_eq!, cfg!, current_exe)。

connectors/src/app_tool_policy_tests.rs源码 ↗
testtest run

可以把这里测试的对象想成门卫:每个 app 是一栋楼,每个 tool 是楼里的一个房间,配置决定谁能进、进门前要不要喊人批准。这个测试文件用很多小场景验证门卫的判断:全局默认能不能关掉危险工具,单个 app 能不能覆盖默认值,单个工具能不能再覆盖 app 规则,管理端下发的强制要求是不是优先,工具的真实名字和显示标题该怎么匹配。下面的辅助函数负责快速拼出假配置、假管理要求,再交给 AppToolPolicyEvaluator(工具策略评估器)算出 AppToolPolicy(是否启用、审批方式)。这些测试很重要,因为一旦规则顺序错了,可能会把本该禁用的工具放出来,或者把本该审批的操作自动放行。

函数细节23
evaluator_reuses_one_snapshot_across_tools25–85 ↗
fn evaluator_reuses_one_snapshot_across_tools()

作用:这个测试确认同一个评估器在连续判断多个工具时,用的是同一份配置快照。这样不会出现前一个工具按一套规则、后一个工具又按另一套规则的混乱情况。

数据流:进去的是一个用户 app 配置和一份管理端要求:calendar app 开着,但默认工具关着,events/create 单独打开;管理端还要求 events/create 自动批准。测试把这些喂给评估器,再连续询问三个工具。出来的结果分别验证:管理端审批能覆盖用户审批、默认关闭会让没单独打开的工具禁用、显示标题匹配用户配置时会走用户规则。

调用关系:这是一个独立测试入口。它会构造配置并调用 AppToolPolicyEvaluator::from_parts 生成评估器,然后用 input 形状的输入多次询问策略,最后把结果交给断言比较。

调用图:调用 1 个内部函数(from_parts);外部调用 4 个(from, default, from, assert_eq!)。

evaluator_uses_global_defaults_for_destructive_hints88–112 ↗
fn evaluator_uses_global_defaults_for_destructive_hints()

作用:这个测试确认全局默认配置能拦住被标记为“破坏性”的工具。破坏性就是可能改数据、删数据、发消息这类有风险的操作。

数据流:进去的是全局默认:app 默认启用,但破坏性工具默认不启用。工具输入带着 destructive_hint=true,也就是它自称有风险。评估后出来的策略是 enabled=false、approval=Auto,说明工具被关掉了,审批方式也不重要了。

调用关系:这是一个测试场景。它先用 defaults 拼出全局默认值,再通过求策略流程得到结果,最后用断言确认评估器真的看了破坏性提示。

调用图:调用 1 个内部函数(defaults);外部调用 2 个(new, assert_eq!)。

evaluator_defaults_missing_destructive_hint_to_true115–139 ↗
fn evaluator_defaults_missing_destructive_hint_to_true()

作用:这个测试确认如果工具没有说明自己是不是破坏性的,系统会按“可能有风险”处理。这样更保守,避免漏放危险工具。

数据流:进去的是全局默认:破坏性工具默认关闭;工具输入里 destructive_hint 是空的。评估器把缺失的破坏性提示当成 true,于是输出禁用策略。

调用关系:这是一个独立测试。它调用 defaults 做默认配置,然后用断言检查缺少提示时走的是安全优先的分支。

调用图:调用 1 个内部函数(defaults);外部调用 2 个(new, assert_eq!)。

evaluator_defaults_missing_open_world_hint_to_true142–166 ↗
fn evaluator_defaults_missing_open_world_hint_to_true()

作用:这个测试确认如果工具没有说明它会不会接触“开放世界”,系统会按会接触来处理。开放世界可以理解为可能访问外部网络、外部服务或不可完全控制的信息来源。

数据流:进去的是全局默认:开放世界工具默认关闭;工具输入里 open_world_hint 是空的。评估后输出 enabled=false,说明缺失提示没有被当成安全,而是按更谨慎的方式禁用。

调用关系:这是一个测试场景。它用 defaults 构造默认规则,再通过断言证明评估器对缺失开放世界提示采取保守默认值。

调用图:调用 1 个内部函数(defaults);外部调用 2 个(new, assert_eq!)。

app_enablement_uses_defaults_and_per_app_overrides169–192 ↗
fn app_enablement_uses_defaults_and_per_app_overrides()

作用:这个测试检查 app 本身的启用状态怎么算:先看全局默认,再看单个 app 有没有特殊设置。它保证“默认关,但 calendar 特别开”这类配置能生效。

数据流:进去的是默认所有 app 关闭,但 calendar 单独 enabled=true 的配置。测试分别询问 calendar、drive 和没有 connector_id 的情况。出来的布尔值是 true、false、false,说明单独配置只影响指定 app,其他仍按默认关闭。

调用关系:这是一个直接测试 app 启用判断的场景。它调用 defaults 生成默认配置,构造 per-app 覆盖项,然后用断言比较 app_is_enabled 的判断结果。

调用图:调用 1 个内部函数(defaults);外部调用 3 个(default, from, assert_eq!)。

managed_disable_overrides_enabled_app195–223 ↗
fn managed_disable_overrides_enabled_app()

作用:这个测试确认管理端下发的“禁用 app”比用户本地的“启用 app”更强。这样组织或管理员可以强制关掉不允许使用的连接器。

数据流:进去的是用户配置里 connector_123123 已启用,以及管理端要求同一个 app enabled=false。评估工具 events/list 后,出来的策略是 enabled=false,说明管理端禁用压过了用户启用。

调用关系:这是一个管理要求优先级测试。它调用 app_enabled_requirement 做出管理端禁用规则,然后通过策略评估流程检查最终门卫是否放行。

调用图:调用 1 个内部函数(app_enabled_requirement);外部调用 3 个(default, from, assert_eq!)。

managed_enable_does_not_override_disabled_app226–254 ↗
fn managed_enable_does_not_override_disabled_app()

作用:这个测试确认管理端说“允许启用”不会强行打开用户自己关掉的 app。也就是说管理端的允许不是替用户按开关。

数据流:进去的是用户配置里 connector_123123 enabled=false,同时管理端要求 enabled=true。评估工具后输出仍然是 enabled=false,说明用户关闭仍然有效。

调用关系:这是一个管理端与用户配置关系的测试。它调用 app_enabled_requirement 生成管理端允许规则,再用断言确认这个允许不会反向覆盖用户禁用。

调用图:调用 1 个内部函数(app_enabled_requirement);外部调用 3 个(default, from, assert_eq!)。

managed_disable_applies_without_apps_config257–275 ↗
fn managed_disable_applies_without_apps_config()

作用:这个测试确认即使没有任何用户 app 配置,管理端禁用也照样生效。没有本地配置不代表可以绕过管理员规则。

数据流:进去的是空的用户配置位置,以及一份管理端 enabled=false 的要求。评估指定连接器的工具后,出来的策略是禁用。

调用关系:这是一个边界情况测试。它调用 app_enabled_requirement 做管理端禁用规则,不依赖用户配置,最后用断言确认策略评估仍会读取管理要求。

调用图:调用 1 个内部函数(app_enabled_requirement);外部调用 1 个(assert_eq!)。

evaluator_honors_default_app_enabled_false278–302 ↗
fn evaluator_honors_default_app_enabled_false()

作用:这个测试确认全局默认的 app 禁用会被尊重。也就是说,如果默认不让 app 用,没有单独开白名单的 app 就不能用。

数据流:进去的是默认 enabled=false 的 app 配置,没有任何单独 app 覆盖。评估 calendar 的工具后,输出 enabled=false。

调用关系:这是一个默认值测试。它调用 defaults 创建默认关闭的配置,再通过断言确保评估器不会把未配置的 app 默认放开。

调用图:调用 1 个内部函数(defaults);外部调用 2 个(new, assert_eq!)。

evaluator_allows_per_app_enable_when_default_is_disabled305–332 ↗
fn evaluator_allows_per_app_enable_when_default_is_disabled()

作用:这个测试确认全局默认关闭时,单个 app 仍然可以被明确打开。它验证白名单式配置能正常工作。

数据流:进去的是默认所有 app 关闭,但 calendar 单独 enabled=true。评估 calendar 的 events/list 后,输出默认允许策略,也就是工具启用、审批走默认自动模式。

调用关系:这是默认值和单 app 覆盖的组合测试。它调用 defaults 做全局关闭,再构造 calendar 的覆盖配置,用断言确认覆盖项生效。

调用图:调用 1 个内部函数(defaults);外部调用 3 个(default, from, assert_eq!)。

evaluator_uses_managed_approval_without_apps_config335–351 ↗
fn evaluator_uses_managed_approval_without_apps_config()

作用:这个测试确认没有用户配置时,管理端仍然可以指定某个工具的审批方式。审批方式就是工具执行前要不要自动放行、提示用户、或直接批准。

数据流:进去的是没有 apps_config,但有 managed_approval=Approve 的场景。评估 calendar 的 events/list 后,输出 enabled=true、approval=Approve,说明管理端审批规则单独也能工作。

调用关系:这是一个没有用户配置的管理审批测试。它主要通过断言验证策略结果,说明评估链路不能依赖用户配置文件才读取管理端审批。

调用图:外部调用 1 个(assert_eq!)。

managed_approval_uses_raw_tool_name354–390 ↗
fn managed_approval_uses_raw_tool_name()

作用:这个测试确认管理端审批规则匹配的是工具的真实名字,而不是显示标题。显示标题只是给人看的别名,不能拿来冒充另一个工具。

数据流:进去的是管理端要求 connector_123123 的 calendar/list_events 使用 Approve。第一次输入真实工具名正好匹配,输出审批为 Approve;第二次真实工具名是 calendar/create_event,只是标题写成 calendar/list_events,输出仍是默认策略。

调用关系:这是工具名匹配规则测试。它调用 app_tool_requirements 生成指定工具的管理审批要求,然后用两个输入对比,确认评估器不被 tool_title 误导。

调用图:调用 1 个内部函数(app_tool_requirements);外部调用 1 个(assert_eq!)。

managed_approval_overrides_user_tool_approval393–434 ↗
fn managed_approval_overrides_user_tool_approval()

作用:这个测试确认管理端指定的工具审批方式优先于用户本地配置。这样管理员要求“批准”时,用户不能把它改成“提示”或别的模式来绕过。

数据流:进去的是用户配置把 calendar/list_events 的 approval_mode 设为 Prompt,同时管理端把同一工具设为 Approve。评估后输出 approval=Approve,说明管理端覆盖了用户的工具级审批设置。

调用关系:这是审批优先级测试。它调用 app_tool_requirements 准备管理端工具要求,再构造用户工具配置,最后用断言检查最终使用哪一边。

调用图:调用 1 个内部函数(app_tool_requirements);外部调用 3 个(default, from, assert_eq!)。

per_tool_enable_overrides_app_level_hints437–472 ↗
fn per_tool_enable_overrides_app_level_hints()

作用:这个测试确认单个工具如果被明确启用,可以覆盖 app 层面对危险工具、开放世界工具的默认限制。简单说,房间门上贴了单独许可,就不只看整栋楼的默认禁令。

数据流:进去的是 calendar app 启用,但 app 层把 destructive_enabled 和 open_world_enabled 都设为 false;同时 events/create 工具自己 enabled=true。工具输入又带着两个风险提示 true。输出仍是默认允许策略,说明工具级 enabled=true 更具体、优先级更高。

调用关系:这是工具级开关优先级测试。它构造用户配置并用断言确认评估结果,不依赖管理端要求。

调用图:外部调用 3 个(default, from, assert_eq!)。

default_tools_enable_overrides_app_level_hints475–521 ↗
fn default_tools_enable_overrides_app_level_hints()

作用:这个测试检查 app 的“默认工具开关”会怎样影响所有没有单独配置的工具。它验证 default_tools_enabled 比 app 层风险提示更直接。

数据流:第一段输入是 app 层风险工具都默认禁用,但 default_tools_enabled=true,于是普通工具仍输出允许。第二段把 default_tools_enabled=false,并设置默认工具审批为 Approve,评估另一个工具后输出 enabled=false、approval=Approve。

调用关系:这是少数会直接调用 policy_from_apps_config 的测试。它先用一个闭包快速生成 apps_config,再两次交给求策略辅助函数,最后把两次结果一起断言。

调用图:调用 1 个内部函数(policy_from_apps_config);外部调用 2 个(default, assert_eq!)。

evaluator_uses_default_tools_approval_mode524–555 ↗
fn evaluator_uses_default_tools_approval_mode()

作用:这个测试确认 app 可以给“没有单独配置的工具”设置默认审批方式。这样不必给每个工具都重复写审批规则。

数据流:进去的是 calendar app 启用,并把 default_tools_approval_mode 设为 Prompt,工具列表为空。评估 events/list 后,输出 enabled=true、approval=Prompt。

调用关系:这是默认审批模式测试。它构造 app 配置,调用评估流程生成策略,然后用断言确认空工具列表不会阻止默认审批生效。

调用图:外部调用 4 个(default, from, new, assert_eq!)。

evaluator_matches_tool_title_for_user_config558–598 ↗
fn evaluator_matches_tool_title_for_user_config()

作用:这个测试确认用户配置可以用工具标题来匹配工具。某些工具的真实名字可能很长或带前缀,标题才是用户配置里看到的名字。

数据流:进去的是用户配置里配置了 events/create 这个工具,真实输入工具名却是 calendar_events/create,同时 tool_title 是 events/create。评估后输出 enabled=true、approval=Approve,说明用户配置能通过标题匹配上。

调用关系:这是用户配置匹配方式测试。它和管理端匹配真实工具名的测试形成对照:用户配置允许看 tool_title,管理端要求则不这样做。

调用图:外部调用 3 个(default, from, assert_eq!)。

input600–608 ↗
fn input(tool_name: &'a str, tool_title: Option<&'a str>) -> AppToolPolicyInput<'a>

作用:这是一个小帮手,用来快速造出测试用的 AppToolPolicyInput。它省掉每个测试都重复写 connector_id 和风险提示的麻烦。

数据流:进去的是工具真实名字和可选的工具标题。函数把它们塞进 AppToolPolicyInput,并固定 connector_id 为 calendar,同时把 destructive_hint 和 open_world_hint 都设为 true。出来的是一份可以直接交给评估器的输入。

调用关系:它服务于需要连续询问工具策略的测试,特别是 evaluator_reuses_one_snapshot_across_tools。它本身不再调用别的项目函数,只是打包数据。

policy_from_apps_config610–635 ↗
fn policy_from_apps_config(
    apps_config: Option<&AppsConfigToml>,
    connector_id: Option<&str>,
    tool_name: &str,
    tool_title: Option<&str>,
    destructive_hint: Option<bool>,
    open_wo

作用:这是测试用的便捷入口:给它用户 app 配置和一个可选的管理审批,它会帮忙算出最终工具策略。它让测试可以专注写场景,不用每次手动搭整套配置栈。

数据流:进去的是可选 apps_config、连接器 id、工具名、标题、两个风险提示,以及可选 managed_approval。如果传了管理审批,它先为当前工具造一份管理端要求;然后把所有材料交给 policy_from_config_parts。出来的是 AppToolPolicy,也就是工具是否启用和用哪种审批。

调用关系:它被 default_tools_enable_overrides_app_level_hints 调用,也会把真正搭配置栈和调用评估器的工作交给 policy_from_config_parts。它是测试里的中间转接器。

调用图:调用 1 个内部函数(policy_from_config_parts);被 1 处调用(default_tools_enable_overrides_app_level_hints)。

policy_from_config_parts637–676 ↗
fn policy_from_config_parts(
    apps_config: Option<&AppsConfigToml>,
    requirements_apps_config: Option<&AppsRequirementsToml>,
    connector_id: Option<&str>,
    tool_name: &str,
    tool_title:

作用:这是测试里最核心的“搭台子”函数。它把用户配置、管理端要求和工具输入组装成接近真实运行时的配置环境,再让评估器算策略。

数据流:进去的是可选用户 apps_config、可选管理端 apps 要求、连接器 id、工具名、标题和风险提示。函数先创建 ConfigRequirementsToml,再建立 ConfigLayerStack(配置层栈,就是把不同来源的配置叠在一起看);如果有用户配置,就把 apps 配置序列化成 TOML 值,放到临时 config.toml 路径对应的用户配置层里。最后创建 AppToolPolicyEvaluator,并输出它算出的 AppToolPolicy。

调用关系:它由 policy_from_apps_config 调用,是大多数测试间接依赖的底层辅助。它把配置构造工作交给 ConfigLayerStack 和 TOML 转换函数,再把最终判断交给 AppToolPolicyEvaluator。

调用图:调用 3 个内部函数(new, new, try_from);被 1 处调用(policy_from_apps_config);外部调用 6 个(default, Table, try_from, new, default, temp_dir)。

app_enabled_requirement678–688 ↗
fn app_enabled_requirement(app_id: &str, enabled: bool) -> AppsRequirementsToml

作用:这是一个小工厂,用来造“管理端要求某个 app 启用或禁用”的测试数据。它避免每个测试都手写一大段嵌套结构。

数据流:进去的是 app_id 和 enabled 布尔值。函数把它包装成 AppsRequirementsToml,里面只设置这个 app 的 enabled 要求,不设置工具要求。出来的是可交给策略评估流程的管理端要求对象。

调用关系:它被 managed_disable_applies_without_apps_config、managed_disable_overrides_enabled_app、managed_enable_does_not_override_disabled_app 使用,专门支撑 app 级管理开关的测试。

调用图:被 3 处调用(managed_disable_applies_without_apps_config, managed_disable_overrides_enabled_app, managed_enable_does_not_override_disabled_app);外部调用 1 个(from)。

app_tool_requirements690–711 ↗
fn app_tool_requirements(
    app_id: &str,
    tool_name: &str,
    approval_mode: AppToolApproval,
) -> AppsRequirementsToml

作用:这是一个小工厂,用来造“管理端要求某个 app 的某个工具使用某种审批方式”的测试数据。它让审批优先级测试更短、更清楚。

数据流:进去的是 app_id、tool_name 和 approval_mode。函数把它们放进 AppsRequirementsToml 的 app→tools→tool 结构里,只设置该工具的 approval_mode。出来的是一份管理端工具要求。

调用关系:它被 managed_approval_overrides_user_tool_approval 和 managed_approval_uses_raw_tool_name 调用,用来准备管理端审批规则,然后交给策略评估流程验证。

调用图:被 2 处调用(managed_approval_overrides_user_tool_approval, managed_approval_uses_raw_tool_name);外部调用 1 个(from)。

defaults713–724 ↗
fn defaults(
    enabled: bool,
    destructive_enabled: bool,
    open_world_enabled: bool,
) -> AppsDefaultConfig

作用:这是一个小帮手,用来快速生成 app 的全局默认配置。它把测试里最常变的三个开关集中到一起。

数据流:进去的是 enabled、destructive_enabled、open_world_enabled 三个布尔值。函数把它们填进 AppsDefaultConfig,并把 approvals_reviewer 留空。出来的是可放进 AppsConfigToml.default 的默认配置。

调用关系:它被多个默认值相关测试调用,包括 app 启用默认值、破坏性工具默认值、开放世界工具默认值等。它不参与评估,只负责准备干净、明确的测试输入。

调用图:被 6 处调用(app_enablement_uses_defaults_and_per_app_overrides, evaluator_allows_per_app_enable_when_default_is_disabled, evaluator_defaults_missing_destructive_hint_to_true, evaluator_defaults_missing_open_world_hint_to_true, evaluator_honors_default_app_enabled_false, evaluator_uses_global_defaults_for_destructive_hints)。

core/tests/common/test_environment_tests.rs源码 ↗
testtest run

这是一组自动测试,用来检查 parse_test_environment 这个解析函数的行为。简单说,它像是在检查“出门前看地址”的规则:没写地址就默认在本地跑;明确写 local、docker、wine-exec 时要认得;如果使用 Docker,就必须知道容器名字;旧版的远程环境变量也还要兼容,不能一下子把老用法弄坏。同时,它还确认一些容易出错的情况,比如 Docker 没给容器名、环境名写成 other、旧变量是空字符串时,都会给出清楚的错误。这个文件本身不实现解析逻辑,只负责用一组例子证明解析逻辑没有变坏,是保护配置兼容性和错误提示质量的安全网。

函数细节6
defaults_to_local8–16 ↗
fn defaults_to_local()

作用:测试在什么配置都没给的时候,系统会不会默认选择本地测试环境。这样可以保证最简单的开发场景不需要额外设置也能运行。

数据流:输入是三个空值:没有指定测试环境、没有旧版远程环境、没有 Docker 容器名。函数把这些交给 parse_test_environment,然后用 assert_eq!(断言相等,用来确认结果符合预期)检查输出是不是 Ok(TestEnvironment::Local)。它不改动外部状态,只判断结果对不对。

调用关系:这个测试由 Rust 测试框架在跑测试时自动调用。它直接验证 parse_test_environment 的默认分支,并把比较工作交给 assert_eq!;如果默认行为被改坏,这个测试会第一时间失败。

调用图:外部调用 1 个(assert_eq!)。

parses_each_explicit_environment19–46 ↗
fn parses_each_explicit_environment()

作用:测试用户明确写出每一种支持的测试环境时,解析函数能不能正确识别。它覆盖 local、docker、wine-exec 三种合法写法。

数据流:输入分别是字符串 local、docker、wine-exec;其中 docker 还带了容器名 container-1。函数逐个调用 parse_test_environment,再检查输出是否分别变成本地环境、带容器名的 Docker 环境、WineExec 环境。结果只是测试通过或失败,不保存数据。

调用关系:这个测试在测试运行阶段由测试框架调用。它集中检查 parse_test_environment 的正常路径,并用 assert_eq! 判断每个合法配置是否被翻译成正确的 TestEnvironment 值。

调用图:外部调用 1 个(assert_eq!)。

treats_the_legacy_remote_value_as_a_docker_container49–60 ↗
fn treats_the_legacy_remote_value_as_a_docker_container()

作用:测试旧版的远程环境变量还能不能继续用,并且会被当成 Docker 容器名。这样老配置不会因为新规则上线而突然失效。

数据流:输入是不指定新的测试环境,但提供旧版远程环境值 legacy-container。函数调用 parse_test_environment,期望它输出 Docker 环境,并把 container_name 设成 legacy-container。它只做验证,不修改配置。

调用关系:这个测试由测试框架自动执行,用来保护向后兼容。它验证 parse_test_environment 在看到旧变量时,会走兼容逻辑,而不是简单忽略它。

调用图:外部调用 1 个(assert_eq!)。

explicit_docker_accepts_the_legacy_container_value63–82 ↗
fn explicit_docker_accepts_the_legacy_container_value()

作用:测试当用户明确选择 docker 时,旧版变量也可以提供容器名;同时也测试旧版变量不能为空。它保证兼容旧写法,但不接受含糊的空配置。

数据流:第一组输入是测试环境 docker,加上旧版容器名 legacy-container,函数期望得到 Docker 环境。第二组输入还是 docker,但旧版变量是空字符串,函数期望得到错误信息,说明旧版远程环境变量不能为空。整个过程只比较返回值。

调用关系:这个测试在测试阶段运行,专门检查 parse_test_environment 对“新环境名 + 旧容器名”组合的处理。它用 assert_eq! 分别确认成功情况和报错情况,避免兼容逻辑变得过于宽松。

调用图:外部调用 1 个(assert_eq!)。

explicit_local_ignores_stale_remote_metadata85–94 ↗
fn explicit_local_ignores_stale_remote_metadata()

作用:测试用户明确选择 local 时,旧的 Docker 或远程信息不会干扰结果。也就是说,只要说了本地跑,就真的按本地跑。

数据流:输入是明确的 local,同时还塞入旧版远程容器名和 Docker 容器名。函数调用 parse_test_environment 后,期望输出仍然是 Ok(TestEnvironment::Local)。这些额外信息被当成过期残留处理,不会影响返回值。

调用关系:这个测试由测试框架调用,用来确认 parse_test_environment 的优先级规则:明确的 local 最大。它通过 assert_eq! 验证解析函数不会被旧元数据带偏。

调用图:外部调用 1 个(assert_eq!)。

rejects_invalid_or_incomplete_configuration97–118 ↗
fn rejects_invalid_or_incomplete_configuration()

作用:测试配置不完整或写错时,解析函数会拒绝,并给出明确错误。这样用户不会在错误环境里悄悄运行测试,而是能看到哪里没配好。

数据流:第一组输入是 docker,但没有容器名,函数期望返回错误,提示必须设置 Docker 容器变量。第二组输入是未知环境 other,函数期望返回错误,提示只能是 local、docker 或 wine-exec。函数不改变任何东西,只验证错误是否准确。

调用关系:这个测试在测试运行时自动执行,是 parse_test_environment 的错误路径检查。它把异常输入交给解析函数,再用 assert_eq! 确认错误文字符合预期,保证用户遇到配置问题时能得到清楚提示。

调用图:外部调用 1 个(assert_eq!)。

core/tests/suite/deprecation_notice.rs源码 ↗
testtest run

这个测试文件只在非 Windows 系统上运行。它关心的是“废弃提醒”:也就是某些老的功能开关还被用户写在配置里时,Codex 应该发一个事件,告诉用户这个配置已经不推荐用了,以及该改成什么。每个测试都会先跳过没有网络的环境,然后启动一个假的服务器,避免真的连到外部服务。接着它用测试版 Codex 构造一份带旧配置的启动环境,等系统运行后,从事件流里等到 DeprecationNoticeEvent(废弃提醒事件)。最后它把提醒的标题和说明文字逐字比对,确保提示既出现了,也说对了。可以把它理解成给“升级路标”做质检:路标不但要竖起来,还要指向正确的新路。

函数细节3
emits_deprecation_notice_for_legacy_feature_flag16–54 ↗
async fn emits_deprecation_notice_for_legacy_feature_flag() -> anyhow::Result<()>

作用:这个测试确认旧的 use_experimental_unified_exec_tool 配置被使用时,系统会提醒用户改用新的 unified_exec 功能开关。它防止用户继续依赖老名字却没有任何提示。

数据流:测试开始时先检查网络条件,不合适就跳过;然后启动假服务器,创建一份测试配置:一边打开新的 UnifiedExec 功能,一边记录旧配置名确实被用过,并把旧字段设为 true。之后它启动测试版 Codex,等待事件流里出现废弃提醒。拿到提醒后,它检查 summary 和 details 两段文字,确认输出结果明确告诉用户旧配置已废弃、应该怎么启用新配置。最后返回成功结果。

调用关系:它是一个异步测试入口,由测试框架在跑测试时调用。它把准备环境的工作交给 start_mock_servertest_codex,把等待事件的工作交给 wait_for_event_match,再用 assert_eq! 做最终验收;如果没有网络,skip_if_no_network! 会让测试提前结束。

调用图:调用 2 个内部函数(start_mock_server, test_codex);外部调用 4 个(Ok, assert_eq!, wait_for_event_match, skip_if_no_network!)。

emits_deprecation_notice_for_web_search_feature_flag_values57–101 ↗
async fn emits_deprecation_notice_for_web_search_feature_flag_values() -> anyhow::Result<()>

作用:这个测试确认老的 [features].web_search_request 配置不管写成 true 还是 false,都会收到废弃提醒。这样可以保证所有旧写法都会被温和地引导到新的 web_search 配置方式。

数据流:测试先检查网络条件。然后分别用 true 和 false 跑两轮:每轮都启动假服务器,构造一份把 web_search_request 写进功能开关表的配置,再启动测试版 Codex。它从事件流里专门等待 summary 包含 [features].web_search_request 的废弃提醒。拿到后,它检查提醒标题是否说明“网页搜索默认开启,所以这个开关废弃了”,并检查详情是否告诉用户可以在 config.toml 里把 web_search 设成 live、cached 或 disabled。

调用关系:它由测试框架执行,用循环覆盖旧配置的两个取值。环境搭建仍然交给 start_mock_servertest_codex;事件筛选交给 wait_for_event_match;文字是否正确由 assert_eq! 判断。它验证的是配置系统发出的提醒能不能被 Codex 的事件通道正确送出来。

调用图:调用 2 个内部函数(start_mock_server, test_codex);外部调用 4 个(Ok, assert_eq!, wait_for_event_match, skip_if_no_network!)。

emits_deprecation_notice_for_use_legacy_landlock104–143 ↗
async fn emits_deprecation_notice_for_use_legacy_landlock() -> anyhow::Result<()>

作用:这个测试确认旧的 use_legacy_landlock 配置被打开时,系统会提醒用户这个 Linux 沙箱旧行为很快会被移除。它帮助避免用户长期依赖即将消失的安全隔离方式。

数据流:测试先检查网络,不满足就跳过;然后启动假服务器,创建测试配置,把 use_legacy_landlock 这个旧功能开关设为 true。接着它启动测试版 Codex,并等待事件流中出现 summary 包含 [features].use_legacy_landlock 的废弃提醒。最后它核对提醒标题是否说明该设置即将移除,详情是否建议用户删除这个设置,以停止选择旧版 Linux 沙箱行为。

调用关系:它是测试框架运行的异步测试。它依赖 start_mock_server 提供假的后端环境,依赖 test_codex 构造并启动受测对象,依赖 wait_for_event_match 从运行中的事件里抓到目标提醒,最后用 assert_eq! 确认提醒内容没有写错。

调用图:调用 2 个内部函数(start_mock_server, test_codex);外部调用 4 个(Ok, assert_eq!, wait_for_event_match, skip_if_no_network!)。

core/tests/suite/personality_migration.rs源码 ↗
testtest

这个测试文件像一套“搬家验收清单”。项目里有个迁移功能:如果用户以前用过会话,但还没有显式设置 personality(人格风格),系统会给配置文件补上默认的 Pragmatic;如果已经迁移过,或用户自己已经设置过,就不能乱动。测试会先在临时目录里假装建一个 codex home,写入假的会话记录、归档会话、配置文件或迁移标记文件。然后调用 maybe_migrate_personality,看它返回的状态、是否生成标记文件、是否写出 config.toml、是否保留原有配置。这里的 JSONL 会话文件可以理解成“聊天流水账”,只有真正出现用户消息,才算用户有会话;只有元数据不算。这样能保证迁移既不会漏掉老用户,也不会打扰没必要迁移的人。

函数细节18
read_config_toml24–27 ↗
async fn read_config_toml(codex_home: &Path) -> io::Result<ConfigToml>

作用:读取临时目录里的 config.toml,并把它变成程序能检查的配置对象。测试用它来确认迁移后磁盘上的配置到底写成了什么。

数据流:输入是一个 codex home 目录路径;它拼出 config.toml 的位置,异步读出文本,再用 TOML 解析器把文本转成 ConfigToml。成功时返回配置对象;如果文件读不到或内容格式不对,就返回输入输出错误。

调用关系:它通常在调用 maybe_migrate_personality 之后使用,像验收人员一样检查迁移结果。多个测试用它确认 personality 是否被写入,同时也确认已有字段比如 model 没被覆盖。

调用图:被 5 处调用(applied_migration_is_idempotent_on_second_run, no_marker_archived_sessions_sets_personality, no_marker_profile_personality_does_not_skip_migration, no_marker_sessions_preserves_existing_config_fields, no_marker_sessions_sets_personality);外部调用 3 个(join, read_to_string, from_str)。

write_session_with_user_event29–37 ↗
async fn write_session_with_user_event(codex_home: &Path) -> io::Result<()>

作用:在临时目录里造出一条“普通会话里确实有用户发言”的假记录。测试用它模拟老用户已经使用过产品的情况。

数据流:输入是 codex home 路径;它生成一个新的线程编号,把目录定位到 sessions/2025/01/01 下面,然后把具体写文件的工作交给 write_rollout_with_user_event。结果是在磁盘上出现一份带用户消息的 rollout JSONL 文件。

调用关系:很多迁移测试会先调用它布置现场,再调用 maybe_migrate_personality。它自己不关心迁移规则,只负责搭好“有会话”的证据。

调用图:调用 2 个内部函数(write_rollout_with_user_event, new);被 5 处调用(applied_migration_is_idempotent_on_second_run, no_marker_explicit_global_personality_skips_migration, no_marker_profile_personality_does_not_skip_migration, no_marker_sessions_preserves_existing_config_fields, no_marker_sessions_sets_personality);外部调用 1 个(join)。

write_archived_session_with_user_event39–43 ↗
async fn write_archived_session_with_user_event(codex_home: &Path) -> io::Result<()>

作用:在归档会话目录里造出一条带用户发言的假记录。它用来确认迁移不仅看当前会话,也看已经归档的历史会话。

数据流:输入是 codex home 路径;它生成线程编号,找到 archived sessions 目录,再调用 write_rollout_with_user_event 写入带用户消息的 JSONL 文件。结果是临时目录里有一份可被迁移逻辑识别的归档会话。

调用关系:它只服务于归档会话相关测试。测试先用它放入归档历史,再让 maybe_migrate_personality 判断用户是否应该被迁移。

调用图:调用 2 个内部函数(write_rollout_with_user_event, new);被 1 处调用(no_marker_archived_sessions_sets_personality);外部调用 1 个(join)。

write_session_with_meta_only45–53 ↗
async fn write_session_with_meta_only(codex_home: &Path) -> io::Result<()>

作用:造出一份只有会话元信息、没有用户发言的假会话文件。测试用它确认“空壳会话”不会被当成真正使用过。

数据流:输入是 codex home 路径;它生成线程编号,定位到 sessions 日期目录,然后调用 write_rollout_with_meta_only 只写一行会话说明。输出是在磁盘上出现一个没有用户消息的 rollout 文件。

调用关系:它被 no_marker_meta_only_rollout_is_treated_as_no_sessions 使用,用来给 maybe_migrate_personality 设置一个容易误判的场景:有文件,但没有真正聊天内容。

调用图:调用 2 个内部函数(write_rollout_with_meta_only, new);被 1 处调用(no_marker_meta_only_rollout_is_treated_as_no_sessions);外部调用 1 个(join)。

write_rollout_with_user_event55–103 ↗
async fn write_rollout_with_user_event(dir: &Path, thread_id: ThreadId) -> io::Result<()>

作用:真正负责写一份带用户消息的会话流水文件。它把“会话开头信息”和“用户说 hello”两行数据写成 JSONL,也就是一行一个 JSON 的日志格式。

数据流:输入是目标目录和线程编号;它先创建目录,再创建 rollout-时间戳-线程编号.jsonl 文件,组装会话元数据和用户消息事件,把它们分别转成 JSON 字符串并写入文件。完成后,磁盘上就有一份迁移逻辑能扫描到的真实会话样本。

调用关系:write_session_with_user_event 和 write_archived_session_with_user_event 都把最终写文件的活交给它。它是测试夹具的核心零件,专门生产“有用户活动”的证据。

调用图:被 2 处调用(write_archived_session_with_user_event, write_session_with_user_event);外部调用 11 个(default, join, new, format!, UserMessage, EventMsg, SessionMeta, to_string, from, create (+1 more))。

write_rollout_with_meta_only105–140 ↗
async fn write_rollout_with_meta_only(dir: &Path, thread_id: ThreadId) -> io::Result<()>

作用:真正负责写一份只有会话元数据的流水文件。它用来制造“看起来像会话文件,但没有用户实际发言”的边界情况。

数据流:输入是目标目录和线程编号;它创建目录和 rollout 文件,只组装 SessionMeta 这一类元信息,转成 JSON 后写入一行。结果是文件存在,但里面没有 UserMessage 之类的用户内容。

调用关系:write_session_with_meta_only 调用它来布置测试现场。随后 maybe_migrate_personality 会扫描这个文件,测试则确认它不应该把这种文件算作已有会话。

调用图:被 1 处调用(write_session_with_meta_only);外部调用 7 个(join, format!, SessionMeta, to_string, from, create, create_dir_all)。

parse_config_toml142–144 ↗
fn parse_config_toml(contents: &str) -> io::Result<ConfigToml>

作用:把测试里手写的 TOML 配置片段解析成 ConfigToml。这样测试不用真的先写配置文件,也能把“当前配置状态”传给迁移函数。

数据流:输入是一段 TOML 文本;它调用 TOML 解析器生成 ConfigToml,如果文本不合法,就包装成标准的输入输出错误。输出是可直接交给 maybe_migrate_personality 的配置对象。

调用关系:涉及已有 personality、profile 等配置的测试会用它准备输入。它相当于把测试字符串翻译成迁移函数能理解的配置。

调用图:被 4 处调用(marker_short_circuits_migration_with_legacy_profile, missing_legacy_profile_does_not_block_migration, no_marker_explicit_global_personality_skips_migration, no_marker_profile_personality_does_not_skip_migration);外部调用 1 个(from_str)。

migration_marker_exists_no_sessions_no_change147–161 ↗
async fn migration_marker_exists_no_sessions_no_change() -> io::Result<()>

作用:测试如果迁移标记文件已经存在,就算没有会话,也应该直接跳过,不能再改配置。标记文件可以理解成“这件事已经办过”的便签。

数据流:它创建临时目录,先写入迁移标记文件,再用默认配置调用 maybe_migrate_personality。之后检查返回状态是 SkippedMarker,并确认没有生成 config.toml。

调用关系:这是对 maybe_migrate_personality 第一层保护的测试:看到标记就短路返回。它不需要辅助写会话函数,因为重点是标记优先级。

调用图:调用 1 个内部函数(maybe_migrate_personality);外部调用 4 个(new, assert_eq!, default, write)。

no_marker_no_sessions_no_change164–180 ↗
async fn no_marker_no_sessions_no_change() -> io::Result<()>

作用:测试没有迁移标记、也没有任何会话时,不应该凭空写 personality。系统只应留下标记,表示已经检查过。

数据流:它创建空临时目录,用默认配置调用 maybe_migrate_personality。结果应是 SkippedNoSessions;迁移标记文件会被创建,但 config.toml 不会出现。

调用关系:这个测试覆盖“新用户或没用过会话的用户”场景。它直接调用迁移函数,确认没有历史证据时不会乱写配置。

调用图:调用 1 个内部函数(maybe_migrate_personality);外部调用 3 个(new, assert_eq!, default)。

no_marker_sessions_sets_personality183–199 ↗
async fn no_marker_sessions_sets_personality() -> io::Result<()>

作用:测试用户有真实会话、没有迁移标记、也没有显式 personality 时,迁移会把 personality 设置为 Pragmatic。

数据流:它先创建临时目录,再用 write_session_with_user_event 写入带用户消息的会话,然后调用 maybe_migrate_personality。之后检查状态是 Applied、标记文件存在,并读取 config.toml 确认 personality 已写成 Pragmatic。

调用关系:这是迁移功能最核心的成功路径测试。它依赖写会话辅助函数布置历史记录,依赖 read_config_toml 验证最终落盘内容。

调用图:调用 3 个内部函数(maybe_migrate_personality, read_config_toml, write_session_with_user_event);外部调用 3 个(new, assert_eq!, default)。

no_marker_sessions_preserves_existing_config_fields202–215 ↗
async fn no_marker_sessions_preserves_existing_config_fields() -> io::Result<()>

作用:测试迁移新增 personality 时,不会把用户原来已有的配置项弄丢。比如已有 model 字段,迁移后还必须在。

数据流:它先写入一条有用户消息的会话,再手动写一个包含 model 的 config.toml,并读成 ConfigToml 传给 maybe_migrate_personality。迁移后重新读取配置,确认 model 仍是原值,同时 personality 被补成 Pragmatic。

调用关系:这个测试把 maybe_migrate_personality 放在“已有配置文件”的场景下检查。read_config_toml 在迁移前后各承担不同角色:先提供输入配置,再验证写回结果。

调用图:调用 3 个内部函数(maybe_migrate_personality, read_config_toml, write_session_with_user_event);外部调用 3 个(new, assert_eq!, write)。

no_marker_meta_only_rollout_is_treated_as_no_sessions218–235 ↗
async fn no_marker_meta_only_rollout_is_treated_as_no_sessions() -> io::Result<()>

作用:测试只有会话元数据、不含用户消息的 rollout 文件,不应该触发人格迁移。换句话说,有空档案不等于真的聊过天。

数据流:它创建临时目录,用 write_session_with_meta_only 写入只有元信息的会话文件,然后调用 maybe_migrate_personality。结果应是 SkippedNoSessions;标记文件会出现,但 config.toml 不会被写出。

调用关系:这个测试防止迁移逻辑只看文件存在就误判。它通过辅助函数制造边界数据,再检查 maybe_migrate_personality 是否真正识别用户消息。

调用图:调用 2 个内部函数(maybe_migrate_personality, write_session_with_meta_only);外部调用 3 个(new, assert_eq!, default)。

no_marker_explicit_global_personality_skips_migration238–258 ↗
async fn no_marker_explicit_global_personality_skips_migration() -> io::Result<()>

作用:测试如果用户已经在全局配置里明确写了 personality,迁移就必须尊重用户选择,不能覆盖。

数据流:它先写入带用户消息的会话,再用 parse_config_toml 解析出 personality = friendly 的配置,然后调用 maybe_migrate_personality。结果应是 SkippedExplicitPersonality;标记文件存在,但测试确认不会写新的 config.toml 去覆盖设置。

调用关系:这个测试验证迁移函数对“用户主动选择”的尊重。它结合会话辅助函数和配置解析函数,制造一个既有历史会话又有明确配置的场景。

调用图:调用 3 个内部函数(maybe_migrate_personality, parse_config_toml, write_session_with_user_event);外部调用 2 个(new, assert_eq!)。

no_marker_profile_personality_does_not_skip_migration261–287 ↗
async fn no_marker_profile_personality_does_not_skip_migration() -> io::Result<()>

作用:测试只有某个 profile(配置档案)里有 personality 时,不等于全局 personality 已设置,迁移仍然应该补全全局默认值。

数据流:它写入带用户消息的会话,再解析一段带 profile 和 profiles.work.personality 的配置,调用 maybe_migrate_personality。之后确认状态是 Applied、config.toml 存在,并读取文件确认全局 personality 被写成 Pragmatic。

调用关系:这个测试区分“全局配置”和“某个档案里的配置”。它防止 maybe_migrate_personality 误把 profile 里的 personality 当成全局设置而跳过迁移。

调用图:调用 4 个内部函数(maybe_migrate_personality, parse_config_toml, read_config_toml, write_session_with_user_event);外部调用 2 个(new, assert_eq!)。

marker_short_circuits_migration_with_legacy_profile290–299 ↗
async fn marker_short_circuits_migration_with_legacy_profile() -> io::Result<()>

作用:测试只要迁移标记已经存在,即使配置里引用了一个不存在的旧 profile,也应该直接跳过,不被这个旧问题绊住。

数据流:它创建临时目录,写入迁移标记文件,再解析 profile = missing 的配置,调用 maybe_migrate_personality。输出状态应是 SkippedMarker。

调用关系:这个测试强调标记文件的短路作用。parse_config_toml 提供一个可能出问题的旧配置,但 maybe_migrate_personality 应该先看到标记并立即结束。

调用图:调用 2 个内部函数(maybe_migrate_personality, parse_config_toml);外部调用 3 个(new, assert_eq!, write)。

missing_legacy_profile_does_not_block_migration302–314 ↗
async fn missing_legacy_profile_does_not_block_migration() -> io::Result<()>

作用:测试配置里引用了不存在的旧 profile 时,迁移检查本身不能崩掉。没有会话时仍应按“无会话”处理。

数据流:它创建临时目录,解析 profile = missing 的配置,然后调用 maybe_migrate_personality。结果应是 SkippedNoSessions,并且迁移标记文件被写入。

调用关系:这个测试覆盖老配置兼容性。它让 maybe_migrate_personality 面对一个不完整的历史配置,确认迁移流程不会因为旧 profile 缺失而卡死。

调用图:调用 2 个内部函数(maybe_migrate_personality, parse_config_toml);外部调用 2 个(new, assert_eq!)。

applied_migration_is_idempotent_on_second_run317–331 ↗
async fn applied_migration_is_idempotent_on_second_run() -> io::Result<()>

作用:测试迁移做过一次后,再跑第二次不会重复改配置。幂等的意思就是“做一遍和做两遍,最终效果一样”。

数据流:它先写入带用户消息的会话,然后连续两次调用 maybe_migrate_personality。第一次应返回 Applied,第二次因为标记文件已存在返回 SkippedMarker;最后读取配置,确认 personality 仍是 Pragmatic。

调用关系:这个测试把迁移函数当作可能多次启动时会反复运行的任务来检查。write_session_with_user_event 布置触发条件,read_config_toml 验证重复运行没有造成额外变化。

调用图:调用 3 个内部函数(maybe_migrate_personality, read_config_toml, write_session_with_user_event);外部调用 3 个(new, assert_eq!, default)。

no_marker_archived_sessions_sets_personality334–350 ↗
async fn no_marker_archived_sessions_sets_personality() -> io::Result<()>

作用:测试即使会话已经被放进归档目录,只要里面有用户消息,也应该触发 personality 迁移。

数据流:它创建临时目录,用 write_archived_session_with_user_event 写入归档会话,再调用 maybe_migrate_personality。结果应是 Applied,标记文件存在,并且 config.toml 里的 personality 是 Pragmatic。

调用关系:这个测试补上归档路径这条分支。它确认 maybe_migrate_personality 不只扫描当前 sessions,也会把 archived sessions 里的真实历史使用记录算进去。

调用图:调用 3 个内部函数(maybe_migrate_personality, read_config_toml, write_archived_session_with_user_event);外部调用 3 个(new, assert_eq!, default)。

core/tests/suite/unstable_features_warning.rs源码 ↗
testtest run

这个测试文件像一名质检员,盯着“未稳定功能提醒”这条安全提示是否正常工作。它先造一个临时的用户目录,再加载一份测试用配置,然后把 child_agents_md 这个还在开发中的功能打开。第一个测试启动一段新的对话,确认系统会发出 WarningEvent,也就是“警告事件”,并且警告文字里说清楚是哪个功能、为什么提醒、以及怎样关闭提醒。第二个测试几乎做同样的事,但额外把 suppress_unstable_features_warning 设为 true,意思是“我知道风险,不要再提示”。它用很短的等待时间确认没有警告出现。这样可以保证系统既不会漏掉重要提醒,也不会在用户明确关闭后继续打扰。

函数细节2
emits_warning_when_unstable_features_enabled_via_config19–61 ↗
async fn emits_warning_when_unstable_features_enabled_via_config()

作用:这个测试确认:当用户通过配置文件打开还不稳定的功能时,系统会主动发出警告。它的价值是防止实验性功能被静默启用,用户却完全不知道风险。

数据流:进去的是一个新建的临时目录和一份默认测试配置;测试把 child_agents_md 功能写进用户配置里,再用测试用登录信息和模型提供器启动一条新对话。之后它等待对话里出现 Warning 警告事件,并检查警告文字是否包含功能名、未稳定功能提示,以及关闭提示的方法。出来的结果不是业务数据,而是测试断言:只要警告没出现或内容不对,测试就失败。

调用关系:这个函数由 Rust 的异步测试运行器在测试阶段调用。它借助 load_default_config_for_test 准备配置,用 from_absolute_path 指向用户配置文件,用 from_api_key 构造假的登录凭据,再通过 thread_manager_with_models_provider 和 auth_manager_from_auth 启动测试对话,最后把验证工作交给 wait_for_event 等到警告事件出现。

调用图:调用 4 个内部函数(auth_manager_from_auth, thread_manager_with_models_provider, from_api_key, from_absolute_path);外部调用 6 个(new, assert!, load_default_config_for_test, wait_for_event, panic!, toml!)。

suppresses_warning_when_configured64–106 ↗
async fn suppresses_warning_when_configured()

作用:这个测试确认:如果用户已经明确设置“不显示未稳定功能警告”,系统就不会再发警告。它保证关闭提醒的开关真的有效,不会让用户被重复打扰。

数据流:进去的是一个临时目录和默认测试配置;测试同样打开 child_agents_md 功能,但同时把 suppress_unstable_features_warning 设为 true。接着它启动一条新对话,并只等待很短一段时间看有没有 Warning 警告事件。出来的结果是一个判断:等待超时才算正确,因为这表示没有警告被发出来;如果等到了警告,测试就失败。

调用关系:这个函数同样由异步测试运行器执行,流程和前一个测试相似:先准备配置和假的认证信息,再启动对话。不同的是,它把等待警告这一步包在 timeout 里,也就是“最多等一小会儿”,用来证明系统没有把警告交给 wait_for_event。

调用图:调用 4 个内部函数(auth_manager_from_auth, thread_manager_with_models_provider, from_api_key, from_absolute_path);外部调用 7 个(from_millis, new, assert!, load_default_config_for_test, wait_for_event, timeout, toml!)。

云配置与主目录环境提供者

本组验证外部配置源和对环境敏感的提供者,从已签名 cloud-config 的缓存和服务刷新行为,到主目录指令和云任务过滤。

cloud-config/src/cache_tests.rs源码 ↗
testtest run

云端配置缓存的作用,是把从云端拿到的配置暂时存到本地,下次不用马上重新请求。但这类缓存很敏感:不能把 A 用户的配置给 B 用户用,也不能接受被人偷偷改过的文件。这个测试文件就围绕这些风险来验证。它先准备一份假的配置包,再把它包装成带签名的缓存文件。签名可以理解成“封条”,内容被改过,封条就对不上。测试会创建临时目录,避免污染真实用户文件;然后分别检查保存后能否读回、缺少登录身份时是否直接拒绝、文件不存在或格式坏掉时是否报对错、签名不对是否拒绝、用户身份不一致是否拒绝、缓存过期或版本不支持是否拒绝。它不实现缓存本身,而是用各种边界情况逼缓存代码证明自己不会乱用旧数据或脏数据。

函数细节12
test_bundle11–28 ↗
fn test_bundle() -> CloudConfigBundle

作用:造出一份固定的假云端配置包,供后面的测试反复使用。这样每个测试不用重新手写一堆配置内容,也能保证比较结果稳定。

数据流:进去没有外部输入 → 它创建一个包含配置片段和要求片段的 CloudConfigBundle,其中配置里写了模型名,要求里写了允许的审批策略 → 出来一份可复制、可签名、可保存的测试配置包。

调用关系:它是测试数据的源头。save_writes_signed_payload_and_loads_for_matching_identity 直接用它测试保存和读取;valid_signed_payload 也会调用它,把这份配置塞进一个带时间和身份信息的缓存载荷里。

调用图:被 2 处调用(save_writes_signed_payload_and_loads_for_matching_identity, valid_signed_payload);外部调用 1 个(vec!)。

signed_cache_file30–38 ↗
fn signed_cache_file(
    signed_payload: CloudConfigBundleCacheSignedPayload,
) -> CloudConfigBundleCacheFile

作用:把一个缓存载荷变成“带签名的缓存文件”。签名就像防拆封条,用来证明缓存内容没有被改过。

数据流:进去一个 CloudConfigBundleCacheSignedPayload,也就是要被保存的缓存正文 → 它先把正文转成用于签名的字节,再给这些字节生成签名 → 出来一个 CloudConfigBundleCacheFile,里面同时有正文和对应签名。

调用关系:多个异常场景测试都会用它先造出一份看似正规的缓存文件,然后再改内容、改时间、改版本或改身份,观察加载逻辑会不会正确拒绝。它依赖底层的 cache_payload_bytes 和 sign_cache_payload 做真正的编码和签名工作。

调用图:被 4 处调用(load_rejects_cache_for_incomplete_or_different_identity, load_rejects_expired_cache, load_rejects_tampered_payload, load_rejects_unsupported_cache_version)。

valid_signed_payload40–50 ↗
fn valid_signed_payload() -> CloudConfigBundleCacheSignedPayload

作用:造出一份默认“合法”的缓存正文,里面有当前时间、半小时后过期、固定用户身份和测试配置包。后面的测试会在这份合法样板上做小改动。

数据流:进去没有参数 → 它读取当前时间,把过期时间设为当前时间加 30 分钟,填入固定的用户 ID、账号 ID、缓存版本和 test_bundle 生成的配置包 → 出来一个完整、默认可通过检查的缓存载荷。

调用关系:它是异常测试的基础模板。load_rejects_tampered_payload、load_rejects_cache_for_incomplete_or_different_identity、load_rejects_expired_cache 和 load_rejects_unsupported_cache_version 都先拿它生成正常载荷,再故意改坏某一项。

调用图:调用 1 个内部函数(test_bundle);被 4 处调用(load_rejects_cache_for_incomplete_or_different_identity, load_rejects_expired_cache, load_rejects_tampered_payload, load_rejects_unsupported_cache_version);外部调用 2 个(minutes, now)。

write_cache_file52–58 ↗
fn write_cache_file(cache: &CloudConfigBundleCache, cache_file: &CloudConfigBundleCacheFile)

作用:把测试里造好的缓存文件写到缓存对象指定的位置。它让测试能模拟“磁盘上已经有一个缓存文件”的情况。

数据流:进去一个缓存对象和一个缓存文件结构 → 它询问缓存对象的文件路径,把缓存文件转成漂亮格式的 JSON 字节,再写入磁盘 → 出来没有返回有用数据,但磁盘上会多出或覆盖一个缓存文件。

调用关系:它是测试和文件系统之间的小帮手。几个加载失败测试都会先用 signed_cache_file 造文件,再交给 write_cache_file 写到临时目录,最后调用缓存加载逻辑验证结果。

调用图:调用 1 个内部函数(path);被 4 处调用(load_rejects_cache_for_incomplete_or_different_identity, load_rejects_expired_cache, load_rejects_tampered_payload, load_rejects_unsupported_cache_version);外部调用 2 个(to_vec_pretty, write)。

create_test_cache60–62 ↗
fn create_test_cache(codex_home: &Path) -> CloudConfigBundleCache

作用:在临时目录里创建一个测试用的 CloudConfigBundleCache。这样测试不会碰到用户真实的缓存文件。

数据流:进去一个临时的 codex_home 路径 → 它把这个路径解析成项目需要的绝对路径格式,再调用 CloudConfigBundleCache::new 创建缓存对象 → 出来一个指向临时位置的缓存实例。

调用关系:几乎每个测试一开始都会调用它。它把 tempdir 创建出的临时文件夹接到真实缓存代码上,让测试能像正式环境一样保存和读取,但所有文件都只留在测试沙盒里。

调用图:调用 2 个内部函数(new, resolve_path_against_base);被 7 处调用(load_rejects_cache_for_incomplete_or_different_identity, load_rejects_expired_cache, load_rejects_missing_request_identity_before_reading_cache_file, load_rejects_tampered_payload, load_rejects_unsupported_cache_version, load_reports_missing_and_malformed_cache_files, save_writes_signed_payload_and_loads_for_matching_identity)。

save_writes_signed_payload_and_loads_for_matching_identity65–103 ↗
async fn save_writes_signed_payload_and_loads_for_matching_identity()

作用:测试最正常的流程:用某个用户身份保存缓存后,同一个用户和账号能成功读回来,并且文件内容确实带了正确签名和有效期。

数据流:进去没有参数,测试自己创建临时目录、缓存对象和测试配置包 → 它调用缓存的 save 写入文件,再从磁盘读出 JSON,检查过期时间合理、签名后的文件内容等于预期,最后用相同用户 ID 和账号 ID 调用 load → 出来是测试断言通过;如果保存、签名、时间或读取身份校验有问题,测试会失败。

调用关系:这是这组测试里的“主线成功案例”。它调用 create_test_cache 和 test_bundle 准备环境与数据,再直接验证 CloudConfigBundleCache 的 save 和 load 是否能配合工作。

调用图:调用 2 个内部函数(create_test_cache, test_bundle);外部调用 5 个(assert!, assert_eq!, from_slice, read, tempdir)。

load_rejects_missing_request_identity_before_reading_cache_file106–120 ↗
async fn load_rejects_missing_request_identity_before_reading_cache_file()

作用:测试读取缓存时,如果请求方没有提供完整身份信息,就应该立刻拒绝。因为不知道是谁在读,就不能冒险给出缓存配置。

数据流:进去没有参数,测试创建一个空的临时缓存环境 → 它分别用缺少用户 ID、缺少账号 ID 的参数调用 load → 出来都应该是 AuthIdentityIncomplete 错误,而且重点是还没必要去读缓存文件。

调用关系:它验证加载流程最前面的安全门槛。它只需要 create_test_cache 建环境,不需要写缓存文件,因为这里测试的是“身份不完整时先挡住”。

调用图:调用 1 个内部函数(create_test_cache);外部调用 2 个(assert_eq!, tempdir)。

load_reports_missing_and_malformed_cache_files123–137 ↗
async fn load_reports_missing_and_malformed_cache_files()

作用:测试缓存文件不存在或文件内容坏掉时,加载函数能给出明确错误,而不是崩溃或误以为有可用缓存。

数据流:进去没有参数,测试创建临时缓存环境 → 它先在没有文件的情况下调用 load,期待 CacheFileNotFound;然后往缓存路径写入一个不完整的 JSON 字符串,再调用 load,期待 CacheParseFailed → 出来是两类文件问题都被正确识别。

调用关系:它覆盖文件读取阶段的常见坏情况。create_test_cache 提供路径,std::fs::write 故意写坏文件,load 负责报告是没文件还是解析失败。

调用图:调用 1 个内部函数(create_test_cache);外部调用 4 个(assert!, assert_eq!, write, tempdir)。

load_rejects_tampered_payload140–156 ↗
async fn load_rejects_tampered_payload()

作用:测试缓存正文被人改过时,加载逻辑会因为签名不匹配而拒绝。这里的核心是防止本地缓存被偷偷篡改后仍然生效。

数据流:进去没有参数 → 它先造一份合法载荷并签名,然后在签名之后改掉配置里的审批策略内容,再把这个“正文被改、签名没变”的文件写到磁盘 → 调用 load 后应得到 CacheSignatureInvalid。

调用关系:它把 signed_cache_file 当作封条工具,先生成正常文件,再故意破坏内容。write_cache_file 负责落盘,最后由缓存加载逻辑检查签名并拒绝。

调用图:调用 4 个内部函数(create_test_cache, signed_cache_file, valid_signed_payload, write_cache_file);外部调用 2 个(assert_eq!, tempdir)。

load_rejects_cache_for_incomplete_or_different_identity159–178 ↗
async fn load_rejects_cache_for_incomplete_or_different_identity()

作用:测试缓存只能给原来的用户和账号使用,不能串号;如果缓存文件自己缺少身份信息,也必须拒绝。

数据流:进去没有参数 → 它先写入一份属于 user-12345/account-12345 的合法缓存,再用另一个用户 ID 去读,期待 CacheIdentityMismatch;随后又造一份缺少 chatgpt_user_id 的缓存文件写入,再用正常身份读取,期待 CacheIdentityIncomplete → 出来是两种身份问题都被拦住。

调用关系:它验证加载流程里的身份核对。valid_signed_payload 提供标准身份,signed_cache_file 负责签名,write_cache_file 写入磁盘,load 最后判断请求身份和缓存身份是否一致且完整。

调用图:调用 4 个内部函数(create_test_cache, signed_cache_file, valid_signed_payload, write_cache_file);外部调用 2 个(assert_eq!, tempdir)。

load_rejects_expired_cache181–192 ↗
async fn load_rejects_expired_cache()

作用:测试过期缓存不会被继续使用。缓存像食品保质期,过了时间就不能再拿来当新鲜配置。

数据流:进去没有参数 → 它造一份原本合法的载荷,把 expires_at 改成当前时间之前,再签名并写入缓存文件 → 调用 load 后应得到 CacheExpired,而不是返回旧配置。

调用关系:它专门检查时间有效性这一关。valid_signed_payload 生成样板,测试把过期时间调到过去,signed_cache_file 和 write_cache_file 让它变成磁盘文件,load 负责发现已经过期。

调用图:调用 4 个内部函数(create_test_cache, signed_cache_file, valid_signed_payload, write_cache_file);外部调用 4 个(seconds, now, assert_eq!, tempdir)。

load_rejects_unsupported_cache_version195–206 ↗
async fn load_rejects_unsupported_cache_version()

作用:测试缓存文件版本号不被当前程序支持时,会被拒绝。这样可以避免新旧程序误读格式不同的缓存文件。

数据流:进去没有参数 → 它造一份合法载荷,把 version 改成 2,再签名写入磁盘 → 调用 load 后应返回 CacheVersionUnsupported(2),明确说明版本不支持。

调用关系:它覆盖兼容性检查。valid_signed_payload 提供正常缓存正文,测试只改版本号,signed_cache_file 保证文件签名仍然自洽,最后由 load 判断这个版本不是当前能理解的版本。

调用图:调用 4 个内部函数(create_test_cache, signed_cache_file, valid_signed_payload, write_cache_file);外部调用 2 个(assert_eq!, tempdir)。

cloud-config/src/service_tests.rs源码 ↗
testtest run

这个文件像一套“演习剧本”。真正的云端服务不好在测试里反复调用,所以这里造了几种假的 BundleClient(可以理解成假服务器):有的永远返回固定配置,有的按顺序先失败后成功,有的故意卡住,有的只认指定访问令牌。测试再配合临时目录里的 auth.json(登录信息文件)和本地缓存文件,模拟用户是 API Key 登录、ChatGPT 企业账号登录、个人账号登录、令牌过期等场景。它重点验证几件事:只有符合条件的企业/工作区套餐才会拉云配置;远端配置必须先校验再缓存;缓存必须和用户身份匹配;请求失败会重试但不会无限等;401 未授权时会尝试重新读取登录信息;后端返回的数据转换成本地配置时不能打乱顺序。没有这些测试,云配置这种“能改变程序行为”的东西很容易在边界情况出错,而且出错后影响会很隐蔽。

函数细节41
write_auth_json29–32 ↗
fn write_auth_json(codex_home: &Path, value: serde_json::Value) -> std::io::Result<()>

作用:把一份假的登录信息写进临时目录里的 auth.json 文件。测试用它来伪装不同的登录状态。

数据流:进去的是一个目录路径和一段 JSON 数据 → 它把 JSON 转成字符串,写到这个目录下的 auth.json → 出来是写文件是否成功的结果,同时磁盘上多了或更新了这个登录文件。

调用关系:它是很多鉴权相关测试的铺垫工具。auth_manager_with_api_key、auth_manager_with_plan_and_identity 以及几个 401 恢复测试会先用它准备登录文件,然后再创建 AuthManager 读取这些内容。

调用图:被 6 处调用(auth_manager_with_api_key, auth_manager_with_plan_and_identity, get_bundle_recovers_after_unauthorized_reload, get_bundle_recovers_after_unauthorized_reload_updates_cache_identity, get_bundle_surfaces_auth_recovery_message, get_bundle_unauthorized_without_recovery_uses_generic_message);外部调用 3 个(join, to_string, write)。

create_test_cache34–36 ↗
fn create_test_cache(codex_home: &Path) -> CloudConfigBundleCache

作用:创建一个指向临时目录的云配置缓存对象。测试用它直接检查缓存有没有被正确写入或刷新。

数据流:进去的是测试用的 codex_home 目录 → 它把这个目录解析成内部使用的绝对路径,再创建 CloudConfigBundleCache → 出来是一个能读写该目录缓存文件的缓存对象。

调用关系:它服务于缓存检查类测试,比如无效缓存被替换、401 后缓存身份更新、手动刷新缓存。测试先让服务跑一遍,再用这个缓存对象把结果读出来核对。

调用图:调用 2 个内部函数(new, resolve_path_against_base);被 3 处调用(get_bundle_ignores_invalid_cache_and_refetches, get_bundle_recovers_after_unauthorized_reload_updates_cache_identity, refresh_from_remote_updates_cached_bundle)。

auth_manager_with_api_key38–56 ↗
async fn auth_manager_with_api_key() -> Arc<AuthManager>

作用:造一个只带 OPENAI_API_KEY 的 AuthManager(鉴权管理器,负责读取和提供登录凭据)。这个场景代表非 ChatGPT 登录。

数据流:进去没有业务参数 → 它创建临时目录,写入只有 API Key、没有 ChatGPT token 的 auth.json,再让 AuthManager 读取 → 出来是可共享的 AuthManager。

调用关系:get_bundle_skips_non_chatgpt_auth 用它证明:如果用户不是 ChatGPT 账号登录,云配置服务不应该去请求企业配置包。

调用图:调用 3 个内部函数(write_auth_json, default, new);被 1 处调用(get_bundle_skips_non_chatgpt_auth);外部调用 3 个(new, json!, tempdir)。

auth_manager_with_plan_and_identity58–85 ↗
async fn auth_manager_with_plan_and_identity(
    plan_type: &str,
    chatgpt_user_id: Option<&str>,
    account_id: Option<&str>,
) -> Arc<AuthManager>

作用:造一个带套餐类型、用户 ID、账号 ID 的 ChatGPT 登录管理器。测试用它精确模拟不同企业身份。

数据流:进去的是套餐名、ChatGPT 用户 ID、账号 ID → 它生成假的 ChatGPT auth.json,写入临时目录,再创建 AuthManager → 出来是带这些身份信息的 AuthManager。

调用关系:auth_manager_with_plan 是它的简化版;多个缓存身份测试直接用它改变用户或账号,观察服务是否会避开不属于当前身份的缓存。

调用图:调用 4 个内部函数(chatgpt_auth_json, write_auth_json, default, new);被 3 处调用(auth_manager_with_plan, get_bundle_does_not_use_cache_when_auth_identity_is_incomplete, get_bundle_ignores_cache_for_different_auth_identity);外部调用 2 个(new, tempdir)。

auth_manager_with_plan87–89 ↗
async fn auth_manager_with_plan(plan_type: &str) -> Arc<AuthManager>

作用:快速创建一个常用的 ChatGPT 登录管理器,只需要指定套餐类型。用户 ID 和账号 ID 使用固定测试值。

数据流:进去的是套餐类型字符串 → 它补上默认 user-12345 和 account-12345,交给 auth_manager_with_plan_and_identity → 出来是一个可用于大多数测试的 AuthManager。

调用关系:大部分套餐、缓存、重试测试都用它,因为这些测试只关心套餐是否合格,不关心具体用户身份变化。

调用图:调用 1 个内部函数(auth_manager_with_plan_and_identity);被 12 处调用(get_bundle_allows_eligible_workspace_plans_and_writes_cache, get_bundle_does_not_use_cache_when_auth_identity_is_incomplete, get_bundle_empty_response_is_success_and_cached, get_bundle_ignores_invalid_cache_and_refetches, get_bundle_rejects_invalid_remote_bundle_before_cache_write, get_bundle_retries_until_success, get_bundle_skips_individual_plan, get_bundle_skips_team_like_usage_based_plan, get_bundle_stops_after_max_retries, get_bundle_times_out (+2 more))。

chatgpt_auth_json91–106 ↗
fn chatgpt_auth_json(
    plan_type: &str,
    chatgpt_user_id: Option<&str>,
    account_id: Option<&str>,
    access_token: &str,
    refresh_token: &str,
) -> serde_json::Value

作用:生成一份标准假的 ChatGPT 登录 JSON。它让测试不用手写复杂的 token 结构。

数据流:进去的是套餐、用户 ID、账号 ID、访问令牌、刷新令牌 → 它使用固定的 last_refresh 时间继续构造 → 出来是一段 serde_json::Value,可直接写入 auth.json。

调用关系:auth_manager_with_plan_and_identity 和部分 401 错误提示测试会调用它;它把实际细节交给 chatgpt_auth_json_with_last_refresh。

调用图:调用 1 个内部函数(chatgpt_auth_json_with_last_refresh);被 2 处调用(auth_manager_with_plan_and_identity, get_bundle_surfaces_auth_recovery_message)。

chatgpt_auth_json_with_last_refresh108–125 ↗
fn chatgpt_auth_json_with_last_refresh(
    plan_type: &str,
    chatgpt_user_id: Option<&str>,
    account_id: Option<&str>,
    access_token: &str,
    refresh_token: &str,
    last_refresh: &str,
)

作用:生成假的 ChatGPT 登录 JSON,并允许指定 last_refresh(上次刷新时间)。测试用它控制令牌看起来是新还是旧。

数据流:进去的是套餐、身份、访问令牌、刷新令牌、刷新时间 → 它再补上默认 auth_mode,交给更底层的构造函数 → 出来是完整登录 JSON。

调用关系:它被普通 auth JSON 构造和未授权恢复测试使用。未授权恢复测试会用很未来的时间,让 AuthManager 不主动刷新,从而专门测试 401 后的恢复路径。

调用图:调用 1 个内部函数(chatgpt_auth_json_with_mode);被 3 处调用(chatgpt_auth_json, get_bundle_recovers_after_unauthorized_reload, get_bundle_recovers_after_unauthorized_reload_updates_cache_identity)。

chatgpt_auth_json_with_mode127–165 ↗
fn chatgpt_auth_json_with_mode(
    plan_type: &str,
    chatgpt_user_id: Option<&str>,
    account_id: Option<&str>,
    access_token: &str,
    refresh_token: &str,
    last_refresh: &str,
    auth_

作用:生成最完整、最可控的假 ChatGPT 登录 JSON,包括可选的 auth_mode。它还会造一个假的 JWT 身份令牌。

数据流:进去的是套餐、身份、token、刷新时间和可选登录模式 → 它把头部和载荷编码成一个假的 JWT(三段式身份令牌),再组装成 auth.json 的结构 → 出来是测试可写入磁盘的 JSON。

调用关系:它是所有 ChatGPT 假登录数据的最底层工厂。普通构造函数会层层调用到这里,特定测试也会直接用它模拟某种登录模式。

调用图:被 2 处调用(chatgpt_auth_json_with_last_refresh, get_bundle_unauthorized_without_recovery_uses_generic_message);外部调用 4 个(format!, json!, String, to_vec)。

test_bundle167–176 ↗
fn test_bundle() -> CloudConfigBundle

作用:生成一份有效的云配置包样本,里面同时有 config 和 requirements 两类企业托管片段。

数据流:进去没有参数 → 它组合 test_config_fragment 和 test_requirements_fragment → 出来是一份可通过校验、可用于缓存和比较的 CloudConfigBundle。

调用关系:大量测试把它作为“远端返回的正常配置”。假的客户端返回它,服务加载后测试再拿它和结果比较。

调用图:被 10 处调用(get_bundle_allows_eligible_workspace_plans_and_writes_cache, get_bundle_does_not_use_cache_when_auth_identity_is_incomplete, get_bundle_ignores_cache_for_different_auth_identity, get_bundle_ignores_invalid_cache_and_refetches, get_bundle_recovers_after_unauthorized_reload, get_bundle_recovers_after_unauthorized_reload_updates_cache_identity, get_bundle_skips_individual_plan, get_bundle_skips_non_chatgpt_auth, get_bundle_skips_team_like_usage_based_plan, get_bundle_uses_cache_when_valid);外部调用 1 个(vec!)。

test_config_fragment178–184 ↗
fn test_config_fragment() -> CloudConfigFragment

作用:生成一小段有效的云端 config.toml 配置片段。这里的内容指定模型为 gpt-5。

数据流:进去没有参数 → 它填好 id、name 和 TOML 内容字符串 → 出来是一个 CloudConfigFragment。

调用关系:test_bundle 和形状标签测试会用它,作为“企业配置存在”的代表。

test_requirements_fragment186–192 ↗
fn test_requirements_fragment() -> CloudRequirementsFragment

作用:生成一小段有效的云端 requirements.toml 要求片段。这里限制审批策略只能是 never。

数据流:进去没有参数 → 它填好 id、name 和 TOML 内容字符串 → 出来是一个 CloudRequirementsFragment。

调用关系:test_bundle 和形状标签测试会用它,作为“企业要求存在”的代表。

invalid_config_bundle194–205 ↗
fn invalid_config_bundle() -> CloudConfigBundle

作用:生成一份故意写坏的云配置包,用来测试服务会不会拒绝坏配置。

数据流:进去没有参数 → 它创建一个 contents 为 model = [ 的 TOML 片段,这不是合法 TOML → 出来是一份看起来有配置但实际无法解析的 CloudConfigBundle。

调用关系:远端坏配置测试和无效缓存测试会用它。前者确认坏远端不会写缓存,后者确认坏缓存会被忽略并重新下载。

调用图:被 2 处调用(get_bundle_ignores_invalid_cache_and_refetches, get_bundle_rejects_invalid_remote_bundle_before_cache_write);外部调用 2 个(default, vec!)。

request_error207–209 ↗
fn request_error() -> BundleRequestError

作用:制造一个可重试的请求错误。测试用它模拟网络或后端临时失败。

数据流:进去没有参数 → 它构造 BundleRequestError::Retryable,状态码为空 → 出来是一个表示“可以稍后再试”的错误。

调用关系:重试相关测试把它放进 SequenceBundleClient 的响应队列,观察服务是否会等待后再次请求,以及最多重试几次。

调用图:外部调用 1 个(Retryable)。

StaticBundleClient::new217–222 ↗
fn new(bundle: CloudConfigBundle) -> Self

作用:创建一个总是返回同一份配置包的假客户端。它像一台只会吐固定答案的假服务器。

数据流:进去是一份 CloudConfigBundle → 它保存这份 bundle,并把请求计数器设为 0 → 出来是 StaticBundleClient。

调用关系:很多测试用它来排除远端变化,只关注服务是否会请求、是否写缓存、是否跳过某些账号。

调用图:被 10 处调用(get_bundle_allows_eligible_workspace_plans_and_writes_cache, get_bundle_does_not_use_cache_when_auth_identity_is_incomplete, get_bundle_empty_response_is_success_and_cached, get_bundle_ignores_cache_for_different_auth_identity, get_bundle_ignores_invalid_cache_and_refetches, get_bundle_rejects_invalid_remote_bundle_before_cache_write, get_bundle_skips_individual_plan, get_bundle_skips_non_chatgpt_auth, get_bundle_skips_team_like_usage_based_plan, get_bundle_uses_cache_when_valid);外部调用 1 个(new)。

StaticBundleClient::get_bundle226–229 ↗
async fn get_bundle(&self, _auth: &CodexAuth) -> Result<CloudConfigBundle, BundleRequestError>

作用:模拟从远端拿配置包,但每次都成功返回固定内容。

数据流:进去的是鉴权对象但这里不检查 → 它把请求次数加 1,然后克隆保存的 bundle → 出来是 Ok(bundle)。

调用关系:CloudConfigBundleService 在测试中会调用它。测试通过 request_count 判断服务到底有没有发起远端请求。

调用图:外部调用 2 个(fetch_add, clone)。

PendingBundleClient::get_bundle235–238 ↗
async fn get_bundle(&self, _auth: &CodexAuth) -> Result<CloudConfigBundle, BundleRequestError>

作用:模拟一个永远不返回的远端请求。它专门用来测试超时保护。

数据流:进去的是鉴权对象但不用 → 它一直等待 pending,不主动完成 → 正常情况下不会出来;如果外层超时,会被测试里的服务超时逻辑打断。

调用关系:get_bundle_times_out 把它交给服务,确认服务不会无限卡住,而是到时间后返回超时错误。

调用图:外部调用 1 个(default)。

SequenceBundleClient::new247–252 ↗
fn new(responses: Vec<Result<CloudConfigBundle, BundleRequestError>>) -> Self

作用:创建一个按顺序返回预设结果的假客户端。它适合模拟“第一次失败、第二次成功”这种过程。

数据流:进去是一串 Result,里面可能是成功 bundle,也可能是错误 → 它放进队列,并设置请求计数器为 0 → 出来是 SequenceBundleClient。

调用关系:重试、缓存失效、身份变化、刷新缓存等测试用它安排远端返回顺序,让服务走到指定分支。

调用图:被 6 处调用(get_bundle_does_not_use_cache_when_auth_identity_is_incomplete, get_bundle_ignores_cache_for_different_auth_identity, get_bundle_retries_until_success, get_bundle_stops_after_max_retries, get_bundle_uses_cache_when_valid, refresh_from_remote_updates_cached_bundle);外部调用 3 个(new, from, new)。

SequenceBundleClient::get_bundle256–262 ↗
async fn get_bundle(&self, _auth: &CodexAuth) -> Result<CloudConfigBundle, BundleRequestError>

作用:每被请求一次,就从队列头取一个预设响应返回。

数据流:进去的是鉴权对象但不用 → 它把请求次数加 1,锁住响应队列,弹出最前面的结果;如果队列空了就返回空配置包 → 出来是这次预设的成功或失败。

调用关系:CloudConfigBundleService 调它时,测试就能控制服务看到的远端世界。比如先给错误触发重试,再给成功验证恢复。

调用图:外部调用 1 个(fetch_add)。

TokenBundleClient::get_bundle272–285 ↗
async fn get_bundle(&self, auth: &CodexAuth) -> Result<CloudConfigBundle, BundleRequestError>

作用:模拟一个会检查访问令牌的远端服务。只有 token 正确才给配置包,否则返回 401 未授权。

数据流:进去的是 CodexAuth → 它读取其中的访问令牌,和 expected_token 比较;匹配就返回 bundle,不匹配就返回 Unauthorized 错误,同时请求次数加 1 → 出来是成功配置或 401 错误。

调用关系:401 后重新加载登录信息的测试会用它。第一次旧 token 失败,服务重新读 auth.json 后第二次用新 token 成功。

调用图:外部调用 3 个(fetch_add, clone, matches!)。

UnauthorizedBundleClient::get_bundle294–300 ↗
async fn get_bundle(&self, _auth: &CodexAuth) -> Result<CloudConfigBundle, BundleRequestError>

作用:模拟远端总是拒绝访问,并返回指定的 401 错误消息。

数据流:进去的是鉴权对象但不用 → 它把请求次数加 1,然后构造 Unauthorized 错误,带上预设 message → 出来永远是 Err。

调用关系:几个认证失败提示测试用它,确认服务在恢复不了登录时,会给用户合适、干净的错误信息,而不是把后端 HTML 原样暴露出来。

调用图:外部调用 1 个(fetch_add)。

bundle_shape_tag_describes_sorted_enterprise_sources304–339 ↗
fn bundle_shape_tag_describes_sorted_enterprise_sources()

作用:测试指标标签 bundle_shape_tag 是否能正确描述配置包里有哪些企业托管内容。

数据流:进去是几种手工构造的 bundle 情况:没有、空、只有 config、只有 requirements、两者都有 → 它逐个调用 bundle_shape_tag 并断言结果字符串 → 出来是测试通过或失败。

调用关系:它直接验证 metrics 模块里的标签生成逻辑,确保后续监控看到的“配置包形状”稳定且顺序一致。

调用图:外部调用 1 个(assert_eq!)。

get_bundle_skips_non_chatgpt_auth342–354 ↗
async fn get_bundle_skips_non_chatgpt_auth()

作用:确认 API Key 登录时不会去拉云配置包。因为这类登录没有 ChatGPT 企业身份,不能套用企业云配置。

数据流:进去是测试环境 → 它创建 API Key 鉴权和固定返回客户端,启动服务加载 bundle → 出来期望结果是 Ok(None),并且假客户端请求次数仍是 0。

调用关系:测试运行器调用它。它用 auth_manager_with_api_key 和 StaticBundleClient 验证 CloudConfigBundleService 的“非 ChatGPT 跳过”分支。

调用图:调用 4 个内部函数(new, new, auth_manager_with_api_key, test_bundle);外部调用 3 个(new, assert_eq!, tempdir)。

get_bundle_skips_individual_plan357–369 ↗
async fn get_bundle_skips_individual_plan()

作用:确认个人套餐比如 pro 不会下载企业云配置。

数据流:进去是测试环境 → 它创建 pro 套餐登录和固定 bundle 客户端,调用启动加载 → 出来应为 Ok(None),远端请求次数为 0。

调用关系:它通过 auth_manager_with_plan 准备个人套餐身份,然后看服务有没有错误地调用 StaticBundleClient。

调用图:调用 4 个内部函数(new, new, auth_manager_with_plan, test_bundle);外部调用 3 个(new, assert_eq!, tempdir)。

get_bundle_allows_eligible_workspace_plans_and_writes_cache372–409 ↗
async fn get_bundle_allows_eligible_workspace_plans_and_writes_cache()

作用:确认符合条件的工作区/企业套餐会下载云配置,并写入本地缓存。

数据流:进去是一组套餐名 → 对每个套餐,它创建登录、假远端、临时 home,调用启动加载 → 出来应拿到 bundle、请求一次,并看到缓存文件存在。

调用关系:它覆盖多个允许的套餐类型,验证 CloudConfigBundleService 的资格判断、远端请求和缓存写入这三步能连起来。

调用图:调用 4 个内部函数(new, new, auth_manager_with_plan, test_bundle);外部调用 4 个(new, assert!, assert_eq!, tempdir)。

get_bundle_skips_team_like_usage_based_plan412–424 ↗
async fn get_bundle_skips_team_like_usage_based_plan()

作用:确认某个看起来像团队但不符合企业条件的 usage based 套餐会被跳过。

数据流:进去是 self_serve_business_usage_based 套餐场景 → 服务尝试启动加载 → 出来应为 Ok(None),假远端没有被请求。

调用关系:它和允许套餐测试形成对照,防止套餐名匹配写得太宽,把不该受管的用户也套上云配置。

调用图:调用 4 个内部函数(new, new, auth_manager_with_plan, test_bundle);外部调用 3 个(new, assert_eq!, tempdir)。

get_bundle_rejects_invalid_remote_bundle_before_cache_write427–451 ↗
async fn get_bundle_rejects_invalid_remote_bundle_before_cache_write()

作用:确认远端返回的坏配置会被拒绝,而且不能写进缓存。

数据流:进去是企业登录和一个返回 invalid_config_bundle 的假远端 → 服务加载时先拿到坏 bundle,再校验失败 → 出来是 InvalidBundle 错误,请求一次,缓存文件不存在。

调用关系:它验证服务的“失败关闭”原则:云端配置有问题时宁可报错,也不能把坏规则保存下来影响后续启动。

调用图:调用 4 个内部函数(new, new, auth_manager_with_plan, invalid_config_bundle);外部调用 4 个(new, assert!, assert_eq!, tempdir)。

get_bundle_ignores_invalid_cache_and_refetches454–487 ↗
async fn get_bundle_ignores_invalid_cache_and_refetches()

作用:确认本地缓存如果已经坏了,服务会忽略它并重新从远端获取。

数据流:进去是一个预先写入坏 bundle 的缓存和一个返回正常 bundle 的假远端 → 服务启动时发现缓存不可用,转而请求远端,再写回新缓存 → 出来返回正常 bundle,远端请求一次,缓存内容变成正常 bundle。

调用关系:它用 create_test_cache 直接布置和检查缓存,用 StaticBundleClient 提供替换内容,验证缓存不会让坏配置长期残留。

调用图:调用 6 个内部函数(new, new, auth_manager_with_plan, create_test_cache, invalid_config_bundle, test_bundle);外部调用 3 个(new, assert_eq!, tempdir)。

get_bundle_empty_response_is_success_and_cached490–508 ↗
async fn get_bundle_empty_response_is_success_and_cached()

作用:确认远端返回空配置包也算成功,并且会缓存这个“空结果”。

数据流:进去是企业登录和返回默认空 bundle 的假远端 → 服务加载后判断没有实际配置可应用 → 出来是 Ok(None),但请求发生了一次,缓存文件存在。

调用关系:它防止服务把“企业明确没有配置”误当成请求失败;缓存空结果也能避免下次重复无意义请求。

调用图:调用 3 个内部函数(new, new, auth_manager_with_plan);外部调用 5 个(new, assert!, assert_eq!, default, tempdir)。

get_bundle_uses_cache_when_valid511–532 ↗
async fn get_bundle_uses_cache_when_valid()

作用:确认已有有效缓存时,启动加载会直接用缓存,不再请求远端。

数据流:进去先用一个服务把正常 bundle 写进缓存,再创建一个远端会失败的新服务 → 新服务启动加载时读取缓存 → 出来仍返回缓存里的 bundle,失败远端请求次数为 0。

调用关系:它验证缓存优先路径。SequenceBundleClient 被安排成会报错,但不应被调用,说明服务确实省掉了远端请求。

调用图:调用 5 个内部函数(new, new, new, auth_manager_with_plan, test_bundle);外部调用 4 个(new, assert_eq!, tempdir, vec!)。

get_bundle_ignores_cache_for_different_auth_identity535–572 ↗
async fn get_bundle_ignores_cache_for_different_auth_identity()

作用:确认缓存只属于写入它的用户身份,换了用户不能继续用旧缓存。

数据流:进去先用 user-12345 写缓存,再用 user-99999 登录并准备一份 replacement_bundle → 服务启动时发现缓存身份不匹配,于是请求远端 → 出来返回 replacement_bundle,请求次数为 1。

调用关系:它用 auth_manager_with_plan_and_identity 改变用户 ID,验证 CloudConfigBundleService 和缓存层不会把一个人的企业配置串给另一个人。

调用图:调用 5 个内部函数(new, new, new, auth_manager_with_plan_and_identity, test_bundle);外部调用 5 个(new, assert_eq!, default, tempdir, vec!)。

get_bundle_times_out575–592 ↗
async fn get_bundle_times_out()

作用:确认远端请求一直不返回时,服务会超时失败,而不是启动过程永远卡住。

数据流:进去是企业登录和 PendingBundleClient → 测试暂停时间并手动推进超过超时值 → 出来是包含“timed out waiting for cloud config bundle”的错误。

调用关系:它把服务调用放进 tokio 任务里,再推进虚拟时间,专门验证 load_startup_bundle_with_timeout 的外层保护。

调用图:调用 2 个内部函数(new, auth_manager_with_plan);外部调用 6 个(new, from_millis, assert!, tempdir, spawn, advance)。

get_bundle_retries_until_success595–614 ↗
async fn get_bundle_retries_until_success()

作用:确认遇到可重试错误后,服务会等一会儿再试,并能在后续成功时返回配置。

数据流:进去是一个先返回 request_error、再返回 test_bundle 的 SequenceBundleClient → 服务第一次失败后等待,时间推进后第二次请求成功 → 出来是 Ok(Some(test_bundle)),请求次数为 2。

调用关系:它验证重试流程的正向路径:CloudConfigBundleService 没有一遇到临时网络问题就放弃。

调用图:调用 3 个内部函数(new, new, auth_manager_with_plan);外部调用 8 个(new, from_secs, assert_eq!, tempdir, spawn, yield_now, advance, vec!)。

get_bundle_recovers_after_unauthorized_reload617–671 ↗
async fn get_bundle_recovers_after_unauthorized_reload()

作用:确认访问令牌过期导致 401 时,服务会重新读取登录文件,并用新令牌再试一次。

数据流:进去先写入旧 access token 创建 AuthManager,再把 auth.json 改成 fresh access token,并让 TokenBundleClient 只接受新 token → 服务第一次用旧 token 失败,重新加载后第二次成功 → 出来返回 test_bundle,请求次数为 2。

调用关系:它串起 write_auth_json、chatgpt_auth_json_with_last_refresh、TokenBundleClient 和服务的 401 恢复逻辑,模拟真实用户登录文件被刷新后的情况。

调用图:调用 6 个内部函数(new, chatgpt_auth_json_with_last_refresh, test_bundle, write_auth_json, default, new);外部调用 4 个(new, new, assert_eq!, tempdir)。

get_bundle_recovers_after_unauthorized_reload_updates_cache_identity674–735 ↗
async fn get_bundle_recovers_after_unauthorized_reload_updates_cache_identity()

作用:确认 401 后重新读取登录信息时,如果用户身份也变了,缓存会按新身份写入。

数据流:进去先用 user-12345 的旧 token 创建 AuthManager,再把 auth.json 改成 user-99999 的新 token → 服务恢复后成功下载 bundle,并保存缓存 → 出来能用 user-99999/account-12345 读到缓存,请求次数为 2。

调用关系:它在上一类 401 恢复基础上多检查缓存身份。create_test_cache 负责事后读取缓存来核对写入的身份。

调用图:调用 7 个内部函数(new, chatgpt_auth_json_with_last_refresh, create_test_cache, test_bundle, write_auth_json, default, new);外部调用 4 个(new, new, assert_eq!, tempdir)。

get_bundle_surfaces_auth_recovery_message738–798 ↗
async fn get_bundle_surfaces_auth_recovery_message()

作用:确认 401 恢复失败且账号已经不一致时,服务会返回明确的重新登录提示。

数据流:进去先创建一个企业登录,再把磁盘 auth.json 改成另一个 account_id,同时假远端总是 401 → 服务尝试处理未授权但发现身份不一致 → 出来是 Auth 错误,消息提示用户可能已登出或换账号,需要重新登录。

调用关系:它用 UnauthorizedBundleClient 强制失败,验证 CloudConfigBundleService 不只是报“401”,而是把登录恢复失败原因翻译成用户能看懂的提示。

调用图:调用 5 个内部函数(new, chatgpt_auth_json, write_auth_json, default, new);外部调用 4 个(new, new, assert_eq!, tempdir)。

get_bundle_unauthorized_without_recovery_uses_generic_message801–854 ↗
async fn get_bundle_unauthorized_without_recovery_uses_generic_message()

作用:确认无法通过恢复解决的 401,会返回统一的安全提示,而不是暴露后端返回的网页内容。

数据流:进去是带特定 auth_mode 的登录和一个返回带 HTML 文本 401 的假远端 → 服务加载失败 → 出来是 Auth 错误,消息等于 CLOUD_CONFIG_BUNDLE_AUTH_RECOVERY_FAILED_MESSAGE,请求次数为 1。

调用关系:它直接用 chatgpt_auth_json_with_mode 构造场景,用 UnauthorizedBundleClient 提供难看的后端错误,验证错误信息清洗。

调用图:调用 5 个内部函数(new, chatgpt_auth_json_with_mode, write_auth_json, default, new);外部调用 4 个(new, new, assert_eq!, tempdir)。

get_bundle_does_not_use_cache_when_auth_identity_is_incomplete857–897 ↗
async fn get_bundle_does_not_use_cache_when_auth_identity_is_incomplete()

作用:确认当前登录身份不完整时,服务不会冒险使用已有缓存。

数据流:进去先用完整身份写入缓存,再用缺少 chatgpt_user_id 的身份启动服务,并准备 replacement_bundle → 服务不信任旧缓存,改为请求远端 → 出来返回 replacement_bundle,请求次数为 1。

调用关系:它覆盖一种容易漏掉的安全边界:身份信息缺一块时,不能证明缓存属于当前用户,就应该重新获取。

调用图:调用 6 个内部函数(new, new, new, auth_manager_with_plan, auth_manager_with_plan_and_identity, test_bundle);外部调用 5 个(new, assert_eq!, default, tempdir, vec!)。

get_bundle_stops_after_max_retries900–928 ↗
async fn get_bundle_stops_after_max_retries()

作用:确认请求一直可重试失败时,服务会在最大次数后停止,而不是无限重试。

数据流:进去是一个连续返回 request_error、数量等于最大尝试次数的 SequenceBundleClient → 服务按重试策略多次请求,虚拟时间推进后耗尽机会 → 出来是 RequestFailed 错误,请求次数等于 CLOUD_CONFIG_BUNDLE_MAX_ATTEMPTS。

调用关系:它验证重试流程的刹车。和 get_bundle_retries_until_success 一起保证既会重试,也不会没完没了。

调用图:调用 3 个内部函数(new, new, auth_manager_with_plan);外部调用 8 个(new, from_secs, assert_eq!, tempdir, spawn, yield_now, advance, vec!)。

refresh_from_remote_updates_cached_bundle931–963 ↗
async fn refresh_from_remote_updates_cached_bundle()

作用:确认手动从远端刷新一次缓存时,会用新配置替换旧缓存。

数据流:进去是一个先返回 test_bundle、再返回 replacement_bundle 的 SequenceBundleClient → 先启动加载写入旧缓存,再调用 refresh_cache_once → 出来刷新成功,直接读缓存能看到 replacement_bundle。

调用关系:它测试服务运行后的缓存刷新入口。load_startup_bundle 先铺底,refresh_cache_once 再触发第二次远端请求,create_test_cache 负责核验结果。

调用图:调用 4 个内部函数(new, new, auth_manager_with_plan, create_test_cache);外部调用 6 个(new, assert!, assert_eq!, default, tempdir, vec!)。

bundle_response_conversion_preserves_fragment_order966–1019 ↗
fn bundle_response_conversion_preserves_fragment_order()

作用:确认后端响应转换成本地 CloudConfigBundle 时,不会打乱片段顺序。

数据流:进去是手工构造的 ConfigBundleResponse,其中 config 片段顺序是 high 再 low,requirements 有一个片段 → 它调用 bundle_from_response → 出来应是同样顺序的本地 CloudConfigBundle。

调用关系:它直接测试 backend 模块的转换函数。顺序很重要,因为后面的配置片段可能覆盖前面的设置,打乱顺序会改变实际生效结果。

调用图:外部调用 3 个(new, assert_eq!, vec!)。

bundle_response_conversion_treats_missing_sections_as_empty1022–1027 ↗
fn bundle_response_conversion_treats_missing_sections_as_empty()

作用:确认后端响应缺少某些区块时,会被当成空配置,而不是报错或产生奇怪默认值。

数据流:进去是 ConfigBundleResponse::new() 生成的空响应 → 它调用 bundle_from_response → 出来应等于 CloudConfigBundle::default()。

调用关系:它补充验证后端数据转换的宽容性:远端没发某个部分时,本地应理解为没有配置。

调用图:外部调用 1 个(assert_eq!)。

cloud-tasks/tests/env_filter.rs源码 ↗
testtest run

这个测试文件检查一件很具体但很重要的事:同一个任务系统,在不同环境里应该看到不同的任务。这里的“环境”可以理解成不同的工作区或分组,比如默认环境、env-A、env-B。测试先创建一个 MockClient,也就是假的客户端,不会真的连网络,只提供预设好的假数据。然后它调用 CloudBackend::list_tasks 三次:第一次不指定环境,确认默认列表里有“Update README”;第二次指定 env-A,确认只返回 1 个任务,而且标题正好是“A: First”;第三次指定 env-B,确认返回 2 个任务,而且第一个标题以“B: ”开头。它像是在检查一个分类抽屉:打开不同标签的抽屉,里面的东西必须不一样,不能串抽屉。

函数细节1
mock_backend_varies_by_env5–39 ↗
async fn mock_backend_varies_by_env()

作用:这个测试函数确认 MockClient 这个假后台会按环境名过滤任务。有人修改云任务列表功能或 mock 数据时,可以靠它发现“不同环境返回了错误任务”这类问题。

数据流:进去的是一个假的 MockClient,以及三次不同的环境参数:不传环境、传入 env-A、传入 env-B。函数把这些参数交给 CloudBackend::list_tasks,拿到任务列表后逐项检查:默认环境里要有指定任务,env-A 必须只有一个固定标题的任务,env-B 必须有两个任务且标题带 B 前缀。出来的结果不是业务数据,而是测试通过或失败;如果实际返回和预期不一致,assert! 或 assert_eq! 会让测试失败。

调用关系:这个函数由测试框架 tokio 在运行测试时自动执行,因为它标了异步测试注解。它自己不实现列任务的细节,而是把核心工作交给 CloudBackend::list_tasks;拿到结果后,用 assert! 和 assert_eq! 这些断言工具来判断结果是否符合预期。

调用图:外部调用 3 个(assert!, assert_eq!, list_tasks)。

codex-home/src/instructions/tests.rs源码 ↗
testtest

这份文件不是给正式运行时用的,而是给测试用的。它像一组“验收清单”,专门检查 CodexHomeUserInstructionsProvider 这个读取用户指令的部件是否靠谱。测试会先创建一个临时目录,当成假的 Codex 主目录,然后在里面放不同情况的 AGENTS.md 文件:没有文件、默认文件、覆盖文件、空文件、目录、坏掉的符号链接(一种指向别处的快捷方式)、以及不是标准 UTF-8 的内容。接着它调用读取逻辑,确认结果符合预期。这里很重要的一点是:本地覆盖文件优先,但如果它没内容或读不了,就应该退回默认文件;如果读文件出现可恢复的问题,还要给出警告;如果文字编码有坏字节,则用替代字符保住内容,而不是直接崩掉。

函数细节9
provider15–19 ↗
fn provider(home: &TempDir) -> CodexHomeUserInstructionsProvider

作用:这个小工具函数用一个临时目录快速造出一个指令读取器,方便每个测试反复使用。这样测试不用每次都手写同样的初始化代码。

数据流:输入是一个临时目录 → 它取出这个目录的路径,并把它转换成程序要求的“绝对路径”(完整路径,不依赖当前所在目录)→ 输出一个 CodexHomeUserInstructionsProvider,也就是后续真正去读 AGENTS.md 的对象。

调用关系:它处在测试准备阶段,帮测试搭好要检查的读取器。调用关系里显示 invalid_utf8_is_lossy 会用它来创建读取器;它内部会调用 new 创建读取器,并用 try_from 检查路径格式。

调用图:调用 2 个内部函数(new, try_from);被 1 处调用(invalid_utf8_is_lossy);外部调用 1 个(path)。

expected21–35 ↗
fn expected(
    home: &TempDir,
    filename: &str,
    text: &str,
    warnings: Vec<String>,
) -> LoadedUserInstructions

作用:这个小工具函数用来拼出“我期望读取结果应该长什么样”。它让断言更清楚,不用在每个测试里重复写一大段结果结构。

数据流:输入是临时目录、文件名、期望读到的文字、期望出现的警告列表 → 它把文件名拼到临时目录后面,做成指令来源路径,并把文字包装成 UserInstructions → 输出一个 LoadedUserInstructions,代表读取完成后的标准答案。

调用关系:它是各个测试的对照答案生成器。测试拿真实读取结果和它生成的结果做比较;它自己只负责组装数据,不去读文件。

调用图:调用 1 个内部函数(try_from);外部调用 1 个(path)。

missing_files_return_no_instructions56–63 ↗
async fn missing_files_return_no_instructions()

作用:这个测试确认:如果默认指令文件和覆盖指令文件都不存在,读取结果应该是“没有用户指令”,而不是报错或编造内容。

数据流:开始时创建一个空的临时目录 → 调用读取逻辑去找指令文件 → 得到默认的空结果,并用 assert_eq! 确认它和预期完全一致。

调用关系:这是最基础的空目录场景测试。它验证读取器在启动时遇到“用户还没写配置文件”的正常情况时,能安静返回空结果。

调用图:外部调用 2 个(new, assert_eq!)。

override_takes_precedence_over_default66–75 ↗
async fn override_takes_precedence_over_default()

作用:这个测试确认:如果默认指令和本地覆盖指令都存在,应该优先使用覆盖指令。这样用户临时指定的内容不会被默认文件盖掉。

数据流:先创建临时目录 → 写入一个默认 AGENTS.md,内容是 default → 再写入一个覆盖文件,内容是 override → 调用读取逻辑 → 结果应来自覆盖文件,文字是 override,且没有警告。

调用关系:它检查优先级规则。测试通过写文件制造两份候选指令,然后把读取结果和 expected 生成的标准答案比较,确保覆盖文件胜出。

调用图:外部调用 3 个(new, assert_eq!, write)。

empty_override_falls_back_to_trimmed_default78–96 ↗
async fn empty_override_falls_back_to_trimmed_default()

作用:这个测试确认:覆盖文件如果只有空格、换行、制表符,就应当被当作没内容,然后退回使用默认指令。同时默认指令前后的空白会被去掉。

数据流:先写入一个只含空白字符的覆盖文件 → 再写入一个前后带空白的默认文件 → 调用读取逻辑 → 输出应来自默认文件,内容变成 default instructions,首尾空白已被清理,没有警告。

调用关系:它验证两个细节:空覆盖文件不能挡住默认文件;真正采用的文本会做首尾空白清理。这个测试靠 assert_eq! 对比 expected 结果来锁住这些行为。

调用图:外部调用 3 个(new, assert_eq!, write)。

directory_override_falls_back_to_default99–108 ↗
async fn directory_override_falls_back_to_default()

作用:这个测试确认:如果覆盖指令的位置不是文件,而是一个目录,程序不应该把它当成有效指令,而应该继续使用默认文件。

数据流:先在覆盖文件应在的位置创建一个目录 → 再写入默认指令文件 → 调用读取逻辑 → 结果应读取默认文件里的 default,且没有警告。

调用关系:它检查一种常见误配置:用户或系统不小心把文件路径建成了目录。这个测试保证读取器不会因此中断,而是走回默认指令。

调用图:外部调用 4 个(new, assert_eq!, create_dir, write)。

recoverable_override_read_error_warns_and_falls_back_to_default111–126 ↗
async fn recoverable_override_read_error_warns_and_falls_back_to_default()

作用:这个测试确认:覆盖文件读取失败时,程序应当给出警告,并且继续读取默认指令,而不是整个失败。这里的“可恢复”意思是问题不致命,还有备用文件可用。

数据流:先创建临时目录 → 用 create_symlink_loop 制造一个读不了的覆盖文件 → 写入默认文件 default → 亲自读一次坏链接拿到系统错误文字,用它拼出期望警告 → 调用读取逻辑 → 输出应是默认指令,同时 warnings 里包含这条警告。

调用关系:它把 create_symlink_loop 当成故障制造器,再用 format! 拼出应出现的警告。这个测试位于错误处理链路上,确保读取器遇到坏覆盖文件时会“报告问题,但继续工作”。

调用图:调用 1 个内部函数(create_symlink_loop);外部调用 5 个(new, assert_eq!, format!, read, write)。

invalid_utf8_is_lossy129–147 ↗
async fn invalid_utf8_is_lossy()

作用:这个测试确认:如果指令文件里混入了非法 UTF-8 字节,程序不会崩溃,而是用替代字符把坏字节标出来并继续读取。UTF-8 是常见文字编码;非法字节就是不符合这种编码规则的内容。

数据流:先创建临时目录和默认指令路径 → 组装一段字节:global、一个非法字节 0xff、再加 doc → 写入文件 → 通过 provider 创建读取器并读取 → 输出文字应变成 globalU+FFFD doc,没有警告。

调用关系:它检查文字解码策略。调用关系里它会用 provider 搭好读取器,再用 assert_eq! 比较结果,确保读取器采用“有损转换”:尽量保留可读文字,用 U+FFFD 代替坏掉的部分。

调用图:调用 1 个内部函数(provider);外部调用 3 个(new, assert_eq!, write)。

权限、沙箱与网络策略

这些测试定义如何解释和执行权限配置、代理与网络规则、沙箱转换、Windows 沙箱行为以及执行环境。

core/src/config/permissions_tests.rs源码 ↗
testtest/CI

这份文件不参与正式运行,而是在测试时专门检查权限系统。它覆盖几类容易出错的地方:Windows 路径里特殊前缀不能被误当成通配符;受限读权限下,程序自己需要的 zsh 和 exec 包装器仍要能读取;旧版网络配置键要被安全忽略;网络域名和 Unix socket(一种本机进程间通信用的文件形插口)允许/拒绝规则要正确合并;权限 profile(权限方案)继承时,父方案先铺底,子方案再覆盖。它还检查通配符路径,比如 **/*.env 这种模式,哪些可以用来拒绝读取,哪些不能随便用于读写。整体作用是把权限系统的“边界条件”写成可重复的检查,避免安全策略悄悄变形。

函数细节18
normalize_absolute_path_for_platform_simplifies_windows_verbatim_paths26–32 ↗
fn normalize_absolute_path_for_platform_simplifies_windows_verbatim_paths()

作用:检查 Windows 的特殊绝对路径前缀 \\?\ 会被正确去掉,变成普通人能认的路径。这样后面的权限判断不会因为路径长得特殊就认错地方。

数据流:输入是一条带 \\?\ 前缀的 Windows 路径和“这是 Windows”的标记 → 调用路径规范化函数把它整理成普通路径 → 输出结果要等于 D:\... 形式的普通路径,测试用断言确认这一点。

调用关系:这是路径处理测试的起点之一。它直接验证底层的路径标准化工具,后续权限编译、读写判断都依赖路径先被认成同一种格式。

调用图:外部调用 1 个(assert_eq!)。

windows_verbatim_path_prefix_does_not_count_as_glob_syntax35–44 ↗
fn windows_verbatim_path_prefix_does_not_count_as_glob_syntax()

作用:检查 Windows 特殊路径前缀里的问号不会被误判成通配符。否则普通路径可能被当成“匹配很多文件的规则”,权限判断就会乱。

数据流:输入两条 Windows 路径:一条只是带 \\?\ 前缀,另一条真的含有 *** 通配符 → 调用通配符检测函数 → 第一条应返回“没有通配符”,第二条应返回“有通配符”。

调用关系:它补充前一个路径测试,专门防止通配符识别误伤 Windows 的系统前缀;后面的 glob 权限规则依赖这个判断准确。

调用图:外部调用 1 个(assert!)。

restricted_read_implicitly_allows_helper_executables47–112 ↗
async fn restricted_read_implicitly_allows_helper_executables() -> std::io::Result<()>

作用:确认在受限读取模式下,程序运行自己必须用的小工具文件仍然可以读。没有这个保护,沙盒太严时程序可能连自己的 shell 或执行包装器都启动不了。

数据流:先创建临时目录,模拟工作区、Codex 主目录、zsh 程序、当前会话的 exec 包装器目录,以及另一个会话的兄弟目录 → 加载带默认权限和覆盖项的配置 → 取出文件系统沙盒策略 → 检查 zsh 和当前会话包装器目录可读,但兄弟会话目录不可读。

调用关系:这是一个更接近真实启动场景的异步测试。它把临时文件系统、配置加载和最终权限策略串起来,验证 Config::load_from_base_config_with_overrides 生成的策略既能保证程序可运行,又不会额外放开无关目录。

调用图:调用 2 个内部函数(from_absolute_path, try_from);外部调用 8 个(from, new, default, new, load_from_base_config_with_overrides, assert!, create_dir_all, write)。

network_toml_ignores_legacy_network_list_keys115–124 ↗
fn network_toml_ignores_legacy_network_list_keys()

作用:检查旧版网络配置里的 allowed_domains 这种老字段会被忽略。这样旧配置不会意外打开现在权限系统不再认可的网络入口。

数据流:输入一段 TOML 配置文本,里面写了旧字段 allowed_domains → 解析成新的 NetworkToml 结构 → 结果应等于默认网络配置,说明旧字段没有生效。

调用关系:它验证配置反序列化,也就是把文本配置读成程序结构的过程。这个测试保护网络权限迁移时的安全边界。

调用图:外部调用 1 个(assert_eq!)。

network_permission_containers_project_allowed_and_denied_entries127–182 ↗
fn network_permission_containers_project_allowed_and_denied_entries()

作用:检查网络域名和 Unix socket 权限容器能把“允许”和“拒绝”的项目分出来。这样后续代理或沙盒可以清楚知道哪些能通、哪些不能通。

数据流:输入一组域名权限和一组 Unix socket 权限,里面混有 allow 和 deny → 调用提取允许列表、拒绝列表的方法 → 输出应只包含对应状态的条目;如果没有拒绝域名,则返回空结果。

调用关系:它测试的是网络权限数据结构的小工具方法。后续把配置转成网络代理策略时,会依赖这些方法把规则分类。

调用图:外部调用 2 个(from, assert_eq!)。

network_toml_overlays_unix_socket_permissions_by_path185–241 ↗
fn network_toml_overlays_unix_socket_permissions_by_path()

作用:检查多段网络配置叠加时,Unix socket 规则按路径合并,后来的同路径规则能覆盖前面的。就像同一个门牌号的新告示会替换旧告示。

数据流:先创建一个默认网络代理配置 → 应用第一段配置,允许两个 socket → 再应用第二段配置,新增一个 socket,并把其中一个旧 socket 改成拒绝 → 最终配置应同时保留未被覆盖的旧项、新项,以及被改成拒绝的覆盖项。

调用关系:它验证 NetworkToml.apply_to_network_proxy_config 的合并行为。权限 profile 继承或多层配置叠加时,都需要这种“同键覆盖、不同键保留”的规则。

调用图:外部调用 4 个(from, default, assert_eq!, default)。

permissions_profiles_resolve_extends_parent_first_with_child_overrides244–330 ↗
fn permissions_profiles_resolve_extends_parent_first_with_child_overrides()

作用:检查权限方案继承时,先加载父方案,再让子方案覆盖同名规则。这样可以写一个基础模板,再在具体场景里只改少数地方。

数据流:输入一段 TOML,定义 base 和继承它的 child → 解析后解析 child 方案 → 程序把父方案的文件系统、网络、socket 等规则先带入,再应用子方案的覆盖和新增项 → 输出应等于手写的期望合并结果。

调用关系:这是权限 profile 继承逻辑的核心测试。后面的几个继承错误测试都围绕同一个解析入口,分别检查缺父级、非法内置父级和循环继承。

调用图:外部调用 1 个(assert_eq!)。

permissions_profiles_reject_undefined_extends_parent333–350 ↗
fn permissions_profiles_reject_undefined_extends_parent()

作用:检查如果一个权限方案继承了不存在的父方案,程序会明确报错。这样配置写错时不会悄悄退回某个不安全默认值。

数据流:输入一段 TOML,其中 child 写着继承 base,但 base 没定义 → 调用解析 profile 的方法 → 得到错误,并检查错误文字说明父方案未定义。

调用关系:它接在继承成功测试之后,验证继承链的错误分支。它保护配置加载阶段,让坏配置尽早失败。

调用图:外部调用 1 个(assert_eq!)。

permissions_profiles_reject_unsupported_builtin_extends_parent353–370 ↗
fn permissions_profiles_reject_unsupported_builtin_extends_parent()

作用:检查权限方案不能继承不支持的内置方案,比如 :danger-full-access。这能避免用户通过继承绕过正常限制。

数据流:输入一段 TOML,child 继承一个带冒号的内置方案名 → 调用解析方法 → 返回错误,并说明这个内置父方案不允许继承。

调用关系:它和 undefined parent 测试一样,都验证 resolve_profile 的防呆能力。不同点是这里父名存在“内置方案”的形式,但出于安全原因仍被拒绝。

调用图:外部调用 1 个(assert_eq!)。

permissions_profiles_reject_extends_cycles373–393 ↗
fn permissions_profiles_reject_extends_cycles()

作用:检查权限方案不能互相继承形成死循环。否则解析时可能永远绕圈,或者合并出不可预测的权限。

数据流:输入一段 TOML,alpha 继承 betabeta 又继承 alpha → 调用解析 alpha → 程序发现继承链回到起点 → 输出带有完整循环路径的错误。

调用关系:这是继承解析的安全阀测试。它确保 profile 解析器不只是会合并正常情况,也能识别配置图里的环。

调用图:外部调用 1 个(assert_eq!)。

profile_network_proxy_config_keeps_proxy_disabled_for_bare_network_access396–403 ↗
fn profile_network_proxy_config_keeps_proxy_disabled_for_bare_network_access()

作用:检查仅仅写了“网络 enabled=true”不会自动打开网络代理。这里区分的是“允许裸网络访问”和“启用代理管控”两件事。

数据流:输入一个只设置 enabled: true 的网络配置 → 转换成网络代理配置 → 输出里的代理开关仍应是关闭状态。

调用关系:它测试 network_proxy_config_from_profile_network 的一个容易误解的行为。网络 profile 可以表达网络策略,但不代表一定启动代理进程。

调用图:外部调用 2 个(default, assert!)。

profile_network_proxy_config_keeps_proxy_disabled_for_proxy_policy406–432 ↗
fn profile_network_proxy_config_keeps_proxy_disabled_for_proxy_policy()

作用:检查即使配置里有代理地址、SOCKS5 开关和域名规则,转换出来的代理配置也不会自动把代理启用。它只搬运策略,不擅自启动开关。

数据流:输入一个包含 enabled=true、代理 URL、关闭 SOCKS5、允许域名的网络配置 → 转成网络代理配置 → 输出中代理仍关闭,但 URL、SOCKS5 设置和域名规则都被正确保存。

调用关系:它延续前一个测试,覆盖更完整的代理策略字段。它说明这个转换函数负责“准备配置”,不负责决定代理是否真正启动。

调用图:外部调用 4 个(from, default, assert!, assert_eq!)。

compile_permission_profile_workspace_roots_resolves_enabled_entries435–467 ↗
fn compile_permission_profile_workspace_roots_resolves_enabled_entries() -> std::io::Result<()>

作用:检查权限方案里的工作区根目录列表只采用启用的条目,并把相对路径变成绝对路径。这样后续沙盒能准确知道哪些目录属于项目范围。

数据流:输入一个临时当前目录和一个权限方案,里面 backend 为启用、disabled 为关闭 → 编译工作区根目录 → 输出只包含基于当前目录解析出来的 backend 绝对路径。

调用关系:它测试权限编译过程中的工作区根目录准备步骤。文件系统权限里的 :workspace_roots 这类特殊范围,后面就靠这些根目录展开。

调用图:外部调用 3 个(from, new, assert_eq!)。

read_write_glob_warnings_skip_supported_deny_read_globs_and_trailing_subpaths470–501 ↗
fn read_write_glob_warnings_skip_supported_deny_read_globs_and_trailing_subpaths()

作用:检查对通配符路径的警告只针对不支持的读写规则,不要误报支持的拒绝读取规则,也不要误报像 docs/** 这种表示整个子目录的写法。

数据流:输入一个文件系统权限配置,里面有读通配符、写通配符、拒绝读取 .env 通配符,以及尾部 /** 的子目录规则 → 调用警告收集函数 → 输出只列出真正不支持的读/写通配符路径。

调用关系:它验证启动警告逻辑。这个逻辑不会直接拒绝配置,但会提醒用户哪些规则看起来写了、实际不能按普通读写 glob 生效。

调用图:外部调用 4 个(from, assert_eq!, Access, Scoped)。

unreadable_globstar_warning_is_suppressed_when_scan_depth_is_configured504–529 ↗
fn unreadable_globstar_warning_is_suppressed_when_scan_depth_is_configured()

作用:检查拒绝读取规则里使用 ** 时,如果没有配置扫描深度就要警告;如果配置了扫描深度,警告就应消失。扫描深度是限制程序往子目录里找多深,避免无边无际地扫。

数据流:先输入一个包含 **/*.env*.pem 拒绝读取规则的文件系统配置,且没有扫描深度 → 函数返回 **/*.env 的警告路径 → 再把扫描深度设为 2 → 同样检查时输出空列表。

调用关系:它测试 deny-read glob 的安全提示。这个提示帮助用户理解:带 ** 的深层匹配需要明确限制扫描范围,才不会带来性能或行为上的意外。

调用图:外部调用 3 个(from, assert_eq!, Scoped)。

glob_scan_max_depth_must_be_positive532–542 ↗
fn glob_scan_max_depth_must_be_positive()

作用:检查通配符扫描深度不能设为 0。因为 0 会让扫描什么都不做,看起来配置了拒绝规则,实际可能没起作用。

数据流:输入扫描深度 0 → 校验函数返回无效输入错误,并给出明确提示 → 再输入 2 → 校验通过并返回 2。

调用关系:它验证配置校验的小边界。这个函数会在权限配置编译或加载时被用来阻止无意义的扫描深度。

调用图:外部调用 1 个(assert_eq!)。

read_write_trailing_glob_suffix_compiles_as_subpath545–586 ↗
fn read_write_trailing_glob_suffix_compiles_as_subpath() -> std::io::Result<()>

作用:检查读写路径里以 /** 结尾的写法会被当成“整个子目录”,而不是普通通配符。这样用户写 docs/** 时,意思就是允许读 docs 下面的所有内容。

数据流:创建临时当前目录和一个权限方案,里面在 :workspace_roots 下配置 docs/** = read → 编译权限 profile → 输出的文件系统策略应是受限策略,并包含一个指向项目根下 docs 子树的读取许可。

调用关系:它连接了配置解析、权限编译和最终沙盒策略。它也和前面的警告测试呼应:尾部 /** 是支持的子路径写法,不应当被当成不支持的 glob。

调用图:外部调用 5 个(from, new, new, assert_eq!, Scoped)。

read_write_glob_patterns_still_reject_non_subpath_globs589–599 ↗
fn read_write_glob_patterns_still_reject_non_subpath_globs()

作用:检查真正的读写通配符,比如 src/**/*.rs,仍然会被拒绝。当前系统只支持这类 glob 用在拒绝读取上,不支持用它来授予读写权限。

数据流:输入路径 src/**/*.rs 和读取权限 → 调用读写 glob 编译函数 → 返回无效输入错误,错误信息说明这种 filesystem glob 只支持 deny 访问。

调用关系:这是对上一条 docs/** 特例的反向保护。它确保系统只把尾部 /** 当子目录快捷写法,而不会放开复杂 glob 来授予读写权限。

调用图:外部调用 2 个(assert!, assert_eq!)。

core/src/config/network_proxy_spec_tests.rs源码 ↗
testtest

这个文件不负责真正运行网络代理,而是像验收清单一样,反复构造不同场景来检查规则合并是否安全。这里的“网络代理”可以理解成一名门卫,决定程序能不能访问某个网站域名;“allowlist/允许名单”是能进的名单,“denylist/禁止名单”是不能进的名单;“managed/托管”表示管理员或系统强制给的规则。测试会把用户自己的配置、管理员要求、权限模式放在一起,调用 NetworkProxySpec::from_config_and_constraints 生成最终规格,再用断言检查结果。重点场景包括:管理员允许名单是否作为基础名单加入、用户禁止是否不会被管理员允许覆盖、“只允许管理员名单”是否会忽略用户自加域名、危险的完全访问模式是否仍遵守托管规则,以及用户后来修改自己的条目时,是否不会违反强制约束。

函数细节15
domain_permissions10–19 ↗
fn domain_permissions(
    entries: impl IntoIterator<Item = (&'static str, NetworkDomainPermissionToml)>,
) -> NetworkDomainPermissionsToml

作用:这是测试用的小帮手,用来快速把几条“域名模式 → 允许或禁止”的数据做成配置对象。它让每个测试不用反复写一大段准备代码。

数据流:进去的是若干对数据,比如“*.example.com”和“允许”。函数把这些条目逐个转成字符串键,收进一个映射表里,最后吐出 NetworkDomainPermissionsToml,也就是测试里模拟的管理员域名规则。

调用关系:很多测试在准备管理员要求时都会先叫它帮忙造数据,然后再把结果交给 NetworkProxySpec::from_config_and_constraints 去合并配置。它本身只做打包,不判断规则对错。

调用图:被 11 处调用(allow_only_requirements_do_not_create_deny_constraints_in_full_access, danger_full_access_keeps_managed_allowlist_and_denylist_fixed, deny_only_requirements_do_not_create_allow_constraints_in_full_access, managed_allowed_domains_only_disables_default_mode_allowlist_expansion, managed_allowed_domains_only_ignores_user_allowlist_and_hard_denies_misses, managed_unrestricted_profile_allows_domain_expansion, requirements_allowed_domains_are_a_baseline_for_user_allowlist, requirements_allowed_domains_do_not_override_user_denies_for_same_pattern, requirements_allowlist_expansion_keeps_user_entries_mutable, requirements_denied_domains_are_a_baseline_for_default_mode (+1 more));外部调用 1 个(into_iter)。

build_state_with_audit_metadata_threads_metadata_to_state22–41 ↗
fn build_state_with_audit_metadata_threads_metadata_to_state()

作用:这个测试确认审计信息不会在创建网络代理状态时丢失。审计信息就是之后查日志时用来知道“是哪次会话、哪个版本、哪个账号”触发了网络行为的标签。

数据流:它先造一个默认的 NetworkProxySpec,再造一份带会话号、版本号、账号号的 metadata。接着调用 build_state_with_audit_metadata 创建状态,最后检查状态里保存的审计信息和输入完全一样。

调用关系:这是针对构建状态流程的单点检查。它不走复杂的域名合并逻辑,只确认 NetworkProxySpec 在生成运行状态时,会把审计标签原样带过去。

调用图:外部调用 4 个(assert_eq!, default, default, default)。

requirements_allowed_domains_are_a_baseline_for_user_allowlist44–76 ↗
fn requirements_allowed_domains_are_a_baseline_for_user_allowlist()

作用:这个测试确认管理员下发的允许域名会成为用户允许名单的基础,而不是被用户配置替换掉。这样管理员要求必须能访问的域名不会消失。

数据流:进去的是一个用户允许 api.example.com 的配置,以及管理员允许 *.example.com 的要求。合并后,最终允许名单同时包含管理员的通配域名和用户的具体域名,约束里也记录管理员那条是基础规则,并标明允许名单还能扩展。

调用关系:它先用 domain_permissions 造管理员规则,再调用 from_config_and_constraints 合并。最后通过断言检查合并结果,验证默认受限模式下“管理员基础 + 用户补充”的故事成立。

调用图:调用 3 个内部函数(from_config_and_constraints, domain_permissions, read_only);外部调用 4 个(default, assert_eq!, default, vec!)。

requirements_allowed_domains_do_not_override_user_denies_for_same_pattern79–108 ↗
fn requirements_allowed_domains_do_not_override_user_denies_for_same_pattern()

作用:这个测试确认:如果用户明确禁止某个域名,管理员的同名允许规则不会直接把用户禁止擦掉。它保护用户的更严格选择。

数据流:它准备用户禁止 api.example.com,同时管理员要求允许 api.example.com。合并后,最终配置里没有允许名单,仍保留用户的禁止名单;但约束里仍记着管理员允许过这个域名,用来表示管理规则存在。

调用关系:它调用 domain_permissions 准备托管允许规则,再交给 from_config_and_constraints。测试关注的是冲突时的优先表现:用户禁止不应被同样模式的允许规则悄悄覆盖。

调用图:调用 3 个内部函数(from_config_and_constraints, domain_permissions, workspace_write);外部调用 4 个(default, assert_eq!, default, vec!)。

requirements_allowlist_expansion_keeps_user_entries_mutable111–148 ↗
fn requirements_allowlist_expansion_keeps_user_entries_mutable()

作用:这个测试确认用户自己加进允许名单的域名,之后仍然可以被用户改成禁止。管理员的基础允许名单不会把用户条目也变成不能动的强制条目。

数据流:先合并出一个包含管理员 *.example.com 和用户 api.example.com 的配置。然后复制这份配置,把用户的 api.example.com 改成禁止。最后检查管理员允许项还在、用户域名进入禁止名单,并且用 validate_policy_against_constraints 验证这没有违反强制约束。

调用关系:它先走 from_config_and_constraints 得到初始规格,再模拟用户后续编辑配置。最后把候选配置交给 validate_policy_against_constraints,确认系统只锁住管理员规则,不锁住用户自己的补充项。

调用图:调用 3 个内部函数(from_config_and_constraints, domain_permissions, workspace_write);外部调用 4 个(default, assert_eq!, default, vec!)。

managed_unrestricted_profile_allows_domain_expansion151–183 ↗
fn managed_unrestricted_profile_allows_domain_expansion()

作用:这个测试确认即使文件系统权限是“不受限制”,网络部分仍然会按托管网络规则来合并。也就是说,文件权限放宽不等于网络规则失效。

数据流:它准备用户允许 api.example.com、管理员允许 *.example.com,并使用一个托管权限档案:文件系统不限制,但网络是受限制的。合并后,允许名单包含两条,且允许名单扩展被打开。

调用关系:它直接构造 Managed 权限档案,再调用 from_config_and_constraints。这个测试连接了“权限档案”和“网络限制”两块,确保网络限制不会因为另一个权限维度较宽而被跳过。

调用图:调用 2 个内部函数(from_config_and_constraints, domain_permissions);外部调用 4 个(default, assert_eq!, default, vec!)。

danger_full_access_keeps_managed_allowlist_and_denylist_fixed186–219 ↗
fn danger_full_access_keeps_managed_allowlist_and_denylist_fixed()

作用:这个测试检查所谓完全访问或危险模式下,管理员给定的允许名单和禁止名单仍会被固定住。它防止“全开模式”绕过组织下发的网络边界。

数据流:它先让用户配置允许 evil.com、禁止 more-blocked.example.com;管理员要求允许 *.example.com、禁止 blocked.example.com。用 Disabled 权限模式合并后,最终只保留管理员的允许和禁止规则,并把允许名单、禁止名单扩展都关掉。

调用关系:它使用 domain_permissions 造出同时有允许和禁止的托管规则,再交给 from_config_and_constraints。测试说明在危险模式下,系统会把有效网络策略钉在管理员基线上,而不是让用户随意扩大。

调用图:调用 2 个内部函数(from_config_and_constraints, domain_permissions);外部调用 4 个(default, assert_eq!, default, vec!)。

managed_allowed_domains_only_disables_default_mode_allowlist_expansion222–248 ↗
fn managed_allowed_domains_only_disables_default_mode_allowlist_expansion()

作用:这个测试确认当管理员说“只能用管理员允许名单”时,普通模式下用户自己的允许域名会被排除,允许名单扩展也会关闭。

数据流:输入里用户允许 api.example.com,管理员允许 .example.com,并设置 managed_allowed_domains_only 为 true。合并后,最终允许名单只剩管理员的 .example.com,约束也标记不允许继续扩展允许名单。

调用关系:它通过 domain_permissions 准备管理员允许名单,再调用 from_config_and_constraints。这个测试覆盖“管理员名单唯一有效”的开关在默认写工作区模式下是否真正生效。

调用图:调用 3 个内部函数(from_config_and_constraints, domain_permissions, workspace_write);外部调用 4 个(default, assert_eq!, default, vec!)。

managed_allowed_domains_only_ignores_user_allowlist_and_hard_denies_misses251–282 ↗
fn managed_allowed_domains_only_ignores_user_allowlist_and_hard_denies_misses()

作用:这个测试确认“只允许管理员名单”不仅会忽略用户允许名单,还会把不在名单里的访问硬性拒绝。硬性拒绝就是不再给模糊解释空间,没列出来就不能访问。

数据流:它准备用户允许 api.example.com,管理员只允许 managed.example.com,并打开 managed_allowed_domains_only。合并后,允许名单只包含 managed.example.com,约束也只包含它,允许扩展关闭,同时 hard_deny_allowlist_misses 变成 true。

调用关系:它使用 domain_permissions 创建单条托管允许规则,再调用 from_config_and_constraints。这个测试检查的是严格白名单模式:用户名单被丢弃,名单外域名会被明确拦下。

调用图:调用 3 个内部函数(from_config_and_constraints, domain_permissions, workspace_write);外部调用 5 个(default, assert!, assert_eq!, default, vec!)。

managed_allowed_domains_only_without_managed_allowlist_blocks_all_user_domains285–306 ↗
fn managed_allowed_domains_only_without_managed_allowlist_blocks_all_user_domains()

作用:这个测试确认如果管理员开启“只允许管理员名单”,但又没有给任何管理员允许域名,那么用户所有自加域名都不能用。空名单在这里就是“一个都不放行”。

数据流:输入里只有用户允许 api.example.com,管理员要求里没有具体域名,但 managed_allowed_domains_only 是 true。合并后,最终配置没有允许名单,约束里的管理员允许名单是空数组,允许扩展关闭,并开启名单外硬拒绝。

调用关系:它不需要 domain_permissions,因为管理员没有域名条目。它直接调用 from_config_and_constraints,验证缺失的管理员名单会被当成空名单,而不是放任用户名单继续生效。

调用图:调用 2 个内部函数(from_config_and_constraints, workspace_write);外部调用 5 个(default, assert!, assert_eq!, default, vec!)。

managed_allowed_domains_only_blocks_all_user_domains_in_full_access_without_managed_list309–330 ↗
fn managed_allowed_domains_only_blocks_all_user_domains_in_full_access_without_managed_list()

作用:这个测试确认即使在完全访问模式下,如果管理员开启“只允许管理员名单”但不给名单,用户域名也全部被挡住。它防止危险模式绕过空白名单。

数据流:它准备用户允许 api.example.com,管理员只设置 managed_allowed_domains_only,没有给允许域名,并使用 Disabled 权限模式。合并后,最终没有允许名单,约束里的允许名单为空,扩展关闭,并开启硬性拒绝名单外访问。

调用关系:它直接把配置、要求和 Disabled 权限档案交给 from_config_and_constraints。这个测试和默认模式的同类测试一起确认:严格管理员名单规则在不同权限模式下都一致生效。

调用图:调用 1 个内部函数(from_config_and_constraints);外部调用 5 个(default, assert!, assert_eq!, default, vec!)。

deny_only_requirements_do_not_create_allow_constraints_in_full_access333–363 ↗
fn deny_only_requirements_do_not_create_allow_constraints_in_full_access()

作用:这个测试确认如果管理员只给了禁止规则,在完全访问模式下不会凭空制造允许名单约束。禁止归禁止,不应该顺手把允许名单也锁住。

数据流:用户允许 api.example.com,管理员只禁止 managed-blocked.example.com。合并后,用户允许名单保留,允许约束仍是空的,允许扩展开关也没有被设置;同时最终禁止名单加入管理员禁止的域名。

调用关系:它用 domain_permissions 做一条托管禁止规则,然后调用 from_config_and_constraints。测试重点是区分两类名单:只有禁止要求时,不要误伤允许名单的自由度。

调用图:调用 2 个内部函数(from_config_and_constraints, domain_permissions);外部调用 4 个(default, assert_eq!, default, vec!)。

allow_only_requirements_do_not_create_deny_constraints_in_full_access366–396 ↗
fn allow_only_requirements_do_not_create_deny_constraints_in_full_access()

作用:这个测试确认如果管理员只给了允许规则,在完全访问模式下不会凭空制造禁止名单约束。允许名单被管住,不代表用户原来的禁止名单也被管理员接管。

数据流:用户原本禁止 blocked.example.com,管理员只允许 managed.example.com。合并后,最终允许名单是管理员允许项,用户禁止名单仍保留;但约束里没有管理员禁止名单,禁止扩展开关也没有被设置。

调用关系:它通过 domain_permissions 准备托管允许规则,再调用 from_config_and_constraints。这个测试和 deny_only 的测试成对出现,确保允许规则和禁止规则不会互相“串味”。

调用图:调用 2 个内部函数(from_config_and_constraints, domain_permissions);外部调用 4 个(default, assert_eq!, default, vec!)。

requirements_denied_domains_are_a_baseline_for_default_mode399–431 ↗
fn requirements_denied_domains_are_a_baseline_for_default_mode()

作用:这个测试确认管理员下发的禁止域名会成为默认模式下禁止名单的基础,同时用户自己的禁止域名也能一起保留。

数据流:输入里用户禁止 blocked.example.com,管理员禁止 managed-blocked.example.com。合并后,最终禁止名单包含管理员条目和用户条目;约束里记录管理员禁止条目,并标明禁止名单允许继续扩展。

调用关系:它用 domain_permissions 创建托管禁止规则,再调用 from_config_and_constraints。这个测试验证默认模式下的禁止名单合并方式:管理员底线先放进去,用户还能加自己的更严格限制。

调用图:调用 3 个内部函数(from_config_and_constraints, domain_permissions, workspace_write);外部调用 4 个(default, assert_eq!, default, vec!)。

requirements_denylist_expansion_keeps_user_entries_mutable434–471 ↗
fn requirements_denylist_expansion_keeps_user_entries_mutable()

作用:这个测试确认用户自己加的禁止域名,之后可以被用户改成允许;管理员的禁止底线不会把用户禁止项也变成强制项。

数据流:先合并出包含管理员禁止 managed-blocked.example.com 和用户禁止 blocked.example.com 的配置。然后复制配置,把用户的 blocked.example.com 改成允许。最后检查管理员禁止仍在、用户域名进入允许名单,并验证候选配置仍符合约束。

调用关系:它先用 from_config_and_constraints 生成带托管底线的规格,再模拟用户后续修改,最后调用 validate_policy_against_constraints 检查合法性。它说明系统只把管理员禁止项当硬约束,用户自己的禁止项仍可撤销。

调用图:调用 3 个内部函数(from_config_and_constraints, domain_permissions, workspace_write);外部调用 4 个(default, assert_eq!, default, vec!)。

core/src/network_proxy_loader_tests.rs源码 ↗
testtest

这个文件不负责真正运行网络代理,而是像验收清单一样,反复拿小段 TOML 配置做实验。TOML 可以理解成一种人能读懂的配置文件格式。测试重点是:多层配置谁优先、权限配置能不能继承、域名大小写会不会统一、内置权限档案比如“:workspace”怎么处理、执行策略里的网络规则会不会叠加到代理配置里。它还检查受信任配置层生成的网络限制,确保最终只看最后选中的权限档案。没有这些测试,网络访问规则很容易在合并时悄悄出错,带来安全风险。

函数细节16
higher_precedence_profile_network_overlays_domain_entries17–65 ↗
fn higher_precedence_profile_network_overlays_domain_entries()

作用:这个测试确认:高优先级配置添加新的允许域名时,不会把低优先级配置里已有的允许或禁止域名全清掉。它是在验证“叠加”而不是“整块替换”。

数据流:进去的是两段 TOML:低层允许 lower.example.com、禁止 blocked.example.com;高层允许 higher.example.com。测试把它们按顺序套到一个默认网络代理配置上,最后检查允许列表里有两个允许域名,禁止列表里仍保留 blocked.example.com。

调用关系:它模拟配置加载时一层一层应用网络表的过程,主要围绕 network_tables_from_toml 和 apply_network_tables 的效果做断言,确保后续真正加载配置时不会丢掉低层仍然有效的规则。

调用图:外部调用 3 个(assert_eq!, default, from_str)。

higher_precedence_profile_network_overrides_matching_domain_entries68–113 ↗
fn higher_precedence_profile_network_overrides_matching_domain_entries()

作用:这个测试确认:同一个域名在低层说禁止、高层说允许时,高层说了算。它防止同名规则冲突时留下两个互相矛盾的结果。

数据流:进去的是两段 TOML:低层把 shared.example.com 设为禁止、other.example.com 设为允许;高层把 shared.example.com 改成允许。测试合并后,shared.example.com 和 other.example.com 都在允许列表里,禁止列表为空。

调用关系:它和前一个测试一起覆盖域名合并的两种情况:新增规则要保留旧规则,同名规则要覆盖旧规则。这里重点验证 apply_network_tables 对同一个域名的优先级处理。

调用图:外部调用 3 个(assert_eq!, default, from_str)。

higher_precedence_profile_network_overrides_named_mitm_actions116–183 ↗
fn higher_precedence_profile_network_overrides_named_mitm_actions()

作用:这个测试确认:MITM 动作(中间人代理动作,也就是代理在请求经过时可以改请求内容)如果同名,高优先级配置会覆盖低优先级配置。它防止旧的改包规则继续偷偷生效。

数据流:进去的是两层配置:低层定义一个 GitHub 写操作钩子,并让动作 strip_auth 删除 authorization 请求头;高层重新定义同名动作,让它删除 x-api-key。合并完成后,测试检查网络模式是 full,MITM 开启,钩子还在,但实际删除的请求头变成 x-api-key。

调用关系:它使用 NetworkConfigAccumulator 先收集多层网络配置,再 finish 生成最终配置。这个测试说明域名规则可以叠加,但同名 MITM 动作会以后来的定义为准。

调用图:外部调用 4 个(assert!, assert_eq!, default, from_str)。

execpolicy_network_rules_overlay_network_lists186–226 ↗
fn execpolicy_network_rules_overlay_network_lists()

作用:这个测试确认:执行策略里的网络规则可以叠加到已有网络允许/禁止列表上。执行策略可以理解成运行时额外给程序的安全规则。

数据流:进去的是一个已有配置:允许 config.example.com、禁止 blocked.example.com;再进去一个执行策略:允许 blocked.example.com 的 HTTPS,禁止 api.example.com 的 HTTP。函数应用策略后,blocked.example.com 从禁止侧变到允许侧,api.example.com 进入禁止列表。

调用关系:它验证 apply_exec_policy_network_rules 在最终网络配置中的位置:基础配置先存在,执行策略再覆盖或补充它,用来保证运行时安全规则能影响网络代理。

调用图:外部调用 4 个(assert_eq!, empty, default, vec!)。

apply_network_constraints_includes_allow_all_unix_sockets_flag229–249 ↗
fn apply_network_constraints_includes_allow_all_unix_sockets_flag()

作用:这个测试确认:配置里的 dangerously_allow_all_unix_sockets 标志会被写进网络限制里。Unix socket 是本机进程之间通信用的特殊通道,这个开关名字里带 dangerously,说明它很敏感。

数据流:进去的是一段 TOML,其中 dev 权限档案把 dangerously_allow_all_unix_sockets 设为 true。测试先选出这个档案的网络配置,再把它应用到默认的 NetworkProxyConstraints,最后检查限制对象里这个字段确实变成 Some(true)。

调用关系:它盯的是 selected_network_from_tables 和 apply_network_constraints 的衔接,确保从配置文件读到的危险开关不会在转换成受限配置时丢失。

调用图:外部调用 3 个(assert_eq!, default, from_str)。

selected_network_from_tables_ignores_builtin_profile_without_permissions_table252–266 ↗
fn selected_network_from_tables_ignores_builtin_profile_without_permissions_table()

作用:这个测试确认:如果默认权限选择的是已知内置档案“:workspace”,但配置文件里没有 permissions 表,也不算错误。内置档案就是程序自己预设的权限模板。

数据流:进去的是只写了 default_permissions = ":workspace" 的 TOML。测试把它解析成网络表并尝试选择网络配置,最后期望结果是 None,意思是这里没有额外的网络配置需要加载。

调用关系:它保护 selected_network_from_tables 的宽容行为:遇到已知内置档案时,不强迫用户在文件里重复写一份权限表。

调用图:外部调用 2 个(assert_eq!, from_str)。

selected_network_from_tables_rejects_unknown_builtin_profile_without_permissions_table269–286 ↗
fn selected_network_from_tables_rejects_unknown_builtin_profile_without_permissions_table()

作用:这个测试确认:如果配置引用了不存在的内置权限档案,比如“:unknown”,加载器会明确报错。这样可以避免拼错名字后系统默默使用错误默认值。

数据流:进去的是 default_permissions = ":unknown" 的 TOML。测试解析后调用选择网络配置的函数,并期待得到错误;最后检查错误文字明确说明 unknown built-in profile 不存在。

调用关系:它和上一个测试形成对照:已知内置档案可以没有 permissions 表,未知内置档案必须被拒绝。重点验证 selected_network_from_tables 的错误处理。

调用图:外部调用 2 个(assert_eq!, from_str)。

selected_network_from_tables_resolves_builtin_workspace_parent289–325 ↗
fn selected_network_from_tables_resolves_builtin_workspace_parent()

作用:这个测试确认:普通权限档案可以继承内置的“:workspace”档案,同时保留自己写的网络配置。继承可以理解成“先拿父模板,再加自己的改动”。

数据流:进去的是一个 dev 档案,它 extends = ":workspace",并开启网络 enabled,同时允许 child.example.com。测试选出网络配置后,期望得到的 NetworkToml 里有 enabled = true 和这个允许域名。

调用关系:它验证 selected_network_from_tables 解析继承关系时,能认出内置父档案,并把子档案自己的网络设置作为最终可见结果。

调用图:外部调用 2 个(assert_eq!, from_str)。

selected_network_from_tables_resolves_permission_profile_inheritance328–385 ↗
fn selected_network_from_tables_resolves_permission_profile_inheritance()

作用:这个测试确认:权限档案之间的继承会正确合并网络设置,而且子档案可以覆盖父档案里同一个域名的决定。

数据流:进去的是 base 和 dev 两个档案:base 开启网络、允许 base.example.com、禁止 shared.example.com;dev 继承 base,允许本地绑定,并把 shared.example.com 改为允许,还新增 child.example.com。最后得到的网络配置同时包含父子设置,并且 shared.example.com 是允许。

调用关系:它覆盖 selected_network_from_tables 对自定义权限档案继承的核心行为,说明继承不是简单复制,而是父层先来、子层再覆盖。

调用图:外部调用 2 个(assert_eq!, from_str)。

config_from_layers_resolves_inherited_profiles_across_layers388–427 ↗
fn config_from_layers_resolves_inherited_profiles_across_layers()

作用:这个测试确认:权限档案的父子关系可以跨配置层工作。也就是说,父档案写在低层,子档案写在高层,最终仍然能继承成功。

数据流:进去的是两个 ConfigLayerEntry:低层定义 base 的允许域名,高层选择 dev,并让 dev 继承 base、再允许 child.example.com。它们组成 ConfigLayerStack 后交给 config_from_layers,最后网络允许列表同时包含 base.example.com 和 child.example.com。

调用关系:它把测试范围从单个 TOML 扩展到真实的多层配置栈,验证 ConfigLayerStack、config_from_layers 和权限继承逻辑能一起正常工作。

调用图:调用 2 个内部函数(new, new);外部调用 6 个(default, assert_eq!, default, empty, toml!, vec!)。

config_from_layers_normalizes_profile_network_domains_before_merging_layers430–464 ↗
fn config_from_layers_normalizes_profile_network_domains_before_merging_layers()

作用:这个测试确认:不同配置层里的域名在合并前会先统一大小写。这样 EXAMPLE.COM 和 example.com 会被当成同一个网站,而不是两条不同规则。

数据流:进去的是两层配置:低层把 example.com 设为禁止,高层把 EXAMPLE.COM 设为允许。config_from_layers 加载后,最终允许列表只有小写的 example.com,禁止列表为空。

调用关系:它验证 config_from_layers 在合并层之前会做域名规范化,避免因为大小写不同导致高优先级规则覆盖失败。

调用图:调用 2 个内部函数(new, new);外部调用 6 个(default, assert_eq!, default, empty, toml!, vec!)。

config_from_layers_uses_only_the_final_selected_profile_network467–497 ↗
fn config_from_layers_uses_only_the_final_selected_profile_network()

作用:这个测试确认:多层配置里如果高层最后把默认权限改成内置“:workspace”,那低层曾经选中的自定义 dev 网络规则不会继续生效。

数据流:进去的是两层配置:低层选择 dev 并允许 lower.example.com;高层把 default_permissions 改成 ":workspace"。加载最终配置后,允许列表和禁止列表都为空,表示只采用最后选中的档案。

调用关系:它保护 config_from_layers 的一个关键原则:不是每层选中的权限档案都叠加,而是先算出最终选择,再使用那个选择对应的网络配置。

调用图:调用 2 个内部函数(new, new);外部调用 6 个(default, assert_eq!, default, empty, toml!, vec!)。

trusted_constraints_use_only_the_final_selected_profile_network500–536 ↗
fn trusted_constraints_use_only_the_final_selected_profile_network()

作用:这个测试确认:生成“受信任配置层”的网络限制时,也只看最后选中的权限档案。受信任配置层通常来自系统或管理员管理的配置,安全含义更强。

数据流:进去的是两层受信任配置:系统层选择 dev 并允许 managed.example.com;更高的托管配置层把默认权限改成 ":workspace"。network_constraints_from_trusted_layers 生成结果后,允许和禁止域名都为空。

调用关系:它把“只看最终选中档案”的规则应用到 network_constraints_from_trusted_layers,确保管理员级限制不会因为旧层选择过某个档案而残留旧域名。

调用图:调用 3 个内部函数(new, new, try_from);外部调用 6 个(default, assert_eq!, default, from, toml!, vec!)。

trusted_constraints_normalize_profile_network_domains_before_merging_layers539–579 ↗
fn trusted_constraints_normalize_profile_network_domains_before_merging_layers()

作用:这个测试确认:受信任网络限制在合并不同配置层前,也会把域名统一成同一种写法。它防止管理员配置里大小写不同造成覆盖失效。

数据流:进去的是两层受信任配置:低层禁止 example.com,高层允许 EXAMPLE.COM。生成 NetworkProxyConstraints 后,允许列表是小写 example.com,禁止列表为空。

调用关系:它验证 network_constraints_from_trusted_layers 和普通 config_from_layers 一样,会先规范化域名,再按层级优先级合并。

调用图:调用 3 个内部函数(new, new, try_from);外部调用 6 个(default, assert_eq!, default, from, toml!, vec!)。

apply_network_constraints_skips_empty_domain_sides582–608 ↗
fn apply_network_constraints_skips_empty_domain_sides()

作用:这个测试确认:把网络配置转成网络限制时,如果只有允许域名,没有禁止域名,禁止列表会保持为空,而不是变成一个空数组。这个区别能表达“没有设置”和“设置为空”。

数据流:进去的是一段只允许 managed.example.com 的 TOML。测试选出网络配置,应用到默认 NetworkProxyConstraints,最后检查 allowed_domains 有这个域名,denied_domains 是 None。

调用关系:它专门检查 apply_network_constraints 的细节行为,避免输出多余的空列表,让后续判断能区分“没有这个限制”和“限制列表为空”。

调用图:外部调用 3 个(assert_eq!, default, from_str)。

apply_network_constraints_overlay_domain_entries611–658 ↗
fn apply_network_constraints_overlay_domain_entries()

作用:这个测试确认:多次把网络配置应用到同一个限制对象时,允许列表和禁止列表可以分别累积,不会互相清空。

数据流:进去的是两段配置:低层禁止 blocked.example.com,高层允许 api.example.com。测试分别选出两个网络表,依次应用到同一个 NetworkProxyConstraints,最后允许列表有 api.example.com,禁止列表有 blocked.example.com。

调用关系:它验证 apply_network_constraints 能像配置层叠加器一样工作:先来的限制保留,后来的限制补充或覆盖对应域名,为受信任配置层合并打基础。

调用图:外部调用 3 个(assert_eq!, default, from_str)。

core/src/network_policy_decision_tests.rs源码 ↗
testtest

这个文件不负责真正拦网络请求,而是用几个小例子来检查相关规则是否可靠。项目里有一套网络策略:有些访问可以放行,有些要问用户,有些直接拒绝。这里测试的重点是:只有来自决策器、并且结果是“询问”的请求,才会变成可给用户审批的上下文;HTTP、HTTPS、SOCKS5 这些协议名字要能正确识别;代理发来的旧名字或别名也要能读懂;用户选择“拒绝”后,要能转换成执行策略里的拒绝规则;真正被策略明确拒绝的网络请求,要给出清楚的提示。可以把它理解成给“门卫规则”做演练:模拟访客、检查门卫记录、确认提示语没有误导人。

函数细节6
network_approval_context_requires_ask_from_decider9–20 ↗
fn network_approval_context_requires_ask_from_decider()

作用:这个测试确认:不是“询问用户”的网络决定,不能被包装成用户审批请求。这样可以避免已经被拒绝的访问又被错误地拿去问用户。

数据流:进去的是一个手工造出来的网络决策数据:来源是决策器,但决定是拒绝。测试把它交给转换函数,期望结果是空,也就是“不生成审批上下文”。最后用断言检查实际结果确实为空。

调用关系:测试运行器会在测试阶段调用它。它主要验证上层的 network_approval_context_from_payload 规则,并把结果交给 assert_eq! 做对比;如果以后有人放宽了这个规则,这个测试会立刻失败提醒。

调用图:外部调用 1 个(assert_eq!)。

network_approval_context_maps_http_https_and_socks_protocols23–103 ↗
fn network_approval_context_maps_http_https_and_socks_protocols()

作用:这个测试确认:当网络请求需要问用户时,HTTP、HTTPS、SOCKS5 TCP、SOCKS5 UDP 这些协议都能被正确带进审批信息里。它防止系统把协议弄丢或弄错。

数据流:进去的是多组模拟的网络决策数据,每组都是“需要询问”、来自决策器,并带有主机名和协议。测试逐个调用转换函数,检查出来的审批上下文里,主机还是 example.com,协议也和输入一致。

调用关系:测试运行器会调用它来覆盖多种协议场景。它围绕 network_approval_context_from_payload 做验证,并用 assert_eq! 比较预期结果;这些场景保证用户看到的审批请求和真实网络请求匹配。

调用图:外部调用 1 个(assert_eq!)。

network_policy_decision_payload_deserializes_proxy_protocol_aliases106–132 ↗
fn network_policy_decision_payload_deserializes_proxy_protocol_aliases()

作用:这个测试确认:从 JSON 文本里读取网络决策时,一些代理协议的别名也能被识别成标准协议。这样不同来源写法略有差别时,系统不会因为名字不同就看不懂。

数据流:进去的是两段 JSON 字符串,协议字段分别写成 https_connect 和 http-connect。测试用 serde_json::from_str 把文本解析成结构化数据,再检查解析后的协议都变成标准的 HTTPS。

调用关系:测试运行器在测试阶段调用它。它把 JSON 解析工作交给 serde_json::from_str,再用 assert_eq! 检查结果;它保护的是外部代理和核心代码之间的“翻译层”。

调用图:外部调用 2 个(assert_eq!, from_str)。

execpolicy_network_rule_amendment_maps_protocol_action_and_justification135–153 ↗
fn execpolicy_network_rule_amendment_maps_protocol_action_and_justification()

作用:这个测试确认:用户或策略给出的网络规则修改,能正确变成执行层真正使用的规则。特别是协议、拒绝动作和说明文字都要对。

数据流:进去的是一个“拒绝 example.com”的规则修改,以及一个说明该请求走 SOCKS5 UDP 协议的审批上下文。测试调用转换函数,期望得到执行策略里的规则:协议是 Socks5Udp,决定是禁止,并生成一句明确的拒绝理由。

调用关系:测试运行器调用它来检查 execpolicy_network_rule_amendment 的转换结果。它最后用 assert_eq! 对比完整结构,确保前端审批里的选择不会在送到执行策略时变形。

调用图:外部调用 1 个(assert_eq!)。

denied_network_policy_message_requires_deny_decision156–170 ↗
fn denied_network_policy_message_requires_deny_decision()

作用:这个测试确认:只有真正被“拒绝”的网络请求,才会生成拒绝提示。若当前决定只是“询问”,就不应该提前告诉用户它已经被拦死了。

数据流:进去的是一个模拟的被拦请求,里面的 decision 写的是 ask,而不是 deny。测试把它交给提示生成函数,期望输出为空,表示暂时不生成“已拒绝”的说明。

调用关系:测试运行器会调用它。它验证 denied_network_policy_message 的前置条件,并通过 assert_eq! 检查结果;这能避免界面在还可以审批时误报成不可批准。

调用图:外部调用 1 个(assert_eq!)。

denied_network_policy_message_for_denylist_block_is_explicit173–192 ↗
fn denied_network_policy_message_for_denylist_block_is_explicit()

作用:这个测试确认:当网络访问被策略明确列入拒绝名单时,系统会给出直白的提示,说明这个域名不能在当前提示里批准。这样用户不会以为只是普通确认框。

数据流:进去的是一个模拟的被拦请求:主机是 example.com,原因是 denied,决定是 deny,来源是 baseline_policy。测试调用提示生成函数,期望得到一段固定文案,说明该域名被策略明确禁止,不能从这个提示里放行。

调用关系:测试运行器在测试阶段调用它。它把被拦请求交给 denied_network_policy_message,再用 assert_eq! 检查提示文字;它保护的是用户最终看到的错误说明,避免含糊或误导。

调用图:外部调用 1 个(assert_eq!)。

sandboxing/src/policy_transforms_tests.rs源码 ↗
testtest run

沙箱可以理解成给程序画的一圈安全围栏:哪些文件能读写,网络能不能用,都要说清楚。这个测试文件就是在反复验证这些围栏规则。它会构造很多权限组合,比如“根目录可写但某个目录禁止”“只允许写当前项目目录”“禁止匹配 .env 的文件”“网络开启或受限”等,然后检查几个核心函数算出的结果是否符合预期。这里特别关注一些容易出错的细节:符号链接,也就是像快捷方式一样指向真实目录的路径,不能被错误改写;空权限要被清理掉;通配符规则只能用于拒绝读取;相对当前目录的规则要在需要复用时变成具体路径。没有这些测试,权限合并和裁剪一旦出错,轻则功能不可用,重则可能让程序碰到不该碰的敏感文件。

函数细节26
full_access_restricted_policy_skips_platform_sandbox_when_network_is_enabled28–44 ↗
fn full_access_restricted_policy_skips_platform_sandbox_when_network_is_enabled()

作用:这个测试确认:如果文件系统已经等同于全盘可写,并且网络也允许使用,就不必再启动平台级沙箱。平台级沙箱是操作系统提供的更底层隔离机制。

数据流:进去的是一条“根目录可写”的受限策略和“网络开启”的设置 → 测试把它们交给 should_require_platform_sandbox 判断 → 出来的期望是 false,也就是不需要额外启用平台沙箱。

调用关系:测试运行器会执行它。它主要检查 should_require_platform_sandbox 在最宽松场景下不要做多余限制。

调用图:调用 1 个内部函数(restricted);外部调用 2 个(assert_eq!, vec!)。

root_write_policy_with_carveouts_still_uses_platform_sandbox47–73 ↗
fn root_write_policy_with_carveouts_still_uses_platform_sandbox()

作用:这个测试确认:即使整体上允许写根目录,只要里面有一个明确禁止的例外目录,仍然需要平台沙箱来真正挡住它。

数据流:进去的是当前目录下一个 blocked 路径,以及“根目录可写但 blocked 禁止”的策略 → 测试调用 should_require_platform_sandbox → 出来的期望是 true,因为有例外规则需要系统级隔离来兜底。

调用关系:测试运行器会执行它。它先用 resolve_path_against_base 准备绝对路径,再检查 should_require_platform_sandbox 对“全开但有洞”的情况是否谨慎。

调用图:调用 2 个内部函数(restricted, resolve_path_against_base);外部调用 3 个(assert_eq!, current_dir, vec!)。

full_access_restricted_policy_still_uses_platform_sandbox_for_restricted_network76–92 ↗
fn full_access_restricted_policy_still_uses_platform_sandbox_for_restricted_network()

作用:这个测试确认:就算文件系统权限很宽,如果网络策略是受限的,仍然要用平台沙箱。因为网络限制也需要被可靠执行。

数据流:进去的是“根目录可写”的文件策略和“网络受限”的网络策略 → 判断函数计算是否需要平台沙箱 → 出来的期望是 true。

调用关系:测试运行器会执行它。它覆盖 should_require_platform_sandbox 的网络限制分支,避免只看文件权限而忽略网络。

调用图:调用 1 个内部函数(restricted);外部调用 2 个(assert_eq!, vec!)。

normalize_additional_permissions_preserves_network95–125 ↗
fn normalize_additional_permissions_preserves_network()

作用:这个测试确认:整理额外权限时,网络开关不会被意外丢掉。额外权限是用户或上层流程临时追加给沙箱的权限。

数据流:进去的是一个包含网络启用、读写同一路径的权限配置 → normalize_additional_permissions 对路径和空字段做整理 → 出来的权限仍然保留 network.enabled=true,并保留原来的读写路径。

调用关系:测试运行器会执行它。它使用临时目录和 canonicalize 得到稳定路径,然后验证 normalize_additional_permissions 不会误删网络配置。

调用图:调用 2 个内部函数(from_read_write_roots, from_absolute_path);外部调用 5 个(new, assert_eq!, canonicalize, normalize_additional_permissions, vec!)。

normalize_additional_permissions_preserves_symlinked_write_paths129–158 ↗
fn normalize_additional_permissions_preserves_symlinked_write_paths()

作用:这个测试确认:如果用户给的是符号链接下面的写入路径,整理权限时要保留这个链接写法,而不是偷偷换成真实目录路径。

数据流:进去的是一个临时真实目录、一个指向它的链接目录,以及链接目录下的 write 路径 → 测试调用 normalize_additional_permissions → 出来的权限仍然写着链接路径 link/write。

调用关系:测试运行器在 Unix 系统上执行它。它先调用 symlink_dir 建好快捷方式式的目录,再检查 normalize_additional_permissions 对符号链接是否尊重用户输入。

调用图:调用 3 个内部函数(from_read_write_roots, symlink_dir, from_absolute_path);外部调用 6 个(default, new, assert_eq!, create_dir_all, normalize_additional_permissions, vec!)。

normalize_additional_permissions_rejects_glob_read_grants161–180 ↗
fn normalize_additional_permissions_rejects_glob_read_grants()

作用:这个测试确认:用通配符给读取权限是不支持的,必须报错。通配符就是像 **/*.env 这样一次匹配很多文件名的规则。

数据流:进去的是一条“允许读取所有 .env 文件”的通配符规则 → normalize_additional_permissions 尝试整理它 → 出来的是错误信息,说明通配符文件权限只支持拒绝读取。

调用关系:测试运行器会执行它。它专门检查 normalize_additional_permissions 的安全边界,防止通配符被用来扩大读取范围。

调用图:外部调用 4 个(default, assert_eq!, normalize_additional_permissions, vec!)。

normalize_additional_permissions_preserves_deny_globs183–213 ↗
fn normalize_additional_permissions_preserves_deny_globs()

作用:这个测试确认:通配符拒绝规则是允许的,并且整理后要原样保留。比如禁止碰所有 .env 文件。

数据流:进去的是一条“拒绝匹配 **/*.env”的规则和扫描深度 2 → normalize_additional_permissions 整理配置 → 出来的权限仍包含同一条拒绝规则和同样的深度限制。

调用关系:测试运行器会执行它。它和拒绝读取测试配套,说明 normalize_additional_permissions 不是一概拒绝通配符,而是只允许安全的拒绝规则。

调用图:外部调用 5 个(default, assert_eq!, new, normalize_additional_permissions, vec!)。

normalize_additional_permissions_drops_empty_nested_profiles216–224 ↗
fn normalize_additional_permissions_drops_empty_nested_profiles()

作用:这个测试确认:空的网络配置和空的文件系统配置会被清理掉,避免后面把“什么都没说”误认为“明确给了权限”。

数据流:进去的是 network.enabled 为空、file_system 也为空的权限结构 → normalize_additional_permissions 做整理 → 出来的是完全默认的空权限。

调用关系:测试运行器会执行它。它检查 normalize_additional_permissions 的收尾清理行为,让后续权限合并更简单、更不容易误判。

调用图:调用 1 个内部函数(default);外部调用 2 个(assert_eq!, normalize_additional_permissions)。

intersect_permission_profiles_preserves_explicit_empty_requested_reads227–246 ↗
fn intersect_permission_profiles_preserves_explicit_empty_requested_reads()

作用:这个测试确认:当请求里明确写了“读取路径列表为空”,并且授予方也同意同样配置时,这个明确的空列表要保留下来。

数据流:进去的是一个请求权限:读列表为空、写某个临时目录;授予权限与请求相同 → intersect_permission_profiles 求两者共同允许的部分 → 出来的结果仍等于原请求。

调用关系:测试运行器会执行它。它检查 intersect_permission_profiles 对“空但有意义”的字段不要粗暴删除。

调用图:调用 2 个内部函数(from_read_write_roots, from_absolute_path);外部调用 5 个(default, new, assert_eq!, canonicalize, vec!)。

intersect_permission_profiles_drops_ungranted_nonempty_path_requests249–267 ↗
fn intersect_permission_profiles_drops_ungranted_nonempty_path_requests()

作用:这个测试确认:请求了某个具体读取路径,但授予方没有给这个权限时,结果必须删掉这个请求。

数据流:进去的是“想读某个临时目录”的请求,以及完全空的授予权限 → intersect_permission_profiles 求交集 → 出来的是默认空权限。

调用关系:测试运行器会执行它。它验证 intersect_permission_profiles 的基本原则:只留下请求和授予双方都允许的内容。

调用图:调用 2 个内部函数(from_read_write_roots, from_absolute_path);外部调用 5 个(default, new, assert_eq!, canonicalize, vec!)。

intersect_permission_profiles_drops_explicit_empty_reads_without_grant270–288 ↗
fn intersect_permission_profiles_drops_explicit_empty_reads_without_grant()

作用:这个测试确认:即使请求里的读取列表是明确空列表,只要授予方完全没有文件系统授权,也不能保留它。

数据流:进去的是“读列表为空、写一个目录”的请求和空授予权限 → intersect_permission_profiles 计算共同部分 → 出来的是默认空权限。

调用关系:测试运行器会执行它。它补充验证 intersect_permission_profiles 不会因为字段存在就把未授权的文件权限留下。

调用图:调用 2 个内部函数(from_read_write_roots, from_absolute_path);外部调用 5 个(default, new, assert_eq!, canonicalize, vec!)。

intersect_permission_profiles_accepts_child_path_granted_for_requested_cwd291–322 ↗
fn intersect_permission_profiles_accepts_child_path_granted_for_requested_cwd()

作用:这个测试确认:请求写当前项目目录时,如果实际授予的是其中一个子目录,那么结果可以接受这个更窄的子目录权限。

数据流:进去的是“写项目根目录”的请求、以及“只写项目下 child 子目录”的授予权限 → intersect_permission_profiles 以当前目录为参照做匹配 → 出来的结果等于授予的子目录权限。

调用关系:测试运行器会执行它。它检查 intersect_permission_profiles 能把宽泛请求收窄到真正被批准的范围。

调用图:调用 2 个内部函数(from_read_write_roots, from_absolute_path);外部调用 5 个(default, new, assert_eq!, canonicalize, vec!)。

intersect_permission_profiles_materializes_cwd_grant_for_reuse325–374 ↗
fn intersect_permission_profiles_materializes_cwd_grant_for_reuse()

作用:这个测试确认:像“当前项目目录”这种相对说法,在求交集后会变成当时的具体路径,方便以后复用时不受新当前目录影响。

数据流:进去的是请求和授予都写着“项目根目录可写”,以及 request-cwd 作为当时当前目录 → intersect_permission_profiles 把它变成具体的 request-cwd 路径 → 后面再拿 later-cwd 的子目录来匹配时,结果为空。

调用关系:测试运行器会执行它。它直接调用 intersect_permission_profiles 两次,先生成可复用授权,再确认这个授权不会漂移到另一个工作目录。

调用图:调用 1 个内部函数(from_absolute_path);外部调用 5 个(default, new, assert_eq!, intersect_permission_profiles, vec!)。

intersect_permission_profiles_deduplicates_materialized_grants377–410 ↗
fn intersect_permission_profiles_deduplicates_materialized_grants()

作用:这个测试确认:如果同一个目录既通过“项目根目录”表达,又通过具体路径表达,求交集后只保留一份,避免重复规则。

数据流:进去的是两条都指向同一个 cwd 的写权限规则 → intersect_permission_profiles 把特殊路径转成具体路径并去重 → 出来的是只含一个 cwd 写权限的配置。

调用关系:测试运行器会执行它。它检查 intersect_permission_profiles 在规则标准化后会清理重复项。

调用图:调用 1 个内部函数(from_absolute_path);外部调用 4 个(default, new, assert_eq!, vec!)。

intersect_permission_profiles_materializes_cwd_deny_entries413–459 ↗
fn intersect_permission_profiles_materializes_cwd_deny_entries()

作用:这个测试确认:禁止当前项目目录这样的规则,也会在求交集后变成具体路径。这样拒绝规则不会因为以后当前目录变化而变味。

数据流:进去的是“根目录可写,但项目根目录禁止”的权限,以及 request-cwd → intersect_permission_profiles 把项目根目录的拒绝项落到 request-cwd 这个具体路径 → 出来的是根目录写入加 request-cwd 禁止。

调用关系:测试运行器会执行它。它覆盖 intersect_permission_profiles 对 Deny,也就是拒绝权限的路径固定行为。

调用图:调用 1 个内部函数(from_absolute_path);外部调用 4 个(default, new, assert_eq!, vec!)。

intersect_permission_profiles_drops_deny_entries_without_filesystem_grants462–500 ↗
fn intersect_permission_profiles_drops_deny_entries_without_filesystem_grants()

作用:这个测试确认:如果最后没有任何文件系统授权,只剩拒绝规则也没意义,应该丢掉。因为拒绝是附着在权限范围上的“例外”。

数据流:进去的是请求:网络开启、项目目录可写但 secret 禁止;授予方只给网络开启 → intersect_permission_profiles 求交集 → 出来的结果只保留网络权限,文件系统部分被清空。

调用关系:测试运行器会执行它。它验证 intersect_permission_profiles 不会留下孤零零的文件拒绝规则。

调用图:调用 1 个内部函数(from_absolute_path);外部调用 5 个(default, new, assert_eq!, canonicalize, vec!)。

intersect_permission_profiles_rejects_concrete_grants_matched_by_requested_deny_globs503–542 ↗
fn intersect_permission_profiles_rejects_concrete_grants_matched_by_requested_deny_globs()

作用:这个测试确认:如果请求里明确禁止 .env 文件,那么授予方给出的具体 .env 文件写权限也必须被拒掉。

数据流:进去的是“项目目录可写,但 **/*.env 禁止”的请求,以及“写 cwd/.env”的授予权限 → intersect_permission_profiles 检查具体路径是否命中拒绝通配符 → 出来的是默认空权限。

调用关系:测试运行器会执行它。它检查 intersect_permission_profiles 在处理通配符拒绝规则时能挡住具体文件授权。

调用图:调用 2 个内部函数(from_read_write_roots, from_absolute_path);外部调用 6 个(default, new, assert_eq!, canonicalize, new, vec!)。

intersect_permission_profiles_materializes_relative_deny_globs_for_reuse545–611 ↗
fn intersect_permission_profiles_materializes_relative_deny_globs_for_reuse()

作用:这个测试确认:相对当前目录的通配符拒绝规则,会被改写成带具体目录前缀的规则,避免以后换目录后误伤或漏挡。

数据流:进去的是“当前项目可写,并拒绝 **/*.env”的权限和 request-cwd → intersect_permission_profiles 把写权限变成 request-cwd,把拒绝通配符变成 request-cwd/**/*.env → 后续用 later-cwd/token.env 匹配时得不到授权。

调用关系:测试运行器会执行它。它先调用 intersect_permission_profiles 生成固定后的授权,再用第二次调用验证这份授权不能被另一个当前目录复用错。

调用图:调用 1 个内部函数(from_absolute_path);外部调用 7 个(default, new, project_roots, assert_eq!, new, intersect_permission_profiles, vec!)。

intersect_permission_profiles_drops_broader_cwd_grant_for_requested_child_path614–645 ↗
fn intersect_permission_profiles_drops_broader_cwd_grant_for_requested_child_path()

作用:这个测试确认:请求只要某个子目录时,授予方给的“整个当前项目目录”不会被自动当作批准。这样避免把结果放得比请求更宽。

数据流:进去的是“想写 cwd/child”的请求,以及“项目根目录可写”的授予权限 → intersect_permission_profiles 计算共同且可复用的权限 → 出来的是空权限。

调用关系:测试运行器会执行它。它检查 intersect_permission_profiles 不会把宽泛的特殊授权直接拿来满足较窄的具体请求。

调用图:调用 2 个内部函数(from_read_write_roots, from_absolute_path);外部调用 5 个(default, new, assert_eq!, canonicalize, vec!)。

intersect_permission_profiles_uses_granted_bounded_glob_scan_depth648–700 ↗
fn intersect_permission_profiles_uses_granted_bounded_glob_scan_depth()

作用:这个测试确认:请求和授予都带通配符拒绝规则时,最终使用授予方给的有限扫描深度。扫描深度就是查找匹配文件时最多往下翻几层目录。

数据流:进去的是请求深度 2、授予深度 4,规则都是根目录可写并拒绝 **/*.env → intersect_permission_profiles 把相对通配符变成当前目录下的绝对模式 → 出来的结果使用深度 4。

调用关系:测试运行器会执行它。它验证 intersect_permission_profiles 在通配符拒绝规则上尊重授予方的扫描范围。

调用图:外部调用 5 个(default, assert_eq!, current_dir, new, vec!)。

intersect_permission_profiles_uses_granted_unbounded_glob_scan_depth703–755 ↗
fn intersect_permission_profiles_uses_granted_unbounded_glob_scan_depth()

作用:这个测试确认:如果授予方的通配符扫描深度是不设上限,最终结果也应是不设上限。

数据流:进去的是请求深度 2、授予深度为空,也就是不限制深度;两边都有根目录可写和拒绝 **/*.env → intersect_permission_profiles 生成当前目录下的绝对拒绝模式 → 出来的 glob_scan_max_depth 是 None。

调用关系:测试运行器会执行它。它和有限深度测试配套,覆盖 intersect_permission_profiles 对“无限制扫描”的处理。

调用图:外部调用 5 个(default, assert_eq!, current_dir, new, vec!)。

merge_file_system_policy_with_additional_permissions_preserves_unreadable_roots758–801 ↗
fn merge_file_system_policy_with_additional_permissions_preserves_unreadable_roots()

作用:这个测试确认:把额外文件权限合进基础沙箱策略时,原来禁止访问的目录不能被覆盖掉,同时新增允许路径也要加进去。

数据流:进去的是基础策略:根目录可读但 denied 路径禁止;额外权限:allowed 路径可读 → merge_file_system_policy_with_additional_permissions 合并两边 → 出来的策略同时包含 denied 禁止项和 allowed 读取项。

调用关系:测试运行器会执行它。它直接检查 merge_file_system_policy_with_additional_permissions 不会因为加权限而丢掉安全例外。

调用图:调用 3 个内部函数(from_read_write_roots, restricted, from_absolute_path);外部调用 6 个(new, new, assert_eq!, canonicalize, merge_file_system_policy_with_additional_permissions, vec!)。

merge_file_system_policy_with_additional_permissions_carries_bounded_glob_scan_depth804–837 ↗
fn merge_file_system_policy_with_additional_permissions_carries_bounded_glob_scan_depth()

作用:这个测试确认:合并额外文件权限时,通配符拒绝规则的扫描深度也会带到最终沙箱策略里。

数据流:进去的是根目录可写的基础策略,以及“拒绝 **/*.env、扫描深度 2”的额外权限 → merge_file_system_policy_with_additional_permissions 合并规则 → 出来的策略包含这条拒绝规则,并把 glob_scan_max_depth 设为 2。

调用关系:测试运行器会执行它。它覆盖 merge_file_system_policy_with_additional_permissions 对通配符元信息的保留。

调用图:调用 1 个内部函数(restricted);外部调用 4 个(assert_eq!, new, merge_file_system_policy_with_additional_permissions, vec!)。

effective_file_system_sandbox_policy_returns_base_policy_without_additional_permissions840–864 ↗
fn effective_file_system_sandbox_policy_returns_base_policy_without_additional_permissions()

作用:这个测试确认:如果没有传入额外权限,最终生效的文件系统沙箱策略就应该原样等于基础策略。

数据流:进去的是一个基础策略:根目录可读、某个 denied 路径禁止;额外权限为 None → effective_file_system_sandbox_policy 做判断 → 出来的策略和基础策略完全一样。

调用关系:测试运行器会执行它。它检查 effective_file_system_sandbox_policy 的最简单分支:没有附加内容时不要乱改。

调用图:调用 2 个内部函数(restricted, from_absolute_path);外部调用 5 个(new, assert_eq!, canonicalize, effective_file_system_sandbox_policy, vec!)。

effective_file_system_sandbox_policy_merges_additional_write_roots867–914 ↗
fn effective_file_system_sandbox_policy_merges_additional_write_roots()

作用:这个测试确认:有额外写入目录时,最终生效策略会把它加进去,同时保留原来的禁止目录。

数据流:进去的是基础策略:根目录可读、denied 禁止;额外权限:allowed 可写 → effective_file_system_sandbox_policy 生成最终策略 → 出来的策略同时包含 denied 禁止项和 allowed 写入项。

调用关系:测试运行器会执行它。它从更高一层验证 effective_file_system_sandbox_policy 会在需要时调用合并逻辑,把基础策略和额外权限拼成真正使用的策略。

调用图:调用 3 个内部函数(from_read_write_roots, restricted, from_absolute_path);外部调用 6 个(default, new, assert_eq!, canonicalize, effective_file_system_sandbox_policy, vec!)。

sandboxing/src/bwrap_tests.rs源码 ↗
testtest run

这个文件不是真正运行沙箱的代码,而是给沙箱相关代码做“体检”的测试。它会临时写出一些假的 bwrap 脚本,让这些脚本模拟真实世界里的各种情况:比如 bwrap 根本不存在、系统不允许创建用户命名空间(可以理解成系统不让程序自己隔出一个小房间)、bwrap 卡住不退出、或者 PATH 搜索路径里先出现了不能执行的文件。测试还检查 WSL1 的识别逻辑,避免把 WSL2 或普通 Linux 误判成 WSL1。文件底部的几个辅助函数负责制造这些假的可执行文件,就像搭临时舞台道具一样,让上面的测试能稳定复现问题,而不依赖开发者机器上真实安装了什么。

函数细节13
system_bwrap_warning_reports_missing_system_bwrap10–15 ↗
fn system_bwrap_warning_reports_missing_system_bwrap()

作用:检查当系统里找不到 bwrap 时,代码会不会给出“缺少 bwrap”的提醒。这样用户不会遇到沙箱失败却不知道少装了什么。

数据流:进去的是一个空的 bwrap 路径,也就是明确告诉被测代码“没找到 bwrap” → 测试调用警告生成逻辑,看它怎么回应 → 出来应该是一段固定的缺失提醒文字;如果不是,断言会让测试失败。

调用关系:这是一个最简单的入口场景测试,只用断言宏比对结果,不需要制造假文件。它验证的是沙箱启动前的提示逻辑是否对缺失依赖给出正确说明。

调用图:外部调用 1 个(assert_eq!)。

system_bwrap_warning_reports_user_namespace_failures18–34 ↗
fn system_bwrap_warning_reports_user_namespace_failures()

作用:检查 bwrap 运行失败且错误内容表示“用户命名空间不可用”时,代码会不会给出正确警告。用户命名空间可以理解成系统允许普通用户开一个隔离小房间的能力。

数据流:进去的是一组已知的失败提示文字 → 每条文字都会被写进一个假的 bwrap 脚本,脚本运行时把这段错误打印出来并退出失败 → 测试把假 bwrap 的路径交给被测逻辑,期望得到“用户命名空间不可用”的警告。

调用关系:这个测试通过 write_fake_bwrap 制造不同表现的假 bwrap,再用断言检查结果。它覆盖的是真实系统里 bwrap 存在但内核或配置不允许沙箱工作的情况。

调用图:调用 1 个内部函数(write_fake_bwrap);外部调用 2 个(assert_eq!, format!)。

system_bwrap_warning_skips_unrelated_bwrap_failures37–47 ↗
fn system_bwrap_warning_skips_unrelated_bwrap_failures()

作用:检查 bwrap 如果因为别的原因失败,代码不会乱报“用户命名空间有问题”。这能避免把普通错误误导成系统权限问题。

数据流:进去的是一个假的 bwrap 脚本,它输出一个无关错误,比如未知参数 → 被测逻辑读取这个失败表现 → 出来应该是没有警告,也就是 None,表示这类错误不该套用特殊提示。

调用关系:这个测试同样靠 write_fake_bwrap 做一个临时脚本,然后用断言确认警告逻辑足够克制,只在看见特定错误时才提示。

调用图:调用 1 个内部函数(write_fake_bwrap);外部调用 1 个(assert_eq!)。

system_bwrap_probe_times_out_without_reporting_a_warning50–65 ↗
fn system_bwrap_probe_times_out_without_reporting_a_warning()

作用:检查探测 bwrap 能力时,如果 bwrap 卡住太久,代码会很快放弃,而不是让测试或程序一直等下去。这里的重点是“不要被外部程序拖死”。

数据流:进去的是一个会 sleep 1 秒的假 bwrap,以及一个只有 10 毫秒的超时时间 → 被测探测逻辑启动它,但必须在超时后尽快返回 → 出来被当作“不要报告问题”的结果,同时测试确认总耗时小于 500 毫秒。

调用关系:这个测试先用 write_fake_bwrap 造出慢脚本,再用 now 记录开始时间,最后通过断言检查返回值和耗时。它保护的是启动或检查阶段的响应速度。

调用图:调用 1 个内部函数(write_fake_bwrap);外部调用 2 个(now, assert!)。

system_bwrap_probe_does_not_wait_for_descendants_holding_stderr_open68–84 ↗
fn system_bwrap_probe_does_not_wait_for_descendants_holding_stderr_open()

作用:检查 bwrap 失败后,如果它启动的子进程还占着错误输出管道,探测逻辑不会傻等子进程结束。否则一个后台进程就可能把程序卡住。

数据流:进去的是一个假 bwrap:先输出“没权限创建命名空间”,再把 sleep 放到后台,然后自己失败退出 → 被测逻辑应该读到关键错误并尽快判断失败 → 出来是 false,表示没有用户命名空间访问能力,同时耗时要很短。

调用关系:这个测试用 write_fake_bwrap 制造一个比较刁钻的脚本,再用 now 和断言确认探测逻辑不会被后台后代进程拖住。它针对的是进程和输出管道上的边角问题。

调用图:调用 1 个内部函数(write_fake_bwrap);外部调用 2 个(now, assert!)。

detects_wsl1_proc_version_formats87–97 ↗
fn detects_wsl1_proc_version_formats()

作用:检查代码能识别几种 WSL1 的 Linux 版本字符串。WSL 是 Windows 上运行 Linux 的环境,WSL1 和普通 Linux 在沙箱能力上可能不一样。

数据流:进去的是几条看起来不同、但都代表 WSL1 的 /proc/version 文本 → 被测识别函数逐条判断 → 出来都应该是 true,说明这些格式都会被认出来。

调用关系:这个测试只用断言检查字符串识别结果。它保障的是环境探测逻辑,防止因为版本字符串格式变化就漏判 WSL1。

调用图:外部调用 1 个(assert!)。

does_not_treat_wsl2_or_native_linux_as_wsl1100–114 ↗
fn does_not_treat_wsl2_or_native_linux_as_wsl1()

作用:检查代码不会把 WSL2、普通 Linux,或者未来看起来相似的字符串误认成 WSL1。误判环境可能会导致沙箱策略走错路。

数据流:进去的是几条不该算作 WSL1 的版本文本 → 被测识别函数分别判断 → 出来都应该是 false,表示这些环境不能套用 WSL1 的特殊处理。

调用关系:这个测试和 detects_wsl1_proc_version_formats 是一正一反。前者确认该认的能认出,后者确认不该认的不会误认。

调用图:外部调用 1 个(assert!)。

finds_first_executable_bwrap_in_joined_search_path117–133 ↗
fn finds_first_executable_bwrap_in_joined_search_path()

作用:检查在 PATH 搜索路径里寻找 bwrap 时,代码会跳过不能执行的文件,并找到第一个真正可执行的 bwrap。PATH 可以理解成系统找命令时依次查看的文件夹列表。

数据流:进去的是几个临时目录:第一个目录里放一个叫 bwrap 但不能执行的文件,第二个目录里放一个可执行的假 bwrap → 测试把这些目录拼成搜索路径交给查找逻辑 → 出来应该是第二个目录里那个真正可执行文件的规范路径。

调用关系:这个测试用 tempdir 和 create_dir_all 搭临时目录,用 write_named_fake_bwrap_in 放置可执行 bwrap,再用 join_paths 组成搜索路径,最后断言查找结果正确。

调用图:调用 1 个内部函数(write_named_fake_bwrap_in);外部调用 5 个(assert_eq!, join_paths, create_dir_all, write, tempdir)。

skips_workspace_local_bwrap_in_joined_search_path136–150 ↗
fn skips_workspace_local_bwrap_in_joined_search_path()

作用:检查代码会跳过当前工作区里的本地 bwrap,转而使用更可信的系统路径里的 bwrap。这样可以避免项目目录里意外或恶意的同名文件被当成系统工具执行。

数据流:进去的是一个当前工作目录和一个可信目录;两个地方都放了名叫 bwrap 的可执行文件 → 搜索路径先放当前目录,再放可信目录 → 查找逻辑应该跳过当前工作区里的那个,输出可信目录中的 bwrap 路径。

调用关系:这个测试通过 tempdir 建临时环境,通过 write_named_fake_bwrap_in 分别放两个假 bwrap,再用断言确认查找逻辑不会被工作区里的同名文件骗到。

调用图:调用 1 个内部函数(write_named_fake_bwrap_in);外部调用 4 个(assert_eq!, join_paths, create_dir_all, tempdir)。

root_cwd_does_not_hide_system_bwrap_candidates153–164 ↗
fn root_cwd_does_not_hide_system_bwrap_candidates()

作用:检查当当前目录是根目录 / 时,代码不会因为“跳过工作区本地 bwrap”的规则而误伤正常的系统 bwrap 候选。根目录是特殊情况,不能把所有系统路径都当成本地工作区。

数据流:进去的是一个临时 bin 目录,里面有可执行的 bwrap;当前目录参数被设成 / → 查找逻辑在搜索路径里寻找 bwrap → 出来应该仍然能找到临时 bin 目录里的那个文件。

调用关系:这个测试用 write_named_fake_bwrap_in 放置可执行文件,用 join_paths 构造搜索路径,再断言根目录这个特殊当前目录不会让查找逻辑过度过滤。

调用图:调用 1 个内部函数(write_named_fake_bwrap_in);外部调用 4 个(assert_eq!, join_paths, create_dir_all, tempdir)。

write_fake_bwrap166–171 ↗
fn write_fake_bwrap(contents: &str) -> tempfile::TempPath

作用:这是测试用的小工具,用来快速写出一个临时的假 bwrap 脚本。上面的测试靠它模拟 bwrap 的各种成功、失败、卡住和输出错误的行为。

数据流:进去的是脚本文本内容 → 它先尝试拿当前工作目录当作放置位置,如果拿不到就用 . 作为兜底 → 然后把具体写文件和加执行权限的工作交给 write_fake_bwrap_in,出来是一个临时文件路径,测试结束后可自动清理。

调用关系:它被多个 bwrap 警告和探测测试调用,是这些测试的道具工厂。它自己不直接写文件,而是把实际创建工作交给 write_fake_bwrap_in。

调用图:调用 1 个内部函数(write_fake_bwrap_in);被 4 处调用(system_bwrap_probe_does_not_wait_for_descendants_holding_stderr_open, system_bwrap_probe_times_out_without_reporting_a_warning, system_bwrap_warning_reports_user_namespace_failures, system_bwrap_warning_skips_unrelated_bwrap_failures);外部调用 1 个(current_dir)。

write_fake_bwrap_in173–190 ↗
fn write_fake_bwrap_in(dir: &Path, contents: &str) -> tempfile::TempPath

作用:这是实际制造假可执行脚本的地方。它把一段文本写成文件,并给文件加上可执行权限,让测试能像运行真实 bwrap 一样运行它。

数据流:进去的是目标目录和脚本内容 → 它优先在指定目录创建临时文件,如果失败就退回系统临时目录;然后关闭写入句柄、写入脚本内容、设置 755 权限,也就是让所有人可执行 → 出来是这个临时脚本的路径。

调用关系:它由 write_fake_bwrap 调用,是更底层的文件制造工具。代码里特意避开一些测试环境把系统临时目录挂成不可执行的问题,也避开 Linux 不允许执行仍打开写入的文件的问题。

调用图:被 1 处调用(write_fake_bwrap);外部调用 4 个(new_in, from_mode, set_permissions, write)。

write_named_fake_bwrap_in192–201 ↗
fn write_named_fake_bwrap_in(dir: &Path) -> PathBuf

作用:这个辅助函数在指定目录里创建一个名字就叫 bwrap 的可执行假文件。它主要服务于测试 PATH 查找逻辑,因为 PATH 查找就是按名字找 bwrap。

数据流:进去的是一个目录路径 → 它在这个目录下写入名为 bwrap 的小脚本,设置可执行权限,然后把路径转换成规范的绝对路径 → 出来是这个假 bwrap 的最终路径。

调用关系:它被三个搜索路径相关测试调用,用来准备“某个目录里有一个可执行 bwrap”的场景。和 write_fake_bwrap_in 不同,它固定文件名为 bwrap,更适合模拟系统 PATH 查命令。

调用图:被 3 处调用(finds_first_executable_bwrap_in_joined_search_path, root_cwd_does_not_hide_system_bwrap_candidates, skips_workspace_local_bwrap_in_joined_search_path);外部调用 5 个(join, from_mode, canonicalize, set_permissions, write)。

core/src/windows_sandbox_read_grants_tests.rs源码 ↗
testtest

这个文件是在给一个安全功能做“踩刹车测试”。被测试的功能是 grant_read_root_non_elevated:它会在非管理员权限下,给 Windows 沙盒增加一个可读取的目录。这里最重要的问题是:不能随便把用户给的路径放进去。比如相对路径可能指向不确定的位置;不存在的路径没法安全授权;普通文件也不是“目录根”。这些测试会先建一个临时目录,当成假的工作区,再用 workspace_write 这种权限配置去调用授权函数。每个测试都故意传一个不合格的路径,然后确认函数必须报错,而且错误信息要说清楚原因。可以把它理解成门卫培训:不是所有拿着地址的人都能进门,地址必须是绝对的、真实存在的、而且是一个文件夹。

函数细节4
workspace_roots_for8–10 ↗
fn workspace_roots_for(root: &Path) -> Vec<AbsolutePathBuf>

作用:这个小工具函数把一个临时目录包装成“工作区根目录”列表,方便后面的测试复用。它的作用是少写重复代码,并保证传给被测函数的是它需要的路径格式。

数据流:进去的是一个 Path,也就是某个目录路径 → 函数把它转换成 AbsolutePathBuf,意思是“确认过的绝对路径” → 出来的是只包含这个根目录的列表;如果传进来的不是绝对路径,测试会直接失败,因为这里期望临时目录本来就是绝对路径。

调用关系:rejects_relative_path、rejects_missing_path 和 rejects_file_path 都会先调用它,准备一份假的工作区根目录。它内部只做包装,后续真正的安全判断交给 grant_read_root_non_elevated。

调用图:被 3 处调用(rejects_file_path, rejects_missing_path, rejects_relative_path);外部调用 1 个(vec!)。

rejects_relative_path13–26 ↗
fn rejects_relative_path()

作用:这个测试确认:如果有人想给沙盒授权一个相对路径,程序必须拒绝。相对路径就像只写“隔壁房间”,但没说从哪里开始找,安全系统不能接受这种含糊地址。

数据流:开始时创建一个临时目录,当作工作区 → 用 workspace_roots_for 把它做成工作区根目录列表 → 调用 grant_read_root_non_elevated,但最后传入 Path::new("relative") 这种相对路径 → 期望得到错误,而不是成功授权;最后检查错误文字里包含“path must be absolute”。

调用关系:它是针对 grant_read_root_non_elevated 的一条负面测试。它先借 workspace_roots_for 准备正常环境,再故意把坏路径交给授权函数,确认授权函数在入口处就把这种路径挡住。

调用图:调用 2 个内部函数(workspace_roots_for, workspace_write);外部调用 5 个(new, new, new, assert!, grant_read_root_non_elevated)。

rejects_missing_path29–43 ↗
fn rejects_missing_path()

作用:这个测试确认:如果要授权的目录根本不存在,程序必须拒绝。这样可以避免沙盒权限记录里出现一个看似合法、实际无法确认安全性的地址。

数据流:开始时创建一个临时目录 → 拼出一个名叫 does-not-exist 的子路径,但不真的创建它 → 准备工作区根目录列表 → 把这个不存在的路径交给 grant_read_root_non_elevated → 期望函数返回错误,并确认错误信息包含“path does not exist”。

调用关系:它同样围绕 grant_read_root_non_elevated 做边界检查。workspace_roots_for 负责搭好正常工作区背景,真正要验证的是授权函数会不会检查磁盘上路径是否真的存在。

调用图:调用 2 个内部函数(workspace_roots_for, workspace_write);外部调用 4 个(new, new, assert!, grant_read_root_non_elevated)。

rejects_file_path46–61 ↗
fn rejects_file_path()

作用:这个测试确认:只读授权的目标必须是目录,不能是普通文件。因为这里授的是“读取某个目录树”的权限,拿一个文件来当目录根会让规则变得不清楚。

数据流:开始时创建临时目录 → 在里面写入一个 file.txt 普通文件 → 准备工作区根目录列表 → 把这个文件路径传给 grant_read_root_non_elevated → 期望函数报错而不是授权;最后检查错误信息包含“path must be a directory”。

调用关系:它是 grant_read_root_non_elevated 的另一条安全测试。和前两个测试一样,它先搭环境,再故意传不合格输入;不同的是,这次路径存在,但类型不对,所以检查的是“必须是文件夹”这道关。

调用图:调用 2 个内部函数(workspace_roots_for, workspace_write);外部调用 5 个(new, new, assert!, write, grant_read_root_non_elevated)。

core/src/windows_sandbox_tests.rs源码 ↗
testtest suite

Windows 沙盒可以理解成给程序套一层“安全隔间”,限制它在 Windows 上能做什么。这个文件不实现沙盒本身,而是像质检员一样,拿各种配置组合去检查结果对不对。它测试了三类事情:第一,功能开关打开后,系统会选择正确的沙盒级别,比如普通受限模式或提权模式;第二,旧版配置名还能不能继续被识别,避免老用户升级后配置突然失效;第三,Windows 专用配置里的“私有桌面”默认是不是开启,以及用户明确关掉时是否真的生效。这里大量用断言,也就是“我预期结果应该是这样,如果不是就立刻报错”。所以它的重要性不在运行时服务用户,而在开发时守住兼容性和安全默认值。

函数细节9
elevated_flag_works_by_itself9–17 ↗
fn elevated_flag_works_by_itself()

作用:这个测试确认:只打开“Windows 沙盒提权模式”这个开关时,系统会选择提权沙盒。它防止这个高优先级模式被误判成别的模式。

数据流:进去的是一份带默认值的功能开关集合,由 with_defaults 造出来;测试把提权沙盒开关打开,然后让 WindowsSandboxLevel::from_features 根据这些开关算出沙盒级别;出来的结果必须是 Elevated,否则 assert_eq! 会让测试失败。

调用关系:它是测试套件里的一个独立用例,运行时先借 with_defaults 准备干净的默认开关,再用 assert_eq! 做最终验收。它主要盯住 from_features 在“只有提权开关”这种场景下的判断。

调用图:调用 1 个内部函数(with_defaults);外部调用 1 个(assert_eq!)。

restricted_token_flag_works_by_itself20–28 ↗
fn restricted_token_flag_works_by_itself()

作用:这个测试确认:只打开普通 Windows 沙盒开关时,系统会选择受限令牌模式。受限令牌可以理解成给程序一张权限更少的“临时通行证”。

数据流:进去的是默认功能开关集合,由 with_defaults 创建;测试打开普通 WindowsSandbox 开关,然后把这份开关交给 WindowsSandboxLevel::from_features;出来的级别必须是 RestrictedToken,assert_eq! 会检查这一点。

调用关系:它和其他沙盒开关测试一起覆盖 from_features 的不同输入情况。它调用 with_defaults 保证起点干净,最后交给 assert_eq! 判断结果是否符合预期。

调用图:调用 1 个内部函数(with_defaults);外部调用 1 个(assert_eq!)。

no_flags_means_no_sandbox31–38 ↗
fn no_flags_means_no_sandbox()

作用:这个测试确认:如果没有打开任何 Windows 沙盒相关开关,系统就不会启用沙盒。它避免默认情况下意外改变程序运行环境。

数据流:进去的是一份完全默认的功能开关集合,由 with_defaults 得到;测试不额外打开任何开关,直接让 WindowsSandboxLevel::from_features 计算结果;出来必须是 Disabled,表示沙盒关闭。

调用关系:它是 from_features 的基础边界测试:没有输入信号时应该得到关闭状态。它只需要 with_defaults 准备默认状态,再用 assert_eq! 核对输出。

调用图:调用 1 个内部函数(with_defaults);外部调用 1 个(assert_eq!)。

elevated_wins_when_both_flags_are_enabled41–50 ↗
fn elevated_wins_when_both_flags_are_enabled()

作用:这个测试确认:如果普通沙盒和提权沙盒两个开关都打开,提权模式会优先。这样可以避免配置冲突时结果含糊不清。

数据流:进去的是默认功能开关集合;测试先后打开普通沙盒和提权沙盒两个开关,再让 WindowsSandboxLevel::from_features 选择最终级别;出来必须是 Elevated,说明冲突时按提权模式处理。

调用关系:它补上了“两个开关同时存在”这个容易出错的场景。流程上仍是 with_defaults 准备输入,from_features 做判断,assert_eq! 检查最后选择。

调用图:调用 1 个内部函数(with_defaults);外部调用 1 个(assert_eq!)。

legacy_mode_prefers_elevated53–65 ↗
fn legacy_mode_prefers_elevated()

作用:这个测试检查旧版配置键同时要求普通沙盒和提权沙盒时,系统会偏向提权模式。它是在保护老配置的兼容规则。

数据流:进去的是一个新的有序字典,由 new 创建,用来模拟旧配置里的键值;测试放入 experimental_windows_sandbox 和 elevated_windows_sandbox 两个为 true 的条目;legacy_windows_sandbox_mode_from_entries 读取这些条目后,出来必须是 Some(Elevated)。

调用关系:它测试的是旧配置解析函数 legacy_windows_sandbox_mode_from_entries 的冲突处理。new 只负责搭一个模拟配置表,assert_eq! 负责确认解析结果。

调用图:外部调用 2 个(new, assert_eq!)。

legacy_mode_supports_alias_key68–79 ↗
fn legacy_mode_supports_alias_key()

作用:这个测试确认一个旧配置别名仍然有效。也就是说,用户以前写的 enable_experimental_windows_sandbox 还会被当作开启普通沙盒来理解。

数据流:进去的是一个新建的配置条目表;测试把 enable_experimental_windows_sandbox 设置为 true;legacy_windows_sandbox_mode_from_entries 读取后,出来必须是 Some(Unelevated),表示普通、不提权的沙盒模式。

调用关系:它专门守住旧名字到新行为的映射关系。它用 new 创建模拟输入,再用 assert_eq! 判断 legacy_windows_sandbox_mode_from_entries 是否还认这个别名。

调用图:外部调用 2 个(new, assert_eq!)。

resolve_windows_sandbox_mode_falls_back_to_legacy_keys82–97 ↗
fn resolve_windows_sandbox_mode_falls_back_to_legacy_keys()

作用:这个测试确认:当新配置里没有直接写 Windows 沙盒模式时,系统还会回头看旧版功能键。它避免升级后老用户的沙盒设置被忽略。

数据流:进去的是一个模拟配置:先用 new 建旧功能键表,放入 experimental_windows_sandbox=true,再用 from 把这些条目变成 FeaturesToml,并和 default 提供的默认配置合成 ConfigToml;resolve_windows_sandbox_mode 读取整份配置后,出来必须是 Some(Unelevated)。

调用关系:它测试的是更高一层的解析入口 resolve_windows_sandbox_mode,而不是只测旧键解析的小函数。它通过 from 和 default 拼出一份接近真实的配置,最后用 assert_eq! 验证该入口会正确退回到旧键规则。

调用图:调用 1 个内部函数(from);外部调用 3 个(new, default, assert_eq!)。

resolve_windows_sandbox_private_desktop_defaults_to_true100–104 ↗
fn resolve_windows_sandbox_private_desktop_defaults_to_true()

作用:这个测试确认 Windows 沙盒的“私有桌面”默认是开启的。私有桌面可以粗略理解成给沙盒程序一个隔离的桌面环境,减少它影响正常桌面的机会。

数据流:进去的是默认配置 ConfigToml::default;resolve_windows_sandbox_private_desktop 读取配置时发现用户没有明确设置;出来应该是 true,assert! 会检查这个默认值是否成立。

调用关系:它守住的是安全默认值:用户没说时就启用私有桌面。它直接调用 resolve_windows_sandbox_private_desktop,并用 assert! 做布尔结果检查。

调用图:外部调用 1 个(assert!)。

resolve_windows_sandbox_private_desktop_respects_explicit_cfg_value107–117 ↗
fn resolve_windows_sandbox_private_desktop_respects_explicit_cfg_value()

作用:这个测试确认:如果用户在配置里明确把“私有桌面”设为 false,系统会尊重这个选择。它防止默认值强行盖过用户配置。

数据流:进去的是一份手工构造的配置,其中 windows.sandbox_private_desktop 被设成 Some(false),其他字段用 default 补齐;resolve_windows_sandbox_private_desktop 读取后应该返回 false;assert! 通过取反来确认结果确实不是 true。

调用关系:它和默认值测试配成一对:一个看没写配置时的行为,一个看明确写了配置时的行为。它使用 default 填补无关字段,重点把输入交给 resolve_windows_sandbox_private_desktop 验证用户设置是否优先。

调用图:外部调用 2 个(default, assert!)。

core/src/exec_env_tests.rs源码 ↗
testtest

这份文件专门测试“执行命令前要带哪些环境变量”。环境变量可以理解成给子程序的一张小纸条,上面写着 PATH、HOME、API_KEY 之类的信息。这里重点检查几类规则:默认情况下保留什么;开启默认排除后,带 KEY、SECRET、TOKEN 这类名字的变量会不会被过滤;只允许某些名字时是否只留下它们;手动设置的新变量是否能覆盖或追加;有没有正确塞入线程编号,方便把一次会话和它启动的命令对应起来。它还单独照顾 Windows,因为 Windows 的变量名通常不区分大小写,并且 PATHEXT 会影响哪些文件能当命令执行。整体上,这个文件像一张安全检查清单,防止以后改环境变量逻辑时不小心破坏这些约定。

函数细节13
make_vars6–11 ↗
fn make_vars(pairs: &[(&str, &str)]) -> Vec<(String, String)>

作用:这是测试里的小帮手,把简单的字符串键值对变成真正用于测试的环境变量列表。这样每个测试不用反复写一堆转换代码。

数据流:进去的是一组类似 “PATH” 到 “/usr/bin” 的字符串对;它把每个名字和值都复制成可拥有的字符串;出来的是一个环境变量列表,后面的测试会把它交给环境构造函数。

调用关系:很多测试都会先叫它准备假环境变量,然后再把这些变量交给 populate_env 或 create_env_from_vars。它本身不判断规则,只负责把测试材料摆好。

调用图:被 12 处调用(create_env_inserts_pathext_on_windows_when_missing, create_env_preserves_existing_pathext_case_insensitively_on_windows, populate_env_inserts_thread_id, populate_env_omits_thread_id_when_missing, test_core_inherit_defaults_keep_sensitive_vars, test_core_inherit_respects_case_insensitive_names_on_windows, test_core_inherit_with_default_excludes_enabled, test_include_only, test_inherit_all, test_inherit_all_with_default_excludes (+2 more))。

test_core_inherit_defaults_keep_sensitive_vars14–35 ↗
fn test_core_inherit_defaults_keep_sensitive_vars()

作用:这个测试确认默认策略不会自动删掉看起来敏感的变量,比如 API_KEY 和 SECRET_TOKEN。它是在保护一个默认行为:没有明确要求过滤时,不要偷偷改用户环境。

数据流:它先准备 PATH、HOME、API_KEY、SECRET_TOKEN 四个变量;再使用默认的 ShellEnvironmentPolicy,也就是环境变量筛选规则;然后调用 populate_env 生成最终环境,并额外传入一个线程编号;最后检查结果里四个原变量和线程编号都还在。

调用关系:它用 make_vars 准备输入,用默认策略和 ThreadId::new 生成测试条件,再用断言比较 populate_env 的输出。这个测试覆盖默认路径,给其他更严格过滤的测试做参照。

调用图:调用 3 个内部函数(make_vars, default, new);外部调用 2 个(assert_eq!, hashmap!)。

test_core_inherit_with_default_excludes_enabled38–60 ↗
fn test_core_inherit_with_default_excludes_enabled()

作用:这个测试确认当“默认排除规则”打开时,名字里像 KEY、SECRET、TOKEN 的变量会被挡掉。它防止敏感信息不小心传给外部命令。

数据流:进去的是包含普通变量和敏感名字变量的一组环境;测试把 ignore_default_excludes 设为 false,意思是启用默认排除;populate_env 处理后,结果只应留下 PATH、HOME 和新加的线程编号,API_KEY 与 SECRET_TOKEN 被移除。

调用关系:它和默认保留敏感变量的测试形成对照:同样的输入,只改策略开关。它通过 make_vars 准备数据,再把判断交给 populate_env,最后用 assert_eq! 确认过滤确实生效。

调用图:调用 2 个内部函数(make_vars, new);外部调用 3 个(default, assert_eq!, hashmap!)。

test_include_only63–82 ↗
fn test_include_only()

作用:这个测试确认“只允许这些变量名”的规则能生效。例子里只允许匹配 PATH 的变量,所以 FOO 这种无关变量应该被丢掉。

数据流:它准备 PATH 和 FOO 两个变量;策略里设置 include_only,带一个不区分大小写的匹配模式 “*PATH”;populate_env 处理后,只输出 PATH 和线程编号,不输出 FOO。

调用关系:它先用 make_vars 做测试输入,再用 EnvironmentVariablePattern 创建名字匹配规则。这个测试专门检查 populate_env 在普通继承之外,还会尊重“白名单”式的筛选。

调用图:调用 2 个内部函数(make_vars, new);外部调用 4 个(default, assert_eq!, hashmap!, vec!)。

test_set_overrides85–104 ↗
fn test_set_overrides()

作用:这个测试确认策略里手动指定的变量会被加入最终环境。也就是说,程序不只能继承旧环境,还能主动塞入新环境变量。

数据流:它先准备一个 PATH;再在策略的 set 表里放入 NEW_VAR=42;populate_env 生成结果时,把原来的 PATH、新增的 NEW_VAR,以及线程编号一起放进最终环境。

调用关系:它用 make_vars 准备原始环境,再修改 ShellEnvironmentPolicy 的 set 字段。它验证 populate_env 不只是过滤器,也会把配置中明确要求设置的变量写进去。

调用图:调用 2 个内部函数(make_vars, new);外部调用 3 个(default, assert_eq!, hashmap!)。

populate_env_inserts_thread_id107–119 ↗
fn populate_env_inserts_thread_id()

作用:这个测试确认只要传了线程编号,最终环境里就会出现对应的线程编号变量。这样被启动的命令可以知道自己属于哪一次会话或任务。

数据流:它准备 PATH 和一个新的 ThreadId;调用 populate_env 时把线程编号传进去;输出结果应包含 PATH,以及一个名字为 CODEX_THREAD_ID_ENV_VAR、值为该线程编号的变量。

调用关系:它走的是 populate_env 的常规入口,重点不在筛选规则,而在附加元信息。它和后面的“没有线程编号就不插入”测试成对出现。

调用图:调用 3 个内部函数(make_vars, default, new);外部调用 2 个(assert_eq!, hashmap!)。

populate_env_omits_thread_id_when_missing122–132 ↗
fn populate_env_omits_thread_id_when_missing()

作用:这个测试确认如果没有传线程编号,最终环境就不会凭空多出线程编号变量。它防止程序制造不存在的上下文信息。

数据流:它准备一个 PATH,并把线程编号参数传成 None,也就是没有;populate_env 只返回 PATH;测试确认结果里没有额外的线程编号变量。

调用关系:它和 populate_env_inserts_thread_id 是一正一反的检查。两者一起说明 populate_env 对线程编号的处理是可选的,不会乱加。

调用图:调用 2 个内部函数(make_vars, default);外部调用 2 个(assert_eq!, hashmap!)。

test_inherit_all135–149 ↗
fn test_inherit_all()

作用:这个测试确认当策略说“全部继承”时,输入的环境变量都会进入最终环境。这里还关掉默认排除,确保没有变量被提前删掉。

数据流:它准备 PATH 和 FOO;策略设置 inherit 为 All,意思是全部继承,并忽略默认排除;populate_env 输出时保留这两个变量,再加入线程编号。

调用关系:它验证 ShellEnvironmentPolicyInherit::All 这条分支。make_vars 提供原始环境,populate_env 执行继承规则,断言确保最终环境等于“原样继承加线程编号”。

调用图:调用 2 个内部函数(make_vars, new);外部调用 2 个(default, assert_eq!)。

test_inherit_all_with_default_excludes152–168 ↗
fn test_inherit_all_with_default_excludes()

作用:这个测试确认“全部继承”也不是绝对的:如果默认排除规则启用,敏感名字变量还是会被过滤。它避免大家误以为 All 会无条件放行一切。

数据流:它准备 PATH 和 API_KEY;策略设置为继承全部,但 ignore_default_excludes 为 false,表示启用默认敏感变量过滤;populate_env 输出只留下 PATH 和线程编号,API_KEY 被去掉。

调用关系:它补充 test_inherit_all 的边界情况。两者一起说明继承范围和安全过滤是两层规则:先决定能继承多少,再看哪些需要排除。

调用图:调用 2 个内部函数(make_vars, new);外部调用 3 个(default, assert_eq!, hashmap!)。

test_core_inherit_respects_case_insensitive_names_on_windows172–196 ↗
fn test_core_inherit_respects_case_insensitive_names_on_windows()

作用:这个 Windows 专用测试确认在 Windows 上变量名按不区分大小写的方式识别。这样 Path、PathExt 这类写法不会因为大小写不同而被误删。

数据流:它准备 Path、PathExt、TEMP、FOO;策略设置为 Core,意思是只继承核心必需变量,并忽略默认排除;在 Windows 上,populate_env 应留下 Path、PathExt、TEMP 和线程编号,丢掉 FOO。

调用关系:这个测试只在 Windows 编译运行。它用 make_vars 提供带不同大小写的变量名,检查 populate_env 的核心变量判断是否符合 Windows 的习惯。

调用图:调用 2 个内部函数(make_vars, new);外部调用 3 个(default, assert_eq!, hashmap!)。

create_env_inserts_pathext_on_windows_when_missing200–215 ↗
fn create_env_inserts_pathext_on_windows_when_missing()

作用:这个 Windows 专用测试确认即使没有继承任何变量,最终环境也会补上 PATHEXT。PATHEXT 在 Windows 上很重要,它告诉系统 .EXE、.BAT 等文件可以当命令运行。

数据流:它传入空的环境变量列表;策略设置为不继承任何东西;create_env_from_vars 构造最终环境时,在 Windows 上自动加入 PATHEXT=.COM;.EXE;.BAT;.CMD;测试确认结果正是这个默认值。

调用关系:它直接测试 create_env_from_vars,而不是 populate_env。这个测试覆盖 Windows 的特殊兜底逻辑:就算用户选择不继承环境,也要保留让命令能正常启动的最低条件。

调用图:调用 1 个内部函数(make_vars);外部调用 3 个(default, assert_eq!, hashmap!)。

create_env_preserves_existing_pathext_case_insensitively_on_windows219–237 ↗
fn create_env_preserves_existing_pathext_case_insensitively_on_windows()

作用:这个 Windows 专用测试确认如果环境里已经有 PATHEXT,即使写成 PathExt,也不会再插入第二个。它防止最终环境里出现重复且可能冲突的变量。

数据流:它准备一个 PathExt,值里额外包含 .PS1;create_env_from_vars 处理后,测试从结果里找所有名字等于 PATHEXT、但忽略大小写的项;最终应该只找到一个,并且值保持原来的 .COM;.EXE;.BAT;.CMD;.PS1。

调用关系:它和“缺失时插入 PATHEXT”的测试配套。一个检查会补默认值,另一个检查已有值会被尊重,说明 Windows 特殊处理既兜底又不抢用户设置。

调用图:调用 1 个内部函数(make_vars);外部调用 2 个(default, assert_eq!)。

test_inherit_none240–259 ↗
fn test_inherit_none()

作用:这个测试确认当策略说“不继承任何环境变量”时,原来的 PATH、HOME 都不会进入结果。只有策略明确设置的变量和线程编号会留下。

数据流:它准备 PATH 和 HOME;策略设置 inherit 为 None,并在 set 里写入 ONLY_VAR=yes;populate_env 处理后,输出只包含 ONLY_VAR 和线程编号,不包含原来的 PATH、HOME。

调用关系:它验证最严格的继承模式。make_vars 提供会被丢弃的原始环境,策略的 set 提供保留的人工变量,populate_env 把这两层规则合成最终结果。

调用图:调用 2 个内部函数(make_vars, new);外部调用 3 个(default, assert_eq!, hashmap!)。

utils/path-utils/src/path_utils_tests.rs源码 ↗
test测试运行时

这个文件不是真正给用户调用的功能代码,而是“验收清单”。它专门检查路径工具在几个容易出错的地方会不会乱套:符号链接(一种像快捷方式一样指向别处的文件)如果互相指来指去形成死循环,程序不能卡住;WSL(Windows 上跑 Linux 的环境)里 /mnt/C 这种盘符路径要按预期变成小写;Windows 的特殊长路径写法 \?\ 要能简化;两个路径比较时,如果能规范化就按规范化后比较,不能规范化就退回到原始文字比较。它用临时目录制造安全的测试现场,不污染真实文件系统。整体作用像给路径处理工具装了一排报警器,防止跨系统的小差异变成真实运行中的大故障。

函数细节9
wsl::wsl_mnt_drive_paths_lowercase31–36 ↗
fn wsl_mnt_drive_paths_lowercase()

作用:这个测试确认 WSL 环境里的 Windows 盘符路径会被统一成小写。这样 /mnt/C/Users/Dev 这类路径不会因为大小写不同而被当成另一个位置。

数据流:它构造输入路径 /mnt/C/Users/Dev,并明确告诉函数当前是 WSL 环境。normalize_for_wsl_with_flag 会把符合 /mnt/单个盘符/ 规则的路径整体转成小写,最后测试确认输出是 /mnt/c/users/dev。

调用关系:它专门覆盖 normalize_for_wsl_with_flag 的 WSL 盘符路径分支。PathBuf::from 负责造路径,assert_eq! 负责确认函数给出的规范化结果正是预期结果。

调用图:外部调用 3 个(from, assert_eq!, normalize_for_wsl_with_flag)。

wsl::wsl_non_drive_paths_unchanged39–44 ↗
fn wsl_non_drive_paths_unchanged()

作用:这个测试确认:看起来在 /mnt 下、但不是单个盘符的路径,不应该被误改。比如 /mnt/cc 不是 Windows 的 C 盘挂载点。

数据流:它输入 /mnt/cc/Users/Dev,并把 is_wsl 设为 true。函数检查后发现 cc 不是单个盘符,所以原样返回。测试再确认输出和输入完全一样。

调用关系:它验证 normalize_for_wsl_with_flag 不会过度热心地修改所有 /mnt 开头的路径。这个测试和盘符小写测试配在一起,说明函数既要会改该改的,也要放过不该改的。

调用图:外部调用 3 个(from, assert_eq!, normalize_for_wsl_with_flag)。

wsl::wsl_non_mnt_paths_unchanged47–52 ↗
fn wsl_non_mnt_paths_unchanged()

作用:这个测试确认:WSL 下普通 Linux 路径不会被当成 Windows 盘符路径处理。比如 /home/Dev 应该保持原样。

数据流:它准备 /home/Dev 这个普通路径,并告诉函数当前是 WSL。normalize_for_wsl_with_flag 看到它不是 /mnt/盘符/ 形式,就不做改动,最后测试确认返回值仍是原路径。

调用关系:它补上 normalize_for_wsl_with_flag 的另一种边界情况:只有特定的 /mnt/盘符路径才会被规范化。它和前两个 WSL 测试一起,圈定了这个函数该动和不该动的范围。

调用图:外部调用 3 个(from, assert_eq!, normalize_for_wsl_with_flag)。

native_workdir::windows_verbatim_paths_are_simplified62–70 ↗
fn windows_verbatim_paths_are_simplified()

作用:这个测试确认 Windows 上的特殊路径前缀 \?\ 会被去掉,变成更普通、更容易显示和比较的路径。这个前缀常用于绕过 Windows 的路径长度限制,但平时读起来很别扭。

数据流:它输入一个带 \?\ 前缀的 Windows 路径,并告诉函数当前是 Windows。normalize_for_native_workdir_with_flag 会把这个特殊前缀简化掉,测试再确认输出变成 D:\c\x\worktrees\2508\swift-base。

调用关系:它验证 normalize_for_native_workdir_with_flag 在 Windows 模式下的核心行为。这个测试只在 Windows 目标系统上编译运行,避免在非 Windows 系统上误测平台专属路径规则。

调用图:外部调用 3 个(from, assert_eq!, normalize_for_native_workdir_with_flag)。

native_workdir::non_windows_paths_are_unchanged73–79 ↗
fn non_windows_paths_are_unchanged()

作用:这个测试确认:如果当前不是 Windows,就算路径文字看起来像 Windows 的特殊写法,也不要擅自简化。这样可以避免在别的系统上误改用户给出的字符串。

数据流:它准备一个带 \?\ 前缀的路径,但把 is_windows 设为 false。normalize_for_native_workdir_with_flag 因为知道当前不是 Windows,所以原样返回。测试确认输出和输入一致。

调用关系:它和 Windows 专属测试形成一正一反的检查:同一个工具函数只有在明确处于 Windows 模式时才会简化路径。assert_eq! 用来守住这个条件判断。

调用图:外部调用 3 个(from, assert_eq!, normalize_for_native_workdir_with_flag)。

path_comparison::matches_identical_existing_paths87–92 ↗
fn matches_identical_existing_paths() -> std::io::Result<()>

作用:这个测试确认:两个完全相同、而且真实存在的路径,经过规范化比较后应该被认为是同一个路径。这是路径比较最基本的正确性要求。

数据流:它创建一个临时目录,然后把同一个目录路径作为左右两边传给 paths_match_after_normalization。函数应该判断它们匹配,测试用 assert! 确认结果为真。

调用关系:它验证 paths_match_after_normalization 的正常路径:路径存在,可以被系统规范化,然后比较结果应该一致。临时目录提供了一个一定存在、又不会影响真实文件的测试对象。

调用图:外部调用 2 个(assert!, tempdir)。

path_comparison::falls_back_to_raw_equality_when_paths_cannot_be_normalized95–104 ↗
fn falls_back_to_raw_equality_when_paths_cannot_be_normalized()

作用:这个测试确认:如果路径不存在,系统没法把它规范化,比较函数也不会直接失败。它会退一步,按原始路径文字是否相同来判断。

数据流:它先比较两个都叫 missing 的不存在路径,预期结果为匹配;再比较 missing-a 和 missing-b,预期结果为不匹配。也就是说,不能规范化时,就看原始写法是不是一样。

调用关系:它覆盖 paths_match_after_normalization 的兜底路线。这个路线很重要,因为很多时候程序要比较的路径可能还没创建出来,函数仍然需要给出稳定、可理解的结果。

调用图:外部调用 1 个(assert!)。

path_comparison::matches_windows_verbatim_paths108–114 ↗
fn matches_windows_verbatim_paths() -> std::io::Result<()>

作用:这个测试确认:Windows 的普通路径和带 \?\ 前缀的特殊路径,如果实际指向同一个目录,比较函数应该认出它们是同一个地方。

数据流:它创建一个临时目录,再用 format! 拼出同一个目录的 \?\ 版本路径。然后把特殊写法和普通写法交给 paths_match_after_normalization,测试确认比较结果为真。

调用关系:它只在 Windows 上运行,用来验证路径比较函数能处理 Windows 特有的路径外观差异。它和前面的 Windows 路径简化测试互相呼应,确保比较时不会被 \?\ 这种前缀迷惑。

调用图:外部调用 4 个(from, assert!, format!, tempdir)。

git-utils/src/fsmonitor_tests.rs源码 ↗
testtest

fsmonitor 可以理解成 Git 的“文件变动提醒器”:它能告诉 Git 哪些文件变了,避免 Git 每次都把整个工作区扫一遍。这个测试文件关心的是:当用户的 Git 配置里写了 core.fsmonitor,程序到底应该认为它是关闭、外部辅助程序,还是 Git 自带的内置监控。为了不真的去运行 Git,它做了一个 FakeRunner,像排队发答案的假服务员一样:检测代码问一次,它就按预先准备好的顺序给一次回答,并顺便检查问的问题是不是预期的命令参数。主测试覆盖了缺少配置、路径形式的外部 helper、各种真假写法、旧 Git 不支持能力、新 Git 支持能力等情况。这样可以保证真实代码以后改动时,不会把这些边界情况弄错。

函数细节7
FakeRunner::run_probe20–24 ↗
fn run_probe(&mut self, args: &[&str]) -> impl Future<Output = Option<Vec<u8>>> + Send

作用:这个函数是假装执行 Git 探测命令。测试里的真实检测代码会调用它,但它不会真的启动 Git,而是从预设答案队列里取出下一条结果。

数据流:进去的是一组命令参数,也就是检测代码想问 Git 的问题;函数从 FakeRunner 里取出最前面的一条 ProbeResponse,先确认这次问的问题和预期完全一样,再把预设的输出包装成一个马上完成的异步结果交回去。它会改动 FakeRunner,把已经用过的回答从队列里移走。

调用关系:它实现了 FsmonitorProbeRunner 这个测试用接口,所以 detect_fsmonitor_override 在测试中会把它当成真正的命令执行器来用。它内部用 pop_front 取下一条假回答,用 assert_eq! 确认调用顺序和参数没跑偏,最后用 ready 返回一个立即完成的结果。

调用图:外部调用 3 个(pop_front, assert_eq!, ready)。

detects_supported_builtin_fsmonitor_values28–108 ↗
async fn detects_supported_builtin_fsmonitor_values()

作用:这是主测试,用一批例子检查 fsmonitor 检测结果是否正确。它验证程序面对不同 Git 配置输出时,会不会该关就关、该认定内置支持就认定内置支持。

数据流:进去的是测试代码里写死的一组案例:每个案例都有名称、假的 Git 回答列表、期望结果。测试为每个案例创建一个 FakeRunner,把它交给 detect_fsmonitor_override;检测完成后,拿实际结果和期望结果比较,同时确认所有预设回答都被用完了,没有多问也没有少问。

调用关系:它是这个文件的核心入口,由 Tokio 的异步测试框架在跑测试时调用。它通过 response、config_args、typed_config_args、capability_args 和 fsmonitor_capability 拼出各种假场景,然后把真正的判断工作交给外部的 detect_fsmonitor_override,最后用 assert_eq! 守住结果。

调用图:外部调用 3 个(assert_eq!, detect_fsmonitor_override, vec!)。

response110–115 ↗
fn response(args: Vec<&'static str>, output: Option<&[u8]>) -> ProbeResponse

作用:这个小工具函数用来快速创建一条假的 Git 回答。它让测试案例写起来更短、更像在描述场景,而不是反复手动组装结构体。

数据流:进去的是期望收到的命令参数,以及这次命令应该返回的字节输出;函数把参数和输出放进 ProbeResponse 里。如果有输出,就把借来的字节切片复制成自己的 Vec<u8> 保存;如果没有输出,就保存 None,表示命令没有查到结果。

调用关系:它主要服务于 detects_supported_builtin_fsmonitor_values。测试用它准备 FakeRunner 的回答队列,随后 FakeRunner::run_probe 会一条条取出这些 ProbeResponse 来回应 detect_fsmonitor_override 的询问。

config_args117–119 ↗
fn config_args() -> Vec<&'static str>

作用:这个函数生成“读取 core.fsmonitor 原始配置值”的 Git 命令参数。测试用它确保检测代码第一步确实是在问正确的配置项。

数据流:它不需要外部输入;函数直接返回一组固定字符串参数,相当于 git config --null --get core.fsmonitor。出来的是一个字符串列表,供 response 记录为预期命令。

调用关系:它被 detects_supported_builtin_fsmonitor_values 用来搭建多个测试案例。之后 FakeRunner::run_probe 会拿真实收到的参数和这里生成的参数比较,确认 detect_fsmonitor_override 没有查错配置。

调用图:外部调用 1 个(vec!)。

typed_config_args121–131 ↗
fn typed_config_args(value: &'static str) -> Vec<&'static str>

作用:这个函数生成“把某个 core.fsmonitor 值按布尔值理解”的 Git 命令参数。它用于测试那些看起来不明显是真假的配置,比如空值或奇怪字符串。

数据流:进去的是一个配置值字符串;函数把这个值放到一组固定的 git config 参数末尾,形成带 --type=bool 和 --fixed-value 的查询。出来的是完整参数列表,用来表示检测代码应该让 Git 帮忙判断这个值到底算 true 还是 false。

调用关系:它在主测试里用于 helper 路径、数字样式、空值等案例。detect_fsmonitor_override 如果遇到不能直接判断的配置值,就应该发出这类查询;FakeRunner::run_probe 会检查它发出的参数是否和 typed_config_args 生成的一样。

调用图:外部调用 1 个(vec!)。

capability_args133–135 ↗
fn capability_args() -> Vec<&'static str>

作用:这个函数生成“查询 Git 构建能力”的命令参数。测试用它判断检测代码是否会确认当前 Git 版本真的支持内置 fsmonitor。

数据流:它不接收输入;函数返回固定参数,相当于 git version --build-options。出来的参数列表会被放进假回答中,表示检测代码接下来应该询问 Git 的编译功能列表。

调用关系:它服务于主测试中需要确认内置支持的案例。detect_fsmonitor_override 在配置看起来开启时,会进一步查询能力;FakeRunner::run_probe 则用 capability_args 的结果核对这一步有没有发生、参数对不对。

调用图:外部调用 1 个(vec!)。

fsmonitor_capability137–139 ↗
fn fsmonitor_capability() -> &'static [u8]

作用:这个函数给出一段代表“Git 支持 fsmonitor daemon”的假输出。daemon 可以理解成常驻后台的小程序,这里表示 Git 自带文件监控后台能力可用。

数据流:它不接收输入;函数直接返回固定字节内容 feature: fsmonitor--daemon。这个输出会被测试当作 git version --build-options 的结果,表示当前 Git 构建包含内置 fsmonitor 能力。

调用关系:它被主测试用来构造支持内置 fsmonitor 的成功场景。detect_fsmonitor_override 读取到这段能力信息后,应该返回 FsmonitorOverride::BuiltIn;测试再用 assert_eq! 确认这个判断没有出错。

Hooks、提示词与运行时防护

最后一组检查面向运行时的策略展示和执行,包括 hook 引擎行为、渲染后的权限指令,以及内存写入速率限制防护。

hooks/src/engine/mod_tests.rs源码 ↗
testtest

这里测试的是 ClaudeHooksEngine,也就是“工具运行前后可以插入外部命令”的那套引擎。钩子可以来自用户配置、系统配置、企业托管配置,或者插件。不同来源的钩子信任级别不一样,有些能直接跑,有些只能列出来等用户信任。这个文件用临时目录和假配置搭出各种场景:托管钩子是否一定启用,用户禁用是否不能关掉托管钩子,Windows 是否用专门命令,插件路径占位符是否会被换成真实路径,坏掉的 hooks.json 是否变成启动警告。它不实现业务功能,但能保证“该跑的钩子跑,不该跑的钩子不跑”,这对安全和可预期行为很重要。

函数细节26
cwd35–37 ↗
fn cwd() -> AbsolutePathBuf

作用:拿到当前程序运行所在的文件夹,给测试里的请求当作工作目录。这样测试不需要硬编码某个机器上的路径。

数据流:进去没有参数 → 它读取系统当前目录,并把它包装成项目里要求的绝对路径类型 → 出来一个 AbsolutePathBuf,供后面的 hook 请求使用。

调用关系:多个测试在构造 PreToolUseRequest 时会用它。它只把“当前目录”这件小事做好,然后把路径交给引擎的预览或执行流程。

调用图:调用 1 个内部函数(current_dir);被 6 处调用(discovers_hooks_from_json_and_toml_in_the_same_layer, plugin_hook_sources_run_with_plugin_env_and_plugin_source, profile_user_layers_load_shared_hooks_json_once, requirements_managed_hooks_execute_from_managed_dir, requirements_managed_hooks_execute_windows_command_override, requirements_managed_hooks_load_when_managed_dir_is_missing)。

managed_hooks_for_current_platform39–57 ↗
fn managed_hooks_for_current_platform(
    managed_dir: impl AsRef<Path>,
    hooks: HookEventsToml,
) -> ManagedHooksRequirementsToml

作用:生成一份适合当前操作系统的“托管钩子”配置。因为 Windows 和类 Unix 系统使用的托管目录字段不一样,所以测试需要这个小帮手避免重复写判断。

数据流:进去一个托管目录和一组 hook 事件配置 → 它判断当前是不是 Windows,把目录放进对应字段,另一个平台字段留空 → 出来一份 ManagedHooksRequirementsToml。

调用关系:很多托管钩子测试先用它拼出企业管控配置,再交给 ConfigLayerStack 和 ClaudeHooksEngine,观察引擎是否按托管规则处理。

调用图:被 6 处调用(allow_managed_hooks_only_keeps_managed_requirement_and_config_layer_hooks, requirements_managed_hooks_execute_from_managed_dir, requirements_managed_hooks_execute_windows_command_override, requirements_managed_hooks_load_when_managed_dir_is_missing, unknown_requirement_source_hooks_stay_managed, user_disablement_filters_non_managed_hooks_but_not_managed_hooks);外部调用 3 个(as_ref, clone, cfg!)。

pre_tool_use_hook_events59–73 ↗
fn pre_tool_use_hook_events(command: impl Into<String>) -> HookEventsToml

作用:快速造一个“运行 Bash 工具前执行命令”的 hook 配置。它让测试能专注验证引擎行为,而不是每次手写一大段配置。

数据流:进去一条命令字符串 → 它放进 PreToolUse 事件里,并设置匹配 Bash、超时 10 秒、状态文案 checking → 出来一份 HookEventsToml。

调用关系:需要托管钩子配置的测试会调用它,再把结果交给 managed_hooks_for_current_platform 或配置栈。

调用图:被 1 处调用(allow_managed_hooks_only_keeps_managed_requirement_and_config_layer_hooks);外部调用 2 个(default, vec!)。

config_toml_with_pre_tool_use75–113 ↗
fn config_toml_with_pre_tool_use(command: &str) -> TomlValue

作用:手工拼出一份带 PreToolUse hook 的 TOML 配置值。这里的 TOML 值可以理解成还没写成文本、但结构已经搭好的配置表。

数据流:进去一条命令 → 它创建 hooks 表、PreToolUse 数组、匹配规则和 command 处理器 → 出来一个 TomlValue,代表完整配置。

调用关系:测试会用它模拟用户或系统配置层,然后交给 ConfigLayerStack,检查引擎发现 hook 的结果。

调用图:被 1 处调用(allow_managed_hooks_only_in_config_toml_does_not_enable_policy);外部调用 7 个(default, Array, Integer, String, Table, unreachable!, vec!)。

requirements_with_managed_hooks_only115–139 ↗
fn requirements_with_managed_hooks_only(
    allow_managed_hooks_only: bool,
    managed_hooks: Option<ManagedHooksRequirementsToml>,
) -> (ConfigRequirements, ConfigRequirementsToml)

作用:生成一对“要求配置”:一份是程序内部用的结构,一份是原始 TOML 形式。重点是设置 allow_managed_hooks_only,也就是“是否只允许托管钩子”。

数据流:进去一个布尔值和可选托管钩子配置 → 它把这些信息同时写进 ConfigRequirements 和 ConfigRequirementsToml → 出来这两份配套配置。

调用关系:一系列测试用它切换策略开关,再把结果交给配置栈,看未托管的 JSON、TOML、插件 hook 会不会被跳过。

调用图:调用 1 个内部函数(new);被 4 处调用(allow_managed_hooks_only_false_keeps_unmanaged_hooks, allow_managed_hooks_only_keeps_managed_requirement_and_config_layer_hooks, allow_managed_hooks_only_skips_unmanaged_json_and_toml_hooks, allow_managed_hooks_only_skips_unmanaged_plugin_hooks);外部调用 2 个(default, default)。

requirements_managed_hooks_execute_from_managed_dir142–262 ↗
async fn requirements_managed_hooks_execute_from_managed_dir()

作用:验证企业托管钩子会从指定托管目录加载、展示,并且真正执行。没有这个保障,管理员下发的安全检查脚本可能根本不会跑。

数据流:进去是测试框架提供的异步执行环境 → 它建临时托管目录,写入一个 Python 脚本,配置成 Bash 前置 hook,然后创建引擎、预览并执行 → 出来是断言结果:hook 来源是托管配置,预览路径正确,脚本日志里收到了 PreToolUse 事件。

调用关系:测试框架运行它时,它调用 managed_hooks_for_current_platform 和 cwd 准备数据,再把请求交给 ClaudeHooksEngine 的 preview_pre_tool_use 和 run_pre_tool_use。

调用图:调用 8 个内部函数(new, allow_any, new, new, cwd, managed_hooks_for_current_platform, new, try_from);外部调用 15 个(default, new, new, default, assert!, assert_eq!, default, list_hooks, format!, create_dir_all (+5 more))。

requirements_managed_hooks_execute_windows_command_override265–342 ↗
async fn requirements_managed_hooks_execute_windows_command_override()

作用:验证同一个 hook 在 Windows 上会使用 command_windows,在其他系统上使用普通 command。这样跨平台配置不会在 Windows 机器上跑错命令。

数据流:进去是异步测试环境 → 它创建带普通命令和 Windows 覆盖命令的托管 hook,运行一次 Bash 前置检查 → 出来是断言结果:hook 失败码在 Windows 是 19,其他系统是 17,并且失败信息被记录为错误输出。

调用关系:测试通过 managed_hooks_for_current_platform 建配置,通过 cwd 建请求,然后让 ClaudeHooksEngine 执行,最后检查 HookRunStatus 和输出条目。

调用图:调用 8 个内部函数(new, allow_any, new, new, cwd, managed_hooks_for_current_platform, new, try_from);外部调用 12 个(default, new, new, default, assert!, assert_eq!, cfg!, default, create_dir_all, json! (+2 more))。

unknown_requirement_source_hooks_stay_managed345–410 ↗
fn unknown_requirement_source_hooks_stay_managed()

作用:验证即使托管配置的来源标成 Unknown,里面的 hook 仍然被当作托管 hook 对待。这样来源标记不完整时,也不会误把管理员下发的 hook 当成普通用户 hook。

数据流:进去没有业务输入 → 它创建托管 hook,但把 RequirementSource 设为 Unknown,然后创建引擎并运行发现流程 → 出来是断言:hook 来源显示 Unknown,但仍启用、is_managed 为真,信任状态是 Managed。

调用关系:它先搭配置栈,再让 ClaudeHooksEngine 和 discovery::discover_handlers 分别读取,确保执行引擎和列表发现逻辑看法一致。

调用图:调用 7 个内部函数(new, allow_any, new, new, discover_handlers, managed_hooks_for_current_platform, try_from);外部调用 9 个(default, new, new, default, assert_eq!, default, create_dir_all, tempdir, vec!)。

user_disablement_filters_non_managed_hooks_but_not_managed_hooks413–498 ↗
fn user_disablement_filters_non_managed_hooks_but_not_managed_hooks()

作用:验证用户配置里的“禁用某个 hook”只能关掉普通 hook,不能关掉托管 hook。这个规则很关键,因为托管 hook 往往代表组织安全策略。

数据流:进去没有业务输入 → 它同时造一个托管 hook 和一个用户 hook,并在用户状态里把两个 key 都标成 disabled → 出来是断言:引擎只保留托管 hook;发现列表里托管 hook 仍启用,用户 hook 被禁用。

调用关系:它调用 config_with_pre_tool_use_hook_and_states 准备用户配置,调用 managed_hooks_for_current_platform 准备托管配置,再交给引擎和 discover_handlers 双重检查。

调用图:调用 8 个内部函数(new, allow_any, new, new, discover_handlers, config_with_pre_tool_use_hook_and_states, managed_hooks_for_current_platform, try_from);外部调用 11 个(default, new, new, default, assert!, assert_eq!, default, format!, create_dir_all, tempdir (+1 more))。

user_disablement_does_not_filter_managed_layer_hooks501–561 ↗
fn user_disablement_does_not_filter_managed_layer_hooks()

作用:验证来自“托管配置文件层”的 hook 也不能被用户状态禁用。也就是说,不只是 requirements 里的托管 hook,托管配置层里的 hook 也受保护。

数据流:进去没有业务输入 → 它造一个用户层禁用状态,再造一个 legacy managed 配置文件层 hook → 出来是断言:引擎仍加载这个 managed-layer hook,发现结果也显示它启用且受托管信任。

调用关系:测试框架调用它;它把配置层交给 ClaudeHooksEngine 和 discovery::discover_handlers,确认两边都不会让用户禁用覆盖托管层。

调用图:调用 4 个内部函数(new, new, discover_handlers, try_from);外部调用 9 个(new, new, default, assert!, assert_eq!, default, format!, tempdir, vec!)。

config_with_hook_state563–574 ↗
fn config_with_hook_state(key: &str, enabled: bool) -> TomlValue

作用:生成一份只包含 hook 启用状态的配置。它用来模拟用户在配置里把某个 hook 打开或关掉。

数据流:进去一个 hook key 和 enabled 布尔值 → 它用 JSON 形状拼出 hooks.state,再反序列化成 TomlValue → 出来是一份可放进配置层的状态配置。

调用关系:相关测试用它制造“用户试图禁用某个 hook”的场景,然后看发现逻辑是否尊重或忽略这个状态。

调用图:外部调用 2 个(from_value, json!)。

config_with_pre_tool_use_hook_and_states576–596 ↗
fn config_with_pre_tool_use_hook_and_states(
    command: &str,
    disabled_keys: [&str; N],
) -> TomlValue

作用:一次性生成“一个 PreToolUse hook 加上一批禁用状态”的配置。它适合测试用户 hook 和状态记录同时存在的情况。

数据流:进去一条 hook 命令和若干 disabled key → 它把每个 key 写成 enabled=false,同时写入一个 command 类型的 PreToolUse hook → 出来一个 TomlValue 配置。

调用关系:user_disablement_filters_non_managed_hooks_but_not_managed_hooks 用它构造用户层配置,然后交给配置栈参与发现。

调用图:被 1 处调用(user_disablement_filters_non_managed_hooks_but_not_managed_hooks);外部调用 2 个(from_value, json!)。

config_with_pre_tool_use_hook598–610 ↗
fn config_with_pre_tool_use_hook(command: &str) -> TomlValue

作用:生成一份最简单的 PreToolUse command hook 配置。它是测试里反复使用的“小样板”。

数据流:进去一条命令 → 它把命令放到 hooks.PreToolUse[0].hooks[0] 里 → 出来一个 TomlValue 配置。

调用关系:多个配置层测试用它快速创建用户、系统或托管文件层 hook,再让引擎去发现。

调用图:外部调用 2 个(from_value, json!)。

trusted_plugin_hook_stack612–653 ↗
fn trusted_plugin_hook_stack(
    config_path: AbsolutePathBuf,
    plugin_hook_sources: &[PluginHookSource],
) -> ConfigLayerStack

作用:给插件 hook 生成一份“已经信任过”的用户状态配置。插件 hook 默认需要信任校验,这个函数帮测试先把当前哈希写进去。

数据流:进去用户配置路径和插件 hook 来源列表 → 它先调用 discover_handlers 算出每个插件 hook 的 key 和当前哈希,再把 trusted_hash 写进 hooks.state → 出来一个 ConfigLayerStack。

调用关系:插件相关测试先调用它建立信任环境,再创建 ClaudeHooksEngine。这样测试能专注检查插件环境变量、来源和占位符展开。

调用图:调用 2 个内部函数(new, discover_handlers);被 2 处调用(plugin_hook_sources_expand_plugin_placeholders, plugin_hook_sources_run_with_plugin_env_and_plugin_source);外部调用 7 个(new, default, default, to_vec, from_value, json!, vec!)。

requirements_managed_hooks_load_when_managed_dir_is_missing656–724 ↗
fn requirements_managed_hooks_load_when_managed_dir_is_missing()

作用:验证托管目录即使还不存在,配置里的托管 hook 也能被加载出来。这样管理员配置不会因为目录暂时缺失就整条失效。

数据流:进去没有业务输入 → 它指向一个不存在的托管目录,配置一个 echo hook,创建引擎并预览 → 出来是断言:没有警告,能预览到 hook,handler 的命令和 source_path 都正确。

调用关系:它通过 managed_hooks_for_current_platform 构造配置,通过 cwd 构造请求,再调用 ClaudeHooksEngine 的预览能力验证发现结果。

调用图:调用 7 个内部函数(new, allow_any, new, new, cwd, managed_hooks_for_current_platform, new);外部调用 10 个(default, new, new, default, assert!, assert_eq!, default, json!, tempdir, vec!)。

allow_managed_hooks_only_false_keeps_unmanaged_hooks727–773 ↗
fn allow_managed_hooks_only_false_keeps_unmanaged_hooks()

作用:验证当“只允许托管钩子”策略为 false 时,普通用户 hook 不会被发现流程删掉。注意这里引擎未执行它,是因为信任/启用规则另有控制。

数据流:进去没有业务输入 → 它创建一个用户 PreToolUse hook,并把 allow_managed_hooks_only 设为 false → 出来是断言:引擎没有可执行 handler,但发现列表里能看到这个非托管 hook 和它的命令。

调用关系:它调用 requirements_with_managed_hooks_only 搭策略,再让 discover_handlers 检查“是否被发现”,用来区分“发现到”和“允许执行”这两件事。

调用图:调用 5 个内部函数(new, new, discover_handlers, requirements_with_managed_hooks_only, try_from);外部调用 6 个(new, new, assert!, assert_eq!, tempdir, vec!)。

allow_managed_hooks_only_in_config_toml_does_not_enable_policy776–827 ↗
fn allow_managed_hooks_only_in_config_toml_does_not_enable_policy()

作用:验证用户自己的 config.toml 里写 allow_managed_hooks_only=true,不会真的开启“只允许托管钩子”策略。这个策略必须来自受控 requirements,不能由普通用户自封。

数据流:进去没有业务输入 → 它在普通用户配置里放一个 hook,又手动加 allow_managed_hooks_only=true → 出来是断言:这个 hook 仍作为非托管 hook 被发现,而不是被策略过滤掉。

调用关系:它用 config_toml_with_pre_tool_use 造配置,再交给 ClaudeHooksEngine 和 discover_handlers,确认策略来源必须正确。

调用图:调用 5 个内部函数(new, new, discover_handlers, config_toml_with_pre_tool_use, try_from);外部调用 10 个(new, Boolean, new, default, assert!, assert_eq!, default, tempdir, unreachable!, vec!)。

allow_managed_hooks_only_skips_unmanaged_json_and_toml_hooks830–885 ↗
fn allow_managed_hooks_only_skips_unmanaged_json_and_toml_hooks()

作用:验证当受控策略要求“只允许托管钩子”时,普通 hooks.json 和普通 TOML 配置里的 hook 都会被跳过。

数据流:进去没有业务输入 → 它写一个 hooks.json,又在用户 config.toml 里放一个 hook,并开启 allow_managed_hooks_only → 出来是断言:引擎没有 handler,也没有警告。

调用关系:它用 requirements_with_managed_hooks_only 开启策略,再创建 ClaudeHooksEngine,重点检查普通文件来源不会混进可执行列表。

调用图:调用 4 个内部函数(new, new, requirements_with_managed_hooks_only, try_from);外部调用 6 个(new, new, assert!, write, tempdir, vec!)。

allow_managed_hooks_only_skips_unmanaged_plugin_hooks888–924 ↗
fn allow_managed_hooks_only_skips_unmanaged_plugin_hooks()

作用:验证“只允许托管钩子”策略也会跳过插件 hook。这样插件不能绕过组织只允许托管脚本的限制。

数据流:进去没有业务输入 → 它创建一个假的插件 hook 来源,并开启 allow_managed_hooks_only → 出来是断言:引擎没有加载任何 handler,也没有警告。

调用关系:它把插件来源和策略配置一起交给 ClaudeHooksEngine,观察发现阶段是否直接过滤插件 hook。

调用图:调用 5 个内部函数(new, new, requirements_with_managed_hooks_only, parse, try_from);外部调用 5 个(new, new, assert!, tempdir, vec!)。

allow_managed_hooks_only_keeps_managed_requirement_and_config_layer_hooks927–1016 ↗
fn allow_managed_hooks_only_keeps_managed_requirement_and_config_layer_hooks()

作用:验证“只允许托管钩子”策略开启时,真正托管的 hook 会被保留。它覆盖 requirements 托管、MDM、系统层、旧托管文件、旧 MDM 等多种来源。

数据流:进去没有业务输入 → 它创建多种托管来源的 PreToolUse hook,并开启 allow_managed_hooks_only → 出来是断言:引擎按预期保留五条托管命令,发现结果里每条都是 managed。

调用关系:它调用 pre_tool_use_hook_events、managed_hooks_for_current_platform 和 requirements_with_managed_hooks_only 搭复杂配置,再用引擎和 discover_handlers 验证过滤后的结果。

调用图:调用 7 个内部函数(new, new, discover_handlers, managed_hooks_for_current_platform, pre_tool_use_hook_events, requirements_with_managed_hooks_only, try_from);外部调用 7 个(new, new, assert!, assert_eq!, create_dir_all, tempdir, vec!)。

discovers_hooks_from_json_and_toml_in_the_same_layer1019–1135 ↗
fn discovers_hooks_from_json_and_toml_in_the_same_layer()

作用:验证同一配置层里同时有 hooks.json 和 config.toml hook 时,两边都会被发现,并且会给出提醒。提醒很重要,因为重复来源可能让用户误以为只配置了一处。

数据流:进去没有业务输入 → 它在同目录写 hooks.json,又在 TOML 配置里写 PreToolUse hook,创建系统配置层 → 出来是断言:引擎有一条关于同时加载两处的警告,预览能看到两条 hook,路径分别指向 JSON 和 TOML。

调用关系:它调用 cwd 构造请求,然后通过 ClaudeHooksEngine 的 warnings 和 preview_pre_tool_use 检查发现顺序、来源和路径。

调用图:调用 5 个内部函数(new, new, cwd, new, try_from);外部调用 15 个(default, new, Array, String, Table, new, default, assert!, assert_eq!, default (+5 more))。

profile_user_layers_load_shared_hooks_json_once1138–1226 ↗
fn profile_user_layers_load_shared_hooks_json_once()

作用:验证普通用户配置和 profile 配置共用同一个 hooks.json 时,只加载一次。profile 可以理解成一套额外的用户配置档案。

数据流:进去没有业务输入 → 它创建默认用户层和 work profile 用户层,二者旁边共享同一个 hooks.json → 出来是断言:引擎只有一个 handler,预览和 list_hooks 也都只列出一次。

调用关系:它用 cwd 构造请求,既调用 ClaudeHooksEngine 预览,也调用 crate::list_hooks 列表接口,确保两个入口的去重逻辑一致。

调用图:调用 5 个内部函数(new, new, cwd, new, try_from);外部调用 12 个(default, new, new, default, assert!, assert_eq!, default, list_hooks, write, json! (+2 more))。

malformed_hooks_json_is_reported_as_startup_warning1229–1282 ↗
fn malformed_hooks_json_is_reported_as_startup_warning()

作用:验证格式不对的 hooks.json 不会让程序悄悄失败,而是变成启动警告。这样用户能知道是哪份文件坏了、坏在哪里。

数据流:进去没有业务输入 → 它写入一个结构不符合预期的 hooks.json,创建系统配置层和引擎 → 出来是断言:没有 handler,warnings 里有一条解析失败信息,包含文件路径和 unknown field SessionStart

调用关系:测试框架运行它时,它只走 ClaudeHooksEngine 初始化阶段,重点检查启动时的配置读取错误报告。

调用图:调用 3 个内部函数(new, new, try_from);外部调用 9 个(new, new, default, assert!, assert_eq!, default, write, tempdir, vec!)。

plugin_hook_sources_run_with_plugin_env_and_plugin_source1285–1414 ↗
async fn plugin_hook_sources_run_with_plugin_env_and_plugin_source()

作用:验证插件 hook 执行时会带上插件专用环境变量,并且运行结果标记为 Plugin 来源。环境变量就是外部命令能读取的小纸条,告诉它插件目录在哪里。

数据流:进去是异步测试环境 → 它创建插件目录和一个会打印环境变量的 Python 脚本,先用 trusted_plugin_hook_stack 写入信任状态,再创建引擎、预览、列出并执行 hook → 出来是断言:来源是 Plugin,插件 ID 正确,运行完成,输出里包含 PLUGIN_ROOT 和 CLAUDE_PLUGIN_ROOT 的真实路径。

调用关系:它调用 trusted_plugin_hook_stack 准备信任,调用 cwd 准备请求,然后把工作交给 ClaudeHooksEngine 的预览和执行,以及 crate::list_hooks 的列表接口。

调用图:调用 6 个内部函数(new, cwd, trusted_plugin_hook_stack, parse, new, try_from);外部调用 10 个(new, new, assert_eq!, list_hooks, create_dir_all, write, from_str, json!, tempdir, vec!)。

plugin_hook_sources_expand_plugin_placeholders1417–1491 ↗
fn plugin_hook_sources_expand_plugin_placeholders()

作用:验证插件 hook 命令里的占位符会被替换成真实插件路径和插件数据路径。占位符就像模板里的空格,运行前必须填成实际地址。

数据流:进去没有业务输入 → 它创建一个命令,里面包含 ${PLUGIN_ROOT}、${CLAUDE_PLUGIN_ROOT}、${PLUGIN_DATA}、${CLAUDE_PLUGIN_DATA},再建立受信任插件栈和引擎 → 出来是断言:handler.command 已替换成真实路径,handler.env 也写入对应环境变量。

调用关系:它先调用 trusted_plugin_hook_stack 让插件 hook 通过信任检查,再检查 ClaudeHooksEngine 初始化后保存的 handler 内容。

调用图:调用 4 个内部函数(new, trusted_plugin_hook_stack, parse, try_from);外部调用 5 个(new, new, assert_eq!, tempdir, vec!)。

plugin_hook_load_warnings_are_startup_warnings1494–1508 ↗
fn plugin_hook_load_warnings_are_startup_warnings()

作用:验证插件加载阶段传进来的警告会原样出现在引擎启动警告里。这样插件 hook 加载失败不会被吞掉。

数据流:进去没有业务输入 → 它创建引擎时传入一条 plugin hook load warning → 出来是断言:engine.warnings() 正好包含这条警告。

调用关系:这是一个很小的启动阶段测试,只检查 ClaudeHooksEngine::new 是否把外部收集到的插件警告接进自己的 warnings 列表。

调用图:调用 1 个内部函数(new);外部调用 4 个(new, new, assert_eq!, vec!)。

prompts/src/permissions_instructions_tests.rs源码 ↗
testtest

这个文件像一张“说明书质检清单”。项目里会根据沙箱模式、网络开关、审批策略等设置,拼出一段给模型看的权限说明。这里的每个测试都造出一种场景,然后检查生成的文字里有没有关键句子。比如:只读模式要说只能读文件;工作区可写模式要列出可写目录;网络开启时要明确写出来;某些目录被禁止读取时,要提醒不要申请绕过。它还测试更细的审批规则,比如哪些审批类别会弹给用户、哪些会自动拒绝,以及 request_permissions 这个内置申请权限工具什么时候该出现。文件里有两个小辅助函数,用来拼出预期的“细粒度审批”文字,避免测试里重复写大段字符串。

函数细节19
renders_sandbox_mode_text14–29 ↗
fn renders_sandbox_mode_text()

作用:检查不同沙箱模式会不会生成正确的基础说明。沙箱可以理解成一个“安全围栏”,规定程序能读写哪些文件、能不能联网。

数据流:进去的是三组固定设置:工作区可写、只读、完全放开访问;每组还带网络是否受限。函数调用生成说明文字的逻辑,然后把结果和预先写好的标准句子逐字比较。出来的结果不是返回值,而是测试通过或失败;如果文字有一点不一样,测试就会报错。

调用关系:这是最基础的文字快照测试。它直接验证 sandbox_text 的输出,不再把工作交给本文件里的其他辅助函数。它用 assert_eq! 做最终裁判。

调用图:外部调用 1 个(assert_eq!)。

builds_permissions_with_network_access_override32–55 ↗
fn builds_permissions_with_network_access_override()

作用:确认在手动指定网络开启时,完整权限说明里确实会写“Network access is enabled.”,并且还会包含申请升级权限的指引。

数据流:进去的是工作区可写沙箱、网络开启、按请求审批等配置。函数把这些交给 PermissionsInstructions::from_permissions_with_network 生成说明对象,再取出正文文本。之后检查正文里是否包含网络开启和如何申请权限升级的文字。

调用关系:它覆盖的是从配置直接生成权限说明的流程。主要调用 from_permissions_with_network,再用断言确认生成结果没有漏掉关键提示。

调用图:调用 1 个内部函数(from_permissions_with_network);外部调用 2 个(assert!, empty)。

builds_permissions_from_profile58–85 ↗
fn builds_permissions_from_profile()

作用:检查从运行时权限配置文件生成说明时,能不能正确识别可写目录和网络开启状态。

数据流:进去的是当前目录 /tmp、一个可写的仓库目录、文件系统沙箱规则和网络开启规则。函数先把这些规则做成 PermissionProfile,再交给 PermissionsInstructions::from_permission_profile 生成正文。最后检查正文里是否写了工作区可写、网络开启,以及那个可写目录路径。

调用关系:它测试的是“运行时权限配置 → 用户可读说明”的入口。它先调用 PermissionProfile::from_runtime_permissions 组装权限档案,再调用 from_permission_profile 生成最终文字。

调用图:调用 4 个内部函数(from_permission_profile, from_runtime_permissions, restricted, from_absolute_path);外部调用 4 个(from, assert!, empty, vec!)。

builds_permissions_from_profile_with_denied_reads88–131 ↗
fn builds_permissions_from_profile_with_denied_reads()

作用:确认当某些路径被明确禁止读取时,说明文字会把这些禁区列出来,并提醒不要申请绕过。

数据流:进去的是一个测试用当前目录、一个被禁止读取的目录、一个被禁止读取的通配路径,以及网络受限设置。函数把“根目录可读,但某些路径拒绝”的规则做成权限档案,再生成说明正文。出来的检查结果是:正文必须包含“被拒绝的文件读取”章节、不要申请更多权限的提醒、具体被拒绝的路径和通配规则。

调用关系:它覆盖权限说明里比较敏感的一块:禁止读取的内容。它通过 from_runtime_permissionsfrom_permission_profile 走完整生成流程,用 test_path_buf 准备稳定的测试路径。

调用图:调用 4 个内部函数(from_permission_profile, from_runtime_permissions, restricted, from_absolute_path);外部调用 4 个(assert!, test_path_buf, empty, vec!)。

includes_request_rule_instructions_for_on_request134–156 ↗
fn includes_request_rule_instructions_for_on_request()

作用:检查在“按请求审批”模式下,如果已有被批准的命令前缀,说明文字会把这些规则告诉模型。

数据流:进去的是一个空执行策略,然后测试往里面加了一条规则:git pull 这个命令前缀允许执行。函数用这份策略生成权限说明,再检查正文里是否出现规则类型、已批准命令前缀标题,以及 git pull 的 JSON 形式。

调用关系:它验证执行策略和说明文字之间的连接。先设置 Policy,再交给 from_permissions_with_network,最后用断言确认规则没有在提示词里丢失。

调用图:调用 1 个内部函数(from_permissions_with_network);外部调用 2 个(assert!, empty)。

includes_request_permissions_tool_instructions_for_unless_trusted_when_enabled159–176 ↗
fn includes_request_permissions_tool_instructions_for_unless_trusted_when_enabled()

作用:确认在 unless-trusted 审批模式下,只要 request_permissions 工具启用,说明文字就会介绍这个工具。

数据流:进去的是工作区可写、网络开启、unless-trusted 审批、工具启用这些设置。函数生成权限正文,然后检查正文同时包含审批策略名称和 request_permissions Tool 章节。

调用关系:它测试工具说明出现的一个条件分支。主要把配置交给 from_permissions_with_network,然后检查生成文本。

调用图:调用 1 个内部函数(from_permissions_with_network);外部调用 2 个(assert!, empty)。

includes_request_permissions_tool_instructions_for_on_failure_when_enabled179–196 ↗
fn includes_request_permissions_tool_instructions_for_on_failure_when_enabled()

作用:确认在 on-failure 审批模式下,如果权限申请工具启用,也会把工具用法写进说明。

数据流:进去的是工作区可写、网络开启、失败后审批、工具启用这些配置。函数生成正文,并检查正文里是否有 on-failure 策略说明和 request_permissions Tool 章节。

调用关系:它和上一个测试类似,但换了审批策略。它帮助确认工具说明不是只在某一种审批模式里才出现。

调用图:调用 1 个内部函数(from_permissions_with_network);外部调用 2 个(assert!, empty)。

includes_request_permission_rule_instructions_for_on_request_when_enabled199–216 ↗
fn includes_request_permission_rule_instructions_for_on_request_when_enabled()

作用:检查在“按请求审批”并允许命令内联申请权限时,说明文字会教模型使用额外权限字段。

数据流:进去的是按请求审批、执行权限审批开启、但 request_permissions 工具关闭的配置。函数生成正文,然后检查里面有没有 with_additional_permissionsadditional_permissions 这些字段名。

调用关系:它验证的是“在执行命令时顺手申请额外权限”的说明。它调用 from_permissions_with_network 生成文本,不涉及工具章节。

调用图:调用 1 个内部函数(from_permissions_with_network);外部调用 2 个(assert!, empty)。

includes_request_permissions_tool_instructions_for_on_request_when_tool_is_enabled219–236 ↗
fn includes_request_permissions_tool_instructions_for_on_request_when_tool_is_enabled()

作用:确认在“按请求审批”模式下,如果内置申请权限工具开启,说明里会明确告诉模型这个工具可用。

数据流:进去的是按请求审批、工具开启、内联执行权限申请关闭的配置。函数生成正文,检查是否包含工具章节标题和“本会话可用”这类说明。

调用关系:它专门覆盖 request_permissions 工具在 on-request 模式下的提示。它把配置交给 from_permissions_with_network,再检查文字。

调用图:调用 1 个内部函数(from_permissions_with_network);外部调用 2 个(assert!, empty)。

on_request_includes_tool_guidance_alongside_inline_permission_guidance_when_both_exist239–256 ↗
fn on_request_includes_tool_guidance_alongside_inline_permission_guidance_when_both_exist()

作用:确认两种申请权限的方法同时开启时,说明文字不会只写其中一种。

数据流:进去的是按请求审批、内联执行权限申请开启、request_permissions 工具也开启的配置。函数生成正文,然后同时检查内联字段 with_additional_permissions 和工具章节都存在。

调用关系:它保护的是组合场景,避免后加的工具说明把原来的命令权限说明挤掉,或反过来。核心仍然是调用 from_permissions_with_network

调用图:调用 1 个内部函数(from_permissions_with_network);外部调用 2 个(assert!, empty)。

auto_review_approvals_append_auto_review_specific_guidance259–271 ↗
fn auto_review_approvals_append_auto_review_specific_guidance()

作用:检查审批由自动审查器处理时,说明文字会加入自动审查特有的提醒。

数据流:进去的是按请求审批、审查者为 auto_review、空执行策略,以及两个权限申请功能都关闭。函数调用 approval_text 生成审批说明,再检查里面有自动审查者标识、没有另一种审查者标识,并且包含“更安全替代方案”相关提醒。

调用关系:它直接测试审批说明生成函数 approval_text 的一个分支。这里不生成完整权限说明,只看审批段落本身。

调用图:外部调用 2 个(assert!, empty)。

auto_review_approvals_omit_auto_review_specific_guidance_when_approval_is_never274–285 ↗
fn auto_review_approvals_omit_auto_review_specific_guidance_when_approval_is_never()

作用:确认当审批策略是“永不审批”时,即使审查者配置成自动审查,也不会出现自动审查的额外提示。

数据流:进去的是 Never 审批策略、自动审查者、空执行策略和关闭的工具开关。函数生成审批说明后,检查里面没有 auto_reviewguardian_subagent 相关文字。

调用关系:它和前一个测试形成对照:自动审查提示只有在确实可能审批时才应该出现。它直接调用 approval_text 并用断言检查结果。

调用图:外部调用 2 个(assert!, empty)。

granular_categories_section287–289 ↗
fn granular_categories_section(title: &str, categories: &[&str]) -> String

作用:这是一个测试辅助函数,用来把一个标题和若干审批类别拼成一小段预期文字。

数据流:进去的是标题字符串和类别列表。函数把标题放第一行,把类别用换行符连起来,最后返回整段字符串。它不改动外部状态。

调用关系:它被 granular_prompt_expected 调用,用来少写重复的拼接代码。底层只用 format! 完成字符串组装。

调用图:被 1 处调用(granular_prompt_expected);外部调用 1 个(format!)。

granular_prompt_expected291–317 ↗
fn granular_prompt_expected(
    prompted_categories: &[&str],
    rejected_categories: &[&str],
    include_shell_permission_request_instructions: bool,
    include_request_permissions_tool_section:

作用:这是一个测试辅助函数,用来按同一套规则拼出“细粒度审批”模式下应该出现的完整文字。

数据流:进去的是会提示用户的类别、会自动拒绝的类别,以及两个布尔开关:是否包含命令权限申请说明、是否包含权限工具章节。函数先放入细粒度审批的开头说明,再按需要追加可提示类别、被拒绝类别、命令内联申请说明和工具说明,最后用空行连接成完整字符串。

调用关系:它调用 granular_categories_section 拼分类段落,并被多个细粒度审批测试拿来生成标准答案。这样测试关注差异,不用每次手写整段长文本。

调用图:调用 1 个内部函数(granular_categories_section);外部调用 1 个(vec!)。

granular_policy_lists_prompted_and_rejected_categories_separately320–354 ↗
fn granular_policy_lists_prompted_and_rejected_categories_separately()

作用:检查细粒度审批模式会把“还能询问用户的类别”和“直接自动拒绝的类别”分开列清楚。

数据流:进去的是一份细粒度配置:只有 rulesrequest_permissions 为真,但工具不可用。函数生成审批说明,并把结果和预期的三段文字逐字比较:开头、可提示类别、自动拒绝类别。

调用关系:它直接测试 approval_textAskForApproval::Granular 的输出。这里使用 assert_eq! 做精确比较,确保段落顺序和内容都固定。

调用图:外部调用 3 个(Granular, assert_eq!, empty)。

granular_policy_includes_command_permission_instructions_when_sandbox_approval_can_prompt357–386 ↗
fn granular_policy_includes_command_permission_instructions_when_sandbox_approval_can_prompt()

作用:确认细粒度审批里只要沙箱审批可以弹给用户,并且命令内联权限申请开启,就会出现命令权限申请说明。

数据流:进去的是所有审批类别都允许提示的细粒度配置,以及内联执行权限申请开启、工具关闭的开关。函数生成审批说明,再和 granular_prompt_expected 拼出的标准文字比较;标准文字里应包含命令权限申请说明。

调用关系:它用 granular_prompt_expected 作为预期生成器,验证 approval_text 在“全部允许提示”的细粒度场景下没有漏掉命令权限说明。

调用图:外部调用 3 个(Granular, assert_eq!, empty)。

granular_policy_omits_shell_permission_instructions_when_inline_requests_are_disabled389–418 ↗
fn granular_policy_omits_shell_permission_instructions_when_inline_requests_are_disabled()

作用:确认即使沙箱审批可以提示用户,只要命令内联权限申请功能关闭,说明里就不该教模型用内联申请方式。

数据流:进去的是所有审批类别都允许提示的细粒度配置,但内联执行权限申请关闭、工具也关闭。函数生成审批说明,并和不包含命令权限申请段落的预期文字比较。

调用关系:它和上一个测试形成对照,说明同一审批配置下,功能开关会影响最终提示内容。它同样依赖 granular_prompt_expected 组装标准答案。

调用图:外部调用 3 个(Granular, assert_eq!, empty)。

granular_policy_includes_request_permissions_tool_only_when_that_prompt_can_still_fire421–451 ↗
fn granular_policy_includes_request_permissions_tool_only_when_that_prompt_can_still_fire()

作用:检查 request_permissions 工具章节只在对应审批类别仍然允许提示时出现。

数据流:函数先生成一份允许 request_permissions 提示的细粒度说明,并检查里面有工具章节。接着再生成一份把 request_permissions 类别关闭的说明,即使工具开关本身是开启的,也检查里面不能有工具章节。

调用关系:它测试两个条件必须同时满足:工具可用,且对应审批类别没有被自动拒绝。它直接调用 approval_text 两次,用断言比较两种结果。

调用图:外部调用 3 个(Granular, assert!, empty)。

granular_policy_lists_request_permissions_category_without_tool_section_when_tool_unavailable454–471 ↗
fn granular_policy_lists_request_permissions_category_without_tool_section_when_tool_unavailable()

作用:确认当 request_permissions 工具不可用时,细粒度审批说明不会单独列出这个工具相关类别,也不会出现工具章节。

数据流:进去的是只有 request_permissions 类别为真的细粒度配置,但工具开关关闭。函数生成审批说明,然后检查正文里既没有 - request_permissions 这一类目,也没有 request_permissions Tool 章节。

调用关系:它补上了工具不可用时的边界情况。它直接测试 approval_text 的输出,确保提示词不会告诉模型一个实际不能用的申请入口。

调用图:外部调用 3 个(Granular, assert!, empty)。

memories/write/src/guard_tests.rs源码 ↗
testtest run

这是一组测试代码,不是正式运行时给用户用的功能。它关心的问题很实际:如果系统调用外部服务有额度限制,就不能在额度快耗尽或已经被限流时还盲目启动。文件里先用 snapshot 和 window 做出简化版“额度快照”,就像给油箱表拍一张照片:主额度用了多少、副额度用了多少、有没有已经到上限。几个测试再把这些假数据喂给 snapshot_allows_startup,看它是否按配置的“至少还要剩多少百分比”来决定能不能启动。重要的是,它同时检查主额度和副额度;只要其中一个太低,启动就应该被拦住。

函数细节5
snapshot4–18 ↗
fn snapshot(
    primary_used_percent: Option<f64>,
    secondary_used_percent: Option<f64>,
) -> RateLimitSnapshot

作用:快速造一个假的限流快照,方便测试不用每次手写一大堆字段。外行可以把它理解成“做一张额度状态表”。

数据流:输入是主额度和副额度的已用百分比,可以有也可以没有。它把这些百分比包装成 RateLimitSnapshot,并填上测试需要的限流编号,其他不关心的字段留空。输出是一份可交给启动检查函数使用的假快照。

调用关系:当测试需要模拟某种额度状态时,会先用它做测试材料;它内部把单个百分比交给 window 包成窗口信息。startup_check_uses_configured_remaining_threshold 和 startup_check_skips_when_limit_is_reached 会用它准备输入,然后再验证启动检查结果。

调用图:被 2 处调用(startup_check_skips_when_limit_is_reached, startup_check_uses_configured_remaining_threshold)。

window20–26 ↗
fn window(used_percent: f64) -> RateLimitWindow

作用:把一个“已用百分比”变成限流窗口对象。窗口可以理解成某段时间内的额度统计,但这里测试只关心用了多少。

数据流:输入是一个数字,比如 89.9,表示额度已经用了 89.9%。它把这个数字放进 RateLimitWindow,重置时间和窗口分钟数这些测试不需要的信息都留空。输出是一个可放进快照里的窗口对象。

调用关系:它是 snapshot 的小助手,负责把简单数字包装成系统实际使用的数据形状。测试本身不需要关心窗口的复杂细节,只靠它保持数据格式正确。

startup_check_uses_configured_remaining_threshold29–41 ↗
fn startup_check_uses_configured_remaining_threshold()

作用:测试启动检查会不会尊重配置里的“最低剩余额度百分比”。也就是说,门槛设成 10% 和 11% 时,结果应该不一样。

数据流:它先造出一份主额度已用 89.9%、副额度已用 50% 的快照。然后分别用最低剩余 10% 和 11% 去检查:前者应该允许启动,后者应该拒绝启动。最后用断言确认结果符合预期。

调用关系:这个测试先调用 snapshot 准备假额度数据,再把数据交给 snapshot_allows_startup。assert! 是测试框架里的检查动作,用来确认函数给出的允许或拒绝结果没有跑偏。

调用图:调用 1 个内部函数(snapshot);外部调用 1 个(assert!)。

startup_check_skips_when_primary_or_secondary_is_too_low44–66 ↗
fn startup_check_skips_when_primary_or_secondary_is_too_low()

作用:测试只要主额度或副额度其中一个剩得不够,启动就应该被跳过。它防止系统只看一边额度,从而误判“还能启动”。

数据流:它依次准备三种情况:主额度太低、副额度太低、两边都刚好够。每种情况都交给 snapshot_allows_startup,并用断言检查:前两种必须拒绝启动,最后一种应该允许启动。

调用关系:这个测试站在“守门员”的角度验证完整规则:启动检查不能只看主额度,也不能只看副额度。它最终依靠 assert! 把预期结果固定下来,避免以后改代码时把这个规则弄坏。

调用图:外部调用 1 个(assert!)。

startup_check_skips_when_limit_is_reached69–79 ↗
fn startup_check_skips_when_limit_is_reached()

作用:测试当系统已经明确收到“限流已达到”的信号时,即使百分比看起来还不高,也必须拒绝启动。

数据流:它先做一份看起来只用了 10% 的快照,然后手动把“已触发限流”的标记写进去。接着调用 snapshot_allows_startup,预期结果必须是不允许启动,并用断言确认。

调用关系:这个测试用 snapshot 准备基础数据,再额外模拟服务端明确告知限流。它验证 snapshot_allows_startup 会优先相信这个红灯信号,而不是只看百分比数字。

调用图:调用 1 个内部函数(snapshot);外部调用 1 个(assert!)。