mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
test(e2e): wipe memory tree during test reset
## Summary - Extends `openhuman.test_reset` so E2E resets also wipe Memory Tree state via the existing `memory_tree_wipe_all` path. - Adds reset summary fields for memory-tree rows, content directories, and Composio sync-state rows so Appium logs show exactly what was cleared. - Adds a focused async unit test covering memory-tree content directory cleanup through the new reset helper. ## Problem - #1862 tracks that `openhuman.test_reset` only cleared auth/onboarding/cron state, while Memory Tree data could survive between specs in a shared Appium session. - That means memory-oriented specs can pass or fail based on chunks, wiki content, or sync cursors left by an earlier spec. ## Solution - Calls `read_rpc::wipe_all_rpc(&config)` from `test_support::rpc::reset` after cron cleanup and before config/auth clearing. - Surfaces `memory_tree_rows_deleted`, `memory_tree_dirs_removed`, and `memory_tree_sync_state_cleared` in `ResetSummary`, `reset_json`, and the controller schema. - Keeps this as a scoped #1862 slice; other domains listed in the issue can land as separate hook PRs. ## Submission Checklist - [x] Tests added or updated (happy path + at least one failure / edge case) per [Testing Strategy](../gitbooks/developing/testing-strategy.md#failure-path-requirement) — focused unit test covers Memory Tree content-dir cleanup; existing `wipe_all_rpc` owns table/sync-state failure behavior. - [x] **Diff coverage >= 80%** — new Rust test covers the new helper path; CI coverage gate is authoritative. - [x] Coverage matrix updated — N/A: E2E test-support reset plumbing, no product feature row added/removed. - [x] All affected feature IDs from the matrix are listed in the PR description under `## Related` — N/A: no feature matrix row. - [x] No new external network dependencies introduced (mock backend used per [Testing Strategy](../gitbooks/developing/testing-strategy.md#mock-policy)) - [x] Manual smoke checklist updated if this touches release-cut surfaces ([`docs/RELEASE-MANUAL-SMOKE.md`](../docs/RELEASE-MANUAL-SMOKE.md)) — N/A: test-support RPC only. - [x] Linked issue closed via `Closes #NNN` in the `## Related` section — N/A: scoped slice; references #1862 without closing the umbrella. ## Impact - E2E specs that call `resetApp(...)` now start without prior Memory Tree chunks, summary/wiki files, or sync cursors. - User runtime behavior is unchanged unless the E2E-only `openhuman.test_reset` controller is compiled/enabled. ## Related - Refs #1862 - Follow-up PR(s)/TODOs: add hook coverage for remaining #1862 domains: channels, skills, webview_accounts, threads, notifications, webhooks, cost, referral, composio. --- ## AI Authored PR Metadata (required for Codex/Linear PRs) ### Linear Issue - Key: N/A - URL: N/A ### Commit & Branch - Branch: `codex/1862-test-reset-memory-tree` - Commit SHA: `48630b40f69400d6a3c5e055e80c25486e3bba6d` ### Validation Run - [x] `pnpm --filter openhuman-app format:check` — N/A: no frontend files changed. - [x] `pnpm typecheck` — N/A: no TypeScript files changed. - [x] Focused tests: attempted `cargo test -p openhuman test_support::rpc::tests::wipe_memory_tree_removes_content_dirs_and_reports_summary --lib`. - [x] Rust fmt/check (if changed): `cargo fmt --all --check`; `git diff --check`. - [x] Tauri fmt/check (if changed): N/A: no Tauri shell files changed. ### Validation Blocked - `command:` `cargo test -p openhuman test_support::rpc::tests::wipe_memory_tree_removes_content_dirs_and_reports_summary --lib` - `error:` local Windows build fails before tests in `whisper-rs-sys` because `clang.dll` / `libclang.dll` is missing and `LIBCLANG_PATH` is unset. - `impact:` focused test did not execute locally; CI Linux/Windows runners with libclang are expected to compile and run it. ### Behavior Changes - Intended behavior change: E2E-only test reset now wipes Memory Tree state in addition to cron/auth/onboarding state. - User-visible effect: none in normal builds; E2E logs show memory-tree wipe counts. ### Parity Contract - Legacy behavior preserved: cron cleanup, auth clearing, onboarding reset, and active-user removal still run and still short-circuit on failure. - Guard/fallback/dispatch parity checks: Memory Tree wipe reuses the existing user-facing `wipe_all_rpc` implementation instead of adding a second deletion path. ### Duplicate / Superseded PR Handling - Duplicate PR(s): N/A - Canonical PR: this PR - Resolution (closed/superseded/updated): N/A <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Reset now clears memory-tree persistent data during fresh-install resets and reports rows deleted, directories removed, and sync-state entries cleared. * **Documentation** * Updated reset operation schema and outputs to include memory-tree cleanup fields. * **Tests** * Added a unit test verifying memory-tree wipe removes content directories and reports summary metrics. * **Bug Fixes** * Increased core startup readiness timeout to reduce startup failures. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2308?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: aqilaziz <gonzes7@gmail.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
This commit is contained in:
co-authored by
Steven Enamakel
parent
28338a603f
commit
369a39288c
@@ -276,14 +276,17 @@ impl CoreProcessHandle {
|
||||
}
|
||||
}
|
||||
|
||||
// Readiness budget: 100 iterations × 100ms = 10s. The embedded
|
||||
// Readiness budget: 200 iterations × 100ms = 20s. The embedded
|
||||
// core's JSON-RPC controller registry has grown over time and
|
||||
// the previous 4s budget started flaking under CI worker load
|
||||
// earlier 4s/10s budgets started flaking under CI worker load
|
||||
// (issue: core_process tests intermittently failing with
|
||||
// "core process did not become ready"). 10s is still well
|
||||
// under any user-visible startup expectation and matches the
|
||||
// upper end of observed cold-start times.
|
||||
for _ in 0..100 {
|
||||
// "core process did not become ready"), especially under
|
||||
// cargo-llvm-cov instrumentation where the binary runs ~2x
|
||||
// slower. 20s is still well under any user-visible startup
|
||||
// expectation: in normal runs the ready signal arrives in well
|
||||
// under 1s and the loop exits immediately; the headroom only
|
||||
// matters on heavily loaded instrumented CI workers.
|
||||
for _ in 0..200 {
|
||||
if !received_ready {
|
||||
match ready_rx.try_recv() {
|
||||
Ok(ready_signal) => {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
//! - no authenticated user (active_user.toml removed, api_key cleared)
|
||||
//! - onboarding not yet completed (chat_onboarding_completed=false)
|
||||
//! - no cron jobs (so the post-onboarding seed re-creates `morning_briefing`)
|
||||
//! - no memory-tree chunks, summaries, content dirs, or sync cursors
|
||||
//!
|
||||
//! It is intentionally in-process: the sidecar keeps running. Specs reload
|
||||
//! the webview after this call so the renderer also starts from a blank slate.
|
||||
@@ -14,6 +15,7 @@ use serde_json::json;
|
||||
use crate::openhuman::config::Config;
|
||||
use crate::openhuman::config::{clear_active_user, default_root_openhuman_dir};
|
||||
use crate::openhuman::cron;
|
||||
use crate::openhuman::memory::tree::read_rpc;
|
||||
use crate::rpc::RpcOutcome;
|
||||
|
||||
const E2E_MODE_ENV_VAR: &str = "OPENHUMAN_E2E_MODE";
|
||||
@@ -22,11 +24,21 @@ const E2E_MODE_ENV_VAR: &str = "OPENHUMAN_E2E_MODE";
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct ResetSummary {
|
||||
pub cron_jobs_removed: usize,
|
||||
pub memory_tree_rows_deleted: u64,
|
||||
pub memory_tree_dirs_removed: Vec<String>,
|
||||
pub memory_tree_sync_state_cleared: u64,
|
||||
pub onboarding_was_completed: bool,
|
||||
pub api_key_was_set: bool,
|
||||
pub active_user_cleared: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct MemoryTreeResetSummary {
|
||||
rows_deleted: u64,
|
||||
dirs_removed: Vec<String>,
|
||||
sync_state_cleared: u64,
|
||||
}
|
||||
|
||||
fn ensure_e2e_mode_enabled() -> Result<(), String> {
|
||||
ensure_e2e_mode_value(std::env::var(E2E_MODE_ENV_VAR).ok().as_deref())
|
||||
}
|
||||
@@ -69,6 +81,15 @@ pub async fn reset() -> Result<RpcOutcome<ResetSummary>, String> {
|
||||
.map_err(|e| format!("test_reset: cron wipe failed: {e:#}"))?;
|
||||
log::debug!("[test_reset] step=wipe_cron ok removed={cron_jobs_removed}");
|
||||
|
||||
log::debug!("[test_reset] step=wipe_memory_tree start");
|
||||
let memory_tree = wipe_memory_tree(&config).await?;
|
||||
log::debug!(
|
||||
"[test_reset] step=wipe_memory_tree ok rows={} dirs={:?} sync_state={}",
|
||||
memory_tree.rows_deleted,
|
||||
memory_tree.dirs_removed,
|
||||
memory_tree.sync_state_cleared
|
||||
);
|
||||
|
||||
log::debug!("[test_reset] step=clear_config_fields start");
|
||||
config.chat_onboarding_completed = false;
|
||||
config.api_key = None;
|
||||
@@ -88,8 +109,16 @@ pub async fn reset() -> Result<RpcOutcome<ResetSummary>, String> {
|
||||
root.display()
|
||||
);
|
||||
|
||||
let memory_tree_log = format!(
|
||||
"memory_tree wiped rows={} dirs={:?} sync_state={}",
|
||||
memory_tree.rows_deleted, memory_tree.dirs_removed, memory_tree.sync_state_cleared
|
||||
);
|
||||
|
||||
let summary = ResetSummary {
|
||||
cron_jobs_removed,
|
||||
memory_tree_rows_deleted: memory_tree.rows_deleted,
|
||||
memory_tree_dirs_removed: memory_tree.dirs_removed,
|
||||
memory_tree_sync_state_cleared: memory_tree.sync_state_cleared,
|
||||
onboarding_was_completed,
|
||||
api_key_was_set,
|
||||
active_user_cleared: true,
|
||||
@@ -104,6 +133,7 @@ pub async fn reset() -> Result<RpcOutcome<ResetSummary>, String> {
|
||||
summary,
|
||||
vec![
|
||||
format!("removed {cron_jobs_removed} cron jobs"),
|
||||
memory_tree_log,
|
||||
format!("chat_onboarding_completed: {onboarding_was_completed} → false"),
|
||||
format!("api_key cleared (was set: {api_key_was_set})"),
|
||||
"active_user.toml removed".to_string(),
|
||||
@@ -111,12 +141,27 @@ pub async fn reset() -> Result<RpcOutcome<ResetSummary>, String> {
|
||||
))
|
||||
}
|
||||
|
||||
async fn wipe_memory_tree(config: &Config) -> Result<MemoryTreeResetSummary, String> {
|
||||
let outcome = read_rpc::wipe_all_rpc(config)
|
||||
.await
|
||||
.map_err(|e| format!("test_reset: memory_tree wipe failed: {e}"))?;
|
||||
let value = outcome.value;
|
||||
Ok(MemoryTreeResetSummary {
|
||||
rows_deleted: value.rows_deleted,
|
||||
dirs_removed: value.dirs_removed,
|
||||
sync_state_cleared: value.sync_state_cleared,
|
||||
})
|
||||
}
|
||||
|
||||
/// Convenience helper for handlers that prefer a raw JSON envelope.
|
||||
#[allow(dead_code)]
|
||||
pub async fn reset_json() -> Result<serde_json::Value, String> {
|
||||
let outcome = reset().await?;
|
||||
Ok(json!({
|
||||
"removed_cron_jobs": outcome.value.cron_jobs_removed,
|
||||
"memory_tree_rows_deleted": outcome.value.memory_tree_rows_deleted,
|
||||
"memory_tree_dirs_removed": outcome.value.memory_tree_dirs_removed,
|
||||
"memory_tree_sync_state_cleared": outcome.value.memory_tree_sync_state_cleared,
|
||||
"previously_onboarded": outcome.value.onboarding_was_completed,
|
||||
"previously_authenticated": outcome.value.api_key_was_set,
|
||||
}))
|
||||
@@ -124,8 +169,9 @@ pub async fn reset_json() -> Result<serde_json::Value, String> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{ensure_e2e_mode_value, reset, E2E_MODE_ENV_VAR};
|
||||
use super::*;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
use tempfile::TempDir;
|
||||
|
||||
static E2E_MODE_ENV_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
|
||||
@@ -163,4 +209,28 @@ mod tests {
|
||||
ensure_e2e_mode_value(Some("true")).expect("true enables E2E mode");
|
||||
ensure_e2e_mode_value(Some("yes")).expect("yes enables E2E mode");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn wipe_memory_tree_removes_content_dirs_and_reports_summary() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let mut config = Config::default();
|
||||
config.workspace_dir = tmp.path().join("workspace");
|
||||
|
||||
let content_root = config.memory_tree_content_root();
|
||||
let raw_dir = content_root.join("raw");
|
||||
let wiki_dir = content_root.join("wiki");
|
||||
std::fs::create_dir_all(&raw_dir).unwrap();
|
||||
std::fs::create_dir_all(&wiki_dir).unwrap();
|
||||
std::fs::write(raw_dir.join("chunk.md"), "test chunk").unwrap();
|
||||
std::fs::write(wiki_dir.join("summary.md"), "test summary").unwrap();
|
||||
|
||||
let summary = wipe_memory_tree(&config).await.unwrap();
|
||||
|
||||
assert_eq!(summary.rows_deleted, 0);
|
||||
assert_eq!(summary.sync_state_cleared, 0);
|
||||
assert!(summary.dirs_removed.contains(&"raw".to_string()));
|
||||
assert!(summary.dirs_removed.contains(&"wiki".to_string()));
|
||||
assert!(!raw_dir.exists());
|
||||
assert!(!wiki_dir.exists());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ pub fn schemas(function: &str) -> ControllerSchema {
|
||||
namespace: "test",
|
||||
function: "reset",
|
||||
description:
|
||||
"Wipe persistent sidecar state in-place: clears auth, onboarding, and cron jobs. \
|
||||
"Wipe persistent sidecar state in-place: clears auth, onboarding, cron jobs, and memory tree. \
|
||||
E2E specs call this between tests so each starts from a fresh-install baseline.",
|
||||
inputs: vec![],
|
||||
outputs: vec![FieldSchema {
|
||||
@@ -64,6 +64,24 @@ pub fn schemas(function: &str) -> ControllerSchema {
|
||||
comment: "Number of cron jobs deleted from the workspace database.",
|
||||
required: true,
|
||||
},
|
||||
FieldSchema {
|
||||
name: "memory_tree_rows_deleted",
|
||||
ty: TypeSchema::U64,
|
||||
comment: "Number of memory-tree SQLite rows deleted.",
|
||||
required: true,
|
||||
},
|
||||
FieldSchema {
|
||||
name: "memory_tree_dirs_removed",
|
||||
ty: TypeSchema::Array(Box::new(TypeSchema::String)),
|
||||
comment: "Memory-tree content directories removed.",
|
||||
required: true,
|
||||
},
|
||||
FieldSchema {
|
||||
name: "memory_tree_sync_state_cleared",
|
||||
ty: TypeSchema::U64,
|
||||
comment: "Number of Composio memory-tree sync-state rows deleted.",
|
||||
required: true,
|
||||
},
|
||||
FieldSchema {
|
||||
name: "onboarding_was_completed",
|
||||
ty: TypeSchema::Bool,
|
||||
|
||||
Reference in New Issue
Block a user