Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions clients/agent-runtime/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 35 additions & 10 deletions clients/cerebro/tests/backup_restore_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ async fn backup_restore_preserves_data() {
};

// Start Cerebro server
let (service, shutdown_tx, base_url) = helpers::start_cerebro_server(config.clone())
.await
.expect("failed to start cerebro server");
let (service, shutdown_tx, base_url, _server_handle) =
helpers::start_cerebro_server(config.clone())
.await
.expect("failed to start cerebro server");

// Wait for service to become ready
helpers::wait_for_ready(&base_url, 10)
Expand Down Expand Up @@ -111,18 +112,30 @@ async fn backup_restore_preserves_data() {
.expect("mem_search request failed");

let search_body: serde_json::Value = search_resp.json().await.expect("failed to parse search");
let hits = search_body["result"]["output"]["results"]
.as_array()
.expect("mem_search should return results array");
assert!(
!hits.is_empty(),
"mem_search should return at least one result"
);
assert!(
search_body["result"].is_object(),
"mem_search should return results"
hits.iter().any(|hit| {
hit["summary"]
.as_str()
.map(|s| s.contains("test content"))
.unwrap_or(false)
}),
"mem_search should return memories with expected content"
);

// ========== BACKUP PHASE ==========
// Gracefully shutdown Cerebro
let _ = shutdown_tx.send(true);

// Drop service to ensure all handles are released
drop(service);

// Wait for RocksDB to release locks and flush buffers
tokio::time::sleep(Duration::from_secs(2)).await;

Expand Down Expand Up @@ -172,7 +185,7 @@ async fn backup_restore_preserves_data() {
);

// Restart Cerebro with restored data
let (_service, _shutdown_tx, base_url) = helpers::start_cerebro_server(config)
let (_service, _shutdown_tx, base_url, _server_handle) = helpers::start_cerebro_server(config)
.await
.expect("failed to restart cerebro after restore");

Expand Down Expand Up @@ -212,9 +225,21 @@ async fn backup_restore_preserves_data() {
.expect("mem_search request failed after restore");

let search_body: serde_json::Value = search_resp.json().await.expect("failed to parse search");
let hits = search_body["result"]["output"]["results"]
.as_array()
.expect("mem_search should return results array after restore");
assert!(
!hits.is_empty(),
"mem_search should return at least one result after restore"
);
assert!(
search_body["result"].is_object(),
"mem_search should return results after restore"
hits.iter().any(|hit| {
hit["summary"]
.as_str()
.map(|s| s.contains("test content"))
.unwrap_or(false)
}),
"mem_search should return memories with expected content after restore"
);

// Verify specific memory IDs are present
Expand Down
19 changes: 13 additions & 6 deletions clients/cerebro/tests/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,16 @@ pub fn auth_header() -> Option<&'static str> {
}

/// Starts Cerebro server in background task with temporary storage.
/// Returns the service, shutdown channel, and base URL.
/// Returns the service, shutdown channel, base URL, and server task handle.
#[allow(dead_code)]
pub async fn start_cerebro_server(
config: CerebroConfig,
) -> anyhow::Result<(Arc<CerebroService>, watch::Sender<bool>, String)> {
) -> anyhow::Result<(
Arc<CerebroService>,
watch::Sender<bool>,
String,
tokio::task::JoinHandle<()>,
)> {
let service = Arc::new(CerebroService::from_config(config.clone()).await?);
let listener = TcpListener::bind("127.0.0.1:0").await?;
let addr = listener.local_addr()?;
Expand All @@ -52,14 +57,14 @@ pub async fn start_cerebro_server(
let (shutdown_tx, shutdown_rx) = watch::channel(false);
let service_clone = service.clone();

tokio::spawn(async move {
let server_handle = tokio::spawn(async move {
axum::serve(listener, service_clone.router())
.with_graceful_shutdown(wait_for_shutdown(shutdown_rx))
.await
.expect("server should run");
});

Ok((service, shutdown_tx, base_url))
Ok((service, shutdown_tx, base_url, server_handle))
}

/// Waits for /readyz to return 200 with exponential backoff.
Expand Down Expand Up @@ -130,11 +135,13 @@ pub async fn create_test_memories(
}

let body: serde_json::Value = resp.json().await?;

// Extract memory_id from result.output.memory_id
let memory_id = body["result"]["output"]["memory_id"]
.as_str()
.ok_or_else(|| anyhow::anyhow!("Failed to extract memory_id from response: {:?}", body))?
.ok_or_else(|| {
anyhow::anyhow!("Failed to extract memory_id from response: {:?}", body)
})?
.to_string();

memory_ids.push(memory_id);
Expand Down
11 changes: 7 additions & 4 deletions clients/web/apps/docs/src/content/docs/cerebro/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,9 @@ After restoring from backup, verify service health and data integrity:
```bash
curl -s -X POST http://127.0.0.1:4040/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"mem_stats","arguments":{}}}' \
| jq '.result.content[0].text'
-H "Authorization: Bearer <YOUR_TOKEN>" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"mem_stats","arguments":{"input":{}}}}' \
| jq '.result.output'
```

Compare the memory count with your pre-backup count.
Expand All @@ -355,8 +356,9 @@ After restoring from backup, verify service health and data integrity:
```bash
curl -s -X POST http://127.0.0.1:4040/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"mem_search","arguments":{"query":"test","limit":5}}}' \
| jq '.result'
-H "Authorization: Bearer <YOUR_TOKEN>" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"mem_search","arguments":{"input":{"query":"test","limit":5}}}}' \
| jq '.result.output'
```

Confirm that search returns expected results.
Expand All @@ -365,6 +367,7 @@ After restoring from backup, verify service health and data integrity:
```bash
curl -s -X POST http://127.0.0.1:4040/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/list"}' \
| jq '.result'
```
Expand Down
Loading
Loading