mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-11-15 05:02:16 +01:00
Two users on 2026.7.x reported the app going half-dead: local editing kept
working, but Send did nothing and every encrypted secret rendered blank until
they restarted. The webview console carried a Rust task panic —
Delete task failed: task 1314 panicked with message
"Failed to get new DB connection from the pool: Error(None)"
`Error(None)` is r2d2's acquire timeout. `QueryManager::connect` and `with_tx`
turned that timeout into `expect`, so a pool that was momentarily full took down
whatever task asked for a connection instead of reporting a failure the caller
could show, retry, or ignore. Every DB-backed path shares those two functions,
which is why one stalled pool reads as "Send is dead and the secrets are gone".
Acquisition is now fallible end to end: `connect()` and `BlobManager::connect()`
return `Result`, and so do the `db()`/`blobs()` accessors on every host. Call
sites propagate with `?` where they already returned a result; the fire-and-forget
writers (streaming events, the model-change poller) log and carry on. `with_conn`
had no callers and only added a third panic, so it is gone.
Two places held a pooled connection across work that has nothing to do with the
database and can block for a long time — the OS keyring in
`EncryptionManager::get_workspace_key`, and the sync directory walk in
`cmd_sync_calculate`. Both now hand the connection back first. Those are the
paths behind the two visible symptoms.
Not fixed here: the gRPC streaming block in yaak-app-client still `.unwrap()`s
its database writes, as it did before, so acquisition failures panic there too.
`ProxyQueryManager` in the separate proxy app keeps its own `expect`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
123 lines
3.6 KiB
Rust
123 lines
3.6 KiB
Rust
mod common;
|
|
|
|
use common::{cli_cmd, parse_created_id, query_manager, seed_workspace};
|
|
use predicates::str::contains;
|
|
use tempfile::TempDir;
|
|
|
|
#[test]
|
|
fn create_list_show_delete_round_trip() {
|
|
let temp_dir = TempDir::new().expect("Failed to create temp dir");
|
|
let data_dir = temp_dir.path();
|
|
seed_workspace(data_dir, "wk_test");
|
|
|
|
let create_assert = cli_cmd(data_dir)
|
|
.args(["folder", "create", "wk_test", "--name", "Auth"])
|
|
.assert()
|
|
.success();
|
|
let folder_id = parse_created_id(&create_assert.get_output().stdout, "folder create");
|
|
|
|
cli_cmd(data_dir)
|
|
.args(["folder", "list", "wk_test"])
|
|
.assert()
|
|
.success()
|
|
.stdout(contains(&folder_id))
|
|
.stdout(contains("Auth"));
|
|
|
|
cli_cmd(data_dir)
|
|
.args(["folder", "show", &folder_id])
|
|
.assert()
|
|
.success()
|
|
.stdout(contains(format!("\"id\": \"{folder_id}\"")))
|
|
.stdout(contains("\"workspaceId\": \"wk_test\""));
|
|
|
|
cli_cmd(data_dir)
|
|
.args(["folder", "delete", &folder_id, "--yes"])
|
|
.assert()
|
|
.success()
|
|
.stdout(contains(format!("Deleted folder: {folder_id}")));
|
|
|
|
assert!(query_manager(data_dir).connect().unwrap().get_folder(&folder_id).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn json_create_and_update_merge_patch_round_trip() {
|
|
let temp_dir = TempDir::new().expect("Failed to create temp dir");
|
|
let data_dir = temp_dir.path();
|
|
seed_workspace(data_dir, "wk_test");
|
|
|
|
let create_assert = cli_cmd(data_dir)
|
|
.args([
|
|
"folder",
|
|
"create",
|
|
r#"{"workspaceId":"wk_test","name":"Json Folder"}"#,
|
|
])
|
|
.assert()
|
|
.success();
|
|
let folder_id = parse_created_id(&create_assert.get_output().stdout, "folder create");
|
|
|
|
cli_cmd(data_dir)
|
|
.args([
|
|
"folder",
|
|
"update",
|
|
&format!(r#"{{"id":"{}","description":"Folder Description"}}"#, folder_id),
|
|
])
|
|
.assert()
|
|
.success()
|
|
.stdout(contains(format!("Updated folder: {folder_id}")));
|
|
|
|
cli_cmd(data_dir)
|
|
.args(["folder", "show", &folder_id])
|
|
.assert()
|
|
.success()
|
|
.stdout(contains("\"name\": \"Json Folder\""))
|
|
.stdout(contains("\"description\": \"Folder Description\""));
|
|
}
|
|
|
|
#[test]
|
|
fn create_merges_positional_workspace_id_into_json_payload() {
|
|
let temp_dir = TempDir::new().expect("Failed to create temp dir");
|
|
let data_dir = temp_dir.path();
|
|
seed_workspace(data_dir, "wk_test");
|
|
|
|
let create_assert = cli_cmd(data_dir)
|
|
.args([
|
|
"folder",
|
|
"create",
|
|
"wk_test",
|
|
"--json",
|
|
r#"{"name":"Merged Folder"}"#,
|
|
])
|
|
.assert()
|
|
.success();
|
|
let folder_id = parse_created_id(&create_assert.get_output().stdout, "folder create");
|
|
|
|
cli_cmd(data_dir)
|
|
.args(["folder", "show", &folder_id])
|
|
.assert()
|
|
.success()
|
|
.stdout(contains("\"workspaceId\": \"wk_test\""))
|
|
.stdout(contains("\"name\": \"Merged Folder\""));
|
|
}
|
|
|
|
#[test]
|
|
fn create_rejects_conflicting_workspace_ids_between_arg_and_json() {
|
|
let temp_dir = TempDir::new().expect("Failed to create temp dir");
|
|
let data_dir = temp_dir.path();
|
|
seed_workspace(data_dir, "wk_test");
|
|
seed_workspace(data_dir, "wk_other");
|
|
|
|
cli_cmd(data_dir)
|
|
.args([
|
|
"folder",
|
|
"create",
|
|
"wk_test",
|
|
"--json",
|
|
r#"{"workspaceId":"wk_other","name":"Mismatch"}"#,
|
|
])
|
|
.assert()
|
|
.failure()
|
|
.stderr(contains(
|
|
"folder create got conflicting workspace_id values between positional arg and JSON payload",
|
|
));
|
|
}
|