Skip to main content

toasty_driver_integration_suite/tests/
tx_lock_mode.rs

1use crate::prelude::*;
2
3use toasty_core::driver::{
4    Operation,
5    operation::{Transaction, TransactionMode},
6};
7
8// `TransactionBuilder::mode(Immediate)` reaches the driver as
9// `Transaction::Start { mode: Immediate, .. }` and the transaction
10// commits normally. Gated on `transaction_lock_mode` so it only runs
11// against drivers that honor the mode (SQLite today).
12#[driver_test(
13    requires(transaction_lock_mode),
14    scenario(crate::scenarios::two_models)
15)]
16pub async fn mode_immediate_reaches_driver_and_commits(t: &mut Test) -> Result<()> {
17    let mut db = setup(t).await;
18
19    t.log().clear();
20
21    let mut tx = db
22        .transaction_builder()
23        .mode(TransactionMode::Immediate)
24        .begin()
25        .await?;
26    User::create().name("Alice").exec(&mut tx).await?;
27    tx.commit().await?;
28
29    assert_struct!(
30        t.log().pop_op(),
31        Operation::Transaction(Transaction::Start {
32            mode: TransactionMode::Immediate,
33            ..
34        })
35    );
36    assert_struct!(t.log().pop_op(), Operation::Insert(_));
37    assert_struct!(
38        t.log().pop_op(),
39        Operation::Transaction(Transaction::Commit)
40    );
41    assert!(t.log().is_empty());
42
43    let users = User::all().exec(&mut db).await?;
44    assert_eq!(users.len(), 1);
45
46    Ok(())
47}
48
49// Same shape for `Exclusive`. Acquires an exclusive lock for the
50// transaction's lifetime — for a single-connection test this is
51// indistinguishable from a successful commit, which is what we assert.
52#[driver_test(
53    requires(transaction_lock_mode),
54    scenario(crate::scenarios::two_models)
55)]
56pub async fn mode_exclusive_reaches_driver_and_commits(t: &mut Test) -> Result<()> {
57    let mut db = setup(t).await;
58
59    t.log().clear();
60
61    let mut tx = db
62        .transaction_builder()
63        .mode(TransactionMode::Exclusive)
64        .begin()
65        .await?;
66    User::create().name("Bob").exec(&mut tx).await?;
67    tx.commit().await?;
68
69    assert_struct!(
70        t.log().pop_op(),
71        Operation::Transaction(Transaction::Start {
72            mode: TransactionMode::Exclusive,
73            ..
74        })
75    );
76    assert_struct!(t.log().pop_op(), Operation::Insert(_));
77    assert_struct!(
78        t.log().pop_op(),
79        Operation::Transaction(Transaction::Commit)
80    );
81    assert!(t.log().is_empty());
82
83    Ok(())
84}
85
86// Drivers without `transaction_lock_mode` must reject non-`Default`
87// modes with `unsupported_feature` rather than silently degrading.
88// Runs on every SQL driver; SQLite is excluded because it supports the
89// mode.
90#[driver_test(requires(sql), scenario(crate::scenarios::two_models))]
91pub async fn mode_immediate_rejected_when_unsupported(t: &mut Test) -> Result<()> {
92    if t.capability().transaction_lock_mode {
93        return Ok(());
94    }
95
96    let mut db = setup(t).await;
97
98    let err = match db
99        .transaction_builder()
100        .mode(TransactionMode::Immediate)
101        .begin()
102        .await
103    {
104        Err(e) => e,
105        Ok(_) => panic!("driver must reject Immediate when transaction_lock_mode is false"),
106    };
107    assert!(
108        err.is_unsupported_feature(),
109        "expected is_unsupported_feature(), got: {err}"
110    );
111
112    Ok(())
113}