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