From 3126ace3d5a2b8791b7581a0ec44c468535dc419 Mon Sep 17 00:00:00 2001 From: dreamgenies Date: Mon, 30 Mar 2026 09:40:25 +0100 Subject: [PATCH] feat: add provider-level record count tracking --- contracts/provider-registry/src/lib.rs | 33 ++++++----- contracts/provider-registry/src/test.rs | 74 ++++++++++++++++++++++--- 2 files changed, 86 insertions(+), 21 deletions(-) diff --git a/contracts/provider-registry/src/lib.rs b/contracts/provider-registry/src/lib.rs index 65e8d97..8af2e2b 100644 --- a/contracts/provider-registry/src/lib.rs +++ b/contracts/provider-registry/src/lib.rs @@ -52,6 +52,7 @@ pub enum DataKey { Provider(Address), Record(String), ProviderRecords(Address), + ProviderRecordCount(Address), RateLimitConfig, ProviderRate(Address), ProviderReputation(Address), @@ -144,6 +145,10 @@ impl ProviderRegistry { ids.push_back(record_id.clone()); env.storage().persistent().set(&list_key, &ids); + let count_key = DataKey::ProviderRecordCount(provider.clone()); + let count: u64 = env.storage().persistent().get(&count_key).unwrap_or(0); + env.storage().persistent().set(&count_key, &(count + 1)); + env.events().publish( (symbol_short!("add_rec"), provider, record_id), symbol_short!("ok"), @@ -159,6 +164,14 @@ impl ProviderRegistry { .expect("Record not found") } + /// Retrieve the total number of records ever created by a provider. + pub fn get_provider_record_count(env: Env, provider: Address) -> u64 { + env.storage() + .persistent() + .get(&DataKey::ProviderRecordCount(provider)) + .unwrap_or(0) + } + /// Rate a provider with score 1..=5. /// A patient can only rate the same provider once. pub fn rate_provider( @@ -169,7 +182,7 @@ impl ProviderRegistry { ) -> Result<(), ContractError> { patient.require_auth(); - if score < 1 || score > 5 { + if !(1..=5).contains(&score) { return Err(ContractError::InvalidScore); } if !Self::is_provider(env.clone(), provider.clone()) { @@ -194,9 +207,7 @@ impl ProviderRegistry { reputation.total_ratings += 1; reputation.total_score += score as u64; - env.storage() - .persistent() - .set(&patient_rating_key, &true); + env.storage().persistent().set(&patient_rating_key, &true); env.storage().persistent().set(&reputation_key, &reputation); env.events().publish( (symbol_short!("rate"), provider), @@ -222,6 +233,8 @@ impl ProviderRegistry { } let average_scaled = (reputation.total_score * 100) / reputation.total_ratings; (reputation.total_ratings, average_scaled) + } + /// Deactivate a provider: reassign all their records to `successor`, /// remove them from the whitelist, and emit deactivation events. Admin only. pub fn deactivate_provider(env: Env, admin: Address, provider: Address, successor: Address) { @@ -238,11 +251,7 @@ impl ProviderRegistry { let count = ids.len(); for id in ids.iter() { let rec_key = DataKey::Record(id.clone()); - if let Some(mut rec) = env - .storage() - .persistent() - .get::(&rec_key) - { + if let Some(mut rec) = env.storage().persistent().get::(&rec_key) { rec.created_by = successor.clone(); env.storage().persistent().set(&rec_key, &rec); } @@ -272,10 +281,8 @@ impl ProviderRegistry { (symbol_short!("prov_deac"), provider.clone()), symbol_short!("ok"), ); - env.events().publish( - (symbol_short!("rec_xfer"), provider, successor), - count, - ); + env.events() + .publish((symbol_short!("rec_xfer"), provider, successor), count); } // ── helpers ────────────────────────────────────────────────────────────── diff --git a/contracts/provider-registry/src/test.rs b/contracts/provider-registry/src/test.rs index 796e564..7234e35 100644 --- a/contracts/provider-registry/src/test.rs +++ b/contracts/provider-registry/src/test.rs @@ -50,6 +50,41 @@ fn test_add_record_by_whitelisted_provider() { let rec = client.get_record(&String::from_str(&env, "REC001")); assert_eq!(rec.data, String::from_str(&env, "Patient data")); assert_eq!(rec.created_by, provider); + assert_eq!(client.get_provider_record_count(&provider), 1); +} + +#[test] +fn test_provider_record_count_starts_at_zero() { + let (env, _admin, client) = setup(); + let provider = Address::generate(&env); + + assert_eq!(client.get_provider_record_count(&provider), 0); +} + +#[test] +fn test_provider_record_count_persists_across_multiple_records() { + let (env, admin, client) = setup(); + let provider = Address::generate(&env); + + client.register_provider(&admin, &provider); + + client.add_record( + &provider, + &String::from_str(&env, "REC100"), + &String::from_str(&env, "Patient data 1"), + ); + client.add_record( + &provider, + &String::from_str(&env, "REC101"), + &String::from_str(&env, "Patient data 2"), + ); + client.add_record( + &provider, + &String::from_str(&env, "REC102"), + &String::from_str(&env, "Patient data 3"), + ); + + assert_eq!(client.get_provider_record_count(&provider), 3); } #[test] @@ -224,7 +259,9 @@ fn test_rate_limit_window_reset_allows_again() { &String::from_str(&env, "data"), ); assert_eq!( - client.get_record(&String::from_str(&env, "REC-AFTER-RESET")).data, + client + .get_record(&String::from_str(&env, "REC-AFTER-RESET")) + .data, String::from_str(&env, "data") ); } @@ -252,8 +289,14 @@ fn test_deactivate_provider_transfers_records_and_removes_whitelist() { ); // Confirm original ownership. - assert_eq!(client.get_record(&String::from_str(&env, "R1")).created_by, provider); - assert_eq!(client.get_record(&String::from_str(&env, "R2")).created_by, provider); + assert_eq!( + client.get_record(&String::from_str(&env, "R1")).created_by, + provider + ); + assert_eq!( + client.get_record(&String::from_str(&env, "R2")).created_by, + provider + ); client.deactivate_provider(&admin, &provider, &successor); @@ -261,8 +304,14 @@ fn test_deactivate_provider_transfers_records_and_removes_whitelist() { assert!(!client.is_provider(&provider)); // Both records now owned by successor. - assert_eq!(client.get_record(&String::from_str(&env, "R1")).created_by, successor); - assert_eq!(client.get_record(&String::from_str(&env, "R2")).created_by, successor); + assert_eq!( + client.get_record(&String::from_str(&env, "R1")).created_by, + successor + ); + assert_eq!( + client.get_record(&String::from_str(&env, "R2")).created_by, + successor + ); } #[test] @@ -319,9 +368,18 @@ fn test_deactivate_provider_successor_accumulates_records() { client.deactivate_provider(&admin, &provider, &successor); // All three records now belong to successor. - assert_eq!(client.get_record(&String::from_str(&env, "S1")).created_by, successor); - assert_eq!(client.get_record(&String::from_str(&env, "P1")).created_by, successor); - assert_eq!(client.get_record(&String::from_str(&env, "P2")).created_by, successor); + assert_eq!( + client.get_record(&String::from_str(&env, "S1")).created_by, + successor + ); + assert_eq!( + client.get_record(&String::from_str(&env, "P1")).created_by, + successor + ); + assert_eq!( + client.get_record(&String::from_str(&env, "P2")).created_by, + successor + ); } #[test]