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));
}