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 -->

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](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:
Aqil Aziz
2026-05-20 17:07:59 -07:00
committed by GitHub
co-authored by Steven Enamakel
parent 28338a603f
commit 369a39288c
3 changed files with 99 additions and 8 deletions
+9 -6
View File
@@ -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) => {
+71 -1
View File
@@ -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());
}
}
+19 -1
View File
@@ -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,