use anyhow::Result; use sqlx::{PgPool, query, query_as, query_scalar}; use uuid::Uuid; use crate::votes::model::BestSlugs; pub async fn insert_new_vote(slug: &str, voter_id: &Uuid, db: &PgPool) -> Result<()> { query("insert into votes (slug, voter_id) values ($1, $2)") .bind(slug) .bind(voter_id) .execute(db) .await?; Ok(()) } pub async fn get_vote_count_for_slug(slug: &str, db: &PgPool) -> Result { let count: i64 = query_scalar("select count(*) from votes where slug=$1") .bind(slug) .fetch_one(db) .await?; Ok(count) } pub async fn get_top_n_slugs(n: i64, db: &PgPool) -> Result> { if n > 0 { let top_slugs = query_as::<_, BestSlugs>( r#"select slug, count(*) AS vote_count from votes group by slug order by vote_count desc limit $1 "#, ) .bind(n) .fetch_all(db) .await?; Ok(top_slugs) } else { Ok(vec![]) } } pub async fn delete_vote(slug: &str, voter_id: &Uuid, db: &PgPool) -> Result<()> { query("delete from votes where slug=$1 and voter_id=$2") .bind(slug) .bind(voter_id) .execute(db) .await?; Ok(()) } #[cfg(test)] mod postgres_tests { use sqlx::PgPool; use uuid::Uuid; use crate::{ test_helpers::db::test_pool, votes::repository::{delete_vote, get_top_n_slugs, get_vote_count_for_slug, insert_new_vote}, }; fn test_votes() -> [(&'static str, Uuid); 9] { [ ("blog_post1", Uuid::from_u128(0x1)), ("blog_post1", Uuid::from_u128(0x2)), ("blog_post2", Uuid::from_u128(0x3)), ("blog_post2", Uuid::from_u128(0x4)), ("blog_post3", Uuid::from_u128(0x5)), ("blog_post3", Uuid::from_u128(0x6)), ("blog_post1", Uuid::from_u128(0x7)), ("blog_post1", Uuid::from_u128(0x8)), ("blog_post3", Uuid::from_u128(0x9)), ] } async fn cleanup(db: &PgPool) { for (slug, voter_id) in test_votes() { delete_vote(slug, &voter_id, db).await.unwrap() } } #[tokio::test] #[ignore] pub async fn postgres_tests() { let db = test_pool().await; cleanup(&db).await; let votes = test_votes(); for (slug, voter_id) in votes.iter() { insert_new_vote(slug, voter_id, &db) .await .expect("Insertions to db failed"); } assert_eq!(get_vote_count_for_slug("blog_post1", &db).await.unwrap(), 4); assert_eq!(get_vote_count_for_slug("blog_post2", &db).await.unwrap(), 2); assert_eq!(get_vote_count_for_slug("blog_post3", &db).await.unwrap(), 3); let top_2 = get_top_n_slugs(2, &db).await.unwrap(); assert_eq!(top_2[0].slug, "blog_post1"); assert_eq!(top_2[1].slug, "blog_post3"); delete_vote(votes[4].0, &votes[4].1, &db).await.unwrap(); delete_vote(votes[5].0, &votes[5].1, &db).await.unwrap(); assert_eq!(get_vote_count_for_slug("blog_post1", &db).await.unwrap(), 4); assert_eq!(get_vote_count_for_slug("blog_post2", &db).await.unwrap(), 2); assert_eq!(get_vote_count_for_slug("blog_post3", &db).await.unwrap(), 1); let top_2 = get_top_n_slugs(2, &db).await.unwrap(); assert_eq!(top_2[0].slug, "blog_post1"); assert_eq!(top_2[1].slug, "blog_post2"); cleanup(&db).await; } }