Update: 将子项目从 submodule 转为完整内容

- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用
- 添加所有子项目的完整源代码
- 保留原始 .git 为 .git.bak 备份
This commit is contained in:
freedak
2026-07-04 19:20:46 +08:00
parent 54d6465fa7
commit f7a720204a
3360 changed files with 802660 additions and 3 deletions
@@ -0,0 +1,305 @@
//! observe 集成测试共享接线(`#[ignore]`,本机/打包 chrome)。
//!
//! 任务 5 的契约母本(launch→connect→run_attach_loop→enable_auto_attach→createTarget→arm→
//! call)已被复制过一次;任务 6 起所有 observe 集成测试**复用**本模块的 helper,勿再复制母本。
//!
//! 与契约母本(`observe_fixtures.rs::observe_inject_contract_iframe`)的关系:那个测试手动接线
//! 注入侧的 `incrementalAriaSnapshot` 单帧调用,验证**注入契约**(aria 形态快照);本模块走更高层
//! ——直接建一个 [`CdpBackend`] 并 navigate 到 fixture,让被测对象就是 `engine.observe()` 全链
//! (逐帧缝合 + 脱敏 + 代际翻新 ref 表)。
//!
//! 手动跑(本机 Windows 有系统 Chrome):
//! set NOMIFUN_CHROME_BINARY=...\chrome.exe
//! cargo nextest run -p nomi-browser-engine --run-ignored all -E 'test(observe_)'
//! 首跑写 `.snap.new``cargo insta accept`(或手动改名 .snap)接受为基线。
//! 跑完核对任务管理器无残留 chromeBuilder kill_on_drop 应自动清)。
#![allow(dead_code)] // 不同集成测试文件用到的 helper 子集不同;未用项不报警。
use std::sync::Arc;
use nomi_browser_engine::backend::CdpBackend;
use nomi_browser_engine::firewall::{EgressApprover, FirewallConfig, HostResolver};
use nomi_browser_engine::launch::{launch_chrome, LaunchConfig};
/// fixture 的 file:// URL。`file://` + 单个前导斜杠 + POSIX 路径:`CARGO_MANIFEST_DIR`
/// 在 unix 是 `/abs`(已带前导斜杠)、在 windows 是 `C:/abs`(需补一个),故仅在缺失时补斜杠。
/// 旧写法 `file:///{manifest}` 在 unix 上产生**四**斜杠(`file:////Users/...`)——chrome 容忍但
/// 回报时归一成三斜杠,导致 navigate 的 redirect 判定(请求 url != final url)误报 redirected。
pub fn fixture_url(name: &str) -> String {
let manifest = env!("CARGO_MANIFEST_DIR").replace('\\', "/");
let abs = if manifest.starts_with('/') {
manifest
} else {
format!("/{manifest}")
};
format!("file://{abs}/tests/fixtures/{name}")
}
/// 解析 chromeenv NOMIFUN_CHROME_BINARY > 打包 > 数据目录 > 下载兜底)+ 托管 launchheadless
/// + 自建 transport connect + flatten auto-attach + 取一个 page session,建一个 [`CdpBackend`]。
///
/// `profile`:本测试专属 user-data-dir 后缀,避免并发测试争用同一 profile。
/// 返回的 backend 持有 chrome 进程句柄——Drop 即清理整棵进程树(kill_on_drop);测试结束自然回收。
///
/// 不挂下载沙箱(`download_dir=None`)——下载相关集成测试用
/// [`build_backend_for_fixture_with_downloads`]。evaluate 全权 OFF(默认 default-deny)。
/// 防火墙用 `FirewallConfig::default()`IP 封禁开 + 跨域 POST 门控开 = 现行为)。
pub async fn build_backend_for_fixture(profile: &str) -> CdpBackend {
build_backend_for_fixture_inner(profile, None, false, false, FirewallConfig::default(), None, None, None)
.await
}
/// 同 [`build_backend_for_fixture`],但 **evaluate 全权模式 ON**`EngineConfig.evaluate_full_power
/// = true` 的等价 test seam)——F1-sec 验「全权 LIVE 开 → act(Evaluate) 放行」。
pub async fn build_backend_for_fixture_full_power(profile: &str) -> CdpBackend {
build_backend_for_fixture_inner(profile, None, true, false, FirewallConfig::default(), None, None, None)
.await
}
/// **SD-6 persistent-login mutex 验证 test seam**`evaluate_full_power = true` +
/// `evaluate_persistent_login` = 调用方指定。用于验证 persistent-login LIVE 值穿透到
/// `EvaluateGate.persistent_login`——`persistent_login=true` 时互斥生效(full_power+persistent
/// → Blocked);`persistent_login=false` 时全权正常放行(控制组)。
pub async fn build_backend_for_fixture_persistent_login(
profile: &str,
persistent_login: bool,
) -> CdpBackend {
build_backend_for_fixture_inner(
profile,
None,
true, // evaluate_full_power = ON (to test the mutex)
persistent_login,
FirewallConfig::default(),
None,
None,
None,
)
.await
}
/// **W4d 持久登录验证 test seam**:同 [`build_backend_for_fixture`],但额外注入
/// `storage_state`(上层从 vault `load_storage_state` 解出的登录态 JSON)——引擎在 page
/// 建好后 `restore_cookies`+`restore_local_storage`**启动注入**灌登录态。配合 W4d 集成测试验
/// 「跨引擎/会话持久登录」(会话 A capture+save vault → 会话 B 新引擎 load vault → 此 seam 注入 →
/// 登录态恢复存活)。`storage_state=None` 走零注入(现行为)。
///
/// Always runs on the default browser context (no per-pet isolation).
pub async fn build_backend_for_fixture_with_storage_state(
profile: &str,
storage_state: Option<serde_json::Value>,
) -> CdpBackend {
build_backend_for_fixture_inner(
profile,
None,
false,
false,
FirewallConfig::default(),
None,
storage_state,
None,
)
.await
}
/// **P3-G1 注入链验证 test seam**:同 [`build_backend_for_fixture`],但注入一个**自定义**
/// [`FirewallConfig`](而非 `default()`)。配合 [`CdpBackend::firewall_config_for_test`] 断言
/// 「自定义配置经 from_launched 真的注入到引擎、未被硬编码 default 吞掉」。
pub async fn build_backend_for_fixture_with_firewall(
profile: &str,
firewall: FirewallConfig,
) -> CdpBackend {
build_backend_for_fixture_inner(profile, None, false, false, firewall, None, None, None).await
}
/// **P3-D2 验证 test seam**:注入自定义 [`FirewallConfig`] **+ 一个出口审批通道**
/// [`EgressApprover`])。配合跨域 POST fixture 验「被门控请求悬挂等裁决 → 批准 continue / 拒绝
/// fail / 超时 / 无通道 fail-closed」。`egress_approver=None` 走引擎 fail-closed 默认(验泄漏窗口闭合)。
///
/// **SD-1 交互**`dns_resolver` 注入式——跨域 POST 的目标域要先过 DNS→IP SSRF 守卫才到 approver。
/// 测试须注入一个把探针域映射到**公网 IP** 的 fake resolver,否则真实 DNS 对离线伪域返 NXDOMAIN →
/// 守卫 fail-closed 在 approver **之前**就 Blockapprover 永不被咨询)。`None` = 默认 TokioResolver。
pub async fn build_backend_for_fixture_with_egress(
profile: &str,
firewall: FirewallConfig,
egress_approver: Option<Arc<dyn EgressApprover>>,
dns_resolver: Option<Arc<dyn HostResolver>>,
) -> CdpBackend {
build_backend_for_fixture_inner(profile, None, false, false, firewall, egress_approver, None, dns_resolver).await
}
/// 同 [`build_backend_for_fixture`],但挂 E4 下载沙箱(`Browser.setDownloadBehavior(allowAndName)`
/// 落 `download_dir` + 下载事件循环打 MOTW)。返回 `(backend, download_dir)`——下载测试用 download_dir
/// 校验落盘文件 + Zone.Identifier ADS。
pub async fn build_backend_for_fixture_with_downloads(
profile: &str,
) -> (CdpBackend, std::path::PathBuf) {
let download_dir = std::env::temp_dir().join(format!("nomifun-dl-{profile}-downloads"));
// 干净起点:清旧目录残留(上次跑的 GUID 文件),再建。
let _ = std::fs::remove_dir_all(&download_dir);
std::fs::create_dir_all(&download_dir).expect("create download dir");
let backend = build_backend_for_fixture_inner(
profile,
Some(download_dir.to_string_lossy().into_owned()),
false,
false,
FirewallConfig::default(),
None,
None,
None,
)
.await;
(backend, download_dir)
}
/// **单标签验证 test seamheadful**:同 [`build_backend_for_fixture`] 但 **headful**
/// `force_headless=false`)——验 `--no-startup-window` 在 headful 下被 REMOTE_DEBUGGING
/// keep-alive 拴住(chrome 不无窗口自退、launch 不报 "exited before DevTools port"+ 恰好
/// 一个受控 page。会开一个**可见** chrome 窗口(需本机有显示器;本机 Windows 有)。
pub async fn build_backend_for_fixture_headful(profile: &str) -> CdpBackend {
let chrome = nomi_browser_engine::acquire::resolve_chrome_path(
&std::env::temp_dir().join("nomifun-browser-data"),
None,
)
.await
.expect("resolve chrome (set NOMIFUN_CHROME_BINARY)");
let cfg = LaunchConfig {
chrome_path: chrome,
user_data_dir: std::env::temp_dir().join(format!("nomifun-observe-{profile}-profile")),
headful: true,
};
// force_headless=false → headful(带可见窗口)。这是 --no-startup-window keep-alive 的风险路径。
let launched = launch_chrome(&cfg, false).await.expect("launch headful chrome");
CdpBackend::from_launched(
launched,
true, // headful
true, // display_available
None,
None, // workspace_dir: headful fixture 不上传
false,
false, // evaluate_persistent_login
FirewallConfig::default(),
None,
None,
nomi_browser_engine::KnownSecretValues::default(),
None, // dns_resolver: 默认 TokioResolver
)
.await
.expect("build CdpBackend (headful)")
}
/// 同 [`build_backend_for_fixture_headful`] 但注入自定义 [`FirewallConfig`]。OOPIF 验证用:
/// **headful = 真浏览器进程模型**,site-isolation 才会把跨站 iframe 起成**独立渲染进程(OOPIF**;
/// `--headless=new` 是单渲染进程,不起 OOPIF。配合 `NOMIFUN_CHROME_BINARY` + 测试侧 `NOMI_CHROME_EXTRA_ARGS`
/// --host-resolver-rules/--site-per-process)使用。会开一个**可见** chrome 窗口(需本机有显示器)。
pub async fn build_backend_for_fixture_headful_with_firewall(
profile: &str,
firewall: FirewallConfig,
) -> CdpBackend {
let chrome = nomi_browser_engine::acquire::resolve_chrome_path(
&std::env::temp_dir().join("nomifun-browser-data"),
None,
)
.await
.expect("resolve chrome (set NOMIFUN_CHROME_BINARY)");
let cfg = LaunchConfig {
chrome_path: chrome,
user_data_dir: std::env::temp_dir().join(format!("nomifun-observe-{profile}-profile")),
headful: true,
};
let launched = launch_chrome(&cfg, false).await.expect("launch headful chrome");
CdpBackend::from_launched(launched, true, true, None, None, false, false, firewall, None, None, nomi_browser_engine::KnownSecretValues::default(), None)
.await
.expect("build CdpBackend (headful+firewall)")
}
/// **Headful + 下载沙箱 test seam**Task 6 / `TODO(verify-headful-printToPDF)`):同
/// [`build_backend_for_fixture_headful`]headful,真可见窗口)但额外挂 E4 下载沙箱
/// `download_dir`)。专供 **save_as_pdf 的 headful 校验**——验 `Page.printToPDF` 在 headful
/// Chrome 下是否仍产非空 PDFheadful 历史上对 printToPDF 有限制)。返回 `(backend, download_dir)`。
/// 会开一个**可见** chrome 窗口(需本机有显示器)。
pub async fn build_backend_for_fixture_headful_with_downloads(
profile: &str,
) -> (CdpBackend, std::path::PathBuf) {
let download_dir =
std::env::temp_dir().join(format!("nomifun-dl-{profile}-headful-downloads"));
// 干净起点:清旧目录残留,再建。
let _ = std::fs::remove_dir_all(&download_dir);
std::fs::create_dir_all(&download_dir).expect("create download dir");
let chrome = nomi_browser_engine::acquire::resolve_chrome_path(
&std::env::temp_dir().join("nomifun-browser-data"),
None,
)
.await
.expect("resolve chrome (set NOMIFUN_CHROME_BINARY)");
let cfg = LaunchConfig {
chrome_path: chrome,
user_data_dir: std::env::temp_dir().join(format!("nomifun-observe-{profile}-profile")),
headful: true,
};
let launched = launch_chrome(&cfg, false).await.expect("launch headful chrome");
let backend = CdpBackend::from_launched(
launched,
true, // headful
true, // display_available
Some(download_dir.to_string_lossy().into_owned()),
None, // workspace_dirsave_as_pdf 不依赖上传沙箱
false,
false, // evaluate_persistent_login
FirewallConfig::default(),
None,
None,
nomi_browser_engine::KnownSecretValues::default(),
None, // dns_resolver: 默认 TokioResolver
)
.await
.expect("build CdpBackend (headful+downloads)");
(backend, download_dir)
}
// 构造参数随安全配置增长(download/full_power/persistent/firewall/egress/storage/dns_resolver);
// 与 build_backend / from_launched 同源的已知取舍(见 cdp.rs 的 too_many_arguments allow + EngineRuntimeParams TODO)。
#[allow(clippy::too_many_arguments)]
async fn build_backend_for_fixture_inner(
profile: &str,
download_dir: Option<String>,
evaluate_full_power: bool,
evaluate_persistent_login: bool,
firewall: FirewallConfig,
egress_approver: Option<Arc<dyn EgressApprover>>,
storage_state: Option<serde_json::Value>,
dns_resolver: Option<Arc<dyn HostResolver>>,
) -> CdpBackend {
let chrome = nomi_browser_engine::acquire::resolve_chrome_path(
&std::env::temp_dir().join("nomifun-browser-data"),
None,
)
.await
.expect("resolve chrome (set NOMIFUN_CHROME_BINARY)");
let cfg = LaunchConfig {
chrome_path: chrome,
user_data_dir: std::env::temp_dir().join(format!("nomifun-observe-{profile}-profile")),
headful: false,
};
let launched = launch_chrome(&cfg, true).await.expect("launch chrome");
// SD-2fixture 测试默认给 temp_dir 作为 workspace(上传沙箱需要一个根;实际产品环境是 per-pet
// workspace,此处用 temp_dir 让 temp 下创建的测试文件都能通过沙箱校验)。
let workspace_dir = Some(std::env::temp_dir());
// headful=false / display=falseobserve 不依赖显示,capabilities 字段对集成断言无关紧要。
CdpBackend::from_launched(
launched,
false,
false,
download_dir,
workspace_dir,
evaluate_full_power,
evaluate_persistent_login,
firewall,
egress_approver,
storage_state,
nomi_browser_engine::KnownSecretValues::default(),
dns_resolver,
)
.await
.expect("build CdpBackend")
}