From 7350d5222c1e2795547e78f65e9135322b14a60f Mon Sep 17 00:00:00 2001 From: Artus Date: Wed, 24 Jul 2019 15:55:08 +0200 Subject: [PATCH] adds basic resolve_claims + test --- lootalot_db/src/lib.rs | 106 ++++++++++++++++++++++----------- lootalot_db/src/models/item.rs | 4 +- 2 files changed, 74 insertions(+), 36 deletions(-) diff --git a/lootalot_db/src/lib.rs b/lootalot_db/src/lib.rs index 2c4d629..8d44ed9 100644 --- a/lootalot_db/src/lib.rs +++ b/lootalot_db/src/lib.rs @@ -42,7 +42,7 @@ pub type ActionResult = Result; /// x .sell_loot([players], [excluded_item_ids]) -> Success status (bool, player_share) /// // Claims should be resolved after a certain delay /// x .set_claims_timeout() -/// x .resolve_claims() +/// v .resolve_claims() /// v .add_player(player_data) /// ``` /// @@ -126,7 +126,7 @@ impl<'q> AsPlayer<'q> { /// # Panics /// /// This currently panics if player wealth fails to be updated, as this is - /// a serious error. TODO: handle deletion of bought item in case of wealth update failure. + /// a serious error. pub fn buy<'a>(self, name: &'a str, price: i32) -> ActionResult<(i32, i32, i32, i32)> { self.conn.transaction(|| { let new_item = models::item::NewLoot::to_player(self.id, (name, price)); @@ -146,31 +146,25 @@ impl<'q> AsPlayer<'q> { /// # Panics /// /// This currently panics if player wealth fails to be updated, as this is - /// a serious error. TODO: handle restoring of sold item in case of wealth update failure. + /// a serious error. pub fn sell( self, loot_id: i32, _price_mod: Option, ) -> ActionResult<(i32, i32, i32, i32)> { - // Check that the item belongs to player - let exists_and_owned: bool = - diesel::select(models::Loot::owns(self.id, loot_id)).get_result(self.conn)?; - if !exists_and_owned { - return Err(diesel::result::Error::NotFound); - } self.conn.transaction(|| { use schema::looted::dsl::*; - let loot_value = looted + let loot = looted .find(loot_id) - .select(base_price) - .first::(self.conn)?; - let sell_value = (loot_value / 2) as f32; + .first::(self.conn)?; + if loot.owner != self.id { + // If the item does not belong to player, + // it can't be what we're looking for + return Err(diesel::result::Error::NotFound); + } + let sell_value = (loot.base_price / 2) as f32; let _deleted = diesel::delete(looted.find(loot_id)) - .execute(self.conn) - .map(|rows_updated| match rows_updated { - 1 => (), - _ => panic!("RuntimeError: Sell did not update DB as expected"), - })?; + .execute(self.conn)?; self.update_wealth(sell_value) }) @@ -271,10 +265,32 @@ impl<'q> AsAdmin<'q> { .grouped_by(&loot); // For each claimed item let data = loot.into_iter().zip(claims).collect::>(); - dbg!(data); - // If mutiples claims -> find highest resolve, give to this player - // If only one claim -> give to claiming - Err(diesel::result::Error::NotFound) + dbg!(&data); + + for (loot, claims) in data { + match claims.len() { + 1 => { + let claim = claims.get(0).unwrap(); + let player_id = claim.player_id; + self.0.transaction(|| { + use schema::looted::dsl::*; + diesel::update(looted.find(claim.loot_id)) + .set(owner_id.eq(player_id)) + .execute(self.0)?; + diesel::delete(schema::claims::table.find(claim.id)) + .execute(self.0)?; + { + use schema::players::dsl::*; + diesel::update(players.find(player_id)) + .set(debt.eq(debt + (loot.base_price / 2))) + .execute(self.0) + } + })?; + }, + _ => (), + } + } + Ok(()) } } @@ -304,7 +320,7 @@ mod tests { /// When migrations are run, a special player with id 0 and name "Groupe" /// must be created. #[test] - fn test_group_is_autocreated() { + fn global_group_is_autocreated() { let conn = test_connection(); let players = DbApi::with_conn(&conn).fetch_players().unwrap(); assert_eq!(players.len(), 1); @@ -316,7 +332,7 @@ mod tests { /// When a player updates wealth, a difference is returned by API. /// Added to the previous amount of coins, it should equal the updated weath. #[test] - fn test_player_updates_wealth() { + fn as_player_updates_wealth() { let conn = test_connection(); DbApi::with_conn(&conn) .as_admin() @@ -339,7 +355,7 @@ mod tests { } #[test] - fn test_admin_add_player() { + fn as_admin_add_player() { let conn = test_connection(); let result = DbApi::with_conn(&conn) .as_admin() @@ -356,15 +372,35 @@ mod tests { } #[test] - fn test_admin_resolve_claims() { + fn as_admin_resolve_claims() { let conn = test_connection(); let claims = DbApi::with_conn(&conn).fetch_claims().unwrap(); assert_eq!(claims.len(), 0); - assert_eq!(true, false); // Failing as test is not complete + + // Add items + assert_eq!(DbApi::with_conn(&conn).as_admin().add_loot(vec![ + ("Épée", 40), + ("Arc", 40), + ]).is_ok(), true); + // Add players + DbApi::with_conn(&conn).as_admin().add_player("Player1".to_string(), 0.0).unwrap(); + DbApi::with_conn(&conn).as_admin().add_player("Player2".to_string(), 0.0).unwrap(); + // Put claims on one different item each + DbApi::with_conn(&conn).as_player(1).claim(1).unwrap(); + DbApi::with_conn(&conn).as_player(2).claim(2).unwrap(); + let result = DbApi::with_conn(&conn).as_admin().resolve_claims(); + assert_eq!(result.is_ok(), true); + // Check that both players received an item + let players = DbApi::with_conn(&conn).fetch_players().unwrap(); + for &i in [1, 2].into_iter() { + assert_eq!(DbApi::with_conn(&conn).as_player(i).loot().unwrap().len(), 1); + let player = players.get(i as usize).unwrap(); + assert_eq!(player.debt, 20); + } } #[test] - fn test_player_claim_item() { + fn as_player_claim_item() { let conn = test_connection(); DbApi::with_conn(&conn) .as_admin() @@ -388,7 +424,7 @@ mod tests { } #[test] - fn test_player_unclaim_item() { + fn as_player_unclaim_item() { let conn = test_connection(); DbApi::with_conn(&conn) .as_admin() @@ -418,8 +454,9 @@ mod tests { /// /// Checks that player's chest and wealth are updated. /// Checks that items are sold at half their value. + /// Checks that a player cannot sell item he does not own. #[test] - fn test_buy_sell_simple() { + fn as_player_simple_buy_sell() { let conn = test_connection(); DbApi::with_conn(&conn) .as_admin() @@ -438,10 +475,11 @@ mod tests { let players = DbApi::with_conn(&conn).fetch_players().unwrap(); let player = players.get(1).unwrap(); assert_eq!(player.pp, 2); + // A player cannot sell loot from an other's chest + let result = DbApi::with_conn(&conn).as_player(0).sell(loot.id, None); + assert_eq!(result.is_ok(), false); // Sell back - let sold = DbApi::with_conn(&conn) - .as_player(1) - .sell(loot.id, None); + let sold = DbApi::with_conn(&conn).as_player(1).sell(loot.id, None); assert_eq!(sold.ok(), Some((0, 0, 0, 4))); let chest = DbApi::with_conn(&conn).as_player(1).loot().unwrap(); assert_eq!(chest.len(), 0); @@ -451,7 +489,7 @@ mod tests { } #[test] - fn test_admin_add_loot() { + fn as_admin_add_loot() { let conn = test_connection(); assert_eq!( 0, diff --git a/lootalot_db/src/models/item.rs b/lootalot_db/src/models/item.rs index bb0e90a..bce60f3 100644 --- a/lootalot_db/src/models/item.rs +++ b/lootalot_db/src/models/item.rs @@ -35,8 +35,8 @@ type OwnedLoot = Filter; pub(crate) struct Loot { id: i32, name: String, - base_price: i32, - owner: i32, + pub(crate) base_price: i32, + pub(crate) owner: i32, } impl Loot {