Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Failed Conditions
Push — live ( 05ca5f...631a24 )
by Dan
05:49
created

test_reconnect_when_connection_not_closed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 9
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
A DatabaseIntegrationTest::test_escapeString_null_throws() 0 4 1
1
<?php declare(strict_types=1);
2
3
namespace SmrTest\lib\DefaultGame;
4
5
use Error;
6
use mysqli;
7
use PHPUnit\Framework\TestCase;
8
use RuntimeException;
9
use Smr\Container\DiContainer;
10
use Smr\Database;
11
use Smr\DatabaseProperties;
12
use TypeError;
13
14
/**
15
 * This is an integration test, but does not need to extend BaseIntegrationTest since we are not writing any data.
16
 *
17
 * @covers \Smr\Database
18
 */
19
class DatabaseIntegrationTest extends TestCase {
20
21
	protected function setUp(): void {
22
		// Start each test with a fresh container (and mysqli connection).
23
		// This ensures the independence of each test.
24
		DiContainer::initialize(false);
25
	}
26
27
	public function test_mysqli_factory(): void {
28
		// Given database properties are retrieved from the container
29
		$dbProperties = DiContainer::get(DatabaseProperties::class);
30
		// When using the factory to retrieve a mysqli instance
31
		$mysql = Database::mysqliFactory($dbProperties);
32
		// Then the connection is successful
33
		self::assertNotNull($mysql->server_info);
34
	}
35
36
	public function test__construct_happy_path(): void {
37
		$db = DiContainer::get(Database::class);
38
		$this->assertNotNull($db);
39
	}
40
41
	public function test_getInstance_always_returns_same_instance(): void {
42
		// Given a Database object
43
		$original = Database::getInstance();
44
		// When calling getInstance again
45
		$second = Database::getInstance();
46
		self::assertSame($original, $second);
47
	}
48
49
	public function test_resetInstance_returns_new_instance(): void {
50
		// Given an original mysql instance
51
		$originalMysql = DiContainer::get(mysqli::class);
52
		// And resetInstance is called
53
		Database::resetInstance();
54
		// And Database is usable again after reconnecting
55
		Database::getInstance()->read('SELECT 1');
56
		// Then new mysqli instance is not the same as the original instance
57
		self::assertNotSame($originalMysql, DiContainer::get(mysqli::class));
58
	}
59
60
	public function test_resetInstance_closes_connection(): void {
61
		$db = Database::getInstance();
62
		Database::resetInstance();
63
		$this->expectException(Error::class);
64
		$this->expectExceptionMessage('mysqli object is already closed');
65
		$db->read('SELECT 1');
66
	}
67
68
	public function test_getDbBytes(): void {
69
		$db = Database::getInstance();
70
		$bytes = $db->getDbBytes();
71
		// This value will need to change whenever database migrations are
72
		// added that modify the base table size. If that becomes too onerous,
73
		// we can do a fuzzier comparison. Until then, this is a useful check
74
		// that the test database is properly reset between invocations.
75
		self::assertSame($bytes, 730800);
76
	}
77
78
	public function test_escapeBoolean(): void {
79
		$db = Database::getInstance();
80
		// Test both boolean values
81
		self::assertSame("'TRUE'", $db->escapeBoolean(true));
82
		self::assertSame("'FALSE'", $db->escapeBoolean(false));
83
	}
84
85
	public function test_escapeString(): void {
86
		$db = Database::getInstance();
87
		// Test the empty string
88
		self::assertSame("''", $db->escapeString(''));
89
		self::assertSame('NULL', $db->escapeString('', true)); // nullable
90
		// Test null
91
		self::assertSame('NULL', $db->escapeString(null, true)); // nullable
92
		// Test a normal string
93
		self::assertSame("'bla'", $db->escapeString('bla'));
94
		self::assertSame("'bla'", $db->escapeString('bla', true)); // nullable
95
	}
96
97
	public function test_escapeString_null_throws(): void {
98
		$db = Database::getInstance();
99
		$this->expectException(TypeError::class);
100
		$db->escapeString(null);
101
	}
102
103
	public function test_escapeArray(): void {
104
		$db = Database::getInstance();
105
		// Test a mixed array
106
		self::assertSame("'a',2,'c'", $db->escapeArray(['a', 2, 'c']));
107
		// Test nested arrays
108
		// Warning: The array is flattened, which may be unexpected!
109
		self::assertSame("'a','x',9,2", $db->escapeArray(['a', ['x', 9], 2]));
110
	}
111
112
	public function test_escapeNumber(): void {
113
		// No escaping is done of numeric types
114
		$db = Database::getInstance();
115
		// Test int
116
		self::assertSame(42, $db->escapeNumber(42));
117
		// Test float
118
		self::assertSame(0.21, $db->escapeNumber(0.21));
119
		// Test numeric string
120
		self::assertSame('42', $db->escapeNumber('42'));
121
	}
122
123
	public function test_escapeNumber_nonnumeric_throws(): void {
124
		$db = Database::getInstance();
125
		$this->expectException(RuntimeException::class);
126
		$this->expectExceptionMessage('Not a number');
127
		$db->escapeNumber('bla');
128
	}
129
130
	public function test_escapeObject(): void {
131
		$db = Database::getInstance();
132
		// Test null
133
		self::assertSame('NULL', $db->escapeObject(null, false, true));
134
		// Test empty array
135
		self::assertSame("'a:0:{}'", $db->escapeObject([]));
136
		// Test empty string
137
		self::assertSame('\'s:0:\"\";\'', $db->escapeObject(''));
138
		// Test zero
139
		self::assertSame("'i:0;'", $db->escapeObject(0));
140
	}
141
142
	public function test_write_throws_on_wrong_query_type(): void {
143
		// Queries that return a result should not use the 'write' method
144
		$db = Database::getInstance();
145
		$this->expectException(RuntimeException::class);
146
		$this->expectExceptionMessage('Wrong query type');
147
		$db->write('SELECT 1');
148
	}
149
150
	public function test_lockTable_throws_if_read_other_table(): void {
151
		$db = Database::getInstance();
152
		$db->lockTable('player');
153
		$this->expectException(RuntimeException::class);
154
		$this->expectExceptionMessage("Table 'account' was not locked with LOCK TABLES");
155
		try {
156
			$db->read('SELECT 1 FROM account LIMIT 1');
157
		} catch (\RuntimeException $err) {
158
			// Avoid leaving database in a locked state
159
			$db->unlock();
160
			throw $err;
161
		}
162
	}
163
164
	public function test_lockTable_allows_read(): void {
165
		$db = Database::getInstance();
166
		$db->lockTable('good');
167
168
		// Perform a query on the locked table
169
		$result = $db->read('SELECT good_name FROM good WHERE good_id = 1');
170
		self::assertSame(['good_name' => 'Wood'], $result->record()->getRow());
171
172
		// After unlock we can access other tables again
173
		$db->unlock();
174
		$db->read('SELECT 1 FROM account LIMIT 1');
175
	}
176
177
	public function test_inversion_of_escape_and_get(): void {
178
		$db = Database::getInstance();
179
		// [value, escape function, getter, comparator, extra args]
180
		$params = [
181
			[true, 'escapeBoolean', 'getBoolean', 'assertSame', []],
182
			[false, 'escapeBoolean', 'getBoolean', 'assertSame', []],
183
			[3, 'escapeNumber', 'getInt', 'assertSame', []],
184
			[3.14, 'escapeNumber', 'getFloat', 'assertSame', []],
185
			['hello', 'escapeString', 'getString', 'assertSame', []],
186
			['hello', 'escapeString', 'getNullableString', 'assertSame', []],
187
			// Test nullable objects
188
			[null, 'escapeString', 'getNullableString', 'assertSame', [true]],
189
			[null, 'escapeObject', 'getObject', 'assertSame', [false, true]],
190
			// Test object with compression
191
			[[1, 2, 3], 'escapeObject', 'getObject', 'assertSame', [true]],
192
			// Test object without compression
193
			[[1, 2, 3], 'escapeObject', 'getObject', 'assertSame', []],
194
		];
195
		foreach ($params as [$value, $escaper, $getter, $cmp, $args]) {
196
			$result = $db->read('SELECT ' . $db->$escaper($value, ...$args) . ' AS val');
197
			self::$cmp($value, $result->record()->$getter('val', ...$args));
198
		}
199
	}
200
201
}
202