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\MySql80Container;
8use Container\MySql84Container;
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(getenv('MYSQL_VERSION') === '8.4.7' ? MySql84Container::class : MySql80Container::class);
38        try {
39            $native = new mysqli(str_replace('localhost', '127.0.0.1', $container->getHost()), 'root', 'root', 'test', $container->getMappedPort(3306));
40            $native->set_charset('utf8mb4');
41            $native->query('CREATE TABLE items (id INT PRIMARY KEY)');
42            $session = (new ConnectionExecution($native))->session();
43            $plan = $session->rewrite('INSERT INTO items VALUES (7)');
44            $statement = $native->prepare($plan->sql());
45            self::assertInstanceOf(mysqli_stmt::class, $statement);
46            $execution = new StatementExecution($statement, $session, $plan);
47
48            self::assertSame($statement, $execution->native());
49        } finally {
50            $container->stop();
51        }
52    }
53
54    public function testResultTracksSimulatedWrites(): void
55    {
56        $container = Testcontainers::run(getenv('MYSQL_VERSION') === '8.4.7' ? MySql84Container::class : MySql80Container::class);
57        try {
58            $native = new mysqli(str_replace('localhost', '127.0.0.1', $container->getHost()), 'root', 'root', 'test', $container->getMappedPort(3306));
59            $native->set_charset('utf8mb4');
60            $native->query('CREATE TABLE items (id INT PRIMARY KEY)');
61            $session = (new ConnectionExecution($native))->session();
62            $plan = $session->rewrite('INSERT INTO items VALUES (7)');
63            $statement = $native->prepare($plan->sql());
64            self::assertInstanceOf(mysqli_stmt::class, $statement);
65            $execution = new StatementExecution($statement, $session, $plan);
66
67            self::assertNull($execution->result());
68            self::assertTrue($execution->execute());
69            self::assertNotNull($execution->result());
70            self::assertSame(1, $execution->result()->rowCount());
71        } finally {
72            $container->stop();
73        }
74    }
75
76    public function testAffectedRowsReturnsTheSimulatedCount(): void
77    {
78        $container = Testcontainers::run(getenv('MYSQL_VERSION') === '8.4.7' ? MySql84Container::class : MySql80Container::class);
79        try {
80            $native = new mysqli(str_replace('localhost', '127.0.0.1', $container->getHost()), 'root', 'root', 'test', $container->getMappedPort(3306));
81            $native->set_charset('utf8mb4');
82            $native->query('CREATE TABLE items (id INT PRIMARY KEY)');
83            $session = (new ConnectionExecution($native))->session();
84            $plan = $session->rewrite('INSERT INTO items VALUES (7)');
85            $statement = $native->prepare($plan->sql());
86            self::assertInstanceOf(mysqli_stmt::class, $statement);
87            $execution = new StatementExecution($statement, $session, $plan);
88
89            self::assertTrue($execution->execute());
90            self::assertSame(1, $execution->affectedRows());
91        } finally {
92            $container->stop();
93        }
94    }
95
96    public function testExecuteDoesNotWriteThePhysicalTable(): void
97    {
98        $container = Testcontainers::run(getenv('MYSQL_VERSION') === '8.4.7' ? MySql84Container::class : MySql80Container::class);
99        try {
100            $native = new mysqli(str_replace('localhost', '127.0.0.1', $container->getHost()), 'root', 'root', 'test', $container->getMappedPort(3306));
101            $native->set_charset('utf8mb4');
102            $native->query('CREATE TABLE items (id INT PRIMARY KEY)');
103            $session = (new ConnectionExecution($native))->session();
104            $plan = $session->rewrite('INSERT INTO items VALUES (7)');
105            $statement = $native->prepare($plan->sql());
106            self::assertInstanceOf(mysqli_stmt::class, $statement);
107            $execution = new StatementExecution($statement, $session, $plan);
108
109            self::assertTrue($execution->execute());
110            $result = $native->query('SELECT COUNT(*) FROM items');
111            self::assertInstanceOf(mysqli_result::class, $result);
112            self::assertSame(['0'], $result->fetch_row());
113        } finally {
114            $container->stop();
115        }
116    }
117
118    public function testGetResultReturnsTheBufferedSelect(): void
119    {
120        $container = Testcontainers::run(getenv('MYSQL_VERSION') === '8.4.7' ? MySql84Container::class : MySql80Container::class);
121        try {
122            $native = new mysqli(str_replace('localhost', '127.0.0.1', $container->getHost()), 'root', 'root', 'test', $container->getMappedPort(3306));
123            $native->set_charset('utf8mb4');
124            $native->query('CREATE TABLE items (id INT PRIMARY KEY)');
125            $session = (new ConnectionExecution($native))->session();
126            $plan = $session->rewrite('SELECT 7 AS id');
127            $statement = $native->prepare($plan->sql());
128            self::assertInstanceOf(mysqli_stmt::class, $statement);
129            $execution = new StatementExecution($statement, $session, $plan);
130
131            self::assertTrue($execution->execute());
132            $result = $execution->getResult();
133            self::assertInstanceOf(mysqli_result::class, $result);
134            self::assertSame([['id' => 7]], $result->fetch_all(MYSQLI_ASSOC));
135        } finally {
136            $container->stop();
137        }
138    }
139
140    public function testFetchReturnsNullForAWritingStatement(): void
141    {
142        $container = Testcontainers::run(getenv('MYSQL_VERSION') === '8.4.7' ? MySql84Container::class : MySql80Container::class);
143        try {
144            $native = new mysqli(str_replace('localhost', '127.0.0.1', $container->getHost()), 'root', 'root', 'test', $container->getMappedPort(3306));
145            $native->set_charset('utf8mb4');
146            $native->query('CREATE TABLE items (id INT PRIMARY KEY)');
147            $session = (new ConnectionExecution($native))->session();
148            $plan = $session->rewrite('INSERT INTO items VALUES (7)');
149            $statement = $native->prepare($plan->sql());
150            self::assertInstanceOf(mysqli_stmt::class, $statement);
151            $execution = new StatementExecution($statement, $session, $plan);
152
153            self::assertTrue($execution->execute());
154            self::assertNull($execution->fetch());
155        } finally {
156            $container->stop();
157        }
158    }
159
160    public function testResetDiscardsTheSimulatedResult(): void
161    {
162        $container = Testcontainers::run(getenv('MYSQL_VERSION') === '8.4.7' ? MySql84Container::class : MySql80Container::class);
163        try {
164            $native = new mysqli(str_replace('localhost', '127.0.0.1', $container->getHost()), 'root', 'root', 'test', $container->getMappedPort(3306));
165            $native->set_charset('utf8mb4');
166            $native->query('CREATE TABLE items (id INT PRIMARY KEY)');
167            $session = (new ConnectionExecution($native))->session();
168            $plan = $session->rewrite('INSERT INTO items VALUES (7)');
169            $statement = $native->prepare($plan->sql());
170            self::assertInstanceOf(mysqli_stmt::class, $statement);
171            $execution = new StatementExecution($statement, $session, $plan);
172
173            self::assertTrue($execution->execute());
174            self::assertNotNull($execution->result());
175            self::assertTrue($execution->reset());
176            self::assertNull($execution->result());
177        } finally {
178            $container->stop();
179        }
180    }
181
182}
183