packages/ztd-query-mysqli-adapter/tests/Integration/MysqliSessionFactoryInjectionTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Integration;
6
7use Container\MySql80Container;
8use Container\MySql84Container;
9use mysqli;
10use mysqli_result;
11use PHPUnit\Framework\Attributes\Large;
12use PHPUnit\Framework\TestCase;
13use Testcontainers\Testcontainers;
14use ZtdQuery\Adapter\Mysqli\ZtdMysqli;
15use ZtdQuery\Platform\MySql\MySqlSessionFactory;
16
17#[\PHPUnit\Framework\Attributes\CoversClass(ZtdMysqli::class)]
18#[\PHPUnit\Framework\Attributes\CoversClass(\ZtdQuery\Adapter\Mysqli\Session\ConnectionExecution::class)]
19#[\PHPUnit\Framework\Attributes\CoversClass(\ZtdQuery\Adapter\Mysqli\ZtdMysqliStatement::class)]
20#[\PHPUnit\Framework\Attributes\CoversClass(\ZtdQuery\Adapter\Mysqli\Session\StatementExecution::class)]
21#[\PHPUnit\Framework\Attributes\CoversClass(\ZtdQuery\Adapter\Mysqli\Driver\MysqliConnection::class)]
22#[\PHPUnit\Framework\Attributes\CoversClass(\ZtdQuery\Adapter\Mysqli\Driver\MysqliResultStatement::class)]
23#[\PHPUnit\Framework\Attributes\CoversClass(\ZtdQuery\Adapter\Mysqli\Driver\MysqliResultColumnExtractor::class)]
24#[\PHPUnit\Framework\Attributes\CoversClass(\ZtdQuery\Adapter\Mysqli\Native\MysqliStatementBindingBridge::class)]
25#[\PHPUnit\Framework\Attributes\CoversClass(\ZtdQuery\Adapter\Mysqli\ZtdMysqliException::class)]
26#[\PHPUnit\Framework\Attributes\CoversClass(\ZtdQuery\Adapter\Mysqli\Native\MysqliPropertyReader::class)]
27#[\PHPUnit\Framework\Attributes\CoversClass(\ZtdQuery\Adapter\Mysqli\Session\MysqliResultProcessor::class)]
28#[Large]
29final class MysqliSessionFactoryInjectionTest extends TestCase
30{
31    public function testExplicitMySqlSessionFactoryInjectionWorks(): void
32    {
33        $container = Testcontainers::run(getenv('MYSQL_VERSION') === '8.4.7' ? MySql84Container::class : MySql80Container::class);
34        try {
35            $rawMysqli = new mysqli(str_replace('localhost', '127.0.0.1', $container->getHost()), 'root', 'root', 'test', $container->getMappedPort(3306));
36            $rawMysqli->set_charset('utf8mb4');
37            $table = 'prefix_' . bin2hex(random_bytes(8));
38            $rawMysqli->query(sprintf('CREATE TABLE `%s` (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL)', $table));
39            $rawMysqli->query(sprintf("INSERT INTO `%s` (name) VALUES ('Alice'), ('Bob')", $table));
40            $factory = new MySqlSessionFactory();
41            $ztd = ZtdMysqli::fromMysqli($rawMysqli, null, $factory);
42
43            $result = $ztd->query(sprintf('SELECT * FROM `%s`', $table));
44            self::assertNotFalse($result);
45            self::assertInstanceOf(mysqli_result::class, $result);
46
47            $rows = $result->fetch_all(MYSQLI_ASSOC);
48            self::assertCount(0, $rows);
49        } finally {
50            $container->stop();
51        }
52    }
53
54    public function testInjectedFactoryInsertIsVisibleViaSelect(): void
55    {
56        $container = Testcontainers::run(getenv('MYSQL_VERSION') === '8.4.7' ? MySql84Container::class : MySql80Container::class);
57        try {
58            $rawMysqli = new mysqli(str_replace('localhost', '127.0.0.1', $container->getHost()), 'root', 'root', 'test', $container->getMappedPort(3306));
59            $rawMysqli->set_charset('utf8mb4');
60            $table = 'prefix_' . bin2hex(random_bytes(8));
61            $rawMysqli->query(sprintf('CREATE TABLE `%s` (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL)', $table));
62            $rawMysqli->query(sprintf("INSERT INTO `%s` (name) VALUES ('Alice'), ('Bob')", $table));
63            $factory = new MySqlSessionFactory();
64            $ztd = ZtdMysqli::fromMysqli($rawMysqli, null, $factory);
65
66            $ztd->query(sprintf(
67                "INSERT INTO `%s` (name) VALUES ('Charlie')",
68                $table
69            ));
70
71            $result = $ztd->query(sprintf('SELECT * FROM `%s` ORDER BY name', $table));
72            self::assertNotFalse($result);
73            self::assertInstanceOf(mysqli_result::class, $result);
74
75            $rows = $result->fetch_all(MYSQLI_ASSOC);
76            self::assertCount(1, $rows);
77            self::assertSame('Charlie', $rows[0]['name']);
78        } finally {
79            $container->stop();
80        }
81    }
82
83    public function testInjectedFactoryDoesNotModifyPhysicalDatabase(): void
84    {
85        $container = Testcontainers::run(getenv('MYSQL_VERSION') === '8.4.7' ? MySql84Container::class : MySql80Container::class);
86        try {
87            $rawMysqli = new mysqli(str_replace('localhost', '127.0.0.1', $container->getHost()), 'root', 'root', 'test', $container->getMappedPort(3306));
88            $rawMysqli->set_charset('utf8mb4');
89            $table = 'prefix_' . bin2hex(random_bytes(8));
90            $rawMysqli->query(sprintf('CREATE TABLE `%s` (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL)', $table));
91            $rawMysqli->query(sprintf("INSERT INTO `%s` (name) VALUES ('Alice'), ('Bob')", $table));
92            $factory = new MySqlSessionFactory();
93            $ztd = ZtdMysqli::fromMysqli($rawMysqli, null, $factory);
94
95            $ztd->query(sprintf(
96                "INSERT INTO `%s` (name) VALUES ('Charlie')",
97                $table
98            ));
99
100            $result = $rawMysqli->query(sprintf('SELECT * FROM `%s` ORDER BY name', $table));
101            self::assertNotFalse($result);
102            self::assertInstanceOf(mysqli_result::class, $result);
103
104            $rows = $result->fetch_all(MYSQLI_ASSOC);
105            self::assertCount(2, $rows);
106            self::assertSame('Alice', $rows[0]['name']);
107            self::assertSame('Bob', $rows[1]['name']);
108        } finally {
109            $container->stop();
110        }
111    }
112
113    public function testInjectedFactoryPreparedStatementWorks(): void
114    {
115        $container = Testcontainers::run(getenv('MYSQL_VERSION') === '8.4.7' ? MySql84Container::class : MySql80Container::class);
116        try {
117            $rawMysqli = new mysqli(str_replace('localhost', '127.0.0.1', $container->getHost()), 'root', 'root', 'test', $container->getMappedPort(3306));
118            $rawMysqli->set_charset('utf8mb4');
119            $table = 'prefix_' . bin2hex(random_bytes(8));
120            $rawMysqli->query(sprintf('CREATE TABLE `%s` (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL)', $table));
121            $rawMysqli->query(sprintf("INSERT INTO `%s` (name) VALUES ('Alice'), ('Bob')", $table));
122            $factory = new MySqlSessionFactory();
123            $ztd = ZtdMysqli::fromMysqli($rawMysqli, null, $factory);
124
125            $ztd->query(sprintf(
126                "INSERT INTO `%s` (name) VALUES ('Charlie')",
127                $table
128            ));
129
130            $stmt = $ztd->prepare(sprintf('SELECT * FROM `%s` WHERE name = ?', $table));
131            self::assertNotFalse($stmt);
132
133            $name = 'Charlie';
134            $stmt->bind_param('s', $name);
135            $stmt->execute();
136            $result = $stmt->get_result();
137            self::assertNotFalse($result);
138
139            $rows = $result->fetch_all(MYSQLI_ASSOC);
140            self::assertCount(1, $rows);
141            self::assertSame('Charlie', $rows[0]['name']);
142        } finally {
143            $container->stop();
144        }
145    }
146
147    public function testInjectedFactoryAffectedRowsTracking(): void
148    {
149        $container = Testcontainers::run(getenv('MYSQL_VERSION') === '8.4.7' ? MySql84Container::class : MySql80Container::class);
150        try {
151            $rawMysqli = new mysqli(str_replace('localhost', '127.0.0.1', $container->getHost()), 'root', 'root', 'test', $container->getMappedPort(3306));
152            $rawMysqli->set_charset('utf8mb4');
153            $table = 'prefix_' . bin2hex(random_bytes(8));
154            $rawMysqli->query(sprintf('CREATE TABLE `%s` (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL)', $table));
155            $rawMysqli->query(sprintf("INSERT INTO `%s` (name) VALUES ('Alice'), ('Bob')", $table));
156            $factory = new MySqlSessionFactory();
157            $ztd = ZtdMysqli::fromMysqli($rawMysqli, null, $factory);
158
159            $ztd->query(sprintf(
160                "INSERT INTO `%s` (name) VALUES ('Charlie')",
161                $table
162            ));
163
164            self::assertSame(1, $ztd->lastAffectedRows());
165        } finally {
166            $container->stop();
167        }
168    }
169
170    public function testDefaultFactoryBehaviorMatchesExplicitInjection(): void
171    {
172        $container = Testcontainers::run(getenv('MYSQL_VERSION') === '8.4.7' ? MySql84Container::class : MySql80Container::class);
173        try {
174            $rawMysqli = new mysqli(str_replace('localhost', '127.0.0.1', $container->getHost()), 'root', 'root', 'test', $container->getMappedPort(3306));
175            $rawMysqli->set_charset('utf8mb4');
176            $table = 'prefix_' . bin2hex(random_bytes(8));
177            $rawMysqli->query(sprintf('CREATE TABLE `%s` (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL)', $table));
178            $rawMysqli->query(sprintf("INSERT INTO `%s` (name) VALUES ('Alice'), ('Bob')", $table));
179            $ztdDefault = ZtdMysqli::fromMysqli($rawMysqli);
180
181            $resultDefault = $ztdDefault->query(sprintf('SELECT * FROM `%s`', $table));
182            self::assertNotFalse($resultDefault);
183            self::assertInstanceOf(mysqli_result::class, $resultDefault);
184
185            $rowsDefault = $resultDefault->fetch_all(MYSQLI_ASSOC);
186            self::assertCount(0, $rowsDefault);
187        } finally {
188            $container->stop();
189        }
190    }
191}
192