Skip to content

Commit 73cfb45

Browse files
Merge pull request Predictify-org#212 from Gbangbolaoluwagbemiga/test/user-balance-management-tests
feat: add comprehensive user balance management tests
2 parents 2ad323d + 3b6f0ba commit 73cfb45

19 files changed

+726
-202
lines changed

contracts/predictify-hybrid/src/admin.rs

Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -465,12 +465,7 @@ impl AdminAccessControl {
465465
admin: &Address,
466466
permission: &AdminPermission,
467467
) -> Result<(), Error> {
468-
// Check original admin for backward compatibility first
469-
if AdminManager::is_original_admin(env, admin) {
470-
return Ok(());
471-
}
472-
473-
// Try new multi-admin system if migrated
468+
// Try new multi-admin system first if migrated
474469
if AdminSystemIntegration::is_migrated(env) {
475470
return AdminManager::validate_admin_permission(env, admin, *permission);
476471
}
@@ -1258,16 +1253,6 @@ impl AdminManager {
12581253
.persistent()
12591254
.set(&count_key, &(current_count + 1));
12601255

1261-
// Maintain a list of admin addresses for iteration
1262-
let list_key = Symbol::new(env, "AdminList");
1263-
let mut admin_list: Vec<Address> = env
1264-
.storage()
1265-
.persistent()
1266-
.get(&list_key)
1267-
.unwrap_or_else(|| Vec::new(env));
1268-
admin_list.push_back(new_admin.clone());
1269-
env.storage().persistent().set(&list_key, &admin_list);
1270-
12711256
// Emit event using existing system
12721257
Self::emit_admin_change_event(env, new_admin, AdminActionType::Added);
12731258

@@ -1310,18 +1295,6 @@ impl AdminManager {
13101295
.set(&count_key, &(current_count - 1));
13111296
}
13121297

1313-
// Remove from admin list
1314-
let list_key = Symbol::new(env, "AdminList");
1315-
if let Some(admin_list) = env.storage().persistent().get::<_, Vec<Address>>(&list_key) {
1316-
let mut new_list: Vec<Address> = Vec::new(env);
1317-
for addr in admin_list.iter() {
1318-
if &addr != admin_to_remove {
1319-
new_list.push_back(addr.clone());
1320-
}
1321-
}
1322-
env.storage().persistent().set(&list_key, &new_list);
1323-
}
1324-
13251298
Self::emit_admin_change_event(env, admin_to_remove, AdminActionType::Removed);
13261299
Ok(())
13271300
}
@@ -1441,6 +1414,7 @@ impl AdminManager {
14411414
None
14421415
}
14431416

1417+
14441418
/// Emits admin change events using existing AdminActionType
14451419
pub fn emit_admin_change_event(env: &Env, admin: &Address, action: AdminActionType) {
14461420
let action_str = match action {
@@ -1462,14 +1436,14 @@ impl AdminManager {
14621436
// ===== Helper Methods =====
14631437

14641438
/// Generate a proper admin storage key using the correct environment
1465-
fn get_admin_key(env: &Env, admin: &Address) -> (Symbol, Address) {
1466-
// Use a tuple key for per-admin storage
1467-
// This avoids Symbol character limitations by using Address directly
1468-
(Symbol::new(env, "MultiAdmin"), admin.clone())
1439+
fn get_admin_key(env: &Env, admin: &Address) -> Symbol {
1440+
// Create a unique key based on admin address
1441+
let key_str = format!("MultiAdmin_{:?}", admin.to_string());
1442+
Symbol::new(env, &key_str)
14691443
}
14701444

14711445
/// Check if an address is the original admin from single-admin system
1472-
fn is_original_admin(env: &Env, admin: &Address) -> bool {
1446+
pub fn is_original_admin(env: &Env, admin: &Address) -> bool {
14731447
if let Some(original_admin) = Self::get_original_admin(env) {
14741448
return admin == &original_admin;
14751449
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#![cfg(test)]
2+
3+
use crate::test::PredictifyTest;
4+
use crate::types::ReflectorAsset;
5+
use crate::errors::Error;
6+
use soroban_sdk::{testutils::Address as _, Address, Env, Symbol};
7+
8+
#[test]
9+
fn test_deposit_and_withdrawal_flow() {
10+
let test = PredictifyTest::setup();
11+
let env = &test.env;
12+
let user = &test.user;
13+
let contract_address = &test.contract_id;
14+
15+
// 1. Initial State: User has 1000 XLM (minted in setup), Contract has 0
16+
let token_client = soroban_sdk::token::Client::new(env, &test.token_test.token_id);
17+
// Verify initial token balances
18+
// Note: token_test.token_client is associated with the token contract
19+
// We need to use the client correctly.
20+
// In PredictifyTest::setup, we minted 1000_0000000 to user.
21+
22+
assert_eq!(token_client.balance(user), 1000_0000000);
23+
assert_eq!(token_client.balance(contract_address), 0);
24+
25+
// 2. Deposit Funds
26+
let deposit_amount = 500_0000000; // 500 XLM
27+
let client = crate::PredictifyHybridClient::new(env, contract_address);
28+
29+
// We need to mock auth for the user
30+
env.mock_all_auths();
31+
32+
let balance = client.deposit(user, &ReflectorAsset::Stellar, &deposit_amount);
33+
34+
// 3. Verify Deposit
35+
assert_eq!(balance.amount, deposit_amount);
36+
assert_eq!(balance.user, *user);
37+
38+
// Verify stored balance matches
39+
let stored_balance = client.get_balance(user, &ReflectorAsset::Stellar);
40+
assert_eq!(stored_balance.amount, deposit_amount);
41+
42+
// Verify token transfer happened
43+
assert_eq!(token_client.balance(user), 500_0000000);
44+
assert_eq!(token_client.balance(contract_address), 500_0000000);
45+
46+
// 4. Withdraw Funds
47+
let withdraw_amount = 200_0000000; // 200 XLM
48+
let balance_after_withdraw = client.withdraw(user, &ReflectorAsset::Stellar, &withdraw_amount);
49+
50+
// 5. Verify Withdrawal
51+
assert_eq!(balance_after_withdraw.amount, 300_0000000); // 500 - 200 = 300
52+
53+
// Verify stored balance updated
54+
let stored_balance_2 = client.get_balance(user, &ReflectorAsset::Stellar);
55+
assert_eq!(stored_balance_2.amount, 300_0000000);
56+
57+
// Verify token transfer happened (Contract -> User)
58+
assert_eq!(token_client.balance(user), 700_0000000); // 500 + 200 = 700
59+
assert_eq!(token_client.balance(contract_address), 300_0000000); // 500 - 200 = 300
60+
}
61+
62+
#[test]
63+
fn test_insufficient_balance_withdrawal() {
64+
let test = PredictifyTest::setup();
65+
let env = &test.env;
66+
let user = &test.user;
67+
let contract_address = &test.contract_id;
68+
let client = crate::PredictifyHybridClient::new(env, contract_address);
69+
70+
env.mock_all_auths();
71+
72+
// Deposit 100
73+
let deposit_amount = 100_0000000;
74+
client.deposit(user, &ReflectorAsset::Stellar, &deposit_amount);
75+
76+
// Try to withdraw 150
77+
let withdraw_amount = 150_0000000;
78+
let result = client.try_withdraw(user, &ReflectorAsset::Stellar, &withdraw_amount);
79+
80+
assert_eq!(result, Err(Ok(Error::InsufficientBalance)));
81+
}
82+
83+
#[test]
84+
fn test_invalid_deposit_amount() {
85+
let test = PredictifyTest::setup();
86+
let env = &test.env;
87+
let user = &test.user;
88+
let contract_address = &test.contract_id;
89+
let client = crate::PredictifyHybridClient::new(env, contract_address);
90+
91+
env.mock_all_auths();
92+
93+
// Try to deposit 0
94+
let result = client.try_deposit(user, &ReflectorAsset::Stellar, &0);
95+
assert_eq!(result, Err(Ok(Error::InvalidInput)));
96+
97+
// Try to deposit negative
98+
let result_neg = client.try_deposit(user, &ReflectorAsset::Stellar, &-100);
99+
assert_eq!(result_neg, Err(Ok(Error::InvalidInput)));
100+
}
101+
102+
#[test]
103+
fn test_invalid_withdraw_amount() {
104+
let test = PredictifyTest::setup();
105+
let env = &test.env;
106+
let user = &test.user;
107+
let contract_address = &test.contract_id;
108+
let client = crate::PredictifyHybridClient::new(env, contract_address);
109+
110+
env.mock_all_auths();
111+
112+
client.deposit(user, &ReflectorAsset::Stellar, &1000);
113+
114+
// Try to withdraw 0
115+
let result = client.try_withdraw(user, &ReflectorAsset::Stellar, &0);
116+
assert_eq!(result, Err(Ok(Error::InvalidInput)));
117+
118+
// Try to withdraw negative
119+
let result_neg = client.try_withdraw(user, &ReflectorAsset::Stellar, &-100);
120+
assert_eq!(result_neg, Err(Ok(Error::InvalidInput)));
121+
}
122+
123+
#[test]
124+
fn test_multiple_deposits() {
125+
let test = PredictifyTest::setup();
126+
let env = &test.env;
127+
let user = &test.user;
128+
let contract_address = &test.contract_id;
129+
let client = crate::PredictifyHybridClient::new(env, contract_address);
130+
131+
env.mock_all_auths();
132+
133+
// Deposit 1
134+
client.deposit(user, &ReflectorAsset::Stellar, &100);
135+
let b1 = client.get_balance(user, &ReflectorAsset::Stellar);
136+
assert_eq!(b1.amount, 100);
137+
138+
// Deposit 2
139+
client.deposit(user, &ReflectorAsset::Stellar, &200);
140+
let b2 = client.get_balance(user, &ReflectorAsset::Stellar);
141+
assert_eq!(b2.amount, 300);
142+
}
143+
144+
#[test]
145+
fn test_deposit_invalid_asset() {
146+
let test = PredictifyTest::setup();
147+
let env = &test.env;
148+
let user = &test.user;
149+
let contract_address = &test.contract_id;
150+
let client = crate::PredictifyHybridClient::new(env, contract_address);
151+
152+
env.mock_all_auths();
153+
154+
// Try to deposit Bitcoin (not configured/supported yet in balances.rs match statement)
155+
// The current implementation in balances.rs returns Error::InvalidInput for non-Stellar assets
156+
// because it can't resolve the token client for them.
157+
let result = client.try_deposit(user, &ReflectorAsset::BTC, &100);
158+
assert_eq!(result, Err(Ok(Error::InvalidInput)));
159+
}

0 commit comments

Comments
 (0)