diff options
Diffstat (limited to 'crates/fidget-spinner-cli')
| -rw-r--r-- | crates/fidget-spinner-cli/src/mcp/host/process.rs | 5 | ||||
| -rw-r--r-- | crates/fidget-spinner-cli/src/mcp/host/runtime.rs | 3 | ||||
| -rw-r--r-- | crates/fidget-spinner-cli/tests/mcp_hardening.rs | 64 |
3 files changed, 69 insertions, 3 deletions
diff --git a/crates/fidget-spinner-cli/src/mcp/host/process.rs b/crates/fidget-spinner-cli/src/mcp/host/process.rs index d4cbb4b..fdbd873 100644 --- a/crates/fidget-spinner-cli/src/mcp/host/process.rs +++ b/crates/fidget-spinner-cli/src/mcp/host/process.rs @@ -55,6 +55,11 @@ impl WorkerSupervisor { self.bound_project_root = Some(project_root); } + pub(super) fn refresh_binding(&mut self, project_root: PathBuf) { + self.kill_current_worker(); + self.bound_project_root = Some(project_root); + } + pub(super) fn execute( &mut self, request_id: HostRequestId, diff --git a/crates/fidget-spinner-cli/src/mcp/host/runtime.rs b/crates/fidget-spinner-cli/src/mcp/host/runtime.rs index bf0484a..f2f10b7 100644 --- a/crates/fidget-spinner-cli/src/mcp/host/runtime.rs +++ b/crates/fidget-spinner-cli/src/mcp/host/runtime.rs @@ -392,7 +392,8 @@ impl HostRuntime { let args = deserialize::<ProjectBindArgs>(arguments, "tools/call:project.bind")?; let resolved = resolve_project_binding(PathBuf::from(args.path)) .map_err(host_store_fault("tools/call:project.bind"))?; - self.worker.rebind(resolved.binding.project_root.clone()); + self.worker + .refresh_binding(resolved.binding.project_root.clone()); self.binding = Some(resolved.binding); tool_success( project_bind_output(&resolved.status)?, diff --git a/crates/fidget-spinner-cli/tests/mcp_hardening.rs b/crates/fidget-spinner-cli/tests/mcp_hardening.rs index fad4937..fc744c5 100644 --- a/crates/fidget-spinner-cli/tests/mcp_hardening.rs +++ b/crates/fidget-spinner-cli/tests/mcp_hardening.rs @@ -7,8 +7,8 @@ use std::path::PathBuf; use std::process::{Child, ChildStdin, ChildStdout, Command, Stdio}; use camino::Utf8PathBuf; -use fidget_spinner_core::NonEmptyText; -use fidget_spinner_store_sqlite::ProjectStore; +use fidget_spinner_core::{NonEmptyText, Slug}; +use fidget_spinner_store_sqlite::{CreateFrontierRequest, ProjectStore}; use libmcp as _; use maud as _; use percent_encoding as _; @@ -199,6 +199,15 @@ fn tool_names(response: &Value) -> Vec<&str> { .collect() } +fn frontier_slugs(response: &Value) -> Vec<&str> { + tool_content(response)["frontiers"] + .as_array() + .into_iter() + .flatten() + .filter_map(|frontier| frontier["slug"].as_str()) + .collect() +} + #[test] fn cold_start_exposes_bound_surface_and_new_toolset() -> TestResult { let project_root = temp_project_root("cold_start")?; @@ -596,3 +605,54 @@ fn experiment_close_drives_metric_best_and_analysis() -> TestResult { ); Ok(()) } + +#[test] +fn same_path_project_bind_refreshes_destructive_reseed() -> TestResult { + let project_root = temp_project_root("same_path_reseed")?; + + let mut harness = McpHarness::spawn(None)?; + let _ = harness.initialize()?; + harness.notify_initialized()?; + + let bind = harness.bind_project(60, &project_root)?; + assert_tool_ok(&bind); + + assert_tool_ok(&harness.call_tool( + 61, + "frontier.create", + json!({ + "label": "alpha frontier", + "objective": "first seeded frontier", + "slug": "alpha", + }), + )?); + let alpha_list = harness.call_tool_full(62, "frontier.list", json!({}))?; + assert_tool_ok(&alpha_list); + assert_eq!(frontier_slugs(&alpha_list), vec!["alpha"]); + + must( + fs::remove_dir_all(project_root.join(fidget_spinner_store_sqlite::STORE_DIR_NAME)), + "remove project store", + )?; + init_project(&project_root)?; + let mut reopened = must(ProjectStore::open(&project_root), "open recreated store")?; + let _beta = must( + reopened.create_frontier(CreateFrontierRequest { + label: must(NonEmptyText::new("beta frontier"), "beta label")?, + objective: must( + NonEmptyText::new("second seeded frontier"), + "beta objective", + )?, + slug: Some(must(Slug::new("beta"), "beta slug")?), + }), + "create beta frontier directly in recreated store", + )?; + + let rebind = harness.bind_project(63, &project_root)?; + assert_tool_ok(&rebind); + + let beta_list = harness.call_tool_full(64, "frontier.list", json!({}))?; + assert_tool_ok(&beta_list); + assert_eq!(frontier_slugs(&beta_list), vec!["beta"]); + Ok(()) +} |