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
1 change: 1 addition & 0 deletions crates/tracedecay/tests/mcp_suite/mcp_handler_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ mod project_context_test;
mod project_list_test;
#[cfg(feature = "test-transport")]
mod project_search_behavior_test;
mod remote_status_test;
#[cfg(feature = "test-transport")]
mod rename_symbol_test;
mod retrieve_truncation_test;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
//! Real `tools/call` coverage for `tracedecay_remote_status`.
//!
//! The production daemon mounts the Remote Brain reader. With no listener and
//! no registered node, that reader is `unconfigured`. A direct server never
//! installs the reader, so the same call is `unavailable`. Neither outcome is
//! an empty success or a semantic tool error.
//!
//! The configured (`observed`) plane is proved at the two seams that can mount
//! one cheaply: `daemon::remote_protocol_tests` provisions a real node and
//! serving listener against the session runtime registry, and
//! `mcp::tools::handlers::info::remote_status_dispatch_tests` drives an
//! observed reader through dispatch. This suite owns the transport boundary
//! those two do not cross.

use std::path::PathBuf;
use std::process::Command;

use serde_json::{Value, json};
use tracedecay::daemon::ProductionProjectCompositionHarnessV1;

use crate::common;
use crate::fixture;
use crate::mcp_server_test::support::{
jsonrpc_request, response_with_id, run_server_with_messages, setup_server, successful_tool_text,
};
use crate::support::{TestTempDir, test_temp_dir};

const UNCONFIGURED_JSON: &str = r#"{"kind":"unconfigured"}"#;
const UNAVAILABLE_JSON: &str = r#"{"kind":"unavailable"}"#;
const UNCONFIGURED_MARKDOWN: &str = "**kind:** unconfigured\n";
const UNAVAILABLE_MARKDOWN: &str = "**kind:** unavailable\n";

struct MountedDaemon {
harness: ProductionProjectCompositionHarnessV1,
project: PathBuf,
_isolation: TestTempDir,
}

async fn mount_daemon_without_remote_plane() -> MountedDaemon {
let isolation = test_temp_dir();
let project = isolation.path().join("project");
std::fs::create_dir_all(&project).expect("remote-status project directory");
fixture::write_indexed_fixture_sources(&project);
for args in [
vec!["init", "-q"],
vec!["add", "."],
vec![
"-c",
"user.name=TraceDecay Test",
"-c",
"user.email=tracedecay@example.invalid",
"commit",
"-qm",
"remote status fixture",
],
] {
let status = Command::new(common::git_program())
.args(args)
.current_dir(&project)
.status()
.expect("git");
assert!(status.success(), "git must succeed for {project:?}");
}
let harness = Box::pin(
ProductionProjectCompositionHarnessV1::open_for_session_retrieval(
isolation.path(),
[project.clone()],
),
)
.await
.expect("production composition");
MountedDaemon {
harness,
project,
_isolation: isolation,
}
}

fn status_text(result: &Value) -> &str {
let content = result["content"]
.as_array()
.unwrap_or_else(|| panic!("remote status returned no content array: {result}"));
assert_eq!(
content.len(),
1,
"remote status must not attach banners or token footers: {result}"
);
assert!(
result.get("isError").is_none(),
"a typed remote-status read is not a semantic tool error: {result}"
);
content[0]["text"]
.as_str()
.unwrap_or_else(|| panic!("remote status text content missing: {result}"))
}

async fn daemon_status(mounted: &MountedDaemon, arguments: Value) -> Value {
let response = mounted
.harness
.call_tool(&mounted.project, "tracedecay_remote_status", arguments)
.await
.expect("production tools/call");
assert!(
response.error.is_none(),
"production remote status must succeed: {:?}",
response.error
);
response
.result
.unwrap_or_else(|| panic!("production remote status missing result"))
}

#[tokio::test]
async fn production_daemon_reports_unconfigured_remote_plane() {
let mounted = mount_daemon_without_remote_plane().await;

let markdown = daemon_status(&mounted, json!({})).await;
assert_eq!(status_text(&markdown), UNCONFIGURED_MARKDOWN);

let json_result = daemon_status(&mounted, json!({"format": "json"})).await;
assert_eq!(status_text(&json_result), UNCONFIGURED_JSON);
}

#[tokio::test]
async fn direct_server_reports_unmounted_remote_authority() {
let (server, _dir) = setup_server().await;
let responses = run_server_with_messages(
server,
vec![
jsonrpc_request(
json!(1),
"tools/call",
json!({
"name": "tracedecay_remote_status",
"arguments": {}
}),
),
jsonrpc_request(
json!(2),
"tools/call",
json!({
"name": "tracedecay_remote_status",
"arguments": {"format": "json"}
}),
),
],
)
.await;

let markdown = response_with_id(&responses, json!(1));
assert_eq!(
successful_tool_text(&markdown, "markdown remote status"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Assert semantic success for the markdown response

If only the default-markdown path regresses to return isError: true while preserving the expected text, this test still passes: successful_tool_text checks the JSON-RPC error and first text block, while the explicit isError assertion applies only to json_response. Assert the markdown result's semantic-success state as well so this case proves that an unmounted authority remains a typed result.

AGENTS.md reference: AGENTS.md:L192-L193

Useful? React with 👍 / 👎.

UNAVAILABLE_MARKDOWN
);
let json_response = response_with_id(&responses, json!(2));
assert_eq!(
successful_tool_text(&json_response, "json remote status"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Assert direct-response content cardinality

If the direct transport appends a banner or token footer as another content block, both assertions still pass because successful_tool_text reads only result.content[0]. The daemon half uses status_text to require exactly one block, but this direct-server half therefore does not enforce the same response-identity contract; validate the content array and its length for both direct responses.

AGENTS.md reference: AGENTS.md:L177-L178

Useful? React with 👍 / 👎.

UNAVAILABLE_JSON
);
assert!(
json_response["result"].get("isError").is_none(),
"an unmounted remote authority is a typed read, not a tool error: {json_response}"
);
}
Loading