From 4652fa52cf747caf42e0e059891c26252f8798a6 Mon Sep 17 00:00:00 2001 From: Condorra Date: Sun, 19 Feb 2023 14:03:15 +1100 Subject: [PATCH] Support mocking DB to increase testability. --- Cargo.lock | 110 ++++++++++++++++++ blastmud_game/Cargo.toml | 2 + blastmud_game/src/db.rs | 64 +++++----- blastmud_game/src/message_handler.rs | 11 ++ .../src/message_handler/user_commands.rs | 37 +++++- .../message_handler/user_commands/movement.rs | 3 +- .../src/message_handler/user_commands/say.rs | 3 +- blastmud_game/src/regular_tasks.rs | 18 ++- blastmud_game/src/services.rs | 3 +- blastmud_game/src/services/capacity.rs | 31 ++++- blastmud_game/src/services/combat.rs | 3 +- blastmud_game/src/services/skills.rs | 3 +- 12 files changed, 240 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62a9a46..c59d362 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,6 +151,8 @@ dependencies = [ "humantime", "itertools", "log", + "mockall", + "mockall_double", "nix", "nom", "once_cell", @@ -479,6 +481,12 @@ dependencies = [ "tokio", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.10.6" @@ -490,6 +498,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "educe" version = "0.4.20" @@ -537,6 +551,15 @@ dependencies = [ "instant", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -552,6 +575,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + [[package]] name = "futures" version = "0.3.25" @@ -1019,6 +1048,45 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "mockall" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e4a1c770583dac7ab5e2f6c139153b783a53a1bbee9729613f193e59828326" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832663583d5fa284ca8810bf7015e46c9fff9622d3cf34bd1eea5003fec06dd0" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "mockall_double" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae71c7bb287375187c775cf82e2dcf1bef3388aaf58f0789a77f9c7ab28466f6" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "multipart" version = "0.18.0" @@ -1061,6 +1129,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "num-bigint" version = "0.4.3" @@ -1281,6 +1355,36 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "predicates" +version = "2.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +dependencies = [ + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" + +[[package]] +name = "predicates-tree" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -1780,6 +1884,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "termtree" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" + [[package]] name = "thiserror" version = "1.0.38" diff --git a/blastmud_game/Cargo.toml b/blastmud_game/Cargo.toml index 1df208c..dde6344 100644 --- a/blastmud_game/Cargo.toml +++ b/blastmud_game/Cargo.toml @@ -39,3 +39,5 @@ async-recursion = "1.0.0" rand_distr = "0.4.3" humantime = "2.1.0" rust_decimal = "1.28.0" +mockall = "0.11.3" +mockall_double = "0.3.0" diff --git a/blastmud_game/src/db.rs b/blastmud_game/src/db.rs index 80ac20c..987357b 100644 --- a/blastmud_game/src/db.rs +++ b/blastmud_game/src/db.rs @@ -25,6 +25,8 @@ use serde::{Serialize, Deserialize}; use serde_json::{self, Value}; use futures::FutureExt; use chrono::{DateTime, Utc}; +#[cfg(test)] +use mockall::automock; #[derive(Clone, Debug)] pub struct DBPool { @@ -70,6 +72,7 @@ pub struct LocationStats { pub total_weight: u64, } +#[cfg_attr(test, allow(dead_code))] impl DBPool { pub async fn record_listener_ping(self: &DBPool, listener: Uuid) -> DResult<()> { self.get_conn().await?.execute( @@ -246,18 +249,19 @@ impl ItemSearchParams<'_> { } } - +#[cfg_attr(test, automock)] +#[cfg_attr(test, allow(dead_code))] impl DBTrans { - pub async fn queue_for_session(self: &Self, - session: &ListenerSession, - message: Option<&str>) -> DResult<()> { + pub async fn queue_for_session<'a>(self: &'a Self, + session: &'a ListenerSession, + message: Option<&'a str>) -> DResult<()> { self.pg_trans()? .execute("INSERT INTO sendqueue (session, listener, message) VALUES ($1, $2, $3)", &[&session.session, &session.listener, &message]).await?; Ok(()) } - pub async fn get_session_user_model(self: &Self, session: &ListenerSession) -> DResult)>> { + pub async fn get_session_user_model<'a>(self: &'a Self, session: &'a ListenerSession) -> DResult)>> { match self.pg_trans()? .query_opt("SELECT s.details AS sess_details, \ u.details AS user_details FROM sessions s \ @@ -277,7 +281,7 @@ impl DBTrans { } } - pub async fn save_session_model(self: &Self, session: &ListenerSession, details: &Session) + pub async fn save_session_model<'a>(self: &'a Self, session: &'a ListenerSession, details: &Session) -> DResult<()> { self.pg_trans()? .execute("UPDATE sessions SET details = $1 WHERE session = $2", @@ -285,7 +289,7 @@ impl DBTrans { Ok(()) } - pub async fn find_by_username(self: &Self, username: &str) -> DResult> { + pub async fn find_by_username<'a>(self: &'a Self, username: &'a str) -> DResult> { if let Some(details_json) = self.pg_trans()? .query_opt("SELECT details FROM users WHERE username=$1", &[&username.to_lowercase()]).await? { @@ -294,13 +298,13 @@ impl DBTrans { Ok(None) } - pub async fn create_item(self: &Self, item: &Item) -> DResult { + pub async fn create_item<'a>(self: &'a Self, item: &'a Item) -> DResult { Ok(self.pg_trans()?.query_one("INSERT INTO items (details) VALUES ($1) RETURNING item_id", &[&serde_json::to_value(item)?]).await? .get("item_id")) } - pub async fn limited_update_static_item(self: &Self, item: &Item) -> DResult<()> { + pub async fn limited_update_static_item<'a>(self: &'a Self, item: &'a Item) -> DResult<()> { let value = serde_json::to_value(item)?; let obj_map = value.as_object() .expect("Static item to be object in JSON"); @@ -323,7 +327,7 @@ impl DBTrans { Ok(()) } - pub async fn limited_update_static_task(self: &Self, task: &Task) -> DResult<()> { + pub async fn limited_update_static_task<'a>(self: &'a Self, task: &'a Task) -> DResult<()> { let value = serde_json::to_value(task)?; let obj_map = value.as_object() .expect("Static task to be object in JSON"); @@ -346,7 +350,7 @@ impl DBTrans { } - pub async fn create_user(self: &Self, session: &ListenerSession, user_dat: &User) -> DResult<()> { + pub async fn create_user<'a>(self: &'a Self, session: &'a ListenerSession, user_dat: &'a User) -> DResult<()> { self.pg_trans()?.execute("INSERT INTO users (\ username, current_session, current_listener, details\ ) VALUES ($1, $2, $3, $4)", &[&user_dat.username.to_lowercase(), @@ -356,7 +360,7 @@ impl DBTrans { Ok(()) } - pub async fn save_user_model(self: &Self, details: &User) + pub async fn save_user_model<'a>(self: &'a Self, details: &'a User) -> DResult<()> { self.pg_trans()? .execute("UPDATE users SET details = $1 WHERE username = $2", @@ -365,8 +369,8 @@ impl DBTrans { Ok(()) } - pub async fn attach_user_to_session(self: &Self, username: &str, - session: &ListenerSession) -> DResult<()> { + pub async fn attach_user_to_session<'a>(self: &'a Self, username: &'a str, + session: &'a ListenerSession) -> DResult<()> { let username_l = username.to_lowercase(); self.pg_trans()? .execute("INSERT INTO sendqueue (session, listener, message) \ @@ -386,7 +390,7 @@ impl DBTrans { Ok(()) } - pub async fn find_static_items_by_type(self: &Self, item_type: &str) -> + pub async fn find_static_items_by_type<'a>(self: &'a Self, item_type: &'a str) -> DResult>> { Ok(Box::new( self.pg_trans()? @@ -399,7 +403,7 @@ impl DBTrans { .collect())) } - pub async fn find_static_tasks_by_type(self: &Self, task_type: &str) -> + pub async fn find_static_tasks_by_type<'a>(self: &'a Self, task_type: &'a str) -> DResult>> { Ok(Box::new( self.pg_trans()? @@ -412,7 +416,7 @@ impl DBTrans { .collect())) } - pub async fn delete_static_items_by_code(self: &Self, item_type: &str, + pub async fn delete_static_items_by_code<'a>(self: &'a Self, item_type: &'a str, item_code: &str) -> DResult<()> { self.pg_trans()?.query( "DELETE FROM items WHERE details->>'is_static' = 'true' AND \ @@ -422,8 +426,8 @@ impl DBTrans { Ok(()) } - pub async fn delete_static_tasks_by_code(self: &Self, task_type: &str, - task_code: &str) -> DResult<()> { + pub async fn delete_static_tasks_by_code<'a>(self: &'a Self, task_type: &'a str, + task_code: &'a str) -> DResult<()> { self.pg_trans()?.query( "DELETE FROM task WHERE details->>'is_static' = 'true' AND \ details->>'task_type' = $1 AND \ @@ -432,7 +436,7 @@ impl DBTrans { Ok(()) } - pub async fn find_item_by_type_code(self: &Self, item_type: &str, item_code: &str) -> + pub async fn find_item_by_type_code<'a>(self: &'a Self, item_type: &'a str, item_code: &'a str) -> DResult>> { if let Some(item) = self.pg_trans()?.query_opt( "SELECT details FROM items WHERE \ @@ -443,7 +447,7 @@ impl DBTrans { Ok(None) } - pub async fn transfer_all_possessions_code(self: &Self, src_loc: &str, dst_loc: &str) -> DResult<()> { + pub async fn transfer_all_possessions_code<'a>(self: &'a Self, src_loc: &'a str, dst_loc: &'a str) -> DResult<()> { self.pg_trans()?.execute( "UPDATE items SET details=JSONB_SET(details, '{location}', $1) \ WHERE details->>'location' = $2", @@ -451,14 +455,14 @@ impl DBTrans { Ok(()) } - pub async fn transfer_all_possessions(self: &Self, source: &Item, dest: &Item) -> DResult<()> { + pub async fn transfer_all_possessions<'a>(self: &'a Self, source: &'a Item, dest: &'a Item) -> DResult<()> { let src_loc = format!("{}/{}", &source.item_type, &source.item_code); let dst_loc = format!("{}/{}", &dest.item_type, &dest.item_code); self.transfer_all_possessions_code(&src_loc, &dst_loc).await?; Ok(()) } - pub async fn find_items_by_location(self: &Self, location: &str) -> DResult>> { + pub async fn find_items_by_location<'a>(self: &'a Self, location: &'a str) -> DResult>> { Ok(self.pg_trans()?.query( "SELECT details FROM items WHERE details->>'location' = $1 \ ORDER BY details->>'display' @@ -480,7 +484,7 @@ impl DBTrans { Ok(()) } - pub async fn delete_item(self: &Self, item_type: &str, item_code: &str) -> DResult<()> { + pub async fn delete_item<'a>(self: &'a Self, item_type: &'a str, item_code: &'a str) -> DResult<()> { self.pg_trans()? .execute("DELETE FROM items WHERE \ details->>'item_type' = $1 AND \ @@ -489,7 +493,7 @@ impl DBTrans { Ok(()) } - pub async fn find_session_for_player(self: &Self, item_code: &str) -> DResult> { + pub async fn find_session_for_player<'a>(self: &'a Self, item_code: &'a str) -> DResult> { Ok(self.pg_trans()? .query_opt("SELECT u.current_listener, u.current_session, s.details \ FROM users u JOIN sessions s ON s.session = u.current_session \ @@ -505,7 +509,7 @@ impl DBTrans { } pub async fn resolve_items_by_display_name_for_player<'l>( - self: &Self, + self: &'l Self, search: &'l ItemSearchParams<'l> ) -> DResult>>> { let mut ctes: Vec = Vec::new(); @@ -587,7 +591,7 @@ impl DBTrans { } } - pub async fn delete_task(&self, task_type: &str, task_code: &str) -> DResult<()> { + pub async fn delete_task<'a>(&'a self, task_type: &'a str, task_code: &'a str) -> DResult<()> { self.pg_trans()?.execute( "DELETE FROM tasks WHERE details->>'task_type' = $1 AND \ details->>'task_code' = $2", &[&task_type, &task_code] @@ -595,7 +599,7 @@ impl DBTrans { Ok(()) } - pub async fn upsert_task(&self, task: &Task) -> DResult<()> { + pub async fn upsert_task<'a>(&'a self, task: &'a Task) -> DResult<()> { self.pg_trans()?.execute( "INSERT INTO tasks (details) \ VALUES ($1) \ @@ -604,7 +608,7 @@ impl DBTrans { Ok(()) } - pub async fn update_task(&self, task_type: &str, task_code: &str, task: &TaskParse) -> DResult<()> { + pub async fn update_task<'a>(&'a self, task_type: &'a str, task_code: &'a str, task: &'a TaskParse) -> DResult<()> { self.pg_trans()?.execute( "UPDATE tasks SET details = $3 WHERE details->>'task_type' = $1 AND \ details->>'task_code' = $2", @@ -681,7 +685,7 @@ impl DBTrans { Ok(()) } - pub fn pg_trans(self: &Self) -> DResult<&Transaction> { + pub fn pg_trans<'a>(self: &'a Self) -> DResult<&'a Transaction<'a>> { self.borrow_trans().as_ref().ok_or("Transaction already closed".into()) } } diff --git a/blastmud_game/src/message_handler.rs b/blastmud_game/src/message_handler.rs index 91493bd..65cf9b5 100644 --- a/blastmud_game/src/message_handler.rs +++ b/blastmud_game/src/message_handler.rs @@ -13,6 +13,17 @@ pub struct ListenerSession { pub session: Uuid } +#[cfg(test)] +impl Default for ListenerSession { + fn default() -> ListenerSession { + use uuid::uuid; + ListenerSession { + listener: uuid!("6f9c9b61-9228-4427-abd7-c4aef127a862"), + session: uuid!("668efb68-79d3-4004-9d6a-1e5757792e1a") + } + } +} + pub async fn handle(listener: Uuid, msg: MessageFromListener, pool: db::DBPool) -> DResult<()> { match msg { diff --git a/blastmud_game/src/message_handler/user_commands.rs b/blastmud_game/src/message_handler/user_commands.rs index f36892a..d6c2348 100644 --- a/blastmud_game/src/message_handler/user_commands.rs +++ b/blastmud_game/src/message_handler/user_commands.rs @@ -1,11 +1,13 @@ use super::ListenerSession; use crate::DResult; -use crate::db::{DBTrans, DBPool, ItemSearchParams}; -use ansi::ansi; +use crate::db::{DBPool, ItemSearchParams}; +use mockall_double::double; +#[double] use crate::db::DBTrans; +#[cfg(not(test))] use ansi::ansi; use phf::phf_map; use async_trait::async_trait; use crate::models::{session::Session, user::User, item::Item}; -use log::warn; +#[cfg(not(test))] use log::warn; use once_cell::sync::OnceCell; use std::sync::Arc; @@ -149,6 +151,7 @@ fn resolve_handler(ctx: &VerbContext, cmd: &str) -> Option<&'static UserVerbRef> result } +#[cfg(not(test))] pub async fn handle(session: &ListenerSession, msg: &str, pool: &DBPool) -> DResult<()> { let (cmd, params) = parsing::parse_command_name(msg); let trans = pool.start_transaction().await?; @@ -161,11 +164,11 @@ pub async fn handle(session: &ListenerSession, msg: &str, pool: &DBPool) -> DRes } Some(v) => v }; - + let mut ctx = VerbContext { session, trans: &trans, session_dat: &mut session_dat, user_dat: &mut user_dat }; let handler_opt = resolve_handler(&ctx, cmd); - + match handler_opt { None => { trans.queue_for_session(session, @@ -190,6 +193,11 @@ pub async fn handle(session: &ListenerSession, msg: &str, pool: &DBPool) -> DRes pool.bump_session_time(&session).await?; Ok(()) } +#[cfg(test)] +pub async fn handle(_session: &ListenerSession, _msg: &str, _pool: &DBPool) -> DResult<()> { + + unimplemented!(); +} pub fn is_likely_explicit(msg: &str) -> bool { static EXPLICIT_MARKER_WORDS: OnceCell> = @@ -230,3 +238,22 @@ pub async fn search_item_for_user<'l>(ctx: &'l VerbContext<'l>, search: &'l Item item1.clone(), }) } + +#[cfg(test)] mod test { + use crate::db::MockDBTrans; + + #[test] + fn resolve_handler_finds_unregistered() { + use super::*; + + let trans = MockDBTrans::new(); + let sess: ListenerSession = Default::default(); + let mut user_dat: Option = None; + let mut session_dat: Session = Default::default(); + let ctx = VerbContext { session: &sess, + trans: &trans, + session_dat: &mut session_dat, + user_dat: &mut user_dat }; + resolve_handler(&ctx, "less_explicit_mode"); + } +} diff --git a/blastmud_game/src/message_handler/user_commands/movement.rs b/blastmud_game/src/message_handler/user_commands/movement.rs index 14ff377..02d2683 100644 --- a/blastmud_game/src/message_handler/user_commands/movement.rs +++ b/blastmud_game/src/message_handler/user_commands/movement.rs @@ -13,7 +13,6 @@ use crate::{ queue_command }, static_content::room::{self, Direction, ExitType}, - db::DBTrans, models::item::{ Item, SkillType, @@ -26,6 +25,8 @@ use crate::{ combat::handle_resurrect, } }; +use mockall_double::double; +#[double] use crate::db::DBTrans; use std::time; pub async fn announce_move(trans: &DBTrans, character: &Item, leaving: &Item, arriving: &Item) -> DResult<()> { diff --git a/blastmud_game/src/message_handler/user_commands/say.rs b/blastmud_game/src/message_handler/user_commands/say.rs index dfb59ed..55f60e5 100644 --- a/blastmud_game/src/message_handler/user_commands/say.rs +++ b/blastmud_game/src/message_handler/user_commands/say.rs @@ -4,8 +4,9 @@ use super::{VerbContext, UserVerb, UserVerbRef, UResult, UserError, use crate::{ models::item::{Item, ItemFlag}, services::broadcast_to_room, - db::DBTrans }; +use mockall_double::double; +#[double] use crate::db::DBTrans; use async_trait::async_trait; use ansi::{ignore_special_characters, ansi}; diff --git a/blastmud_game/src/regular_tasks.rs b/blastmud_game/src/regular_tasks.rs index a52996c..7d7d9d1 100644 --- a/blastmud_game/src/regular_tasks.rs +++ b/blastmud_game/src/regular_tasks.rs @@ -3,22 +3,25 @@ use async_trait::async_trait; use crate::{ DResult, db, - models::task::{Task, TaskParse, TaskRecurrence}, + models::task::Task, listener::{ListenerMap, ListenerSend}, static_content::npc, services::combat, }; +#[cfg(not(test))] use crate::models::task::{TaskParse, TaskRecurrence}; +use mockall_double::double; +#[double] use crate::db::DBTrans; use blastmud_interfaces::MessageToListener; use log::warn; use once_cell::sync::OnceCell; -use std::ops::AddAssign; +#[cfg(not(test))] use std::ops::AddAssign; use std::collections::BTreeMap; -use chrono::Utc; +#[cfg(not(test))] use chrono::Utc; pub mod queued_command; pub struct TaskRunContext<'l> { - pub trans: &'l db::DBTrans, + pub trans: &'l DBTrans, pub task: &'l mut Task } @@ -112,6 +115,7 @@ fn start_send_queue_task(pool: db::DBPool, listener_map: ListenerMap) { }); } +#[cfg(not(test))] async fn process_tasks_once(pool: db::DBPool) -> DResult<()> { loop { let tx = pool.start_transaction().await?; @@ -198,6 +202,12 @@ async fn process_tasks_once(pool: db::DBPool) -> DResult<()> { Ok(()) } +#[cfg(test)] +async fn process_tasks_once(_pool: db::DBPool) -> DResult<()> { + task_handler_registry(); + unimplemented!(); +} + fn start_task_runner(pool: db::DBPool) { task::spawn(async move { loop { diff --git a/blastmud_game/src/services.rs b/blastmud_game/src/services.rs index 7fdef42..dfdcbd0 100644 --- a/blastmud_game/src/services.rs +++ b/blastmud_game/src/services.rs @@ -1,8 +1,9 @@ use crate::{ - db::DBTrans, DResult, models::item::Item, }; +use mockall_double::double; +#[double] use crate::db::DBTrans; pub mod skills; pub mod combat; diff --git a/blastmud_game/src/services/capacity.rs b/blastmud_game/src/services/capacity.rs index a97bc4c..5bee7d1 100644 --- a/blastmud_game/src/services/capacity.rs +++ b/blastmud_game/src/services/capacity.rs @@ -1,8 +1,9 @@ -use crate::{ - db::DBTrans, - DResult, -}; +use crate::DResult; +use mockall_double::double; +#[double] use crate::db::DBTrans; + +#[derive(Debug, PartialEq)] pub enum CapacityLevel { Unburdened, SlightlyBurdened, @@ -34,3 +35,25 @@ pub async fn check_item_capacity(trans: &DBTrans, } Ok(CapacityLevel::Unburdened) } + +#[cfg(test)] +mod test { + use crate::db::{ + MockDBTrans, + LocationStats + }; + use super::*; + + #[tokio::test] + async fn check_item_capacity_should_say_above_item_limit_if_over() { + let mut mock_db = MockDBTrans::new(); + mock_db.expect_get_location_stats() + .withf(|s| s == "player/foo") + .returning(|_| Ok(LocationStats { + total_count: 49, + total_weight: 100, + })); + assert_eq!(check_item_capacity(&mock_db, "player/foo", 10).await.unwrap(), + CapacityLevel::AboveItemLimit); + } +} diff --git a/blastmud_game/src/services/combat.rs b/blastmud_game/src/services/combat.rs index 71a5d2a..e09f118 100644 --- a/blastmud_game/src/services/combat.rs +++ b/blastmud_game/src/services/combat.rs @@ -15,8 +15,9 @@ use crate::{ message_handler::user_commands::{user_error, UResult}, regular_tasks::{TaskRunContext, TaskHandler}, DResult, - db::DBTrans, }; +use mockall_double::double; +#[double] use crate::db::DBTrans; use async_trait::async_trait; use chrono::Utc; use async_recursion::async_recursion; diff --git a/blastmud_game/src/services/skills.rs b/blastmud_game/src/services/skills.rs index e4fba67..d5ea2ba 100644 --- a/blastmud_game/src/services/skills.rs +++ b/blastmud_game/src/services/skills.rs @@ -3,12 +3,13 @@ use crate::{ item::{Item, SkillType, StatType, BuffImpact}, user::User }, - db::DBTrans, DResult, }; use rand::{self, Rng}; use chrono::Utc; use std::collections::BTreeMap; +use mockall_double::double; +#[double] use crate::db::DBTrans; pub fn calculate_total_stats_skills_for_user(target_item: &mut Item, user: &User) { target_item.total_stats = BTreeMap::new();