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,246 @@
//! **P3 W4clocalStorage storage_state 捕获/恢复 端到端集成**`#[ignore]`,本机/打包 chrome)。
//!
//! 验证 W4clocalStorageorigin-bound)捕获/恢复机制):
//! - **localStorage 跨 capture/restore 往返保真(origin-bound,核心验收)**:在引擎 A 页面
//! `setItem` 几个键 → `capture_local_storage` 读回(origin + 键值保真)→ 在**新**引擎 B
//! navigate 到同 origin → `restore_local_storage` 灌进去 → 页面读回 `getItem` 验值生效。
//! - **origin-bound**:捕获/恢复都绑**当前页面 origin**localStorage 同源分区,无法跨 origin 全局取/设);
//! restore 只灌 state 中 origin == 当前页面 origin 的那份(不跨 origin 误写)。
//! - **默认 context 零回归**capture/restore 走默认 context 页面,机制仍工作。
//!
//! **W4c 不验 IndexedDBbest-effort/TODO`OriginStorage.index_db` 恒 None)也不验磁盘 vault 持久化
//! W4d**。只走**内存往返**capture → StorageState → restore+ origin-bound 注入。
//!
//! **为何用真实 `https://example.com` 而非 file:// fixture**localStorage 在 `file://` origin 上
//! Chrome 行为不稳(origin 形态 `null`/`file://` 视版本而异,且 storage 可能被分区/禁用);`https://
//! example.com` 是稳定可达的真实 origin(与 W4b cookie 测试同选),localStorage 在其上可靠工作——
//! 这是「origin-bound 往返保真」最贴近真实登录态的验证场景。
//!
//! 手动跑(本机 Windows 有系统 Chrome):
//! set NOMIFUN_CHROME_BINARY=...\chrome.exe
//! cargo nextest run -p nomi-browser-engine --run-ignored all -E 'test(w4c)'
//! 跑完核对任务管理器无残留 chromeBuilder kill_on_drop + disposeOnDetach 自动清)。
//!
//! 真实结果(本机首跑 eprintln 出捕到的 localStorage——填回任务汇报)。
use nomi_browser_engine::storage_state::{OriginStorage, StorageState};
use nomi_browser_engine::BrowserEngine;
mod common;
const ORIGIN: &str = "https://example.com";
/// 在当前页面(默认 page world`setItem` 一组键值(测试 seam:模拟「页面写了 localStorage 登录态」)。
async fn seed_local_storage(
backend: &nomi_browser_engine::backend::CdpBackend,
pairs: &[(&str, &str)],
) {
// 用 JSON 安全编码键值,逐键 setItem。
let pairs_owned: Vec<[&str; 2]> = pairs.iter().map(|(k, v)| [*k, *v]).collect();
let pairs_json = serde_json::to_string(&pairs_owned).expect("json pairs");
let script = format!(
"(() => {{ const pairs = {pairs_json}; for (const [k, v] of pairs) localStorage.setItem(k, v); return localStorage.length; }})()"
);
let r = backend
.__eval_page_world_for_test(&script)
.await
.expect("seed localStorage setItem");
eprintln!("=== seeded localStorage, length now = {:?}", r.get("value"));
}
/// 读回当前页面某 localStorage 键(默认 page world `getItem`)。None = 键不存在(返回 JS null)。
async fn read_local_storage(
backend: &nomi_browser_engine::backend::CdpBackend,
key: &str,
) -> Option<String> {
let key_json = serde_json::to_string(key).expect("json key");
let script = format!("localStorage.getItem({key_json})");
let r = backend
.__eval_page_world_for_test(&script)
.await
.expect("read localStorage getItem");
r.get("value").and_then(|v| v.as_str()).map(|s| s.to_string())
}
/// **核心:localStorage 跨 capture/restore 往返保真(origin-bound**。
/// engine Anavigate example.com → seed localStorage → captureorigin + 键值保真)。
/// engine B(新引擎,独立 profile):navigate example.com → restore A 的快照 → 页面 getItem 读回值生效。
#[tokio::test]
#[ignore = "需本机/打包 chromeset NOMIFUN_CHROME_BINARY 后 --run-ignored all"]
async fn w4c_local_storage_round_trip_origin_bound() {
// ── 捕获侧:engine A ──────────────────────────────────────────────────────
let backend_a = common::build_backend_for_fixture("w4c-capA").await;
let nav = backend_a
.navigate(ORIGIN, false)
.await
.expect("navigate A to example.com");
eprintln!("=== w4c capture nav === final_url={}", nav.final_url);
// 页面写 localStorage(含特殊字符值,验往返不被吞)。
seed_local_storage(
&backend_a,
&[
("ls_auth", "jwt.eyJhbGciOiJ9.sig-abc123"),
("ls_theme", "dark"),
("ls_json", "{\"a\":1,\"b\":\"x=y&z\"}"),
],
)
.await;
// capture:读回当前 origin 的 localStorageorigin + 键值保真)。
let captured = backend_a
.capture_local_storage()
.await
.expect("capture_local_storage")
.expect("page has an origin to capture");
eprintln!(
"=== w4c captured origin={} items={:?}",
captured.origin, captured.local_storage
);
// **核心验收:捕到的 origin == 页面 origin;键值保真**。
assert_eq!(captured.origin, ORIGIN, "captured origin must match page origin (origin-bound)");
let find = |k: &str| {
captured
.local_storage
.iter()
.find(|i| i.name == k)
.map(|i| i.value.as_str())
};
assert_eq!(find("ls_auth"), Some("jwt.eyJhbGciOiJ9.sig-abc123"), "auth value captured");
assert_eq!(find("ls_theme"), Some("dark"), "theme value captured");
assert_eq!(find("ls_json"), Some("{\"a\":1,\"b\":\"x=y&z\"}"), "special chars captured intact");
// 组装一份 storage_state(只含 localStorage——本任务范围)。
let state = StorageState {
cookies: vec![],
local_storage: vec![captured.clone()],
};
// ── 恢复侧:engine B(独立 profilelocalStorage 必为空白起点)──────────────────
let backend_b = common::build_backend_for_fixture("w4c-resB").await;
backend_b
.navigate(ORIGIN, false)
.await
.expect("navigate B to example.com");
// 恢复前:B 的 localStorage 该键应不存在(新引擎 + 独立 profile = 干净起点)。
assert_eq!(
read_local_storage(&backend_b, "ls_auth").await,
None,
"fresh engine B must not have A's localStorage before restore"
);
// restore:把 A 的 localStorage 灌进 B 当前页面(origin-boundorigin 匹配 example.com)。
backend_b
.restore_local_storage(&state)
.await
.expect("restore_local_storage into B");
// 页面读回:值生效(往返保真——这是「localStorage 登录态经 restore 灌入、页面读得到」的真实信号)。
let auth = read_local_storage(&backend_b, "ls_auth").await;
let theme = read_local_storage(&backend_b, "ls_theme").await;
let json = read_local_storage(&backend_b, "ls_json").await;
eprintln!("=== w4c restored read-back === ls_auth={auth:?} ls_theme={theme:?} ls_json={json:?}");
assert_eq!(
auth.as_deref(),
Some("jwt.eyJhbGciOiJ9.sig-abc123"),
"restored localStorage value must be readable in page (round-trip fidelity)"
);
assert_eq!(theme.as_deref(), Some("dark"));
assert_eq!(json.as_deref(), Some("{\"a\":1,\"b\":\"x=y&z\"}"), "special chars survive restore");
// re-capture 在 B:应捕到刚恢复的项(capture↔restore 对称)。
let recap = backend_b
.capture_local_storage()
.await
.expect("re-capture B")
.expect("B has origin");
assert!(
recap.local_storage.iter().any(|i| i.name == "ls_auth"),
"re-capture after restore must see the restored key: {:?}",
recap.local_storage
);
}
/// **origin-boundrestore 不跨 origin 误写**。state 里只有 example.com 的 localStorage;当前页面停在
/// about:blank(无 example.com origin)→ restore 是 no-op(不把 example.com 的键写进无关页面)。
#[tokio::test]
#[ignore = "需本机/打包 chromeset NOMIFUN_CHROME_BINARY 后 --run-ignored all"]
async fn w4c_restore_is_origin_bound_no_cross_origin_write() {
let backend = common::build_backend_for_fixture("w4c-origin").await;
// 当前页面在另一个 originfixture file://),与 state 里的 example.com 不同 origin。
backend
.navigate(&common::fixture_url("act-c1.html"), false)
.await
.expect("navigate to fixture origin");
// state 只含 example.com 的 localStorage。
let state = StorageState {
cookies: vec![],
local_storage: vec![OriginStorage::new_local_storage(
ORIGIN,
[("ls_cross", "should-not-leak".to_string())]
.into_iter()
.map(|(k, v)| (k.to_string(), v)),
)],
};
// restore:当前页面 origin != example.com → no-op(绝不把 example.com 键写进 fixture 页面)。
backend
.restore_local_storage(&state)
.await
.expect("restore is no-op when origin mismatches (origin-bound)");
// fixture 页面不该出现 example.com 的键(origin-bound 防误写)。
let leaked = read_local_storage(&backend, "ls_cross").await;
eprintln!("=== w4c origin-bound no-cross-write === fixture-page ls_cross={leaked:?}");
assert_eq!(
leaked, None,
"restore must NOT write example.com's localStorage into a different-origin page (origin-bound)"
);
}
/// **默认 context 零回归**capture/restore 走默认 context 页面,机制仍工作
/// (往返保真)。
#[tokio::test]
#[ignore = "需本机/打包 chromeset NOMIFUN_CHROME_BINARY 后 --run-ignored all"]
async fn w4c_default_context_local_storage_works() {
let backend = common::build_backend_for_fixture("w4c-default").await;
backend
.navigate(ORIGIN, false)
.await
.expect("navigate default context to example.com");
seed_local_storage(&backend, &[("ls_default", "v-default-ctx")]).await;
let captured = backend
.capture_local_storage()
.await
.expect("capture default context")
.expect("default context page has origin");
eprintln!(
"=== w4c default-context captured origin={} items={:?}",
captured.origin, captured.local_storage
);
assert_eq!(captured.origin, ORIGIN);
assert!(
captured.local_storage.iter().any(|i| i.name == "ls_default" && i.value == "v-default-ctx"),
"default-context localStorage must round-trip: {:?}",
captured.local_storage
);
// restore 同一份回当前页面(覆盖即幂等)→ 读回仍在。
let state = StorageState {
cookies: vec![],
local_storage: vec![captured],
};
backend
.restore_local_storage(&state)
.await
.expect("restore default context");
assert_eq!(
read_local_storage(&backend, "ls_default").await.as_deref(),
Some("v-default-ctx"),
"default-context restore round-trip"
);
}