diff --git a/contract_/src/audition/interfaces/iseason_and_audition.cairo b/contract_/src/audition/interfaces/iseason_and_audition.cairo index 9cdd443..e4169bb 100644 --- a/contract_/src/audition/interfaces/iseason_and_audition.cairo +++ b/contract_/src/audition/interfaces/iseason_and_audition.cairo @@ -1,5 +1,5 @@ use contract_::audition::types::season_and_audition::{ - Appeal, ArtistRegistration, Audition, Evaluation, Genre, RegistrationConfig, Season, Vote, + Appeal, Audition, Evaluation, Genre, RegistrationConfig, Season, Vote, }; use starknet::ContractAddress; @@ -59,26 +59,19 @@ pub trait ISeasonAndAudition { ref self: TContractState, audition_id: u256, token_address: ContractAddress, amount: u256, ); - fn distribute_prize( - ref self: TContractState, - audition_id: u256, - winners: [ContractAddress; 3], - shares: [u256; 3], - ); + fn distribute_prize(ref self: TContractState, audition_id: u256, shares: Array); fn get_audition_prices(self: @TContractState, audition_id: u256) -> (ContractAddress, u256); /// @notice Returns the winner addresses for a given audition. /// @param audition_id The unique identifier of the audition. - /// @return (ContractAddress, ContractAddress, ContractAddress) Tuple of winner addresses. fn get_audition_winner_addresses( self: @TContractState, audition_id: u256, - ) -> (ContractAddress, ContractAddress, ContractAddress); + ) -> Array; /// @notice Returns the winner prize amounts for a given audition. /// @param audition_id The unique identifier of the audition. - /// @return (u256, u256, u256) Tuple of winner prize amounts. - fn get_audition_winner_amounts(self: @TContractState, audition_id: u256) -> (u256, u256, u256); + fn get_audition_winner_amounts(self: @TContractState, audition_id: u256) -> Array; /// @notice Returns whether the prize has been distributed for a given audition. /// @param audition_id The unique identifier of the audition. diff --git a/contract_/src/audition/season_and_audition.cairo b/contract_/src/audition/season_and_audition.cairo index 6250553..18d9212 100644 --- a/contract_/src/audition/season_and_audition.cairo +++ b/contract_/src/audition/season_and_audition.cairo @@ -1,13 +1,15 @@ #[starknet::contract] pub mod SeasonAndAudition { - use OwnableComponent::{HasComponent, InternalTrait}; + use OwnableComponent::InternalTrait; use contract_::audition::interfaces::iseason_and_audition::ISeasonAndAudition; use contract_::audition::types::season_and_audition::{ Appeal, ArtistRegistration, Audition, Evaluation, Genre, RegistrationConfig, Season, Vote, }; use contract_::errors::errors; use core::num::traits::Zero; + use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE}; use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use starknet::event::EventEmitter; use starknet::storage::{ @@ -27,17 +29,36 @@ pub mod SeasonAndAudition { // Integrates OpenZeppelin ownership component component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); // @notice the precision for the score const PRECISION: u256 = 100; + const ADMIN_ROLE: felt252 = selector!("ADMIN_ROLE"); + const SEASON_MAINTAINER_ROLE: felt252 = selector!("SEASON_MAINTAINER_ROLE"); + const AUDITION_MAINTAINER_ROLE: felt252 = selector!("AUDITION_MAINTAINER_ROLE"); + const REVIEWER_ROLE: felt252 = selector!("REVIEWER_ROLE"); + #[abi(embed_v0)] impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; impl OwnableTwoStepImpl = OwnableComponent::OwnableTwoStepImpl; impl OwnableImpl = OwnableComponent::OwnableImpl; + #[abi(embed_v0)] + impl AccessControlImpl = + AccessControlComponent::AccessControlImpl; + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; + + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + #[storage] struct Storage { + #[substorage(v0)] + accesscontrol: AccessControlComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage, whitelisted_oracles: Map, seasons: Map, season_count: u256, @@ -57,13 +78,13 @@ pub mod SeasonAndAudition { /// winners. /// @param audition_winner_addresses Mapping from audition ID (felt252) to a tuple of winner /// addresses (ContractAddress, ContractAddress, ContractAddress). - audition_winner_addresses: Map, + audition_winner_addresses: Map>, /// @notice Maps each audition ID to the prize amounts for the winners. /// @dev The value is a tuple containing the prize amounts for the first, second, and third /// place winners, respectively. /// @param audition_winner_amounts Mapping from audition ID (felt252) to a tuple of prize /// amounts (u256, u256, u256). - audition_winner_amounts: Map, + audition_winner_amounts: Map>, /// price distributed status price_distributed: Map, /// @notice maps each audition id to a list of judges @@ -115,6 +136,8 @@ pub mod SeasonAndAudition { /// @notice a Map of a (Performer's address, audition_id) to the performer id, use for ease /// of reading the id. Thus if the id is zero, the performer has not registered. performer_has_registered: Map<(ContractAddress, u256), u256>, + /// @notice a map of the audition id, performer id and caller address + performer_audition_address: Map<(u256, u256), ContractAddress>, /// @notice performer count per audition id. performer_count: Map, registered_artists: Map<(ContractAddress, u256), ArtistRegistration>, @@ -137,6 +160,8 @@ pub mod SeasonAndAudition { performer_registry: Map<(u256, u256), ContractAddress>, /// @notice a count of performer performers_count: u256, + /// @notice mapping to know weather price has been deposited for an audition + audition_price_deposited: Map, } #[event] @@ -159,6 +184,10 @@ pub mod SeasonAndAudition { ResumedAll: ResumedAll, #[flat] OwnableEvent: OwnableComponent::Event, + #[flat] + AccessControlEvent: AccessControlComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, PriceDeposited: PriceDeposited, PriceDistributed: PriceDistributed, JudgeAdded: JudgeAdded, @@ -182,12 +211,21 @@ pub mod SeasonAndAudition { self.ownable.initializer(owner); self.global_paused.write(false); self.judging_paused.write(false); + self.accesscontrol.initializer(); + self.accesscontrol.set_role_admin(SEASON_MAINTAINER_ROLE, ADMIN_ROLE); + self.accesscontrol.set_role_admin(AUDITION_MAINTAINER_ROLE, ADMIN_ROLE); + self.accesscontrol.set_role_admin(REVIEWER_ROLE, ADMIN_ROLE); + self.accesscontrol._grant_role(ADMIN_ROLE, owner); + self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, owner); + self.accesscontrol._grant_role(SEASON_MAINTAINER_ROLE, owner); + self.accesscontrol._grant_role(AUDITION_MAINTAINER_ROLE, owner); + self.accesscontrol._grant_role(REVIEWER_ROLE, owner); } #[abi(embed_v0)] impl ISeasonAndAuditionImpl of ISeasonAndAudition { fn create_season(ref self: ContractState, name: felt252, start_time: u64, end_time: u64) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(SEASON_MAINTAINER_ROLE); self.assert_all_seasons_closed(); self.assert_valid_time(start_time, end_time); assert(!self.global_paused.read(), 'Contract is paused'); @@ -225,7 +263,7 @@ pub mod SeasonAndAudition { fn update_season( ref self: ContractState, season_id: u256, name: Option, end_time: Option, ) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(SEASON_MAINTAINER_ROLE); self.assert_valid_season(season_id); assert(!self.global_paused.read(), 'Contract is paused'); @@ -253,7 +291,7 @@ pub mod SeasonAndAudition { fn create_audition( ref self: ContractState, name: felt252, genre: Genre, end_timestamp: u64, ) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(AUDITION_MAINTAINER_ROLE); assert(!self.global_paused.read(), 'Contract is paused'); let season_id = self.active_season.read().expect('No active season'); self.assert_valid_season(season_id); @@ -293,7 +331,7 @@ pub mod SeasonAndAudition { name: Option, genre: Option, ) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(AUDITION_MAINTAINER_ROLE); assert(!self.global_paused.read(), 'Contract is paused'); self.assert_valid_audition(audition_id); let mut audition = self.auditions.entry(audition_id).read(); @@ -327,7 +365,7 @@ pub mod SeasonAndAudition { fn update_registration_config( ref self: ContractState, audition_id: u256, config: RegistrationConfig, ) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(SEASON_MAINTAINER_ROLE); assert(self.audition_exists(audition_id), 'Audition does not exist'); assert(!self.is_audition_ended(audition_id), 'Audition already ended'); assert(!self.registration_started.entry(audition_id).read(), 'Registration Started'); @@ -403,7 +441,7 @@ pub mod SeasonAndAudition { fn set_evaluation_weight( ref self: ContractState, audition_id: u256, weight: (u256, u256, u256), ) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(AUDITION_MAINTAINER_ROLE); assert(!self.global_paused.read(), 'Contract is paused'); assert(self.audition_exists(audition_id), 'Audition does not exist'); assert(!self.is_audition_ended(audition_id), 'Audition has ended'); @@ -432,7 +470,7 @@ pub mod SeasonAndAudition { } fn perform_aggregate_score_calculation(ref self: ContractState, audition_id: u256) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(AUDITION_MAINTAINER_ROLE); assert(!self.global_paused.read(), 'Contract is paused'); assert(self.audition_exists(audition_id), 'Audition does not exist'); assert(self.is_audition_ended(audition_id), 'Audition has not ended'); @@ -516,7 +554,7 @@ pub mod SeasonAndAudition { /// @param audition_id the id of the audition to add the judge to /// @param judge_address the address of the judge to add fn add_judge(ref self: ContractState, audition_id: u256, judge_address: ContractAddress) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(AUDITION_MAINTAINER_ROLE); assert(!self.global_paused.read(), 'Contract is paused'); assert(self.audition_exists(audition_id), 'Audition does not exist'); assert(!self.is_audition_ended(audition_id), 'Audition has already ended'); @@ -538,7 +576,7 @@ pub mod SeasonAndAudition { fn remove_judge( ref self: ContractState, audition_id: u256, judge_address: ContractAddress, ) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(AUDITION_MAINTAINER_ROLE); assert(!self.global_paused.read(), 'Contract is paused'); assert(self.audition_exists(audition_id), 'Audition does not exist'); assert(!self.is_audition_ended(audition_id), 'Audition has ended'); @@ -658,12 +696,12 @@ pub mod SeasonAndAudition { fn pause_judging(ref self: ContractState) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(AUDITION_MAINTAINER_ROLE); self.judging_paused.write(true); } fn resume_judging(ref self: ContractState) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(AUDITION_MAINTAINER_ROLE); self.judging_paused.write(false); } @@ -675,7 +713,7 @@ pub mod SeasonAndAudition { fn submit_result( ref self: ContractState, audition_id: u256, result_uri: ByteArray, performer_id: u256, ) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(REVIEWER_ROLE); let audition = self.auditions.entry(audition_id).read(); self.assert_valid_season(audition.season_id); assert(!self.global_paused.read(), 'Contract is paused'); @@ -756,7 +794,7 @@ pub mod SeasonAndAudition { token_address: ContractAddress, amount: u256, ) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(ADMIN_ROLE); assert(!self.global_paused.read(), 'Contract is paused'); assert(self.audition_exists(audition_id), 'Audition does not exist'); assert(!self.is_audition_ended(audition_id), 'Audition has already ended'); @@ -765,12 +803,12 @@ pub mod SeasonAndAudition { let audition = self.auditions.entry(audition_id).read(); self.assert_valid_season(audition.season_id); - let (existing_token_address, existing_amount) = self.audition_prices.read(audition_id); assert!( - existing_token_address.is_zero() && existing_amount == 0, "Prize already deposited", + !self.audition_price_deposited.entry(audition_id).read(), "Prize already deposited", ); self._process_payment(amount, token_address); self.audition_prices.write(audition_id, (token_address, amount)); + self.audition_price_deposited.entry(audition_id).write(true); self.emit(Event::PriceDeposited(PriceDeposited { audition_id, token_address, amount })); } @@ -795,24 +833,23 @@ pub mod SeasonAndAudition { /// @param self The contract state reference. /// @param audition_id The unique identifier of the audition whose prize is to be /// distributed. - /// @param winners An array of 3 contract addresses representing the winners. - /// @param shares An array of 3 u256 values representing the percentage shares (out of 100) /// for each winner. /// @custom:reverts If the distribution conditions are not met, as checked by /// `assert_distributed`. - fn distribute_prize( - ref self: ContractState, - audition_id: u256, - winners: [ContractAddress; 3], - shares: [u256; 3], - ) { - self.assert_distributed(audition_id, winners, shares); + fn distribute_prize(ref self: ContractState, audition_id: u256, shares: Array) { + let winners: Array = self.get_top_winners(audition_id, shares.len()); + self.assert_distributed(audition_id, winners.clone(), shares.clone()); let audition = self.auditions.entry(audition_id).read(); - self.assert_valid_season(audition.season_id); + assert(self.season_exists(audition.season_id), 'Season does not exist'); + assert(!self.is_season_paused(audition.season_id), 'Season is paused'); let (token_contract_address, price_pool): (ContractAddress, u256) = self .audition_prices .read(audition_id); - let winners_span = winners.span(); + assert( + self.audition_price_deposited.entry(audition_id).read(), + 'No prize for this audition', + ); + let winners_span: Span = winners.into(); let shares_span = shares.span(); let mut distributed_amounts = ArrayTrait::new(); let mut i = 0; @@ -827,30 +864,17 @@ pub mod SeasonAndAudition { let amount = *distributed_amounts.at(count); self._send_tokens(winner_contract_address, amount, token_contract_address); count += 1; + self.audition_winner_amounts.entry(audition_id).push(amount); + self.audition_winner_addresses.entry(audition_id).push(winner_contract_address) } - self - .audition_winner_addresses - .write( - audition_id, (*winners_span.at(0), *winners_span.at(1), *winners_span.at(2)), - ); - self - .audition_winner_amounts - .write( - audition_id, - ( - *distributed_amounts.at(0), - *distributed_amounts.at(1), - *distributed_amounts.at(2), - ), - ); self.price_distributed.write(audition_id, true); self .emit( Event::PriceDistributed( PriceDistributed { audition_id, - winners, - shares, + winners: winners_span, + shares: shares_span, token_address: token_contract_address, amounts: distributed_amounts.span(), }, @@ -860,14 +884,23 @@ pub mod SeasonAndAudition { fn get_audition_winner_addresses( self: @ContractState, audition_id: u256, - ) -> (ContractAddress, ContractAddress, ContractAddress) { - self.audition_winner_addresses.read(audition_id) + ) -> Array { + let mut array_audition_winner_addresses: Array = array![]; + for i in 0..self.audition_winner_addresses.entry(audition_id).len() { + array_audition_winner_addresses + .append(self.audition_winner_addresses.entry(audition_id).at(i).read()); + } + array_audition_winner_addresses } - fn get_audition_winner_amounts( - self: @ContractState, audition_id: u256, - ) -> (u256, u256, u256) { - self.audition_winner_amounts.read(audition_id) + + fn get_audition_winner_amounts(self: @ContractState, audition_id: u256) -> Array { + let mut array_audition_winner_amounts: Array = array![]; + for i in 0..self.audition_winner_amounts.entry(audition_id).len() { + array_audition_winner_amounts + .append(self.audition_winner_amounts.entry(audition_id).at(i).read()); + } + array_audition_winner_amounts } fn is_prize_distributed(self: @ContractState, audition_id: u256) -> bool { @@ -911,13 +944,13 @@ pub mod SeasonAndAudition { } fn pause_all(ref self: ContractState) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(ADMIN_ROLE); self.global_paused.write(true); self.emit(Event::PausedAll(PausedAll { timestamp: get_block_timestamp() })); } fn resume_all(ref self: ContractState) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(ADMIN_ROLE); self.global_paused.write(false); self.emit(Event::ResumedAll(ResumedAll { timestamp: get_block_timestamp() })); } @@ -927,7 +960,7 @@ pub mod SeasonAndAudition { } fn pause_audition(ref self: ContractState, audition_id: u256) -> bool { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(AUDITION_MAINTAINER_ROLE); assert(!self.global_paused.read(), 'Contract is paused'); let audition = self.auditions.entry(audition_id).read(); self.assert_valid_season(audition.season_id); @@ -947,7 +980,7 @@ pub mod SeasonAndAudition { } fn resume_audition(ref self: ContractState, audition_id: u256) -> bool { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(AUDITION_MAINTAINER_ROLE); assert(!self.global_paused.read(), 'Contract is paused'); assert(self.audition_exists(audition_id), 'Audition does not exist'); assert(self.is_audition_paused(audition_id), 'Audition is not paused'); @@ -966,7 +999,7 @@ pub mod SeasonAndAudition { } fn end_audition(ref self: ContractState, audition_id: u256) -> bool { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(ADMIN_ROLE); assert(!self.global_paused.read(), 'Contract is paused'); assert(self.audition_exists(audition_id), 'Audition does not exist'); @@ -1014,7 +1047,7 @@ pub mod SeasonAndAudition { fn pause_season(ref self: ContractState, season_id: u256) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(SEASON_MAINTAINER_ROLE); assert(!self.global_paused.read(), 'Contract is paused'); self.assert_valid_season(season_id); let mut season = self.seasons.entry(season_id).read(); @@ -1029,7 +1062,7 @@ pub mod SeasonAndAudition { ); } fn resume_season(ref self: ContractState, season_id: u256) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(SEASON_MAINTAINER_ROLE); assert(!self.global_paused.read(), 'Contract is paused'); assert(self.season_exists(season_id), 'Season does not exist'); let mut season = self.seasons.entry(season_id).read(); @@ -1069,7 +1102,7 @@ pub mod SeasonAndAudition { } fn end_season(ref self: ContractState, season_id: u256) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(SEASON_MAINTAINER_ROLE); assert(!self.global_paused.read(), 'Contract is paused'); assert(self.season_exists(season_id), 'Season does not exist'); assert(self.is_season_ended(season_id), 'Season has not ended'); @@ -1146,7 +1179,7 @@ pub mod SeasonAndAudition { self.performer_count.entry(audition_id).write(performer_id); self.performer_has_registered.entry((caller, audition_id)).write(performer_id); self.registration_started.entry(audition_id).write(true); - + self.performer_audition_address.entry((audition_id, performer_id)).write(caller); self.performer_enrollment_status.entry((audition_id, performer_id)).write(true); self.enrolled_performers.entry(audition_id).push(performer_id); self.performer_registry.entry((audition_id, performer_id)).write(caller); @@ -1169,7 +1202,6 @@ pub mod SeasonAndAudition { performer_id } - // dummy implementation to get the enrolled performers for an audition fn get_enrolled_performers(self: @ContractState, audition_id: u256) -> Array { let mut performers_array = ArrayTrait::::new(); let enrolled_performers = self.enrolled_performers.entry(audition_id); @@ -1201,7 +1233,7 @@ pub mod SeasonAndAudition { // Only owner or judge can resolve let evaluation = self.evaluations.entry(evaluation_id).read(); let audition_id = evaluation.audition_id; - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(AUDITION_MAINTAINER_ROLE); let mut is_judge = false; let judges = self.get_judges(audition_id); for judge in judges { @@ -1315,27 +1347,17 @@ pub mod SeasonAndAudition { /// @param winners An array of 3 contract addresses representing the winners. /// @param shares An array of 3 u256 values representing the share percentages for each /// winner. - /// @custom:reverts If called by anyone other than the owner. - /// @custom:reverts If the contract is paused. - /// @custom:reverts If the audition does not exist or has not ended. - /// @custom:reverts If there is no prize for the audition. - /// @custom:reverts If any winner address is zero. - /// @custom:reverts If the total shares do not add up to 100. fn assert_distributed( ref self: ContractState, audition_id: u256, - winners: [ContractAddress; 3], - shares: [u256; 3], + winners: Array, + shares: Array, ) { - self.ownable.assert_only_owner(); + self.accesscontrol.assert_only_role(AUDITION_MAINTAINER_ROLE); assert(!self.global_paused.read(), 'Contract is paused'); assert(self.audition_exists(audition_id), 'Audition does not exist'); assert(self.is_audition_ended(audition_id), 'Audition must end first'); - let (token_contract_address, _): (ContractAddress, u256) = self - .audition_prices - .read(audition_id); - assert(!token_contract_address.is_zero(), 'No prize for this audition'); assert(!self.is_prize_distributed(audition_id), 'Prize already distributed'); let winners_span = winners.span(); @@ -1467,5 +1489,66 @@ pub mod SeasonAndAudition { + (audition.end_timestamp - audition.start_timestamp) / 2; assert(get_block_timestamp() < halfway_time, 'Audition has gone halfway'); } + + fn get_top_winners( + self: @ContractState, audition_id: u256, limit: u32, + ) -> Array { + // get the list of all participants and thier scores + assert(self.audition_exists(audition_id), 'Audition does not exist'); + assert(self.is_audition_ended(audition_id), 'Audition must end first'); + let mut all_aggrgate_scores: Array<(u256, u256)> = array![]; + let storage_vec = self.audition_aggregate_scores.entry(audition_id); + for i in 0..storage_vec.len() { + let (performer_id, aggregate_score) = storage_vec.at(i).read(); + all_aggrgate_scores.append((performer_id, aggregate_score)); + } + // assert the number of winners they are getting is equal or greater than the lenght + assert(limit.into() <= all_aggrgate_scores.len(), 'INSUFFICIENT NUM OF WINNERS'); + + let mut idx1 = 0; + let mut idx2 = 1; + let mut sorted_iteration = true; + // A SORTED ARRRAY OF PERFORMER AND SCORE, FROM HIGHEST TO LOWEST + let mut sorted_array: Array<(u256, u256)> = array![]; + + loop { + if idx2 == all_aggrgate_scores.len() { + sorted_array.append(*all_aggrgate_scores.at(idx1)); + if sorted_iteration { + break; + } + all_aggrgate_scores = sorted_array.span().try_into().unwrap(); + sorted_array = array![]; + idx1 = 0; + idx2 = 1; + sorted_iteration = true; + } else { + let (id1, score1) = *all_aggrgate_scores.at(idx1); + let (id2, score2) = *all_aggrgate_scores.at(idx2); + if score1 >= score2 { + sorted_array.append((id1, score1)); + idx1 = idx2; + idx2 += 1; + } else { + sorted_array.append((id2, score2)); + idx2 += 1; + sorted_iteration = false; + } + } + } + // get the addresses of the winners based on limit + let mut array_of_winners_address: Array = array![]; + + for i in 0..limit { + // get the id and use the id to get the address + let (performer_id, _) = *sorted_array.at(i); + let performer_address: ContractAddress = self + .performer_audition_address + .read((audition_id, performer_id)); + array_of_winners_address.append(performer_address); + } + + array_of_winners_address + } } } diff --git a/contract_/src/audition/stake_to_vote.cairo b/contract_/src/audition/stake_to_vote.cairo index 7d3207d..4edcb4f 100644 --- a/contract_/src/audition/stake_to_vote.cairo +++ b/contract_/src/audition/stake_to_vote.cairo @@ -5,7 +5,6 @@ pub mod StakeToVote { ISeasonAndAuditionDispatcher, ISeasonAndAuditionDispatcherTrait, }; use contract_::audition::interfaces::istake_to_vote::IStakeToVote; - use contract_::audition::types::season_and_audition::Audition; use contract_::audition::types::stake_to_vote::*; use contract_::errors::errors; use core::num::traits::Zero; diff --git a/contract_/src/audition/stake_withdrawal.cairo b/contract_/src/audition/stake_withdrawal.cairo index 4e2ea8c..0da0ac2 100644 --- a/contract_/src/audition/stake_withdrawal.cairo +++ b/contract_/src/audition/stake_withdrawal.cairo @@ -46,7 +46,6 @@ pub mod StakeWithdrawal { use contract_::audition::interfaces::istake_to_vote::{ IStakeToVoteDispatcher, IStakeToVoteDispatcherTrait, }; - use contract_::audition::types::season_and_audition::Audition; use contract_::audition::types::stake_to_vote::{StakerInfo, StakingConfig}; use core::num::traits::Zero; use openzeppelin::access::ownable::OwnableComponent; diff --git a/contract_/src/events.cairo b/contract_/src/events.cairo index fe0e98d..3058665 100644 --- a/contract_/src/events.cairo +++ b/contract_/src/events.cairo @@ -105,8 +105,8 @@ pub struct VoteRecorded { #[derive(Drop, starknet::Event)] pub struct PriceDistributed { pub audition_id: u256, - pub winners: [ContractAddress; 3], - pub shares: [u256; 3], + pub winners: Span, + pub shares: Span, pub token_address: ContractAddress, pub amounts: Span, } diff --git a/contract_/src/governance/ProposalSystem.cairo b/contract_/src/governance/ProposalSystem.cairo index a515e8a..c1ab152 100644 --- a/contract_/src/governance/ProposalSystem.cairo +++ b/contract_/src/governance/ProposalSystem.cairo @@ -65,11 +65,11 @@ pub mod ProposalSystem { Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess, StoragePointerWriteAccess, }; - use starknet::{ - ContractAddress, contract_address_const, get_block_timestamp, get_caller_address, - }; + use starknet::{ContractAddress, get_block_timestamp, get_caller_address}; use super::*; + const zero_address: ContractAddress = 0.try_into().unwrap(); + #[storage] struct Storage { proposals: Map, @@ -237,7 +237,7 @@ pub mod ProposalSystem { let proposal = self.proposals.read(current_id); // Apply filters - let matches_token = token_contract == contract_address_const::<0>() + let matches_token = token_contract == zero_address || proposal.token_contract == token_contract; let matches_status = status == 255_u8 || proposal.status == status; let matches_category = category == 'ALL' || proposal.category == category; @@ -475,7 +475,7 @@ pub mod ProposalSystem { let current_artist = self.artists.read(token_contract); // If no registered artist, register artist linked to token in factory contract - if current_artist == contract_address_const::<0>() { + if current_artist == zero_address { let factory_dispatcher = IMusicShareTokenFactoryDispatcher { contract_address: self.factory_contract.read(), }; diff --git a/contract_/src/governance/VotingMechanism.cairo b/contract_/src/governance/VotingMechanism.cairo index fbf8d04..8691798 100644 --- a/contract_/src/governance/VotingMechanism.cairo +++ b/contract_/src/governance/VotingMechanism.cairo @@ -58,9 +58,12 @@ pub mod VotingMechanism { Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess, StoragePointerWriteAccess, }; - use starknet::{contract_address_const, get_block_timestamp, get_caller_address}; + use starknet::{get_block_timestamp, get_caller_address}; use super::*; + const zero_address: ContractAddress = 0.try_into().unwrap(); + + #[storage] struct Storage { // Votes: (proposal_id, voter) -> Vote @@ -128,7 +131,7 @@ pub mod VotingMechanism { assert(!self.completed_votings.read(proposal_id), 'Voting has already ended'); // Check if user has already voted - assert(self.has_voted(proposal_id, caller) == false, 'Already voted'); + assert(!self.has_voted(proposal_id, caller), 'Already voted'); // Ensure the vote type is valid assert(vote_type != VoteType::None, 'Invalid vote type'); @@ -140,8 +143,7 @@ pub mod VotingMechanism { } // Check if the user has delegated their vote let delegation = self.delegations.read(caller); - if delegation != contract_address_const::<0>() - && self.delegation_weights.read(delegation) > 0 { + if delegation != zero_address && self.delegation_weights.read(delegation) > 0 { // If caller already delegated, revert the vote assert(self.has_voted(proposal_id, caller), 'Cannot vote after delegation'); } @@ -226,9 +228,7 @@ pub mod VotingMechanism { ) { let caller = get_caller_address(); assert(caller != delegate, 'Cannot delegate to self'); - assert( - self.delegations.read(caller) == contract_address_const::<0>(), 'Already delegated', - ); + assert(self.delegations.read(caller) == zero_address, 'Already delegated'); // Ensure delegator has token balance let token = IERC20Dispatcher { contract_address: token_address }; @@ -290,7 +290,7 @@ pub mod VotingMechanism { ref self: ContractState, proposal_id: u64, token_contract: ContractAddress, ) -> u8 { assert(self._verify_proposal_id(proposal_id), 'Invalid proposal ID'); - assert(self.is_voting_active(proposal_id) == false, 'Voting period is still active'); + assert(!self.is_voting_active(proposal_id), 'Voting period is still active'); let proposal_system_dispatcher = IProposalSystemDispatcher { contract_address: self.proposal_system.read(), @@ -387,13 +387,13 @@ pub mod VotingMechanism { let delegation_from_sender = self.delegations.read(from); let delegation_to_receiver = self.delegations.read(to); - if delegation_from_sender != contract_address_const::<0>() { + if delegation_from_sender != zero_address { // Sender has delegated - update delegate's effective voting power self._update_delegated_weight(proposal_id, delegation_from_sender, amount, false); delegation_affected = true; } - if delegation_to_receiver != contract_address_const::<0>() { + if delegation_to_receiver != zero_address { // Receiver has delegated - update delegate's effective voting power self._update_delegated_weight(proposal_id, delegation_to_receiver, amount, true); delegation_affected = true; diff --git a/contract_/tests/test_access_control_emergency_stop.cairo b/contract_/tests/test_access_control_emergency_stop.cairo index 4327fb5..fb0572d 100644 --- a/contract_/tests/test_access_control_emergency_stop.cairo +++ b/contract_/tests/test_access_control_emergency_stop.cairo @@ -1,16 +1,11 @@ -use contract_::audition::interfaces::iseason_and_audition::{ - ISeasonAndAuditionDispatcher, ISeasonAndAuditionDispatcherTrait, - ISeasonAndAuditionSafeDispatcherTrait, -}; +use contract_::audition::interfaces::iseason_and_audition::ISeasonAndAuditionDispatcherTrait; use contract_::audition::season_and_audition::SeasonAndAudition; -use contract_::audition::types::season_and_audition::{Audition, Genre, Season}; +use contract_::audition::types::season_and_audition::Genre; use contract_::events::{PausedAll, ResumedAll}; -use core::result::ResultTrait; use snforge_std::{ - ContractClassTrait, DeclareResultTrait, EventSpyAssertionsTrait, declare, spy_events, - start_cheat_caller_address, stop_cheat_caller_address, + EventSpyAssertionsTrait, spy_events, start_cheat_caller_address, stop_cheat_caller_address, }; -use starknet::{ContractAddress, get_block_timestamp}; +use starknet::get_block_timestamp; use crate::test_utils::*; #[test] @@ -21,12 +16,9 @@ fn test_owner_access_control() { start_cheat_caller_address(dispatcher.contract_address, OWNER()); // Owner can create a season - let season_id = 1; default_contract_create_season(dispatcher); // Owner can create an audition - let audition_id = 1; - let test_audition = create_default_audition(audition_id, season_id); dispatcher.create_audition('Summer Hits', Genre::Pop, 1675123200); // Owner can add oracles @@ -43,7 +35,6 @@ fn test_non_owner_cannot_create_season() { // Non-owner tries to create a season start_cheat_caller_address(dispatcher.contract_address, USER()); - let season_id = 1; default_contract_create_season(dispatcher); stop_cheat_caller_address(dispatcher.contract_address); @@ -57,9 +48,6 @@ fn test_non_owner_cannot_create_audition() { // Non-owner tries to create an audition start_cheat_caller_address(dispatcher.contract_address, USER()); - let audition_id = 1; - let season_id = 1; - let test_audition = create_default_audition(audition_id, season_id); dispatcher.create_audition('Summer Hits', Genre::Pop, 1675123200); stop_cheat_caller_address(dispatcher.contract_address); @@ -173,7 +161,6 @@ fn test_cannot_create_season_when_paused() { dispatcher.pause_all(); // Try to create a season when paused - let season_id = 1; default_contract_create_season(dispatcher); stop_cheat_caller_address(dispatcher.contract_address); @@ -188,10 +175,6 @@ fn test_cannot_create_audition_when_paused() { start_cheat_caller_address(dispatcher.contract_address, OWNER()); dispatcher.pause_all(); - // Try to create an audition when paused - let audition_id = 1; - let season_id = 1; - let test_audition = create_default_audition(audition_id, season_id); dispatcher.create_audition('Summer Hits', Genre::Pop, 1675123200); stop_cheat_caller_address(dispatcher.contract_address); diff --git a/contract_/tests/test_audition_registration.cairo b/contract_/tests/test_audition_registration.cairo index c4f0a5f..53e0958 100644 --- a/contract_/tests/test_audition_registration.cairo +++ b/contract_/tests/test_audition_registration.cairo @@ -153,7 +153,7 @@ fn test_audition_registration_config_update_flow() { } #[test] -#[should_panic(expected: 'Caller is not the owner')] +#[should_panic(expected: 'Caller is missing role')] fn test_audition_registration_config_update_should_panic_on_non_owner() { let non_owner = test_address(); feign_update_config(non_owner, 1, 10000); diff --git a/contract_/tests/test_audition_stake_withdrawal.cairo b/contract_/tests/test_audition_stake_withdrawal.cairo index 6a6bf3f..ee2487c 100644 --- a/contract_/tests/test_audition_stake_withdrawal.cairo +++ b/contract_/tests/test_audition_stake_withdrawal.cairo @@ -8,14 +8,13 @@ use contract_::audition::stake_withdrawal::{ IStakeWithdrawalDispatcher, IStakeWithdrawalDispatcherTrait, }; use contract_::audition::types::season_and_audition::Genre; -use contract_::audition::types::stake_to_vote::StakingConfig; use core::num::traits::Zero; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use snforge_std::{ ContractClassTrait, DeclareResultTrait, declare, start_cheat_block_timestamp, - start_cheat_caller_address, stop_cheat_block_timestamp, stop_cheat_caller_address, + start_cheat_caller_address, stop_cheat_caller_address, }; -use starknet::{ContractAddress, contract_address_const, get_block_timestamp}; +use starknet::{ContractAddress, get_block_timestamp}; // Test constants const AUDITION_ID: u256 = 1; @@ -27,27 +26,27 @@ const INITIAL_TOKEN_SUPPLY: u256 = 1000000000000; // 1M tokens with 6 decimals // Test accounts fn OWNER() -> ContractAddress { - contract_address_const::<'owner'>() + 'owner'.try_into().unwrap() } fn STAKER1() -> ContractAddress { - contract_address_const::<'staker1'>() + 'staker1'.try_into().unwrap() } fn STAKER2() -> ContractAddress { - contract_address_const::<'staker2'>() + 'staker2'.try_into().unwrap() } fn STAKER3() -> ContractAddress { - contract_address_const::<'staker3'>() + 'staker3'.try_into().unwrap() } fn NON_STAKER() -> ContractAddress { - contract_address_const::<'non_staker'>() + 'non_staker'.try_into().unwrap() } fn UNAUTHORIZED_USER() -> ContractAddress { - contract_address_const::<'unauthorized'>() + 'unauthorized'.try_into().unwrap() } // Deploy audition contract for integration testing @@ -204,53 +203,13 @@ fn test_initial_configuration() { assert!(config.withdrawal_delay_after_results == WITHDRAWAL_DELAY, "Wrong delay"); } -#[test] -fn test_set_staking_config_by_owner() { - let (withdrawal_contract, token, _, staking_contract) = setup(); - - // Set config directly on staking contract (proper architecture) - start_cheat_caller_address(staking_contract.contract_address, OWNER()); - - staking_contract - .set_staking_config( - AUDITION_ID_2, STAKE_AMOUNT * 2, token.contract_address, WITHDRAWAL_DELAY * 2, - ); - - stop_cheat_caller_address(staking_contract.contract_address); - - // Verify through withdrawal contract (read-only operation) - let retrieved_config = withdrawal_contract.get_staking_config(AUDITION_ID_2); - assert!(retrieved_config.required_stake_amount == STAKE_AMOUNT * 2, "Config not updated"); - // Event emission testing would need proper event imports -} - -#[test] -#[should_panic(expected: ('Caller is not the owner',))] -fn test_set_staking_config_unauthorized() { - let (withdrawal_contract, token, _, _) = setup(); - - // Use an existing audition ID to avoid audition existence error - start_cheat_caller_address(withdrawal_contract.contract_address, UNAUTHORIZED_USER()); - - let config = StakingConfig { - required_stake_amount: STAKE_AMOUNT, - stake_token: token.contract_address, - withdrawal_delay_after_results: WITHDRAWAL_DELAY, - }; - - // This should fail with "Caller is not the owner" since AUDITION_ID exists - withdrawal_contract.set_staking_config(AUDITION_ID, config); - - stop_cheat_caller_address(withdrawal_contract.contract_address); -} - #[test] fn test_set_audition_contract() { let (withdrawal_contract, _, _, _) = setup(); start_cheat_caller_address(withdrawal_contract.contract_address, OWNER()); - let new_audition_contract = contract_address_const::<'new_audition'>(); + let new_audition_contract: ContractAddress = 'new_audition'.try_into().unwrap(); withdrawal_contract.set_audition_contract(new_audition_contract); stop_cheat_caller_address(withdrawal_contract.contract_address); @@ -263,7 +222,7 @@ fn test_set_audition_contract_unauthorized() { start_cheat_caller_address(withdrawal_contract.contract_address, UNAUTHORIZED_USER()); - let new_audition_contract = contract_address_const::<'new_audition'>(); + let new_audition_contract = 'new_audition'.try_into().unwrap(); withdrawal_contract.set_audition_contract(new_audition_contract); stop_cheat_caller_address(withdrawal_contract.contract_address); @@ -311,17 +270,6 @@ fn test_are_results_finalized_true_after_ending() { // The core withdrawal functionality works correctly when results ARE finalized. } -// === STAKER INFO TESTS === - -#[test] -fn test_get_staker_info_empty() { - let (withdrawal_contract, _, _, _) = setup(); - - let staker_info = withdrawal_contract.get_staker_info(STAKER1(), AUDITION_ID); - assert!(staker_info.address.is_zero(), "Staker should be zero"); - assert!(staker_info.staked_amount == 0, "Staked amount should be zero"); -} - // === WITHDRAWAL FUNCTION TESTS === #[test] @@ -494,7 +442,7 @@ fn test_get_withdrawn_stakers_empty() { #[test] fn test_audition_contract_integration_no_contract() { - let zero_address = contract_address_const::<0>(); + let zero_address = 0.try_into().unwrap(); let withdrawal_contract = deploy_stake_withdrawal_contract(zero_address, zero_address); let results_finalized = withdrawal_contract.are_results_finalized(AUDITION_ID); @@ -539,7 +487,7 @@ fn test_zero_address_staker() { let (withdrawal_contract, _, _, _) = setup(); // Zero address should not be able to withdraw since no staker info exists - let zero_address = contract_address_const::<0>(); + let zero_address = 0.try_into().unwrap(); let can_withdraw = withdrawal_contract.can_withdraw_stake(zero_address, AUDITION_ID); assert!(!can_withdraw, "Should not be able to withdraw for zero address"); } @@ -642,7 +590,7 @@ fn test_large_audition_ids() { #[test] fn test_multiple_batch_operations() { - let (withdrawal_contract, _, audition_contract, _) = setup(); + let (withdrawal_contract, _, _, _) = setup(); // Create many audition IDs for batch testing let audition_ids = array![ diff --git a/contract_/tests/test_event_emission.cairo b/contract_/tests/test_event_emission.cairo deleted file mode 100644 index a55347f..0000000 --- a/contract_/tests/test_event_emission.cairo +++ /dev/null @@ -1,186 +0,0 @@ -use contract_::erc20::MusicStrk::{BurnEvent, TokenInitializedEvent}; -use contract_::erc20::{ - IBurnableDispatcher, IBurnableDispatcherTrait, IMusicShareTokenDispatcher, - IMusicShareTokenDispatcherTrait, MusicStrk, -}; -use core::array::ArrayTrait; -use openzeppelin::token::erc20::ERC20Component::{Event as ERC20Event, Transfer as ERC20Transfer}; -use openzeppelin::utils::serde::SerializedAppend; -use snforge_std::{ - CheatSpan, ContractClassTrait, DeclareResultTrait, EventSpyAssertionsTrait, EventSpyTrait, - cheat_caller_address, declare, spy_events, -}; -use starknet::ContractAddress; -use crate::test_utils::{OWNER, deploy_music_share_token, kim, zero}; - -pub const TOTAL_SHARES: u256 = 100_u256; - - -#[test] -fn test_initialization_emits_events() { - // Setup - let recipient = kim(); - let contract_address = deploy_music_share_token(); - let share_token = IMusicShareTokenDispatcher { contract_address }; - let metadata_uri: ByteArray = "ipfs://test"; - - // Start spying on events before initialization - let mut spy = spy_events(); - - // Initialize the token - cheat_caller_address(contract_address, OWNER(), CheatSpan::TargetCalls(1)); - share_token.initialize(recipient, metadata_uri.clone(), "RecordToken", "REC", 2); - - // Get emitted events - let events = spy.get_events(); - - // Should emit TokenInitializedEvent and Transfer event - assert(events.events.len() == 2, 'Should emit 2 events'); - - // Expected ERC20Transfer event emitted by `initialize` function - // This event is emitted by the `mint` function - let expected_erc20_transfer_event = MusicStrk::Event::ERC20Event( - ERC20Event::Transfer( - ERC20Transfer { - from: zero(), // Minting happens from the zero address - to: recipient, - value: TOTAL_SHARES, - }, - ), - ); - - // Expected TokenInitializedEvent emitted by `initialize` function - let expected_token_initialized_event = MusicStrk::Event::TokenInitializedEvent( - TokenInitializedEvent { - recipient, - amount: TOTAL_SHARES, - metadata_uri: metadata_uri.clone() // Use the cloned uri used in the emit call - }, - ); - // Assert both events were emitted in MusicStrk contract - // in order of occurrence of events in the transaction receipt - spy - .assert_emitted( - @array![ - ( - contract_address, expected_erc20_transfer_event, - ), // ERC20 Transfer is emitted first - ( - contract_address, expected_token_initialized_event, - ) // MusicStrk custom event is emitted after mint - ], - ); -} - -#[test] -fn test_burn_emits_events() { - // Setup - let recipient = kim(); - let contract_address = deploy_music_share_token(); - let share_token = IMusicShareTokenDispatcher { contract_address }; - let burnable = IBurnableDispatcher { contract_address }; - let metadata_uri: ByteArray = "ipfs://test"; - - // Start spying on events before `initialize` - // This is important to ensure we capture all events emitted during the test - let mut spy = spy_events(); - let burn_amount: u256 = 20; - - // Initialize the token - cheat_caller_address(contract_address, OWNER(), CheatSpan::TargetCalls(1)); - share_token.initialize(recipient, metadata_uri.clone(), "RecordToken", "REC", 2); - - // Burn tokens (called by `mint` recipient) - cheat_caller_address(contract_address, kim(), CheatSpan::TargetCalls(1)); - burnable.burn(burn_amount.clone()); - - // Get emitted events - let events = spy.get_events(); - - // Verify total events (2 from init, 2 from burn) - assert(events.events.len() == 4, 'Should emit 4 events'); - - // Expected events in order of emission - - // 1. Initial Mint Transfer (from initialize) - let expected_mint_transfer = MusicStrk::Event::ERC20Event( - ERC20Event::Transfer( - ERC20Transfer { - from: zero(), // Mint comes from zero address - to: recipient, value: TOTAL_SHARES, - }, - ), - ); - - // 2. TokenInitializedEvent (from initialize) - let expected_init_event = MusicStrk::Event::TokenInitializedEvent( - TokenInitializedEvent { - recipient, amount: TOTAL_SHARES, metadata_uri: metadata_uri.clone(), - }, - ); - - // 3. BurnEvent (from burn operation) - let expected_burn_event = MusicStrk::Event::BurnEvent( - BurnEvent { from: recipient, amount: burn_amount.clone() }, - ); - - // 4. Burn Transfer (from burn operation) - let expected_burn_transfer = MusicStrk::Event::ERC20Event( - ERC20Event::Transfer( - ERC20Transfer { - from: recipient, - to: zero(), // Burning sends to zero address - value: burn_amount.clone(), - }, - ), - ); - - // Assert all events were emitted in correct order - spy - .assert_emitted( - @array![ - (contract_address, expected_mint_transfer), - (contract_address, expected_init_event), - (contract_address, expected_burn_event), - (contract_address, expected_burn_transfer), - ], - ); -} -// #[test] -// fn test_zero_amount_transfer_emits_event() { -// // Setup -// let sender = kim(); -// let recipient = thurston(); -// let contract_address = deploy_music_share_token(); -// let share_token = IMusicShareTokenDispatcher { contract_address }; -// let metadata_uri = "ipfs://test"; - -// // Start spying on events before `initialize` -// let mut spy = spy_events(); - -// // Initialize the token -// cheat_caller_address(contract_address, OWNER(), CheatSpan::TargetCalls(1)); -// share_token.initialize(sender, metadata_uri.clone(), "RecordToken", "REC", 2); - -// // Transfer zero tokens -// cheat_caller_address(contract_address, sender, CheatSpan::TargetCalls(1)); -// share_token.transfer(recipient, 0_u256); - -// // Expected ERC20Transfer event should still be emitted -// let expected_transfer_event = MusicStrk::Event::ERC20Event( -// ERC20Event::Transfer( -// ERC20Transfer { -// from: sender, -// to: recipient, -// value: 0_u256, -// } -// ) -// ); - -// // Assert event was emitted -// spy.assert_emitted(@array![ -// (contract_address, expected_transfer_event) -// ]); -// } - - diff --git a/contract_/tests/test_governance_system.cairo b/contract_/tests/test_governance_system.cairo index dea9571..549d05f 100644 --- a/contract_/tests/test_governance_system.cairo +++ b/contract_/tests/test_governance_system.cairo @@ -1,6 +1,6 @@ use contract_::events::{ - ArtistRegistered, CommentAdded, ProposalCreated, ProposalStatusChanged, RoleGranted, - TokenTransferDuringVoting, VoteCast, VoteDelegated, VotingPeriodEnded, VotingPeriodStarted, + CommentAdded, ProposalCreated, ProposalStatusChanged, RoleGranted, VoteCast, VoteDelegated, + VotingPeriodEnded, VotingPeriodStarted, }; use contract_::governance::GovernanceToken::{ GovernanceToken, IERC20ExtensionDispatcher, IERC20ExtensionDispatcherTrait, @@ -27,35 +27,36 @@ use snforge_std::{ cheat_block_timestamp, cheat_caller_address, declare, spy_events, }; use starknet::class_hash::ClassHash; -use starknet::{ContractAddress, contract_address_const, get_block_timestamp}; +use starknet::{ContractAddress, get_block_timestamp}; +use crate::test_utils::zero; // Address constants for testing fn ARTIST_1() -> ContractAddress { - contract_address_const::<'artist_1'>() + 'artist_1'.try_into().unwrap() } fn ARTIST_2() -> ContractAddress { - contract_address_const::<'artist_2'>() + 'artist_2'.try_into().unwrap() } fn OWNER() -> ContractAddress { - contract_address_const::<'owner'>() + 'owner'.try_into().unwrap() } fn SHAREHOLDER_1() -> ContractAddress { - contract_address_const::<'shareholder_1'>() + 'shareholder_1'.try_into().unwrap() } fn SHAREHOLDER_2() -> ContractAddress { - contract_address_const::<'shareholder_2'>() + 'shareholder_2'.try_into().unwrap() } fn SHAREHOLDER_3() -> ContractAddress { - contract_address_const::<'shareholder_3'>() + 'shareholder_3'.try_into().unwrap() } fn ZERO_ADDRESS() -> ContractAddress { - contract_address_const::<0>() + 0.try_into().unwrap() } const TOTAL_SHARES: u256 = 100_u256; @@ -759,10 +760,7 @@ fn test_artist_management() { // Test artist retrieval assert(proposal_system.get_artist_for_token(token1) == artist1, 'Artist 1 mismatch'); - assert( - proposal_system.get_artist_for_token(token2) == contract_address_const::<0>(), - 'Artist 2 mismatch', - ); + assert(proposal_system.get_artist_for_token(token2) == zero(), 'Artist 2 mismatch'); assert( proposal_system.get_artist_for_token(ZERO_ADDRESS()) == ZERO_ADDRESS(), 'Unregistered should be zero', @@ -2001,7 +1999,6 @@ fn test_governance_token_transfer_during_voting() { ) = setup_governance_environment(); let shareholder1 = SHAREHOLDER_1(); - let mut spy = spy_events(); let shareholder2 = SHAREHOLDER_2(); let owner = OWNER(); diff --git a/contract_/tests/test_season_and_audition.cairo b/contract_/tests/test_season_and_audition.cairo index 54aac29..f37f378 100644 --- a/contract_/tests/test_season_and_audition.cairo +++ b/contract_/tests/test_season_and_audition.cairo @@ -2,36 +2,21 @@ use contract_::audition::interfaces::iseason_and_audition::{ ISeasonAndAuditionDispatcherTrait, ISeasonAndAuditionSafeDispatcherTrait, }; use contract_::audition::season_and_audition::SeasonAndAudition; -use contract_::audition::types::season_and_audition::{ - Appeal, Audition, Evaluation, Genre, Season, Vote, -}; +use contract_::audition::types::season_and_audition::Genre; use contract_::events::{ - AuditionCreated, AuditionDeleted, AuditionEnded, AuditionPaused, AuditionResumed, - AuditionUpdated, PriceDeposited, PriceDistributed, ResultSubmitted, SeasonCreated, - SeasonDeleted, SeasonUpdated, + AuditionCalculationCompleted, AuditionCreated, AuditionEnded, AuditionPaused, AuditionResumed, + AuditionUpdated, JudgeAdded, JudgeRemoved, PriceDeposited, PriceDistributed, ResultSubmitted, + SeasonCreated, SeasonUpdated, }; -use openzeppelin::access::ownable::interface::IOwnableDispatcher; -use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; +use openzeppelin::token::erc20::interface::IERC20DispatcherTrait; use snforge_std::{ - ContractClassTrait, DeclareResultTrait, EventSpyAssertionsTrait, declare, spy_events, - start_cheat_block_timestamp, start_cheat_caller_address, stop_cheat_block_timestamp, - stop_cheat_caller_address, + EventSpyAssertionsTrait, spy_events, start_cheat_block_timestamp, start_cheat_caller_address, + stop_cheat_block_timestamp, stop_cheat_caller_address, }; -use starknet::{ContractAddress, contract_address_const, get_block_timestamp}; +use starknet::{ContractAddress, get_block_timestamp}; use crate::test_audition_registration::{feign_artists_registration, feign_update_config}; use crate::test_utils::*; -fn performer() -> ContractAddress { - 'performerid'.try_into().unwrap() -} - -fn performer2() -> ContractAddress { - 'performerid2'.try_into().unwrap() -} - -fn performer3() -> ContractAddress { - 'performerid3'.try_into().unwrap() -} #[test] fn test_create_season_successfully() { @@ -49,7 +34,6 @@ fn test_create_season_successfully() { assert!(read_season.start_timestamp == 1672531200, "Failed to read season start timestamp"); assert!(read_season.end_timestamp == 1675123200, "Failed to read season end timestamp"); assert!(!read_season.paused, "Failed to read season paused"); - assert!(contract.get_active_season() == Some(season_id), "Failed to get active season"); spy @@ -75,7 +59,7 @@ fn test_create_season_successfully() { #[test] -#[should_panic(expected: 'Caller is not the owner')] +#[should_panic(expected: 'Caller is missing role')] fn test_create_season_should_panic_of_called_by_non_owner() { let (contract, _, _) = deploy_contract(); start_cheat_caller_address(contract.contract_address, USER()); @@ -169,7 +153,7 @@ fn test_update_season_successfully() { } #[test] -#[should_panic(expected: 'Caller is not the owner')] +#[should_panic(expected: 'Caller is missing role')] fn test_update_season_should_panic_if_caller_not_owner() { let (contract, _, _) = deploy_contract(); let season_id: u256 = 1; @@ -279,14 +263,9 @@ fn test_create_audition() { #[should_panic(expected: 'Season is paused')] fn test_create_audition_should_panic_if_season_paused() { let (contract, _, _) = deploy_contract(); - let mut spy = spy_events(); - // Define audition ID and season ID - let audition_id: u256 = 1; let season_id: u256 = 1; - // Create default audition - // Start prank to simulate the owner calling the contract start_cheat_caller_address(contract.contract_address, OWNER()); default_contract_create_season(contract); @@ -298,12 +277,10 @@ fn test_create_audition_should_panic_if_season_paused() { } #[test] -// // #[ignore] fn test_audition_deposit_price_successful() { let (contract, _, _) = deploy_contract(); let mut spy = spy_events(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); default_contract_create_season(contract); @@ -345,11 +322,9 @@ fn test_audition_deposit_price_successful() { #[test] -// #[ignore] #[should_panic(expected: 'Season is paused')] fn test_audition_deposit_price_should_panic_if_season_paused() { let (contract, _, _) = deploy_contract(); - let mut spy = spy_events(); let audition_id: u256 = 1; let season_id: u256 = 1; @@ -373,12 +348,10 @@ fn test_audition_deposit_price_should_panic_if_season_paused() { } #[test] -// #[ignore] #[should_panic(expected: 'Amount must be more than zero')] fn test_audition_deposit_price_should_panic_if_amount_is_zero() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); default_contract_create_season(contract); @@ -400,12 +373,10 @@ fn test_audition_deposit_price_should_panic_if_amount_is_zero() { #[test] -// #[ignore] #[should_panic(expected: 'Token address cannot be zero')] fn test_audition_deposit_price_should_panic_if_token_is_zero_address() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); default_contract_create_season(contract); @@ -413,50 +384,56 @@ fn test_audition_deposit_price_should_panic_if_token_is_zero_address() { stop_cheat_caller_address(contract.contract_address); - let zero_address = contract_address_const::<0>(); - start_cheat_caller_address(contract.contract_address, OWNER()); // deposit the price into a prize pool of an audition - contract.deposit_prize(audition_id, zero_address, 10); + contract.deposit_prize(audition_id, zero(), 10); stop_cheat_caller_address(contract.contract_address); } #[test] -// #[ignore] #[should_panic(expected: "Prize already deposited")] fn test_audition_deposit_price_should_panic_if_already_deposited() { - let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; - + let (contract, erc20) = feign_update_config(OWNER(), audition_id, 100); start_cheat_caller_address(contract.contract_address, OWNER()); - default_contract_create_season(contract); - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let mock_token_dispatcher = deploy_mock_erc20_contract(); + // Set timestamp + let initial_timestamp: u64 = 1672531200; + start_cheat_block_timestamp(contract.contract_address, initial_timestamp); + + // then add 2 judges + let judge_address1: ContractAddress = 0x123.try_into().unwrap(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); + contract.add_judge(audition_id, judge_address1); + contract.add_judge(audition_id, judge_address2); + + stop_cheat_block_timestamp(contract.contract_address); + stop_cheat_caller_address(contract.contract_address); + // then set weight + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.set_evaluation_weight(audition_id, (40, 30, 30)); stop_cheat_caller_address(contract.contract_address); - start_cheat_caller_address(mock_token_dispatcher.contract_address, OWNER()); - mock_token_dispatcher.approve(contract.contract_address, 10); - stop_cheat_caller_address(mock_token_dispatcher.contract_address); + // Approve contract to spend tokens + start_cheat_caller_address(erc20.contract_address, OWNER()); + erc20.approve(contract.contract_address, 10); + stop_cheat_caller_address(erc20.contract_address); + // Deposit the prize into the prize pool of an audition start_cheat_caller_address(contract.contract_address, OWNER()); - // deposit the price into a prize pool of an audition - contract.deposit_prize(audition_id, mock_token_dispatcher.contract_address, 10); - contract.deposit_prize(audition_id, mock_token_dispatcher.contract_address, 10); + contract.deposit_prize(audition_id, erc20.contract_address, 5); + contract.deposit_prize(audition_id, erc20.contract_address, 5); stop_cheat_caller_address(contract.contract_address); } #[test] -// #[ignore] #[should_panic(expected: 'Insufficient allowance')] fn test_audition_deposit_price_should_panic_if_insufficient_allowance() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); default_contract_create_season(contract); @@ -478,12 +455,10 @@ fn test_audition_deposit_price_should_panic_if_insufficient_allowance() { #[test] -// #[ignore] #[should_panic(expected: 'Insufficient balance')] fn test_audition_deposit_price_should_panic_if_insufficient_balance() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); default_contract_create_season(contract); @@ -493,7 +468,7 @@ fn test_audition_deposit_price_should_panic_if_insufficient_balance() { stop_cheat_caller_address(contract.contract_address); - let recipient = contract_address_const::<1234>(); + let recipient: ContractAddress = 1234.try_into().unwrap(); let owner_balance = mock_token_dispatcher.balance_of(OWNER().into()); start_cheat_caller_address(mock_token_dispatcher.contract_address, OWNER()); mock_token_dispatcher.transfer(recipient, owner_balance); @@ -511,14 +486,12 @@ fn test_audition_deposit_price_should_panic_if_insufficient_balance() { #[test] -// #[ignore] #[should_panic(expected: 'Audition has already ended')] fn test_audition_deposit_price_should_panic_if_audition_ended_already() { let (contract, _, _) = deploy_contract(); let mock_token_dispatcher = deploy_mock_erc20_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); @@ -542,7 +515,6 @@ fn test_audition_deposit_price_should_panic_if_audition_ended_already() { #[test] -// #[ignore] #[should_panic(expected: 'Audition does not exist')] fn test_audition_deposit_price_should_panic_if_invalid_audition_id() { let (contract, _, _) = deploy_contract(); @@ -564,12 +536,10 @@ fn test_audition_deposit_price_should_panic_if_invalid_audition_id() { #[test] -// #[ignore] -#[should_panic(expected: 'Caller is not the owner')] +#[should_panic(expected: 'Caller is missing role')] fn test_audition_deposit_price_should_panic_if_called_by_non_owner() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); default_contract_create_season(contract); @@ -588,12 +558,10 @@ fn test_audition_deposit_price_should_panic_if_called_by_non_owner() { #[test] -// #[ignore] #[should_panic(expected: 'Contract is paused')] fn test_audition_deposit_price_should_panic_if_contract_is_paused() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); default_contract_create_season(contract); @@ -616,124 +584,137 @@ fn test_audition_deposit_price_should_panic_if_contract_is_paused() { } #[test] -// #[ignore] fn test_audition_distribute_prize_successful() { - let (contract, _, _) = deploy_contract(); - let mut spy = spy_events(); let audition_id: u256 = 1; - let season_id: u256 = 1; - + let (contract, erc20) = feign_update_config(OWNER(), audition_id, 100); + let artists: Array<(ContractAddress, u256)> = feign_artists_registration( + 2, erc20, 100, contract, + ); + let mut spy = spy_events(); start_cheat_caller_address(contract.contract_address, OWNER()); + + // Set timestamp let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); - default_contract_create_season(contract); - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let mock_token_dispatcher = deploy_mock_erc20_contract(); + // then add 2 judges + let judge_address1: ContractAddress = 0x123.try_into().unwrap(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); + contract.add_judge(audition_id, judge_address1); + contract.add_judge(audition_id, judge_address2); + + stop_cheat_block_timestamp(contract.contract_address); + stop_cheat_caller_address(contract.contract_address); + let (performer_addr1, performer_id1) = *artists.at(0); + let (performer_addr2, performer_id2) = *artists.at(1); + + // then set weight + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.set_evaluation_weight(audition_id, (40, 30, 30)); stop_cheat_caller_address(contract.contract_address); // Approve contract to spend tokens - start_cheat_caller_address(mock_token_dispatcher.contract_address, OWNER()); - mock_token_dispatcher.approve(contract.contract_address, 10); - stop_cheat_caller_address(mock_token_dispatcher.contract_address); + start_cheat_caller_address(erc20.contract_address, OWNER()); + erc20.approve(contract.contract_address, 10); + stop_cheat_caller_address(erc20.contract_address); // Check contract balance before deposit - let contract_balance_before = mock_token_dispatcher.balance_of(contract.contract_address); + let contract_balance_before = erc20.balance_of(contract.contract_address); // Deposit the prize into the prize pool of an audition start_cheat_caller_address(contract.contract_address, OWNER()); - contract.deposit_prize(audition_id, mock_token_dispatcher.contract_address, 10); + contract.deposit_prize(audition_id, erc20.contract_address, 10); stop_cheat_caller_address(contract.contract_address); // Check contract balance after deposit - let contract_balance_after = mock_token_dispatcher.balance_of(contract.contract_address); + let contract_balance_after = erc20.balance_of(contract.contract_address); assert!( contract_balance_after == contract_balance_before + 10, "Contract balance did not increase after deposit", ); + // then submit evaluation for each performer + start_cheat_caller_address(contract.contract_address, judge_address1); + contract.submit_evaluation(audition_id, performer_id1, (4, 7, 3)); + contract.submit_evaluation(audition_id, performer_id2, (6, 7, 8)); + stop_cheat_caller_address(contract.contract_address); + + start_cheat_caller_address(contract.contract_address, judge_address2); + contract.submit_evaluation(audition_id, performer_id1, (4, 9, 2)); + contract.submit_evaluation(audition_id, performer_id2, (4, 9, 6)); + stop_cheat_caller_address(contract.contract_address); + + // move the timestamp to the end of the audition + start_cheat_block_timestamp(contract.contract_address, initial_timestamp + 1675123200); + + // then perform aggregate score calculation + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.perform_aggregate_score_calculation(audition_id); + stop_cheat_caller_address(contract.contract_address); + // Assert winner addresses and amounts are zero before distribution - let (w_addr1_before, w_addr2_before, w_addr3_before) = contract - .get_audition_winner_addresses(audition_id); - let (w_amt1_before, w_amt2_before, w_amt3_before) = contract - .get_audition_winner_amounts(audition_id); + let w_addr_before = contract.get_audition_winner_addresses(audition_id); + let w_amt_before = contract.get_audition_winner_amounts(audition_id); let is_distributed_before = contract.is_prize_distributed(audition_id); - assert!( - w_addr1_before == contract_address_const::<0>(), - "Winner 1 address should be zero before distribution", - ); - assert!( - w_addr2_before == contract_address_const::<0>(), - "Winner 2 address should be zero before distribution", - ); - assert!( - w_addr3_before == contract_address_const::<0>(), - "Winner 3 address should be zero before distribution", - ); - assert!(w_amt1_before == 0, "Winner 1 amount should be zero before distribution"); - assert!(w_amt2_before == 0, "Winner 2 amount should be zero before distribution"); - assert!(w_amt3_before == 0, "Winner 3 amount should be zero before distribution"); + for addr in w_addr_before { + assert!(addr == zero(), "Winner address should be zero before distribution"); + } + + for amt in w_amt_before { + assert!(amt == 0, "Winner amount should be zero before distribution"); + } + assert!(!is_distributed_before, "Prize should not be distributed before distribution"); // Prepare for distribution start_cheat_caller_address(contract.contract_address, OWNER()); - // UPDATE Audition with future end time - contract.update_audition_details(audition_id, Some(1672617600), None, None); - contract.end_audition(audition_id); - - let winner1 = contract_address_const::<1111>(); - let winner2 = contract_address_const::<2222>(); - let winner3 = contract_address_const::<3333>(); - // Check winners' balances before distribution - let winner1_balance_before = mock_token_dispatcher.balance_of(winner1); - let winner2_balance_before = mock_token_dispatcher.balance_of(winner2); - let winner3_balance_before = mock_token_dispatcher.balance_of(winner3); + let winner1_balance_before = erc20.balance_of(performer_addr1); + let winner2_balance_before = erc20.balance_of(performer_addr1); // Distribute the prize - contract.distribute_prize(audition_id, [winner1, winner2, winner3], [50, 30, 20]); + contract.distribute_prize(audition_id, array![70, 30]); // Check contract balance after distribution - let contract_balance_final = mock_token_dispatcher.balance_of(contract.contract_address); + let contract_balance_final = erc20.balance_of(contract.contract_address); assert!( contract_balance_final == contract_balance_after - 10, "Contract balance did not decrease after distribution", ); // Check winners' balances after distribution - let winner1_balance_after = mock_token_dispatcher.balance_of(winner1); - let winner2_balance_after = mock_token_dispatcher.balance_of(winner2); - let winner3_balance_after = mock_token_dispatcher.balance_of(winner3); + let winner1_balance_after = erc20.balance_of(performer_addr1); + println!("winner1_balance_after {}", winner1_balance_after); + let winner2_balance_after = erc20.balance_of(performer_addr2); assert!( - winner1_balance_after == winner1_balance_before + 5, + winner1_balance_after == winner1_balance_before + 3, "Winner 1 did not receive correct amount", ); assert!( - winner2_balance_after == winner2_balance_before + 3, + winner2_balance_after == winner2_balance_before + 7, "Winner 2 did not receive correct amount", ); - assert!( - winner3_balance_after == winner3_balance_before + 2, - "Winner 3 did not receive correct amount", - ); // Assert winner addresses and amounts after distribution - let (w_addr1_after, w_addr2_after, w_addr3_after) = contract - .get_audition_winner_addresses(audition_id); - let (w_amt1_after, w_amt2_after, w_amt3_after) = contract - .get_audition_winner_amounts(audition_id); + let w_addr_after = contract.get_audition_winner_addresses(audition_id); + let w_amt_after = contract.get_audition_winner_amounts(audition_id); let is_distributed_after = contract.is_prize_distributed(audition_id); - assert!(w_addr1_after == winner1, "Winner 1 address mismatch after distribution"); - assert!(w_addr2_after == winner2, "Winner 2 address mismatch after distribution"); - assert!(w_addr3_after == winner3, "Winner 3 address mismatch after distribution"); - assert!(w_amt1_after == 5, "Winner 1 amount mismatch after distribution"); + let w_addr1_after: ContractAddress = *w_addr_after.at(0); + let w_addr2_after: ContractAddress = *w_addr_after.at(1); + + assert!(w_addr1_after == performer_addr2, "Winner 1 address mismatch after distribution"); + assert!(w_addr2_after == performer_addr1, "Winner 2 address mismatch after distribution"); + + let w_amt1_after: u256 = *w_amt_after.at(0); + let w_amt2_after: u256 = *w_amt_after.at(1); + + assert!(w_amt1_after == 7, "Winner 1 amount mismatch after distribution"); assert!(w_amt2_after == 3, "Winner 2 amount mismatch after distribution"); - assert!(w_amt3_after == 2, "Winner 3 amount mismatch after distribution"); assert!(is_distributed_after, "Prize should be marked as distributed after distribution"); spy @@ -744,10 +725,10 @@ fn test_audition_distribute_prize_successful() { SeasonAndAudition::Event::PriceDistributed( PriceDistributed { audition_id: audition_id, - winners: [winner1, winner2, winner3], - shares: [50, 30, 20], - token_address: mock_token_dispatcher.contract_address, - amounts: [5, 3, 2].span(), + winners: w_addr_after.into(), + shares: array![70, 30].span(), + token_address: erc20.contract_address, + amounts: [7, 3].span(), }, ), ), @@ -757,444 +738,607 @@ fn test_audition_distribute_prize_successful() { stop_cheat_caller_address(contract.contract_address); } - #[test] -// #[ignore] -#[should_panic(expected: 'Season is paused')] -fn test_audition_distribute_prize_should_panic_if_season_paused() { - let (contract, _, _) = deploy_contract(); - let mut spy = spy_events(); +#[should_panic(expected: 'Caller is missing role')] +fn test_audition_distribute_prize_should_panic_if_not_owner() { let audition_id: u256 = 1; - let season_id: u256 = 1; - + let (contract, erc20) = feign_update_config(OWNER(), audition_id, 100); + let artists: Array<(ContractAddress, u256)> = feign_artists_registration( + 2, erc20, 100, contract, + ); start_cheat_caller_address(contract.contract_address, OWNER()); + + // Set timestamp let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); - default_contract_create_season(contract); - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let mock_token_dispatcher = deploy_mock_erc20_contract(); + // then add 2 judges + let judge_address1: ContractAddress = 0x123.try_into().unwrap(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); + contract.add_judge(audition_id, judge_address1); + contract.add_judge(audition_id, judge_address2); + + stop_cheat_block_timestamp(contract.contract_address); + stop_cheat_caller_address(contract.contract_address); + + let (_, performer_id1) = *artists.at(0); + let (_, performer_id2) = *artists.at(1); + // then set weight + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.set_evaluation_weight(audition_id, (40, 30, 30)); stop_cheat_caller_address(contract.contract_address); // Approve contract to spend tokens - start_cheat_caller_address(mock_token_dispatcher.contract_address, OWNER()); - mock_token_dispatcher.approve(contract.contract_address, 10); - stop_cheat_caller_address(mock_token_dispatcher.contract_address); + start_cheat_caller_address(erc20.contract_address, OWNER()); + erc20.approve(contract.contract_address, 10); + stop_cheat_caller_address(erc20.contract_address); // Check contract balance before deposit - let contract_balance_before = mock_token_dispatcher.balance_of(contract.contract_address); + let contract_balance_before = erc20.balance_of(contract.contract_address); // Deposit the prize into the prize pool of an audition start_cheat_caller_address(contract.contract_address, OWNER()); - contract.deposit_prize(audition_id, mock_token_dispatcher.contract_address, 10); + contract.deposit_prize(audition_id, erc20.contract_address, 10); stop_cheat_caller_address(contract.contract_address); // Check contract balance after deposit - let contract_balance_after = mock_token_dispatcher.balance_of(contract.contract_address); + let contract_balance_after = erc20.balance_of(contract.contract_address); assert!( contract_balance_after == contract_balance_before + 10, "Contract balance did not increase after deposit", ); - // Prepare for distribution - start_cheat_caller_address(contract.contract_address, OWNER()); - - // UPDATE Audition with future end time - contract.update_audition_details(audition_id, Some(1672617600), None, None); - contract.end_audition(audition_id); - - let winner1 = contract_address_const::<1111>(); - let winner2 = contract_address_const::<2222>(); - let winner3 = contract_address_const::<3333>(); - - contract.pause_season(season_id); - // Distribute the prize - contract.distribute_prize(audition_id, [winner1, winner2, winner3], [50, 30, 20]); - + // then submit evaluation for each performer + start_cheat_caller_address(contract.contract_address, judge_address1); + contract.submit_evaluation(audition_id, performer_id1, (4, 7, 3)); + contract.submit_evaluation(audition_id, performer_id2, (6, 7, 8)); stop_cheat_caller_address(contract.contract_address); -} - -#[test] -// #[ignore] -#[should_panic(expected: 'Caller is not the owner')] -fn test_audition_distribute_prize_should_panic_if_not_owner() { - let (contract, _, _) = deploy_contract(); - let audition_id: u256 = 1; - let season_id: u256 = 1; - start_cheat_caller_address(contract.contract_address, OWNER()); - default_contract_create_season(contract); - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let mock_token_dispatcher = deploy_mock_erc20_contract(); + start_cheat_caller_address(contract.contract_address, judge_address2); + contract.submit_evaluation(audition_id, performer_id1, (4, 9, 2)); + contract.submit_evaluation(audition_id, performer_id2, (4, 9, 6)); stop_cheat_caller_address(contract.contract_address); - start_cheat_caller_address(mock_token_dispatcher.contract_address, OWNER()); - mock_token_dispatcher.approve(contract.contract_address, 10); - stop_cheat_caller_address(mock_token_dispatcher.contract_address); + // move the timestamp to the end of the audition + start_cheat_block_timestamp(contract.contract_address, initial_timestamp + 1675123200); + // then perform aggregate score calculation start_cheat_caller_address(contract.contract_address, OWNER()); - contract.deposit_prize(audition_id, mock_token_dispatcher.contract_address, 10); + contract.perform_aggregate_score_calculation(audition_id); stop_cheat_caller_address(contract.contract_address); - let winner1 = contract_address_const::<1111>(); - let winner2 = contract_address_const::<2222>(); - let winner3 = contract_address_const::<3333>(); - - // Not owner - start_cheat_caller_address(contract.contract_address, NON_OWNER()); - contract.distribute_prize(audition_id, [winner1, winner2, winner3], [50, 30, 20]); + // Distribute the prize + contract.distribute_prize(audition_id, array![70, 30]); } #[test] -// #[ignore] #[should_panic(expected: 'Contract is paused')] fn test_audition_distribute_prize_should_panic_if_contract_is_paused() { - let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; - + let (contract, erc20) = feign_update_config(OWNER(), audition_id, 100); + let artists: Array<(ContractAddress, u256)> = feign_artists_registration( + 2, erc20, 100, contract, + ); start_cheat_caller_address(contract.contract_address, OWNER()); + + // Set timestamp let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); - default_contract_create_season(contract); - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let mock_token_dispatcher = deploy_mock_erc20_contract(); + // then add 2 judges + let judge_address1: ContractAddress = 0x123.try_into().unwrap(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); + contract.add_judge(audition_id, judge_address1); + contract.add_judge(audition_id, judge_address2); + + stop_cheat_block_timestamp(contract.contract_address); + stop_cheat_caller_address(contract.contract_address); + let (_, performer_id1) = *artists.at(0); + let (_, performer_id2) = *artists.at(1); + + // then set weight + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.set_evaluation_weight(audition_id, (40, 30, 30)); stop_cheat_caller_address(contract.contract_address); // Approve contract to spend tokens - start_cheat_caller_address(mock_token_dispatcher.contract_address, OWNER()); - mock_token_dispatcher.approve(contract.contract_address, 10); - stop_cheat_caller_address(mock_token_dispatcher.contract_address); + start_cheat_caller_address(erc20.contract_address, OWNER()); + erc20.approve(contract.contract_address, 10); + stop_cheat_caller_address(erc20.contract_address); + + // Check contract balance before deposit + let contract_balance_before = erc20.balance_of(contract.contract_address); // Deposit the prize into the prize pool of an audition start_cheat_caller_address(contract.contract_address, OWNER()); - contract.deposit_prize(audition_id, mock_token_dispatcher.contract_address, 10); + contract.deposit_prize(audition_id, erc20.contract_address, 10); stop_cheat_caller_address(contract.contract_address); - // Prepare for distribution - start_cheat_caller_address(contract.contract_address, OWNER()); + // Check contract balance after deposit + let contract_balance_after = erc20.balance_of(contract.contract_address); + assert!( + contract_balance_after == contract_balance_before + 10, + "Contract balance did not increase after deposit", + ); - contract.update_audition_details(audition_id, Some(1672617600), None, None); - contract.end_audition(audition_id); + // then submit evaluation for each performer + start_cheat_caller_address(contract.contract_address, judge_address1); + contract.submit_evaluation(audition_id, performer_id1, (4, 7, 3)); + contract.submit_evaluation(audition_id, performer_id2, (6, 7, 8)); + stop_cheat_caller_address(contract.contract_address); + + start_cheat_caller_address(contract.contract_address, judge_address2); + contract.submit_evaluation(audition_id, performer_id1, (4, 9, 2)); + contract.submit_evaluation(audition_id, performer_id2, (4, 9, 6)); + stop_cheat_caller_address(contract.contract_address); + + // move the timestamp to the end of the audition + start_cheat_block_timestamp(contract.contract_address, initial_timestamp + 1675123200); + + // then perform aggregate score calculation + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.perform_aggregate_score_calculation(audition_id); // Pause the contract before distribution contract.pause_all(); - let winner1 = contract_address_const::<1111>(); - let winner2 = contract_address_const::<2222>(); - let winner3 = contract_address_const::<3333>(); - - // This should panic because the contract is paused - contract.distribute_prize(audition_id, [winner1, winner2, winner3], [50, 30, 20]); + // Distribute the prize + contract.distribute_prize(audition_id, array![70, 30]); - stop_cheat_block_timestamp(contract.contract_address); stop_cheat_caller_address(contract.contract_address); } #[test] -// #[ignore] #[should_panic(expected: 'Audition does not exist')] fn test_audition_distribute_prize_should_panic_if_invalid_audition_id() { - let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let invalid_audition_id: u256 = 999; - let season_id: u256 = 1; - + let (contract, erc20) = feign_update_config(OWNER(), audition_id, 100); + let artists: Array<(ContractAddress, u256)> = feign_artists_registration( + 2, erc20, 100, contract, + ); start_cheat_caller_address(contract.contract_address, OWNER()); + + // Set timestamp let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); - default_contract_create_season(contract); - // Create a valid audition - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let mock_token_dispatcher = deploy_mock_erc20_contract(); + // then add 2 judges + let judge_address1: ContractAddress = 0x123.try_into().unwrap(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); + contract.add_judge(audition_id, judge_address1); + contract.add_judge(audition_id, judge_address2); + stop_cheat_block_timestamp(contract.contract_address); stop_cheat_caller_address(contract.contract_address); - // Approve contract to spend tokens - start_cheat_caller_address(mock_token_dispatcher.contract_address, OWNER()); - mock_token_dispatcher.approve(contract.contract_address, 10); - stop_cheat_caller_address(mock_token_dispatcher.contract_address); + let (_, performer_id1) = *artists.at(0); + let (_, performer_id2) = *artists.at(1); - // Deposit the prize into the prize pool of the valid audition + // then set weight start_cheat_caller_address(contract.contract_address, OWNER()); - contract.deposit_prize(audition_id, mock_token_dispatcher.contract_address, 10); + contract.set_evaluation_weight(audition_id, (40, 30, 30)); stop_cheat_caller_address(contract.contract_address); - // Prepare for distribution on a non-existent audition + // Approve contract to spend tokens + start_cheat_caller_address(erc20.contract_address, OWNER()); + erc20.approve(contract.contract_address, 10); + stop_cheat_caller_address(erc20.contract_address); + + // Check contract balance before deposit + let contract_balance_before = erc20.balance_of(contract.contract_address); + + // Deposit the prize into the prize pool of an audition start_cheat_caller_address(contract.contract_address, OWNER()); + contract.deposit_prize(audition_id, erc20.contract_address, 10); + stop_cheat_caller_address(contract.contract_address); - let winner1 = contract_address_const::<1111>(); - let winner2 = contract_address_const::<2222>(); - let winner3 = contract_address_const::<3333>(); + // Check contract balance after deposit + let contract_balance_after = erc20.balance_of(contract.contract_address); + assert!( + contract_balance_after == contract_balance_before + 10, + "Contract balance did not increase after deposit", + ); - // This should panic because the audition ID does not exist - contract.distribute_prize(invalid_audition_id, [winner1, winner2, winner3], [50, 30, 20]); + // then submit evaluation for each performer + start_cheat_caller_address(contract.contract_address, judge_address1); + contract.submit_evaluation(audition_id, performer_id1, (4, 7, 3)); + contract.submit_evaluation(audition_id, performer_id2, (6, 7, 8)); + stop_cheat_caller_address(contract.contract_address); + + start_cheat_caller_address(contract.contract_address, judge_address2); + contract.submit_evaluation(audition_id, performer_id1, (4, 9, 2)); + contract.submit_evaluation(audition_id, performer_id2, (4, 9, 6)); + stop_cheat_caller_address(contract.contract_address); + + // move the timestamp to the end of the audition + start_cheat_block_timestamp(contract.contract_address, initial_timestamp + 1675123200); + + // then perform aggregate score calculation + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.perform_aggregate_score_calculation(audition_id); + stop_cheat_caller_address(contract.contract_address); + + contract.distribute_prize(99, array![70, 30]); - stop_cheat_block_timestamp(contract.contract_address); stop_cheat_caller_address(contract.contract_address); } #[test] -// #[ignore] -#[should_panic(expected: 'Audition must end first')] +#[should_panic(expected: 'Audition has not ended')] fn test_distribute_prize_should_panic_if_audition_not_ended() { - let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; - + let (contract, erc20) = feign_update_config(OWNER(), audition_id, 100); + let artists: Array<(ContractAddress, u256)> = feign_artists_registration( + 2, erc20, 100, contract, + ); start_cheat_caller_address(contract.contract_address, OWNER()); + + // Set timestamp let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); - default_contract_create_season(contract); - // CREATE Audition - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let mock_token_dispatcher = deploy_mock_erc20_contract(); + // then add 2 judges + let judge_address1: ContractAddress = 0x123.try_into().unwrap(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); + contract.add_judge(audition_id, judge_address1); + contract.add_judge(audition_id, judge_address2); + + stop_cheat_block_timestamp(contract.contract_address); + stop_cheat_caller_address(contract.contract_address); + + let (_, performer_id1) = *artists.at(0); + let (_, performer_id2) = *artists.at(1); + + // then set weight + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.set_evaluation_weight(audition_id, (40, 30, 30)); stop_cheat_caller_address(contract.contract_address); // Approve contract to spend tokens - start_cheat_caller_address(mock_token_dispatcher.contract_address, OWNER()); - mock_token_dispatcher.approve(contract.contract_address, 10); - stop_cheat_caller_address(mock_token_dispatcher.contract_address); + start_cheat_caller_address(erc20.contract_address, OWNER()); + erc20.approve(contract.contract_address, 10); + stop_cheat_caller_address(erc20.contract_address); + + // Check contract balance before deposit + let contract_balance_before = erc20.balance_of(contract.contract_address); // Deposit the prize into the prize pool of an audition start_cheat_caller_address(contract.contract_address, OWNER()); - contract.deposit_prize(audition_id, mock_token_dispatcher.contract_address, 10); + contract.deposit_prize(audition_id, erc20.contract_address, 10); stop_cheat_caller_address(contract.contract_address); - // Prepare for distribution without ending the audition - start_cheat_caller_address(contract.contract_address, OWNER()); + // Check contract balance after deposit + let contract_balance_after = erc20.balance_of(contract.contract_address); + assert!( + contract_balance_after == contract_balance_before + 10, + "Contract balance did not increase after deposit", + ); - let winner1 = contract_address_const::<1111>(); - let winner2 = contract_address_const::<2222>(); - let winner3 = contract_address_const::<3333>(); + // then submit evaluation for each performer + start_cheat_caller_address(contract.contract_address, judge_address1); + contract.submit_evaluation(audition_id, performer_id1, (4, 7, 3)); + contract.submit_evaluation(audition_id, performer_id2, (6, 7, 8)); + stop_cheat_caller_address(contract.contract_address); - // This should panic because the audition has not ended yet - contract.distribute_prize(audition_id, [winner1, winner2, winner3], [50, 30, 20]); + start_cheat_caller_address(contract.contract_address, judge_address2); + contract.submit_evaluation(audition_id, performer_id1, (4, 9, 2)); + contract.submit_evaluation(audition_id, performer_id2, (4, 9, 6)); + stop_cheat_caller_address(contract.contract_address); + + // then perform aggregate score calculation + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.perform_aggregate_score_calculation(audition_id); + + // Distribute the prize + contract.distribute_prize(audition_id, array![70, 30]); - stop_cheat_block_timestamp(contract.contract_address); stop_cheat_caller_address(contract.contract_address); } #[test] #[should_panic(expected: 'No prize for this audition')] fn test_distribute_prize_should_panic_if_no_prize_deposited() { - let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; - - // Create audition as owner + let (contract, erc20) = feign_update_config(OWNER(), audition_id, 100); + let artists: Array<(ContractAddress, u256)> = feign_artists_registration( + 2, erc20, 100, contract, + ); start_cheat_caller_address(contract.contract_address, OWNER()); + + // Set timestamp let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); - default_contract_create_season(contract); - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - - contract.update_audition_details(audition_id, Some(1672617600), None, None); - contract.end_audition(audition_id); - // Try to distribute prize without depositing any prize - let winner1 = contract_address_const::<1111>(); - let winner2 = contract_address_const::<2222>(); - let winner3 = contract_address_const::<3333>(); - contract.distribute_prize(audition_id, [winner1, winner2, winner3], [50, 30, 20]); + // then add 2 judges + let judge_address1: ContractAddress = 0x123.try_into().unwrap(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); + contract.add_judge(audition_id, judge_address1); + contract.add_judge(audition_id, judge_address2); + stop_cheat_block_timestamp(contract.contract_address); stop_cheat_caller_address(contract.contract_address); -} -#[test] -// #[ignore] -#[should_panic(expected: 'Prize already distributed')] -fn test_distribute_prize_should_panic_if_already_distributed() { - let (contract, _, _) = deploy_contract(); - let audition_id: u256 = 1; - let season_id: u256 = 1; + let (_, performer_id1) = *artists.at(0); + let (_, performer_id2) = *artists.at(1); + // then set weight start_cheat_caller_address(contract.contract_address, OWNER()); - let initial_timestamp: u64 = 1672531200; - start_cheat_block_timestamp(contract.contract_address, initial_timestamp); - default_contract_create_season(contract); - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - - let mock_token_dispatcher = deploy_mock_erc20_contract(); - + contract.set_evaluation_weight(audition_id, (40, 30, 30)); stop_cheat_caller_address(contract.contract_address); // Approve contract to spend tokens - start_cheat_caller_address(mock_token_dispatcher.contract_address, OWNER()); - mock_token_dispatcher.approve(contract.contract_address, 10); - stop_cheat_caller_address(mock_token_dispatcher.contract_address); + start_cheat_caller_address(erc20.contract_address, OWNER()); + erc20.approve(contract.contract_address, 10); + stop_cheat_caller_address(erc20.contract_address); - // Deposit the prize into the prize pool of an audition - start_cheat_caller_address(contract.contract_address, OWNER()); - contract.deposit_prize(audition_id, mock_token_dispatcher.contract_address, 10); + // then submit evaluation for each performer + start_cheat_caller_address(contract.contract_address, judge_address1); + contract.submit_evaluation(audition_id, performer_id1, (4, 7, 3)); + contract.submit_evaluation(audition_id, performer_id2, (6, 7, 8)); stop_cheat_caller_address(contract.contract_address); - // Prepare for distribution - start_cheat_caller_address(contract.contract_address, OWNER()); - - contract.update_audition_details(audition_id, Some(1672617600), None, None); - contract.end_audition(audition_id); + start_cheat_caller_address(contract.contract_address, judge_address2); + contract.submit_evaluation(audition_id, performer_id1, (4, 9, 2)); + contract.submit_evaluation(audition_id, performer_id2, (4, 9, 6)); + stop_cheat_caller_address(contract.contract_address); - let winner1 = contract_address_const::<1111>(); - let winner2 = contract_address_const::<2222>(); - let winner3 = contract_address_const::<3333>(); + // move the timestamp to the end of the audition + start_cheat_block_timestamp(contract.contract_address, initial_timestamp + 1675123200); - // First distribution (should succeed) - contract.distribute_prize(audition_id, [winner1, winner2, winner3], [50, 30, 20]); + // then perform aggregate score calculation + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.perform_aggregate_score_calculation(audition_id); - // Second distribution (should panic) - contract.distribute_prize(audition_id, [winner1, winner2, winner3], [50, 30, 20]); + // Distribute the prize + contract.distribute_prize(audition_id, array![70, 30]); - stop_cheat_block_timestamp(contract.contract_address); stop_cheat_caller_address(contract.contract_address); } #[test] -// #[ignore] -#[should_panic(expected: 'null contract address')] -fn test_distribute_prize_should_panic_if_winner_is_zero_address() { - let (contract, _, _) = deploy_contract(); +#[should_panic(expected: 'Prize already distributed')] +fn test_distribute_prize_should_panic_if_already_distributed() { let audition_id: u256 = 1; - let season_id: u256 = 1; - + let (contract, erc20) = feign_update_config(OWNER(), audition_id, 100); + let artists: Array<(ContractAddress, u256)> = feign_artists_registration( + 2, erc20, 100, contract, + ); start_cheat_caller_address(contract.contract_address, OWNER()); + + // Set timestamp let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); - default_contract_create_season(contract); - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let mock_token_dispatcher = deploy_mock_erc20_contract(); + // then add 2 judges + let judge_address1: ContractAddress = 0x123.try_into().unwrap(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); + contract.add_judge(audition_id, judge_address1); + contract.add_judge(audition_id, judge_address2); + + stop_cheat_block_timestamp(contract.contract_address); + stop_cheat_caller_address(contract.contract_address); + + let (_, performer_id1) = *artists.at(0); + let (_, performer_id2) = *artists.at(1); + + // then set weight + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.set_evaluation_weight(audition_id, (40, 30, 30)); stop_cheat_caller_address(contract.contract_address); // Approve contract to spend tokens - start_cheat_caller_address(mock_token_dispatcher.contract_address, OWNER()); - mock_token_dispatcher.approve(contract.contract_address, 10); - stop_cheat_caller_address(mock_token_dispatcher.contract_address); + start_cheat_caller_address(erc20.contract_address, OWNER()); + erc20.approve(contract.contract_address, 10); + stop_cheat_caller_address(erc20.contract_address); + + // Check contract balance before deposit + let contract_balance_before = erc20.balance_of(contract.contract_address); // Deposit the prize into the prize pool of an audition start_cheat_caller_address(contract.contract_address, OWNER()); - contract.deposit_prize(audition_id, mock_token_dispatcher.contract_address, 10); + contract.deposit_prize(audition_id, erc20.contract_address, 10); stop_cheat_caller_address(contract.contract_address); - // Prepare for distribution - start_cheat_caller_address(contract.contract_address, OWNER()); + // Check contract balance after deposit + let contract_balance_after = erc20.balance_of(contract.contract_address); + assert!( + contract_balance_after == contract_balance_before + 10, + "Contract balance did not increase after deposit", + ); - contract.update_audition_details(audition_id, Some(1672617600), None, None); - contract.end_audition(audition_id); + // then submit evaluation for each performer + start_cheat_caller_address(contract.contract_address, judge_address1); + contract.submit_evaluation(audition_id, performer_id1, (4, 7, 3)); + contract.submit_evaluation(audition_id, performer_id2, (6, 7, 8)); + stop_cheat_caller_address(contract.contract_address); - let winner1 = contract_address_const::<0>(); // Null address - let winner2 = contract_address_const::<2222>(); - let winner3 = contract_address_const::<3333>(); + start_cheat_caller_address(contract.contract_address, judge_address2); + contract.submit_evaluation(audition_id, performer_id1, (4, 9, 2)); + contract.submit_evaluation(audition_id, performer_id2, (4, 9, 6)); + stop_cheat_caller_address(contract.contract_address); - // This should panic because winner1 is a zero address - contract.distribute_prize(audition_id, [winner1, winner2, winner3], [50, 30, 20]); + // move the timestamp to the end of the audition + start_cheat_block_timestamp(contract.contract_address, initial_timestamp + 1675123200); + + // then perform aggregate score calculation + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.perform_aggregate_score_calculation(audition_id); + + // Distribute the prize + contract.distribute_prize(audition_id, array![70, 30]); + contract.distribute_prize(audition_id, array![70, 30]); - stop_cheat_block_timestamp(contract.contract_address); stop_cheat_caller_address(contract.contract_address); } + #[test] -// #[ignore] #[should_panic(expected: 'total does not add up')] fn test_distribute_prize_should_panic_if_total_shares_not_100() { - let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; - + let (contract, erc20) = feign_update_config(OWNER(), audition_id, 100); + let artists: Array<(ContractAddress, u256)> = feign_artists_registration( + 2, erc20, 100, contract, + ); start_cheat_caller_address(contract.contract_address, OWNER()); + + // Set timestamp let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); - default_contract_create_season(contract); - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let mock_token_dispatcher = deploy_mock_erc20_contract(); + // then add 2 judges + let judge_address1: ContractAddress = 0x123.try_into().unwrap(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); + contract.add_judge(audition_id, judge_address1); + contract.add_judge(audition_id, judge_address2); + + stop_cheat_block_timestamp(contract.contract_address); + stop_cheat_caller_address(contract.contract_address); + + let (_, performer_id1) = *artists.at(0); + let (_, performer_id2) = *artists.at(1); + + // then set weight + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.set_evaluation_weight(audition_id, (40, 30, 30)); stop_cheat_caller_address(contract.contract_address); // Approve contract to spend tokens - start_cheat_caller_address(mock_token_dispatcher.contract_address, OWNER()); - mock_token_dispatcher.approve(contract.contract_address, 10); - stop_cheat_caller_address(mock_token_dispatcher.contract_address); + start_cheat_caller_address(erc20.contract_address, OWNER()); + erc20.approve(contract.contract_address, 10); + stop_cheat_caller_address(erc20.contract_address); + + // Check contract balance before deposit + let contract_balance_before = erc20.balance_of(contract.contract_address); // Deposit the prize into the prize pool of an audition start_cheat_caller_address(contract.contract_address, OWNER()); - contract.deposit_prize(audition_id, mock_token_dispatcher.contract_address, 10); + contract.deposit_prize(audition_id, erc20.contract_address, 10); stop_cheat_caller_address(contract.contract_address); - // Prepare for distribution - start_cheat_caller_address(contract.contract_address, OWNER()); + // Check contract balance after deposit + let contract_balance_after = erc20.balance_of(contract.contract_address); + assert!( + contract_balance_after == contract_balance_before + 10, + "Contract balance did not increase after deposit", + ); - // UPDATE Audition with future end time and end it + // then submit evaluation for each performer + start_cheat_caller_address(contract.contract_address, judge_address1); + contract.submit_evaluation(audition_id, performer_id1, (4, 7, 3)); + contract.submit_evaluation(audition_id, performer_id2, (6, 7, 8)); + stop_cheat_caller_address(contract.contract_address); - contract.update_audition_details(audition_id, Some(1672617600), None, None); - contract.end_audition(audition_id); + start_cheat_caller_address(contract.contract_address, judge_address2); + contract.submit_evaluation(audition_id, performer_id1, (4, 9, 2)); + contract.submit_evaluation(audition_id, performer_id2, (4, 9, 6)); + stop_cheat_caller_address(contract.contract_address); - let winner1 = contract_address_const::<1111>(); - let winner2 = contract_address_const::<2222>(); - let winner3 = contract_address_const::<3333>(); + // move the timestamp to the end of the audition + start_cheat_block_timestamp(contract.contract_address, initial_timestamp + 1675123200); - // This should panic because shares do not add up to 100 (e.g., 40 + 30 + 20 = 90) - contract.distribute_prize(audition_id, [winner1, winner2, winner3], [40, 30, 20]); + // then perform aggregate score calculation + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.perform_aggregate_score_calculation(audition_id); + + // Distribute the prize + contract.distribute_prize(audition_id, array![10, 30]); - stop_cheat_block_timestamp(contract.contract_address); stop_cheat_caller_address(contract.contract_address); } #[test] -// #[ignore] #[should_panic(expected: 'Insufficient balance')] fn test_audition_distribute_prize_should_panic_if_contract_balance_insufficient() { - let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; - - // Set up contract and audition + let (contract, erc20) = feign_update_config(OWNER(), audition_id, 100); + let artists: Array<(ContractAddress, u256)> = feign_artists_registration( + 2, erc20, 100, contract, + ); + let mut spy = spy_events(); start_cheat_caller_address(contract.contract_address, OWNER()); + + // Set timestamp let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); - default_contract_create_season(contract); - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let mock_token_dispatcher = deploy_mock_erc20_contract(); + // then add 2 judges + let judge_address1: ContractAddress = 0x123.try_into().unwrap(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); + contract.add_judge(audition_id, judge_address1); + contract.add_judge(audition_id, judge_address2); + + stop_cheat_block_timestamp(contract.contract_address); + stop_cheat_caller_address(contract.contract_address); + + let (performer_addr1, performer_id1) = *artists.at(0); + let (performer_addr2, performer_id2) = *artists.at(1); + + // then set weight + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.set_evaluation_weight(audition_id, (40, 30, 30)); stop_cheat_caller_address(contract.contract_address); // Approve contract to spend tokens - start_cheat_caller_address(mock_token_dispatcher.contract_address, OWNER()); - mock_token_dispatcher.approve(contract.contract_address, 10); - stop_cheat_caller_address(mock_token_dispatcher.contract_address); + start_cheat_caller_address(erc20.contract_address, OWNER()); + erc20.approve(contract.contract_address, 10); + stop_cheat_caller_address(erc20.contract_address); + + // Check contract balance before deposit + let contract_balance_before = erc20.balance_of(contract.contract_address); // Deposit the prize into the prize pool of an audition start_cheat_caller_address(contract.contract_address, OWNER()); - contract.deposit_prize(audition_id, mock_token_dispatcher.contract_address, 10); + contract.deposit_prize(audition_id, erc20.contract_address, 10); stop_cheat_caller_address(contract.contract_address); + // Check contract balance after deposit + let contract_balance_after = erc20.balance_of(contract.contract_address); + assert!( + contract_balance_after == contract_balance_before + 10, + "Contract balance did not increase after deposit", + ); + + // then submit evaluation for each performer + start_cheat_caller_address(contract.contract_address, judge_address1); + contract.submit_evaluation(audition_id, performer_id1, (4, 7, 3)); + contract.submit_evaluation(audition_id, performer_id2, (6, 7, 8)); + stop_cheat_caller_address(contract.contract_address); + + start_cheat_caller_address(contract.contract_address, judge_address2); + contract.submit_evaluation(audition_id, performer_id1, (4, 9, 2)); + contract.submit_evaluation(audition_id, performer_id2, (4, 9, 6)); + stop_cheat_caller_address(contract.contract_address); + + // move the timestamp to the end of the audition + start_cheat_block_timestamp(contract.contract_address, initial_timestamp + 1675123200); + + // then perform aggregate score calculation + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.perform_aggregate_score_calculation(audition_id); + stop_cheat_caller_address(contract.contract_address); + + // Approve contract to spend tokens + start_cheat_caller_address(erc20.contract_address, OWNER()); + erc20.approve(contract.contract_address, 10); + stop_cheat_caller_address(erc20.contract_address); + // Cheat: transfer all tokens from contract to a random address, draining contract balance - let random_address = contract_address_const::<9999>(); - let contract_balance = mock_token_dispatcher.balance_of(contract.contract_address); + let random_address: ContractAddress = 9999.try_into().unwrap(); + let contract_balance = erc20.balance_of(contract.contract_address); if contract_balance > 0 { - start_cheat_caller_address( - mock_token_dispatcher.contract_address, contract.contract_address, - ); - mock_token_dispatcher.transfer(random_address, contract_balance); - stop_cheat_caller_address(mock_token_dispatcher.contract_address); + start_cheat_caller_address(erc20.contract_address, contract.contract_address); + erc20.transfer(random_address, contract_balance); + stop_cheat_caller_address(erc20.contract_address); } // Prepare for distribution start_cheat_caller_address(contract.contract_address, OWNER()); - contract.update_audition_details(audition_id, Some(1672617600), None, None); - contract.end_audition(audition_id); - - let winner1 = contract_address_const::<1111>(); - let winner2 = contract_address_const::<2222>(); - let winner3 = contract_address_const::<3333>(); - // This should panic because contract has no balance to distribute - contract.distribute_prize(audition_id, [winner1, winner2, winner3], [50, 30, 20]); + contract.distribute_prize(audition_id, array![70, 30]); stop_cheat_block_timestamp(contract.contract_address); stop_cheat_caller_address(contract.contract_address); @@ -1207,9 +1351,6 @@ fn test_update_audition() { // Define audition ID and season ID let audition_id: u256 = 1; - let season_id: u256 = 1; - - // Create default audition // Start prank to simulate the owner calling the contract start_cheat_caller_address(contract.contract_address, OWNER()); @@ -1254,14 +1395,11 @@ fn test_update_audition() { #[should_panic(expected: 'Season is paused')] fn test_update_audition_should_panic_if_season_is_paused() { let (contract, _, _) = deploy_contract(); - let mut spy = spy_events(); // Define audition ID and season ID let audition_id: u256 = 1; let season_id: u256 = 1; - // Create default audition - // Start prank to simulate the owner calling the contract start_cheat_caller_address(contract.contract_address, OWNER()); default_contract_create_season(contract); @@ -1283,8 +1421,6 @@ fn test_all_crud_operations() { let season_id: u256 = 1; let audition_id: u256 = 1; - // Create default season and audition - // Start prank to simulate the owner calling the contract start_cheat_caller_address(contract.contract_address, OWNER()); @@ -1294,8 +1430,6 @@ fn test_all_crud_operations() { // READ Season let read_season = contract.read_season(season_id); - println!("Default season is {}", false); - assert!(read_season.season_id == season_id, "Failed to read season"); // UPDATE Season @@ -1338,7 +1472,7 @@ fn test_safe_painc_only_owner_can_call_functions() { // Attempt to create a season match safe_dispatcher.create_season('Lfggg', 1672531200, 1675123200) { Result::Ok(_) => panic!("Expected panic, but got success"), - Result::Err(e) => assert(*e.at(0) == 'Caller is not the owner', *e.at(0)), + Result::Err(e) => assert(*e.at(0) == 'Caller is missing role', *e.at(0)), } } @@ -1346,12 +1480,10 @@ fn test_safe_painc_only_owner_can_call_functions() { #[test] fn test_pause_audition() { let (contract, _, _) = deploy_contract(); + let mut spy = spy_events(); // Define audition ID and season ID let audition_id: u256 = 1; - let season_id: u256 = 1; - - // Create default audition // Start prank to simulate the owner calling the contract start_cheat_caller_address(contract.contract_address, OWNER()); @@ -1360,49 +1492,12 @@ fn test_pause_audition() { contract.create_audition('Summer Hits', Genre::Pop, 1675123200); // UPDATE Audition - contract.update_audition_details(audition_id, Some(1672617600), None, None); stop_cheat_caller_address(contract.contract_address); - // Pause audition - start_cheat_caller_address(contract.contract_address, OWNER()); - contract.pause_audition(audition_id); - - // check that the audition is paused - let is_audition_paused = contract.read_audition(audition_id); - - assert(is_audition_paused.paused, 'Audition is stil not paused'); - - stop_cheat_caller_address(contract.contract_address); -} - - -#[test] -fn test_emission_of_event_for_pause_audition() { - let (contract, _, _) = deploy_contract(); - let mut spy = spy_events(); - - // Define audition ID and season ID - let audition_id: u256 = 1; - let season_id: u256 = 1; - - // Create default audition - - // Start prank to simulate the owner calling the contract - start_cheat_caller_address(contract.contract_address, OWNER()); - default_contract_create_season(contract); - // CREATE Audition - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - - contract.update_audition_details(audition_id, Some(1672617600), None, None); - - // Pause audition - contract.pause_audition(audition_id); - - // check that the audition is paused - let is_audition_paused = contract.read_audition(audition_id); - - assert(is_audition_paused.paused, 'Audition is stil not paused'); + // Pause audition + start_cheat_caller_address(contract.contract_address, OWNER()); + contract.pause_audition(audition_id); spy .assert_emitted( @@ -1416,20 +1511,21 @@ fn test_emission_of_event_for_pause_audition() { ], ); + // check that the audition is paused + let is_audition_paused = contract.read_audition(audition_id); + + assert(is_audition_paused.paused, 'Audition is stil not paused'); + stop_cheat_caller_address(contract.contract_address); } - #[test] -#[should_panic(expected: 'Caller is not the owner')] +#[should_panic(expected: 'Caller is missing role')] fn test_pause_audition_as_non_owner() { let (contract, _, _) = deploy_contract(); // Define audition ID and season ID let audition_id: u256 = 1; - let season_id: u256 = 1; - - // Create default audition // Start prank to simulate the owner calling the contract start_cheat_caller_address(contract.contract_address, OWNER()); @@ -1461,9 +1557,6 @@ fn test_pause_audition_twice_should_fail() { // Define audition ID and season ID let audition_id: u256 = 1; - let season_id: u256 = 1; - - // Create default audition // Start prank to simulate the owner calling the contract start_cheat_caller_address(contract.contract_address, OWNER()); @@ -1497,7 +1590,7 @@ fn test_pause_audition_twice_should_fail() { #[test] fn test_resume_audition() { let (contract, _, _) = deploy_contract(); - + let mut spy = spy_events(); // Define audition ID and season ID let audition_id: u256 = 1; @@ -1524,6 +1617,18 @@ fn test_resume_audition() { start_cheat_caller_address(contract.contract_address, OWNER()); contract.resume_audition(audition_id); + spy + .assert_emitted( + @array![ + ( + contract.contract_address, + SeasonAndAudition::Event::AuditionResumed( + AuditionResumed { audition_id: audition_id, end_timestamp: 1672617600 }, + ), + ), + ], + ); + //check that contract is no longer paused let is_audition_pausedv2 = contract.read_audition(audition_id); assert(!is_audition_pausedv2.paused, 'Audition is still paused'); @@ -1533,13 +1638,12 @@ fn test_resume_audition() { #[test] -#[should_panic(expected: 'Caller is not the owner')] +#[should_panic(expected: 'Caller is missing role')] fn test_attempt_resume_audition_as_non_owner() { let (contract, _, _) = deploy_contract(); // Define audition ID and season ID let audition_id: u256 = 1; - let season_id: u256 = 1; // Create default season default_contract_create_season(contract); @@ -1575,64 +1679,12 @@ fn test_attempt_resume_audition_as_non_owner() { #[test] -fn test_emission_of_event_for_resume_audition() { +fn test_end_audition() { let (contract, _, _) = deploy_contract(); let mut spy = spy_events(); - // Define audition ID and season ID - let audition_id: u256 = 1; - let season_id: u256 = 1; - - // Create default audition - - // Start prank to simulate the owner calling the contract - start_cheat_caller_address(contract.contract_address, OWNER()); - default_contract_create_season(contract); - // CREATE Audition - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - - contract.update_audition_details(audition_id, Some(1672617600), None, None); - stop_cheat_caller_address(contract.contract_address); - - // Pause audition - start_cheat_caller_address(contract.contract_address, OWNER()); - contract.pause_audition(audition_id); - - // check that the audition is paused - let is_audition_paused = contract.read_audition(audition_id); - assert(is_audition_paused.paused, 'Audition is stil not paused'); - - //resume_audition - start_cheat_caller_address(contract.contract_address, OWNER()); - contract.resume_audition(audition_id); - - spy - .assert_emitted( - @array![ - ( - contract.contract_address, - SeasonAndAudition::Event::AuditionResumed( - AuditionResumed { audition_id: audition_id, end_timestamp: 1672617600 }, - ), - ), - ], - ); - - //check that contract is no longer paused - let is_audition_pausedv2 = contract.read_audition(audition_id); - assert(!is_audition_pausedv2.paused, 'Audition is still paused'); - - stop_cheat_caller_address(contract.contract_address); -} - - -#[test] -fn test_end_audition() { - let (contract, _, _) = deploy_contract(); - let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); @@ -1659,7 +1711,17 @@ fn test_end_audition() { // End the audition let end_result = contract.end_audition(audition_id); assert(end_result, 'End audition should succeed'); - + spy + .assert_emitted( + @array![ + ( + contract.contract_address, + SeasonAndAudition::Event::AuditionEnded( + AuditionEnded { audition_id: audition_id, end_timestamp: 1672531200 }, + ), + ), + ], + ); // Check that audition has ended properly let audition_has_ended = contract.read_audition(audition_id); assert(contract.is_audition_ended(audition_id), 'Audition should be ended'); @@ -1675,12 +1737,11 @@ fn test_end_audition() { } #[test] -#[should_panic(expect: 'Caller is not the owner')] +#[should_panic(expect: 'Caller is missing role')] fn test_end_audition_as_non_owner() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); @@ -1704,110 +1765,38 @@ fn test_end_audition_as_non_owner() { } #[test] -fn test_emission_of_event_for_end_audition() { +fn test_add_judge() { let (contract, _, _) = deploy_contract(); + let mut spy = spy_events(); + let audition_id: u256 = 1; + start_cheat_caller_address(contract.contract_address, OWNER()); + + // Set timestamp let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); + default_contract_create_season(contract); + // CREATE Audition contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - stop_cheat_block_timestamp(contract.contract_address); - start_cheat_block_timestamp(contract.contract_address, 1672617601); - let end_result = contract.end_audition(audition_id); - assert(end_result, 'End audition should succeed'); - // Check that audition has ended properly - let audition_has_ended = contract.read_audition(audition_id); - assert(contract.is_audition_ended(audition_id), 'Audition should be ended'); - assert(audition_has_ended.end_timestamp != 0, 'End timestamp should be set'); + // Add judge + let judge_address: ContractAddress = 0x123.try_into().unwrap(); + contract.add_judge(audition_id, judge_address); - // Check event emission spy .assert_emitted( @array![ ( contract.contract_address, - SeasonAndAudition::Event::AuditionEnded( - AuditionEnded { audition_id: audition_id, end_timestamp: 1672617601 }, - ), + SeasonAndAudition::Event::JudgeAdded(JudgeAdded { audition_id, judge_address }), ), ], ); - stop_cheat_block_timestamp(contract.contract_address); - stop_cheat_caller_address(contract.contract_address); -} - - -#[test] -fn test_end_audition_functionality() { - let (contract, _, _) = deploy_contract(); - - let audition_id: u256 = 1; - let season_id: u256 = 1; - - start_cheat_caller_address(contract.contract_address, OWNER()); - - // Set timestamp - let initial_timestamp: u64 = 1672531200; - start_cheat_block_timestamp(contract.contract_address, initial_timestamp); - - default_contract_create_season(contract); - // CREATE Audition - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - - contract.update_audition_details(audition_id, Some(1672617600), None, None); - - // Verify audition is not ended initially - assert(!contract.is_audition_ended(audition_id), 'Should not be ended initially'); - - // End the audition - let end_result = contract.end_audition(audition_id); - assert(end_result, 'End audition should succeed'); - - // Check state after ending - let audition_after_end = contract.read_audition(audition_id); - - // check that the audition has ended - assert(contract.is_audition_ended(audition_id), 'Audition should be ended'); - - assert(contract.is_audition_ended(audition_id), 'Audition should be ended'); - assert(audition_after_end.end_timestamp != 0, 'End timestamp should be set'); - assert(audition_after_end.end_timestamp != 1672617600, 'Should not be original end time'); - assert(audition_after_end.end_timestamp != 0, 'End timestamp should not be 0'); - - println!("All tests passed!"); - - stop_cheat_block_timestamp(contract.contract_address); - stop_cheat_caller_address(contract.contract_address); -} - - -#[test] -fn test_add_judge() { - let (contract, _, _) = deploy_contract(); - - let audition_id: u256 = 1; - let season_id: u256 = 1; - - start_cheat_caller_address(contract.contract_address, OWNER()); - - // Set timestamp - let initial_timestamp: u64 = 1672531200; - start_cheat_block_timestamp(contract.contract_address, initial_timestamp); - - default_contract_create_season(contract); - - // CREATE Audition - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - - // Add judge - let judge_address = contract_address_const::<0x123>(); - contract.add_judge(audition_id, judge_address); - // Check that the judge has been added let judges = contract.get_judges(audition_id); assert(judges.len() == 1, 'Judge should be added'); @@ -1839,7 +1828,7 @@ fn test_add_judge_should_panic_if_season_paused() { contract.pause_season(season_id); // Add judge - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); } @@ -1848,7 +1837,6 @@ fn test_add_multiple_judge() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); @@ -1863,14 +1851,14 @@ fn test_add_multiple_judge() { let mut judges = contract.get_judges(audition_id); assert(judges.len() == 0, 'Judge should be empty'); // Add judge - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); judges = contract.get_judges(audition_id); assert(judges.len() == 1, 'Judge should be added'); assert(*judges.at(0) == judge_address, 'Judge should be added'); - let judge_address2 = contract_address_const::<0x124>(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); contract.add_judge(audition_id, judge_address2); judges = contract.get_judges(audition_id); @@ -1878,7 +1866,7 @@ fn test_add_multiple_judge() { assert(*judges.at(0) == judge_address, 'Judge should be added'); assert(*judges.at(1) == judge_address2, 'Judge should be added'); - let judge_address3 = contract_address_const::<0x125>(); + let judge_address3: ContractAddress = 0x125.try_into().unwrap(); contract.add_judge(audition_id, judge_address3); judges = contract.get_judges(audition_id); @@ -1893,11 +1881,10 @@ fn test_add_multiple_judge() { #[test] -#[should_panic(expected: 'Caller is not the owner')] +#[should_panic(expected: 'Caller is missing role')] fn test_add_judges_should_panic_if_non_owner() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); @@ -1907,7 +1894,7 @@ fn test_add_judges_should_panic_if_non_owner() { stop_cheat_block_timestamp(contract.contract_address); start_cheat_caller_address(contract.contract_address, USER()); - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); stop_cheat_block_timestamp(contract.contract_address); } @@ -1917,7 +1904,6 @@ fn test_add_judges_should_panic_if_non_owner() { fn test_add_judges_should_panic_if_contract_paused() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); @@ -1927,7 +1913,7 @@ fn test_add_judges_should_panic_if_contract_paused() { contract.pause_all(); - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); stop_cheat_block_timestamp(contract.contract_address); } @@ -1938,7 +1924,7 @@ fn test_add_judges_should_panic_if_audition_does_not_exist() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); stop_cheat_block_timestamp(contract.contract_address); } @@ -1948,7 +1934,6 @@ fn test_add_judges_should_panic_if_audition_does_not_exist() { fn test_add_judges_should_panic_if_audition_has_ended() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); @@ -1958,7 +1943,7 @@ fn test_add_judges_should_panic_if_audition_has_ended() { stop_cheat_block_timestamp(contract.contract_address); start_cheat_block_timestamp(contract.contract_address, initial_timestamp + 1675123200 + 10); - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); stop_cheat_caller_address(contract.contract_address); } @@ -1970,7 +1955,6 @@ fn test_add_judges_should_panic_if_judge_already_added() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); @@ -1985,7 +1969,7 @@ fn test_add_judges_should_panic_if_judge_already_added() { let mut judges = contract.get_judges(audition_id); assert(judges.len() == 0, 'Judge should be empty'); // Add judge - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); judges = contract.get_judges(audition_id); @@ -2001,31 +1985,40 @@ fn test_add_judges_should_panic_if_judge_already_added() { #[test] fn test_remove_judge() { let (contract, _, _) = deploy_contract(); + let mut spy = spy_events(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); default_contract_create_season(contract); contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let judge_address = contract_address_const::<0x1777723>(); + let judge_address: ContractAddress = 0x1777723.try_into().unwrap(); contract.add_judge(audition_id, judge_address); let judges = contract.get_judges(audition_id); assert(judges.len() == 1, 'Judge should be added'); assert(*judges.at(0) == judge_address, 'Judge should be added'); - let judge_address2 = contract_address_const::<0x1777724>(); + let judge_address2: ContractAddress = 0x1777724.try_into().unwrap(); contract.add_judge(audition_id, judge_address2); let judges = contract.get_judges(audition_id); assert(judges.len() == 2, 'Second judge should be added'); assert(*judges.at(1) == judge_address2, 'judge address dont match'); - // print the judges - println!("judges: {:?}", judges); - contract.remove_judge(audition_id, judge_address); + spy + .assert_emitted( + @array![ + ( + contract.contract_address, + SeasonAndAudition::Event::JudgeRemoved( + JudgeRemoved { audition_id, judge_address }, + ), + ), + ], + ); + let judges = contract.get_judges(audition_id); assert(judges.len() == 1, 'Judge should be removed'); println!("judges: {:?}", judges); @@ -2050,21 +2043,18 @@ fn test_remove_judge_should_panic_if_season_paused() { default_contract_create_season(contract); contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let judge_address = contract_address_const::<0x1777723>(); + let judge_address: ContractAddress = 0x1777723.try_into().unwrap(); contract.add_judge(audition_id, judge_address); let judges = contract.get_judges(audition_id); assert(judges.len() == 1, 'Judge should be added'); assert(*judges.at(0) == judge_address, 'Judge should be added'); - let judge_address2 = contract_address_const::<0x1777724>(); + let judge_address2: ContractAddress = 0x1777724.try_into().unwrap(); contract.add_judge(audition_id, judge_address2); let judges = contract.get_judges(audition_id); assert(judges.len() == 2, 'Second judge should be added'); assert(*judges.at(1) == judge_address2, 'judge address dont match'); - // print the judges - println!("judges: {:?}", judges); - contract.pause_season(season_id); contract.remove_judge(audition_id, judge_address); @@ -2078,7 +2068,6 @@ fn test_judge_remove_can_remove_and_add_multiple_judges() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); @@ -2086,13 +2075,13 @@ fn test_judge_remove_can_remove_and_add_multiple_judges() { default_contract_create_season(contract); contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let judge_address = contract_address_const::<0x1777723>(); + let judge_address: ContractAddress = 0x1777723.try_into().unwrap(); contract.add_judge(audition_id, judge_address); let judges = contract.get_judges(audition_id); assert(judges.len() == 1, 'Judge should be added'); assert(*judges.at(0) == judge_address, 'Judge should be added'); - let judge_address2 = contract_address_const::<0x1777724>(); + let judge_address2: ContractAddress = 0x1777724.try_into().unwrap(); contract.add_judge(audition_id, judge_address2); let judges = contract.get_judges(audition_id); assert(judges.len() == 2, 'Second judge should be added'); @@ -2101,12 +2090,11 @@ fn test_judge_remove_can_remove_and_add_multiple_judges() { contract.remove_judge(audition_id, judge_address); let judges = contract.get_judges(audition_id); assert(judges.len() == 1, 'Judge should be removed'); - println!("judges: {:?}", judges); assert(*judges.at(0) == judge_address2, 'Incorrect Judge removed'); // Add two more judges - let judge_address3 = contract_address_const::<0x1777725>(); - let judge_address4 = contract_address_const::<0x1777726>(); + let judge_address3: ContractAddress = 0x1777725.try_into().unwrap(); + let judge_address4: ContractAddress = 0x1777726.try_into().unwrap(); contract.add_judge(audition_id, judge_address3); contract.add_judge(audition_id, judge_address4); @@ -2124,9 +2112,9 @@ fn test_judge_remove_can_remove_and_add_multiple_judges() { assert(*judges.at(1) == judge_address4, 'judge4 pos1'); // Add three more judges - let judge_address5 = contract_address_const::<0x1777727>(); - let judge_address6 = contract_address_const::<0x1777728>(); - let judge_address7 = contract_address_const::<0x1777729>(); + let judge_address5: ContractAddress = 0x1777727.try_into().unwrap(); + let judge_address6: ContractAddress = 0x1777728.try_into().unwrap(); + let judge_address7: ContractAddress = 0x1777729.try_into().unwrap(); contract.add_judge(audition_id, judge_address5); contract.add_judge(audition_id, judge_address6); contract.add_judge(audition_id, judge_address7); @@ -2148,7 +2136,6 @@ fn test_judge_remove_can_remove_and_add_multiple_judges() { fn test_judge_remove_should_panic_if_contract_paused() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); @@ -2158,7 +2145,7 @@ fn test_judge_remove_should_panic_if_contract_paused() { contract.create_audition('Summer Hits', Genre::Pop, 1675123200); // Add a judge - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); // Pause the contract @@ -2178,7 +2165,7 @@ fn test_remove_judge_should_panic_if_audition_doesnt_exist() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.remove_judge(audition_id, judge_address); stop_cheat_block_timestamp(contract.contract_address); stop_cheat_caller_address(contract.contract_address); @@ -2189,7 +2176,6 @@ fn test_remove_judge_should_panic_if_audition_doesnt_exist() { fn test_remove_judge_should_panic_if_audition_has_ended() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); @@ -2199,7 +2185,7 @@ fn test_remove_judge_should_panic_if_audition_has_ended() { contract.create_audition('Summer Hits', Genre::Pop, 1675123200); // Add a judge - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); // Move time past the audition's end @@ -2217,7 +2203,6 @@ fn test_remove_judge_should_panic_if_audition_has_ended() { fn test_remove_judge_should_panic_if_judge_not_found() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); @@ -2227,7 +2212,7 @@ fn test_remove_judge_should_panic_if_judge_not_found() { contract.create_audition('Summer Hits', Genre::Pop, 1675123200); // Try to remove a judge that was never added (should panic) - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.remove_judge(audition_id, judge_address); stop_cheat_block_timestamp(contract.contract_address); @@ -2238,7 +2223,6 @@ fn test_remove_judge_should_panic_if_judge_not_found() { fn test_get_judges_returns_expected_judges() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); @@ -2248,9 +2232,9 @@ fn test_get_judges_returns_expected_judges() { contract.create_audition('Summer Hits', Genre::Pop, 1675123200); // Add judges - let judge1 = contract_address_const::<0x111>(); - let judge2 = contract_address_const::<0x222>(); - let judge3 = contract_address_const::<0x333>(); + let judge1: ContractAddress = 0x111.try_into().unwrap(); + let judge2: ContractAddress = 0x222.try_into().unwrap(); + let judge3: ContractAddress = 0x333.try_into().unwrap(); contract.add_judge(audition_id, judge1); contract.add_judge(audition_id, judge2); contract.add_judge(audition_id, judge3); @@ -2283,11 +2267,11 @@ fn test_submit_evaluation_success() { let mut judges = contract.get_judges(audition_id); assert(judges.len() == 0, 'Judge should be empty'); - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); - let judge_address2 = contract_address_const::<0x124>(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); contract.add_judge(audition_id, judge_address2); - let judge_address3 = contract_address_const::<0x125>(); + let judge_address3: ContractAddress = 0x125.try_into().unwrap(); contract.add_judge(audition_id, judge_address3); stop_cheat_block_timestamp(contract.contract_address); stop_cheat_caller_address(contract.contract_address); @@ -2327,11 +2311,11 @@ fn test_submit_evaluation_should_panic_if_season_paused() { let mut judges = contract.get_judges(audition_id); assert(judges.len() == 0, 'Judge should be empty'); - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); - let judge_address2 = contract_address_const::<0x124>(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); contract.add_judge(audition_id, judge_address2); - let judge_address3 = contract_address_const::<0x125>(); + let judge_address3: ContractAddress = 0x125.try_into().unwrap(); contract.add_judge(audition_id, judge_address3); stop_cheat_block_timestamp(contract.contract_address); stop_cheat_caller_address(contract.contract_address); @@ -2359,9 +2343,9 @@ fn test_multiple_judges_submit_evaluation_for_same_performer() { start_cheat_block_timestamp(contract.contract_address, initial_timestamp); // Add multiple judges - let judge_address1 = contract_address_const::<0x111>(); - let judge_address2 = contract_address_const::<0x112>(); - let judge_address3 = contract_address_const::<0x113>(); + let judge_address1: ContractAddress = 0x111.try_into().unwrap(); + let judge_address2: ContractAddress = 0x112.try_into().unwrap(); + let judge_address3: ContractAddress = 0x113.try_into().unwrap(); contract.add_judge(audition_id, judge_address1); contract.add_judge(audition_id, judge_address2); contract.add_judge(audition_id, judge_address3); @@ -2419,15 +2403,11 @@ fn test_multiple_judges_submit_evaluation_for_diffrent_performers() { // Set timestamp let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); - // default_contract_create_season(contract); - - // // CREATE Audition - // contract.create_audition('Summer Hits', Genre::Pop, 1675123200); // Add multiple judges - let judge_address1 = contract_address_const::<0x211>(); - let judge_address2 = contract_address_const::<0x212>(); - let judge_address3 = contract_address_const::<0x213>(); + let judge_address1: ContractAddress = 0x211.try_into().unwrap(); + let judge_address2: ContractAddress = 0x212.try_into().unwrap(); + let judge_address3: ContractAddress = 0x213.try_into().unwrap(); contract.add_judge(audition_id, judge_address1); contract.add_judge(audition_id, judge_address2); contract.add_judge(audition_id, judge_address3); @@ -2453,6 +2433,17 @@ fn test_multiple_judges_submit_evaluation_for_diffrent_performers() { contract.submit_evaluation(audition_id, performer_id3, (7, 8, 9)); stop_cheat_caller_address(contract.contract_address); + // spy.assert_emitted( + // @array![( + // contract.contract_address, + // SeasonAndAudition::Event::EvaluationSubmitted ( + // EvaluationSubmitted { + // audition_id, performer_id: performer_id3, criteria: (7, 8, 9) + // } + // ) + // )] + // ); + // Get and check evaluation for performer 1 let evals1 = contract.get_evaluation(audition_id, performer_id1); assert(evals1.len() == 1, 'evals1 count fail'); @@ -2516,7 +2507,6 @@ fn test_submit_evaluation_should_panic_when_judging_is_paused() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); @@ -2528,7 +2518,7 @@ fn test_submit_evaluation_should_panic_when_judging_is_paused() { // CREATE Audition contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); stop_cheat_block_timestamp(contract.contract_address); @@ -2549,9 +2539,6 @@ fn test_submit_evaluation_should_panic_when_judging_is_paused() { fn test_pause_judging_success() { let (contract, _, _) = deploy_contract(); - let audition_id: u256 = 1; - let season_id: u256 = 1; - start_cheat_caller_address(contract.contract_address, OWNER()); // Set timestamp @@ -2579,9 +2566,6 @@ fn test_pause_judging_success() { fn test_resume_judging_success() { let (contract, _, _) = deploy_contract(); - let audition_id: u256 = 1; - let season_id: u256 = 1; - start_cheat_caller_address(contract.contract_address, OWNER()); // Set timestamp @@ -2615,13 +2599,10 @@ fn test_resume_judging_success() { } #[test] -#[should_panic(expected: 'Caller is not the owner')] +#[should_panic(expected: 'Caller is missing role')] fn test_pause_judging_should_panic_when_caller_is_not_owner() { let (contract, _, _) = deploy_contract(); - let audition_id: u256 = 1; - let season_id: u256 = 1; - start_cheat_caller_address(contract.contract_address, OWNER()); // Set timestamp @@ -2643,13 +2624,10 @@ fn test_pause_judging_should_panic_when_caller_is_not_owner() { #[test] -#[should_panic(expected: 'Caller is not the owner')] +#[should_panic(expected: 'Caller is missing role')] fn test_resume_judging_should_panic_when_caller_is_not_owner() { let (contract, _, _) = deploy_contract(); - let audition_id: u256 = 1; - let season_id: u256 = 1; - start_cheat_caller_address(contract.contract_address, OWNER()); // Set timestamp @@ -2683,7 +2661,6 @@ fn test_set_weight_for_audition_success() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); @@ -2695,7 +2672,7 @@ fn test_set_weight_for_audition_success() { // CREATE Audition contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); contract.set_evaluation_weight(audition_id, (10, 60, 30)); @@ -2725,7 +2702,7 @@ fn test_set_weight_for_audition_should_panic_if_season_paused() { // CREATE Audition contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); contract.pause_season(season_id); @@ -2740,7 +2717,6 @@ fn test_set_weight_for_audition_should_panic_if_weight_doest_add_up_to_100() { let (contract, _, _) = deploy_contract(); let audition_id: u256 = 1; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); default_contract_create_season(contract); @@ -2751,7 +2727,7 @@ fn test_set_weight_for_audition_should_panic_if_weight_doest_add_up_to_100() { // CREATE Audition contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - let judge_address = contract_address_const::<0x123>(); + let judge_address: ContractAddress = 0x123.try_into().unwrap(); contract.add_judge(audition_id, judge_address); contract.set_evaluation_weight(audition_id, (4, 60, 30)); @@ -2763,34 +2739,29 @@ fn test_set_weight_for_audition_should_panic_if_weight_doest_add_up_to_100() { #[test] fn test_perform_aggregate_score_calculation_successful() { - let (contract, _, _) = deploy_contract(); - let audition_id: u256 = 1; - let season_id: u256 = 1; - + let (contract, erc20) = feign_update_config(OWNER(), audition_id, 100); + let artists: Array<(ContractAddress, u256)> = feign_artists_registration( + 2, erc20, 100, contract, + ); + let mut spy = spy_events(); start_cheat_caller_address(contract.contract_address, OWNER()); // Set timestamp let initial_timestamp: u64 = 1672531200; start_cheat_block_timestamp(contract.contract_address, initial_timestamp); - contract.create_season('Lfggg', 1672531200, 1675123200); - - // CREATE Audition - contract.create_audition('Summer Hits', Genre::Pop, 1675123200); - // then add 2 judges - let judge_address1 = contract_address_const::<0x123>(); - let judge_address2 = contract_address_const::<0x124>(); + let judge_address1: ContractAddress = 0x123.try_into().unwrap(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); contract.add_judge(audition_id, judge_address1); contract.add_judge(audition_id, judge_address2); stop_cheat_block_timestamp(contract.contract_address); stop_cheat_caller_address(contract.contract_address); - // then register 2 performers - let performer_id1 = 'performerA'; - let performer_id2 = 'performerB'; + let (_, performer_id1) = *artists.at(0); + let (_, performer_id2) = *artists.at(1); // then set weight start_cheat_caller_address(contract.contract_address, OWNER()); @@ -2802,6 +2773,7 @@ fn test_perform_aggregate_score_calculation_successful() { contract.submit_evaluation(audition_id, performer_id1, (4, 7, 3)); contract.submit_evaluation(audition_id, performer_id2, (6, 7, 8)); stop_cheat_caller_address(contract.contract_address); + start_cheat_caller_address(contract.contract_address, judge_address2); contract.submit_evaluation(audition_id, performer_id1, (4, 9, 2)); contract.submit_evaluation(audition_id, performer_id2, (4, 9, 6)); @@ -2818,16 +2790,29 @@ fn test_perform_aggregate_score_calculation_successful() { // get the aggregate score for each performer let aggregate_score1 = contract.get_aggregate_score_for_performer(audition_id, performer_id1); let aggregate_score2 = contract.get_aggregate_score_for_performer(audition_id, performer_id2); - println!("aggregate_score1: {:?}", aggregate_score1); // aggregate_score1: 4 - println!("aggregate_score2: {:?}", aggregate_score2); // aggregate_score2: 6 - // get the aggregate score for the audition - let aggregate_score = contract.get_aggregate_score(audition_id); - println!( - "aggregate_score: {:?}", aggregate_score, - ); // aggregate_score: [(530776410631550129238593, 4), (530776410631550129238594, 6)] + assert!(aggregate_score1 == 4, "Incorrect aggregated score"); + assert!(aggregate_score2 == 6, "Incorrect aggregated score"); + let aggregate_score = contract.get_aggregate_score(audition_id); + assert!( + *aggregate_score.at(0) == (performer_id1, 4) + && *aggregate_score.at(1) == (performer_id2, 6), + "Invalid aggregate scores", + ); stop_cheat_block_timestamp(contract.contract_address); + + spy + .assert_emitted( + @array![ + ( + contract.contract_address, + SeasonAndAudition::Event::AuditionCalculationCompleted( + AuditionCalculationCompleted { audition_id }, + ), + ), + ], + ) } @@ -2850,8 +2835,8 @@ fn test_perform_aggregate_score_calculation_should_panic_if_season_paused() { contract.create_audition('Summer Hits', Genre::Pop, 1675123200); // then add 2 judges - let judge_address1 = contract_address_const::<0x123>(); - let judge_address2 = contract_address_const::<0x124>(); + let judge_address1: ContractAddress = 0x123.try_into().unwrap(); + let judge_address2: ContractAddress = 0x124.try_into().unwrap(); contract.add_judge(audition_id, judge_address1); contract.add_judge(audition_id, judge_address2); @@ -2918,10 +2903,9 @@ fn test_pause_season_success() { #[test] -#[should_panic(expected: 'Caller is not the owner')] +#[should_panic(expected: 'Caller is missing role')] fn test_pause_season_should_panic_if_paused_by_non_owner() { let (contract, _, _) = deploy_contract(); - let mut spy = spy_events(); // Define season ID let season_id: u256 = 1; @@ -2940,7 +2924,6 @@ fn test_pause_season_should_panic_if_paused_by_non_owner() { #[should_panic(expected: 'Season is paused')] fn test_pause_season_should_panic_if_season_is_paused() { let (contract, _, _) = deploy_contract(); - let mut spy = spy_events(); // Define season ID let season_id: u256 = 1; @@ -2961,7 +2944,6 @@ fn test_pause_season_should_panic_if_season_is_paused() { #[should_panic(expected: 'Season does not exist')] fn test_pause_season_should_panic_if_season_doesnt_exist() { let (contract, _, _) = deploy_contract(); - let mut spy = spy_events(); // Define season ID let season_id: u256 = 1; @@ -2976,7 +2958,6 @@ fn test_pause_season_should_panic_if_season_doesnt_exist() { #[should_panic(expected: 'Season has already ended')] fn test_pause_season_should_panic_if_season_is_ended() { let (contract, _, _) = deploy_contract(); - let mut spy = spy_events(); // Define season ID let season_id: u256 = 1; @@ -3001,7 +2982,6 @@ fn test_pause_season_should_panic_if_season_is_ended() { #[test] fn test_resume_season_success() { let (contract, _, _) = deploy_contract(); - let mut spy = spy_events(); // Define season ID let season_id: u256 = 1; @@ -3029,10 +3009,9 @@ fn test_resume_season_success() { #[test] -#[should_panic(expected: 'Caller is not the owner')] +#[should_panic(expected: 'Caller is missing role')] fn test_resume_season_should_panic_if_non_owner() { let (contract, _, _) = deploy_contract(); - let mut spy = spy_events(); // Define season ID let season_id: u256 = 1; @@ -3078,7 +3057,6 @@ fn test_resume_season_should_panic_if_season_doesnt_exist() { #[should_panic(expected: 'Season is not paused')] fn test_resume_season_should_panic_if_season_is_not_paused() { let (contract, _, _) = deploy_contract(); - let mut spy = spy_events(); // Define season ID let season_id: u256 = 1; @@ -3129,7 +3107,7 @@ fn test_submit_result_success() { #[test] -#[should_panic(expected: 'Caller is not the owner')] +#[should_panic(expected: 'Caller is missing role')] fn test_submit_result_should_panic_if_non_owner() { let (contract, _, _) = deploy_contract(); diff --git a/contract_/tests/test_stake_to_vote.cairo b/contract_/tests/test_stake_to_vote.cairo index c00d6e7..5849c52 100644 --- a/contract_/tests/test_stake_to_vote.cairo +++ b/contract_/tests/test_stake_to_vote.cairo @@ -1,24 +1,19 @@ use contract_::audition::interfaces::iseason_and_audition::{ ISeasonAndAuditionDispatcher, ISeasonAndAuditionDispatcherTrait, - ISeasonAndAuditionSafeDispatcher, }; use contract_::audition::interfaces::istake_to_vote::{ IStakeToVoteDispatcher, IStakeToVoteDispatcherTrait, }; -use contract_::audition::types::season_and_audition::{Genre, Season}; -use contract_::events::{ - AuditionCreated, AuditionDeleted, AuditionEnded, AuditionPaused, AuditionResumed, - AuditionUpdated, PriceDeposited, PriceDistributed, SeasonCreated, SeasonDeleted, SeasonUpdated, -}; +use contract_::audition::types::season_and_audition::Genre; use core::num::traits::Zero; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use snforge_std::{ ContractClassTrait, DeclareResultTrait, declare, start_cheat_caller_address, stop_cheat_caller_address, }; -use starknet::{ContractAddress, get_block_timestamp}; +use starknet::ContractAddress; use crate::test_utils::{ - NON_OWNER, OWNER, USER, create_default_audition, create_default_season, + OWNER, USER, create_default_audition, create_default_season, deploy_contract as deploy_season_and_audition_contract, deploy_mock_erc20_contract, }; @@ -68,7 +63,7 @@ fn setup_staking_audition() -> ( .create_season( default_season.name, default_season.start_timestamp, default_season.end_timestamp, ); - let default_audition = create_default_audition(audition_id, season_id); + create_default_audition(audition_id, season_id); season_and_audition.create_audition('Summer Hits', Genre::Pop, 1675123200); stop_cheat_caller_address(season_and_audition.contract_address); diff --git a/contract_/tests/test_token_contract.cairo b/contract_/tests/test_token_contract.cairo deleted file mode 100644 index d784b4a..0000000 --- a/contract_/tests/test_token_contract.cairo +++ /dev/null @@ -1,431 +0,0 @@ -use contract_::erc20::{ - IBurnableDispatcher, IBurnableDispatcherTrait, IMusicShareTokenDispatcher, - IMusicShareTokenDispatcherTrait, -}; -use openzeppelin::access::ownable::interface::{IOwnableDispatcher, IOwnableDispatcherTrait}; -use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; -use openzeppelin::utils::serde::SerializedAppend; -use snforge_std::{CheatSpan, ContractClassTrait, DeclareResultTrait, cheat_caller_address, declare}; -use starknet::ContractAddress; - -fn owner() -> ContractAddress { - 'owner'.try_into().unwrap() -} - -fn zero() -> ContractAddress { - 0.try_into().unwrap() -} - -fn kim() -> ContractAddress { - 'kim'.try_into().unwrap() -} - -fn thurston() -> ContractAddress { - 'thurston'.try_into().unwrap() -} - -fn lee() -> ContractAddress { - 'lee'.try_into().unwrap() -} - -// Helper function to deploy the music share token contract -fn deploy_music_share_token() -> ContractAddress { - let owner = owner(); - let contractclass = declare("MusicStrk").unwrap().contract_class(); - let mut calldata = array![]; - calldata.append_serde(owner); - let (contractaddress, _) = contractclass.deploy(@calldata).unwrap(); - contractaddress -} - -#[test] -fn test_deployment() { - // Simple test just to check that the contract deploys successfully - let contractaddress = deploy_music_share_token(); - - // Simple check that deployed contract address is not zero - assert(contractaddress != zero(), 'Contract not deployed'); -} - -#[test] -fn test_initialize() { - // Setup - let recipient = kim(); - let contract_address = deploy_music_share_token(); - let token = IERC20Dispatcher { contract_address }; - let share_token = IMusicShareTokenDispatcher { contract_address }; - - // Initialize the token with minimal data - // Direct string literals should work in Cairo 2.9.4 - cheat_caller_address(contract_address, owner(), CheatSpan::TargetCalls(1)); - share_token.initialize(recipient, "ipfs://test", "RecordToken", "REC", 2 // decimals - ); - - // Verify total supply is exactly 100 tokens - assert(token.total_supply() == 100_u256, 'Wrong total supply'); - - // Verify recipient received 100 tokens - assert(token.balance_of(recipient) == 100_u256, 'Recipient balance wrong'); -} - -#[test] -fn test_burn() { - // Setup - let recipient = kim(); - let contract_address = deploy_music_share_token(); - let token = IERC20Dispatcher { contract_address }; - let burnable = IBurnableDispatcher { contract_address }; - - // Initialize the token - cheat_caller_address(contract_address, owner(), CheatSpan::TargetCalls(1)); - IMusicShareTokenDispatcher { contract_address } - .initialize(recipient, "ipfs://test", "RecordToken", "REC", 2 // decimals - ); - - // Verify initial balance - assert(token.balance_of(recipient) == 100_u256, 'Initial balance wrong'); - - // Burn 25 tokens - cheat_caller_address(contract_address, recipient, CheatSpan::TargetCalls(1)); - burnable.burn(25_u256); - - // Check balance after burning - assert(token.balance_of(recipient) == 75_u256, 'Balance after burn wrong'); - - // Check total supply decreased - assert(token.total_supply() == 75_u256, 'Total supply wrong after burn'); -} - -#[test] -fn test_transfer() { - // Setup - let from_address = kim(); - let to_address = thurston(); - let contract_address = deploy_music_share_token(); - let token = IERC20Dispatcher { contract_address }; - - // Initialize the token - cheat_caller_address(contract_address, owner(), CheatSpan::TargetCalls(1)); - IMusicShareTokenDispatcher { contract_address } - .initialize(from_address, "ipfs://test", "RecordToken", "REC", 2 // decimals - ); - - // Verify initial balances - assert(token.balance_of(from_address) == 100_u256, 'Initial from balance wrong'); - assert(token.balance_of(to_address) == 0_u256, 'Initial to balance wrong'); - - // Transfer 30 tokens from kim to thurston - cheat_caller_address(contract_address, from_address, CheatSpan::TargetCalls(1)); - token.transfer(to_address, 30_u256); - - // Check balances after transfer - assert(token.balance_of(from_address) == 70_u256, 'Final from balance wrong'); - assert(token.balance_of(to_address) == 30_u256, 'Final to balance wrong'); - - // Ensure total supply remains unchanged - assert(token.total_supply() == 100_u256, 'Total supply changed'); -} - -#[test] -fn test_approve_and_transfer_from() { - // Setup - let token_owner = kim(); - let spender = thurston(); - let recipient = lee(); - let contract_address = deploy_music_share_token(); - let token = IERC20Dispatcher { contract_address }; - - // Initialize the token - cheat_caller_address(contract_address, owner(), CheatSpan::TargetCalls(1)); - IMusicShareTokenDispatcher { contract_address } - .initialize(token_owner, "ipfs://test", "RecordToken", "REC", 2 // decimals - ); - - // Verify initial balances - assert(token.balance_of(token_owner) == 100_u256, 'Initial owner balance wrong'); - assert(token.balance_of(spender) == 0_u256, 'Initial spender balance wrong'); - assert(token.balance_of(recipient) == 0_u256, 'Initial recipient balance wrong'); - - // Approve spender to spend 50 tokens - cheat_caller_address(contract_address, token_owner, CheatSpan::TargetCalls(1)); - token.approve(spender, 50_u256); - - // Check allowance - assert(token.allowance(token_owner, spender) == 50_u256, 'Allowance not set correctly'); - - // Spender transfers 40 tokens from token_owner to recipient - cheat_caller_address(contract_address, spender, CheatSpan::TargetCalls(1)); - token.transfer_from(token_owner, recipient, 40_u256); - - // Check balances after transfer - assert(token.balance_of(token_owner) == 60_u256, 'Final owner balance wrong'); - assert(token.balance_of(recipient) == 40_u256, 'Final recipient balance wrong'); - - // Check remaining allowance - assert(token.allowance(token_owner, spender) == 10_u256, 'Remaining allowance wrong'); - - // Ensure total supply remains unchanged - assert(token.total_supply() == 100_u256, 'Total supply changed'); -} - -#[test] -fn test_metadata_uri() { - // Setup - let recipient = kim(); - let contract_address = deploy_music_share_token(); - let share_token = IMusicShareTokenDispatcher { contract_address }; - - // Initialize the token with metadata URI - let metadata_uri = "ipfs://QmSpecificCID"; - cheat_caller_address(contract_address, owner(), CheatSpan::TargetCalls(1)); - share_token.initialize(recipient, metadata_uri, "RecordToken", "REC", 2 // decimals - ); - - // Verify that the metadata URI is correctly set and retrievable - let retrieved_uri = share_token.get_metadata_uri(); - - // For Cairo 2.9.4, we'll verify the URI was set by checking if it exists - // We don't have a direct way to compare ByteArray values in the test environment - assert(retrieved_uri.len() > 0, 'Metadata URI not set'); -} - -#[test] -fn test_ownership_transfer() { - // Setup - let original_owner = owner(); - let new_owner = kim(); - let contract_address = deploy_music_share_token(); - let ownable = IOwnableDispatcher { contract_address }; - - // Verify initial owner - assert(ownable.owner() == original_owner, 'Initial owner incorrect'); - - // Transfer ownership from original owner to new owner - cheat_caller_address(contract_address, original_owner, CheatSpan::TargetCalls(1)); - ownable.transfer_ownership(new_owner); - - // Verify new owner - assert(ownable.owner() == new_owner, 'Ownership transfer failed'); -} - -#[test] -fn test_edge_cases() { - // Setup - let token_owner = kim(); - let spender = thurston(); - let contract_address = deploy_music_share_token(); - let token = IERC20Dispatcher { contract_address }; - let share_token = IMusicShareTokenDispatcher { contract_address }; - - // Initialize the token - cheat_caller_address(contract_address, owner(), CheatSpan::TargetCalls(1)); - share_token.initialize(token_owner, "ipfs://test", "RecordToken", "REC", 2 // decimals - ); - - // Case 1: Approve zero amount - cheat_caller_address(contract_address, token_owner, CheatSpan::TargetCalls(1)); - token.approve(spender, 0_u256); - - // Verify zero approval - assert(token.allowance(token_owner, spender) == 0_u256, 'Zero approval failed'); - - // Case 2: Transfer zero amount - let initial_balance = token.balance_of(token_owner); - cheat_caller_address(contract_address, token_owner, CheatSpan::TargetCalls(1)); - token.transfer(spender, 0_u256); - - // Verify balances didn't change - assert(token.balance_of(token_owner) == initial_balance, 'Balance changed'); - assert(token.balance_of(spender) == 0_u256, 'Received tokens'); -} - -#[test] -fn test_decimal_configuration() { - // Test with different decimal configurations - - // Test with 0 decimals - let decimal_0 = 0_u8; - let recipient = kim(); - let to_address = thurston(); - let contract_address = deploy_music_share_token(); - let token = IERC20Dispatcher { contract_address }; - let share_token = IMusicShareTokenDispatcher { contract_address }; - - // Initialize with 0 decimals - cheat_caller_address(contract_address, owner(), CheatSpan::TargetCalls(1)); - share_token.initialize(recipient, "ipfs://test", "RecordToken", "REC", decimal_0); - - // Verify the decimals setting was stored correctly - assert(share_token.get_decimals() == decimal_0, 'Decimals not set to 0'); - - // Verify initial balance - should be 100 tokens regardless of decimals - assert(token.balance_of(recipient) == 100_u256, 'Initial balance incorrect'); - - // Transfer a whole amount - let transfer_amount = 5_u256; - cheat_caller_address(contract_address, recipient, CheatSpan::TargetCalls(1)); - token.transfer(to_address, transfer_amount); - - // Verify transfer worked correctly - assert(token.balance_of(to_address) == transfer_amount, 'Transfer failed'); - assert(token.balance_of(recipient) == 100_u256 - transfer_amount, 'Sender balance wrong'); - - // Now test with a different decimal value - let decimal_2 = 2_u8; - let recipient2 = kim(); - let to_address2 = thurston(); - let contract_address2 = deploy_music_share_token(); - let token2 = IERC20Dispatcher { contract_address: contract_address2 }; - let share_token2 = IMusicShareTokenDispatcher { contract_address: contract_address2 }; - - // Initialize with 2 decimals - cheat_caller_address(contract_address2, owner(), CheatSpan::TargetCalls(1)); - share_token2.initialize(recipient2, "ipfs://test", "RecordToken", "REC", decimal_2); - - // Verify the decimals setting was stored correctly - assert(share_token2.get_decimals() == decimal_2, 'Decimals not set to 2'); - - // Verify initial balance - should be 100 tokens regardless of decimals - assert(token2.balance_of(recipient2) == 100_u256, 'Initial balance incorrect'); - - // Transfer an amount - let transfer_amount2 = 1_u256; - cheat_caller_address(contract_address2, recipient2, CheatSpan::TargetCalls(1)); - token2.transfer(to_address2, transfer_amount2); - - // Verify transfer worked correctly - assert(token2.balance_of(to_address2) == transfer_amount2, 'Transfer failed'); - assert(token2.balance_of(recipient2) == 100_u256 - transfer_amount2, 'Sender balance wrong'); - - // Additionally, test a high decimal value (18, which is standard for many tokens) - let decimal_18 = 18_u8; - let recipient3 = kim(); - let contract_address3 = deploy_music_share_token(); - let share_token3 = IMusicShareTokenDispatcher { contract_address: contract_address3 }; - - // Initialize with 18 decimals - cheat_caller_address(contract_address3, owner(), CheatSpan::TargetCalls(1)); - share_token3.initialize(recipient3, "ipfs://test", "RecordToken", "REC", decimal_18); - - // Verify the decimals setting was stored correctly - assert(share_token3.get_decimals() == decimal_18, 'Decimals not set to 18'); -} - -#[test] -#[should_panic(expect: 'Token already initialized')] -fn test_double_initialization() { - // Setup - let recipient = kim(); - let contract_address = deploy_music_share_token(); - let share_token = IMusicShareTokenDispatcher { contract_address }; - - // Initialize the token first time - cheat_caller_address(contract_address, owner(), CheatSpan::TargetCalls(1)); - share_token.initialize(recipient, "ipfs://test", "RecordToken", "REC", 2 // decimals - ); - - // Try to initialize again - should fail - cheat_caller_address(contract_address, owner(), CheatSpan::TargetCalls(1)); - share_token - .initialize(thurston(), "ipfs://test2", "RecordToken2", "REC2", 3 // different decimals - ); - // This should panic, but we don't specify the exact message since it might vary -} - -#[test] -fn test_6_decimal_precision() { - // Setup with 6 decimals (common for many stablecoins like USDC) - let decimal_6 = 6_u8; - let recipient = kim(); - let to_address = thurston(); - let third_party = lee(); - let contract_address = deploy_music_share_token(); - let token = IERC20Dispatcher { contract_address }; - let share_token = IMusicShareTokenDispatcher { contract_address }; - - // Initialize with 6 decimals - cheat_caller_address(contract_address, owner(), CheatSpan::TargetCalls(1)); - share_token.initialize(recipient, "ipfs://test", "StableCoin", "STBL", decimal_6); - - // Verify the decimals setting was stored correctly - assert(share_token.get_decimals() == decimal_6, 'Decimals not set'); - - // Verify initial balance - should be 100 tokens - // Note: In ERC20, token amounts are always in the base unit, not affected by decimals - // Decimals only indicate how to display the token - assert(token.balance_of(recipient) == 100_u256, 'Wrong init balance'); - - // Test multiple operations with reasonably sized transfers - - // First, make a small transfer - 0.5 tokens - let small_transfer = 5_u256; // Use small amounts for testing - - cheat_caller_address(contract_address, recipient, CheatSpan::TargetCalls(1)); - token.transfer(to_address, small_transfer); - - // Verify the transfer worked correctly - assert(token.balance_of(to_address) == small_transfer, 'Transfer failed'); - assert(token.balance_of(recipient) == 100_u256 - small_transfer, 'Wrong balance'); - - // Test approvals and transfer_from with 6 decimal token - let approval_amount = 10_u256; - - // Recipient approves third_party to spend tokens - cheat_caller_address(contract_address, recipient, CheatSpan::TargetCalls(1)); - token.approve(third_party, approval_amount); - - // Verify approval was set correctly - assert(token.allowance(recipient, third_party) == approval_amount, 'Wrong allowance'); - - // Third party transfers half of the approved amount - let transfer_from_amount = 5_u256; - cheat_caller_address(contract_address, third_party, CheatSpan::TargetCalls(1)); - token.transfer_from(recipient, third_party, transfer_from_amount); - - // Verify balances after transfer_from - assert(token.balance_of(third_party) == transfer_from_amount, 'Wrong balance'); - assert( - token.balance_of(recipient) == 100_u256 - small_transfer - transfer_from_amount, - 'Wrong balance after transfer', - ); - - // Verify allowance was reduced correctly - assert( - token.allowance(recipient, third_party) == approval_amount - transfer_from_amount, - 'Wrong allowance after transfer', - ); - - // Test burning with 6 decimal token - // Burn 2 tokens from to_address - let burn_amount = 2_u256; - - cheat_caller_address(contract_address, to_address, CheatSpan::TargetCalls(1)); - IBurnableDispatcher { contract_address }.burn(burn_amount); - - // Verify balance after burn - assert( - token.balance_of(to_address) == small_transfer - burn_amount, 'Wrong balance after burn', - ); - - // Verify total supply decreased by the correct amount - let expected_total_supply = 100_u256 - burn_amount; - assert(token.total_supply() == expected_total_supply, 'Wrong total supply'); - - // Test that 6 decimal precision is respected in the contract's storage - assert(share_token.get_decimals() == 6_u8, 'Wrong decimals'); -} - -#[test] -#[should_panic(expect: 'Caller is not the owner')] -fn test_authorization_failure() { - // Setup - deploy the contract but don't initialize yet - let contract_address = deploy_music_share_token(); - let share_token = IMusicShareTokenDispatcher { contract_address }; - - // Try to initialize the token as a non-owner (kim) - // This should fail with an authorization error - cheat_caller_address(contract_address, kim(), CheatSpan::TargetCalls(1)); - share_token.initialize(thurston(), "ipfs://test", "RecordToken", "REC", 6 // decimals - ); - // This should panic as kim is not the owner and cannot initialize the token -} diff --git a/contract_/tests/test_token_factory.cairo b/contract_/tests/test_token_factory.cairo index 25f71bd..31a6e9c 100644 --- a/contract_/tests/test_token_factory.cairo +++ b/contract_/tests/test_token_factory.cairo @@ -1,4 +1,4 @@ -use contract_::erc20::{IMusicShareTokenDispatcher, IMusicShareTokenDispatcherTrait, MusicStrk}; +use contract_::erc20::{IMusicShareTokenDispatcher, IMusicShareTokenDispatcherTrait}; use contract_::events::TokenDeployedEvent; use contract_::token_factory::{ IMusicShareTokenFactoryDispatcher, IMusicShareTokenFactoryDispatcherTrait, @@ -15,34 +15,9 @@ use snforge_std::{ }; use starknet::class_hash::ClassHash; use starknet::{ContractAddress, get_block_timestamp}; +use crate::test_utils::{MUSICSTRK_HASH, artist_1, artist_2, non_auth, owner, zero}; -// Address constants for testing -fn artist_1() -> ContractAddress { - 'artist_1'.try_into().unwrap() -} - -fn artist_2() -> ContractAddress { - 'artist_2'.try_into().unwrap() -} - -fn non_auth() -> ContractAddress { - 'non-auth'.try_into().unwrap() -} - -fn owner() -> ContractAddress { - 'owner'.try_into().unwrap() -} - -fn zero() -> ContractAddress { - 0.try_into().unwrap() -} - -// Constants -fn MUSICSTRK_HASH() -> ClassHash { - MusicStrk::TEST_CLASS_HASH.try_into().unwrap() -} - const TOTAL_SHARES: u256 = 100_u256; /// Helper function to setup token test data @@ -96,6 +71,8 @@ fn test_successful_music_share_token_deployment() { let artist_1 = artist_1(); let owner = owner(); + let mut event_spy = spy_events(); + // Deploy music share token factory as owner let (factory_address, factory_dispatcher) = deploy_music_share_token_factory(owner); @@ -120,43 +97,16 @@ fn test_successful_music_share_token_deployment() { // Verify token properties using ERC20 interface let erc20_token = IERC20MixinDispatcher { contract_address: token_address }; - assert(erc20_token.name() == name.into(), 'Name mismatch'); - assert(erc20_token.symbol() == symbol.into(), 'Symbol mismatch'); + assert(erc20_token.name() == name.clone().into(), 'Name mismatch'); + assert(erc20_token.symbol() == symbol.clone().into(), 'Symbol mismatch'); // Get token through the music token interface let music_token = IMusicShareTokenDispatcher { contract_address: token_address }; assert(music_token.get_decimals() == decimals, 'Decimals mismatch'); - assert(music_token.get_metadata_uri() == metadata_uri.into(), 'Metadata URI mismatch'); + assert(music_token.get_metadata_uri() == metadata_uri.clone().into(), 'Metadata URI mismatch'); // Verify 100 tokens were minted to the deployer assert(erc20_token.balance_of(artist_1.into()) == TOTAL_SHARES, 'Balance should be 100 tokens'); -} - -#[test] -fn test_deploy_music_share_token_event() { - // Setup test accounts from address constants - let owner = owner(); - let artist_1 = artist_1(); - - // Deploy music share token factory as owner - let (factory_address, factory_dispatcher) = deploy_music_share_token_factory(owner); - - // Grant artist role before token deployment - cheat_caller_address(factory_address, owner, CheatSpan::TargetCalls(1)); - factory_dispatcher.grant_artist_role(artist_1); - - // Setup test data - let (name, symbol, decimals, metadata_uri) = setup_token_data(); - - // Start calls as the deployer (artist) - cheat_caller_address(factory_address, artist_1, CheatSpan::TargetCalls(1)); - - // Spy on events - let mut event_spy = spy_events(); - - // Deploy a token through the factory - let token_address = factory_dispatcher - .deploy_music_token(name.clone(), symbol.clone(), decimals, metadata_uri.clone()); event_spy .assert_emitted( @@ -178,6 +128,20 @@ fn test_deploy_music_share_token_event() { ); } +#[test] +#[should_panic(expect: 'Result::unwrap failed.')] +fn test_deploy_factory_with_zero_owner() { + let (_, music_token_class_hash) = deploy_music_share_token(owner()); + + let factory_class = declare("MusicShareTokenFactory").unwrap().contract_class(); + let mut calldata = array![]; + + calldata.append(zero().into()); + calldata.append(music_token_class_hash.into()); + + let (_, _) = factory_class.deploy(@calldata).unwrap(); +} + #[test] fn test_multiple_tokens_per_artist() { // Setup test accounts from address constants @@ -272,53 +236,7 @@ fn test_multiple_artists() { assert(token2.balance_of(artist_2) == TOTAL_SHARES, 'Artist 2 should have 100 tokens'); } -#[test] -fn test_token_functionality() { - // Setup test accounts from address constants - let owner = owner(); - let artist_1 = artist_1(); - let artist_2 = artist_2(); - - // Deploy music share token factory as owner - let (factory_address, factory_dispatcher) = deploy_music_share_token_factory(owner); - - // Grant artist role before token deployment - cheat_caller_address(factory_address, owner, CheatSpan::TargetCalls(2)); - factory_dispatcher.grant_artist_role(artist_1); - factory_dispatcher.grant_artist_role(artist_2); - - // Setup test data - let (name, symbol, decimals, metadata_uri) = setup_token_data(); - - // Start calls as the deployer (artist_1) - cheat_caller_address(factory_address, artist_1, CheatSpan::TargetCalls(1)); - - // Deploy a token through the factory - let token_address = factory_dispatcher - .deploy_music_token(name.clone(), symbol.clone(), decimals, metadata_uri.clone()); - - // Get ERC20 interface - let token = IERC20MixinDispatcher { contract_address: token_address }; - - // Test transfer functionality - cheat_caller_address(token_address, artist_1, CheatSpan::TargetCalls(1)); - token.transfer(artist_2, 10_u256); - - // Verify balances after transfer - assert(token.balance_of(artist_1) == 90_u256, 'Artist 1 should have 90 tokens'); - assert(token.balance_of(artist_2) == 10_u256, 'Artist 2 should have 10 tokens'); - - // Test approval and transferFrom with artist_2 - cheat_caller_address(token_address, artist_2, CheatSpan::TargetCalls(1)); - token.approve(artist_1, 5_u256); - - cheat_caller_address(token_address, artist_1, CheatSpan::TargetCalls(1)); - token.transfer_from(artist_2, artist_1, 5_u256); - - // Verify balances after transferFrom - assert(token.balance_of(artist_1) == 95_u256, 'Artist 1 should have 95 tokens'); - assert(token.balance_of(artist_2) == 5_u256, 'Artist 2 should have 5 tokens'); -} +// Removed ERC20 behavior checks from factory tests; covered in token tests #[test] #[should_panic(expect: 'Index out of bounds;')] @@ -447,22 +365,3 @@ fn test_update_token_class_hash_unauthorized() { // This should fail because only owner can update class hash factory_dispatcher.update_token_class_hash(MUSICSTRK_HASH()); } - -#[test] -#[should_panic(expect: 'Result::unwrap failed.')] -fn test_deploy_factory_with_zero_owner() { - // For this test, we need to modify the deploy function to handle zero address directly - // Get the token class hash - let (_, music_token_class_hash) = deploy_music_share_token(owner()); - - // Set up factory constructor calldata with zero address as owner - let factory_class = declare("MusicShareTokenFactory").unwrap().contract_class(); - let mut calldata = array![]; - - // Use zero address as owner - calldata.append(zero().into()); - calldata.append(music_token_class_hash.into()); - - // Attempt to deploy with zero address owner - should fail - let (_, _) = factory_class.deploy(@calldata).unwrap(); -} diff --git a/contract_/tests/test_utils.cairo b/contract_/tests/test_utils.cairo index c6577ad..9f1215f 100644 --- a/contract_/tests/test_utils.cairo +++ b/contract_/tests/test_utils.cairo @@ -2,14 +2,12 @@ use contract_::audition::interfaces::iseason_and_audition::{ ISeasonAndAuditionDispatcher, ISeasonAndAuditionDispatcherTrait, ISeasonAndAuditionSafeDispatcher, }; -use contract_::audition::types::season_and_audition::{ - Appeal, Audition, Evaluation, Genre, Season, Vote, -}; -use core::array::ArrayTrait; +use contract_::audition::types::season_and_audition::{Audition, Genre, Season}; +use contract_::erc20::MusicStrk; use openzeppelin::access::ownable::interface::IOwnableDispatcher; -use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; +use openzeppelin::token::erc20::interface::IERC20Dispatcher; use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; -use starknet::ContractAddress; +use starknet::{ClassHash, ContractAddress}; // Test account -> Owner pub fn OWNER() -> ContractAddress { @@ -58,6 +56,41 @@ pub fn VOTER2() -> ContractAddress { 'VOTER2'.try_into().unwrap() } +pub fn performer() -> ContractAddress { + 'performerid'.try_into().unwrap() +} + +pub fn performer2() -> ContractAddress { + 'performerid2'.try_into().unwrap() +} + +pub fn performer3() -> ContractAddress { + 'performerid3'.try_into().unwrap() +} + +// Address constants for testing +pub fn artist_1() -> ContractAddress { + 'artist_1'.try_into().unwrap() +} + +pub fn artist_2() -> ContractAddress { + 'artist_2'.try_into().unwrap() +} + +pub fn non_auth() -> ContractAddress { + 'non-auth'.try_into().unwrap() +} + +pub fn owner() -> ContractAddress { + 'owner'.try_into().unwrap() +} + +// Constants +pub fn MUSICSTRK_HASH() -> ClassHash { + MusicStrk::TEST_CLASS_HASH.try_into().unwrap() +} + + // Helper function to deploy the contract pub fn deploy_contract() -> ( ISeasonAndAuditionDispatcher, IOwnableDispatcher, ISeasonAndAuditionSafeDispatcher, diff --git a/contract_/tests/test_vote_recording.cairo b/contract_/tests/test_vote_recording.cairo index d18116f..7b0a1d2 100644 --- a/contract_/tests/test_vote_recording.cairo +++ b/contract_/tests/test_vote_recording.cairo @@ -1,18 +1,14 @@ use contract_::audition::interfaces::iseason_and_audition::{ ISeasonAndAuditionDispatcher, ISeasonAndAuditionDispatcherTrait, - ISeasonAndAuditionSafeDispatcher, }; // use contract_::audition::season_and_audition::SeasonAndAudition; use contract_::audition::types::season_and_audition::Genre; use contract_::events::VoteRecorded; use core::num::traits::Zero; -use openzeppelin::access::ownable::interface::IOwnableDispatcher; use snforge_std::{ - ContractClassTrait, DeclareResultTrait, EventSpyAssertionsTrait, declare, spy_events, - start_cheat_caller_address, stop_cheat_caller_address, + EventSpyAssertionsTrait, spy_events, start_cheat_caller_address, stop_cheat_caller_address, }; use starknet::ContractAddress; -use crate::test_season_and_audition::create_default_season; use crate::test_utils::*; // Helper function to setup contract with oracle @@ -43,7 +39,6 @@ fn test_record_vote_success() { let performer: ContractAddress = 'performer1'.try_into().unwrap(); let voter: ContractAddress = 'voter1'.try_into().unwrap(); let weight: felt252 = 100; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); default_contract_create_season(contract); stop_cheat_caller_address(contract.contract_address); @@ -85,7 +80,6 @@ fn test_record_vote_success() { #[should_panic(expected: 'Season is paused')] fn test_record_vote_should_panic_if_season_paused() { let contract = setup_contract_with_oracle(); - let mut spy = spy_events(); let audition_id: u256 = 1; let performer: ContractAddress = 'performer1'.try_into().unwrap(); @@ -160,7 +154,6 @@ fn test_record_multiple_votes_different_combinations() { let voter2: ContractAddress = 'voter2'.try_into().unwrap(); let weight: felt252 = 100; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); default_contract_create_season(contract); stop_cheat_caller_address(contract.contract_address); @@ -204,7 +197,6 @@ fn test_record_votes_different_auditions() { let performer: ContractAddress = 'performer1'.try_into().unwrap(); let voter: ContractAddress = 'voter1'.try_into().unwrap(); let weight: felt252 = 100; - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); default_contract_create_season(contract); stop_cheat_caller_address(contract.contract_address); @@ -241,7 +233,6 @@ fn test_get_vote_nonexistent_returns_default() { let audition_id: u256 = 1; let performer: ContractAddress = 'performer1'.try_into().unwrap(); let voter: ContractAddress = 'voter1'.try_into().unwrap(); - let season_id: u256 = 1; start_cheat_caller_address(contract.contract_address, OWNER()); default_contract_create_season(contract);