3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
10 * Copyright © 2020-2023 The Galette Team
12 * This file is part of Galette (http://galette.tuxfamily.org).
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.
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.
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/>.
28 * @package GaletteTests
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2020-2023 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
39 use PHPUnit\Framework\TestCase
;
42 * Galette tests case main class
45 * @name GaletteTestCase
46 * @package GaletteTests
47 * @author Johan Cwiklinski <johan@x-tnd.be>
48 * @copyright 2020-2023 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
53 abstract class GaletteTestCase
extends TestCase
55 /** @var \Galette\Core\Db */
56 protected \Galette\Core\Db
$zdb;
57 protected array $members_fields;
58 protected array $members_fields_cats;
59 /** @var \Galette\Core\I18n */
60 protected \Galette\Core\I18n
$i18n;
61 /** @var \Galette\Core\Preferences */
62 protected \Galette\Core\Preferences
$preferences;
63 protected \RKA\Session
$session;
64 /** @var \Galette\Core\Login */
65 protected \Galette\Core\Login
$login;
66 /** @var \Galette\Core\History */
67 protected \Galette\Core\History
$history;
68 protected $logger_storage = '';
70 /** @var \Galette\Entity\Adherent */
71 protected \Galette\Entity\Adherent
$adh;
72 /** @var \Galette\Entity\Contribution */
73 protected \Galette\Entity\Contribution
$contrib;
74 protected array $adh_ids = [];
75 protected array $contrib_ids = [];
77 protected array $flash_data;
78 /** @var \Slim\Flash\Messages */
79 protected \Slim\Flash\Messages
$flash;
80 protected \DI\Container
$container;
82 protected array $expected_mysql_warnings = [];
89 public function setUp(): void
92 $this->flash_data
= &$flash_data;
93 $this->flash
= new \Slim\Flash\
Messages($flash_data);
95 $gapp = new \Galette\Core\
SlimApp();
96 $app = $gapp->getApp();
97 $plugins = new \Galette\Core\
Plugins();
98 require GALETTE_BASE_PATH
. '/includes/dependencies.php';
99 /** @var \DI\Container $container */
100 $container = $app->getContainer();
101 $_SERVER['HTTP_HOST'] = '';
103 $container->set('flash', $this->flash
);
104 $container->set(\Slim\Flash\Messages
::class, $this->flash
);
106 $app->addRoutingMiddleware();
108 $this->container
= $container;
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->session
= $container->get('session');
119 global $zdb, $login, $hist, $i18n, $container, $galette_log_var; // globals :(
121 $login = $this->login
;
122 $hist = $this->history
;
124 $container = $this->container
;
125 $galette_log_var = $this->logger_storage
;
127 $authenticate = new \Galette\Middleware\
Authenticate($container);
128 $showPublicPages = function (\Slim\Psr7\Request
$request, \Psr\Http\Server\RequestHandlerInterface
$handler) {
129 return $handler->handle($request);
132 require GALETTE_ROOT
. 'includes/routes/main.routes.php';
133 require GALETTE_ROOT
. 'includes/routes/authentication.routes.php';
134 require GALETTE_ROOT
. 'includes/routes/management.routes.php';
135 require GALETTE_ROOT
. 'includes/routes/members.routes.php';
136 require GALETTE_ROOT
. 'includes/routes/groups.routes.php';
137 require GALETTE_ROOT
. 'includes/routes/contributions.routes.php';
138 require GALETTE_ROOT
. 'includes/routes/public_pages.routes.php';
139 require GALETTE_ROOT
. 'includes/routes/ajax.routes.php';
140 require GALETTE_ROOT
. 'includes/routes/plugins.routes.php';
148 public function tearDown(): void
150 if (TYPE_DB
=== 'mysql') {
151 $this->assertSame($this->expected_mysql_warnings
, $this->zdb
->getWarnings());
153 $this->cleanHistory();
157 * Loads member from a resultset
159 * @param integer $id Id
163 protected function loadAdherent($id)
165 $this->adh
= new \Galette\Entity\
Adherent($this->zdb
, (int)$id);
166 $this->adh
->setDependencies(
168 $this->members_fields
,
174 * Get Faker data for one member
178 protected function dataAdherentOne(): array
180 $bdate = new \
DateTime(date('Y') . '-12-26');
181 //member is expected to be 82 years old
183 $now = new \
DateTime();
184 if ($now <= $bdate) {
187 $bdate->sub(new \
DateInterval('P' . $years . 'Y'));
189 'nom_adh' => 'Durand',
190 'prenom_adh' => 'René',
191 'ville_adh' => 'Martel',
192 'cp_adh' => '39 069',
193 'adresse_adh' => '66, boulevard De Oliveira',
194 'email_adh' => 'meunier.josephine' . $this->seed
. '@ledoux.com',
195 'login_adh' => 'arthur.hamon' . $this->seed
,
196 'mdp_adh' => 'J^B-()f',
197 'mdp_adh2' => 'J^B-()f',
198 'bool_admin_adh' => false,
199 'bool_exempt_adh' => false,
200 'bool_display_info' => true,
202 'prof_adh' => 'Chef de fabrication',
204 'ddn_adh' => $bdate->format('Y-m-d'),
205 'lieu_naissance' => 'Gonzalez-sur-Meunier',
206 'pseudo_adh' => 'ubertrand',
207 'pays_adh' => 'Antarctique',
208 'tel_adh' => '0439153432',
209 'activite_adh' => true,
211 'date_crea_adh' => '2020-06-10',
212 'pref_lang' => 'en_US',
213 'fingerprint' => 'FAKER' . $this->seed
,
219 * Get Faker data for second member
223 protected function dataAdherentTwo(): array
225 $bdate = new \
DateTime(date('Y') . '-09-13');
226 //member is expected to be 28 years old
228 $now = new \
DateTime();
229 if ($now <= $bdate) {
232 $bdate->sub(new \
DateInterval('P' . $years . 'Y'));
235 'nom_adh' => 'Hoarau',
236 'prenom_adh' => 'Lucas',
237 'ville_adh' => 'Reynaudnec',
239 'adresse_adh' => '2, boulevard Legros',
240 'email_adh' => 'phoarau' . $this->seed
. '@tele2.fr',
241 'login_adh' => 'nathalie51' . $this->seed
,
242 'mdp_adh' => 'T.u!IbKOi|06',
243 'mdp_adh2' => 'T.u!IbKOi|06',
244 'bool_admin_adh' => false,
245 'bool_exempt_adh' => false,
246 'bool_display_info' => false,
248 'prof_adh' => 'Extraction',
250 'ddn_adh' => $bdate->format('Y-m-d'),
251 'lieu_naissance' => 'Fischer',
252 'pseudo_adh' => 'vallet.camille',
254 'tel_adh' => '05 59 53 59 43',
255 'activite_adh' => true,
257 'date_crea_adh' => '2019-05-20',
259 'fingerprint' => 'FAKER' . $this->seed
,
260 'societe_adh' => 'Philippe',
261 'is_company' => true,
267 * Create member from data
269 * @param array $data Data to use to create member
271 * @return \Galette\Entity\Adherent
273 public function createMember(array $data)
275 $this->adh
= new \Galette\Entity\
Adherent($this->zdb
);
276 $this->adh
->setDependencies(
278 $this->members_fields
,
282 $check = $this->adh
->check($data, [], []);
283 if (is_array($check)) {
286 $this->assertTrue($check);
288 $store = $this->adh
->store();
289 $this->assertTrue($store);
295 * Check members expecteds
297 * @param \Galette\Entity\Adherent $adh Member instance, if any
298 * @param array $new_expecteds Changes on expected values
302 protected function checkMemberOneExpected($adh = null, $new_expecteds = [])
309 'nom_adh' => 'Durand',
310 'prenom_adh' => 'René',
311 'ville_adh' => 'Martel',
312 'adresse_adh' => '66, boulevard De Oliveira',
313 'email_adh' => 'meunier.josephine' . $this->seed
. '@ledoux.com',
314 'login_adh' => 'arthur.hamon' . $this->seed
,
315 'mdp_adh' => 'J^B-()f',
316 'bool_admin_adh' => false,
317 'bool_exempt_adh' => false,
318 'bool_display_info' => true,
320 'prof_adh' => 'Chef de fabrication',
322 'ddn_adh' => 'NOT USED',
323 'lieu_naissance' => 'Gonzalez-sur-Meunier',
324 'pseudo_adh' => 'ubertrand',
325 'cp_adh' => '39 069',
326 'pays_adh' => 'Antarctique',
327 'tel_adh' => '0439153432',
328 'activite_adh' => true,
330 'pref_lang' => 'en_US',
331 'fingerprint' => 'FAKER95842354',
334 $expecteds = array_merge($expecteds, $new_expecteds);
336 foreach ($expecteds as $key => $value) {
337 $property = $this->members_fields
[$key]['propname'];
339 case 'bool_admin_adh':
340 $this->assertSame($value, $adh->isAdmin());
342 case 'bool_exempt_adh':
343 $this->assertSame($value, $adh->isDueFree());
345 case 'bool_display_info':
346 $this->assertSame($value, $adh->appearsInMembersList());
349 $this->assertSame($value, $adh->isActive());
352 $pw_checked = password_verify($value, $adh->password
);
353 $this->assertTrue($pw_checked);
356 //rely on age, not on birthdate
357 $this->assertNotNull($adh->$property);
358 $this->assertSame(' (82 years old)', $adh->getAge());
364 "$property expected {$value} got {$adh->$property}"
371 $d = \DateTime
::createFromFormat('Y-m-d', $expecteds['ddn_adh']);
373 $this->assertFalse($adh->hasChildren());
374 $this->assertFalse($adh->hasParent());
375 $this->assertFalse($adh->hasPicture());
377 $this->assertSame('No', $adh->sadmin
);
378 $this->assertSame('No', $adh->sdue_free
);
379 $this->assertSame('Yes', $adh->sappears_in_list
);
380 $this->assertSame('No', $adh->sstaff
);
381 $this->assertSame('Active', $adh->sactive
);
382 $this->assertNull($adh->stitle
);
383 $this->assertSame('Non-member', $adh->sstatus
);
384 $this->assertSame('DURAND René', $adh->sfullname
);
385 $this->assertSame('66, boulevard De Oliveira', $adh->saddress
);
386 $this->assertSame('DURAND René', $adh->sname
);
388 $this->assertSame($expecteds['adresse_adh'], $adh->getAddress());
389 $this->assertSame($expecteds['cp_adh'], $adh->getZipcode());
390 $this->assertSame($expecteds['ville_adh'], $adh->getTown());
391 $this->assertSame($expecteds['pays_adh'], $adh->getCountry());
393 $this->assertSame('DURAND René', $adh::getSName($this->zdb
, $adh->id
));
394 $this->assertSame('active-account cotis-never', $adh->getRowClass());
398 * Check members expecteds
400 * @param \Galette\Entity\Adherent $adh Member instance, if any
401 * @param array $new_expecteds Changes on expected values
405 protected function checkMemberTwoExpected($adh = null, $new_expecteds = [])
412 'nom_adh' => 'Hoarau',
413 'prenom_adh' => 'Lucas',
414 'ville_adh' => 'Reynaudnec',
416 'adresse_adh' => '2, boulevard Legros',
417 'email_adh' => 'phoarau' . $this->seed
. '@tele2.fr',
418 'login_adh' => 'nathalie51' . $this->seed
,
419 'mdp_adh' => 'T.u!IbKOi|06',
420 'bool_admin_adh' => false,
421 'bool_exempt_adh' => false,
422 'bool_display_info' => false,
424 'prof_adh' => 'Extraction',
426 'ddn_adh' => 'NOT USED',
427 'lieu_naissance' => 'Fischer',
428 'pseudo_adh' => 'vallet.camille',
430 'tel_adh' => '05 59 53 59 43',
431 'activite_adh' => true,
434 'fingerprint' => 'FAKER' . $this->seed
,
435 'societe_adh' => 'Philippe'
437 $expecteds = array_merge($expecteds, $new_expecteds);
439 foreach ($expecteds as $key => $value) {
440 $property = $this->members_fields
[$key]['propname'];
442 case 'bool_admin_adh':
443 $this->assertSame($value, $adh->isAdmin());
445 case 'bool_exempt_adh':
446 $this->assertSame($value, $adh->isDueFree());
448 case 'bool_display_info':
449 $this->assertSame($value, $adh->appearsInMembersList());
452 $this->assertSame($value, $adh->isActive());
455 $pw_checked = password_verify($value, $adh->password
);
456 $this->assertTrue($pw_checked);
459 //rely on age, not on birthdate
460 $this->assertNotNull($adh->$property);
461 $this->assertSame(' (28 years old)', $adh->getAge());
467 "$property expected {$value} got {$adh->$property}"
473 $d = \DateTime
::createFromFormat('Y-m-d', $expecteds['ddn_adh']);
475 $this->assertFalse($adh->hasChildren());
476 $this->assertFalse($adh->hasParent());
477 $this->assertFalse($adh->hasPicture());
479 $this->assertSame('No', $adh->sadmin
);
480 $this->assertSame('No', $adh->sdue_free
);
481 $this->assertSame('No', $adh->sappears_in_list
);
482 $this->assertSame('No', $adh->sstaff
);
483 $this->assertSame('Active', $adh->sactive
);
484 $this->assertNull($adh->stitle
);
485 $this->assertSame('Non-member', $adh->sstatus
);
486 $this->assertSame('HOARAU Lucas', $adh->sfullname
);
487 $this->assertSame('2, boulevard Legros', $adh->saddress
);
488 $this->assertSame('HOARAU Lucas', $adh->sname
);
490 $this->assertSame($expecteds['adresse_adh'], $adh->getAddress());
491 $this->assertSame($expecteds['cp_adh'], $adh->getZipcode());
492 $this->assertSame($expecteds['ville_adh'], $adh->getTown());
493 $this->assertSame($expecteds['pays_adh'], $adh->getCountry());
495 $this->assertSame('HOARAU Lucas', $adh::getSName($this->zdb
, $adh->id
));
496 $this->assertSame('active-account cotis-never', $adh->getRowClass());
500 * Look in database if test member already exists
502 * @return false|\Laminas\Db\ResultSet\ResultSet
504 protected function adhOneExists()
506 $mdata = $this->dataAdherentOne();
507 $select = $this->zdb
->select(\Galette\Entity\Adherent
::TABLE
, 'a');
510 'a.fingerprint' => 'FAKER' . $this->seed
,
511 'a.login_adh' => $mdata['login_adh']
515 $results = $this->zdb
->execute($select);
516 if ($results->count() === 0) {
524 * Look in database if test member already exists
526 * @return false|\Laminas\Db\ResultSet\ResultSet
528 protected function adhTwoExists()
530 $mdata = $this->dataAdherentTwo();
531 $select = $this->zdb
->select(\Galette\Entity\Adherent
::TABLE
, 'a');
534 'a.fingerprint' => 'FAKER' . $this->seed
,
535 'a.login_adh' => $mdata['login_adh']
539 $results = $this->zdb
->execute($select);
540 if ($results->count() === 0) {
550 * @return \Galette\Entity\Adherent
552 protected function getMemberOne()
554 $rs = $this->adhOneExists();
556 $this->createMember($this->dataAdherentOne());
558 $this->loadAdherent($rs->current()->id_adh
);
566 * @return \Galette\Entity\Adherent
568 protected function getMemberTwo()
570 $rs = $this->adhTwoExists();
572 $this->createMember($this->dataAdherentTwo());
574 $this->loadAdherent($rs->current()->id_adh
);
580 * Create contribution from data
582 * @param array $data Data to use to create contribution
583 * @param ?\Galette\Entity\Contribution $contrib Contribution instance, if any
585 * @return \Galette\Entity\Contribution
587 public function createContrib(array $data, \Galette\Entity\Contribution
$contrib = null)
589 if ($contrib === null) {
590 $this->contrib
= new \Galette\Entity\
Contribution($this->zdb
, $this->login
);
591 $contrib = $this->contrib
;
594 $check = $contrib->check($data, [], []);
595 if (is_array($check)) {
598 $this->assertTrue($check);
600 $store = $contrib->store();
601 $this->assertTrue($store);
607 * Create test contribution in database
611 protected function createContribution()
613 $now = new \
DateTime(); // 2020-11-07
614 $begin_date = clone $now;
615 $begin_date->sub(new \
DateInterval('P5M')); // 2020-06-08
616 $begin_date->add(new \
DateInterval('P3D')); // 2020-06-11
618 $due_date = clone $begin_date;
619 $due_date->sub(new \
DateInterval('P1D'));
620 $due_date->add(new \
DateInterval('P1Y'));
623 'id_adh' => $this->adh
->id
,
624 'id_type_cotis' => 1, //annual fee
625 'montant_cotis' => 92,
626 'type_paiement_cotis' => 3,
627 'info_cotis' => 'FAKER' . $this->seed
,
628 'date_enreg' => $begin_date->format('Y-m-d'),
629 'date_debut_cotis' => $begin_date->format('Y-m-d'),
630 'date_fin_cotis' => $due_date->format('Y-m-d'),
632 $this->createContrib($data);
633 $this->checkContribExpected();
637 * Check contributions expected
639 * @param \Galette\Entity\Contribution $contrib Contribution instance, if any
640 * @param array $new_expecteds Changes on expected values
644 protected function checkContribExpected($contrib = null, $new_expecteds = [])
646 if ($contrib === null) {
647 $contrib = $this->contrib
;
650 $begin_date = $contrib->raw_begin_date
;
652 $due_date = clone $begin_date;
653 $due_date->sub(new \
DateInterval('P1D'));
654 $due_date->add(new \
DateInterval('P1Y'));
656 $this->assertInstanceOf('DateTime', $contrib->raw_date
);
657 $this->assertInstanceOf('DateTime', $contrib->raw_begin_date
);
658 $this->assertInstanceOf('DateTime', $contrib->raw_end_date
);
661 'id_adh' => "{$this->adh->id}",
662 'id_type_cotis' => 1, //annual fee
663 'montant_cotis' => '92',
664 'type_paiement_cotis' => '3',
665 'info_cotis' => 'FAKER' . $this->seed
,
666 'date_fin_cotis' => $due_date->format('Y-m-d'),
668 $expecteds = array_merge($expecteds, $new_expecteds);
670 $this->assertSame($expecteds['date_fin_cotis'], $contrib->raw_end_date
->format('Y-m-d'));
672 foreach ($expecteds as $key => $value) {
673 $property = $this->contrib
->fields
[$key]['propname'];
675 case \Galette\Entity\ContributionsTypes
::PK
:
676 $ct = $this->contrib
->type
;
677 if ($ct instanceof \Galette\Entity\ContributionsTypes
) {
678 $this->assertSame($value, (int)$ct->id
);
680 $this->assertSame($value, $ct);
684 $this->assertEquals($contrib->$property, $value, $property);
689 //load member from db
690 $this->adh
= new \Galette\Entity\
Adherent($this->zdb
, $this->adh
->id
);
691 //member is now up-to-date
692 $this->assertSame('active-account cotis-ok', $this->adh
->getRowClass());
693 $this->assertSame($this->contrib
->end_date
, $this->adh
->due_date
);
694 $this->assertTrue($this->adh
->isUp2Date());
695 $this->assertTrue($contrib->isFee());
696 $this->assertSame('Membership', $contrib->getTypeLabel());
697 $this->assertSame('membership', $contrib->getRawType());
699 $this->contrib
->getRequired(),
701 'id_type_cotis' => 1,
704 'date_debut_cotis' => 1,
705 'date_fin_cotis' => 1,
712 * Initialize default status in database
716 protected function initStatus(): void
718 $status = new \Galette\Entity\
Status($this->zdb
);
719 if (count($status->getList()) === 0) {
720 //status are not yet instantiated.
721 $res = $status->installInit();
722 $this->assertTrue($res);
727 * Initialize default contributions types in database
731 protected function initContributionsTypes(): void
733 $ct = new \Galette\Entity\
ContributionsTypes($this->zdb
);
734 if (count($ct->getCompleteList()) === 0) {
735 //status are not yet instanciated.
736 $res = $ct->installInit();
737 $this->assertTrue($res);
742 * Initialize default titles in database
746 protected function initTitles(): void
748 $titles = new \Galette\Repository\
Titles($this->zdb
);
749 if (count($titles->getList($this->zdb
)) === 0) {
750 $res = $titles->installInit();
751 $this->assertTrue($res);
756 * Initialize default PDF models in database
760 protected function initModels(): void
762 $models = new \Galette\Repository\
PdfModels($this->zdb
, $this->preferences
, $this->login
);
763 $res = $models->installInit(false);
764 $this->assertTrue($res);
772 protected function cleanHistory(): void
774 $this->zdb
->db
->query(
775 'TRUNCATE TABLE ' . PREFIX_DB
. \Galette\Core\History
::TABLE
,
776 \Laminas\Db\Adapter\Adapter
::QUERY_MODE_EXECUTE
781 * Log-in as super administrator
785 protected function logSuperAdmin(): void
787 $this->login
->logAdmin('superadmin', $this->preferences
);
788 $this->assertTrue($this->login
->isLogged());
789 $this->assertTrue($this->login
->isSuperAdmin());