packages/ztd-query-mysqli-adapter/tests/Integration/MysqliPlatformInjectionTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Integration;
6
7use Container\Endpoint;
8use Container\MySqlRelease;
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\MySqlPlatform;
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 MysqliPlatformInjectionTest extends TestCase
30{
31 public function testExplicitMySqlPlatformInjectionWorks(): void
32 {
33 $container = Testcontainers::run(MySqlRelease::fromEnvironment());
34 $endpoint = $container->getData(Endpoint::class);
35 try {
36 $rawMysqli = new mysqli($endpoint->host, $endpoint->username, $endpoint->password, $endpoint->database, $endpoint->port);
37 $rawMysqli->set_charset('utf8mb4');
38 $table = 'prefix_' . bin2hex(random_bytes(8));
39 $rawMysqli->query(sprintf('CREATE TABLE `%s` (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL)', $table));
40 $rawMysqli->query(sprintf("INSERT INTO `%s` (name) VALUES ('Alice'), ('Bob')", $table));
41 $platform = new MySqlPlatform();
42 $ztd = ZtdMysqli::fromMysqli($rawMysqli, null, $platform);
43
44 $result = $ztd->query(sprintf('SELECT * FROM `%s`', $table));
45 self::assertNotFalse($result);
46 self::assertInstanceOf(mysqli_result::class, $result);
47
48 $rows = $result->fetch_all(MYSQLI_ASSOC);
49 self::assertCount(0, $rows);
50 } finally {
51 $container->stop();
52 }
53 }
54
55 public function testInjectedPlatformInsertIsVisibleViaSelect(): void
56 {
57 $container = Testcontainers::run(MySqlRelease::fromEnvironment());
58 $endpoint = $container->getData(Endpoint::class);
59 try {
60 $rawMysqli = new mysqli($endpoint->host, $endpoint->username, $endpoint->password, $endpoint->database, $endpoint->port);
61 $rawMysqli->set_charset('utf8mb4');
62 $table = 'prefix_' . bin2hex(random_bytes(8));
63 $rawMysqli->query(sprintf('CREATE TABLE `%s` (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL)', $table));
64 $rawMysqli->query(sprintf("INSERT INTO `%s` (name) VALUES ('Alice'), ('Bob')", $table));
65 $platform = new MySqlPlatform();
66 $ztd = ZtdMysqli::fromMysqli($rawMysqli, null, $platform);
67
68 $ztd->query(sprintf(
69 "INSERT INTO `%s` (name) VALUES ('Charlie')",
70 $table
71 ));
72
73 $result = $ztd->query(sprintf('SELECT * FROM `%s` ORDER BY name', $table));
74 self::assertNotFalse($result);
75 self::assertInstanceOf(mysqli_result::class, $result);
76
77 $rows = $result->fetch_all(MYSQLI_ASSOC);
78 self::assertCount(1, $rows);
79 self::assertSame('Charlie', $rows[0]['name']);
80 } finally {
81 $container->stop();
82 }
83 }
84
85 public function testInjectedPlatformDoesNotModifyPhysicalDatabase(): void
86 {
87 $container = Testcontainers::run(MySqlRelease::fromEnvironment());
88 $endpoint = $container->getData(Endpoint::class);
89 try {
90 $rawMysqli = new mysqli($endpoint->host, $endpoint->username, $endpoint->password, $endpoint->database, $endpoint->port);
91 $rawMysqli->set_charset('utf8mb4');
92 $table = 'prefix_' . bin2hex(random_bytes(8));
93 $rawMysqli->query(sprintf('CREATE TABLE `%s` (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL)', $table));
94 $rawMysqli->query(sprintf("INSERT INTO `%s` (name) VALUES ('Alice'), ('Bob')", $table));
95 $platform = new MySqlPlatform();
96 $ztd = ZtdMysqli::fromMysqli($rawMysqli, null, $platform);
97
98 $ztd->query(sprintf(
99 "INSERT INTO `%s` (name) VALUES ('Charlie')",
100 $table
101 ));
102
103 $result = $rawMysqli->query(sprintf('SELECT * FROM `%s` ORDER BY name', $table));
104 self::assertNotFalse($result);
105 self::assertInstanceOf(mysqli_result::class, $result);
106
107 $rows = $result->fetch_all(MYSQLI_ASSOC);
108 self::assertCount(2, $rows);
109 self::assertSame('Alice', $rows[0]['name']);
110 self::assertSame('Bob', $rows[1]['name']);
111 } finally {
112 $container->stop();
113 }
114 }
115
116 public function testInjectedPlatformPreparedStatementWorks(): void
117 {
118 $container = Testcontainers::run(MySqlRelease::fromEnvironment());
119 $endpoint = $container->getData(Endpoint::class);
120 try {
121 $rawMysqli = new mysqli($endpoint->host, $endpoint->username, $endpoint->password, $endpoint->database, $endpoint->port);
122 $rawMysqli->set_charset('utf8mb4');
123 $table = 'prefix_' . bin2hex(random_bytes(8));
124 $rawMysqli->query(sprintf('CREATE TABLE `%s` (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL)', $table));
125 $rawMysqli->query(sprintf("INSERT INTO `%s` (name) VALUES ('Alice'), ('Bob')", $table));
126 $platform = new MySqlPlatform();
127 $ztd = ZtdMysqli::fromMysqli($rawMysqli, null, $platform);
128
129 $ztd->query(sprintf(
130 "INSERT INTO `%s` (name) VALUES ('Charlie')",
131 $table
132 ));
133
134 $stmt = $ztd->prepare(sprintf('SELECT * FROM `%s` WHERE name = ?', $table));
135 self::assertNotFalse($stmt);
136
137 $name = 'Charlie';
138 $stmt->bind_param('s', $name);
139 $stmt->execute();
140 $result = $stmt->get_result();
141 self::assertNotFalse($result);
142
143 $rows = $result->fetch_all(MYSQLI_ASSOC);
144 self::assertCount(1, $rows);
145 self::assertSame('Charlie', $rows[0]['name']);
146 } finally {
147 $container->stop();
148 }
149 }
150
151 public function testInjectedPlatformAffectedRowsTracking(): void
152 {
153 $container = Testcontainers::run(MySqlRelease::fromEnvironment());
154 $endpoint = $container->getData(Endpoint::class);
155 try {
156 $rawMysqli = new mysqli($endpoint->host, $endpoint->username, $endpoint->password, $endpoint->database, $endpoint->port);
157 $rawMysqli->set_charset('utf8mb4');
158 $table = 'prefix_' . bin2hex(random_bytes(8));
159 $rawMysqli->query(sprintf('CREATE TABLE `%s` (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL)', $table));
160 $rawMysqli->query(sprintf("INSERT INTO `%s` (name) VALUES ('Alice'), ('Bob')", $table));
161 $platform = new MySqlPlatform();
162 $ztd = ZtdMysqli::fromMysqli($rawMysqli, null, $platform);
163
164 $ztd->query(sprintf(
165 "INSERT INTO `%s` (name) VALUES ('Charlie')",
166 $table
167 ));
168
169 self::assertSame(1, $ztd->lastAffectedRows());
170 } finally {
171 $container->stop();
172 }
173 }
174
175 public function testDefaultPlatformBehaviorMatchesExplicitInjection(): void
176 {
177 $container = Testcontainers::run(MySqlRelease::fromEnvironment());
178 $endpoint = $container->getData(Endpoint::class);
179 try {
180 $rawMysqli = new mysqli($endpoint->host, $endpoint->username, $endpoint->password, $endpoint->database, $endpoint->port);
181 $rawMysqli->set_charset('utf8mb4');
182 $table = 'prefix_' . bin2hex(random_bytes(8));
183 $rawMysqli->query(sprintf('CREATE TABLE `%s` (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL)', $table));
184 $rawMysqli->query(sprintf("INSERT INTO `%s` (name) VALUES ('Alice'), ('Bob')", $table));
185 $ztdDefault = ZtdMysqli::fromMysqli($rawMysqli);
186
187 $resultDefault = $ztdDefault->query(sprintf('SELECT * FROM `%s`', $table));
188 self::assertNotFalse($resultDefault);
189 self::assertInstanceOf(mysqli_result::class, $resultDefault);
190
191 $rowsDefault = $resultDefault->fetch_all(MYSQLI_ASSOC);
192 self::assertCount(0, $rowsDefault);
193 } finally {
194 $container->stop();
195 }
196 }
197}
198