]> git.agnieray.net Git - galette.git/blob - tests/GaletteTestCase.php
4378493019ea8be04b3bfa77eadd029f954601c2
[galette.git] / tests / GaletteTestCase.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Galette tests
7 *
8 * PHP version 5
9 *
10 * Copyright © 2020 The Galette Team
11 *
12 * This file is part of Galette (http://galette.tuxfamily.org).
13 *
14 * Galette is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * Galette is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
26 *
27 * @category Core
28 * @package GaletteTests
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2020 The Galette Team
32 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
33 * @link http://galette.eu
34 * @since 2020-12-27
35 */
36
37 namespace Galette;
38
39 use atoum;
40
41 /**
42 * Galette tests case main class
43 *
44 * @category Core
45 * @name GaletteTestCase
46 * @package GaletteTests
47 * @author Johan Cwiklinski <johan@x-tnd.be>
48 * @copyright 2020 The Galette Team
49 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
50 * @link http://galette.eu
51 * @since 2020-12-27
52 */
53 abstract class GaletteTestCase extends atoum
54 {
55 protected $zdb;
56 protected $members_fields;
57 protected $members_fields_cats;
58 protected $i18n;
59 protected $preferences;
60 protected $session;
61 protected $login;
62 protected $history;
63 protected $logger_storage = '';
64
65 protected $adh;
66 protected $contrib;
67 protected $adh_ids = [];
68 protected $contrib_ids = [];
69 /** @var \mock\Slim\Router */
70 protected $mocked_router;
71 /** @var array */
72 protected $flash_data;
73 /** @var \Slim\Flash\Messages */
74 protected $flash;
75 protected $container;
76 protected $request;
77 protected $response;
78 protected $seed;
79
80 /**
81 * Set up tests
82 *
83 * @param stgring $testMethod Method tested
84 *
85 * @return void
86 */
87 public function beforeTestMethod($testMethod)
88 {
89 $this->mocked_router = new \mock\Slim\Router();
90 $this->calling($this->mocked_router)->pathFor = function ($name, $params) {
91 return $name;
92 };
93 $flash_data = [];
94 $this->flash_data = &$flash_data;
95 $this->flash = new \Slim\Flash\Messages($flash_data);
96
97 $app = new \Galette\Core\SlimApp();
98 $plugins = new \Galette\Core\Plugins();
99 require GALETTE_BASE_PATH . '/includes/dependencies.php';
100 $container = $app->getContainer();
101 $_SERVER['HTTP_HOST'] = '';
102
103 $container->set('flash', $this->flash);
104 $container->set(Slim\Flash\Messages::class, $this->flash);
105 $container->set('router', $this->mocked_router);
106 $container->set(Slim\Router::class, $this->mocked_router);
107
108 $this->container = $container;
109
110 $this->zdb = $container->get('zdb');
111 $this->i18n = $container->get('i18n');
112 $this->login = $container->get('login');
113 $this->preferences = $container->get('preferences');
114 $this->history = $container->get('history');
115 $this->members_fields = $container->get('members_fields');
116 $this->members_fields_cats = $container->get('members_fields_cats');
117 $this->request = $container->get('request');
118 $this->response = $container->get('response');
119
120 global $zdb, $login, $hist, $i18n, $container, $galette_log_var; // globals :(
121 $zdb = $this->zdb;
122 $login = $this->login;
123 $hist = $this->history;
124 $i18n = $this->i18n;
125 $container = $this->container;
126 $galette_log_var = $this->logger_storage;
127 }
128
129 /**
130 * Loads member from a resultset
131 *
132 * @param integer $id Id
133 *
134 * @return void
135 */
136 protected function loadAdherent($id)
137 {
138 $this->adh = new \Galette\Entity\Adherent($this->zdb, (int)$id);
139 $this->adh->setDependencies(
140 $this->preferences,
141 $this->members_fields,
142 $this->history
143 );
144 }
145
146 /**
147 * Get Faker data for one member
148 *
149 * @return array
150 */
151 protected function dataAdherentOne(): array
152 {
153 $bdate = new \DateTime(date('Y') . '-12-26');
154 //member is expected to be 82 years old
155 $years = 82;
156 $now = new \DateTime();
157 if ($now <= $bdate) {
158 ++$years;
159 }
160 $bdate->sub(new \DateInterval('P' . $years . 'Y'));
161 $data = [
162 'nom_adh' => 'Durand',
163 'prenom_adh' => 'René',
164 'ville_adh' => 'Martel',
165 'cp_adh' => '39 069',
166 'adresse_adh' => '66, boulevard De Oliveira',
167 'email_adh' => 'meunier.josephine@ledoux.com',
168 'login_adh' => 'arthur.hamon',
169 'mdp_adh' => 'J^B-()f',
170 'mdp_adh2' => 'J^B-()f',
171 'bool_admin_adh' => false,
172 'bool_exempt_adh' => false,
173 'bool_display_info' => true,
174 'sexe_adh' => 0,
175 'prof_adh' => 'Chef de fabrication',
176 'titre_adh' => null,
177 'ddn_adh' => $bdate->format('Y-m-d'),
178 'lieu_naissance' => 'Gonzalez-sur-Meunier',
179 'pseudo_adh' => 'ubertrand',
180 'pays_adh' => 'Antarctique',
181 'tel_adh' => '0439153432',
182 'url_adh' => 'http://bouchet.com/',
183 'activite_adh' => true,
184 'id_statut' => 9,
185 'date_crea_adh' => '2020-06-10',
186 'pref_lang' => 'en_US',
187 'fingerprint' => 'FAKER' . $this->seed,
188 ];
189 return $data;
190 }
191
192 /**
193 * Get Faker data for second member
194 *
195 * @return array
196 */
197 protected function dataAdherentTwo(): array
198 {
199 $bdate = new \DateTime(date('Y') . '-09-13');
200 //member is expected to be 28 years old
201 $years = 28;
202 $now = new \DateTime();
203 if ($now <= $bdate) {
204 ++$years;
205 }
206 $bdate->sub(new \DateInterval('P' . $years . 'Y'));
207
208 $data = [
209 'nom_adh' => 'Hoarau',
210 'prenom_adh' => 'Lucas',
211 'ville_adh' => 'Reynaudnec',
212 'cp_adh' => '63077',
213 'adresse_adh' => '2, boulevard Legros',
214 'email_adh' => 'phoarau@tele2.fr',
215 'login_adh' => 'nathalie51',
216 'mdp_adh' => 'T.u!IbKOi|06',
217 'mdp_adh2' => 'T.u!IbKOi|06',
218 'bool_admin_adh' => false,
219 'bool_exempt_adh' => false,
220 'bool_display_info' => false,
221 'sexe_adh' => 1,
222 'prof_adh' => 'Extraction',
223 'titre_adh' => null,
224 'ddn_adh' => $bdate->format('Y-m-d'),
225 'lieu_naissance' => 'Fischer',
226 'pseudo_adh' => 'vallet.camille',
227 'pays_adh' => null,
228 'tel_adh' => '05 59 53 59 43',
229 'url_adh' => 'http://bodin.net/omnis-ratione-sint-dolorem-architecto',
230 'activite_adh' => true,
231 'id_statut' => 9,
232 'date_crea_adh' => '2019-05-20',
233 'pref_lang' => 'ca',
234 'fingerprint' => 'FAKER' . $this->seed,
235 'societe_adh' => 'Philippe',
236 'is_company' => true,
237 ];
238 return $data;
239 }
240
241 /**
242 * Create member from data
243 *
244 * @param array $data Data to use to create member
245 *
246 * @return \Galette\Entity\Adherent
247 */
248 public function createMember(array $data)
249 {
250 $this->adh = new \Galette\Entity\Adherent($this->zdb);
251 $this->adh->setDependencies(
252 $this->preferences,
253 $this->members_fields,
254 $this->history
255 );
256
257 $check = $this->adh->check($data, [], []);
258 if (is_array($check)) {
259 var_dump($check);
260 }
261 $this->boolean($check)->isTrue();
262
263 $store = $this->adh->store();
264 $this->boolean($store)->isTrue();
265
266 return $this->adh;
267 }
268
269 /**
270 * Check members expecteds
271 *
272 * @param Adherent $adh Member instance, if any
273 * @param array $new_expecteds Changes on expected values
274 *
275 * @return void
276 */
277 protected function checkMemberOneExpected($adh = null, $new_expecteds = [])
278 {
279 if ($adh === null) {
280 $adh = $this->adh;
281 }
282
283 $expecteds = [
284 'nom_adh' => 'Durand',
285 'prenom_adh' => 'René',
286 'ville_adh' => 'Martel',
287 'cp_adh' => '07 926',
288 'adresse_adh' => '66, boulevard De Oliveira',
289 'email_adh' => 'meunier.josephine@ledoux.com',
290 'login_adh' => 'arthur.hamon',
291 'mdp_adh' => 'J^B-()f',
292 'bool_admin_adh' => false,
293 'bool_exempt_adh' => false,
294 'bool_display_info' => true,
295 'sexe_adh' => 0,
296 'prof_adh' => 'Chef de fabrication',
297 'titre_adh' => null,
298 'ddn_adh' => 'NOT USED',
299 'lieu_naissance' => 'Gonzalez-sur-Meunier',
300 'pseudo_adh' => 'ubertrand',
301 'cp_adh' => '39 069',
302 'pays_adh' => 'Antarctique',
303 'tel_adh' => '0439153432',
304 'url_adh' => 'http://bouchet.com/',
305 'activite_adh' => true,
306 'id_statut' => 9,
307 'pref_lang' => 'en_US',
308 'fingerprint' => 'FAKER95842354',
309 'societe_adh' => ''
310 ];
311 $expecteds = array_merge($expecteds, $new_expecteds);
312
313 foreach ($expecteds as $key => $value) {
314 $property = $this->members_fields[$key]['propname'];
315 switch ($key) {
316 case 'bool_admin_adh':
317 $this->boolean($adh->isAdmin())->isIdenticalTo($value);
318 break;
319 case 'bool_exempt_adh':
320 $this->boolean($adh->isDueFree())->isIdenticalTo($value);
321 break;
322 case 'bool_display_info':
323 $this->boolean($adh->appearsInMembersList())->isIdenticalTo($value);
324 break;
325 case 'activite_adh':
326 $this->boolean($adh->isActive())->isIdenticalTo($value);
327 break;
328 case 'mdp_adh':
329 $pw_checked = password_verify($value, $adh->password);
330 $this->boolean($pw_checked)->isTrue();
331 break;
332 case 'ddn_adh':
333 //rely on age, not on birthdate
334 $this->variable($adh->$property)->isNotNull();
335 $this->string($adh->getAge())->isIdenticalTo(' (82 years old)');
336 break;
337 default:
338 $this->variable($adh->$property)->isIdenticalTo(
339 $value,
340 "$property expected {$value} got {$adh->$property}"
341 );
342
343 break;
344 }
345 }
346
347 $d = \DateTime::createFromFormat('Y-m-d', $expecteds['ddn_adh']);
348
349 $expected_str = ' (82 years old)';
350 $this->string($adh->getAge())->isIdenticalTo($expected_str);
351 $this->boolean($adh->hasChildren())->isFalse();
352 $this->boolean($adh->hasParent())->isFalse();
353 $this->boolean($adh->hasPicture())->isFalse();
354
355 $this->string($adh->sadmin)->isIdenticalTo('No');
356 $this->string($adh->sdue_free)->isIdenticalTo('No');
357 $this->string($adh->sappears_in_list)->isIdenticalTo('Yes');
358 $this->string($adh->sstaff)->isIdenticalTo('No');
359 $this->string($adh->sactive)->isIdenticalTo('Active');
360 $this->variable($adh->stitle)->isNull();
361 $this->string($adh->sstatus)->isIdenticalTo('Non-member');
362 $this->string($adh->sfullname)->isIdenticalTo('DURAND René');
363 $this->string($adh->saddress)->isIdenticalTo('66, boulevard De Oliveira');
364 $this->string($adh->sname)->isIdenticalTo('DURAND René');
365
366 $this->string($adh->getAddress())->isIdenticalTo($expecteds['adresse_adh']);
367 $this->string($adh->getAddressContinuation())->isEmpty();
368 $this->string($adh->getZipcode())->isIdenticalTo($expecteds['cp_adh']);
369 $this->string($adh->getTown())->isIdenticalTo($expecteds['ville_adh']);
370 $this->string($adh->getCountry())->isIdenticalTo($expecteds['pays_adh']);
371
372 $this->string($adh::getSName($this->zdb, $adh->id))->isIdenticalTo('DURAND René');
373 $this->string($adh->getRowClass())->isIdenticalTo('active cotis-never');
374 }
375
376 /**
377 * Check members expecteds
378 *
379 * @param Adherent $adh Member instance, if any
380 * @param array $new_expecteds Changes on expected values
381 *
382 * @return void
383 */
384 protected function checkMemberTwoExpected($adh = null, $new_expecteds = [])
385 {
386 if ($adh === null) {
387 $adh = $this->adh;
388 }
389
390 $expecteds = [
391 'nom_adh' => 'Hoarau',
392 'prenom_adh' => 'Lucas',
393 'ville_adh' => 'Reynaudnec',
394 'cp_adh' => '63077',
395 'adresse_adh' => '2, boulevard Legros',
396 'email_adh' => 'phoarau@tele2.fr',
397 'login_adh' => 'nathalie51',
398 'mdp_adh' => 'T.u!IbKOi|06',
399 'bool_admin_adh' => false,
400 'bool_exempt_adh' => false,
401 'bool_display_info' => false,
402 'sexe_adh' => 1,
403 'prof_adh' => 'Extraction',
404 'titre_adh' => null,
405 'ddn_adh' => 'NOT USED',
406 'lieu_naissance' => 'Fischer',
407 'pseudo_adh' => 'vallet.camille',
408 'pays_adh' => '',
409 'tel_adh' => '05 59 53 59 43',
410 'url_adh' => 'http://bodin.net/omnis-ratione-sint-dolorem-architecto',
411 'activite_adh' => true,
412 'id_statut' => 9,
413 'pref_lang' => 'ca',
414 'fingerprint' => 'FAKER' . $this->seed,
415 'societe_adh' => 'Philippe'
416 ];
417 $expecteds = array_merge($expecteds, $new_expecteds);
418
419 foreach ($expecteds as $key => $value) {
420 $property = $this->members_fields[$key]['propname'];
421 switch ($key) {
422 case 'bool_admin_adh':
423 $this->boolean($adh->isAdmin())->isIdenticalTo($value);
424 break;
425 case 'bool_exempt_adh':
426 $this->boolean($adh->isDueFree())->isIdenticalTo($value);
427 break;
428 case 'bool_display_info':
429 $this->boolean($adh->appearsInMembersList())->isIdenticalTo($value);
430 break;
431 case 'activite_adh':
432 $this->boolean($adh->isActive())->isIdenticalTo($value);
433 break;
434 case 'mdp_adh':
435 $pw_checked = password_verify($value, $adh->password);
436 $this->boolean($pw_checked)->isTrue();
437 break;
438 case 'ddn_adh':
439 //rely on age, not on birthdate
440 $this->variable($adh->$property)->isNotNull();
441 $this->string($adh->getAge())->isIdenticalTo(' (28 years old)');
442 break;
443 default:
444 $this->variable($adh->$property)->isIdenticalTo(
445 $value,
446 "$property expected {$value} got {$adh->$property}"
447 );
448 break;
449 }
450 }
451
452 $d = \DateTime::createFromFormat('Y-m-d', $expecteds['ddn_adh']);
453
454 $expected_str = ' (28 years old)';
455 $this->string($adh->getAge())->isIdenticalTo($expected_str);
456 $this->boolean($adh->hasChildren())->isFalse();
457 $this->boolean($adh->hasParent())->isFalse();
458 $this->boolean($adh->hasPicture())->isFalse();
459
460 $this->string($adh->sadmin)->isIdenticalTo('No');
461 $this->string($adh->sdue_free)->isIdenticalTo('No');
462 $this->string($adh->sappears_in_list)->isIdenticalTo('No');
463 $this->string($adh->sstaff)->isIdenticalTo('No');
464 $this->string($adh->sactive)->isIdenticalTo('Active');
465 $this->variable($adh->stitle)->isNull();
466 $this->string($adh->sstatus)->isIdenticalTo('Non-member');
467 $this->string($adh->sfullname)->isIdenticalTo('HOARAU Lucas');
468 $this->string($adh->saddress)->isIdenticalTo('2, boulevard Legros');
469 $this->string($adh->sname)->isIdenticalTo('HOARAU Lucas');
470
471 $this->string($adh->getAddress())->isIdenticalTo($expecteds['adresse_adh']);
472 $this->string($adh->getAddressContinuation())->isEmpty();
473 $this->string($adh->getZipcode())->isIdenticalTo($expecteds['cp_adh']);
474 $this->string($adh->getTown())->isIdenticalTo($expecteds['ville_adh']);
475 $this->string($adh->getCountry())->isIdenticalTo($expecteds['pays_adh']);
476
477 $this->string($adh::getSName($this->zdb, $adh->id))->isIdenticalTo('HOARAU Lucas');
478 $this->string($adh->getRowClass())->isIdenticalTo('active cotis-never');
479 }
480
481 /**
482 * Look in database if test member already exists
483 *
484 * @return false|ResultSet
485 */
486 protected function adhOneExists()
487 {
488 $mdata = $this->dataAdherentOne();
489 $select = $this->zdb->select(\Galette\Entity\Adherent::TABLE, 'a');
490 $select->where(
491 array(
492 'a.fingerprint' => 'FAKER' . $this->seed,
493 'a.login_adh' => $mdata['login_adh']
494 )
495 );
496
497 $results = $this->zdb->execute($select);
498 if ($results->count() === 0) {
499 return false;
500 } else {
501 return $results;
502 }
503 }
504
505 /**
506 * Look in database if test member already exists
507 *
508 * @return false|ResultSet
509 */
510 protected function adhTwoExists()
511 {
512 $mdata = $this->dataAdherentTwo();
513 $select = $this->zdb->select(\Galette\Entity\Adherent::TABLE, 'a');
514 $select->where(
515 array(
516 'a.fingerprint' => 'FAKER' . $this->seed,
517 'a.login_adh' => $mdata['login_adh']
518 )
519 );
520
521 $results = $this->zdb->execute($select);
522 if ($results->count() === 0) {
523 return false;
524 } else {
525 return $results;
526 }
527 }
528
529 /**
530 * Get member one
531 *
532 * @return \Galette\Entity\Adherent
533 */
534 protected function getMemberOne()
535 {
536 $rs = $this->adhOneExists();
537 if ($rs === false) {
538 $this->createMember($this->dataAdherentOne());
539 } else {
540 $this->loadAdherent($rs->current()->id_adh);
541 }
542 }
543
544 /**
545 * Get member two
546 *
547 * @return \Galette\Entity\Adherent
548 */
549 protected function getMemberTwo()
550 {
551 $rs = $this->adhTwoExists();
552 if ($rs === false) {
553 $this->createMember($this->dataAdherentTwo());
554 } else {
555 $this->loadAdherent($rs->current()->id_adh);
556 }
557 }
558
559 /**
560 * Create contribution from data
561 *
562 * @param array $data Data to use to create contribution
563 *
564 * @return \Galette\Entity\Contribution
565 */
566 public function createContrib(array $data)
567 {
568 $this->contrib = new \Galette\Entity\Contribution($this->zdb, $this->login);
569 $contrib = $this->contrib;
570 $check = $contrib->check($data, [], []);
571 if (is_array($check)) {
572 var_dump($check);
573 }
574 $this->boolean($check)->isTrue();
575
576 $store = $contrib->store();
577 $this->boolean($store)->isTrue();
578
579 return $contrib;
580 }
581
582 /**
583 * Create test contribution in database
584 *
585 * @return void
586 */
587 protected function createContribution()
588 {
589 $bdate = new \DateTime(); // 2020-11-07
590 $bdate->sub(new \DateInterval('P5M')); // 2020-06-07
591 $bdate->add(new \DateInterval('P3D')); // 2020-06-10
592
593 $edate = clone $bdate;
594 $edate->add(new \DateInterval('P1Y'));
595
596 $data = [
597 'id_adh' => $this->adh->id,
598 'id_type_cotis' => 1,
599 'montant_cotis' => 92,
600 'type_paiement_cotis' => 3,
601 'info_cotis' => 'FAKER' . $this->seed,
602 'date_enreg' => $bdate->format('Y-m-d'),
603 'date_debut_cotis' => $bdate->format('Y-m-d'),
604 'date_fin_cotis' => $edate->format('Y-m-d'),
605 ];
606 $this->createContrib($data);
607 $this->checkContribExpected();
608 }
609
610 /**
611 * Check contributions expected
612 *
613 * @param Contribution $contrib Contribution instance, if any
614 * @param array $new_expecteds Changes on expected values
615 *
616 * @return void
617 */
618 protected function checkContribExpected($contrib = null, $new_expecteds = [])
619 {
620 if ($contrib === null) {
621 $contrib = $this->contrib;
622 }
623
624 $date_begin = $contrib->raw_begin_date;
625 $date_end = clone $date_begin;
626 $date_end->add(new \DateInterval('P1Y'));
627
628 $this->object($contrib->raw_date)->isInstanceOf('DateTime');
629 $this->object($contrib->raw_begin_date)->isInstanceOf('DateTime');
630 $this->object($contrib->raw_end_date)->isInstanceOf('DateTime');
631
632 $expecteds = [
633 'id_adh' => "{$this->adh->id}",
634 'id_type_cotis' => 1,
635 'montant_cotis' => '92',
636 'type_paiement_cotis' => '3',
637 'info_cotis' => 'FAKER' . $this->seed,
638 'date_fin_cotis' => $date_end->format('Y-m-d'),
639 ];
640 $expecteds = array_merge($expecteds, $new_expecteds);
641
642 $this->string($contrib->raw_end_date->format('Y-m-d'))->isIdenticalTo($expecteds['date_fin_cotis']);
643
644 foreach ($expecteds as $key => $value) {
645 $property = $this->contrib->fields[$key]['propname'];
646 switch ($key) {
647 case \Galette\Entity\ContributionsTypes::PK:
648 $ct = $this->contrib->type;
649 if ($ct instanceof \Galette\Entity\ContributionsTypes) {
650 $this->integer((int)$ct->id)->isIdenticalTo($value);
651 } else {
652 $this->integer($ct)->isIdenticalTo($value);
653 }
654 break;
655 default:
656 $this->variable($contrib->$property)->isEqualTo($value, $property);
657 break;
658 }
659 }
660
661 //load member from db
662 $this->adh = new \Galette\Entity\Adherent($this->zdb, $this->adh->id);
663 //member is now up-to-date
664 $this->string($this->adh->getRowClass())->isIdenticalTo('active cotis-ok');
665 $this->string($this->adh->due_date)->isIdenticalTo($this->contrib->end_date);
666 $this->boolean($this->adh->isUp2Date())->isTrue();
667 }
668
669 /**
670 * Initialize default status in database
671 *
672 * @return void
673 */
674 protected function initStatus(): void
675 {
676 $status = new \Galette\Entity\Status($this->zdb);
677 if (count($status->getList()) === 0) {
678 //status are not yet instantiated.
679 $res = $status->installInit();
680 $this->boolean($res)->isTrue();
681 }
682 }
683
684 /**
685 * Initialize default contributions types in database
686 *
687 * @return void
688 */
689 protected function initContributionsTypes(): void
690 {
691 $ct = new \Galette\Entity\ContributionsTypes($this->zdb);
692 if (count($ct->getCompleteList()) === 0) {
693 //status are not yet instanciated.
694 $res = $ct->installInit();
695 $this->boolean($res)->isTrue();
696 }
697 }
698
699 /**
700 * Initialize default titles in database
701 *
702 * @return void
703 */
704 protected function initTitles(): void
705 {
706 $titles = new \Galette\Repository\Titles($this->zdb);
707 if (count($titles->getList($this->zdb)) === 0) {
708 $res = $titles->installInit($this->zdb);
709 $this->boolean($res)->isTrue();
710 }
711 }
712 }