Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
scarb 2.11.4
starknet-foundry 0.40.0
starknet-foundry 0.40.0
40 changes: 36 additions & 4 deletions src/contracts/Cairofy.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod CairofyV0 {
use cairofy_contract::events::Events::{SongPriceUpdated, Song_Registered};
use cairofy_contract::interfaces::ICairofy::ICairofy;
use cairofy_contract::structs::Structs::Song;
use core::num::traits::Zero;
use openzeppelin::access::ownable::OwnableComponent;
use openzeppelin::security::pausable::PausableComponent;
use openzeppelin::token::erc20::interface::{
Expand All @@ -18,6 +19,7 @@ pub mod CairofyV0 {
StoragePointerWriteAccess,
};
use starknet::{ClassHash, ContractAddress, get_caller_address};
// use OwnableComponent::InternalTrait;

component!(path: PausableComponent, storage: pausable, event: PausableEvent);
component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
Expand Down Expand Up @@ -243,10 +245,40 @@ pub mod CairofyV0 {

song.ipfs_hash
}
// TODO: Implement function to fetch songs owned by a specific user
// fn get_user_songs(...) -> Array<u64> { ... }

// TODO: Implement function to check if a user is the owner of a song
// fn is_song_owner(...) -> bool { ... }
fn get_user_songs(self: @ContractState, user: ContractAddress) -> Array<u64> {
let caller = get_caller_address();
assert(!caller.is_zero(), 'ZERO_ADDRESS_CALLER');
// get the count of songs for the user
let user_song_count = self.user_song_count.read(user);
// create an array to store the song IDs
let mut song_ids = ArrayTrait::new();
// iterate through the user's songs
let mut i: u64 = 0;
while i < user_song_count {
let song_id = self.user_song_ids.read((user, i));
song_ids.append(song_id);
i += 1;
}

song_ids
}

fn is_song_owner(self: @ContractState, song_id: u64) -> bool {
// check if the song ID is valid
let user = get_caller_address();
assert(!user.is_zero(), 'ZERO_ADDRESS_CALLER');
assert(!song_id.is_zero(), 'ZERO_SONG_ID');

// check if the song ID is valid
let total_songs = self.song_count.read();
if song_id > total_songs {
return false;
}

// check if the user is the owner of the song
let song = self.songs.read(song_id);
song.owner == user
}
}
}
7 changes: 3 additions & 4 deletions src/interfaces/ICairofy.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use cairofy_contract::structs::Structs::Song;
use starknet::ContractAddress;

#[starknet::interface]
pub trait ICairofy<TContractState> {
Expand All @@ -10,12 +11,10 @@ pub trait ICairofy<TContractState> {
price: u256,
for_sale: bool,
) -> u64;

fn get_song_info(self: @TContractState, song_id: u64) -> Song;
fn update_song_price(ref self: TContractState, song_id: u64, new_price: u256);
fn get_preview(self: @TContractState, song_id: u64) -> felt252;
fn buy_song(ref self: TContractState, song_id: u64) -> felt252;
// fn get_user_songs(self: @TContractState, user: ContractAddress) -> Array<u64>;

// fn is_song_owner(self: @TContractState, user: ContractAddress, song_id: u64) -> bool;
fn get_user_songs(self: @TContractState, user: ContractAddress) -> Array<u64>;
fn is_song_owner(self: @TContractState, song_id: u64) -> bool;
}
175 changes: 175 additions & 0 deletions tests/test_cairofy.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
use cairofy_contract::contracts::Cairofy::CairofyV0;
use cairofy_contract::events::Events::{SongPriceUpdated, Song_Registered};
use cairofy_contract::interfaces::ICairofy::{ICairofyDispatcher, ICairofyDispatcherTrait};
use core::array::Array;
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, test_address,
};
use starknet::{ContractAddress, contract_address_const};
use super::*;

fn OWNER() -> ContractAddress {
contract_address_const::<'owner'>()
}

fn NON_OWNER() -> ContractAddress {
contract_address_const::<'non_owner'>()
}

fn TEST_OWNER1() -> ContractAddress {
contract_address_const::<'test_owner1'>()
}

fn TEST_OWNER2() -> ContractAddress {
contract_address_const::<'test_owner2'>()
}

fn TEST_OWNER3() -> ContractAddress {
contract_address_const::<'test_owner3'>()
}

fn register_default_song() -> u64 {
let (dispatcher, _) = deploy_contract();
Expand Down Expand Up @@ -231,3 +260,149 @@ fn test_buy_song_by_owner() {
let new_owner = dispatcher.get_song_info(song_id).owner;
assert(new_owner == TEST_OWNER2(), 'buy failed');
}

// CaxtonStone Start
#[test]
fn test_get_user_songs_empty() {
let (dispatcher, _) = deploy_contract();

let user_felt: felt252 = 0x12345.into();
let user: ContractAddress = user_felt.try_into().unwrap();
// Get songs for a user who hasn't registered any
let songs = dispatcher.get_user_songs(user);

// Check that the returned array is empty
assert(songs.len() == 0, 'Should return empty array');
}

#[test]
fn test_get_user_songs_single() {
let (dispatcher, _) = deploy_contract();

let user_felt: felt252 = 0x12345.into();
let user: ContractAddress = user_felt.try_into().unwrap();
// Set the caller to the test user
start_cheat_caller_address(dispatcher.contract_address, user);

// Register a song using the actual contract function
let song_id = dispatcher
.register_song('Test Song', 'ipfs_hash_1', 'preview_hash_1', 1000_u256, true);

// Get songs for the user
let songs = dispatcher.get_user_songs(user);

// Check that the returned array contains the registered song
assert(songs.len() == 1, 'Should have 1 song');
assert(*songs.at(0) == song_id, 'Song ID mismatch');

stop_cheat_caller_address(dispatcher.contract_address);
}

#[test]
fn test_get_user_songs_multiple() {
let (dispatcher, _) = deploy_contract();
let user_felt: felt252 = 0x12345.into();
let user: ContractAddress = user_felt.try_into().unwrap();
// Set the caller to the test user
start_cheat_caller_address(dispatcher.contract_address, user);

// Register multiple songs
let song_id1 = dispatcher
.register_song('Test Song 1', 'ipfs_hash_1', 'preview_hash_1', 1000_u256, true);

let song_id2 = dispatcher
.register_song('Test Song 2', 'ipfs_hash_2', 'preview_hash_2', 2000_u256, false);

let song_id3 = dispatcher
.register_song('Test Song 3', 'ipfs_hash_3', 'preview_hash_3', 3000_u256, true);

// Get songs for the user
let songs = dispatcher.get_user_songs(user);

// Check that the returned array contains all registered songs
assert(songs.len() == 3, 'Should have 3 songs');
assert(*songs.at(0) == song_id1, 'Song ID 1 mismatch');
assert(*songs.at(1) == song_id2, 'Song ID 2 mismatch');
assert(*songs.at(2) == song_id3, 'Song ID 3 mismatch');

stop_cheat_caller_address(dispatcher.contract_address);
}


#[test]
fn test_is_song_owner_true() {
let (dispatcher, _) = deploy_contract();

let user_felt: felt252 = 0x12345.into();
let user: ContractAddress = user_felt.try_into().unwrap();

start_cheat_caller_address(dispatcher.contract_address, user);

let song_id = dispatcher
.register_song('My Song', 'my_ipfs_hash', 'my_preview_hash', 1000_u256, false);

let song = dispatcher.get_song_info(song_id);

println!("song: {:?}", song);

// Check if the user is the owner of the song
let is_owner = dispatcher.is_song_owner(song_id);
println!("is_owner: {:?}", is_owner);
assert(is_owner, 'User should be the owner');

stop_cheat_caller_address(dispatcher.contract_address);
}

#[test]
#[should_panic(expect: 'Non-owner should not be owner')]
fn test_is_song_owner_false() {
let (dispatcher, _) = deploy_contract();

// user addresses
let owner_felt: felt252 = 0x12345.into();
let owner: ContractAddress = owner_felt.try_into().unwrap();

let non_owner_felt: felt252 = 0x12346.into();
let non_owner: ContractAddress = non_owner_felt.try_into().unwrap();

// set the caller to the owner
start_cheat_caller_address(dispatcher.contract_address, owner);

// register a song as the owner
let song_id = dispatcher
.register_song('Owner Song', 'owner_ipfs_hash', 'owner_preview_hash', 1000_u256, false);

stop_cheat_caller_address(dispatcher.contract_address);

start_cheat_caller_address(dispatcher.contract_address, non_owner);

// check if the non-owner is the owner of the song
let is_owner = dispatcher.is_song_owner(song_id);

// the non-owner should not be the owner
assert(is_owner, 'Non-owner should not be owner');
stop_cheat_caller_address(dispatcher.contract_address);
}


#[test]
fn test_is_song_owner_invalid_id() {
let (dispatcher, _) = deploy_contract();

// user address
let user_felt: felt252 = 0x12345.into();
let user: ContractAddress = user_felt.try_into().unwrap();

start_cheat_caller_address(dispatcher.contract_address, user);

// check if the user is the owner of a song with an invalid ID
let is_owner = dispatcher.is_song_owner(9999);

// should return false for invalid song ID
assert(!is_owner, 'Should be false for invalid ID');

stop_cheat_caller_address(dispatcher.contract_address);
}
// CaxtonStone Stop


Loading