use parlov_core::{
EndpointStopReason, EndpointVerdict, OracleClass, OracleResult, OracleVerdict, Severity,
Signal, SignalKind, Vector,
};
use crate::context::test_helpers::{exchange_ctx, probe_ctx};
use crate::{
render_endpoint_verdict_sarif, render_scan_json, render_scan_sarif, render_scan_table,
ReproInfo, ScanFinding,
};
const BASELINE_CURL: &str = "curl -X GET 'http://x/y' \\\n -H 'authorization: Bearer t'";
const PROBE_CURL: &str = "curl -X GET 'http://x/zzz' \\\n -H 'authorization: Bearer t'";
fn confirmed() -> OracleResult {
OracleResult {
class: OracleClass::Existence,
verdict: OracleVerdict::Confirmed,
severity: Some(Severity::High),
confidence: 85,
impact_class: None,
reasons: vec![],
signals: vec![Signal {
kind: SignalKind::StatusCodeDiff,
evidence: "200 vs 404".into(),
rfc_basis: None,
}],
technique_id: Some("get-200-404".into()),
vector: Some(Vector::StatusCodeDiff),
normative_strength: None,
label: None,
leaks: None,
rfc_basis: None,
}
}
fn finding(verdict: OracleVerdict, with_repro: bool) -> ScanFinding {
let mut result = confirmed();
result.verdict = verdict;
ScanFinding {
target_url: "http://x/y".to_owned(),
strategy_id: "s1".to_owned(),
strategy_name: "n1".to_owned(),
method: "GET".to_owned(),
result,
repro: if with_repro {
Some(ReproInfo {
baseline_curl: BASELINE_CURL.to_owned(),
probe_curl: PROBE_CURL.to_owned(),
})
} else {
None
},
probe: probe_ctx("GET", "http://x/y", "http://x/zzz"),
exchange: exchange_ctx(200, 404),
chain_provenance: None,
}
}
fn endpoint_verdict() -> EndpointVerdict {
use parlov_core::ObservabilityStatus;
EndpointVerdict {
oracle_class: OracleClass::Existence,
posterior_probability: 0.92,
verdict: OracleVerdict::Confirmed,
severity: Some(Severity::High),
strategies_run: 1,
strategies_total: 1,
stop_reason: Some(EndpointStopReason::EarlyAccept),
first_threshold_crossed_by: None,
final_confirming_strategy: None,
contributing_findings: vec![],
observability_status: ObservabilityStatus::EvidenceObserved,
block_summary: None,
}
}
#[test]
fn repro_renders_in_json_when_flag_set() {
let findings = vec![finding(OracleVerdict::Confirmed, true)];
let json = render_scan_json("http://x/y", &findings).expect("render");
let v: serde_json::Value = serde_json::from_str(&json).expect("parse");
assert_eq!(
v["findings"][0]["repro"]["baseline_curl"], BASELINE_CURL,
"baseline_curl missing or mismatched: {json}"
);
assert_eq!(v["findings"][0]["repro"]["probe_curl"], PROBE_CURL);
}
#[test]
fn repro_absent_in_json_when_flag_unset() {
let findings = vec![finding(OracleVerdict::Confirmed, false)];
let json = render_scan_json("http://x/y", &findings).expect("render");
let v: serde_json::Value = serde_json::from_str(&json).expect("parse");
assert!(
v["findings"][0]["repro"].is_null(),
"repro must be omitted: {json}"
);
}
#[test]
fn repro_renders_in_sarif_properties_when_flag_set() {
let findings = vec![finding(OracleVerdict::Confirmed, true)];
let json = render_scan_sarif("http://x/y", &findings).expect("render");
let v: serde_json::Value = serde_json::from_str(&json).expect("parse");
let result_props = &v["runs"][0]["results"][0]["properties"];
assert_eq!(result_props["repro"]["baseline_curl"], BASELINE_CURL);
assert_eq!(result_props["repro"]["probe_curl"], PROBE_CURL);
}
#[test]
fn repro_renders_in_endpoint_sarif_properties_when_flag_set() {
let findings = vec![finding(OracleVerdict::Confirmed, true)];
let verdict = endpoint_verdict();
let json = render_endpoint_verdict_sarif("http://x/y", &verdict, &findings).expect("render");
let v: serde_json::Value = serde_json::from_str(&json).expect("parse");
assert_eq!(
v["runs"][0]["results"][0]["properties"]["repro"]["baseline_curl"],
BASELINE_CURL
);
}
#[test]
fn repro_absent_in_sarif_when_flag_unset() {
let findings = vec![finding(OracleVerdict::Confirmed, false)];
let json = render_scan_sarif("http://x/y", &findings).expect("render");
let v: serde_json::Value = serde_json::from_str(&json).expect("parse");
assert!(v["runs"][0]["results"][0]["properties"]["repro"].is_null());
}
#[test]
fn repro_renders_in_table_when_flag_set_and_verdict_present() {
let findings = vec![finding(OracleVerdict::Confirmed, true)];
let table = render_scan_table(&findings);
assert!(
table.contains("Reproduce baseline:"),
"missing baseline row:\n{table}"
);
assert!(
table.contains("Reproduce probe:"),
"missing probe row:\n{table}"
);
}
#[test]
fn repro_suppressed_in_table_when_verdict_not_present() {
let findings = vec![finding(OracleVerdict::NotPresent, true)];
let table = render_scan_table(&findings);
assert!(
!table.contains("Reproduce baseline:"),
"should suppress on NotPresent:\n{table}"
);
}
#[test]
fn repro_absent_in_table_when_flag_unset() {
let findings = vec![finding(OracleVerdict::Confirmed, false)];
let table = render_scan_table(&findings);
assert!(!table.contains("Reproduce"));
}