test(core): 使用隔离会话验证二十次打包冷启动
This commit is contained in:
@@ -4,6 +4,12 @@
|
|||||||
|
|
||||||
**状态:实施中,未 done,未通过生产发布门禁。** 本文记录工程进度,不将单元测试或开发机运行替代[生产化计划](../architecture/第三阶段生产化工程规划与验收目标.md)中 A–E 的完整验收。
|
**状态:实施中,未 done,未通过生产发布门禁。** 本文记录工程进度,不将单元测试或开发机运行替代[生产化计划](../architecture/第三阶段生产化工程规划与验收目标.md)中 A–E 的完整验收。
|
||||||
|
|
||||||
|
## 当前状态摘要
|
||||||
|
|
||||||
|
截至 `7b69537`,Host Sync capability 已开启,扩展 capability 仍关闭。笔记与 Task CRUD 已接入当前 Rust Vault;同步具备上传/拉取、冲突解决、非空初始合并、100 MiB 续传、主题和编辑器偏好、持久退避。Workspace 当前 schema 9。凭据备份/恢复及 WTS 撤销已实现,但实机锁屏验收仍缺。
|
||||||
|
|
||||||
|
最近完整测试:后端 900 项(Task 增量后)、前端 517 项、Rust desktop 52 项;后两者在重试持久化增量后复跑。以下各节为历史增量,出现的旧 capability、旧 schema 和旧测试数量描述对应当时状态,以本摘要及后续增量为准。OS 沙箱、Rust 扩展管理、剩余逻辑数据类别、完整故障/性能/发布验收仍未完成。
|
||||||
|
|
||||||
## 持续实施增量
|
## 持续实施增量
|
||||||
|
|
||||||
- Rust 同步新增 schema 4 inbox/boundary、稳定远端 file_id、remote 来源的移动/删除日志和冲突保留。真实 HTTP 双客户端测试覆盖历史拉取、零回流、本机提交回放保护和同改冲突;文件提交前后的 inbox 重启测试通过。冲突解决和用户界面正在继续实施,仍不开放完整 Sync capability。
|
- Rust 同步新增 schema 4 inbox/boundary、稳定远端 file_id、remote 来源的移动/删除日志和冲突保留。真实 HTTP 双客户端测试覆盖历史拉取、零回流、本机提交回放保护和同改冲突;文件提交前后的 inbox 重启测试通过。冲突解决和用户界面正在继续实施,仍不开放完整 Sync capability。
|
||||||
@@ -116,3 +122,10 @@ Core 的独立数据目录目前不等于已授权 Vault。Python 旧笔记写
|
|||||||
- 取消/切换绑定不增加失败次数,旧绑定回调不能修改新绑定;保存的错误仅允许有长度限制的机器码,不保存服务端任意响应正文。Retry-After 和指数退避保持 1–3600 秒限制。界面对停止自动重试状态不再显示误导性的倒计时。
|
- 取消/切换绑定不增加失败次数,旧绑定回调不能修改新绑定;保存的错误仅允许有长度限制的机器码,不保存服务端任意响应正文。Retry-After 和指数退避保持 1–3600 秒限制。界面对停止自动重试状态不再显示误导性的倒计时。
|
||||||
- 连续关闭重开 20 次验证失败次数与时间保留,另验证认证停止、状态清除、取消、错误正文过滤和新旧绑定隔离。当前是绑定级轮次状态;每个上传作业的独立尝试历史、强制刷新后的 401 重试和完整服务故障矩阵仍需完成。
|
- 连续关闭重开 20 次验证失败次数与时间保留,另验证认证停止、状态清除、取消、错误正文过滤和新旧绑定隔离。当前是绑定级轮次状态;每个上传作业的独立尝试历史、强制刷新后的 401 重试和完整服务故障矩阵仍需完成。
|
||||||
- 全量前端 97 文件 / 517 项、Rust desktop 全目标 52 项通过(另 1 个父测试驱动并强杀的辅助入口);TypeScript 两项目检查和 Clippy `-D warnings` 通过。日志 `.build/retry-frontend-tests.log`、`.build/retry-rust-tests.log`。Core 打包正在更新,不视为签名发布包。
|
- 全量前端 97 文件 / 517 项、Rust desktop 全目标 52 项通过(另 1 个父测试驱动并强杀的辅助入口);TypeScript 两项目检查和 Clippy `-D warnings` 通过。日志 `.build/retry-frontend-tests.log`、`.build/retry-rust-tests.log`。Core 打包正在更新,不视为签名发布包。
|
||||||
|
|
||||||
|
|
||||||
|
## 增量:最新 Core 打包与 20 次冷启动
|
||||||
|
|
||||||
|
- 使用锁定 packaging 环境重新生成 PyInstaller onedir,清单包含 1523 个文件。构建日志 `.build/core-build-production.log`,产物 `.build/sidecar/dist/opennexus-core` 与 `.build/sidecar/manifest.json`。
|
||||||
|
- 新增显式打包测试 `cargo test --locked --features desktop --test core_bundle packaged_core_twenty -- --ignored`,调用实际可执行文件并在每次启动前校验完整文件清单。20 个独立临时数据目录、20 个不同会话代际、带鉴权和代际头的 HTTP 健康请求全部成功,逐次退出后端口关闭。测试耗时 95.64 秒,日志 `.build/core-packaged-cold-starts.log`。初版测试遗漏代际请求头导致拒绝,修正测试后通过。
|
||||||
|
- 该测试按要求先构建后显式运行,普通源码全目标测试会跳过此入口。Clippy `-D warnings` 通过。此证据来自当前 Windows 开发机,不能替代干净 VM、MSVC 签名安装包、两台真实设备或完整 A-01 验收。
|
||||||
|
|||||||
@@ -19,3 +19,55 @@ fn bundle_rejects_modified_missing_and_extra_files() {
|
|||||||
std::fs::remove_file(file).unwrap();
|
std::fs::remove_file(file).unwrap();
|
||||||
assert!(verify_bundle(dir.path(), manifest).is_err());
|
assert!(verify_bundle(dir.path(), manifest).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore = "requires scripts/build-core.py; run explicitly after building the isolated Core"]
|
||||||
|
fn packaged_core_twenty_cold_starts_use_isolated_data_and_rotate_sessions() {
|
||||||
|
use notesagent_host::core::CoreSupervisor;
|
||||||
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
io::{Read, Write},
|
||||||
|
path::Path,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
let output = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../.build/sidecar");
|
||||||
|
let bundle = output.join("dist/opennexus-core").canonicalize().unwrap();
|
||||||
|
let manifest = std::fs::read_to_string(output.join("manifest.json")).unwrap();
|
||||||
|
let executable = bundle.join(if cfg!(windows) {
|
||||||
|
"opennexus-core.exe"
|
||||||
|
} else {
|
||||||
|
"opennexus-core"
|
||||||
|
});
|
||||||
|
let temp = tempfile::tempdir().unwrap();
|
||||||
|
let mut generations = HashSet::new();
|
||||||
|
for attempt in 0..20 {
|
||||||
|
let mut core = CoreSupervisor::new(
|
||||||
|
executable.clone(),
|
||||||
|
vec![],
|
||||||
|
bundle.clone(),
|
||||||
|
temp.path().join(format!("run-{attempt}")),
|
||||||
|
)
|
||||||
|
.with_bundle_manifest(manifest.clone());
|
||||||
|
let request = core.request_session("/health").unwrap();
|
||||||
|
assert!(generations.insert(request.generation.clone()));
|
||||||
|
let endpoint = request
|
||||||
|
.url
|
||||||
|
.trim_start_matches("http://")
|
||||||
|
.trim_end_matches("/health");
|
||||||
|
let mut stream = std::net::TcpStream::connect(endpoint).unwrap();
|
||||||
|
stream
|
||||||
|
.set_read_timeout(Some(Duration::from_secs(20)))
|
||||||
|
.unwrap();
|
||||||
|
write!(stream, "GET /health HTTP/1.1\r\nHost: {endpoint}\r\nAuthorization: {}\r\nX-Core-Generation: {}\r\nConnection: close\r\n\r\n", request.authorization.as_str(), request.generation).unwrap();
|
||||||
|
let mut response = String::new();
|
||||||
|
stream.read_to_string(&mut response).unwrap();
|
||||||
|
assert!(
|
||||||
|
response.starts_with("HTTP/1.1 200"),
|
||||||
|
"packaged health failed on attempt {attempt}"
|
||||||
|
);
|
||||||
|
assert!(!response.contains(request.authorization.as_str()));
|
||||||
|
drop(stream);
|
||||||
|
drop(core);
|
||||||
|
assert!(std::net::TcpStream::connect(endpoint).is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user