packages/ztd-query-core/tests/Unit/Sql/TransactionStatementTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Sql;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use ZtdQuery\Shadow\ShadowStore;
11use ZtdQuery\Shadow\ShadowTransactions;
12use ZtdQuery\Sql\TransactionStatement;
13
14#[CoversClass(TransactionStatement::class)]
15#[UsesClass(ShadowStore::class)]
16#[UsesClass(ShadowTransactions::class)]
17#[UsesClass(\ZtdQuery\Shadow\ShadowSavepoint::class)]
18#[UsesClass(\ZtdQuery\Schema\RowSet::class)]
19final class TransactionStatementTest extends TestCase
20{
21    public function testReleaseCommitAppliesNamedTransactionLifecycleWithoutSql(): void
22    {
23        $store = new ShadowStore();
24        $store->set('items', [['id' => 1]]);
25        $transactions = new ShadowTransactions($store);
26
27        TransactionStatement::begin()->apply($transactions);
28        $store->insert('items', [['id' => 2]]);
29        TransactionStatement::savepoint('order item')->apply($transactions);
30        $store->insert('items', [['id' => 3]]);
31        TransactionStatement::rollbackTo('order item')->apply($transactions);
32        TransactionStatement::release('order item')->apply($transactions);
33        TransactionStatement::commit()->apply($transactions);
34
35        self::assertSame([['id' => 1], ['id' => 2]], $store->get('items'));
36    }
37
38    public function testRollbackAppliesRollback(): void
39    {
40        $store = new ShadowStore();
41        $store->set('items', [['id' => 1]]);
42        $transactions = new ShadowTransactions($store);
43
44        TransactionStatement::begin()->apply($transactions);
45        $store->insert('items', [['id' => 2]]);
46        TransactionStatement::rollback()->apply($transactions);
47
48        self::assertSame([['id' => 1]], $store->get('items'));
49    }
50
51    public function testBeginStartsATransactionARollbackCanGoBackTo(): void
52    {
53        $store = new ShadowStore();
54        $store->set('order', [['id' => 1]]);
55        $transactions = new ShadowTransactions($store);
56
57        TransactionStatement::begin()->apply($transactions);
58        $store->set('order', []);
59        TransactionStatement::rollback()->apply($transactions);
60
61        self::assertSame([['id' => 1]], $store->get('order'));
62    }
63
64    public function testCommitKeepsEverythingTheTransactionDid(): void
65    {
66        $store = new ShadowStore();
67        $store->set('order', [['id' => 1]]);
68        $transactions = new ShadowTransactions($store);
69
70        TransactionStatement::begin()->apply($transactions);
71        $store->set('order', []);
72        TransactionStatement::commit()->apply($transactions);
73        TransactionStatement::rollback()->apply($transactions);
74
75        self::assertSame([], $store->get('order'));
76    }
77
78    public function testRollbackToGoesBackToTheSavepointAndLeavesItDeclared(): void
79    {
80        $store = new ShadowStore();
81        $transactions = new ShadowTransactions($store);
82        TransactionStatement::begin()->apply($transactions);
83        $store->set('order', [['id' => 1]]);
84        TransactionStatement::savepoint('sp1')->apply($transactions);
85        $store->set('order', []);
86
87        TransactionStatement::rollbackTo('sp1')->apply($transactions);
88
89        self::assertSame([['id' => 1]], $store->get('order'));
90        self::assertNotNull($transactions->positionOf('sp1'));
91    }
92
93    public function testApplyDoesWhatEachOperationSays(): void
94    {
95        $store = new ShadowStore();
96        $transactions = new ShadowTransactions($store);
97        TransactionStatement::begin()->apply($transactions);
98        TransactionStatement::savepoint('sp1')->apply($transactions);
99
100        TransactionStatement::release('sp1')->apply($transactions);
101
102        self::assertNull($transactions->positionOf('sp1'));
103    }
104    public function testRequiredNameAnswersTheNameTheStatementCarried(): void
105    {
106        self::assertSame('sp1', TransactionStatement::requiredName('sp1'));
107    }
108
109    public function testSavepointPreservesTheNamedTransactionBoundary(): void
110    {
111        $statement = TransactionStatement::savepoint('before_update');
112
113        $transactions = new ShadowTransactions(new ShadowStore());
114        $transactions->begin();
115        $statement->apply($transactions);
116
117        self::assertSame(1, $transactions->positionOf('before_update'));
118    }
119
120}
121