packages/ztd-query-core/tests/Unit/SessionTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use ZtdQuery\Schema\TableDefinition;
11use ZtdQuery\Schema\TableDefinitionRegistry;
12use ZtdQuery\Schema\ViewDefinitionSet;
13use ZtdQuery\Session;
14use ZtdQuery\Shadow\ShadowStore;
15use ZtdQuery\Shadow\ShadowTransactions;
16use ZtdQuery\Sql\TransactionStatement;
17
18#[CoversClass(Session::class)]
19#[UsesClass(\ZtdQuery\Schema\RowSet::class)]
20#[UsesClass(ShadowStore::class)]
21#[UsesClass(ShadowTransactions::class)]
22#[UsesClass(TableDefinitionRegistry::class)]
23#[UsesClass(ViewDefinitionSet::class)]
24#[UsesClass(TableDefinition::class)]
25#[UsesClass(\ZtdQuery\Schema\Key\CandidateKeySet::class)]
26#[UsesClass(\ZtdQuery\Shadow\ShadowSavepoint::class)]
27#[UsesClass(TransactionStatement::class)]
28final class SessionTest extends TestCase
29{
30    public function testStoreRegistryViewsAndTableDefinitionBelongToOneSession(): void
31    {
32        $first = new Session();
33        $second = new Session();
34        $definition = new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], ['id'], []);
35        $first->registry()->register('items', $definition);
36        $first->store()->set('items', [['id' => 1]]);
37        self::assertSame($definition, $first->tableDefinition('items'));
38        self::assertNull($second->tableDefinition('items'));
39        self::assertSame([], $second->store()->get('items'));
40        self::assertNotSame($first->views(), $second->views());
41    }
42
43    public function testEnableDisableAndIsEnabledDoNotDiscardFixtures(): void
44    {
45        $session = new Session();
46        $session->store()->set('items', [['id' => 1]]);
47        $session->disable();
48        self::assertFalse($session->isEnabled());
49        $session->enable();
50        self::assertTrue($session->isEnabled());
51        self::assertSame([['id' => 1]], $session->store()->get('items'));
52    }
53
54    public function testBeginTransactionAndRollBackTransactionRestoreRowsAndSchema(): void
55    {
56        $session = new Session();
57        $definition = new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], ['id'], []);
58        $session->registry()->register('items', $definition);
59        $session->store()->set('items', [['id' => 1]]);
60        $session->beginTransaction();
61        $session->registry()->unregister('items');
62        $session->store()->set('items', [['id' => 2]]);
63        $session->rollBackTransaction();
64        self::assertSame($definition, $session->tableDefinition('items'));
65        self::assertSame([['id' => 1]], $session->store()->get('items'));
66    }
67
68    public function testCommitTransactionKeepsVirtualWrites(): void
69    {
70        $session = new Session();
71        $session->beginTransaction();
72        $session->store()->set('items', [['id' => 1]]);
73        $session->commitTransaction();
74        $session->rollBackTransaction();
75        self::assertSame([['id' => 1]], $session->store()->get('items'));
76    }
77
78    public function testTransactionsAndApplyTransactionStatementShareTheSameSnapshots(): void
79    {
80        $session = new Session();
81        $session->applyTransactionStatement(TransactionStatement::begin());
82        $session->transactions()->savepoint('before_write');
83        $session->store()->set('items', [['id' => 1]]);
84        $session->transactions()->rollBackTo('before_write');
85        self::assertSame([], $session->store()->get('items'));
86    }
87
88    public function testRememberInsertIdAndLastInsertIdRetainTheLastGeneratedIdentity(): void
89    {
90        $session = new Session();
91        self::assertFalse($session->lastInsertId());
92        $session->rememberInsertId('42');
93        $session->rememberInsertId(null);
94        self::assertSame('42', $session->lastInsertId());
95    }
96
97    public function testRegistryReturnsTheSuppliedVirtualCatalog(): void
98    {
99        $registry = new TableDefinitionRegistry();
100        self::assertSame($registry, (new Session(registry: $registry))->registry());
101    }
102
103    public function testViewsReturnsTheSuppliedViewDefinitions(): void
104    {
105        $views = new ViewDefinitionSet();
106        self::assertSame($views, (new Session(views: $views))->views());
107    }
108
109    public function testTableDefinitionSeesCatalogChanges(): void
110    {
111        $session = new Session();
112        $definition = new TableDefinition(['id'], ['id' => 'INTEGER'], ['id'], ['id'], []);
113        $session->registry()->register('items', $definition);
114        self::assertSame($definition, $session->tableDefinition('items'));
115        $session->registry()->unregister('items');
116        self::assertNull($session->tableDefinition('items'));
117    }
118
119    public function testDisableLeavesOtherSessionsEnabled(): void
120    {
121        $first = new Session();
122        $second = new Session();
123        $first->disable();
124        self::assertFalse($first->isEnabled());
125        self::assertTrue($second->isEnabled());
126    }
127
128    public function testIsEnabledStartsTrueForNewSessions(): void
129    {
130        self::assertTrue((new Session())->isEnabled());
131    }
132
133    public function testRollBackTransactionWithoutATransactionKeepsFixtures(): void
134    {
135        $session = new Session();
136        $session->store()->set('items', [['id' => 1]]);
137        $session->rollBackTransaction();
138        self::assertSame([['id' => 1]], $session->store()->get('items'));
139    }
140
141    public function testApplyTransactionStatementRollsBackVirtualWrites(): void
142    {
143        $session = new Session();
144        $session->applyTransactionStatement(TransactionStatement::begin());
145        $session->store()->set('items', [['id' => 1]]);
146        $session->applyTransactionStatement(TransactionStatement::rollback());
147        self::assertSame([], $session->store()->get('items'));
148    }
149
150    public function testLastInsertIdDoesNotLeakBetweenSessions(): void
151    {
152        $first = new Session();
153        $second = new Session();
154        $first->rememberInsertId('7');
155        self::assertFalse($second->lastInsertId());
156    }
157}
158