summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKjetil Orbekk <kj@orbekk.com>2023-01-02 11:58:49 -0500
committerKjetil Orbekk <kj@orbekk.com>2023-01-02 11:58:49 -0500
commit8696dcc0afdc0896081405df13c887384500961b (patch)
treeeea3f0d0534795611ba4c05dcc943ff9af364465
parent2d50ae0b134366b875fa54912422b2484dd70d9b (diff)
Insert table in db on creation
-rw-r--r--server/src/table.rs62
-rw-r--r--server/tests/table_test.rs50
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(())
}