1
|
|
|
<?php |
2
|
|
|
namespace FwlibTest\Db; |
3
|
|
|
|
4
|
|
|
use Fwlib\Bridge\Adodb; |
5
|
|
|
use Fwlib\Db\CodeDictionary; |
6
|
|
|
use Fwolf\Wrapper\PHPUnit\PHPUnitTestCase; |
7
|
|
|
use PHPUnit_Framework_MockObject_MockObject as MockObject; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* @SuppressWarnings(PHPMD.TooManyMethods) |
11
|
|
|
* |
12
|
|
|
* @copyright Copyright 2011-2015 Fwolf |
13
|
|
|
* @license http://www.gnu.org/licenses/lgpl.html LGPL-3.0+ |
14
|
|
|
*/ |
15
|
|
|
class CodeDictionaryTest extends PHPUnitTestCase |
16
|
|
|
{ |
17
|
|
|
/** |
18
|
|
|
* Db mock return value |
19
|
|
|
* |
20
|
|
|
* @var bool |
21
|
|
|
*/ |
22
|
|
|
public static $isConnected; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @var bool |
26
|
|
|
*/ |
27
|
|
|
public static $isDbMysql; |
28
|
|
|
|
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @return MockObject|Adodb |
32
|
|
|
*/ |
33
|
|
|
protected function buildDbMock() |
34
|
|
|
{ |
35
|
|
|
$dbConn = $this->getMockBuilder( |
36
|
|
|
Adodb::class |
37
|
|
|
) |
38
|
|
|
->setMethods([ |
39
|
|
|
'getProfile', |
40
|
|
|
'getSqlDelimiter', |
41
|
|
|
'getSqlTruncate', |
42
|
|
|
'getSqlTransBegin', |
43
|
|
|
'getSqlTransCommit', |
44
|
|
|
'isConnected', |
45
|
|
|
'isDbMysql', |
46
|
|
|
'quoteValue', |
47
|
|
|
]) |
48
|
|
|
->disableOriginalConstructor() |
49
|
|
|
->getMock(); |
50
|
|
|
|
51
|
|
|
$dbConn->expects($this->any()) |
52
|
|
|
->method('getProfile') |
53
|
|
|
->will($this->returnValue(['lang' => '{profileLang}'])); |
54
|
|
|
|
55
|
|
|
$dbConn->expects($this->any()) |
56
|
|
|
->method('getSqlDelimiter') |
57
|
|
|
->will($this->returnValue("{sqlDelimiter}\n")); |
58
|
|
|
|
59
|
|
|
$dbConn->expects($this->any()) |
60
|
|
|
->method('getSqlTransBegin') |
61
|
|
|
->will($this->returnValue("{sqlTransBegin}\n")); |
62
|
|
|
|
63
|
|
|
$dbConn->expects($this->any()) |
64
|
|
|
->method('getSqlTransCommit') |
65
|
|
|
->will($this->returnValue("{sqlTransCommit}\n")); |
66
|
|
|
|
67
|
|
|
$dbConn->expects($this->any()) |
68
|
|
|
->method('getSqlTruncate') |
69
|
|
|
->will($this->returnValue('{sqlTruncate}')); |
70
|
|
|
|
71
|
|
|
$dbConn->expects($this->any()) |
72
|
|
|
->method('isConnected') |
73
|
|
|
->will($this->returnCallback(function () { |
74
|
|
|
return CodeDictionaryTest::$isConnected; |
75
|
|
|
})); |
76
|
|
|
|
77
|
|
|
$dbConn->expects($this->any()) |
78
|
|
|
->method('isDbMysql') |
79
|
|
|
->will($this->returnCallback(function () { |
80
|
|
|
return CodeDictionaryTest::$isDbMysql; |
81
|
|
|
})); |
82
|
|
|
|
83
|
|
|
$dbConn->expects($this->any()) |
84
|
|
|
->method('quoteValue') |
85
|
|
|
->will($this->returnValue('{quoteValue}')); |
86
|
|
|
|
87
|
|
|
return $dbConn; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* @return MockObject|CodeDictionary |
93
|
|
|
*/ |
94
|
|
|
protected function buildMock() |
95
|
|
|
{ |
96
|
|
|
$dictionary = new CodeDictionary(); |
97
|
|
|
|
98
|
|
|
$dictionary->set([ |
99
|
|
|
[123, 'a'], |
100
|
|
|
['bac', 2], |
101
|
|
|
[321, 'c'], |
102
|
|
|
]); |
103
|
|
|
|
104
|
|
|
return $dictionary; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
|
108
|
|
|
public function testAccessors() |
109
|
|
|
{ |
110
|
|
|
$dictionary = new CodeDictionary; |
111
|
|
|
|
112
|
|
|
$dictionary->setColumns(['a', 'b']); |
113
|
|
|
$this->assertEqualArray(['a', 'b'], $dictionary->getColumns()); |
114
|
|
|
|
115
|
|
|
$dictionary->setPrimaryKey('a'); |
116
|
|
|
$this->assertEquals('a', $dictionary->getPrimaryKey()); |
117
|
|
|
|
118
|
|
|
$dictionary->setTable('dummyTable'); |
119
|
|
|
$this->assertEquals('dummyTable', $dictionary->getTable()); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
|
123
|
|
|
public function testFixDictionaryIndex() |
124
|
|
|
{ |
125
|
|
|
$arrayWithoutIndex = [ |
126
|
|
|
[123, 'a'], |
127
|
|
|
['bac', 2], |
128
|
|
|
[321, 'c'], |
129
|
|
|
]; |
130
|
|
|
$arrayWithIndex = [ |
131
|
|
|
123 => [ |
132
|
|
|
'code' => 123, |
133
|
|
|
'title' => 'a', |
134
|
|
|
], |
135
|
|
|
'bac' => [ |
136
|
|
|
'code' => 'bac', |
137
|
|
|
'title' => 2, |
138
|
|
|
], |
139
|
|
|
321 => [ |
140
|
|
|
'code' => 321, |
141
|
|
|
'title' => 'c', |
142
|
|
|
], |
143
|
|
|
]; |
144
|
|
|
|
145
|
|
|
$dictionary = $this->buildMock(); |
146
|
|
|
|
147
|
|
|
// Simulate $dictionary define by assign value to it |
148
|
|
|
$this->reflectionSet($dictionary, 'dictionary', $arrayWithoutIndex); |
149
|
|
|
// Then call constructor to simulate new operate |
150
|
|
|
$this->reflectionCall($dictionary, '__construct'); |
151
|
|
|
|
152
|
|
|
$this->assertEqualArray($arrayWithIndex, $dictionary->getAll()); |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
|
156
|
|
|
public function testGet() |
157
|
|
|
{ |
158
|
|
|
$dictionary = $this->buildMock(); |
159
|
|
|
|
160
|
|
|
$this->assertEquals(null, $dictionary->get(null)); |
161
|
|
|
$this->assertEquals(null, $dictionary->getMultiple([])); |
162
|
|
|
$this->assertEquals(null, $dictionary->get('notExistKey')); |
163
|
|
|
$this->assertEquals( |
164
|
|
|
['notExistKey' => null], |
165
|
|
|
$dictionary->getMultiple(['notExistKey']) |
166
|
|
|
); |
167
|
|
|
$this->assertEquals('a', $dictionary->get(123)); |
168
|
|
|
$this->assertEquals(2, $dictionary->get('bac')); |
169
|
|
|
$this->assertEquals( |
170
|
|
|
[123 => 'a', 321 => 'c'], |
171
|
|
|
$dictionary->getMultiple([123, 321]) |
172
|
|
|
); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
|
176
|
|
|
public function testGetSingleColumn() |
177
|
|
|
{ |
178
|
|
|
$dictionary = $this->buildMock(); |
179
|
|
|
$this->reflectionSet($dictionary, 'dictionary', []); |
180
|
|
|
|
181
|
|
|
$dictionary->setColumns(['a', 'b', 'c']) |
182
|
|
|
->setPrimaryKey('a') |
183
|
|
|
->set([ |
184
|
|
|
['a' => 1, 'b' => 2, 'c' => 3], |
185
|
|
|
['a' => 10, 'b' => 20, 'c' => 30], |
186
|
|
|
['a' => 100, 'b' => 200, 'c' => 300], |
187
|
|
|
]); |
188
|
|
|
|
189
|
|
|
$this->assertEqualArray( |
190
|
|
|
$dictionary->getSingleColumn('c'), |
191
|
|
|
[1 => 3, 10 => 30, 100 => 300] |
192
|
|
|
); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* @expectedException \Fwlib\Db\Exception\InvalidColumnException |
198
|
|
|
*/ |
199
|
|
|
public function testGetSingleColumnWithInvalidColumn() |
200
|
|
|
{ |
201
|
|
|
$dictionary = $this->buildMock(); |
202
|
|
|
|
203
|
|
|
$dictionary->getSingleColumn('notExist'); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* @expectedException \Exception |
209
|
|
|
* @expectedExceptionMessage Database not connected |
210
|
|
|
*/ |
211
|
|
|
public function testGetSqlWithDbNotConnected() |
212
|
|
|
{ |
213
|
|
|
$dictionary = $this->buildMock(); |
214
|
|
|
self::$isConnected = false; |
215
|
|
|
|
216
|
|
|
$dictionary->getSql($this->buildDbMock()); |
|
|
|
|
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
|
220
|
|
|
public function testGetSqlWithNoTableName() |
221
|
|
|
{ |
222
|
|
|
$dictionary = $this->buildMock(); |
223
|
|
|
|
224
|
|
|
$dictionary->setTable(''); |
225
|
|
|
|
226
|
|
|
$this->assertEmpty($dictionary->getSql($this->buildDbMock())); |
|
|
|
|
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
|
230
|
|
|
public function testGetSqlWithTable() |
231
|
|
|
{ |
232
|
|
|
$dictionary = $this->buildMock(); |
233
|
|
|
|
234
|
|
|
self::$isConnected = true; |
235
|
|
|
self::$isDbMysql = true; |
236
|
|
|
|
237
|
|
|
/** @noinspection SpellCheckingInspection */ |
238
|
|
|
$sqlExpected = 'SET NAMES \'{PROFILELANG}\'{sqlDelimiter} |
239
|
|
|
{sqlTransBegin} |
240
|
|
|
TRUNCATE TABLE code_dictionary{sqlDelimiter} |
241
|
|
|
{sqlTransCommit} |
242
|
|
|
{sqlTransBegin} |
243
|
|
|
INSERT INTO code_dictionary (code, title) VALUES ({quoteValue}, {quoteValue}){sqlDelimiter} |
244
|
|
|
INSERT INTO code_dictionary (code, title) VALUES ({quoteValue}, {quoteValue}){sqlDelimiter} |
245
|
|
|
INSERT INTO code_dictionary (code, title) VALUES ({quoteValue}, {quoteValue}){sqlDelimiter} |
246
|
|
|
{sqlTransCommit} |
247
|
|
|
'; |
248
|
|
|
|
249
|
|
|
$sql = $dictionary->getSql($this->buildDbMock()); |
|
|
|
|
250
|
|
|
|
251
|
|
|
$this->assertEquals($sqlExpected, $sql); |
252
|
|
|
$this->assertEquals(3, preg_match_all('/INSERT INTO/', $sql, $match)); |
253
|
|
|
$this->assertEquals(1, preg_match_all('/TRUNCATE/', $sql, $match)); |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
|
257
|
|
|
public function testSearch() |
258
|
|
|
{ |
259
|
|
|
$dictionary = $this->buildMock(); |
260
|
|
|
$code = CodeDictionary::COL_CODE; |
261
|
|
|
$title = CodeDictionary::COL_TITLE; |
262
|
|
|
|
263
|
|
|
$this->assertEquals( |
264
|
|
|
['bac' => [$code => 'bac', $title => 2]], |
265
|
|
|
$dictionary->search(function ($row) { |
266
|
|
|
return !is_numeric($row[CodeDictionary::COL_CODE]); |
267
|
|
|
}) |
268
|
|
|
); |
269
|
|
|
|
270
|
|
|
$this->assertEquals( |
271
|
|
|
[ |
272
|
|
|
123 => [$code => 123, $title => 'a'], |
273
|
|
|
321 => [$code => 321, $title => 'c'], |
274
|
|
|
], |
275
|
|
|
$dictionary->search(function ($row) { |
276
|
|
|
return '2' == substr($row[CodeDictionary::COL_CODE], 1, 1); |
277
|
|
|
}) |
278
|
|
|
); |
279
|
|
|
|
280
|
|
|
$this->assertEquals( |
281
|
|
|
[ |
282
|
|
|
321 => [$code => 321, $title => 'c'], |
283
|
|
|
], |
284
|
|
|
$dictionary->search(function ($row) { |
285
|
|
|
return 'c' == $row[CodeDictionary::COL_TITLE] && |
286
|
|
|
'2' == substr($row[CodeDictionary::COL_CODE], 1, 1); |
287
|
|
|
}) |
288
|
|
|
); |
289
|
|
|
|
290
|
|
|
// Search with assign cols |
291
|
|
|
$this->assertEquals( |
292
|
|
|
[321 => 'c'], |
293
|
|
|
$dictionary->search(function ($row) { |
294
|
|
|
return 'c' == $row[CodeDictionary::COL_TITLE]; |
295
|
|
|
}, $title) |
296
|
|
|
); |
297
|
|
|
|
298
|
|
|
// Search on empty dictionary will return empty array |
299
|
|
|
$this->reflectionSet($dictionary, 'dictionary', []); |
300
|
|
|
$this->assertEqualArray( |
301
|
|
|
[], |
302
|
|
|
$dictionary->search('time') |
303
|
|
|
); |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
|
307
|
|
|
public function testSet() |
308
|
|
|
{ |
309
|
|
|
$dictionary = $this->buildMock(); |
310
|
|
|
|
311
|
|
|
$dictionary->set(['foo', 'bar']); |
312
|
|
|
$this->assertEquals(4, count($dictionary->getAll())); |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
|
316
|
|
|
/** |
317
|
|
|
* @expectedException \Exception |
318
|
|
|
* @expectedExceptionMessage Primary key value is empty or not set |
319
|
|
|
*/ |
320
|
|
|
public function testSetWithEmptyPk() |
321
|
|
|
{ |
322
|
|
|
$dictionary = $this->buildMock(); |
323
|
|
|
|
324
|
|
|
$dictionary->set(['', 'bar']); |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* @expectedException \Exception |
330
|
|
|
* @expectedExceptionMessage contain all columns |
331
|
|
|
*/ |
332
|
|
|
public function testSetWithEmptyRowInData() |
333
|
|
|
{ |
334
|
|
|
$dictionary = $this->buildMock(); |
335
|
|
|
|
336
|
|
|
$dictionary->set([[null], ['foo', 'bar']]); |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
|
340
|
|
|
public function testSetWithEmptyValue() |
341
|
|
|
{ |
342
|
|
|
$dictionary = $this->buildMock(); |
343
|
|
|
|
344
|
|
|
$dictionaryBefore = $dictionary->getAll(); |
345
|
|
|
$dictionary->set([]); |
346
|
|
|
$dictionaryAfter = $dictionary->getAll(); |
347
|
|
|
|
348
|
|
|
$this->assertEqualArray($dictionaryBefore, $dictionaryAfter); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
|
352
|
|
|
/** |
353
|
|
|
* @expectedException \Exception |
354
|
|
|
* @expectedExceptionMessage Dictionary column not defined |
355
|
|
|
*/ |
356
|
|
|
public function testSetWithNoColumn() |
357
|
|
|
{ |
358
|
|
|
$dictionary = $this->buildMock(); |
359
|
|
|
|
360
|
|
|
$dictionary->setColumns([]); |
361
|
|
|
|
362
|
|
|
$dictionary->set(['foo' => 'bar']); |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* @expectedException \Exception |
368
|
|
|
* @expectedExceptionMessage include primary key |
369
|
|
|
*/ |
370
|
|
|
public function testSetWithPrimaryKeyNotInColumn() |
371
|
|
|
{ |
372
|
|
|
$dictionary = $this->buildMock(); |
373
|
|
|
|
374
|
|
|
$dictionary->setPrimaryKey('notExistColumn'); |
375
|
|
|
|
376
|
|
|
$dictionary->set(['foo' => 'bar']); |
377
|
|
|
} |
378
|
|
|
} |
379
|
|
|
|
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.