diff --git a/packages/controllers/src/claim.rs b/packages/controllers/src/claim.rs index d5b1104ea..bcc8d8304 100644 --- a/packages/controllers/src/claim.rs +++ b/packages/controllers/src/claim.rs @@ -92,4 +92,494 @@ impl<'a> Claims<'a> { } } -// TODO: add test coverage +#[cfg(test)] +mod test { + use cosmwasm_std::{ + testing::{mock_dependencies, mock_env}, + Order, + }; + + use super::*; + const TEST_AMOUNT: u128 = 1000u128; + const TEST_EXPIRATION: Expiration = Expiration::AtHeight(10); + + #[test] + fn can_create_claim() { + let claim = Claim::new(TEST_AMOUNT, TEST_EXPIRATION); + assert_eq!(claim.amount, TEST_AMOUNT.into()); + assert_eq!(claim.release_at, TEST_EXPIRATION); + } + + #[test] + fn can_create_claims() { + let deps = mock_dependencies(&[]); + let claims = Claims::new("claims"); + // Assert that claims creates a map and there are no keys in the map. + assert_eq!( + claims + .0 + .range(&deps.storage, None, None, Order::Ascending) + .collect::>>() + .unwrap() + .len(), + 0 + ); + } + + #[test] + fn check_create_claim_updates_map() { + let mut deps = mock_dependencies(&[]); + let claims = Claims::new("claims"); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + TEST_AMOUNT.into(), + TEST_EXPIRATION, + ) + .unwrap(); + + // Assert that claims creates a map and there is one claim for the address. + let saved_claims = claims + .0 + .load(deps.as_mut().storage, &Addr::unchecked("addr")) + .unwrap(); + assert_eq!(saved_claims.len(), 1); + assert_eq!(saved_claims[0].amount, TEST_AMOUNT.into()); + assert_eq!(saved_claims[0].release_at, TEST_EXPIRATION); + + // Adding another claim to same address, make sure that both claims are saved. + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + (TEST_AMOUNT + 100).into(), + TEST_EXPIRATION, + ) + .unwrap(); + + // Assert that both claims exist for the address. + let saved_claims = claims + .0 + .load(deps.as_mut().storage, &Addr::unchecked("addr")) + .unwrap(); + assert_eq!(saved_claims.len(), 2); + assert_eq!(saved_claims[0].amount, TEST_AMOUNT.into()); + assert_eq!(saved_claims[0].release_at, TEST_EXPIRATION); + assert_eq!(saved_claims[1].amount, (TEST_AMOUNT + 100).into()); + assert_eq!(saved_claims[1].release_at, TEST_EXPIRATION); + + // Adding another claim to different address, make sure that other address only has one claim. + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr2"), + (TEST_AMOUNT + 100).into(), + TEST_EXPIRATION, + ) + .unwrap(); + + // Assert that both claims exist for the address. + let saved_claims = claims + .0 + .load(deps.as_mut().storage, &Addr::unchecked("addr")) + .unwrap(); + + let saved_claims_addr2 = claims + .0 + .load(deps.as_mut().storage, &Addr::unchecked("addr2")) + .unwrap(); + assert_eq!(saved_claims.len(), 2); + assert_eq!(saved_claims_addr2.len(), 1); + } + + #[test] + fn test_claim_tokens_with_no_claims() { + let mut deps = mock_dependencies(&[]); + let claims = Claims::new("claims"); + + let amount = claims + .claim_tokens( + deps.as_mut().storage, + &Addr::unchecked("addr"), + &mock_env().block, + None, + ) + .unwrap(); + let saved_claims = claims + .0 + .load(deps.as_mut().storage, &Addr::unchecked("addr")) + .unwrap(); + + assert_eq!(amount, Uint128::zero()); + assert_eq!(saved_claims.len(), 0); + } + + #[test] + fn test_claim_tokens_with_no_released_claims() { + let mut deps = mock_dependencies(&[]); + let claims = Claims::new("claims"); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + (TEST_AMOUNT + 100).into(), + Expiration::AtHeight(10), + ) + .unwrap(); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + (TEST_AMOUNT + 100).into(), + Expiration::AtHeight(100), + ) + .unwrap(); + + let mut env = mock_env(); + env.block.height = 0; + // the address has two claims however they are both not expired + let amount = claims + .claim_tokens( + deps.as_mut().storage, + &Addr::unchecked("addr"), + &env.block, + None, + ) + .unwrap(); + + let saved_claims = claims + .0 + .load(deps.as_mut().storage, &Addr::unchecked("addr")) + .unwrap(); + + assert_eq!(amount, Uint128::zero()); + assert_eq!(saved_claims.len(), 2); + assert_eq!(saved_claims[0].amount, (TEST_AMOUNT + 100).into()); + assert_eq!(saved_claims[0].release_at, Expiration::AtHeight(10)); + assert_eq!(saved_claims[1].amount, (TEST_AMOUNT + 100).into()); + assert_eq!(saved_claims[1].release_at, Expiration::AtHeight(100)); + } + + #[test] + fn test_claim_tokens_with_one_released_claim() { + let mut deps = mock_dependencies(&[]); + let claims = Claims::new("claims"); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + TEST_AMOUNT.into(), + Expiration::AtHeight(10), + ) + .unwrap(); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + (TEST_AMOUNT + 100).into(), + Expiration::AtHeight(100), + ) + .unwrap(); + + let mut env = mock_env(); + env.block.height = 20; + // the address has two claims and the first one can be released + let amount = claims + .claim_tokens( + deps.as_mut().storage, + &Addr::unchecked("addr"), + &env.block, + None, + ) + .unwrap(); + + let saved_claims = claims + .0 + .load(deps.as_mut().storage, &Addr::unchecked("addr")) + .unwrap(); + + assert_eq!(amount, TEST_AMOUNT.into()); + assert_eq!(saved_claims.len(), 1); + assert_eq!(saved_claims[0].amount, (TEST_AMOUNT + 100).into()); + assert_eq!(saved_claims[0].release_at, Expiration::AtHeight(100)); + } + + #[test] + fn test_claim_tokens_with_all_released_claims() { + let mut deps = mock_dependencies(&[]); + let claims = Claims::new("claims"); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + TEST_AMOUNT.into(), + Expiration::AtHeight(10), + ) + .unwrap(); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + (TEST_AMOUNT + 100).into(), + Expiration::AtHeight(100), + ) + .unwrap(); + + let mut env = mock_env(); + env.block.height = 1000; + // the address has two claims and both can be released + let amount = claims + .claim_tokens( + deps.as_mut().storage, + &Addr::unchecked("addr"), + &env.block, + None, + ) + .unwrap(); + + let saved_claims = claims + .0 + .load(deps.as_mut().storage, &Addr::unchecked("addr")) + .unwrap(); + + assert_eq!(amount, (TEST_AMOUNT + TEST_AMOUNT + 100).into()); + assert_eq!(saved_claims.len(), 0); + } + + #[test] + fn test_claim_tokens_with_zero_cap() { + let mut deps = mock_dependencies(&[]); + let claims = Claims::new("claims"); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + TEST_AMOUNT.into(), + Expiration::AtHeight(10), + ) + .unwrap(); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + (TEST_AMOUNT + 100).into(), + Expiration::AtHeight(100), + ) + .unwrap(); + + let mut env = mock_env(); + env.block.height = 1000; + + let amount = claims + .claim_tokens( + deps.as_mut().storage, + &Addr::unchecked("addr"), + &env.block, + Some(Uint128::zero()), + ) + .unwrap(); + + let saved_claims = claims + .0 + .load(deps.as_mut().storage, &Addr::unchecked("addr")) + .unwrap(); + + assert_eq!(amount, Uint128::zero()); + assert_eq!(saved_claims.len(), 2); + assert_eq!(saved_claims[0].amount, (TEST_AMOUNT).into()); + assert_eq!(saved_claims[0].release_at, Expiration::AtHeight(10)); + assert_eq!(saved_claims[1].amount, (TEST_AMOUNT + 100).into()); + assert_eq!(saved_claims[1].release_at, Expiration::AtHeight(100)); + } + + #[test] + fn test_claim_tokens_with_cap_greater_than_pending_claims() { + let mut deps = mock_dependencies(&[]); + let claims = Claims::new("claims"); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + TEST_AMOUNT.into(), + Expiration::AtHeight(10), + ) + .unwrap(); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + (TEST_AMOUNT + 100).into(), + Expiration::AtHeight(100), + ) + .unwrap(); + + let mut env = mock_env(); + env.block.height = 1000; + + let amount = claims + .claim_tokens( + deps.as_mut().storage, + &Addr::unchecked("addr"), + &env.block, + Some(Uint128::from(2100u128)), + ) + .unwrap(); + + let saved_claims = claims + .0 + .load(deps.as_mut().storage, &Addr::unchecked("addr")) + .unwrap(); + + assert_eq!(amount, (TEST_AMOUNT + TEST_AMOUNT + 100).into()); + assert_eq!(saved_claims.len(), 0); + } + + #[test] + fn test_claim_tokens_with_cap_only_one_claim_released() { + let mut deps = mock_dependencies(&[]); + let claims = Claims::new("claims"); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + (TEST_AMOUNT + 100).into(), + Expiration::AtHeight(10), + ) + .unwrap(); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + TEST_AMOUNT.into(), + Expiration::AtHeight(5), + ) + .unwrap(); + + let mut env = mock_env(); + env.block.height = 1000; + // the address has two claims and the first one can be released + let amount = claims + .claim_tokens( + deps.as_mut().storage, + &Addr::unchecked("addr"), + &env.block, + Some((TEST_AMOUNT + 50).into()), + ) + .unwrap(); + assert_eq!(amount, (TEST_AMOUNT).into()); + + let saved_claims = claims + .0 + .load(deps.as_mut().storage, &Addr::unchecked("addr")) + .unwrap(); + assert_eq!(saved_claims.len(), 1); + assert_eq!(saved_claims[0].amount, (TEST_AMOUNT + 100).into()); + assert_eq!(saved_claims[0].release_at, Expiration::AtHeight(10)); + } + + #[test] + fn test_claim_tokens_with_cap_too_low_no_claims_released() { + let mut deps = mock_dependencies(&[]); + let claims = Claims::new("claims"); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + (TEST_AMOUNT + 100).into(), + Expiration::AtHeight(10), + ) + .unwrap(); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + TEST_AMOUNT.into(), + Expiration::AtHeight(5), + ) + .unwrap(); + + let mut env = mock_env(); + env.block.height = 1000; + // the address has two claims and the first one can be released + let amount = claims + .claim_tokens( + deps.as_mut().storage, + &Addr::unchecked("addr"), + &env.block, + Some((TEST_AMOUNT - 50).into()), + ) + .unwrap(); + assert_eq!(amount, Uint128::zero()); + + let saved_claims = claims + .0 + .load(deps.as_mut().storage, &Addr::unchecked("addr")) + .unwrap(); + assert_eq!(saved_claims.len(), 2); + assert_eq!(saved_claims[0].amount, (TEST_AMOUNT + 100).into()); + assert_eq!(saved_claims[0].release_at, Expiration::AtHeight(10)); + assert_eq!(saved_claims[1].amount, (TEST_AMOUNT).into()); + assert_eq!(saved_claims[1].release_at, Expiration::AtHeight(5)); + } + + #[test] + fn test_query_claims_returns_correct_claims() { + let mut deps = mock_dependencies(&[]); + let claims = Claims::new("claims"); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + (TEST_AMOUNT + 100).into(), + Expiration::AtHeight(10), + ) + .unwrap(); + + let queried_claims = claims + .query_claims(deps.as_ref(), &Addr::unchecked("addr")) + .unwrap(); + let saved_claims = claims + .0 + .load(deps.as_mut().storage, &Addr::unchecked("addr")) + .unwrap(); + assert_eq!(queried_claims.claims, saved_claims); + } + + #[test] + fn test_query_claims_returns_empty_for_non_existent_user() { + let mut deps = mock_dependencies(&[]); + let claims = Claims::new("claims"); + + claims + .create_claim( + deps.as_mut().storage, + &Addr::unchecked("addr"), + (TEST_AMOUNT + 100).into(), + Expiration::AtHeight(10), + ) + .unwrap(); + + let queried_claims = claims + .query_claims(deps.as_ref(), &Addr::unchecked("addr2")) + .unwrap(); + + assert_eq!(queried_claims.claims.len(), 0); + } +}