diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6200d4..aa37910 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: push: pull_request: -env: +env: CARGO_TERM_COLOR: always jobs: @@ -18,6 +18,6 @@ jobs: steps: - uses: actions/checkout@v3 - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} - - run: cargo build --verbose - - run: cargo test --verbose - + - run: rustup target add wasm32-unknown-unknown + - run: cargo wasm --verbose + - run: cargo unit-test --verbose diff --git a/contracts/orderbook/src/lib.rs b/contracts/orderbook/src/lib.rs index 7c6ba75..29d45d5 100644 --- a/contracts/orderbook/src/lib.rs +++ b/contracts/orderbook/src/lib.rs @@ -1,9 +1,12 @@ pub mod contract; mod error; pub mod msg; -pub mod types; -pub mod state; -mod orderbook; mod order; +mod orderbook; +pub mod state; +pub mod types; + +#[cfg(test)] +pub mod tests; pub use crate::error::ContractError; diff --git a/contracts/orderbook/src/orderbook.rs b/contracts/orderbook/src/orderbook.rs index 1677f61..bda243e 100644 --- a/contracts/orderbook/src/orderbook.rs +++ b/contracts/orderbook/src/orderbook.rs @@ -29,46 +29,3 @@ pub fn create_orderbook( .add_attribute("method", "createOrderbook") .add_attribute("book_id", book_id.to_string())) } - -#[cfg(test)] -mod tests { - use super::*; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - - #[test] - fn test_create_orderbook() { - let mut deps = mock_dependencies(); - let env = mock_env(); - let info = mock_info("creator", &[]); - - // Attempt to create an orderbook - let quote_denom = "quote".to_string(); - let base_denom = "base".to_string(); - let create_response = create_orderbook( - deps.as_mut(), - env, - info, - quote_denom.clone(), - base_denom.clone(), - ) - .unwrap(); - - // Verify response - let expected_book_id: u64 = 0; - assert_eq!(create_response.attributes[0], ("method", "createOrderbook")); - assert_eq!( - create_response.attributes[1], - ("book_id", &expected_book_id.to_string()) - ); - - // Verify orderbook is saved correctly - let orderbook = ORDERBOOKS - .load(deps.as_ref().storage, &expected_book_id) - .unwrap(); - assert_eq!(orderbook.quote_denom, quote_denom); - assert_eq!(orderbook.base_denom, base_denom); - assert_eq!(orderbook.current_tick, 0); - assert_eq!(orderbook.next_bid_tick, MIN_TICK); - assert_eq!(orderbook.next_ask_tick, MAX_TICK); - } -} diff --git a/contracts/orderbook/src/state.rs b/contracts/orderbook/src/state.rs index 04948e9..d23bdd1 100644 --- a/contracts/orderbook/src/state.rs +++ b/contracts/orderbook/src/state.rs @@ -70,7 +70,6 @@ pub fn new_order_id(storage: &mut dyn Storage) -> Result { // TODO: Add pagination // TODO: How finite do we need queries? - /// Retrieves a list of `LimitOrder` filtered by the specified `FilterOwnerOrders`. pub fn get_orders_by_owner( storage: &dyn Storage, @@ -104,211 +103,3 @@ pub fn get_orders_by_owner( }; Ok(orders) } - -#[cfg(test)] -mod test { - use super::*; - use crate::types::OrderDirection; - use cosmwasm_std::testing::MockStorage; - use cosmwasm_std::{Addr, Order}; - - #[test] - fn test_new_orderbook_id() { - let mut storage = MockStorage::new(); - let id = new_orderbook_id(&mut storage).unwrap(); - assert_eq!(id, 0); - let id = new_orderbook_id(&mut storage).unwrap(); - assert_eq!(id, 1); - } - - #[test] - fn test_order_id_works() { - let mut storage = MockStorage::new(); - let id = new_order_id(&mut storage).unwrap(); - assert_eq!(id, 0); - let id = new_order_id(&mut storage).unwrap(); - assert_eq!(id, 1); - } - - #[test] - fn test_tick_iteration() { - let mut storage = MockStorage::new(); - let book_id = new_orderbook_id(&mut storage).unwrap(); - let tick_amount = 50; - for i in -tick_amount..tick_amount { - TICK_LIQUIDITY - .save(&mut storage, &(book_id, i), &Uint128::new(i as u128)) - .unwrap(); - } - let prefix = TICK_LIQUIDITY.prefix(book_id); - let ticks_asc: Vec = prefix - .keys(&storage, None, None, Order::Ascending) - .map(|result| result.unwrap()) - .collect(); - let ticks_desc: Vec = prefix - .keys(&storage, None, None, Order::Descending) - .map(|result| result.unwrap()) - .collect(); - for i in 0..tick_amount * 2 { - assert_eq!(ticks_asc[i as usize], -tick_amount + i); - assert_eq!(ticks_desc[i as usize], tick_amount - i - 1); - } - } - - #[test] - fn test_order_iteration() { - let mut storage = MockStorage::new(); - let order_amount = 50; - let book_id = new_orderbook_id(&mut storage).unwrap(); - let tick = 0; - for i in 0..order_amount { - let order_id = new_order_id(&mut storage).unwrap(); - let order = LimitOrder { - tick_id: tick, - book_id, - order_id, - owner: Addr::unchecked(format!("maker{i}")), - quantity: Uint128::new(i as u128), - order_direction: OrderDirection::Ask, - }; - orders() - .save(&mut storage, &(book_id, tick, i), &order) - .unwrap(); - } - - let tick_orders = orders().prefix((book_id, tick)); - let orders_desc: Vec = tick_orders - .range(&storage, None, None, Order::Descending) - .map(|result| result.unwrap().1) - .collect(); - let orders_asc: Vec = tick_orders - .range(&storage, None, None, Order::Ascending) - .map(|result| result.unwrap().1) - .collect(); - for i in 0..order_amount { - assert_eq!(orders_desc[i as usize].order_id, order_amount - i - 1); - assert_eq!(orders_asc[i as usize].order_id, i); - } - } - - #[test] - fn test_get_orders_by_owner_all() { - let mut storage = MockStorage::new(); - let order_amount = 10; - let owner = "owner1"; - - let book_ids: Vec = (0..3) - .map(|_| new_orderbook_id(&mut storage).unwrap()) - .collect(); - - (0..order_amount).for_each(|i| { - let order_id = new_order_id(&mut storage).unwrap(); - let other_owner = &format!("owner{i}"); - let current_owner = Addr::unchecked(if i % 2 == 0 { owner } else { other_owner }); - let order = LimitOrder::new( - book_ids[i % 3], - 0, - order_id, - OrderDirection::Ask, - current_owner, - Uint128::new(i as u128), - ); - orders() - .save(&mut storage, &(order.book_id, 0, i as u64), &order) - .unwrap(); - }); - - let owner_orders: Vec = - get_orders_by_owner(&storage, FilterOwnerOrders::All(Addr::unchecked(owner))).unwrap(); - - assert_eq!(owner_orders.len(), order_amount / 2 + 1); - owner_orders.iter().for_each(|order| { - assert_eq!(order.owner, Addr::unchecked(owner)); - }); - } - - #[test] - fn test_get_orders_by_owner_by_book() { - let mut storage = MockStorage::new(); - let order_amount = 100; - let owner = "owner1"; - - // Generate three new book IDs - let book_ids: Vec = (0..3) - .map(|_| new_orderbook_id(&mut storage).unwrap()) - .collect(); - - // Create orders alternating ownership between `owner` and dynamically generated owners amongst all books evenly - (0..order_amount).for_each(|i| { - let order_id = new_order_id(&mut storage).unwrap(); - let other_owner = &format!("owner{i}"); - let current_owner = Addr::unchecked(if i % 2 == 0 { owner } else { other_owner }); - let order = LimitOrder::new( - book_ids[i % 3], - 0, - order_id, - OrderDirection::Ask, - current_owner, - Uint128::new(i as u128), - ); - orders() - .save(&mut storage, &(order.book_id, 0, i as u64), &order) - .unwrap(); - }); - - // Verify orders by book ID - book_ids.iter().for_each(|&book_id| { - let owner_orders = get_orders_by_owner( - &storage, - FilterOwnerOrders::ByBook(book_id, Addr::unchecked(owner)), - ) - .unwrap(); - assert!(!owner_orders.is_empty()); - owner_orders.iter().for_each(|order| { - assert_eq!(order.owner, Addr::unchecked(owner)); - assert_eq!(order.book_id, book_id); - }); - }); - } - - #[test] - fn test_get_orders_by_owner_by_tick() { - let mut storage = MockStorage::new(); - let order_amount = 100; - let ticks = [0, 1, 2]; - let owner = "owner1"; - let book_id = new_orderbook_id(&mut storage).unwrap(); - - // Create orders alternating ownership between `owner` and dynamically generated owners amongst all ticks evenly - (0..order_amount).for_each(|i| { - let order_id = new_order_id(&mut storage).unwrap(); - let other_owner = &format!("owner{i}"); - let current_owner = Addr::unchecked(if i % 2 == 0 { owner } else { other_owner }); - let tick = ticks[i % 3]; - let order = LimitOrder::new( - book_id, - tick, - order_id, - OrderDirection::Ask, - current_owner, - Uint128::new(i as u128), - ); - orders() - .save(&mut storage, &(book_id, tick, i as u64), &order) - .unwrap(); - }); - - ticks.iter().for_each(|&tick| { - let owner_orders = get_orders_by_owner( - &storage, - FilterOwnerOrders::ByTick(book_id, tick, Addr::unchecked(owner)), - ) - .unwrap(); - assert!(!owner_orders.is_empty()); - owner_orders.iter().for_each(|order| { - assert_eq!(order.owner, Addr::unchecked(owner)); - assert_eq!(order.tick_id, tick); - }); - }); - } -} diff --git a/contracts/orderbook/src/tests/mod.rs b/contracts/orderbook/src/tests/mod.rs new file mode 100644 index 0000000..38f2823 --- /dev/null +++ b/contracts/orderbook/src/tests/mod.rs @@ -0,0 +1,2 @@ +pub mod test_orderbook; +pub mod test_state; diff --git a/contracts/orderbook/src/tests/test_orderbook.rs b/contracts/orderbook/src/tests/test_orderbook.rs new file mode 100644 index 0000000..6a8dd00 --- /dev/null +++ b/contracts/orderbook/src/tests/test_orderbook.rs @@ -0,0 +1,42 @@ +use crate::{ + orderbook::*, + state::{MAX_TICK, MIN_TICK, ORDERBOOKS}, +}; +use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + +#[test] +fn test_create_orderbook() { + let mut deps = mock_dependencies(); + let env = mock_env(); + let info = mock_info("creator", &[]); + + // Attempt to create an orderbook + let quote_denom = "quote".to_string(); + let base_denom = "base".to_string(); + let create_response = create_orderbook( + deps.as_mut(), + env, + info, + quote_denom.clone(), + base_denom.clone(), + ) + .unwrap(); + + // Verify response + let expected_book_id: u64 = 0; + assert_eq!(create_response.attributes[0], ("method", "createOrderbook")); + assert_eq!( + create_response.attributes[1], + ("book_id", &expected_book_id.to_string()) + ); + + // Verify orderbook is saved correctly + let orderbook = ORDERBOOKS + .load(deps.as_ref().storage, &expected_book_id) + .unwrap(); + assert_eq!(orderbook.quote_denom, quote_denom); + assert_eq!(orderbook.base_denom, base_denom); + assert_eq!(orderbook.current_tick, 0); + assert_eq!(orderbook.next_bid_tick, MIN_TICK); + assert_eq!(orderbook.next_ask_tick, MAX_TICK); +} diff --git a/contracts/orderbook/src/tests/test_state.rs b/contracts/orderbook/src/tests/test_state.rs new file mode 100644 index 0000000..7108a35 --- /dev/null +++ b/contracts/orderbook/src/tests/test_state.rs @@ -0,0 +1,204 @@ +use crate::state::*; +use crate::types::{FilterOwnerOrders, LimitOrder, OrderDirection}; +use cosmwasm_std::testing::MockStorage; +use cosmwasm_std::{Addr, Order, Uint128}; + +#[test] +fn test_new_orderbook_id() { + let mut storage = MockStorage::new(); + let id = new_orderbook_id(&mut storage).unwrap(); + assert_eq!(id, 0); + let id = new_orderbook_id(&mut storage).unwrap(); + assert_eq!(id, 1); +} + +#[test] +fn test_order_id_works() { + let mut storage = MockStorage::new(); + let id = new_order_id(&mut storage).unwrap(); + assert_eq!(id, 0); + let id = new_order_id(&mut storage).unwrap(); + assert_eq!(id, 1); +} + +#[test] +fn test_tick_iteration() { + let mut storage = MockStorage::new(); + let book_id = new_orderbook_id(&mut storage).unwrap(); + let tick_amount = 50; + for i in -tick_amount..tick_amount { + TICK_LIQUIDITY + .save(&mut storage, &(book_id, i), &Uint128::new(i as u128)) + .unwrap(); + } + let prefix = TICK_LIQUIDITY.prefix(book_id); + let ticks_asc: Vec = prefix + .keys(&storage, None, None, Order::Ascending) + .map(|result| result.unwrap()) + .collect(); + let ticks_desc: Vec = prefix + .keys(&storage, None, None, Order::Descending) + .map(|result| result.unwrap()) + .collect(); + for i in 0..tick_amount * 2 { + assert_eq!(ticks_asc[i as usize], -tick_amount + i); + assert_eq!(ticks_desc[i as usize], tick_amount - i - 1); + } +} + +#[test] +fn test_order_iteration() { + let mut storage = MockStorage::new(); + let order_amount = 50; + let book_id = new_orderbook_id(&mut storage).unwrap(); + let tick = 0; + for i in 0..order_amount { + let order_id = new_order_id(&mut storage).unwrap(); + let order = LimitOrder { + tick_id: tick, + book_id, + order_id, + owner: Addr::unchecked(format!("maker{i}")), + quantity: Uint128::new(i as u128), + order_direction: OrderDirection::Ask, + }; + orders() + .save(&mut storage, &(book_id, tick, i), &order) + .unwrap(); + } + + let tick_orders = orders().prefix((book_id, tick)); + let orders_desc: Vec = tick_orders + .range(&storage, None, None, Order::Descending) + .map(|result| result.unwrap().1) + .collect(); + let orders_asc: Vec = tick_orders + .range(&storage, None, None, Order::Ascending) + .map(|result| result.unwrap().1) + .collect(); + for i in 0..order_amount { + assert_eq!(orders_desc[i as usize].order_id, order_amount - i - 1); + assert_eq!(orders_asc[i as usize].order_id, i); + } +} + +#[test] +fn test_get_orders_by_owner_all() { + let mut storage = MockStorage::new(); + let order_amount = 10; + let owner = "owner1"; + + let book_ids: Vec = (0..3) + .map(|_| new_orderbook_id(&mut storage).unwrap()) + .collect(); + + (0..order_amount).for_each(|i| { + let order_id = new_order_id(&mut storage).unwrap(); + let other_owner = &format!("owner{i}"); + let current_owner = Addr::unchecked(if i % 2 == 0 { owner } else { other_owner }); + let order = LimitOrder::new( + book_ids[i % 3], + 0, + order_id, + OrderDirection::Ask, + current_owner, + Uint128::new(i as u128), + ); + orders() + .save(&mut storage, &(order.book_id, 0, i as u64), &order) + .unwrap(); + }); + + let owner_orders: Vec = + get_orders_by_owner(&storage, FilterOwnerOrders::All(Addr::unchecked(owner))).unwrap(); + + assert_eq!(owner_orders.len(), order_amount / 2 + 1); + owner_orders.iter().for_each(|order| { + assert_eq!(order.owner, Addr::unchecked(owner)); + }); +} + +#[test] +fn test_get_orders_by_owner_by_book() { + let mut storage = MockStorage::new(); + let order_amount = 100; + let owner = "owner1"; + + // Generate three new book IDs + let book_ids: Vec = (0..3) + .map(|_| new_orderbook_id(&mut storage).unwrap()) + .collect(); + + // Create orders alternating ownership between `owner` and dynamically generated owners amongst all books evenly + (0..order_amount).for_each(|i| { + let order_id = new_order_id(&mut storage).unwrap(); + let other_owner = &format!("owner{i}"); + let current_owner = Addr::unchecked(if i % 2 == 0 { owner } else { other_owner }); + let order = LimitOrder::new( + book_ids[i % 3], + 0, + order_id, + OrderDirection::Ask, + current_owner, + Uint128::new(i as u128), + ); + orders() + .save(&mut storage, &(order.book_id, 0, i as u64), &order) + .unwrap(); + }); + + // Verify orders by book ID + book_ids.iter().for_each(|&book_id| { + let owner_orders = get_orders_by_owner( + &storage, + FilterOwnerOrders::ByBook(book_id, Addr::unchecked(owner)), + ) + .unwrap(); + assert!(!owner_orders.is_empty()); + owner_orders.iter().for_each(|order| { + assert_eq!(order.owner, Addr::unchecked(owner)); + assert_eq!(order.book_id, book_id); + }); + }); +} + +#[test] +fn test_get_orders_by_owner_by_tick() { + let mut storage = MockStorage::new(); + let order_amount = 100; + let ticks = [0, 1, 2]; + let owner = "owner1"; + let book_id = new_orderbook_id(&mut storage).unwrap(); + + // Create orders alternating ownership between `owner` and dynamically generated owners amongst all ticks evenly + (0..order_amount).for_each(|i| { + let order_id = new_order_id(&mut storage).unwrap(); + let other_owner = &format!("owner{i}"); + let current_owner = Addr::unchecked(if i % 2 == 0 { owner } else { other_owner }); + let tick = ticks[i % 3]; + let order = LimitOrder::new( + book_id, + tick, + order_id, + OrderDirection::Ask, + current_owner, + Uint128::new(i as u128), + ); + orders() + .save(&mut storage, &(book_id, tick, i as u64), &order) + .unwrap(); + }); + + ticks.iter().for_each(|&tick| { + let owner_orders = get_orders_by_owner( + &storage, + FilterOwnerOrders::ByTick(book_id, tick, Addr::unchecked(owner)), + ) + .unwrap(); + assert!(!owner_orders.is_empty()); + owner_orders.iter().for_each(|order| { + assert_eq!(order.owner, Addr::unchecked(owner)); + assert_eq!(order.tick_id, tick); + }); + }); +}