diff options
author | Kjetil Orbekk <kj@orbekk.com> | 2023-01-02 11:58:49 -0500 |
---|---|---|
committer | Kjetil Orbekk <kj@orbekk.com> | 2023-01-02 11:58:49 -0500 |
commit | 8696dcc0afdc0896081405df13c887384500961b (patch) | |
tree | eea3f0d0534795611ba4c05dcc943ff9af364465 | |
parent | 2d50ae0b134366b875fa54912422b2484dd70d9b (diff) |
Insert table in db on creation
-rw-r--r-- | server/src/table.rs | 62 | ||||
-rw-r--r-- | server/tests/table_test.rs | 50 |
2 files changed, 96 insertions, 16 deletions
diff --git a/server/src/table.rs b/server/src/table.rs index 7e49c7f..d2eff51 100644 --- a/server/src/table.rs +++ b/server/src/table.rs @@ -9,7 +9,8 @@ use protocol::{ simple_bots::{AlwaysPassBiddingBot, RandomPlayingBot}, }; use rand::random; -use sqlx::PgPool; +use sqlx::{PgPool, Postgres, Transaction}; +use uuid::Uuid; use crate::error::BridgeError; @@ -24,7 +25,9 @@ pub trait Table { self: Box<Self>, card: Card, ) -> Result<Box<dyn Table + Send>, BridgeError>; - async fn new_deal(self: Box<Self>) -> Result<Box<dyn Table + Send>, BridgeError>; + async fn new_deal( + self: Box<Self>, + ) -> Result<Box<dyn Table + Send>, BridgeError>; } pub struct InMemoryTable { @@ -73,7 +76,9 @@ impl Table for InMemoryTable { Ok(Box::new(Self { state: game.into() })) } - async fn new_deal(self: Box<Self>) -> Result<Box<dyn Table + Send>, BridgeError> { + async fn new_deal( + self: Box<Self>, + ) -> Result<Box<dyn Table + Send>, BridgeError> { Ok(Box::new(Self { state: GameState::new(random(), random()).into(), })) @@ -116,11 +121,37 @@ pub struct DbTable { } impl DbTable { - pub fn new(db: PgPool) -> Self { - Self { + pub async fn new(db: PgPool, id: Uuid) -> Result<Self, BridgeError> { + let mut txn = db.begin().await?; + if let Some(_) = + sqlx::query!("select id from bridge_table where id = $1", id) + .fetch_optional(&mut txn) + .await? + { + return Self::restore(db, txn, id).await; + } + + sqlx::query!("insert into bridge_table (id) values($1)", id) + .execute(&mut txn) + .await?; + + txn.commit().await?; + Ok(Self { db, inner: Box::new(InMemoryTable::new()), - } + }) + } + + pub async fn restore( + db: PgPool, + txn: Transaction<'_, Postgres>, + id: Uuid, + ) -> Result<Self, BridgeError> { + txn.rollback().await?; + Ok(Self { + db, + inner: Box::new(InMemoryTable::new()), + }) } } @@ -134,17 +165,28 @@ impl Table for DbTable { self: Box<Self>, bid: Bid, ) -> Result<Box<dyn Table + Send>, BridgeError> { - Ok(Box::new(Self { inner: self.inner.bid(bid).await?, ..*self })) + Ok(Box::new(Self { + inner: self.inner.bid(bid).await?, + ..*self + })) } async fn play( self: Box<Self>, card: Card, ) -> Result<Box<dyn Table + Send>, BridgeError> { - Ok(Box::new(Self { inner: self.inner.play(card).await?, ..*self })) + Ok(Box::new(Self { + inner: self.inner.play(card).await?, + ..*self + })) } - async fn new_deal(self: Box<Self>) -> Result<Box<dyn Table + Send>, BridgeError> { - Ok(Box::new(Self { inner: self.inner.new_deal().await?, ..*self })) + async fn new_deal( + self: Box<Self>, + ) -> Result<Box<dyn Table + Send>, BridgeError> { + Ok(Box::new(Self { + inner: self.inner.new_deal().await?, + ..*self + })) } } diff --git a/server/tests/table_test.rs b/server/tests/table_test.rs index 8b691b9..87547c3 100644 --- a/server/tests/table_test.rs +++ b/server/tests/table_test.rs @@ -1,12 +1,18 @@ use protocol::bridge_engine::TableState; use rand::{thread_rng, Rng}; -use server::table::{Table, InMemoryTable, DbTable}; +use server::{ + error::BridgeError, + table::{DbTable, InMemoryTable, Table}, +}; +use uuid::Uuid; mod common; -async fn table_basic_test(table: Box<dyn Table>) -> Result<(), anyhow::Error> { +async fn table_basic_test( + mut table: Box<dyn Table + Send>, +) -> Result<(), anyhow::Error> { assert!(matches!(table.state(), TableState::Unknown)); - let mut table = table.new_deal().await?; + table = table.new_deal().await?; assert!(matches!(table.state(), TableState::Game(_))); while matches!(table.state(), TableState::Game(_)) { table = server::table::advance_play(table).await?; @@ -18,6 +24,16 @@ async fn table_basic_test(table: Box<dyn Table>) -> Result<(), anyhow::Error> { Ok(()) } +async fn advance_table( + table: Box<dyn Table + Send>, +) -> Result<Box<dyn Table + Send>, BridgeError> { + match table.state() { + TableState::Unknown => panic!("unexpected state"), + TableState::Game(g) => server::table::advance_play(table).await, + TableState::Result(g) => table.new_deal().await, + } +} + #[tokio::test] #[ignore] async fn in_memory_table() -> Result<(), anyhow::Error> { @@ -30,7 +46,27 @@ async fn in_memory_table() -> Result<(), anyhow::Error> { #[ignore] async fn db_table() -> Result<(), anyhow::Error> { let db = common::TestDb::new().await; - table_basic_test(Box::new(DbTable::new(db.db().clone()))).await?; + table_basic_test(Box::new( + DbTable::new(db.db().clone(), Uuid::new_v4()).await?, + )) + .await?; + Ok(()) +} + +// Table testing: +// Creating a table for a specific owner. +// Recreating a table with the same owner should return the same table. + +#[tokio::test] +#[ignore] +async fn db_table_idempotent() -> Result<(), anyhow::Error> { + let db = common::TestDb::new().await; + let uuid = Uuid::new_v4(); + let table1: Box<dyn Table + Send> = + Box::new(DbTable::new(db.db().clone(), uuid).await?); + let table2: Box<dyn Table + Send> = + Box::new(DbTable::new(db.db().clone(), uuid).await?); + assert_eq!(table1.state(), table2.state()); Ok(()) } @@ -38,9 +74,11 @@ async fn db_table() -> Result<(), anyhow::Error> { #[ignore] async fn db_table_persistence() -> Result<(), anyhow::Error> { let db = common::TestDb::new().await; - let mut table = Box::new(DbTable::new(db.db().clone())); + let mut table: Box<dyn Table + Send> = + Box::new(DbTable::new(db.db().clone(), Uuid::new_v4()).await?); + table = table.new_deal().await?; for i in 0..(thread_rng().gen_range(0..200)) { - todo!() + table = advance_table(table).await?; } Ok(()) } |