bee-check 0.2.0

Retrievability checker for Ethereum Swarm references. Multi-vantage stewardship probes, per-chunk drill-down, and one-shot re-seed.
Documentation
//! Unit test for the status-aggregation logic. The network path is
//! exercised by the binary smoke test (see README).

use bee_check::{Report, Status, VantageResult};

fn v(url: &str, r: Option<bool>, err: Option<&str>) -> VantageResult {
    VantageResult {
        bee_url: url.into(),
        retrievable: r,
        elapsed_ms: 1,
        error: err.map(str::to_string),
        overlay: None,
        bee_version: None,
        proximity_to_root: None,
    }
}

fn report_with(vantages: Vec<VantageResult>) -> Report {
    let json = serde_json::json!({
        "reference": "00".repeat(32),
        "status": "retrievable",
        "vantages": vantages,
        "spec_version": 1,
    });
    serde_json::from_value(json).unwrap()
}

#[test]
fn json_roundtrip_preserves_shape() {
    let r = report_with(vec![v("http://a", Some(true), None)]);
    let json = serde_json::to_string(&r).unwrap();
    assert!(json.contains("\"retrievable\":true"));
    assert!(json.contains("\"spec_version\":1"));
    let back: Report = serde_json::from_str(&json).unwrap();
    assert_eq!(back.vantages.len(), 1);
}

#[test]
fn status_serializes_snake_case() {
    let json = serde_json::to_string(&Status::Unretrievable).unwrap();
    assert_eq!(json, "\"unretrievable\"");
    let json = serde_json::to_string(&Status::Partial).unwrap();
    assert_eq!(json, "\"partial\"");
}

#[test]
fn error_field_omitted_when_none() {
    let r = report_with(vec![v("http://a", Some(false), None)]);
    let json = serde_json::to_string(&r).unwrap();
    assert!(!json.contains("\"error\""));
}

#[test]
fn neighborhood_fields_round_trip() {
    // v0.2 additive fields: overlay, bee_version, proximity_to_root.
    let json = serde_json::json!({
        "reference": "00".repeat(32),
        "status": "retrievable",
        "vantages": [{
            "bee_url": "http://a",
            "retrievable": true,
            "elapsed_ms": 5,
            "overlay": "7179856eb3c28642983d0e1b7f264693e407a2738593dec9fd59f2ee29046c9a",
            "bee_version": "2.7.2-rc1",
            "proximity_to_root": 3
        }],
        "spec_version": 1,
    });
    let r: Report = serde_json::from_value(json).unwrap();
    let v = &r.vantages[0];
    assert_eq!(v.overlay.as_deref(), Some("7179856eb3c28642983d0e1b7f264693e407a2738593dec9fd59f2ee29046c9a"));
    assert_eq!(v.bee_version.as_deref(), Some("2.7.2-rc1"));
    assert_eq!(v.proximity_to_root, Some(3));

    // Re-emit and make sure the optional fields survive.
    let s = serde_json::to_string(&r).unwrap();
    assert!(s.contains("\"overlay\":\"7179"));
    assert!(s.contains("\"proximity_to_root\":3"));
}

#[test]
fn old_v01_reports_still_parse() {
    // A v0.1 report (no overlay/version/PO) should still parse cleanly.
    let json = serde_json::json!({
        "reference": "00".repeat(32),
        "status": "unretrievable",
        "vantages": [{
            "bee_url": "http://a",
            "retrievable": false,
            "elapsed_ms": 1,
        }],
        "spec_version": 1,
    });
    let r: Report = serde_json::from_value(json).unwrap();
    assert!(r.vantages[0].overlay.is_none());
    assert!(r.vantages[0].bee_version.is_none());
}