quorum-cli 0.2.1

Quorum CLI: the quorum binary.
//! Markdown rendering: takes the fixture-derived Review, renders, asserts
//! on structural features of the output.
//!
//! Not a strict snapshot test because the live fixture's `summary_text`
//! contains LLM prose that can vary in length across sessions. We assert
//! invariants: section headers, severity grouping, deterministic ordering.

use quorum_core::{review_from_json, RepoMetadata, Severity};

fn fixture() -> quorum_core::Review {
    let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("tests/fixtures/lippa_v1_session_detail.json");
    let bytes = std::fs::read(&path).unwrap();
    let v: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
    let repo = RepoMetadata {
        remote_url: None,
        branch: "main".into(),
        head_sha: "abc1234".into(),
        project_id: None,
        base_url: "https://app.lippa.ai".into(),
    };
    review_from_json(&v, &repo).unwrap()
}

// Re-import the render fn from the bin crate via a path-based test. Since
// render lives in main.rs's tree, the simplest portable approach is to
// duplicate the rendering invocation through the public types — but
// rendering is in quorum-cli (bin) and not reachable from tests/. So
// instead, this file asserts on the *Review* contract (`has_high_severity`,
// findings counts, model_names ordering). End-to-end markdown is covered by
// the live-verification step.

#[test]
fn summary_text_present_for_real_session() {
    let r = fixture();
    let sum = r.summary_text.unwrap_or_default();
    assert!(!sum.is_empty(), "preflight session produced a summary");
}

#[test]
fn high_severity_signal_matches_divergence_presence() {
    let r = fixture();
    let has_divergence = r
        .findings
        .iter()
        .any(|f| f.source == quorum_core::FindingSource::Divergence);
    assert_eq!(r.has_high_severity(), has_divergence);
}

#[test]
fn final_agreement_score_in_unit_interval() {
    let r = fixture();
    if let Some(score) = r.final_agreement_score {
        assert!((0.0..=1.0).contains(&score), "score in [0,1], got {score}");
    }
}

#[test]
fn findings_severity_distribution_matches_clusters() {
    let r = fixture();
    // Every finding has exactly one severity; no Info from agreement/divergence sources.
    for f in &r.findings {
        match f.source {
            quorum_core::FindingSource::Divergence => assert_eq!(f.severity, Severity::High),
            quorum_core::FindingSource::Agreement => {
                assert!(matches!(f.severity, Severity::Medium | Severity::Low))
            }
            quorum_core::FindingSource::Assumption => assert_eq!(f.severity, Severity::Info),
        }
    }
}