diff --git a/contracts/reputation/src/lib.rs b/contracts/reputation/src/lib.rs index 5eea06ee..ec22f312 100644 --- a/contracts/reputation/src/lib.rs +++ b/contracts/reputation/src/lib.rs @@ -8,6 +8,7 @@ pub use profile::BadgeLevel; mod profile; mod storage; +pub use profile::{BadgeMetadataEntry, BadgeTier}; use profile::{Profile, RoleMetrics}; @@ -1024,6 +1025,75 @@ mod test { assert_eq!(client.get_badge(&addr, &Role::Client), BadgeLevel::Bronze); } + // ── Issue #406: badge metadata mapping ── + + #[test] + fn test_set_and_get_badge_metadata() { + let env = Env::default(); + env.mock_all_auths(); + let admin = Address::generate(&env); + let addr = Address::generate(&env); + let cid = env.register_contract(None, ReputationContract); + let client = ReputationContractClient::new(&env, &cid); + client.initialize(&admin); + + let uri = Bytes::from_slice(&env, b"ipfs://QmBronzeBadge"); + client.set_badge_metadata(&admin, &addr, &BadgeTier::Bronze, &uri); + + let result = client.get_badge_metadata(&addr, &BadgeTier::Bronze); + assert_eq!(result, Some(uri)); + } + + #[test] + fn test_badge_metadata_returns_none_when_unset() { + let env = Env::default(); + env.mock_all_auths(); + let addr = Address::generate(&env); + let cid = env.register_contract(None, ReputationContract); + let client = ReputationContractClient::new(&env, &cid); + + let result = client.get_badge_metadata(&addr, &BadgeTier::Gold); + assert_eq!(result, None); + } + + #[test] + fn test_badge_metadata_update_overwrites_existing() { + let env = Env::default(); + env.mock_all_auths(); + let admin = Address::generate(&env); + let addr = Address::generate(&env); + let cid = env.register_contract(None, ReputationContract); + let client = ReputationContractClient::new(&env, &cid); + client.initialize(&admin); + + let uri_v1 = Bytes::from_slice(&env, b"ipfs://QmSilverV1"); + let uri_v2 = Bytes::from_slice(&env, b"ipfs://QmSilverV2"); + client.set_badge_metadata(&admin, &addr, &BadgeTier::Silver, &uri_v1); + client.set_badge_metadata(&admin, &addr, &BadgeTier::Silver, &uri_v2); + + assert_eq!(client.get_badge_metadata(&addr, &BadgeTier::Silver), Some(uri_v2)); + } + + #[test] + fn test_multiple_tiers_stored_independently() { + let env = Env::default(); + env.mock_all_auths(); + let admin = Address::generate(&env); + let addr = Address::generate(&env); + let cid = env.register_contract(None, ReputationContract); + let client = ReputationContractClient::new(&env, &cid); + client.initialize(&admin); + + let bronze_uri = Bytes::from_slice(&env, b"ipfs://Bronze"); + let gold_uri = Bytes::from_slice(&env, b"ipfs://Gold"); + client.set_badge_metadata(&admin, &addr, &BadgeTier::Bronze, &bronze_uri); + client.set_badge_metadata(&admin, &addr, &BadgeTier::Gold, &gold_uri); + + assert_eq!(client.get_badge_metadata(&addr, &BadgeTier::Bronze), Some(bronze_uri)); + assert_eq!(client.get_badge_metadata(&addr, &BadgeTier::Gold), Some(gold_uri)); + assert_eq!(client.get_badge_metadata(&addr, &BadgeTier::Silver), None); + } + #[test] #[should_panic(expected = "Error(Contract, #2)")] fn test_upgrade_requires_admin() { diff --git a/contracts/reputation/src/profile.rs b/contracts/reputation/src/profile.rs index df611d65..fc8a0afd 100644 --- a/contracts/reputation/src/profile.rs +++ b/contracts/reputation/src/profile.rs @@ -68,6 +68,24 @@ impl BadgeLevel { } } +/// Badge tiers keyed in the metadata map. +#[contracttype] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum BadgeTier { + Bronze, + Silver, + Gold, + Platinum, +} + +#[contracttype] +#[derive(Clone, Debug, PartialEq)] +pub struct BadgeMetadataEntry { + pub tier: BadgeTier, + /// IPFS CID (or any URI) pointing to the badge image / JSON metadata. + pub uri: Bytes, +} + #[contracttype] #[derive(Clone, Debug, PartialEq)] pub struct Profile { @@ -76,6 +94,8 @@ pub struct Profile { pub freelancer: RoleMetrics, pub is_blacklisted: bool, pub metadata_hash: Option, + /// Per-tier badge metadata URIs set by the admin. + pub badge_metadata: soroban_sdk::Vec, } impl Profile { @@ -86,6 +106,7 @@ impl Profile { freelancer: RoleMetrics::new(), is_blacklisted: false, metadata_hash: None, + badge_metadata: soroban_sdk::Vec::new(_env), } } }