diff --git a/src/base/constants/errors.cairo b/src/base/constants/errors.cairo index 504fbe4..2e65a80 100644 --- a/src/base/constants/errors.cairo +++ b/src/base/constants/errors.cairo @@ -36,4 +36,5 @@ pub mod Errors { pub const AUTO_RENEW_DURATION_ENDED: felt252 = 'Karst: auto renew ended!'; pub const INVALID_JOLT: felt252 = 'Karst: invalid jolt!'; pub const INVALID_JOLT_RECIPIENT: felt252 = 'Karst: not request recipient!'; + pub const INVALID_LENGTH: felt252 = 'Karst: Invalid Length'; } diff --git a/src/community/community.cairo b/src/community/community.cairo index 7c8b3fd..c15bb4e 100644 --- a/src/community/community.cairo +++ b/src/community/community.cairo @@ -19,7 +19,7 @@ pub mod CommunityComponent { }; use karst::base::constants::errors::Errors::{ ALREADY_MEMBER, NOT_COMMUNITY_OWNER, NOT_MEMBER, BANNED_MEMBER, UNAUTHORIZED, - ONLY_PREMIUM_COMMUNITIES + ONLY_PREMIUM_COMMUNITIES, INVALID_LENGTH }; @@ -74,20 +74,20 @@ pub mod CommunityComponent { #[derive(Drop, starknet::Event)] pub struct JoinedCommunity { - community_id: u256, - transaction_executor: ContractAddress, - token_id: u256, - profile: ContractAddress, - block_timestamp: u64, + pub community_id: u256, + pub transaction_executor: ContractAddress, + pub token_id: u256, + pub profile: ContractAddress, + pub block_timestamp: u64, } #[derive(Drop, starknet::Event)] pub struct LeftCommunity { - community_id: u256, - transaction_executor: ContractAddress, - token_id: u256, - profile: ContractAddress, - block_timestamp: u64, + pub community_id: u256, + pub transaction_executor: ContractAddress, + pub token_id: u256, + pub profile: ContractAddress, + pub block_timestamp: u64, } #[derive(Drop, starknet::Event)] @@ -151,146 +151,57 @@ pub mod CommunityComponent { ref self: ComponentState, community_type: CommunityType ) -> u256 { let community_owner = get_caller_address(); - let community_counter = self.community_counter.read(); - let community_nft_classhash = self.community_nft_classhash.read(); - let community_id = community_counter + 1; + let community_nft_classhash = self.community_nft_classhash.read(); + let community_id = self.community_counter.read() + 1; let community_nft_address = self ._deploy_community_nft( community_id, community_nft_classhash, community_id.try_into().unwrap() ); // use community_id as salt since its unique - // write to storage - let community_details = CommunityDetails { - community_id: community_id, - community_owner: community_owner, - community_metadata_uri: "", - community_nft_address: community_nft_address, - community_premium_status: false, - community_total_members: 0, - community_type: CommunityType::Free, - }; - - let gate_keep_details = CommunityGateKeepDetails { - community_id: community_id, - gate_keep_type: GateKeepType::None, - community_nft_address: contract_address_const::<0>(), - entry_fee: 0 - }; - - self.communities.write(community_id, community_details); - self.community_owner.write(community_id, community_owner); - self.community_gate_keep.write(community_id, gate_keep_details); - self.community_counter.write(community_counter + 1); - - // upgrade if community type is not free - if (community_type != CommunityType::Free) { - self._upgrade_community(community_id, community_type); - } - - // emit event self - .emit( - CommunityCreated { - community_id: community_id, - community_owner: community_owner, - community_nft_address: community_nft_address, - block_timestamp: get_block_timestamp() - } + ._create_community( + community_owner, community_nft_address, community_id, community_type, ); + community_id } /// @notice adds a new user to a community /// @param profile user who wants to join community /// @param community_id id of community to be joined - fn join_community( - ref self: ComponentState, profile: ContractAddress, community_id: u256 - ) { + fn join_community(ref self: ComponentState, community_id: u256) { + let profile_caller = get_caller_address(); let community = self.communities.read(community_id); // check user is not already a member and wasn't previously banned - let is_community_member = self.is_community_member(profile, community_id); - let is_banned = self.get_ban_status(profile, community_id); + let (is_community_member, _) = self.is_community_member(profile_caller, community_id); + let is_banned = self.get_ban_status(profile_caller, community_id); assert(is_community_member != true, ALREADY_MEMBER); assert(is_banned != true, BANNED_MEMBER); - // mint a community token to new joiner - let minted_token_id = self - ._mint_community_nft(profile, community.community_nft_address); - - let community_member = CommunityMember { - profile_address: profile, - community_id: community_id, - total_publications: 0, - community_token_id: minted_token_id, - ban_status: false - }; - - // update storage - self.community_member.write((community_id, profile), community_member); - let community_total_members = community.community_total_members + 1; - let updated_community = CommunityDetails { - community_total_members: community_total_members, ..community - }; - self.communities.write(community_id, updated_community); - - // emit event - self - .emit( - JoinedCommunity { - community_id: community_id, - transaction_executor: get_caller_address(), - token_id: minted_token_id, - profile: profile, - block_timestamp: get_block_timestamp(), - } - ); + self._join_community(profile_caller, community.community_nft_address, community_id); } /// @notice removes a member from a community /// @param profile user who wants to leave the community /// @param community_id id of community to be left - fn leave_community( - ref self: ComponentState, profile: ContractAddress, community_id: u256 - ) { + fn leave_community(ref self: ComponentState, community_id: u256) { + let profile_caller = get_caller_address(); let community = self.communities.read(community_id); - let community_member_details = self.community_member.read((community_id, profile)); + let community_member_details = self + .community_member + .read((community_id, profile_caller)); - let is_community_member = self.is_community_member(profile, community_id); + let (is_community_member, _) = self.is_community_member(profile_caller, community_id); assert(is_community_member == true, NOT_MEMBER); - // remove member details and update storage - let updated_member_details = CommunityMember { - profile_address: contract_address_const::<0>(), - community_id: 0, - total_publications: 0, - community_token_id: 0, - ban_status: true - }; - self.community_member.write((community_id, profile), updated_member_details); - let community_total_members = community.community_total_members - 1; - let updated_community = CommunityDetails { - community_total_members: community_total_members, ..community - }; - self.communities.write(community_id, updated_community); - - // burn user's community token - self - ._burn_community_nft( - community.community_nft_address, community_member_details.community_token_id - ); - - // emit event self - .emit( - LeftCommunity { - community_id: community_id, - transaction_executor: get_caller_address(), - token_id: community_member_details.community_token_id, - profile: profile, - block_timestamp: get_block_timestamp(), - } + ._leave_community( + profile_caller, + community.community_nft_address, + community_id, + community_member_details.community_token_id ); } @@ -310,58 +221,39 @@ pub mod CommunityComponent { self.communities.write(community_id, updated_community); } - // TODO: MAKE IT RECEIVE AN ARRAY OF MODERATORS + /// @notice adds a new community mod /// @param community_id id of community to add moderator /// @param moderator address to be added as moderator fn add_community_mods( - ref self: ComponentState, community_id: u256, moderator: ContractAddress + ref self: ComponentState, + community_id: u256, + moderators: Array ) { let community_owner = self.community_owner.read(community_id); assert(community_owner == get_caller_address(), NOT_COMMUNITY_OWNER); - // Mod must first be a member of the community - let is_community_member = self.is_community_member(moderator, community_id); - assert(is_community_member == true, NOT_MEMBER); - - // update storage - self.community_mod.write((community_id, moderator), true); - - // emit event - self - .emit( - CommunityModAdded { - community_id: community_id, - transaction_executor: community_owner, - mod_address: moderator, - block_timestamp: get_block_timestamp() - } - ); + self._add_community_mods(community_id, community_owner, moderators); } - // TODO: MAKE IT RECEIVE AN ARRAY OF MODERATORS + /// @notice removes a new community mod /// @param community_id id of community to remove moderator /// @param moderator address to be removed as moderator fn remove_community_mods( - ref self: ComponentState, community_id: u256, moderator: ContractAddress + ref self: ComponentState, + community_id: u256, + moderators: Array ) { let community_owner = self.community_owner.read(community_id); + // only community owner can remove a mod + assert(community_owner == get_caller_address(), NOT_COMMUNITY_OWNER); + // _remove_community_mods + self._remove_community_mods(community_id, community_owner, moderators); // update storage - self.community_mod.write((community_id, moderator), false); - // emit event - self - .emit( - CommunityModRemoved { - community_id: community_id, - mod_address: moderator, - transaction_executor: community_owner, - block_timestamp: get_block_timestamp() - } - ); } // TODO: MAKE IT RECEIVE AN ARRAY OF PROFILES @@ -371,8 +263,8 @@ pub mod CommunityComponent { fn set_ban_status( ref self: ComponentState, community_id: u256, - profile: ContractAddress, - ban_status: bool + profiles: Array, + ban_statuses: Array ) { let caller = get_caller_address(); let is_community_mod = self.community_mod.read((community_id, caller)); @@ -381,26 +273,8 @@ pub mod CommunityComponent { // check caller is mod or owner assert(is_community_mod == true || community_owner == caller, UNAUTHORIZED); - // check profile is a community member - let is_community_member = self.is_community_member(profile, community_id); - assert(is_community_member == true, NOT_MEMBER); - - // update storage - let community_member = self.community_member.read((community_id, profile)); - let updated_member = CommunityMember { ban_status: ban_status, ..community_member }; - self.community_member.write((community_id, profile), updated_member); - - // emit event - self - .emit( - CommunityBanStatusUpdated { - community_id: community_id, - transaction_executor: caller, - profile: profile, - ban_status: ban_status, - block_timestamp: get_block_timestamp() - } - ); + // _set_ban_statu + self._set_ban_status(community_id, profiles, ban_statuses); } /// @notice upgrades a community @@ -496,12 +370,12 @@ pub mod CommunityComponent { /// @return bool true/false stating user's membership status fn is_community_member( self: @ComponentState, profile: ContractAddress, community_id: u256 - ) -> bool { - let community_memeber = self.community_member.read((community_id, profile)); - if (community_memeber.community_id == community_id) { - true + ) -> (bool, CommunityMember) { + let community_member = self.community_member.read((community_id, profile)); + if (community_member.community_id == community_id) { + (true, community_member) } else { - false + (false, community_member) } } @@ -581,6 +455,148 @@ pub mod CommunityComponent { self.community_nft_classhash.write(community_nft_classhash.try_into().unwrap()); } + /// @notice internal function to create a community + /// @param community type of the new community + fn _create_community( + ref self: ComponentState, + community_owner: ContractAddress, + community_nft_address: ContractAddress, + community_id: u256, + community_type: CommunityType, + ) { + // write to storage + let community_details = CommunityDetails { + community_id: community_id, + community_owner: community_owner, + community_metadata_uri: "", + community_nft_address: community_nft_address, + community_premium_status: false, + community_total_members: 0, + community_type: CommunityType::Free, + }; + + let gate_keep_details = CommunityGateKeepDetails { + community_id: community_id, + gate_keep_type: GateKeepType::None, + community_nft_address: community_nft_address, + entry_fee: 0 + }; + + self.communities.write(community_id, community_details); + self.community_owner.write(community_id, community_owner); + self.community_gate_keep.write(community_id, gate_keep_details); + self.community_counter.write(community_id); + + // upgrade if community type is not free + if (community_type != CommunityType::Free) { + self._upgrade_community(community_id, community_type); + } + + // emit event + self + .emit( + CommunityCreated { + community_id: community_id, + community_owner: community_owner, + community_nft_address: community_nft_address, + block_timestamp: get_block_timestamp() + } + ); + + // let the owner join the community by default + self._join_community(community_owner, community_nft_address, community_id); + } + + /// @notice internal function to join a community + /// @param profile of the new community member + /// @param community_nft_classhash classhash of community NFT + /// @param community_id of community the new member is trying to join + fn _join_community( + ref self: ComponentState, + profile: ContractAddress, + community_nft_address: ContractAddress, + community_id: u256 + ) { + // mint a community token to new joiner + let minted_token_id = self._mint_community_nft(profile, community_nft_address); + + let community_member = CommunityMember { + profile_address: profile, + community_id: community_id, + total_publications: 0, + community_token_id: minted_token_id, + ban_status: false + }; + + // update storage + self.community_member.write((community_id, profile), community_member); + + let community = self.communities.read(community_id); + let community_total_members = community.community_total_members + 1; + + // update community details + let updated_community = CommunityDetails { + community_total_members: community_total_members, ..community + }; + self.communities.write(community_id, updated_community); + + // emit event + self + .emit( + JoinedCommunity { + community_id: community_id, + transaction_executor: profile, + token_id: minted_token_id, + profile: profile, + block_timestamp: get_block_timestamp(), + } + ); + } + + /// @notice internal function to leave a community + /// @param profile of the new community member + /// @param community_nft_classhash classhash of community NFT + /// @param community_id of community the new member is trying to join + fn _leave_community( + ref self: ComponentState, + profile_caller: ContractAddress, + community_nft_address: ContractAddress, + community_id: u256, + community_token_id: u256 + ) { + let community = self.communities.read(community_id); + // burn user's community token + self._burn_community_nft(community_nft_address, profile_caller, community_token_id); + + // remove member details and update storage + let updated_member_details = CommunityMember { + profile_address: contract_address_const::<0>(), + community_id: 0, + total_publications: 0, + community_token_id: 0, + ban_status: false + }; + self.community_member.write((community_id, profile_caller), updated_member_details); + let community_total_members = community.community_total_members - 1; + let updated_community = CommunityDetails { + community_total_members: community_total_members, ..community + }; + self.communities.write(community_id, updated_community); + + // emit event + self + .emit( + LeftCommunity { + community_id: community_id, + transaction_executor: profile_caller, + token_id: community_token_id, + profile: profile_caller, + block_timestamp: get_block_timestamp(), + } + ); + } + + // TODO: JOLT UPGRADE SUBSCRIPTION /// @notice internal function to upgrade community /// @param community_id id of community to be upgraded @@ -630,6 +646,113 @@ pub mod CommunityComponent { }; } + /// @notice internal function for add community mod + /// @param community_id id of community + // @param moderator + fn _add_community_mods( + ref self: ComponentState, + community_id: u256, + community_owner: ContractAddress, + moderators: Array + ) { + let length = moderators.len(); + let mut index: u32 = 0; + + while index < length { + let moderator = *moderators.at(index); + let (is_community_member, _) = self.is_community_member(moderator, community_id); + assert(is_community_member == true, NOT_MEMBER); + self.community_mod.write((community_id, moderator), true); + + // emit event + self + .emit( + CommunityModAdded { + community_id: community_id, + transaction_executor: community_owner, + mod_address: moderator, + block_timestamp: get_block_timestamp() + } + ); + index += 1; + }; + } + + /// @notice internal function for remove community mod + /// @param community_id id of community + // @param moderators to remove + fn _remove_community_mods( + ref self: ComponentState, + community_id: u256, + community_owner: ContractAddress, + moderators: Array + ) { + let length = moderators.len(); + let mut index: u32 = 0; + + while index < length { + let moderator = *moderators.at(index); + let (is_community_member, _) = self.is_community_member(moderator, community_id); + assert(is_community_member == true, NOT_MEMBER); + + self.community_mod.write((community_id, moderator), false); + + // emit event + self + .emit( + CommunityModRemoved { + community_id: community_id, + mod_address: moderator, + transaction_executor: community_owner, + block_timestamp: get_block_timestamp() + } + ); + index += 1; + }; + } + + /// @notice internal function for set ban status for members + /// @param community_id id of community to be banned or unbanned + /// @param profiles addresses + /// @param ban_statuses bool values + fn _set_ban_status( + ref self: ComponentState, + community_id: u256, + profiles: Array, + ban_statuses: Array + ) { + assert(profiles.len() == ban_statuses.len(), INVALID_LENGTH); + // for permissioned gating update array of addresses + let length = profiles.len(); + let mut index: u32 = 0; + + while index < length { + let profile = *profiles[index]; + let ban_status = *ban_statuses[index]; + // check profile is a community member + let (is_community_member, _) = self.is_community_member(profile, community_id); + assert(is_community_member == true, NOT_MEMBER); + + // update storage + let community_member = self.community_member.read((community_id, profile)); + let updated_member = CommunityMember { ban_status: ban_status, ..community_member }; + self.community_member.write((community_id, profile), updated_member); + + // emit event + self + .emit( + CommunityBanStatusUpdated { + community_id: community_id, + transaction_executor: get_caller_address(), + profile: profile, + ban_status: ban_status, + block_timestamp: get_block_timestamp() + } + ); + index += 1; + }; + } + /// @notice internal function to deploy a community nft /// @param community_id id of community /// @param salt for randomization @@ -678,10 +801,11 @@ pub mod CommunityComponent { fn _burn_community_nft( ref self: ComponentState, community_nft_address: ContractAddress, + profile: ContractAddress, token_id: u256 ) { ICommunityNftDispatcher { contract_address: community_nft_address } - .burn_nft(get_caller_address(), token_id); + .burn_nft(profile, token_id); } } } diff --git a/src/interfaces/ICommunity.cairo b/src/interfaces/ICommunity.cairo index bb2e04d..9ec22dd 100644 --- a/src/interfaces/ICommunity.cairo +++ b/src/interfaces/ICommunity.cairo @@ -1,6 +1,6 @@ use starknet::{ContractAddress}; use karst::base::constants::types::{ - GateKeepType, CommunityGateKeepDetails, CommunityType, CommunityDetails + GateKeepType, CommunityGateKeepDetails, CommunityType, CommunityDetails, CommunityMember }; // ************************************************************************* @@ -13,13 +13,18 @@ pub trait ICommunity { // EXTERNALS // ************************************************************************* fn create_comminuty(ref self: TState, community_type: CommunityType) -> u256; - fn join_community(ref self: TState, profile: ContractAddress, community_id: u256); - fn leave_community(ref self: TState, profile: ContractAddress, community_id: u256); + fn join_community(ref self: TState, community_id: u256); + fn leave_community(ref self: TState, community_id: u256); fn set_community_metadata_uri(ref self: TState, community_id: u256, metadata_uri: ByteArray); - fn add_community_mods(ref self: TState, community_id: u256, moderator: ContractAddress); - fn remove_community_mods(ref self: TState, community_id: u256, moderator: ContractAddress); + fn add_community_mods(ref self: TState, community_id: u256, moderators: Array); + fn remove_community_mods( + ref self: TState, community_id: u256, moderators: Array + ); fn set_ban_status( - ref self: TState, community_id: u256, profile: ContractAddress, ban_status: bool + ref self: TState, + community_id: u256, + profiles: Array, + ban_statuses: Array ); fn upgrade_community(ref self: TState, community_id: u256, upgrade_type: CommunityType); fn gatekeep( @@ -36,7 +41,9 @@ pub trait ICommunity { // ************************************************************************* fn get_community(self: @TState, community_id: u256) -> CommunityDetails; fn get_community_metadata_uri(self: @TState, community_id: u256) -> ByteArray; - fn is_community_member(self: @TState, profile: ContractAddress, community_id: u256) -> bool; + fn is_community_member( + self: @TState, profile: ContractAddress, community_id: u256 + ) -> (bool, CommunityMember); fn get_total_members(self: @TState, community_id: u256) -> u256; fn is_community_mod(self: @TState, profile: ContractAddress, community_id: u256) -> bool; fn get_ban_status(self: @TState, profile: ContractAddress, community_id: u256) -> bool; diff --git a/tests/test_community.cairo b/tests/test_community.cairo index 8864b9f..95d2ed5 100644 --- a/tests/test_community.cairo +++ b/tests/test_community.cairo @@ -1,724 +1,1325 @@ -// // ************************************************************************* -// // COMMUNITY TEST -// // ************************************************************************* -// use core::option::OptionTrait; -// use core::starknet::SyscallResultTrait; -// use core::result::ResultTrait; -// use core::traits::{TryInto, Into}; -// use starknet::{ContractAddress, get_block_timestamp}; - -// use snforge_std::{ -// declare, start_cheat_caller_address, stop_cheat_caller_address, spy_events, -// EventSpyAssertionsTrait, ContractClassTrait, DeclareResultTrait, EventSpy -// }; - -// use karst::community::community::CommunityComponent; -// use karst::base::constants::types::{GateKeepType, CommunityType}; -// use karst::interfaces::ICommunity::{ICommunityDispatcher, ICommunityDispatcherTrait}; - -// const HUB_ADDRESS: felt252 = 'HUB'; -// const USER_ONE: felt252 = 'BOB'; -// const USER_TWO: felt252 = 'ALICE'; -// const USER_THREE: felt252 = 'ROB'; -// const USER_FOUR: felt252 = 'DAN'; -// const USER_FIVE: felt252 = 'RANDY'; -// const USER_SIX: felt252 = 'JOE'; -// const NFT_ONE: felt252 = 'JOE_NFT'; - -// // ************************************************************************* -// // SETUP -// // ************************************************************************* -// fn __setup__() -> ContractAddress { -// // deploy community nft -// let community_nft_class_hash = declare("CommunityNft").unwrap().contract_class(); - -// // deploy community preset contract -// let community_contract = declare("KarstCommunity").unwrap().contract_class(); -// let mut community_constructor_calldata: Array = array![ -// (*community_nft_class_hash.class_hash).into(), -// ]; -// let (community_contract_address, _) = community_contract -// .deploy(@community_constructor_calldata) -// .unwrap(); - -// return (community_contract_address); -// } - -// // ************************************************************************* -// // TESTS -// // ************************************************************************* -// #[test] -// fn test_community_creation() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = '5t74rhufhu5'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); -// assert(community_id == 1, 'invalid community creation'); -// // TEST TODO: use assert to check for every single item within CommunityDetails structs to -// // ensure they were instantiated with the correct values TEST TODO: check that community nft -// was // deployed correctly and you received an address TEST TODO: use assert to check for -// every // single item within CommunityGateKeepDetails structs to ensure they were instantiated -// with the // correct values -// stop_cheat_caller_address(community_contract_address); -// } - -// // TEST TODO: create a new test fn called `test_community_upgrade_on_creation` where you pass in -// a // premium package type and checks upgrade was successful - -// #[test] -// fn test_create_community_emits_events() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; - -// let salt: felt252 = 'djkngkylu349586'; -// // spy on emitted events -// let mut spy = spy_events(); -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); -// assert(community_id == 1, 'invalid community creation'); -// // check events are emitted -// spy -// .assert_emitted( -// @array![ -// ( -// community_contract_address, -// CommunityComponent::Event::CommunityCreated( -// CommunityComponent::CommunityCreated { -// community_id: community_id, -// community_owner: USER_ONE.try_into().unwrap(), -// community_nft_address: USER_ONE -// .try_into() -// .unwrap(), // COMING BACK TO THIS -// block_timestamp: get_block_timestamp() -// } -// ) -// ) -// ] -// ); -// } - -// #[test] -// fn test_join_community() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'ngkylu349586'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// //create the community -// let community_id = communityDispatcher.create_comminuty(salt); -// // join the community -// communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); -// let (is_member, community) = communityDispatcher -// .is_community_member(USER_ONE.try_into().unwrap(), community_id); -// // println!("is member: {}", is_member); -// assert(is_member == true, 'Not Community Member'); -// // TEST TODO: check every single struct item in CommunityMember was instantiated correctly -// // TEST TODO: check that a community NFT was minted to the user joining -// stop_cheat_caller_address(community_contract_address); -// } - -// #[test] -// #[should_panic(expected: ('Karst: already a Member',))] -// fn test_should_panic_if_a_user_joins_one_community_twice() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'djkn49586'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); - -// let community_id = communityDispatcher.create_comminuty(salt); - -// communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); -// } - -// // TEST TODO: test that joining a community emits event - -// #[test] -// fn test_leave_community() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'djkn4t76349586'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// //create the community -// let community_id = communityDispatcher.create_comminuty(salt); -// // join the community -// communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); - -// stop_cheat_caller_address(community_contract_address); - -// start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); -// communityDispatcher.join_community(USER_TWO.try_into().unwrap(), community_id); - -// stop_cheat_caller_address(community_contract_address); - -// // leave community -// start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); -// communityDispatcher.leave_community(USER_TWO.try_into().unwrap(), community_id); - -// let (is_member, community) = communityDispatcher -// .is_community_member(USER_TWO.try_into().unwrap(), community_id); -// // println!("is member: {}", is_member); -// assert(is_member != true, 'still a community member'); - -// // TEST TODO: check that community total member reduced -// // TEST TODO: check that user NFT is burned on leaving - -// stop_cheat_caller_address(community_contract_address); -// } - -// #[test] -// #[should_panic(expected: ('Karst: Not a Community Member',))] -// fn test_should_panic_if_profile_leaving_is_not_a_member() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'djkn092346'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// //create the community -// let community_id = communityDispatcher.create_comminuty(salt); -// // join the community -// communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); - -// stop_cheat_caller_address(community_contract_address); - -// // leave community -// start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); -// communityDispatcher.leave_community(USER_TWO.try_into().unwrap(), community_id); -// } - -// // TEST TODO: test that leaving a community emits event - -// #[test] -// fn test_set_community_metadata_uri() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'dlosheyr586'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); - -// communityDispatcher -// .set_community_metadata_uri( -// community_id, "ipfs://QmSkDCsS32eLpcymxtn1cEn7Rc5hfefLBgfvZyjaYXr4gQ/" -// ); -// let result_meta_uri = communityDispatcher.get_community_metadata_uri(community_id); -// assert( -// result_meta_uri == "ipfs://QmSkDCsS32eLpcymxtn1cEn7Rc5hfefLBgfvZyjaYXr4gQ/", 'invalid -// uri' -// ); -// stop_cheat_caller_address(community_contract_address); -// } - -// #[test] -// #[should_panic(expected: ('Karst: Not Community owner',))] -// fn test_should_panic_set_community_metadata_uri() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'o0ijh9586'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); - -// stop_cheat_caller_address(community_contract_address); - -// start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); -// let metadata_uri = "ipfs://helloworld"; -// communityDispatcher.set_community_metadata_uri(community_id, metadata_uri); -// } - -// #[test] -// fn test_add_community_mod() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'lkkhjfegky'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); -// // add a community mod -// communityDispatcher.add_community_mods(community_id, USER_SIX.try_into().unwrap()); - -// // check a community mod - is_community_mod -// let is_community_mod = communityDispatcher -// .is_community_mod(USER_SIX.try_into().unwrap(), community_id); -// assert(is_community_mod == true, 'Not a Community Mod'); -// stop_cheat_caller_address(community_contract_address); -// } - -// #[test] -// fn test_add_community_mod_emits_event() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; - -// // spy on emitted events -// let mut spy = spy_events(); -// let salt: felt252 = 'ryehggjh586'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); -// // add a community mod -// communityDispatcher.add_community_mods(community_id, USER_SIX.try_into().unwrap()); - -// spy -// .assert_emitted( -// @array![ -// ( -// community_contract_address, -// CommunityComponent::Event::CommunityModAdded( -// CommunityComponent::CommunityModAdded { -// community_id: community_id, -// transaction_executor: USER_ONE.try_into().unwrap(), -// mod_address: USER_SIX.try_into().unwrap(), -// block_timestamp: get_block_timestamp() -// } -// ) -// ) -// ] -// ); -// } - -// #[test] -// #[should_panic(expected: ('Karst: Not Community owner',))] -// fn should_panic_if_caller_adding_mod_is_not_owner() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'dfghopeuryljk'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); - -// stop_cheat_caller_address(community_contract_address); - -// // when a wrong community owner try to add a MOD -// start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); - -// // add a community mod -// communityDispatcher.add_community_mods(community_id, USER_SIX.try_into().unwrap()); -// } - -// // TEST TODO: write an extra test called `should_panic_if_mod_is_not_member` to check that a mod -// // must first be a community member - -// #[test] -// fn test_remove_community_mod() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'djsdfghk9586'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); -// // add a community mod -// communityDispatcher.add_community_mods(community_id, USER_SIX.try_into().unwrap()); -// communityDispatcher.add_community_mods(community_id, USER_FOUR.try_into().unwrap()); -// communityDispatcher.add_community_mods(community_id, USER_FIVE.try_into().unwrap()); - -// // REMOVE A MOD -// communityDispatcher.remove_community_mods(community_id, USER_FIVE.try_into().unwrap()); - -// // check a community mod - is_community_mod -// let is_community_mod = communityDispatcher -// .is_community_mod(USER_FIVE.try_into().unwrap(), community_id); -// assert(is_community_mod == false, 'Community Mod Not Remove'); -// stop_cheat_caller_address(community_contract_address); -// } - -// #[test] -// fn test_remove_community_mod_emit_event() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'dddfhjk86'; -// // spy on emitted events -// let mut spy = spy_events(); -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); -// // add a community mod -// communityDispatcher.add_community_mods(community_id, USER_SIX.try_into().unwrap()); -// communityDispatcher.add_community_mods(community_id, USER_FOUR.try_into().unwrap()); -// communityDispatcher.add_community_mods(community_id, USER_FIVE.try_into().unwrap()); - -// // REMOVE A MOD -// communityDispatcher.remove_community_mods(community_id, USER_FIVE.try_into().unwrap()); - -// spy -// .assert_emitted( -// @array![ -// ( -// community_contract_address, -// CommunityComponent::Event::CommunityModRemoved( -// CommunityComponent::CommunityModRemoved { -// community_id: community_id, -// transaction_executor: USER_ONE.try_into().unwrap(), -// mod_address: USER_FIVE.try_into().unwrap(), -// block_timestamp: get_block_timestamp() -// } -// ) -// ) -// ] -// ); -// } - -// #[test] -// #[should_panic(expected: ('Karst: Not Community owner',))] -// fn should_panic_if_caller_removing_mod_is_not_owner() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'djkngkylu349586'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); - -// stop_cheat_caller_address(community_contract_address); - -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); - -// // add a community mod -// communityDispatcher.add_community_mods(community_id, USER_SIX.try_into().unwrap()); -// communityDispatcher.add_community_mods(community_id, USER_FOUR.try_into().unwrap()); - -// stop_cheat_caller_address(community_contract_address); - -// // when a wrong community owner try to REMOVE a MOD -// start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); - -// // REMOVE A MOD -// communityDispatcher.remove_community_mods(community_id, USER_FIVE.try_into().unwrap()); -// } - -// #[test] -// fn test_set_ban_status_by_owner() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'djkngkylu349586'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// //create the community -// let community_id = communityDispatcher.create_comminuty(salt); -// // join the community -// communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_TWO.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_THREE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_FOUR.try_into().unwrap(), community_id); - -// communityDispatcher.set_ban_status(community_id, USER_TWO.try_into().unwrap(), true); - -// let is_ban = communityDispatcher.get_ban_status(USER_TWO.try_into().unwrap(), community_id); - -// assert(is_ban == true, 'Community Member is not banned'); - -// stop_cheat_caller_address(community_contract_address); -// } - -// #[test] -// fn test_set_ban_status_by_mod() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'sdery586'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// //create the community -// let community_id = communityDispatcher.create_comminuty(salt); -// // join the community -// communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_TWO.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_THREE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_FOUR.try_into().unwrap(), community_id); -// // add a community mod -// communityDispatcher.add_community_mods(community_id, USER_SIX.try_into().unwrap()); -// stop_cheat_caller_address(community_contract_address); - -// start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); -// communityDispatcher.set_ban_status(community_id, USER_TWO.try_into().unwrap(), true); - -// let is_ban = communityDispatcher.get_ban_status(USER_TWO.try_into().unwrap(), community_id); - -// assert(is_ban == true, 'Community Member is not banned'); - -// stop_cheat_caller_address(community_contract_address); -// } - -// #[test] -// fn test_set_ban_status_emit_event() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = '495ksjdhfgjrkf86'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// //create the community -// let community_id = communityDispatcher.create_comminuty(salt); -// // join the community -// communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_TWO.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_THREE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_FOUR.try_into().unwrap(), community_id); -// // add a community mod -// communityDispatcher.add_community_mods(community_id, USER_SIX.try_into().unwrap()); -// stop_cheat_caller_address(community_contract_address); - -// // spy on emitted events -// let mut spy = spy_events(); -// start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); -// // set ban -// communityDispatcher.set_ban_status(community_id, USER_TWO.try_into().unwrap(), true); - -// spy -// .assert_emitted( -// @array![ -// ( -// community_contract_address, -// CommunityComponent::Event::CommunityBanStatusUpdated( -// CommunityComponent::CommunityBanStatusUpdated { -// community_id: community_id, -// transaction_executor: USER_SIX.try_into().unwrap(), -// profile: USER_TWO.try_into().unwrap(), -// block_timestamp: get_block_timestamp() -// } -// ) -// ) -// ] -// ); -// } - -// #[test] -// #[should_panic(expected: ('Karst: user unauthorized!',))] -// fn test_should_panic_if_caller_to_set_ban_status_is_not_owner_or_mod() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'djkrtyhjejfg6'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// //create the community -// let community_id = communityDispatcher.create_comminuty(salt); -// // join the community -// communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_TWO.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_THREE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_FOUR.try_into().unwrap(), community_id); -// // add a community mod -// communityDispatcher.add_community_mods(community_id, USER_SIX.try_into().unwrap()); -// stop_cheat_caller_address(community_contract_address); - -// start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); -// communityDispatcher.set_ban_status(community_id, USER_TWO.try_into().unwrap(), true); -// } - -// // TEST TODO: create a test fn called `test_can_only_set_ban_status_for_members` to check that -// you // can only ban existing members - -// #[test] -// fn test_community_upgrade() { -// let community_contract_address = __setup__(); -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'sfhkmpkippe86'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); -// communityDispatcher.upgrade_community(community_id, CommunityType::Standard); -// let community = communityDispatcher.get_community(community_id); -// assert(community.community_type == CommunityType::Standard, 'Community Upgrade failed'); -// // TEST TODO: check that upgraded communities have a premium status -// stop_cheat_caller_address(community_contract_address); -// } - -// #[test] -// #[should_panic(expected: ('Karst: Not Community owner',))] -// fn test_should_panic_if_caller_upgrading_is_not_owner() { -// let community_contract_address = __setup__(); -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'djkdgjlorityi86'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); - -// stop_cheat_caller_address(community_contract_address); - -// start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); -// communityDispatcher.upgrade_community(community_id, CommunityType::Standard); -// let community = communityDispatcher.get_community(community_id); -// assert(community.community_type == CommunityType::Standard, 'Community Upgrade failed'); -// } - -// #[test] -// fn test_community_upgrade_emits_event() { -// let community_contract_address = __setup__(); -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// let salt: felt252 = 'djcxbvnk586'; -// // spy on emitted events -// let mut spy = spy_events(); - -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); -// communityDispatcher.upgrade_community(community_id, CommunityType::Standard); - -// // check events are emitted -// spy -// .assert_emitted( -// @array![ -// ( -// community_contract_address, -// CommunityComponent::Event::CommunityUpgraded( -// CommunityComponent::CommunityUpgraded { -// community_id: community_id, -// transaction_executor: USER_ONE.try_into().unwrap(), -// premiumType: CommunityType::Standard, -// block_timestamp: get_block_timestamp() -// } -// ) -// ) -// ] -// ); -// } - -// #[test] -// fn test_permissioned_gatekeeping() { -// let community_contract_address = __setup__(); -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; - -// let mut permission_addresses = ArrayTrait::new(); -// permission_addresses.append(USER_SIX.try_into().unwrap()); -// permission_addresses.append(USER_FIVE.try_into().unwrap()); -// permission_addresses.append(USER_THREE.try_into().unwrap()); -// let salt: felt252 = 'djzcvnyoy6'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); -// communityDispatcher -// .gatekeep( -// community_id, -// GateKeepType::PermissionedGating, -// NFT_ONE.try_into().unwrap(), -// permission_addresses, -// 0 -// ); - -// // check is_gatekeeped -// let (is_gatekeeped, _) = communityDispatcher.is_gatekeeped(community_id); -// assert(is_gatekeeped == true, 'Community gatekeep failed'); -// stop_cheat_caller_address(community_contract_address); -// } - -// #[test] -// fn test_paid_gatekeeping() { -// let community_contract_address = __setup__(); -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; - -// let mut permission_addresses = ArrayTrait::new(); -// permission_addresses.append(USER_SIX.try_into().unwrap()); -// permission_addresses.append(USER_FIVE.try_into().unwrap()); -// permission_addresses.append(USER_THREE.try_into().unwrap()); -// let salt: felt252 = 'djkngzxvbnlk'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); -// communityDispatcher -// .gatekeep( -// community_id, GateKeepType::Paid, NFT_ONE.try_into().unwrap(), permission_addresses, -// 450 -// ); - -// // check is_gatekeeped -// let (is_gatekeeped, _) = communityDispatcher.is_gatekeeped(community_id); -// assert(is_gatekeeped == true, 'Community gatekeep failed'); -// stop_cheat_caller_address(community_contract_address); -// } - -// // TEST TODO: add test fn `test_nft_gatekeeping` for NFTGating -// // TEST TODO: add test fn `test_only_premium_communities_can_be_paid_gated` to test that only -// // premium communities can enforce PaidGating TEST TODO: add test fn -// // `test_only_premium_communities_can_be_nft_gated` to test that only premium communities can -// // enforce NFTGating - -// #[test] -// #[should_panic(expected: ('Karst: Not Community owner',))] -// fn test_should_panic_if_caller_to_gatekeep_is_not_owner() { -// let community_contract_address = __setup__(); -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; - -// let mut permission_addresses = ArrayTrait::new(); -// permission_addresses.append(USER_SIX.try_into().unwrap()); -// permission_addresses.append(USER_FIVE.try_into().unwrap()); -// permission_addresses.append(USER_THREE.try_into().unwrap()); -// let salt: felt252 = 'djksfkityu9586'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); - -// stop_cheat_caller_address(community_contract_address); - -// // Wrong owner trying to gate keep -// start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); -// communityDispatcher -// .gatekeep( -// community_id, GateKeepType::Paid, NFT_ONE.try_into().unwrap(), permission_addresses, -// 450 -// ); -// } - -// #[test] -// fn test_community_gatekeep_emits_event() { -// let community_contract_address = __setup__(); -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; - -// let mut permission_addresses = ArrayTrait::new(); -// permission_addresses.append(USER_SIX.try_into().unwrap()); -// permission_addresses.append(USER_FIVE.try_into().unwrap()); -// permission_addresses.append(USER_THREE.try_into().unwrap()); -// let salt: felt252 = 'djadfyh09023'; -// // spy on emitted events -// let mut spy = spy_events(); - -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(salt); -// communityDispatcher -// .gatekeep( -// community_id, -// GateKeepType::PermissionedGating, -// NFT_ONE.try_into().unwrap(), -// permission_addresses, -// 0 -// ); - -// // check events are emitted -// spy -// .assert_emitted( -// @array![ -// ( -// community_contract_address, -// CommunityComponent::Event::CommunityGatekeeped( -// CommunityComponent::CommunityGatekeeped { -// community_id: community_id, -// transaction_executor: USER_ONE.try_into().unwrap(), -// gatekeepType: GateKeepType::PermissionedGating, -// block_timestamp: get_block_timestamp() -// } -// ) -// ) -// ] -// ); -// } +// ************************************************************************* +// COMMUNITY TEST +// ************************************************************************* +use core::option::OptionTrait; +use core::starknet::SyscallResultTrait; +use core::result::ResultTrait; +use core::traits::{TryInto, Into}; +use starknet::{ContractAddress, get_block_timestamp, contract_address_const}; + +use snforge_std::{ + declare, start_cheat_caller_address, stop_cheat_caller_address, spy_events, + EventSpyAssertionsTrait, ContractClassTrait, DeclareResultTrait, EventSpy +}; + +use karst::community::community::CommunityComponent; +use karst::base::constants::types::{GateKeepType, CommunityType}; +use karst::interfaces::ICommunity::{ICommunityDispatcher, ICommunityDispatcherTrait}; + +const HUB_ADDRESS: felt252 = 'HUB'; +const USER_ONE: felt252 = 'BOB'; +const USER_TWO: felt252 = 'ALICE'; +const USER_THREE: felt252 = 'ROB'; +const USER_FOUR: felt252 = 'DAN'; +const USER_FIVE: felt252 = 'RANDY'; +const USER_SIX: felt252 = 'JOE'; +const NFT_ONE: felt252 = 'JOE_NFT'; + +// ************************************************************************* +// SETUP +// ************************************************************************* +fn __setup__() -> ContractAddress { + // deploy community nft + let community_nft_class_hash = declare("CommunityNFT").unwrap().contract_class(); + + // deploy community preset contract + let community_contract = declare("KarstCommunity").unwrap().contract_class(); + let mut community_constructor_calldata: Array = array![ + (*community_nft_class_hash.class_hash).into(), + ]; + let (community_contract_address, _) = community_contract + .deploy(@community_constructor_calldata) + .unwrap(); + + return (community_contract_address); +} + +// ************************************************************************* +// TESTS +// ************************************************************************* +#[test] +fn test_community_creation() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + assert(community_id == 1, 'invalid community creation'); + let community_data = communityDispatcher.get_community(community_id); + + assert(community_data.community_id == community_id, 'invalid community ID'); + assert(community_data.community_type == CommunityType::Free, 'invalid community type'); + assert(community_data.community_owner == USER_ONE.try_into().unwrap(), 'invalid owner'); + assert(community_data.community_total_members == 1, 'invalid total members'); + assert(community_data.community_premium_status == false, 'invalid premium status'); + + assert( + community_data.community_nft_address != contract_address_const::<0>(), + 'community nft was not deployed' + ); + + let (_, gate_keep_details) = communityDispatcher.is_gatekeeped(community_id); + assert(gate_keep_details.gate_keep_type == GateKeepType::None, 'invalid community type'); + assert( + gate_keep_details.community_nft_address == community_data.community_nft_address, + 'invalid community NFT address' + ); + assert(community_data.community_id == community_id, 'invalid community ID'); + assert(gate_keep_details.entry_fee == 0, 'invalid community gatekeep fee'); + + stop_cheat_caller_address(community_contract_address); +} + + +#[test] +fn test_community_upgrade_on_creation() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Standard); + assert(community_id == 1, 'invalid community creation'); + let community_data = communityDispatcher.get_community(community_id); + assert(community_data.community_type == CommunityType::Standard, 'invalid community type'); + assert(community_data.community_premium_status == true, 'invalid community type'); +} +#[test] +fn test_create_community_emits_events() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + // spy on emitted events + let mut spy = spy_events(); + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + assert(community_id == 1, 'invalid community creation'); + // get community details + let community = communityDispatcher.get_community(community_id); + // check events are emitted + spy + .assert_emitted( + @array![ + ( + community_contract_address, + CommunityComponent::Event::CommunityCreated( + CommunityComponent::CommunityCreated { + community_id: community_id, + community_owner: USER_ONE.try_into().unwrap(), + community_nft_address: community.community_nft_address, + block_timestamp: get_block_timestamp() + } + ) + ) + ] + ); +} + +#[test] +fn test_join_community() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + //create the community + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + let (is_member, _) = communityDispatcher + .is_community_member(USER_ONE.try_into().unwrap(), community_id); + + assert(is_member == true, 'Not Community Member'); + + let (_, community_member) = communityDispatcher + .is_community_member(USER_ONE.try_into().unwrap(), community_id); + assert(community_member.community_id == community_id, 'Invalid Community id'); + assert( + community_member.profile_address == USER_ONE.try_into().unwrap(), + 'invalid community memeber' + ); + assert(community_member.total_publications == 0, 'invalid total publication'); + assert(community_member.ban_status == false, 'invalid ban status'); // community_token_id + assert(community_member.community_token_id != 0, 'invalid nft mint token'); + + stop_cheat_caller_address(community_contract_address); +} + +#[test] +#[should_panic(expected: ('Karst: already a Member',))] +fn test_should_panic_if_a_user_joins_one_community_twice() { + let community_contract_address = __setup__(); + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); + + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + stop_cheat_caller_address(community_contract_address); +} + + +#[test] +fn test_joining_community_emits_event() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + // spy on emitted events + let mut spy = spy_events(); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + let (_, member_details) = communityDispatcher + .is_community_member(USER_THREE.try_into().unwrap(), community_id); + + spy + .assert_emitted( + @array![ + ( + community_contract_address, + CommunityComponent::Event::JoinedCommunity( + CommunityComponent::JoinedCommunity { + community_id: community_id, + transaction_executor: USER_THREE.try_into().unwrap(), + token_id: member_details.community_token_id, + profile: USER_THREE.try_into().unwrap(), + block_timestamp: get_block_timestamp(), + } + ) + ) + ] + ); +} + + +#[test] +fn test_leave_community() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + //create the community + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); + + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + // leave community + start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); + + communityDispatcher.leave_community(community_id); + + let (is_member, member) = communityDispatcher + .is_community_member(USER_TWO.try_into().unwrap(), community_id); + + assert(is_member != true, 'still a community member'); + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); + + let get_total_members = communityDispatcher.get_total_members(community_id); + assert(get_total_members == 1, 'No reduction in total member'); + + assert(member.community_token_id == 0, 'NFT is not burn'); + stop_cheat_caller_address(community_contract_address); +} + + +#[test] +#[should_panic(expected: ('Karst: Not a Community Member',))] +fn test_should_panic_if_profile_leaving_is_not_a_member() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + //create the community + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + stop_cheat_caller_address(community_contract_address); + + // leave community + start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); + communityDispatcher.leave_community(community_id); +} + + +#[test] +fn test_leave_community_emits_event() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + // spy on emitted events + let mut spy = spy_events(); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); + let (_, member_details) = communityDispatcher + .is_community_member(USER_THREE.try_into().unwrap(), community_id); + communityDispatcher.leave_community(community_id); + + spy + .assert_emitted( + @array![ + ( + community_contract_address, + CommunityComponent::Event::LeftCommunity( + CommunityComponent::LeftCommunity { + community_id: community_id, + transaction_executor: USER_THREE.try_into().unwrap(), + token_id: member_details.community_token_id, + profile: USER_THREE.try_into().unwrap(), + block_timestamp: get_block_timestamp(), + } + ) + ) + ] + ); +} + + +#[test] +fn test_set_community_metadata_uri() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + communityDispatcher + .set_community_metadata_uri( + community_id, "ipfs://QmSkDCsS32eLpcymxtn1cEn7Rc5hfefLBgfvZyjaYXr4gQ/" + ); + let result_meta_uri = communityDispatcher.get_community_metadata_uri(community_id); + assert( + result_meta_uri == "ipfs://QmSkDCsS32eLpcymxtn1cEn7Rc5hfefLBgfvZyjaYXr4gQ/", + 'invalid + uri' + ); + stop_cheat_caller_address(community_contract_address); +} + +#[test] +#[should_panic(expected: ('Karst: Not Community owner',))] +fn test_should_panic_set_community_metadata_uri() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); + let metadata_uri = "ipfs://helloworld"; + communityDispatcher.set_community_metadata_uri(community_id, metadata_uri); +} + +#[test] +fn test_add_community_mod() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + // join the community + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_FIVE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + // mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_SIX.try_into().unwrap()); + moderators.append(USER_FIVE.try_into().unwrap()); + + // add a community mod + communityDispatcher.add_community_mods(community_id, moderators); + + // check a community mod - is_community_mod + let is_community_mod = communityDispatcher + .is_community_mod(USER_SIX.try_into().unwrap(), community_id); + assert(is_community_mod == true, 'Not a Community Mod'); + stop_cheat_caller_address(community_contract_address); +} + +#[test] +fn test_add_community_mod_emits_event() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + // spy on emitted events + let mut spy = spy_events(); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + // join the community first + communityDispatcher.join_community(community_id); + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + // mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_SIX.try_into().unwrap()); + + // add a community mod + communityDispatcher.add_community_mods(community_id, moderators); + + spy + .assert_emitted( + @array![ + ( + community_contract_address, + CommunityComponent::Event::CommunityModAdded( + CommunityComponent::CommunityModAdded { + community_id: community_id, + transaction_executor: USER_ONE.try_into().unwrap(), + mod_address: USER_SIX.try_into().unwrap(), + block_timestamp: get_block_timestamp() + } + ) + ) + ] + ); +} + +#[test] +#[should_panic(expected: ('Karst: Not Community owner',))] +fn should_panic_if_caller_adding_mod_is_not_owner() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + stop_cheat_caller_address(community_contract_address); + + // when a wrong community owner try to add a MOD + start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); + + // mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_SIX.try_into().unwrap()); + // add a community mod + communityDispatcher.add_community_mods(community_id, moderators); +} + +#[test] +#[should_panic(expected: ('Karst: Not a Community Member',))] +fn should_panic_if_mod_is_not_member() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_FIVE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + // mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_SIX.try_into().unwrap()); + moderators.append(USER_FIVE.try_into().unwrap()); + + // add a community mod + communityDispatcher.add_community_mods(community_id, moderators); +} + + +#[test] +fn test_remove_community_mod() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + // join commmunity + start_cheat_caller_address(community_contract_address, USER_FIVE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_FOUR.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + // add mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_SIX.try_into().unwrap()); + moderators.append(USER_FOUR.try_into().unwrap()); + + // add a community mod + communityDispatcher.add_community_mods(community_id, moderators); + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + // remove mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_SIX.try_into().unwrap()); + moderators.append(USER_FOUR.try_into().unwrap()); + // REMOVE A MOD + communityDispatcher.remove_community_mods(community_id, moderators); + + // check a community mod - is_community_mod + let is_community_mod = communityDispatcher + .is_community_mod(USER_FOUR.try_into().unwrap(), community_id); + assert(is_community_mod == false, 'Community Mod Not Remove'); + stop_cheat_caller_address(community_contract_address); +} + +#[test] +fn test_remove_community_mod_emit_event() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + // spy on emitted events + let mut spy = spy_events(); + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + // join commmunity + start_cheat_caller_address(community_contract_address, USER_FIVE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_FOUR.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + // add mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_SIX.try_into().unwrap()); + moderators.append(USER_FIVE.try_into().unwrap()); + moderators.append(USER_FOUR.try_into().unwrap()); + + // add a community mod + communityDispatcher.add_community_mods(community_id, moderators); + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + // remove mod array + let mut remove_moderators = ArrayTrait::new(); + remove_moderators.append(USER_SIX.try_into().unwrap()); + remove_moderators.append(USER_FOUR.try_into().unwrap()); + // REMOVE A MOD + communityDispatcher.remove_community_mods(community_id, remove_moderators); + + spy + .assert_emitted( + @array![ + ( + community_contract_address, + CommunityComponent::Event::CommunityModRemoved( + CommunityComponent::CommunityModRemoved { + community_id: community_id, + transaction_executor: USER_ONE.try_into().unwrap(), + mod_address: USER_FOUR.try_into().unwrap(), + block_timestamp: get_block_timestamp() + } + ) + ) + ] + ); +} + +#[test] +#[should_panic(expected: ('Karst: Not a Community Member',))] +fn test_should_panic_if_mod_is_not_community_member() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + // join commmunity + start_cheat_caller_address(community_contract_address, USER_FIVE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_FOUR.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + // add mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_SIX.try_into().unwrap()); + moderators.append(USER_FOUR.try_into().unwrap()); + + // add a community mod + communityDispatcher.add_community_mods(community_id, moderators); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + + // remove mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_SIX.try_into().unwrap()); + moderators.append(USER_TWO.try_into().unwrap()); + // REMOVE A MOD + communityDispatcher.remove_community_mods(community_id, moderators); +} + +#[test] +#[should_panic(expected: ('Karst: Not Community owner',))] +fn test_should_panic_if_caller_removing_mod_is_not_owner() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + // join commmunity + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_FIVE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_FOUR.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + + // add mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_SIX.try_into().unwrap()); + moderators.append(USER_FOUR.try_into().unwrap()); + + // add a community mod + communityDispatcher.add_community_mods(community_id, moderators); + stop_cheat_caller_address(community_contract_address); + + // when a wrong community owner try to REMOVE a MOD + start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); + // remove mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_SIX.try_into().unwrap()); + moderators.append(USER_FOUR.try_into().unwrap()); + // REMOVE A MOD + communityDispatcher.remove_community_mods(community_id, moderators); +} + +#[test] +fn test_set_ban_status_by_owner() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + //create the community + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + // join the community + start_cheat_caller_address(community_contract_address, USER_FOUR.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + // ban profile list + let mut profiles = ArrayTrait::new(); + profiles.append(USER_SIX.try_into().unwrap()); + profiles.append(USER_FOUR.try_into().unwrap()); + + // ban status list + let mut ban_statuses = ArrayTrait::new(); + ban_statuses.append(true); + ban_statuses.append(true); + + communityDispatcher.set_ban_status(community_id, profiles, ban_statuses); + + let is_ban = communityDispatcher.get_ban_status(USER_FOUR.try_into().unwrap(), community_id); + + assert(is_ban == true, 'Community Member is not banned'); + + stop_cheat_caller_address(community_contract_address); +} + +#[test] +fn test_set_ban_status_by_mod() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + stop_cheat_caller_address(community_contract_address); + + // join the community + start_cheat_caller_address(community_contract_address, USER_FOUR.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + // add a community mod + // add mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_FOUR.try_into().unwrap()); + + // add a community mod + communityDispatcher.add_community_mods(community_id, moderators); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_FOUR.try_into().unwrap()); + + // ban profile list + let mut profiles = ArrayTrait::new(); + profiles.append(USER_THREE.try_into().unwrap()); + profiles.append(USER_TWO.try_into().unwrap()); + + // ban status list + let mut ban_statuses = ArrayTrait::new(); + ban_statuses.append(true); + ban_statuses.append(true); + + communityDispatcher.set_ban_status(community_id, profiles, ban_statuses); + + let is_ban = communityDispatcher.get_ban_status(USER_TWO.try_into().unwrap(), community_id); + + assert(is_ban == true, 'Community Member is not banned'); + + stop_cheat_caller_address(community_contract_address); +} + +#[test] +fn test_set_ban_status_emit_event() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + // + + // spy on emitted events + let mut spy = spy_events(); + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_FOUR.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + // add a community mod + // add mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_FOUR.try_into().unwrap()); + + // add a community mod + communityDispatcher.add_community_mods(community_id, moderators); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + // ban profile list + let mut profiles = ArrayTrait::new(); + profiles.append(USER_THREE.try_into().unwrap()); + profiles.append(USER_TWO.try_into().unwrap()); + + // ban status list + let mut ban_statuses = ArrayTrait::new(); + ban_statuses.append(true); + ban_statuses.append(true); + + // set ban + communityDispatcher.set_ban_status(community_id, profiles, ban_statuses); + + spy + .assert_emitted( + @array![ + ( + community_contract_address, + CommunityComponent::Event::CommunityBanStatusUpdated( + CommunityComponent::CommunityBanStatusUpdated { + community_id: community_id, + transaction_executor: USER_SIX.try_into().unwrap(), + profile: USER_TWO.try_into().unwrap(), + ban_status: true, + block_timestamp: get_block_timestamp() + } + ) + ) + ] + ); +} + +#[test] +#[should_panic(expected: ('Karst: user unauthorized!',))] +fn test_should_panic_if_caller_to_set_ban_status_is_not_owner_or_mod() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + //create the community + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + stop_cheat_caller_address(community_contract_address); + + // join the community + start_cheat_caller_address(community_contract_address, USER_FOUR.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_FIVE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + // mode array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_SIX.try_into().unwrap()); + moderators.append(USER_FIVE.try_into().unwrap()); + + // add a community mod + communityDispatcher.add_community_mods(community_id, moderators); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); + // ban status list + let mut ban_statuses = ArrayTrait::new(); + ban_statuses.append(true); + ban_statuses.append(true); + + // ban profile list + let mut profiles = ArrayTrait::new(); + profiles.append(USER_FOUR.try_into().unwrap()); + profiles.append(USER_TWO.try_into().unwrap()); + // set ban + communityDispatcher.set_ban_status(community_id, profiles, ban_statuses); +} + +#[test] +#[should_panic(expected: ('Karst: Not a Community Member',))] +fn test_can_only_set_ban_status_for_members() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + //create the community + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + // join the community + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + + // ban profile list + let mut profiles = ArrayTrait::new(); + profiles.append(USER_SIX.try_into().unwrap()); + profiles.append(USER_FOUR.try_into().unwrap()); + + // ban status list + let mut ban_statuses = ArrayTrait::new(); + ban_statuses.append(true); + ban_statuses.append(true); + + communityDispatcher.set_ban_status(community_id, profiles, ban_statuses); +} + + +// TEST TODO: TEST To make sure length of ban status and profiles are same +#[test] +#[should_panic(expected: ('Karst: Invalid Length',))] +fn test_should_set_ban_status_for_invalid_array_length() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + //create the community + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + // join the community + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); + communityDispatcher.join_community(community_id); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + // ban profile list + let mut profiles = ArrayTrait::new(); + profiles.append(USER_SIX.try_into().unwrap()); + + // ban status list + let mut ban_statuses = ArrayTrait::new(); + ban_statuses.append(true); + ban_statuses.append(true); + + communityDispatcher.set_ban_status(community_id, profiles, ban_statuses); +} + + +#[test] +fn test_community_upgrade() { + let community_contract_address = __setup__(); + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + communityDispatcher.upgrade_community(community_id, CommunityType::Standard); + let community = communityDispatcher.get_community(community_id); + assert(community.community_type == CommunityType::Standard, 'Community Upgrade failed'); + // TEST TODO: check that upgraded communities have a premium status + assert(community.community_premium_status == true, 'No Premium Status for Community'); + stop_cheat_caller_address(community_contract_address); +} + +#[test] +#[should_panic(expected: ('Karst: Not Community owner',))] +fn test_should_panic_if_caller_upgrading_is_not_owner() { + let community_contract_address = __setup__(); + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); + communityDispatcher.upgrade_community(community_id, CommunityType::Standard); + let community = communityDispatcher.get_community(community_id); + assert(community.community_type == CommunityType::Standard, 'Community Upgrade failed'); +} + +#[test] +fn test_community_upgrade_emits_event() { + let community_contract_address = __setup__(); + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + // spy on emitted events + let mut spy = spy_events(); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + communityDispatcher.upgrade_community(community_id, CommunityType::Standard); + + // check events are emitted + spy + .assert_emitted( + @array![ + ( + community_contract_address, + CommunityComponent::Event::CommunityUpgraded( + CommunityComponent::CommunityUpgraded { + community_id: community_id, + transaction_executor: USER_ONE.try_into().unwrap(), + premiumType: CommunityType::Standard, + block_timestamp: get_block_timestamp() + } + ) + ) + ] + ); +} + +#[test] +fn test_permissioned_gatekeeping() { + let community_contract_address = __setup__(); + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + let mut permission_addresses = ArrayTrait::new(); + permission_addresses.append(USER_SIX.try_into().unwrap()); + permission_addresses.append(USER_FIVE.try_into().unwrap()); + permission_addresses.append(USER_THREE.try_into().unwrap()); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + communityDispatcher + .gatekeep( + community_id, + GateKeepType::PermissionedGating, + NFT_ONE.try_into().unwrap(), + permission_addresses, + 0 + ); + + // check is_gatekeeped + let (is_gatekeeped, _) = communityDispatcher.is_gatekeeped(community_id); + assert(is_gatekeeped == true, 'Community gatekeep failed'); + stop_cheat_caller_address(community_contract_address); +} + +#[test] +fn test_paid_gatekeeping() { + let community_contract_address = __setup__(); + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + let mut permission_addresses = ArrayTrait::new(); + permission_addresses.append(USER_SIX.try_into().unwrap()); + permission_addresses.append(USER_FIVE.try_into().unwrap()); + permission_addresses.append(USER_THREE.try_into().unwrap()); + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + communityDispatcher.upgrade_community(community_id, CommunityType::Standard); + communityDispatcher + .gatekeep( + community_id, + GateKeepType::PaidGating, + NFT_ONE.try_into().unwrap(), + permission_addresses, + 450 + ); + + // check is_gatekeeped + let (is_gatekeeped, _) = communityDispatcher.is_gatekeeped(community_id); + assert(is_gatekeeped == true, 'Community gatekeep failed'); + stop_cheat_caller_address(community_contract_address); +} + +#[test] +fn test_nft_gatekeeping() { + let community_contract_address = __setup__(); + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + let mut permission_addresses = ArrayTrait::new(); + permission_addresses.append(USER_SIX.try_into().unwrap()); + permission_addresses.append(USER_FIVE.try_into().unwrap()); + permission_addresses.append(USER_THREE.try_into().unwrap()); + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + communityDispatcher.upgrade_community(community_id, CommunityType::Standard); + communityDispatcher + .gatekeep( + community_id, + GateKeepType::NFTGating, + NFT_ONE.try_into().unwrap(), + permission_addresses, + 450 + ); + + // check is_gatekeeped + let (is_gatekeeped, gatekeep_details) = communityDispatcher.is_gatekeeped(community_id); + assert( + gatekeep_details.gate_keep_type == GateKeepType::NFTGating, 'Community NFT Gatekeep Failed' + ); + assert( + gatekeep_details.community_nft_address != contract_address_const::<0>(), + 'NFT Gatekeep was not Added' + ); + + stop_cheat_caller_address(community_contract_address); +} + + +#[test] +#[should_panic(expected: ('Karst: only premium communities',))] +fn test_only_premium_communities_can_be_paid_gated() { + let community_contract_address = __setup__(); + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + let mut permission_addresses = ArrayTrait::new(); + permission_addresses.append(USER_SIX.try_into().unwrap()); + permission_addresses.append(USER_FIVE.try_into().unwrap()); + permission_addresses.append(USER_THREE.try_into().unwrap()); + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + communityDispatcher + .gatekeep( + community_id, + GateKeepType::PaidGating, + NFT_ONE.try_into().unwrap(), + permission_addresses, + 450 + ); + + // check is_gatekeeped + let (is_gatekeeped, gatekeep_details) = communityDispatcher.is_gatekeeped(community_id); + assert( + gatekeep_details.gate_keep_type == GateKeepType::PaidGating, + 'Community Paid Gatekeep Failed' + ); + stop_cheat_caller_address(community_contract_address); +} + +#[test] +#[should_panic(expected: ('Karst: only premium communities',))] +fn test_only_premium_communities_can_be_nft_gated() { + let community_contract_address = __setup__(); + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + let mut permission_addresses = ArrayTrait::new(); + permission_addresses.append(USER_SIX.try_into().unwrap()); + permission_addresses.append(USER_FIVE.try_into().unwrap()); + permission_addresses.append(USER_THREE.try_into().unwrap()); + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + communityDispatcher + .gatekeep( + community_id, + GateKeepType::PaidGating, + NFT_ONE.try_into().unwrap(), + permission_addresses, + 450 + ); + + // check is_gatekeeped + let (is_gatekeeped, gatekeep_details) = communityDispatcher.is_gatekeeped(community_id); + assert( + gatekeep_details.gate_keep_type == GateKeepType::PaidGating, + 'Community Paid Gatekeep Failed' + ); + stop_cheat_caller_address(community_contract_address); +} + + +#[test] +#[should_panic(expected: ('Karst: Not Community owner',))] +fn test_should_panic_if_caller_to_gatekeep_is_not_owner() { + let community_contract_address = __setup__(); + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + let mut permission_addresses = ArrayTrait::new(); + permission_addresses.append(USER_SIX.try_into().unwrap()); + permission_addresses.append(USER_FIVE.try_into().unwrap()); + permission_addresses.append(USER_THREE.try_into().unwrap()); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + + stop_cheat_caller_address(community_contract_address); + + // Wrong owner trying to gate keep + start_cheat_caller_address(community_contract_address, USER_TWO.try_into().unwrap()); + communityDispatcher + .gatekeep( + community_id, + GateKeepType::PaidGating, + NFT_ONE.try_into().unwrap(), + permission_addresses, + 450 + ); +} + +#[test] +fn test_community_gatekeep_emits_event() { + let community_contract_address = __setup__(); + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + let mut permission_addresses = ArrayTrait::new(); + permission_addresses.append(USER_SIX.try_into().unwrap()); + permission_addresses.append(USER_FIVE.try_into().unwrap()); + permission_addresses.append(USER_THREE.try_into().unwrap()); + + // spy on emitted events + let mut spy = spy_events(); + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + communityDispatcher + .gatekeep( + community_id, + GateKeepType::PermissionedGating, + NFT_ONE.try_into().unwrap(), + permission_addresses, + 0 + ); + + // check events are emitted + spy + .assert_emitted( + @array![ + ( + community_contract_address, + CommunityComponent::Event::CommunityGatekeeped( + CommunityComponent::CommunityGatekeeped { + community_id: community_id, + transaction_executor: USER_ONE.try_into().unwrap(), + gatekeepType: GateKeepType::PermissionedGating, + block_timestamp: get_block_timestamp() + } + ) + ) + ] + ); +}