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
127 changes: 127 additions & 0 deletions contracts/geev-core/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5972,3 +5972,130 @@ fn test_midflight_fee_change_does_not_alter_collected_fees() {
});
assert_eq!(collected_after, collected_before);
}

// ── Additional edge-case tests ────────────────────────────────────────────────

/// `pick_winner` must emit a `GiveawayWinnerSelected` event whose data vec
/// carries the correct `giveaway_id` and gross `prize_amount` (before the
/// per-claim fee deduction). The topics are `["giveaway", "winner", <winner>]`.
#[test]
fn test_pick_winner_emits_winner_selected_event() {
let env = Env::default();
env.mock_all_auths();

let (contract_id, client, _tc, token, creator) = setup_giveaway_env(&env, 100); // 1 %

let amount: i128 = 500;
let giveaway_id = client.create_giveaway(
&creator,
&token,
&amount,
&String::from_str(&env, "Winner Event"),
&60,
&1,
&None,
&None, // fee_bps
);

let winner = Address::generate(&env);
client.enter_giveaway(&winner, &giveaway_id);
env.ledger().with_mut(|li| li.timestamp += 100);
client.pick_winner(&giveaway_id);

let events = env.events().all();
// Topics: symbol "giveaway", symbol "winner", winner address.
let expected_topics: soroban_sdk::Vec<Val> = vec![
&env,
Symbol::new(&env, "giveaway").into_val(&env),
Symbol::new(&env, "winner").into_val(&env),
winner.into_val(&env),
];
assert!(
events.iter().any(|(ec, topics, data)| {
if ec != contract_id || topics != expected_topics.into_val(&env) {
return false;
}
let v: soroban_sdk::Vec<Val> = soroban_sdk::Vec::from_val(&env, &data);
let ev_giveaway_id = u64::from_val(&env, &v.get(0).unwrap());
// prize_amount is the gross share — equal to `amount` for a single winner.
let ev_prize = i128::from_val(&env, &v.get(1).unwrap());
ev_giveaway_id == giveaway_id && ev_prize == amount
}),
"GiveawayWinnerSelected event not found or fields do not match"
);
}

/// For a 3-winner giveaway where only winner-0 claims before the deadline,
/// `recover_unclaimed_prize` must return exactly the two unclaimed gross shares
/// to the creator with no fee deduction, and the contract must retain only the
/// fee collected from the single successful claim.
#[test]
fn test_partial_claim_creator_receives_exact_unclaimed_gross_shares() {
let env = Env::default();
env.mock_all_auths();

// 1 % fee so arithmetic is easy to track.
let (contract_id, client, tc, token, creator) = setup_giveaway_env(&env, 100);

// 3 winners, 300 tokens → 100 gross each (even split, no remainder).
let amount: i128 = 300;
let giveaway_id = client.create_giveaway(
&creator,
&token,
&amount,
&String::from_str(&env, "Partial 3-winner"),
&60,
&3,
&None,
&None, // fee_bps
);

let p1 = Address::generate(&env);
let p2 = Address::generate(&env);
let p3 = Address::generate(&env);
client.enter_giveaway(&p1, &giveaway_id);
client.enter_giveaway(&p2, &giveaway_id);
client.enter_giveaway(&p3, &giveaway_id);
env.ledger().with_mut(|li| li.timestamp += 100);
client.pick_winner(&giveaway_id);

let winners: Vec<Address> = env.as_contract(&contract_id, || {
env.storage()
.persistent()
.get::<DataKey, Giveaway>(&DataKey::Giveaway(giveaway_id))
.unwrap()
.winners
.clone()
});

// Only winner-0 claims (gross share = 100, net = 99, fee = 1).
client.claim_prize(&giveaway_id, &winners.get(0).unwrap());
assert_eq!(tc.balance(&winners.get(0).unwrap()), 99);

let creator_before = tc.balance(&creator);

// Advance past claim window.
env.ledger()
.with_mut(|li| li.timestamp += 7 * 24 * 60 * 60 + 1);
client.recover_unclaimed_prize(&giveaway_id, &creator);

// Creator recovers the two unclaimed gross shares (100 + 100 = 200) — no fee.
assert_eq!(tc.balance(&creator) - creator_before, 200);

// Contract holds only the 1-token fee from the single claim.
assert_eq!(tc.balance(&contract_id), 1);

// Unclaiming winners received nothing.
assert_eq!(tc.balance(&winners.get(1).unwrap()), 0);
assert_eq!(tc.balance(&winners.get(2).unwrap()), 0);

// Giveaway is finalized.
env.as_contract(&contract_id, || {
let g: Giveaway = env
.storage()
.persistent()
.get(&DataKey::Giveaway(giveaway_id))
.unwrap();
assert_eq!(g.status, GiveawayStatus::Completed);
});
}
Loading
Loading