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