packages/ztd-query-mysqli-adapter/tests/Unit/Session/StatementExecutionTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Session;
6
7use Container\Endpoint;
8use Container\MySqlRelease;
9use mysqli;
10use mysqli_result;
11use mysqli_stmt;
12use PHPUnit\Framework\Attributes\CoversClass;
13use PHPUnit\Framework\Attributes\Large;
14use PHPUnit\Framework\Attributes\UsesClass;
15use PHPUnit\Framework\TestCase;
16use Testcontainers\Testcontainers;
17use ZtdQuery\Adapter\Mysqli\Driver\MysqliConnection;
18use ZtdQuery\Adapter\Mysqli\Driver\MysqliResultColumnExtractor;
19use ZtdQuery\Adapter\Mysqli\Driver\MysqliResultStatement;
20use ZtdQuery\Adapter\Mysqli\Session\ConnectionExecution;
21use ZtdQuery\Adapter\Mysqli\Session\MysqliResultProcessor;
22use ZtdQuery\Adapter\Mysqli\Session\StatementExecution;
23use ZtdQuery\Adapter\Mysqli\ZtdMysqliException;
24
25#[CoversClass(StatementExecution::class)]
26#[Large]
27#[UsesClass(MysqliConnection::class)]
28#[UsesClass(MysqliResultStatement::class)]
29#[UsesClass(MysqliResultColumnExtractor::class)]
30#[UsesClass(MysqliResultProcessor::class)]
31#[UsesClass(ZtdMysqliException::class)]
32#[UsesClass(ConnectionExecution::class)]
33final class StatementExecutionTest extends TestCase
34{
35 public function testNativeReturnsThePreparedStatement(): void
36 {
37 $container = Testcontainers::run(MySqlRelease::fromEnvironment());
38 $endpoint = $container->getData(Endpoint::class);
39 try {
40 $native = new mysqli($endpoint->host, $endpoint->username, $endpoint->password, $endpoint->database, $endpoint->port);
41 $native->set_charset('utf8mb4');
42 $native->query('CREATE TABLE items (id INT PRIMARY KEY)');
43 $executor = (new ConnectionExecution($native))->executor();
44 $plan = $executor->rewrite('INSERT INTO items VALUES (7)');
45 $statement = $native->prepare($plan->sql());
46 self::assertInstanceOf(mysqli_stmt::class, $statement);
47 $execution = new StatementExecution($statement, $executor, $plan);
48
49 self::assertSame($statement, $execution->native());
50 } finally {
51 $container->stop();
52 }
53 }
54
55 public function testResultTracksSimulatedWrites(): void
56 {
57 $container = Testcontainers::run(MySqlRelease::fromEnvironment());
58 $endpoint = $container->getData(Endpoint::class);
59 try {
60 $native = new mysqli($endpoint->host, $endpoint->username, $endpoint->password, $endpoint->database, $endpoint->port);
61 $native->set_charset('utf8mb4');
62 $native->query('CREATE TABLE items (id INT PRIMARY KEY)');
63 $executor = (new ConnectionExecution($native))->executor();
64 $plan = $executor->rewrite('INSERT INTO items VALUES (7)');
65 $statement = $native->prepare($plan->sql());
66 self::assertInstanceOf(mysqli_stmt::class, $statement);
67 $execution = new StatementExecution($statement, $executor, $plan);
68
69 self::assertNull($execution->result());
70 self::assertTrue($execution->execute());
71 self::assertNotNull($execution->result());
72 self::assertSame(1, $execution->result()->rowCount());
73 } finally {
74 $container->stop();
75 }
76 }
77
78 public function testAffectedRowsReturnsTheSimulatedCount(): void
79 {
80 $container = Testcontainers::run(MySqlRelease::fromEnvironment());
81 $endpoint = $container->getData(Endpoint::class);
82 try {
83 $native = new mysqli($endpoint->host, $endpoint->username, $endpoint->password, $endpoint->database, $endpoint->port);
84 $native->set_charset('utf8mb4');
85 $native->query('CREATE TABLE items (id INT PRIMARY KEY)');
86 $executor = (new ConnectionExecution($native))->executor();
87 $plan = $executor->rewrite('INSERT INTO items VALUES (7)');
88 $statement = $native->prepare($plan->sql());
89 self::assertInstanceOf(mysqli_stmt::class, $statement);
90 $execution = new StatementExecution($statement, $executor, $plan);
91
92 self::assertTrue($execution->execute());
93 self::assertSame(1, $execution->affectedRows());
94 } finally {
95 $container->stop();
96 }
97 }
98
99 public function testExecuteDoesNotWriteThePhysicalTable(): void
100 {
101 $container = Testcontainers::run(MySqlRelease::fromEnvironment());
102 $endpoint = $container->getData(Endpoint::class);
103 try {
104 $native = new mysqli($endpoint->host, $endpoint->username, $endpoint->password, $endpoint->database, $endpoint->port);
105 $native->set_charset('utf8mb4');
106 $native->query('CREATE TABLE items (id INT PRIMARY KEY)');
107 $executor = (new ConnectionExecution($native))->executor();
108 $plan = $executor->rewrite('INSERT INTO items VALUES (7)');
109 $statement = $native->prepare($plan->sql());
110 self::assertInstanceOf(mysqli_stmt::class, $statement);
111 $execution = new StatementExecution($statement, $executor, $plan);
112
113 self::assertTrue($execution->execute());
114 $result = $native->query('SELECT COUNT(*) FROM items');
115 self::assertInstanceOf(mysqli_result::class, $result);
116 self::assertSame(['0'], $result->fetch_row());
117 } finally {
118 $container->stop();
119 }
120 }
121
122 public function testGetResultReturnsTheBufferedSelect(): void
123 {
124 $container = Testcontainers::run(MySqlRelease::fromEnvironment());
125 $endpoint = $container->getData(Endpoint::class);
126 try {
127 $native = new mysqli($endpoint->host, $endpoint->username, $endpoint->password, $endpoint->database, $endpoint->port);
128 $native->set_charset('utf8mb4');
129 $native->query('CREATE TABLE items (id INT PRIMARY KEY)');
130 $executor = (new ConnectionExecution($native))->executor();
131 $plan = $executor->rewrite('SELECT 7 AS id');
132 $statement = $native->prepare($plan->sql());
133 self::assertInstanceOf(mysqli_stmt::class, $statement);
134 $execution = new StatementExecution($statement, $executor, $plan);
135
136 self::assertTrue($execution->execute());
137 $result = $execution->getResult();
138 self::assertInstanceOf(mysqli_result::class, $result);
139 self::assertSame([['id' => 7]], $result->fetch_all(MYSQLI_ASSOC));
140 } finally {
141 $container->stop();
142 }
143 }
144
145 public function testFetchReturnsNullForAWritingStatement(): void
146 {
147 $container = Testcontainers::run(MySqlRelease::fromEnvironment());
148 $endpoint = $container->getData(Endpoint::class);
149 try {
150 $native = new mysqli($endpoint->host, $endpoint->username, $endpoint->password, $endpoint->database, $endpoint->port);
151 $native->set_charset('utf8mb4');
152 $native->query('CREATE TABLE items (id INT PRIMARY KEY)');
153 $executor = (new ConnectionExecution($native))->executor();
154 $plan = $executor->rewrite('INSERT INTO items VALUES (7)');
155 $statement = $native->prepare($plan->sql());
156 self::assertInstanceOf(mysqli_stmt::class, $statement);
157 $execution = new StatementExecution($statement, $executor, $plan);
158
159 self::assertTrue($execution->execute());
160 self::assertNull($execution->fetch());
161 } finally {
162 $container->stop();
163 }
164 }
165
166 public function testResetDiscardsTheSimulatedResult(): void
167 {
168 $container = Testcontainers::run(MySqlRelease::fromEnvironment());
169 $endpoint = $container->getData(Endpoint::class);
170 try {
171 $native = new mysqli($endpoint->host, $endpoint->username, $endpoint->password, $endpoint->database, $endpoint->port);
172 $native->set_charset('utf8mb4');
173 $native->query('CREATE TABLE items (id INT PRIMARY KEY)');
174 $executor = (new ConnectionExecution($native))->executor();
175 $plan = $executor->rewrite('INSERT INTO items VALUES (7)');
176 $statement = $native->prepare($plan->sql());
177 self::assertInstanceOf(mysqli_stmt::class, $statement);
178 $execution = new StatementExecution($statement, $executor, $plan);
179
180 self::assertTrue($execution->execute());
181 self::assertNotNull($execution->result());
182 self::assertTrue($execution->reset());
183 self::assertNull($execution->result());
184 } finally {
185 $container->stop();
186 }
187 }
188
189}
190