tangle


git clone https://radroots.dev/git/tangle.git
Log | Files | Refs | README | LICENSE

commit 2f3c7c12867ee16927574d81b7588e5578eb7b13
parent 29b266a30d719c8ff3c4b536ba004a6b5c1fbeb0
Author: triesap <tyson@radroots.org>
Date:   Sun, 14 Jun 2026 04:35:31 -0700

tests: cover strict closed group semantics

- Add a Phase 2 acceptance guard proving closed groups reject join requests while still allowing normal writes.
- Assert strict closed behavior is independent of public_join by enabling public joins in the closed-group policy fixture.
- Cover rejection of the obsolete compat_zooid_closed_means_restricted policy flag in the same strict semantics target.
- Validated with cargo fmt --all -- --check, cargo check --workspace --all-targets, cargo test --workspace, and cargo clippy --workspace --all-targets -- -D warnings.

Diffstat:
Mcrates/tangle_runtime/tests/phase2_acceptance_targets.rs | 62+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 59 insertions(+), 3 deletions(-)

diff --git a/crates/tangle_runtime/tests/phase2_acceptance_targets.rs b/crates/tangle_runtime/tests/phase2_acceptance_targets.rs @@ -12,9 +12,10 @@ use std::{ use tangle_groups::{ GroupAuthContext, GroupAuthority, GroupErrorKind, GroupEventClass, GroupId, GroupMetadata, GroupMetadataFlags, GroupMetadataText, GroupPolicyConfig, GroupProjection, GroupReadDecision, - GroupReadGate, GroupState, GroupWritePolicy, KIND_GROUP_ADMINS, KIND_GROUP_JOIN_REQUEST, - KIND_GROUP_LEAVE_REQUEST, KIND_GROUP_MEMBERS, KIND_GROUP_METADATA, MemberState, MemberStatus, - ProjectionOrderTuple, StoreOffset, SupportedKinds, + GroupReadGate, GroupState, GroupWriteDecision, GroupWritePolicy, KIND_GROUP_ADMINS, + KIND_GROUP_JOIN_REQUEST, KIND_GROUP_LEAVE_REQUEST, KIND_GROUP_MEMBERS, KIND_GROUP_METADATA, + MemberState, MemberStatus, ProjectionOrderTuple, StoreOffset, SupportedKinds, + parse_group_runtime_config_json, }; use tangle_protocol::{ Event, EventId, Kind, PublicKeyHex, RelayMessage, SignatureHex, Tag, UnixTimestamp, @@ -407,6 +408,61 @@ fn duplicate_join_and_leave_use_duplicate_prefix() { } #[test] +fn closed_groups_use_strict_nip29_semantics_without_compatibility_flag() { + let owner = phase2_pubkey("1"); + let outsider = phase2_pubkey("2"); + let projection = phase2_projection_with_group( + "Closed", + phase2_metadata(false, false, false, true), + owner.clone(), + ); + let authority = GroupAuthority::new([owner], Vec::<PublicKeyHex>::new()); + let policy = GroupWritePolicy::new( + &projection, + &authority, + GroupPolicyConfig::new(true, false).expect("policy"), + ); + + let join_error = policy + .check_event( + &phase2_group_event(KIND_GROUP_JOIN_REQUEST, "Closed", outsider.clone()), + &GroupEventClass::Normal { + group_id: GroupId::new("Closed").expect("group"), + }, + &GroupAuthContext::new([outsider.clone()]), + ) + .expect_err("closed join"); + assert_eq!(join_error.kind(), GroupErrorKind::GroupUnavailable); + assert_eq!( + join_error.prefixed_message(), + "restricted: group is unavailable" + ); + + assert_eq!( + policy + .check_event( + &phase2_group_event(1, "Closed", outsider.clone()), + &GroupEventClass::Normal { + group_id: GroupId::new("Closed").expect("group"), + }, + &GroupAuthContext::new([outsider]), + ) + .expect("normal write"), + GroupWriteDecision::Accept + ); + + let error = parse_group_runtime_config_json( + r#"{"enabled": false, "policy": {"compat_zooid_closed_means_restricted": true}}"#, + ) + .expect_err("compat"); + assert!( + error + .message() + .contains("unknown field `compat_zooid_closed_means_restricted`") + ); +} + +#[test] #[ignore = "phase2 target: central read gate"] fn req_count_and_live_fanout_share_one_group_read_gate() { pending("REQ COUNT and live fanout must use one central group read gate");