1 | <?php |
||||
2 | /** |
||||
3 | * EGroupware - Setup - db-schema-processor - unit tests |
||||
4 | * |
||||
5 | * Written by Ralf Becker <[email protected]> |
||||
6 | * |
||||
7 | * @link http://www.egroupware.org |
||||
8 | * @author Ralf Becker <[email protected]> |
||||
9 | * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License |
||||
10 | * @package api |
||||
11 | * @subpackage db |
||||
12 | * @version $Id$ |
||||
13 | */ |
||||
14 | |||||
15 | namespace Egroupware\Api; |
||||
16 | |||||
17 | use EGroupware\Api\Db; |
||||
18 | use EGroupware\Api\Db\Schema; |
||||
19 | |||||
20 | // test base providing Egw environment |
||||
21 | require_once realpath(__DIR__.'/../LoggedInTest.php'); |
||||
22 | |||||
23 | // For security reasons we exit by default if called via the webserver |
||||
24 | if (php_sapi_name() !== 'cli') |
||||
25 | { |
||||
26 | die ('Access denied !!!'); |
||||
27 | } |
||||
28 | |||||
29 | /** |
||||
30 | * Testing the Schema processor |
||||
31 | * |
||||
32 | */ |
||||
33 | class SchemaTest extends LoggedInTest { |
||||
34 | |||||
35 | protected static $adodb; |
||||
36 | protected static $db; |
||||
37 | protected static $schema_proc; |
||||
38 | protected static $test_app = 'login'; |
||||
39 | |||||
40 | // define a test-table to create |
||||
41 | protected static $test_tables = array( |
||||
42 | 'schema_proc_test' => array( |
||||
43 | 'fd' => array( |
||||
44 | 'test_auto' => array('type' => 'auto'), |
||||
45 | 'test_int4' => array('type' => 'int','precision' => '4'), |
||||
46 | 'test_varchar' => array('type' => 'varchar','precision' => '128'), |
||||
47 | 'test_char' => array('type' => 'char','precision' => '10'), |
||||
48 | 'test_timestamp' => array('type' => 'timestamp','default'=>'current_timestamp'), |
||||
49 | 'test_text' => array('type' => 'text'), |
||||
50 | 'test_blob' => array('type' => 'blob'), |
||||
51 | ), |
||||
52 | 'pk' => array('test_auto'), |
||||
53 | 'fk' => array(), |
||||
54 | 'ix' => array(array('test_char','test_varchar'),'test_varchar',array('test_text','options'=>array('mysql'=>'FULLTEXT','sapdb'=>false,'maxdb'=>false,'pgsql'=>false,'mssql'=>false))), |
||||
55 | 'uc' => array('test_char') |
||||
56 | ), |
||||
57 | ); |
||||
58 | |||||
59 | /** |
||||
60 | * Get a database connection |
||||
61 | */ |
||||
62 | public static function setUpBeforeClass() : void |
||||
63 | { |
||||
64 | parent::setUpBeforeClass(); |
||||
65 | |||||
66 | // now we should have a valid db-connection |
||||
67 | self::$adodb = $GLOBALS['egw']->ADOdb; |
||||
68 | self::$db = $GLOBALS['egw']->db; |
||||
69 | |||||
70 | // Show lots of debug |
||||
71 | //self::$db->query_log = 'php://stdout'; |
||||
72 | |||||
73 | Db::set_table_definitions(self::$test_app, 'schema_proc_test', self::$test_tables['schema_proc_test']); |
||||
74 | |||||
75 | // dropping test-tables, if they are there from a previous failed run |
||||
76 | self::$schema_proc = new Schema(self::$db->Type); |
||||
77 | foreach(self::$adodb->MetaTables() as $table) |
||||
78 | { |
||||
79 | $table = strtolower($table); |
||||
80 | if (strstr($table,'schema_proc')) |
||||
81 | { |
||||
82 | $aSql = self::$schema_proc->dict->DropTableSQL($table); |
||||
83 | self::$schema_proc->ExecuteSqlArray($aSql,1,"DropTableSQL('%1') = %2",$table,$aSql); |
||||
84 | } |
||||
85 | } |
||||
86 | } |
||||
87 | |||||
88 | /** |
||||
89 | * Try to create test tables, check to see if it worked |
||||
90 | */ |
||||
91 | public function testCreateTable() |
||||
92 | { |
||||
93 | foreach(self::$test_tables as $name => $definition) |
||||
94 | { |
||||
95 | self::$schema_proc->CreateTable($name,$definition); |
||||
96 | |||||
97 | $columns = self::$adodb->MetaColumns($name); |
||||
98 | $this->assertNotFalse($columns); |
||||
99 | $this->assertGreaterThan(0, count($columns)); |
||||
100 | |||||
101 | // check if all columns are there |
||||
102 | foreach($definition['fd'] as $column => $data) |
||||
103 | { |
||||
104 | $this->check_column($column,$columns); |
||||
105 | } |
||||
106 | |||||
107 | // check if all indexes are there |
||||
108 | $indexes = self::$adodb->MetaIndexes($name,true); |
||||
109 | $this->assertNotFalse($indexes); |
||||
110 | if ($indexes !== False) |
||||
111 | { |
||||
112 | foreach(array('ix','uc') as $kind) |
||||
113 | { |
||||
114 | foreach($definition[$kind] as $key => $idx) |
||||
115 | { |
||||
116 | $this->check_index($idx,$kind=='uc',$indexes); |
||||
117 | } |
||||
118 | } |
||||
119 | if (count($definition['pk'])) $this->check_index($definition['pk'],True,$indexes); |
||||
120 | } |
||||
121 | } |
||||
122 | } |
||||
123 | |||||
124 | /** |
||||
125 | * Try to insert some content into the created table(s) |
||||
126 | * |
||||
127 | * @depends testCreateTable |
||||
128 | */ |
||||
129 | public function testInsertContent() |
||||
130 | { |
||||
131 | self::$adodb->Execute("INSERT INTO schema_proc_test (test_int4,test_varchar,test_char) VALUES (1,'Hallo Ralf','0123456789')"); |
||||
132 | |||||
133 | self::$db->insert('schema_proc_test',array( |
||||
134 | 'test_int4' => 2, |
||||
135 | 'test_varchar' => 'Hallo wer noch?', |
||||
136 | 'test_char' => '9876543210', |
||||
137 | 'test_text' => 'This is a test-value for the text-column, insert-value', |
||||
138 | 'test_blob' => 'This is a test-value for the blob-column, insert-value', |
||||
139 | ), False, __LINE__, __FILE__, self::$test_app); |
||||
140 | |||||
141 | $this->check_content( |
||||
142 | self::$adodb->GetAll("SELECT * FROM schema_proc_test"), array( |
||||
143 | array( |
||||
144 | 'test_auto' => 1, 'test_int4' => 1, 'test_varchar' => 'Hallo Ralf','test_char' => '0123456789', |
||||
145 | ), |
||||
146 | array( |
||||
147 | 'test_auto' => 2, 'test_int4' => 2, 'test_varchar' => 'Hallo wer noch?','test_char' => '9876543210', |
||||
148 | 'test_text' => 'This is a test-value for the text-column, insert-value', |
||||
149 | 'test_blob' => 'This is a test-value for the blob-column, insert-value', |
||||
150 | ), |
||||
151 | ) |
||||
152 | ); |
||||
153 | } |
||||
154 | |||||
155 | /** |
||||
156 | * Try to update existing content |
||||
157 | * |
||||
158 | * @depends testInsertContent |
||||
159 | */ |
||||
160 | public function testUpdateContent() |
||||
161 | { |
||||
162 | // updating blob's and other columns |
||||
163 | self::$db->update('schema_proc_test', array( |
||||
164 | 'test_int4' => 99, |
||||
165 | 'test_char' => 'abcdefghij', |
||||
166 | 'test_text' => 'This is a test-value for the text-column', |
||||
167 | 'test_blob' => 'This is a test-value for the blob-column', |
||||
168 | ), array('test_auto'=>1), __LINE__, __FILE__, self::$test_app); |
||||
169 | |||||
170 | // updating only the blob's |
||||
171 | self::$db->update('schema_proc_test',array( |
||||
172 | 'test_text' => 'This is a test-value for the text-column, 2.row', |
||||
173 | 'test_blob' => 'This is a test-value for the blob-column, 2.row', |
||||
174 | ), array('test_auto'=>2), __LINE__, __FILE__, self::$test_app); |
||||
175 | |||||
176 | // db::update uses UpdateBlob only for MaxDB at the moment, it works for MySql too, but fails for postgres with text / CLOB's |
||||
177 | // $adodb->UpdateBlob('schema_proc_test','test_text','This is a test-value for the text-column, 2.row','test_auto=2','CLOB'); |
||||
178 | // $adodb->UpdateBlob('schema_proc_test','test_blob','This is a test-value for the blob-column, 2.row','test_auto=2','BLOB'); |
||||
179 | |||||
180 | $this->check_content(self::$adodb->GetAll("SELECT * FROM schema_proc_test"),array( |
||||
181 | array( |
||||
182 | 'test_auto' => 1, 'test_int4' => 99, 'test_varchar' => 'Hallo Ralf','test_char' => 'abcdefghij', |
||||
183 | 'test_text' => 'This is a test-value for the text-column', |
||||
184 | 'test_blob'=>'This is a test-value for the blob-column', |
||||
185 | ), |
||||
186 | array( |
||||
187 | 'test_auto' => 2, 'test_int4' => 2, 'test_varchar' => 'Hallo wer noch?','test_char' => '9876543210', |
||||
188 | 'test_text' => 'This is a test-value for the text-column, 2.row', |
||||
189 | 'test_blob'=>'This is a test-value for the blob-column, 2.row', |
||||
190 | ), |
||||
191 | )); |
||||
192 | } |
||||
193 | |||||
194 | /** |
||||
195 | * Drop the test_blob column |
||||
196 | * |
||||
197 | * @depends testCreateTable |
||||
198 | */ |
||||
199 | public function testDropColumn() |
||||
200 | { |
||||
201 | $new_table_def = $test_tables['schema_proc_test']; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
![]() |
|||||
202 | unset($new_table_def['fd']['test_blob']); |
||||
203 | self::$schema_proc->DropColumn('schema_proc_test',$new_table_def,'test_blob'); |
||||
204 | $this->check_column('test_blob',self::$adodb->MetaColumns('schema_proc_test'),False); |
||||
205 | } |
||||
206 | |||||
207 | /** |
||||
208 | * Alter the test_char column |
||||
209 | * |
||||
210 | * @depends testCreateTable |
||||
211 | */ |
||||
212 | public function testAlterColumn() |
||||
213 | { |
||||
214 | self::$schema_proc->AlterColumn('schema_proc_test','test_char',array('type' => 'varchar','precision' => 32)); |
||||
215 | $this->check_column_type('test_char','varchar',32,self::$adodb->MetaColumns('schema_proc_test')); |
||||
216 | } |
||||
217 | |||||
218 | /** |
||||
219 | * Add a column |
||||
220 | * |
||||
221 | * @depends testCreateTable |
||||
222 | */ |
||||
223 | public function testAddColumn() |
||||
224 | { |
||||
225 | self::$schema_proc->AddColumn('schema_proc_test','test_bool',array('type' => 'bool')); |
||||
226 | $this->check_column('test_bool',self::$adodb->MetaColumns('schema_proc_test')); |
||||
227 | } |
||||
228 | |||||
229 | /** |
||||
230 | * Rename a column |
||||
231 | * |
||||
232 | * @depends testCreateTable |
||||
233 | */ |
||||
234 | public function testRenameColumn() |
||||
235 | { |
||||
236 | self::$schema_proc->RenameColumn('schema_proc_test','test_timestamp','test_time'); |
||||
237 | $this->check_column('test_timestamp',self::$adodb->MetaColumns('schema_proc_test'),false); |
||||
238 | $this->check_column('test_time',self::$adodb->MetaColumns('schema_proc_test')); |
||||
239 | } |
||||
240 | |||||
241 | /** |
||||
242 | * Rename a table |
||||
243 | * |
||||
244 | * @depends testCreateTable |
||||
245 | */ |
||||
246 | public function testRenameTable() |
||||
247 | { |
||||
248 | self::$schema_proc->RenameTable('schema_proc_test','schema_proc_renamed'); |
||||
249 | $tables = self::$adodb->MetaTables(); |
||||
250 | $this->check_table('schema_proc_test',$tables,False); |
||||
251 | $this->check_table('schema_proc_renamed',$tables); |
||||
252 | } |
||||
253 | |||||
254 | /** |
||||
255 | * @depends testRenameTable |
||||
256 | */ |
||||
257 | public function testRenameColumnWithIndex() |
||||
258 | { |
||||
259 | self::$schema_proc->RenameColumn('schema_proc_renamed','test_varchar','test_varchar_renamed'); |
||||
260 | $columns = self::$adodb->MetaColumns('schema_proc_renamed'); |
||||
261 | $this->check_column('test_varchar',$columns,False); |
||||
262 | $this->check_column('test_varchar_renamed',$columns); |
||||
263 | $indexes = self::$adodb->MetaIndexes('schema_proc_renamed'); |
||||
264 | if ($indexes !== False) |
||||
265 | { |
||||
266 | $this->check_index('test_varchar',False,$indexes,False); |
||||
267 | $this->check_index('test_varchar_renamed',False,$indexes); |
||||
268 | } |
||||
269 | else |
||||
270 | { |
||||
271 | $this->markTestIncomplete(); |
||||
272 | } |
||||
273 | } |
||||
274 | |||||
275 | /** |
||||
276 | * @depends testRenameColumnWithIndex |
||||
277 | */ |
||||
278 | public function testDropIndex() |
||||
279 | { |
||||
280 | self::$schema_proc->DropIndex('schema_proc_renamed',array('test_char','test_varchar_renamed')); |
||||
281 | $indexes = self::$adodb->MetaIndexes('schema_proc_renamed'); |
||||
282 | if ($indexes !== False) |
||||
283 | { |
||||
284 | $this->check_index(array('test_char','test_varchar_renamed'),False,$indexes,False); |
||||
285 | } |
||||
286 | else |
||||
287 | { |
||||
288 | $this->markTestIncomplete(); |
||||
289 | } |
||||
290 | } |
||||
291 | |||||
292 | /** |
||||
293 | * @depends testDropIndex |
||||
294 | */ |
||||
295 | public function testInsertMoreContent() |
||||
296 | { |
||||
297 | self::$db->query("INSERT INTO schema_proc_renamed (test_int4,test_varchar_renamed,test_char) VALUES (10,'Hallo Hallo Hallo ...','12345678901234567890123456789012')"); |
||||
298 | $this->check_content(self::$adodb->GetAll("SELECT * FROM schema_proc_renamed"),array( |
||||
299 | array('test_auto' => 1, 'test_int4' => 99, 'test_varchar_renamed' => 'Hallo Ralf','test_char' => 'abcdefghij'), |
||||
300 | array('test_auto' => 2, 'test_int4' => 2, 'test_varchar_renamed' => 'Hallo wer noch?','test_char' => '9876543210'), |
||||
301 | array('test_auto' => 3, 'test_int4' => 10, 'test_varchar_renamed' => 'Hallo Hallo Hallo ...','test_char' => '12345678901234567890123456789012'), |
||||
302 | )); |
||||
303 | } |
||||
304 | |||||
305 | /** |
||||
306 | * @depends testInsertMoreContent |
||||
307 | */ |
||||
308 | public function testDropTable() |
||||
309 | { |
||||
310 | foreach(self::$adodb->MetaTables() as $table) |
||||
311 | { |
||||
312 | $table = strtolower($table); |
||||
313 | if (strstr($table,'schema_proc')) |
||||
314 | { |
||||
315 | $aSql = self::$schema_proc->dict->DropTableSQL($table); |
||||
316 | self::$schema_proc->ExecuteSqlArray($aSql,1,"DropTableSQL('%1') = %2",$table,$aSql); |
||||
317 | } |
||||
318 | } |
||||
319 | } |
||||
320 | |||||
321 | |||||
322 | /** |
||||
323 | * Checks if table $table exists or not |
||||
324 | * |
||||
325 | * @param string $table table-name |
||||
326 | * @param array $tables array of table-names from call to MetaTables() |
||||
327 | * @param boolean $existence =true should we check for existence or non-existence, default existence |
||||
328 | */ |
||||
329 | protected function check_table($table,$tables,$existence=True) |
||||
330 | { |
||||
331 | $exist = in_array($table,$tables) || in_array(strtoupper($table),$tables); |
||||
332 | |||||
333 | $this->assertEquals($existence, $exist, "Checking for $table"); |
||||
334 | } |
||||
335 | |||||
336 | /** |
||||
337 | * Checks if $column exists or not |
||||
338 | * |
||||
339 | * @param string $column column-name |
||||
340 | * @param array $columns array of adodb field objects from MetaColumns($table) |
||||
341 | * @param boolean $existence =true should we check for existence or non-existence, default existence |
||||
342 | */ |
||||
343 | protected function check_column($column,$columns,$existence=True) |
||||
344 | { |
||||
345 | $exist = isset($columns[$column]) || isset($columns[strtoupper($column)]); |
||||
346 | |||||
347 | $this->assertEquals($existence, $exist, "Checking for $column"); |
||||
348 | } |
||||
349 | |||||
350 | /** |
||||
351 | * Checks the type of a column |
||||
352 | * |
||||
353 | * @param string $column column-name |
||||
354 | * @param string $type column-type as the DB uses it, no eGW type !!! |
||||
355 | * @param int $precision precision |
||||
356 | * @param array $columns array of adodb field objects from MetaColumns($table) |
||||
357 | */ |
||||
358 | protected function check_column_type($column,$type,$precision,$columns) |
||||
359 | { |
||||
360 | static $alternate_types = array( |
||||
361 | 'varchar' => array('C'), |
||||
362 | 'int' => array('I'), |
||||
363 | ); |
||||
364 | |||||
365 | $data = isset($columns[$column]) ? $columns[$column] : $columns[strtoupper($column)]; |
||||
366 | $this->assertInstanceOf('ADOFieldObject', $data, "Column '$column' does not exist."); |
||||
367 | $data->type = strtolower($data->type); |
||||
368 | |||||
369 | $this->assertFalse($data->type != $type && !in_array($data->type,$alternate_types[$type]), |
||||
370 | "Column '$column' is NOT of type '$type', but '$data->type'" |
||||
371 | ); |
||||
372 | |||||
373 | if ($precision) |
||||
374 | { |
||||
375 | $this->assertEquals($precision, $data->max_length, |
||||
376 | "Precision of column '$column' is NOT $precision, but $data->precision" |
||||
377 | ); |
||||
378 | } |
||||
379 | } |
||||
380 | |||||
381 | /** |
||||
382 | * Checks if $idx exists or not |
||||
383 | * |
||||
384 | * @param array $columns array of strings with column-names of that index |
||||
385 | * @param boolean $unique unique index or not |
||||
386 | * @param array $indexes array of index-describtions from call to MetaIndexes($table) |
||||
387 | * @param boolean $_existence =true should we check for existence or none-existence, default existence |
||||
388 | */ |
||||
389 | protected function check_index($columns,$unique,$indexes,$_existence=True) |
||||
390 | { |
||||
391 | if (!is_array($columns)) $columns = array($columns); |
||||
0 ignored issues
–
show
|
|||||
392 | $existence = $_existence && $columns['options'][$GLOBALS['db']->Type] !== False; |
||||
393 | unset($columns['options']); |
||||
394 | |||||
395 | $exist = False; |
||||
396 | $idx_data = array(); |
||||
397 | foreach($indexes as $idx_data) |
||||
398 | { |
||||
399 | if (implode(':',$columns) == strtolower(implode(':',$idx_data['columns']))) |
||||
400 | { |
||||
401 | $exist = true; |
||||
402 | break; |
||||
403 | } |
||||
404 | } |
||||
405 | $this->assertEquals($existence, $exist, |
||||
406 | "Index (".implode(', ',$columns).") is ".($existence ? 'missing' : 'still there') |
||||
407 | ); |
||||
408 | |||||
409 | if ($existence) |
||||
410 | { |
||||
411 | $this->assertEquals($unique, !!$idx_data['unique'], |
||||
412 | "Index (".implode(', ',$columns).") is ".($unique ? 'NOT ' : '')."unique" |
||||
413 | ); |
||||
414 | } |
||||
415 | } |
||||
416 | |||||
417 | /** |
||||
418 | * Checks the content written to the table |
||||
419 | * |
||||
420 | * @param array $is content read from the database via GetAll() |
||||
421 | * @param array $should content against which we test |
||||
422 | */ |
||||
423 | protected function check_content($is,$should) |
||||
424 | { |
||||
425 | foreach($should as $key => $val) |
||||
426 | { |
||||
427 | if (!isset($is[$key]) && isset($is[strtoupper($key)])) |
||||
428 | { |
||||
429 | $key = strtoupper($key); |
||||
430 | } |
||||
431 | if (!is_array($val)) |
||||
432 | { |
||||
433 | $this->assertEquals($val, $is[$key], 'Content read back from table is not as expected'); |
||||
434 | } |
||||
435 | if (is_array($val) && !$this->check_content($is[$key],$val,True) || !is_array($val) && $is[$key] != $val) |
||||
0 ignored issues
–
show
The call to
Egroupware\Api\SchemaTest::check_content() has too many arguments starting with True .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||
436 | { |
||||
437 | $this->fail('Content read back from table is not as expected'); |
||||
438 | return False; |
||||
439 | } |
||||
440 | } |
||||
441 | return True; |
||||
442 | } |
||||
443 | } |