tests for bidding when auction is expired

This commit is contained in:
Dylan Knutson
2025-09-09 02:45:56 +00:00
parent 0d18016993
commit 7eada9588c
5 changed files with 119 additions and 6 deletions

View File

@@ -6,6 +6,7 @@ use crate::{
App, BotError, BotResult, RootDialogue,
};
use anyhow::{anyhow, Context};
use chrono::Utc;
use itertools::Itertools;
use log::{error, info};
use teloxide::types::*;
@@ -84,9 +85,14 @@ pub async fn handle_awaiting_confirm_bid_amount_callback(
}
};
if listing.base.ends_at < Utc::now() {
return app
.send_message(MessageType::BidInvalidListingExpired { listing, buyer })
.await;
}
let bid = NewBid::new_basic(listing.persisted.id, buyer.persisted.id, bid_amount);
let bid = app.daos.bid.insert_bid(&bid).await?;
dialogue.exit().await.context("failed to exit dialogue")?;
app.send_message(MessageType::BidHasBeenConfirmedForBuyer {
@@ -132,12 +138,22 @@ pub async fn handle_awaiting_confirm_bid_amount_callback(
mod tests {
use super::*;
use crate::{db::DAOs, message_sender::MockMessageSender, test_utils::*};
use dptree::{deps, di::Injectable};
use chrono::{Duration, Utc};
use dptree::{
deps,
di::{DependencyMap, Injectable},
};
use mockall::predicate::function;
use std::str::FromStr;
#[tokio::test]
async fn test_confirm_bid_amount() {
struct Fixtures {
deps: DependencyMap,
seller: PersistedUser,
buyer: PersistedUser,
prev_buyer: PersistedUser,
listing: PersistedListing,
}
async fn set_up_fixtures() -> Fixtures {
let deps = create_deps().await;
let seller = with_test_user(&deps, |seller| {
seller.username = Some("seller".to_string());
@@ -155,6 +171,77 @@ mod tests {
})
.await;
let listing = with_test_listing(&deps, &seller, |_| {}).await;
Fixtures {
deps,
seller,
buyer,
prev_buyer,
listing,
}
}
#[tokio::test]
async fn test_confirm_bid_with_expired_listing() {
let Fixtures {
deps,
buyer,
mut listing,
..
} = set_up_fixtures().await;
// listing has already expired
listing.base.ends_at = Utc::now() - Duration::days(1);
let listing = deps
.get::<DAOs>()
.listing
.update_listing(&listing)
.await
.unwrap();
let cb_query = create_tele_callback_query(
"confirm_bid",
create_tele_user(|user| user.id = buyer.telegram_id.into()),
);
let mut message_sender = MockMessageSender::new();
{
let l = listing.clone();
let b = buyer.clone();
message_sender
.expect_send_message()
.once()
.with(function(move |m| match m {
MessageType::BidInvalidListingExpired { listing, buyer } => {
assert_eq!(listing, &l);
assert_eq!(buyer, &b);
true
}
_ => false,
}))
.returning(|_| Ok(()));
}
let deps = with_message_sender(deps, message_sender).await;
let mut deps = with_dialogue(deps, &buyer).await;
deps.insert_container(deps![
listing,
cb_query,
buyer,
MoneyAmount::from_str("100.00").unwrap()
]);
let ret = handle_awaiting_confirm_bid_amount_callback.inject(&deps)().await;
assert!(ret.is_ok(), "{ret:?}");
}
#[tokio::test]
async fn test_confirm_bid_amount() {
let Fixtures {
deps,
seller,
buyer,
prev_buyer,
listing,
} = set_up_fixtures().await;
deps.get::<DAOs>()
.bid

View File

@@ -103,6 +103,10 @@ impl MessageSender for BotMessageSender {
MessageType::BidHasBeenConfirmedForBuyer { listing, bid } => {
self.send_bid_has_been_confirmed(listing, bid).await?;
}
MessageType::BidInvalidListingExpired { listing, buyer } => {
self.send_bid_invalid_listing_expired(listing, buyer)
.await?;
}
}
Ok(())
}
@@ -165,4 +169,20 @@ impl BotMessageSender {
)
.await
}
async fn send_bid_invalid_listing_expired(
&self,
listing: PersistedListing,
buyer: PersistedUser,
) -> BotResult {
self.with_target(buyer.into())
.send_html_message(
format!(
"Auction <b>{title}</b> has already ended",
title = listing.base.title
),
None,
)
.await
}
}

View File

@@ -262,7 +262,7 @@ pub async fn enter_edit_listing_draft(
async fn save_listing(listing_dao: &ListingDAO, draft: ListingDraft) -> BotResult<String> {
let (listing, success_message) = if let Some(fields) = draft.persisted {
let listing = listing_dao
.update_listing(PersistedListing {
.update_listing(&PersistedListing {
persisted: fields,
base: draft.base,
fields: draft.fields,

View File

@@ -54,7 +54,7 @@ impl ListingDAO {
Ok(FromRow::from_row(&row)?)
}
pub async fn update_listing(&self, listing: PersistedListing) -> Result<PersistedListing> {
pub async fn update_listing(&self, listing: &PersistedListing) -> Result<PersistedListing> {
let now = Utc::now();
let binds = binds_for_listing(&listing).push("updated_at", &now);
@@ -123,6 +123,8 @@ fn binds_for_base(base: &ListingBase) -> BindFields {
.push("title", &base.title)
.push("description", &base.description)
.push("currency_type", &base.currency_type)
.push("starts_at", &base.starts_at)
.push("ends_at", &base.ends_at)
}
fn binds_for_fields(fields: &ListingFields) -> BindFields {

View File

@@ -16,4 +16,8 @@ pub enum MessageType {
listing: PersistedListing,
bid: PersistedBid,
},
BidInvalidListingExpired {
listing: PersistedListing,
buyer: PersistedUser,
},
}