acorn-schema 0.4.0

Portable ACORN schema, validation, and codecs
use super::*;
use crate::pid::{PersistentIdentifierConvert, PID};
use crate::validation::Validate;
use proptest::prelude::*;
use serde::Deserialize;

const CONTRACT: &str = include_str!("../../../../../tests/fixtures/rrid/portable.json");
const JATS: &str = include_str!("../../../../../tests/fixtures/rrid/jats-1.4.xml");

#[derive(Deserialize)]
struct Contract {
    invalid: Vec<String>,
    valid: Vec<ValidCase>,
}
#[derive(Deserialize)]
struct ValidCase {
    expected: String,
    input: String,
}

#[test]
fn test_bare_payloads_are_not_discovered() {
    let found = RRID::find_all("SCR_003512 CVCL_3588 Addgene_12260");
    assert!(found.is_empty());
}
#[test]
fn test_contract_fixture() {
    let contract: Contract = serde_json::from_str(CONTRACT).expect("portable RRID contract should deserialize");
    contract.valid.into_iter().for_each(|case| {
        let parsed: RRID = case.input.parse().expect("valid RRID fixture should parse");
        assert_eq!(parsed.to_string(), case.expected, "{}", case.input);
        assert_eq!(RRID::format(&case.input), case.expected, "{}", case.input);
    });
    contract
        .invalid
        .into_iter()
        .for_each(|value| assert!(!RRID::is_valid(&value), "invalid RRID fixture parsed: {value}"));
}
#[test]
fn test_direct_validation_rejects_invalid_payload() {
    let rrid = RRID {
        payload: "SCR 014641".to_string(),
    };
    assert!(rrid.validate().is_err());
}
#[test]
fn test_discovery_ignores_arbitrary_url_context() {
    let found = RRID::find_all("https://example.org/search?q=RRID:SCR_003512 and https://example.org/RRID:CVCL_3588");
    assert!(found.is_empty());
}
#[test]
fn test_discovery_trims_prose_and_deduplicates_aliases() {
    let found = RRID::find_all("Use (RRID:SCR_003512), rrid : SCR_003512, and https://n2t.net/RRID:SCR_003512.");
    assert_eq!(found.len(), 3);
    assert!(found.iter().all(|identifier| identifier.to_string() == "RRID:SCR_003512"));
}
#[test]
fn test_idutils_rrid_case() {
    let (value, schemes, normalized, url) = (
        "RRID:SCR_014641",
        ["rrid"],
        "RRID:SCR_014641",
        "http://scicrunch.org/resolver/RRID:SCR_014641",
    );
    assert_eq!(schemes.map(PID::from), [PID::RRID]);
    assert_eq!(RRID::format(value), normalized);
    assert_eq!(RRID::format(url), normalized);
}
#[test]
fn test_jats_14_fixture_preserves_explicit_rrid() {
    assert!(JATS.contains("resource-id-type=\"RRID\""));
    let found = RRID::find_all(JATS);
    assert_eq!(
        found.into_iter().map(|identifier| identifier.to_string()).collect::<Vec<_>>(),
        ["RRID:SCR_003512"]
    );
}
#[test]
fn test_normalization_is_idempotent() {
    let once = RRID::format("rrid: IMSR_JAX:000664");
    assert_eq!(RRID::format(&once), once);
}
#[test]
fn test_rrid_trait_contract() {
    let value = "RRID:SCR_003512";
    let rrid = RRID::from_string(value);
    assert_eq!(rrid.identifier(), value);
    assert_eq!(rrid.prefix().as_deref(), Some("RRID"));
    assert_eq!(rrid.suffix().as_deref(), Some("SCR_003512"));
    assert_eq!(rrid.url(), "https://n2t.net/RRID:SCR_003512");
    assert!(rrid.check_digit().is_none());
    assert!(value.is_pid(PID::RRID));
    assert_eq!(value.format_as(PID::RRID), value);
    assert_eq!(value.to_pid(PID::RRID).to_rrid(), rrid);
}
#[test]
fn test_serde_round_trip() {
    let rrid: RRID = "RRID:CVCL_3588".parse().expect("RRID should parse");
    let json = serde_json::to_string(&rrid).expect("RRID should serialize");
    assert_eq!(serde_json::from_str::<RRID>(&json).expect("RRID should deserialize"), rrid);
}

proptest! {
    #[test]
    fn test_parse_is_total_and_canonicalization_is_idempotent(value in any::<String>()) {
        if let Ok(parsed) = value.parse::<RRID>() {
            let canonical = parsed.to_string();
            prop_assert_eq!(canonical.parse::<RRID>().map(|rrid| rrid.to_string()), Ok(canonical));
        }
    }
}