use crate::channels::discord::access::{DiscordIdentity, identity_admitted};
const OWNER_ID: u64 = 111_111_111_111_111_111;
const STRANGER_ID: u64 = 222_222_222_222_222_222;
fn identity<'a>(
user_id: u64,
allowed_users: &'a [String],
allowed_roles: &'a [String],
bot_owner: &'a [String],
guild_roles: &'a [u64],
open_here: bool,
) -> DiscordIdentity<'a> {
DiscordIdentity {
user_id,
allowed_users,
allowed_roles,
bot_owner,
guild_roles,
open_here,
}
}
#[test]
fn an_owner_who_is_not_in_the_allowlist_may_react() {
let allowed = vec![STRANGER_ID.to_string()];
let owner = vec![OWNER_ID.to_string()];
let id = identity(OWNER_ID, &allowed, &[], &owner, &[], false);
assert!(
!allowed.contains(&OWNER_ID.to_string()),
"the fixture must reproduce the old gate denying the owner"
);
assert!(
identity_admitted(&id),
"the owner must be admitted on the reaction path too"
);
}
#[test]
fn a_role_granted_user_may_react() {
let roles = vec!["900".to_string()];
let id = identity(STRANGER_ID, &[], &roles, &[], &[900], false);
assert!(identity_admitted(&id), "a held allowed role must admit");
let wrong = identity(STRANGER_ID, &[], &roles, &[], &[901], false);
assert!(
!identity_admitted(&wrong),
"an unlisted role must not admit"
);
}
#[test]
fn a_cowork_opened_channel_may_react() {
let owner = vec![OWNER_ID.to_string()];
let id = identity(STRANGER_ID, &[], &[], &owner, &[], true);
assert!(
identity_admitted(&id),
"an open channel must admit a reaction like it admits a message"
);
}
#[test]
fn a_stranger_is_still_denied() {
let allowed = vec![OWNER_ID.to_string()];
let owner = vec![OWNER_ID.to_string()];
let id = identity(STRANGER_ID, &allowed, &[], &owner, &[], false);
assert!(!identity_admitted(&id), "a stranger must still be denied");
let empty: Vec<String> = Vec::new();
let unconfigured = identity(STRANGER_ID, &empty, &empty, &empty, &[], false);
assert!(
!identity_admitted(&unconfigured),
"an unconfigured channel must deny, matching the message path"
);
}
#[test]
fn a_dm_without_guild_roles_is_not_role_granted() {
let roles = vec!["900".to_string()];
let dm = identity(STRANGER_ID, &[], &roles, &[], &[], false);
assert!(
!identity_admitted(&dm),
"guild roles do not apply in a DM, so a role config must not admit"
);
}
#[test]
fn both_paths_call_the_shared_gate() {
const REACTIONS: &str = include_str!("../channels/discord/reactions.rs");
const HANDLER: &str = include_str!("../channels/discord/handler.rs");
assert!(
REACTIONS.contains("access::identity_admitted"),
"the reaction path must use the shared gate"
);
assert!(
!REACTIONS.contains("allowed_users\n .iter()\n .any"),
"the allowlist-only gate is the defect; it must not come back"
);
assert!(
HANDLER.contains("commands::identity_admitted("),
"the message path must call the same predicate, not an inline copy"
);
}