4 * Copyright © 2003-2024 The Galette Team
6 * This file is part of Galette (https://galette.eu).
8 * Galette is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * Galette is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
24 use PHPUnit\Framework\TestCase
;
27 * Galette tests case main class
29 * @author Johan Cwiklinski <johan@x-tnd.be>
31 abstract class GaletteTestCase
extends TestCase
33 /** @var \Galette\Core\Db */
34 protected \Galette\Core\Db
$zdb;
35 protected array $members_fields;
36 protected array $members_fields_cats;
37 /** @var \Galette\Core\I18n */
38 protected \Galette\Core\I18n
$i18n;
39 /** @var \Galette\Core\Preferences */
40 protected \Galette\Core\Preferences
$preferences;
41 protected \RKA\Session
$session;
42 /** @var \Galette\Core\Login */
43 protected \Galette\Core\Login
$login;
44 /** @var \Galette\Core\History */
45 protected \Galette\Core\History
$history;
46 protected $logger_storage = '';
48 /** @var \Galette\Entity\Adherent */
49 protected \Galette\Entity\Adherent
$adh;
50 /** @var \Galette\Entity\Contribution */
51 protected \Galette\Entity\Contribution
$contrib;
52 protected array $adh_ids = [];
53 protected array $contrib_ids = [];
55 protected array $flash_data;
56 /** @var \Slim\Flash\Messages */
57 protected \Slim\Flash\Messages
$flash;
58 protected \DI\Container
$container;
60 protected array $expected_mysql_warnings = [];
67 public function setUp(): void
70 $this->flash_data
= &$flash_data;
71 $this->flash
= new \Slim\Flash\
Messages($flash_data);
73 $gapp = new \Galette\Core\
SlimApp();
74 $app = $gapp->getApp();
75 $plugins = new \Galette\Core\
Plugins();
76 require GALETTE_BASE_PATH
. '/includes/dependencies.php';
77 /** @var \DI\Container $container */
78 $container = $app->getContainer();
79 $_SERVER['HTTP_HOST'] = '';
81 $container->set('flash', $this->flash
);
82 $container->set(\Slim\Flash\Messages
::class, $this->flash
);
84 $app->addRoutingMiddleware();
86 $this->container
= $container;
88 $this->zdb
= $container->get('zdb');
89 $this->i18n
= $container->get('i18n');
90 $this->login
= $container->get('login');
91 $this->preferences
= $container->get('preferences');
92 $this->history
= $container->get('history');
93 $this->members_fields
= $container->get('members_fields');
94 $this->members_fields_cats
= $container->get('members_fields_cats');
95 $this->session
= $container->get('session');
97 global $zdb, $login, $hist, $i18n, $container, $galette_log_var; // globals :(
99 $login = $this->login
;
100 $hist = $this->history
;
102 $container = $this->container
;
103 $galette_log_var = $this->logger_storage
;
105 $this->initPaymentTypes();
107 $this->initContributionsTypes();
111 $authenticate = new \Galette\Middleware\
Authenticate($container);
113 require GALETTE_ROOT
. 'includes/routes/main.routes.php';
114 require GALETTE_ROOT
. 'includes/routes/authentication.routes.php';
115 require GALETTE_ROOT
. 'includes/routes/management.routes.php';
116 require GALETTE_ROOT
. 'includes/routes/members.routes.php';
117 require GALETTE_ROOT
. 'includes/routes/groups.routes.php';
118 require GALETTE_ROOT
. 'includes/routes/contributions.routes.php';
119 require GALETTE_ROOT
. 'includes/routes/public_pages.routes.php';
120 require GALETTE_ROOT
. 'includes/routes/ajax.routes.php';
121 require GALETTE_ROOT
. 'includes/routes/plugins.routes.php';
129 public function tearDown(): void
131 if (TYPE_DB
=== 'mysql') {
132 $this->assertSame($this->expected_mysql_warnings
, $this->zdb
->getWarnings());
134 $this->cleanHistory();
138 * Loads member from a resultset
140 * @param integer $id Id
144 protected function loadAdherent($id)
146 $this->adh
= new \Galette\Entity\
Adherent($this->zdb
, (int)$id);
147 $this->adh
->setDependencies(
149 $this->members_fields
,
155 * Get Faker data for one member
159 protected function dataAdherentOne(): array
161 $bdate = new \
DateTime(date('Y') . '-12-26');
162 //member is expected to be 82 years old
164 $now = new \
DateTime();
165 if ($now <= $bdate) {
168 $bdate->sub(new \
DateInterval('P' . $years . 'Y'));
170 'nom_adh' => 'Durand',
171 'prenom_adh' => 'René',
172 'ville_adh' => 'Martel',
173 'cp_adh' => '39 069',
174 'adresse_adh' => '66, boulevard De Oliveira',
175 'email_adh' => 'meunier.josephine' . $this->seed
. '@ledoux.com',
176 'login_adh' => 'arthur.hamon' . $this->seed
,
177 'mdp_adh' => 'J^B-()f',
178 'mdp_adh2' => 'J^B-()f',
179 'bool_admin_adh' => false,
180 'bool_exempt_adh' => false,
181 'bool_display_info' => true,
183 'prof_adh' => 'Chef de fabrication',
185 'ddn_adh' => $bdate->format('Y-m-d'),
186 'lieu_naissance' => 'Gonzalez-sur-Meunier',
187 'pseudo_adh' => 'ubertrand',
188 'pays_adh' => 'Antarctique',
189 'tel_adh' => '0439153432',
190 'activite_adh' => true,
192 'date_crea_adh' => '2020-06-10',
193 'pref_lang' => 'en_US',
194 'fingerprint' => 'FAKER' . $this->seed
,
200 * Get Faker data for second member
204 protected function dataAdherentTwo(): array
206 $bdate = new \
DateTime(date('Y') . '-09-13');
207 //member is expected to be 28 years old
209 $now = new \
DateTime();
210 if ($now <= $bdate) {
213 $bdate->sub(new \
DateInterval('P' . $years . 'Y'));
216 'nom_adh' => 'Hoarau',
217 'prenom_adh' => 'Lucas',
218 'ville_adh' => 'Reynaudnec',
220 'adresse_adh' => '2, boulevard Legros',
221 'email_adh' => 'phoarau' . $this->seed
. '@tele2.fr',
222 'login_adh' => 'nathalie51' . $this->seed
,
223 'mdp_adh' => 'T.u!IbKOi|06',
224 'mdp_adh2' => 'T.u!IbKOi|06',
225 'bool_admin_adh' => false,
226 'bool_exempt_adh' => false,
227 'bool_display_info' => false,
229 'prof_adh' => 'Extraction',
231 'ddn_adh' => $bdate->format('Y-m-d'),
232 'lieu_naissance' => 'Fischer',
233 'pseudo_adh' => 'vallet.camille',
235 'tel_adh' => '05 59 53 59 43',
236 'activite_adh' => true,
238 'date_crea_adh' => '2019-05-20',
240 'fingerprint' => 'FAKER' . $this->seed
,
241 'societe_adh' => 'Philippe',
242 'is_company' => true,
248 * Create member from data
250 * @param array $data Data to use to create member
252 * @return \Galette\Entity\Adherent
254 public function createMember(array $data)
256 $this->adh
= new \Galette\Entity\
Adherent($this->zdb
);
257 $this->adh
->setDependencies(
259 $this->members_fields
,
263 $check = $this->adh
->check($data, [], []);
264 if (is_array($check)) {
267 $this->assertTrue($check);
269 $store = $this->adh
->store();
270 $this->assertTrue($store);
276 * Check members expecteds
278 * @param \Galette\Entity\Adherent $adh Member instance, if any
279 * @param array $new_expecteds Changes on expected values
283 protected function checkMemberOneExpected($adh = null, $new_expecteds = [])
290 'nom_adh' => 'Durand',
291 'prenom_adh' => 'René',
292 'ville_adh' => 'Martel',
293 'adresse_adh' => '66, boulevard De Oliveira',
294 'email_adh' => 'meunier.josephine' . $this->seed
. '@ledoux.com',
295 'login_adh' => 'arthur.hamon' . $this->seed
,
296 'mdp_adh' => 'J^B-()f',
297 'bool_admin_adh' => false,
298 'bool_exempt_adh' => false,
299 'bool_display_info' => true,
301 'prof_adh' => 'Chef de fabrication',
303 'ddn_adh' => 'NOT USED',
304 'lieu_naissance' => 'Gonzalez-sur-Meunier',
305 'pseudo_adh' => 'ubertrand',
306 'cp_adh' => '39 069',
307 'pays_adh' => 'Antarctique',
308 'tel_adh' => '0439153432',
309 'activite_adh' => true,
311 'pref_lang' => 'en_US',
312 'fingerprint' => 'FAKER95842354',
315 $expecteds = array_merge($expecteds, $new_expecteds);
317 foreach ($expecteds as $key => $value) {
318 $property = $this->members_fields
[$key]['propname'];
320 case 'bool_admin_adh':
321 $this->assertSame($value, $adh->isAdmin());
323 case 'bool_exempt_adh':
324 $this->assertSame($value, $adh->isDueFree());
326 case 'bool_display_info':
327 $this->assertSame($value, $adh->appearsInMembersList());
330 $this->assertSame($value, $adh->isActive());
333 $pw_checked = password_verify($value, $adh->password
);
334 $this->assertTrue($pw_checked);
337 //rely on age, not on birthdate
338 $this->assertNotNull($adh->$property);
339 $this->assertSame(' (82 years old)', $adh->getAge());
345 "$property expected {$value} got {$adh->$property}"
352 $d = \DateTime
::createFromFormat('Y-m-d', $expecteds['ddn_adh']);
354 $this->assertFalse($adh->hasChildren());
355 $this->assertFalse($adh->hasParent());
356 $this->assertFalse($adh->hasPicture());
358 $this->assertSame('No', $adh->sadmin
);
359 $this->assertSame('No', $adh->sdue_free
);
360 $this->assertSame('Yes', $adh->sappears_in_list
);
361 $this->assertSame('No', $adh->sstaff
);
362 $this->assertSame('Active', $adh->sactive
);
363 $this->assertNull($adh->stitle
);
364 $this->assertSame('Non-member', $adh->sstatus
);
365 $this->assertSame('DURAND René', $adh->sfullname
);
366 $this->assertSame('66, boulevard De Oliveira', $adh->saddress
);
367 $this->assertSame('DURAND René', $adh->sname
);
369 $this->assertSame($expecteds['adresse_adh'], $adh->getAddress());
370 $this->assertSame($expecteds['cp_adh'], $adh->getZipcode());
371 $this->assertSame($expecteds['ville_adh'], $adh->getTown());
372 $this->assertSame($expecteds['pays_adh'], $adh->getCountry());
374 $this->assertSame('DURAND René', $adh::getSName($this->zdb
, $adh->id
));
375 $this->assertSame('active-account cotis-never', $adh->getRowClass());
379 * Check members expecteds
381 * @param \Galette\Entity\Adherent $adh Member instance, if any
382 * @param array $new_expecteds Changes on expected values
386 protected function checkMemberTwoExpected($adh = null, $new_expecteds = [])
393 'nom_adh' => 'Hoarau',
394 'prenom_adh' => 'Lucas',
395 'ville_adh' => 'Reynaudnec',
397 'adresse_adh' => '2, boulevard Legros',
398 'email_adh' => 'phoarau' . $this->seed
. '@tele2.fr',
399 'login_adh' => 'nathalie51' . $this->seed
,
400 'mdp_adh' => 'T.u!IbKOi|06',
401 'bool_admin_adh' => false,
402 'bool_exempt_adh' => false,
403 'bool_display_info' => false,
405 'prof_adh' => 'Extraction',
407 'ddn_adh' => 'NOT USED',
408 'lieu_naissance' => 'Fischer',
409 'pseudo_adh' => 'vallet.camille',
411 'tel_adh' => '05 59 53 59 43',
412 'activite_adh' => true,
415 'fingerprint' => 'FAKER' . $this->seed
,
416 'societe_adh' => 'Philippe'
418 $expecteds = array_merge($expecteds, $new_expecteds);
420 foreach ($expecteds as $key => $value) {
421 $property = $this->members_fields
[$key]['propname'];
423 case 'bool_admin_adh':
424 $this->assertSame($value, $adh->isAdmin());
426 case 'bool_exempt_adh':
427 $this->assertSame($value, $adh->isDueFree());
429 case 'bool_display_info':
430 $this->assertSame($value, $adh->appearsInMembersList());
433 $this->assertSame($value, $adh->isActive());
436 $pw_checked = password_verify($value, $adh->password
);
437 $this->assertTrue($pw_checked);
440 //rely on age, not on birthdate
441 $this->assertNotNull($adh->$property);
442 $this->assertSame(' (28 years old)', $adh->getAge());
448 "$property expected {$value} got {$adh->$property}"
454 $d = \DateTime
::createFromFormat('Y-m-d', $expecteds['ddn_adh']);
456 $this->assertFalse($adh->hasChildren());
457 $this->assertFalse($adh->hasParent());
458 $this->assertFalse($adh->hasPicture());
460 $this->assertSame('No', $adh->sadmin
);
461 $this->assertSame('No', $adh->sdue_free
);
462 $this->assertSame('No', $adh->sappears_in_list
);
463 $this->assertSame('No', $adh->sstaff
);
464 $this->assertSame('Active', $adh->sactive
);
465 $this->assertNull($adh->stitle
);
466 $this->assertSame('Non-member', $adh->sstatus
);
467 $this->assertSame('HOARAU Lucas', $adh->sfullname
);
468 $this->assertSame('2, boulevard Legros', $adh->saddress
);
469 $this->assertSame('HOARAU Lucas', $adh->sname
);
471 $this->assertSame($expecteds['adresse_adh'], $adh->getAddress());
472 $this->assertSame($expecteds['cp_adh'], $adh->getZipcode());
473 $this->assertSame($expecteds['ville_adh'], $adh->getTown());
474 $this->assertSame($expecteds['pays_adh'], $adh->getCountry());
476 $this->assertSame('HOARAU Lucas', $adh::getSName($this->zdb
, $adh->id
));
477 $this->assertSame('active-account cotis-never', $adh->getRowClass());
481 * Look in database if test member already exists
483 * @return false|\Laminas\Db\ResultSet\ResultSet
485 protected function adhOneExists()
487 $mdata = $this->dataAdherentOne();
488 $select = $this->zdb
->select(\Galette\Entity\Adherent
::TABLE
, 'a');
491 'a.fingerprint' => 'FAKER' . $this->seed
,
492 'a.login_adh' => $mdata['login_adh']
496 $results = $this->zdb
->execute($select);
497 if ($results->count() === 0) {
505 * Look in database if test member already exists
507 * @return false|\Laminas\Db\ResultSet\ResultSet
509 protected function adhTwoExists()
511 $mdata = $this->dataAdherentTwo();
512 $select = $this->zdb
->select(\Galette\Entity\Adherent
::TABLE
, 'a');
515 'a.fingerprint' => 'FAKER' . $this->seed
,
516 'a.login_adh' => $mdata['login_adh']
520 $results = $this->zdb
->execute($select);
521 if ($results->count() === 0) {
531 * @return \Galette\Entity\Adherent
533 protected function getMemberOne()
535 $rs = $this->adhOneExists();
537 $this->createMember($this->dataAdherentOne());
539 $this->loadAdherent($rs->current()->id_adh
);
547 * @return \Galette\Entity\Adherent
549 protected function getMemberTwo()
551 $rs = $this->adhTwoExists();
553 $this->createMember($this->dataAdherentTwo());
555 $this->loadAdherent($rs->current()->id_adh
);
561 * Create contribution from data
563 * @param array<string,mixed> $data Data to use to create contribution
564 * @param ?\Galette\Entity\Contribution $contrib Contribution instance, if any
566 * @return \Galette\Entity\Contribution
568 public function createContrib(array $data, \Galette\Entity\Contribution
$contrib = null)
570 if ($contrib === null) {
571 $this->contrib
= new \Galette\Entity\
Contribution($this->zdb
, $this->login
);
572 $contrib = $this->contrib
;
575 $check = $contrib->check($data, [], []);
576 if (is_array($check)) {
579 $this->assertTrue($check);
581 $store = $contrib->store();
582 $this->assertTrue($store);
588 * Create test contribution in database
592 protected function createContribution()
594 $now = new \
DateTime(); // 2020-11-07
595 $begin_date = clone $now;
596 $begin_date->sub(new \
DateInterval('P5M')); // 2020-06-08
597 $begin_date->add(new \
DateInterval('P3D')); // 2020-06-11
599 $due_date = clone $begin_date;
600 $due_date->sub(new \
DateInterval('P1D'));
601 $due_date->add(new \
DateInterval('P1Y'));
604 'id_adh' => $this->adh
->id
,
605 'id_type_cotis' => 1, //annual fee
606 'montant_cotis' => 92,
607 'type_paiement_cotis' => 3,
608 'info_cotis' => 'FAKER' . $this->seed
,
609 'date_enreg' => $begin_date->format('Y-m-d'),
610 'date_debut_cotis' => $begin_date->format('Y-m-d'),
611 'date_fin_cotis' => $due_date->format('Y-m-d'),
613 $this->createContrib($data);
614 $this->checkContribExpected();
618 * Check contributions expected
620 * @param \Galette\Entity\Contribution $contrib Contribution instance, if any
621 * @param array $new_expecteds Changes on expected values
625 protected function checkContribExpected($contrib = null, $new_expecteds = [])
627 if ($contrib === null) {
628 $contrib = $this->contrib
;
631 $begin_date = $contrib->raw_begin_date
;
633 $due_date = clone $begin_date;
634 $due_date->sub(new \
DateInterval('P1D'));
635 $due_date->add(new \
DateInterval('P1Y'));
637 $this->assertInstanceOf('DateTime', $contrib->raw_date
);
638 $this->assertInstanceOf('DateTime', $contrib->raw_begin_date
);
639 $this->assertInstanceOf('DateTime', $contrib->raw_end_date
);
642 'id_adh' => "{$this->adh->id}",
643 'id_type_cotis' => 1, //annual fee
644 'montant_cotis' => '92',
645 'type_paiement_cotis' => '3',
646 'info_cotis' => 'FAKER' . $this->seed
,
647 'date_fin_cotis' => $due_date->format('Y-m-d'),
649 $expecteds = array_merge($expecteds, $new_expecteds);
651 $this->assertSame($expecteds['date_fin_cotis'], $contrib->raw_end_date
->format('Y-m-d'));
653 foreach ($expecteds as $key => $value) {
654 $property = $this->contrib
->fields
[$key]['propname'];
656 case \Galette\Entity\ContributionsTypes
::PK
:
657 $ct = $this->contrib
->type
;
658 if ($ct instanceof \Galette\Entity\ContributionsTypes
) {
659 $this->assertSame($value, (int)$ct->id
);
661 $this->assertSame($value, $ct);
665 $this->assertEquals($contrib->$property, $value, $property);
670 //load member from db
671 $this->adh
= new \Galette\Entity\
Adherent($this->zdb
, $this->adh
->id
);
672 //member is now up-to-date
673 $this->assertSame('active-account cotis-ok', $this->adh
->getRowClass());
674 $this->assertSame($this->contrib
->end_date
, $this->adh
->due_date
);
675 $this->assertTrue($this->adh
->isUp2Date());
676 $this->assertTrue($contrib->isFee());
677 $this->assertSame('Membership', $contrib->getTypeLabel());
678 $this->assertSame('membership', $contrib->getRawType());
680 $this->contrib
->getRequired(),
682 'id_type_cotis' => 1,
685 'date_debut_cotis' => 1,
686 'date_fin_cotis' => 1,
693 * Initialize default status in database
697 protected function initStatus(): void
699 $status = new \Galette\Entity\
Status($this->zdb
);
700 if (count($status->getList()) === 0) {
701 //status are not yet instantiated.
702 $res = $status->installInit();
703 $this->assertTrue($res);
708 * Initialize default contributions types in database
712 protected function initContributionsTypes(): void
714 $ct = new \Galette\Entity\
ContributionsTypes($this->zdb
);
715 if (count($ct->getCompleteList()) === 0) {
716 //status are not yet instanciated.
717 $res = $ct->installInit();
718 $this->assertTrue($res);
723 * Initialize default payment types in database
727 protected function initPaymentTypes(): void
729 $types = new \Galette\Repository\
PaymentTypes($this->zdb
, $this->preferences
, $this->login
);
730 if (count($types->getList()) === 0) {
731 //payment types are not yet instanciated.
732 $res = $types->installInit(false);
733 $this->assertTrue($res);
738 * Initialize default titles in database
742 protected function initTitles(): void
744 $titles = new \Galette\Repository\
Titles($this->zdb
);
745 if (count($titles->getList($this->zdb
)) === 0) {
746 $res = $titles->installInit();
747 $this->assertTrue($res);
752 * Initialize default PDF models in database
756 protected function initModels(): void
758 $models = new \Galette\Repository\
PdfModels($this->zdb
, $this->preferences
, $this->login
);
759 $res = $models->installInit(false);
760 $this->assertTrue($res);
768 protected function cleanHistory(): void
770 $this->zdb
->db
->query(
771 'TRUNCATE TABLE ' . PREFIX_DB
. \Galette\Core\History
::TABLE
,
772 \Laminas\Db\Adapter\Adapter
::QUERY_MODE_EXECUTE
777 * Log-in as super administrator
781 protected function logSuperAdmin(): void
783 $this->login
->logAdmin('superadmin', $this->preferences
);
784 $this->assertTrue($this->login
->isLogged());
785 $this->assertTrue($this->login
->isSuperAdmin());