kcode-k1-groups-domain 0.1.0

K1 group domain vocabulary
Documentation
use kcode_k1_groups_domain::{
    ALL_MODELS, ALL_USERS, ApplyOutcome, GroupAction, GroupId, GroupName, GroupRole, LOCAL_MODELS,
    ModelId, SentinelGroup, TxId, UserId, projection_values,
};
use std::{
    fmt::Debug,
    hash::Hash,
    hint::black_box,
    time::{Duration, Instant},
};

fn tx(byte: u8) -> TxId {
    TxId::from_bytes([byte; 12])
}
fn user(byte: u8) -> UserId {
    UserId::from_tx_id(tx(byte))
}
fn value<T: Copy + Clone + Debug + Eq + Hash + Ord + PartialEq + PartialOrd>(value: T) {
    assert_eq!(value, value.clone());
    assert_eq!(value.cmp(&value), std::cmp::Ordering::Equal);
    assert!(!format!("{value:?}").is_empty());
}
fn ordered<T: Clone + Debug + Eq + Hash + Ord + PartialEq + PartialOrd>(value: T) {
    assert_eq!(value, value.clone());
    assert_eq!(value.cmp(&value), std::cmp::Ordering::Equal);
    assert!(!format!("{value:?}").is_empty());
}
fn owned<T: Clone + Debug + Eq + PartialEq>(value: T) {
    assert_eq!(value, value.clone());
    assert!(!format!("{value:?}").is_empty());
}

#[test]
fn identities_accessors_and_value_traits_are_exact() {
    for (group, bytes, sentinel) in [
        (
            ALL_USERS,
            [255, 75, 49, 71, 82, 80, 0, 0, 0, 0, 0, 1],
            SentinelGroup::AllUsers,
        ),
        (
            ALL_MODELS,
            [255, 75, 49, 71, 82, 80, 0, 0, 0, 0, 0, 2],
            SentinelGroup::AllModels,
        ),
        (
            LOCAL_MODELS,
            [255, 75, 49, 71, 82, 80, 0, 0, 0, 0, 0, 3],
            SentinelGroup::LocalModels,
        ),
    ] {
        assert_eq!(group.txid().as_bytes(), &bytes);
        assert_eq!(group.sentinel(), Some(sentinel));
        value(group);
        value(sentinel);
    }
    let ordinary = GroupId::new(tx(7));
    let model = ModelId::from_bytes([9; 32]);
    assert_eq!((ordinary.txid(), ordinary.sentinel()), (tx(7), None));
    assert_eq!(model.as_bytes(), &[9; 32]);
    assert_eq!(model.into_bytes(), [9; 32]);
    value(ordinary);
    value(model);
    for role in [GroupRole::User, GroupRole::Admin, GroupRole::Owner] {
        value(role);
    }
    assert_eq!(user(4).as_tx_id(), tx(4));
}

#[test]
fn names_validate_and_preserve_exact_text() {
    for text in [
        "a".to_owned(),
        "  É e\u{301} A  ".to_owned(),
        "é".repeat(64),
        "x".repeat(128),
        "Mixed CASE".to_owned(),
    ] {
        let name = GroupName::new(text.clone()).unwrap();
        assert_eq!(name.as_str(), text);
        assert_eq!(name.clone().into_string(), text);
        ordered(name);
    }
    for (text, error) in [
        (
            String::new(),
            "group name must be 1 through 128 UTF-8 bytes",
        ),
        (
            "x".repeat(129),
            "group name must be 1 through 128 UTF-8 bytes",
        ),
        (
            "a\nb".to_owned(),
            "group name must not contain control characters",
        ),
        (
            " \u{2003}\u{3000}".to_owned(),
            "group name must contain a non-whitespace character",
        ),
    ] {
        assert_eq!(GroupName::new(text).unwrap_err(), error);
    }
}

#[test]
fn projection_values_accessors_variants_and_owned_traits() {
    let id = GroupId::new(tx(1));
    let revision = projection_values::group_revision(id, tx(2));
    let entry = projection_values::group_user(user(3), GroupRole::Admin);
    let model = ModelId::from_bytes([4; 32]);
    let group = projection_values::group(
        id,
        GroupName::new("Exact".to_owned()).unwrap(),
        revision.clone(),
        vec![entry.clone()],
        vec![model],
    );
    let memberships = projection_values::group_memberships(
        Some(tx(2)),
        vec![id, ALL_USERS],
        vec![id, ALL_MODELS],
        vec![id],
    );
    assert_eq!((revision.group_id(), revision.txid()), (id, tx(2)));
    assert_eq!((entry.user_id(), entry.role()), (user(3), GroupRole::Admin));
    assert_eq!(
        (group.id(), group.name().as_str(), group.revision()),
        (id, "Exact", &revision)
    );
    assert_eq!(group.users(), std::slice::from_ref(&entry));
    assert_eq!(group.models(), &[model]);
    assert_eq!(memberships.revision(), Some(tx(2)));
    assert_eq!(memberships.user_groups(), &[id, ALL_USERS]);
    assert_eq!(memberships.model_groups(), &[id, ALL_MODELS]);
    assert_eq!(memberships.shared_groups(), &[id]);
    for action in [
        GroupAction::Create {
            owner: user(1),
            name: GroupName::new("Create".to_owned()).unwrap(),
        },
        GroupAction::Rename {
            group: id,
            actor: user(2),
            name: GroupName::new("Rename".to_owned()).unwrap(),
        },
        GroupAction::SetUserRole {
            group: id,
            actor: user(2),
            user: user(3),
            role: Some(GroupRole::Owner),
        },
        GroupAction::SetModelMembership {
            group: id,
            actor: user(2),
            model,
            present: true,
        },
    ] {
        owned(action);
    }
    for outcome in [
        ApplyOutcome::Applied(revision.clone()),
        ApplyOutcome::Unchanged(revision.clone()),
        ApplyOutcome::Rejected("reason".to_owned()),
    ] {
        owned(outcome);
    }
    owned(entry);
    owned(revision);
    owned(group);
    owned(memberships);
}

#[test]
fn operations_meet_the_reference_envelope() {
    let started = Instant::now();
    for byte in (0_u8..=255).cycle().take(100_000) {
        let id = GroupId::new(tx(byte));
        black_box((id.txid(), ModelId::from_bytes([byte; 32]).into_bytes()));
        black_box(projection_values::group_revision(id, tx(byte)));
    }
    assert!(started.elapsed() < Duration::from_secs(5));
    let started = Instant::now();
    for _ in 0..10_000 {
        black_box(GroupName::new("é".repeat(64)).unwrap());
    }
    assert!(started.elapsed() < Duration::from_secs(5));
}