use std::error::Error;
use std::thread;
use std::time::Duration;
use rstest::rstest;
use sqlx::{Pool, Postgres, query, Row};
use sqlx_cache::cache_manager::CacheManager;
use sqlx_cache::cache_manager_config::CacheManagerConfig;
use sqlx_cache::db_cache::DbCache;
use sqlx_cache::db_cache_config::DbCacheConfig;
use sqlx_cache::db_commands::DbCommands;
use sqlx_cache::utils::GenericError;
use crate::containers;
#[derive(Default)]
pub struct GameDbCommands;
impl DbCommands for GameDbCommands {
type Key = i64;
type Value = Game;
type Db = Postgres;
async fn get(db_pool: &Pool<Self::Db>, key: &Self::Key) -> Option<Self::Value> {
query("SELECT game_id, name FROM game.game where game_id = $1")
.bind(key)
.fetch_one(&*db_pool)
.await.ok().map(|val| {
Game::new(val.try_get::<i64, &str>("game_id").unwrap() as u64
, val.try_get("name").unwrap())
})
}
async fn put(db_pool: &Pool<Self::Db>, key: Self::Key, value: Self::Value) -> Result<(), GenericError> {
query("INSERT INTO game.game (game_id, name) VALUES ($1, $2)")
.bind(key)
.bind(&value.title)
.execute(db_pool)
.await?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Game {
id: u64,
title: String,
}
impl Game {
pub fn new(id: u64, title: String) -> Self {
Self { id, title }
}
}
const MAX_PENDING_MS_AWAIT: u64 = 2000;
#[tokio::test]
#[rstest]
#[case(47530, "The Legend of Zelda: Ocarina of Time 3D", 1000)]
#[case(47557, "The Last of Us", 2500)]
#[case(47573, "American Truck Simulator", 5000)]
#[case(47577, "Euro Truck Simulator 2", 10000)]
async fn should_get_successfully(#[case] game_id: i64, #[case] title: &str, #[case] exp_time: u64) {
let db_pool = containers::get_db_pool().await;
let mut cache_manager = CacheManager::new(CacheManagerConfig::new(MAX_PENDING_MS_AWAIT, 20, 2048));
let game_repo = DbCache::<GameDbCommands>::build(&mut cache_manager, DbCacheConfig::new("game_repo", exp_time), db_pool);
cache_manager.start();
assert_eq!(true, game_repo.cache().get(&game_id).is_none());
let result = game_repo.get(&game_id).await.unwrap();
assert_eq!(game_id as u64, result.id);
assert_eq!(title, result.title);
assert_eq!(true, game_repo.cache().get(&game_id).is_some());
let result = game_repo.get(&game_id).await.unwrap();
assert_eq!(game_id as u64, result.id);
assert_eq!(title, result.title);
thread::sleep(Duration::from_millis(exp_time as u64 + MAX_PENDING_MS_AWAIT));
assert_eq!(true, game_repo.cache().get(&game_id).is_none());
let result = game_repo.get(&game_id).await.unwrap();
assert_eq!(game_id as u64, result.id);
assert_eq!(title, result.title);
}
#[tokio::test]
#[rstest]
#[case(47523423, "Dragon Ball Sparking Zero", 1000)]
#[case(42342347, "Naruto Shippuden", 2500)]
#[case(46556473, "Amazing Spiderman 2", 5000)]
async fn should_put_successfully(#[case] game_id: i64, #[case] title: &str, #[case] exp_time: u64) {
let db_pool = containers::get_db_pool().await;
let mut cache_manager = CacheManager::new(CacheManagerConfig::new(MAX_PENDING_MS_AWAIT, 20, 2048));
let game_repo = DbCache::<GameDbCommands>::build(&mut cache_manager, DbCacheConfig::new("game_repo", exp_time), db_pool);
cache_manager.start();
assert_eq!(None, game_repo.get(&game_id).await);
let game = Game::new(game_id as u64, title.to_string());
assert_eq!(true, game_repo.put(game_id, game).await.is_ok());
assert_eq!(true, game_repo.cache().get(&game_id).is_some());
thread::sleep(Duration::from_millis(exp_time as u64 + MAX_PENDING_MS_AWAIT));
assert_eq!(true, game_repo.cache().get(&game_id).is_none());
let result = game_repo.get(&game_id).await.unwrap();
assert_eq!(game_id as u64, result.id);
assert_eq!(title, result.title);
}
const LOWER_EXP_TIME: u64 = 100;
#[tokio::test]
#[rstest]
#[case(47557, "The Last of Us", 5000)]
#[case(47573, "American Truck Simulator", 7500)]
#[case(47577, "Euro Truck Simulator 2", 10000)]
async fn should_process_events_in_priority_order(#[case] game_id: i64, #[case] title: &str, #[case] exp_time: u64) {
let db_pool = containers::get_db_pool().await;
let mut cache_manager = CacheManager::new(CacheManagerConfig::new(MAX_PENDING_MS_AWAIT, 20, 2048));
let game_repo = DbCache::<GameDbCommands>::build(
&mut cache_manager,
DbCacheConfig::new("game_repo", exp_time),
db_pool.clone(),
);
let game_repo_2 = DbCache::<GameDbCommands>::build(
&mut cache_manager,
DbCacheConfig::new("game_repo_2", LOWER_EXP_TIME),
db_pool,
);
cache_manager.start();
let _ = game_repo.get(&game_id).await.unwrap();
let _ = game_repo_2.get(&game_id).await.unwrap();
assert!(game_repo.cache().get(&game_id).is_some());
assert!(game_repo_2.cache().get(&game_id).is_some());
thread::sleep(Duration::from_millis(LOWER_EXP_TIME + MAX_PENDING_MS_AWAIT));
assert!(game_repo.cache().get(&game_id).is_some());
assert!(game_repo_2.cache().get(&game_id).is_none());
}
#[tokio::test]
#[should_panic]
async fn should_panic_when_duplicated_cache_ids() {
let db_pool = containers::get_db_pool().await;
let mut cache_manager = CacheManager::new(CacheManagerConfig::new(MAX_PENDING_MS_AWAIT, 20, 2048));
DbCache::<GameDbCommands>::build(
&mut cache_manager,
DbCacheConfig::new("game_repo", LOWER_EXP_TIME),
db_pool.clone(),
);
DbCache::<GameDbCommands>::build(
&mut cache_manager,
DbCacheConfig::new("game_repo", LOWER_EXP_TIME),
db_pool,
);
}