//! Integration test for `output.assemble:` jq assembly.
//!
//! Runs a mocked composition with two parallel branches and an
//! assembly that fuses them into one combined output. Verifies the
//! jq program runs against `{nodes: {<id>: <output>, ...}}` and the
//! result rides on the trace.

use std::sync::Arc;

use mcptest_core::composition::{self, CompositionSpec, ResolvedComposition};
use mcptest_core::connector::ServerPool;
use serde_json::json;

#[tokio::test]
async fn assemble_fuses_two_branches_into_one_output() {
    let yaml = r##"
name: assembled
nodes:
  - id: stars
    tool: get_stars
    args: {}
    mock:
      result: { count: 42 }
  - id: latest
    tool: get_latest_release
    args: {}
    mock:
      result: { tag: "v1.2.0" }
output:
  assemble: "{ stars: .nodes.stars.count, release: .nodes.latest.tag }"
"##;
    let spec: CompositionSpec = serde_yaml::from_str(yaml).unwrap();
    let (resolved, _) = ResolvedComposition::resolve(&spec).unwrap();
    let pool = Arc::new(ServerPool::new());
    let trace = composition::run(&resolved, pool.as_ref(), None).await;

    assert!(trace.ran);
    assert_eq!(trace.output, json!({"stars": 42, "release": "v1.2.0"}));
}

#[tokio::test]
async fn assemble_with_jq_compile_error_is_rejected_at_load() {
    let yaml = r##"
name: bad assemble
nodes:
  - id: a
    tool: t
    mock: { result: 1 }
output:
  assemble: "(((("
"##;
    let spec: CompositionSpec = serde_yaml::from_str(yaml).unwrap();
    let err = ResolvedComposition::resolve(&spec).unwrap_err();
    let formatted = format!("{err}");
    assert!(
        formatted.contains("output.assemble"),
        "expected output.assemble in error, got {formatted}"
    );
}
