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 protected \Galette\Core\Db
$zdb;
34 protected array $members_fields;
35 protected array $members_fields_cats;
36 protected \Galette\Core\I18n
$i18n;
37 protected \Galette\Core\Preferences
$preferences;
38 protected \RKA\Session
$session;
39 protected \Galette\Core\Login
$login;
40 protected \Galette\Core\History
$history;
41 protected string $logger_storage = '';
43 protected \Galette\Entity\Adherent
$adh;
44 protected \Galette\Entity\Contribution
$contrib;
45 protected array $adh_ids = [];
46 protected array $contrib_ids = [];
48 protected array $flash_data;
49 protected \Slim\Flash\Messages
$flash;
50 protected \DI\Container
$container;
52 protected array $expected_mysql_warnings = [];
59 public function setUp(): void
62 $this->flash_data
= &$flash_data;
63 $this->flash
= new \Slim\Flash\
Messages($flash_data);
65 $gapp = new \Galette\Core\
SlimApp();
66 $app = $gapp->getApp();
67 $plugins = new \Galette\Core\
Plugins();
68 require GALETTE_BASE_PATH
. '/includes/dependencies.php';
69 /** @var \DI\Container $container */
70 $container = $app->getContainer();
71 $_SERVER['HTTP_HOST'] = '';
73 $container->set('flash', $this->flash
);
74 $container->set(\Slim\Flash\Messages
::class, $this->flash
);
76 $app->addRoutingMiddleware();
78 $this->container
= $container;
80 $this->zdb
= $container->get('zdb');
81 $this->i18n
= $container->get('i18n');
82 $this->login
= $container->get('login');
83 $this->preferences
= $container->get('preferences');
84 $this->history
= $container->get('history');
85 $this->members_fields
= $container->get('members_fields');
86 $this->members_fields_cats
= $container->get('members_fields_cats');
87 $this->session
= $container->get('session');
89 global $zdb, $login, $hist, $i18n, $container, $galette_log_var; // globals :(
91 $login = $this->login
;
92 $hist = $this->history
;
94 $container = $this->container
;
95 $galette_log_var = $this->logger_storage
;
97 $this->initPaymentTypes();
99 $this->initContributionsTypes();
103 $authenticate = new \Galette\Middleware\
Authenticate($container);
105 require GALETTE_ROOT
. 'includes/routes/main.routes.php';
106 require GALETTE_ROOT
. 'includes/routes/authentication.routes.php';
107 require GALETTE_ROOT
. 'includes/routes/management.routes.php';
108 require GALETTE_ROOT
. 'includes/routes/members.routes.php';
109 require GALETTE_ROOT
. 'includes/routes/groups.routes.php';
110 require GALETTE_ROOT
. 'includes/routes/contributions.routes.php';
111 require GALETTE_ROOT
. 'includes/routes/public_pages.routes.php';
112 require GALETTE_ROOT
. 'includes/routes/ajax.routes.php';
113 require GALETTE_ROOT
. 'includes/routes/plugins.routes.php';
121 public function tearDown(): void
123 if (TYPE_DB
=== 'mysql') {
124 $this->assertSame($this->expected_mysql_warnings
, $this->zdb
->getWarnings());
126 $this->cleanHistory();
130 * Loads member from a resultset
132 * @param integer $id Id
136 protected function loadAdherent(int $id): void
138 $this->adh
= new \Galette\Entity\
Adherent($this->zdb
, (int)$id);
139 $this->adh
->setDependencies(
141 $this->members_fields
,
147 * Get Faker data for one member
151 protected function dataAdherentOne(): array
153 $bdate = new \
DateTime(date('Y') . '-12-26');
154 //member is expected to be 82 years old
156 $now = new \
DateTime();
157 if ($now <= $bdate) {
160 $bdate->sub(new \
DateInterval('P' . $years . 'Y'));
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' . $this->seed
. '@ledoux.com',
168 'login_adh' => 'arthur.hamon' . $this->seed
,
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,
175 'prof_adh' => 'Chef de fabrication',
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 'activite_adh' => true,
184 'date_crea_adh' => '2020-06-10',
185 'pref_lang' => 'en_US',
186 'fingerprint' => 'FAKER' . $this->seed
,
192 * Get Faker data for second member
196 protected function dataAdherentTwo(): array
198 $bdate = new \
DateTime(date('Y') . '-09-13');
199 //member is expected to be 28 years old
201 $now = new \
DateTime();
202 if ($now <= $bdate) {
205 $bdate->sub(new \
DateInterval('P' . $years . 'Y'));
208 'nom_adh' => 'Hoarau',
209 'prenom_adh' => 'Lucas',
210 'ville_adh' => 'Reynaudnec',
212 'adresse_adh' => '2, boulevard Legros',
213 'email_adh' => 'phoarau' . $this->seed
. '@tele2.fr',
214 'login_adh' => 'nathalie51' . $this->seed
,
215 'mdp_adh' => 'T.u!IbKOi|06',
216 'mdp_adh2' => 'T.u!IbKOi|06',
217 'bool_admin_adh' => false,
218 'bool_exempt_adh' => false,
219 'bool_display_info' => false,
221 'prof_adh' => 'Extraction',
223 'ddn_adh' => $bdate->format('Y-m-d'),
224 'lieu_naissance' => 'Fischer',
225 'pseudo_adh' => 'vallet.camille',
227 'tel_adh' => '05 59 53 59 43',
228 'activite_adh' => true,
230 'date_crea_adh' => '2019-05-20',
232 'fingerprint' => 'FAKER' . $this->seed
,
233 'societe_adh' => 'Philippe',
234 'is_company' => true,
240 * Create member from data
242 * @param array $data Data to use to create member
244 * @return \Galette\Entity\Adherent
246 public function createMember(array $data): \Galette\Entity\Adherent
248 $this->adh
= new \Galette\Entity\
Adherent($this->zdb
);
249 $this->adh
->setDependencies(
251 $this->members_fields
,
255 $check = $this->adh
->check($data, [], []);
256 if (is_array($check)) {
259 $this->assertTrue($check);
261 $store = $this->adh
->store();
262 $this->assertTrue($store);
268 * Check members expecteds
270 * @param \Galette\Entity\Adherent $adh Member instance, if any
271 * @param array $new_expecteds Changes on expected values
275 protected function checkMemberOneExpected(\Galette\Entity\Adherent
$adh = null, array $new_expecteds = []): void
282 'nom_adh' => 'Durand',
283 'prenom_adh' => 'René',
284 'ville_adh' => 'Martel',
285 'adresse_adh' => '66, boulevard De Oliveira',
286 'email_adh' => 'meunier.josephine' . $this->seed
. '@ledoux.com',
287 'login_adh' => 'arthur.hamon' . $this->seed
,
288 'mdp_adh' => 'J^B-()f',
289 'bool_admin_adh' => false,
290 'bool_exempt_adh' => false,
291 'bool_display_info' => true,
293 'prof_adh' => 'Chef de fabrication',
295 'ddn_adh' => 'NOT USED',
296 'lieu_naissance' => 'Gonzalez-sur-Meunier',
297 'pseudo_adh' => 'ubertrand',
298 'cp_adh' => '39 069',
299 'pays_adh' => 'Antarctique',
300 'tel_adh' => '0439153432',
301 'activite_adh' => true,
303 'pref_lang' => 'en_US',
304 'fingerprint' => 'FAKER95842354',
307 $expecteds = array_merge($expecteds, $new_expecteds);
309 foreach ($expecteds as $key => $value) {
310 $property = $this->members_fields
[$key]['propname'];
312 case 'bool_admin_adh':
313 $this->assertSame($value, $adh->isAdmin());
315 case 'bool_exempt_adh':
316 $this->assertSame($value, $adh->isDueFree());
318 case 'bool_display_info':
319 $this->assertSame($value, $adh->appearsInMembersList());
322 $this->assertSame($value, $adh->isActive());
325 $pw_checked = password_verify($value, $adh->password
);
326 $this->assertTrue($pw_checked);
329 //rely on age, not on birthdate
330 $this->assertNotNull($adh->$property);
331 $this->assertSame(' (82 years old)', $adh->getAge());
337 "$property expected {$value} got {$adh->$property}"
344 $d = \DateTime
::createFromFormat('Y-m-d', $expecteds['ddn_adh']);
346 $this->assertFalse($adh->hasChildren());
347 $this->assertFalse($adh->hasParent());
348 $this->assertFalse($adh->hasPicture());
350 $this->assertSame('No', $adh->sadmin
);
351 $this->assertSame('No', $adh->sdue_free
);
352 $this->assertSame('Yes', $adh->sappears_in_list
);
353 $this->assertSame('No', $adh->sstaff
);
354 $this->assertSame('Active', $adh->sactive
);
355 $this->assertNull($adh->stitle
);
356 $this->assertSame('Non-member', $adh->sstatus
);
357 $this->assertSame('DURAND René', $adh->sfullname
);
358 $this->assertSame('66, boulevard De Oliveira', $adh->saddress
);
359 $this->assertSame('DURAND René', $adh->sname
);
361 $this->assertSame($expecteds['adresse_adh'], $adh->getAddress());
362 $this->assertSame($expecteds['cp_adh'], $adh->getZipcode());
363 $this->assertSame($expecteds['ville_adh'], $adh->getTown());
364 $this->assertSame($expecteds['pays_adh'], $adh->getCountry());
366 $this->assertSame('DURAND René', $adh::getSName($this->zdb
, $adh->id
));
367 $this->assertSame('active-account cotis-never', $adh->getRowClass());
371 * Check members expecteds
373 * @param \Galette\Entity\Adherent $adh Member instance, if any
374 * @param array $new_expecteds Changes on expected values
378 protected function checkMemberTwoExpected(\Galette\Entity\Adherent
$adh = null, array $new_expecteds = []): void
385 'nom_adh' => 'Hoarau',
386 'prenom_adh' => 'Lucas',
387 'ville_adh' => 'Reynaudnec',
389 'adresse_adh' => '2, boulevard Legros',
390 'email_adh' => 'phoarau' . $this->seed
. '@tele2.fr',
391 'login_adh' => 'nathalie51' . $this->seed
,
392 'mdp_adh' => 'T.u!IbKOi|06',
393 'bool_admin_adh' => false,
394 'bool_exempt_adh' => false,
395 'bool_display_info' => false,
397 'prof_adh' => 'Extraction',
399 'ddn_adh' => 'NOT USED',
400 'lieu_naissance' => 'Fischer',
401 'pseudo_adh' => 'vallet.camille',
403 'tel_adh' => '05 59 53 59 43',
404 'activite_adh' => true,
407 'fingerprint' => 'FAKER' . $this->seed
,
408 'societe_adh' => 'Philippe'
410 $expecteds = array_merge($expecteds, $new_expecteds);
412 foreach ($expecteds as $key => $value) {
413 $property = $this->members_fields
[$key]['propname'];
415 case 'bool_admin_adh':
416 $this->assertSame($value, $adh->isAdmin());
418 case 'bool_exempt_adh':
419 $this->assertSame($value, $adh->isDueFree());
421 case 'bool_display_info':
422 $this->assertSame($value, $adh->appearsInMembersList());
425 $this->assertSame($value, $adh->isActive());
428 $pw_checked = password_verify($value, $adh->password
);
429 $this->assertTrue($pw_checked);
432 //rely on age, not on birthdate
433 $this->assertNotNull($adh->$property);
434 $this->assertSame(' (28 years old)', $adh->getAge());
440 "$property expected {$value} got {$adh->$property}"
446 $d = \DateTime
::createFromFormat('Y-m-d', $expecteds['ddn_adh']);
448 $this->assertFalse($adh->hasChildren());
449 $this->assertFalse($adh->hasParent());
450 $this->assertFalse($adh->hasPicture());
452 $this->assertSame('No', $adh->sadmin
);
453 $this->assertSame('No', $adh->sdue_free
);
454 $this->assertSame('No', $adh->sappears_in_list
);
455 $this->assertSame('No', $adh->sstaff
);
456 $this->assertSame('Active', $adh->sactive
);
457 $this->assertNull($adh->stitle
);
458 $this->assertSame('Non-member', $adh->sstatus
);
459 $this->assertSame('HOARAU Lucas', $adh->sfullname
);
460 $this->assertSame('2, boulevard Legros', $adh->saddress
);
461 $this->assertSame('HOARAU Lucas', $adh->sname
);
463 $this->assertSame($expecteds['adresse_adh'], $adh->getAddress());
464 $this->assertSame($expecteds['cp_adh'], $adh->getZipcode());
465 $this->assertSame($expecteds['ville_adh'], $adh->getTown());
466 $this->assertSame($expecteds['pays_adh'], $adh->getCountry());
468 $this->assertSame('HOARAU Lucas', $adh::getSName($this->zdb
, $adh->id
));
469 $this->assertSame('active-account cotis-never', $adh->getRowClass());
473 * Look in database if test member already exists
475 * @return false|\Laminas\Db\ResultSet\ResultSet
477 protected function adhOneExists(): false|\Laminas\Db\ResultSet\ResultSet
479 $mdata = $this->dataAdherentOne();
480 $select = $this->zdb
->select(\Galette\Entity\Adherent
::TABLE
, 'a');
483 'a.fingerprint' => 'FAKER' . $this->seed
,
484 'a.login_adh' => $mdata['login_adh']
488 $results = $this->zdb
->execute($select);
489 if ($results->count() === 0) {
497 * Look in database if test member already exists
499 * @return false|\Laminas\Db\ResultSet\ResultSet
501 protected function adhTwoExists(): false|\Laminas\Db\ResultSet\ResultSet
503 $mdata = $this->dataAdherentTwo();
504 $select = $this->zdb
->select(\Galette\Entity\Adherent
::TABLE
, 'a');
507 'a.fingerprint' => 'FAKER' . $this->seed
,
508 'a.login_adh' => $mdata['login_adh']
512 $results = $this->zdb
->execute($select);
513 if ($results->count() === 0) {
523 * @return \Galette\Entity\Adherent
525 protected function getMemberOne(): \Galette\Entity\Adherent
527 $rs = $this->adhOneExists();
529 $this->createMember($this->dataAdherentOne());
531 $this->loadAdherent($rs->current()->id_adh
);
539 * @return \Galette\Entity\Adherent
541 protected function getMemberTwo(): \Galette\Entity\Adherent
543 $rs = $this->adhTwoExists();
545 $this->createMember($this->dataAdherentTwo());
547 $this->loadAdherent($rs->current()->id_adh
);
553 * Create contribution from data
555 * @param array<string,mixed> $data Data to use to create contribution
556 * @param ?\Galette\Entity\Contribution $contrib Contribution instance, if any
558 * @return \Galette\Entity\Contribution
560 public function createContrib(array $data, \Galette\Entity\Contribution
$contrib = null): \Galette\Entity\Contribution
562 if ($contrib === null) {
563 $this->contrib
= new \Galette\Entity\
Contribution($this->zdb
, $this->login
);
564 $contrib = $this->contrib
;
567 $check = $contrib->check($data, [], []);
568 if (is_array($check)) {
571 $this->assertTrue($check);
573 $store = $contrib->store();
574 $this->assertTrue($store);
580 * Create test contribution in database
584 protected function createContribution(): void
586 $now = new \
DateTime(); // 2020-11-07
587 $begin_date = clone $now;
588 $begin_date->sub(new \
DateInterval('P5M')); // 2020-06-08
589 $begin_date->add(new \
DateInterval('P3D')); // 2020-06-11
591 $due_date = clone $begin_date;
592 $due_date->sub(new \
DateInterval('P1D'));
593 $due_date->add(new \
DateInterval('P1Y'));
596 'id_adh' => $this->adh
->id
,
597 'id_type_cotis' => 1, //annual fee
598 'montant_cotis' => 92,
599 'type_paiement_cotis' => 3,
600 'info_cotis' => 'FAKER' . $this->seed
,
601 'date_enreg' => $begin_date->format('Y-m-d'),
602 'date_debut_cotis' => $begin_date->format('Y-m-d'),
603 'date_fin_cotis' => $due_date->format('Y-m-d'),
605 $this->createContrib($data);
606 $this->checkContribExpected();
610 * Check contributions expected
612 * @param ?\Galette\Entity\Contribution $contrib Contribution instance, if any
613 * @param array<string,mixed> $new_expecteds Changes on expected values
617 protected function checkContribExpected(\Galette\Entity\Contribution
$contrib = null, array $new_expecteds = []): void
619 if ($contrib === null) {
620 $contrib = $this->contrib
;
623 $begin_date = $contrib->raw_begin_date
;
625 $due_date = clone $begin_date;
626 $due_date->sub(new \
DateInterval('P1D'));
627 $due_date->add(new \
DateInterval('P1Y'));
629 $this->assertInstanceOf('DateTime', $contrib->raw_date
);
630 $this->assertInstanceOf('DateTime', $contrib->raw_begin_date
);
631 $this->assertInstanceOf('DateTime', $contrib->raw_end_date
);
634 'id_adh' => "{$this->adh->id}",
635 'id_type_cotis' => 1, //annual fee
636 'montant_cotis' => '92',
637 'type_paiement_cotis' => '3',
638 'info_cotis' => 'FAKER' . $this->seed
,
639 'date_fin_cotis' => $due_date->format('Y-m-d'),
641 $expecteds = array_merge($expecteds, $new_expecteds);
643 $this->assertSame($expecteds['date_fin_cotis'], $contrib->raw_end_date
->format('Y-m-d'));
645 foreach ($expecteds as $key => $value) {
646 $property = $this->contrib
->fields
[$key]['propname'];
648 case \Galette\Entity\ContributionsTypes
::PK
:
649 $ct = $this->contrib
->type
;
650 if ($ct instanceof \Galette\Entity\ContributionsTypes
) {
651 $this->assertSame($value, (int)$ct->id
);
653 $this->assertSame($value, $ct);
657 $this->assertEquals($contrib->$property, $value, $property);
662 //load member from db
663 $this->adh
= new \Galette\Entity\
Adherent($this->zdb
, $this->adh
->id
);
664 //member is now up-to-date
665 $this->assertSame('active-account cotis-ok', $this->adh
->getRowClass());
666 $this->assertSame($this->contrib
->end_date
, $this->adh
->due_date
);
667 $this->assertTrue($this->adh
->isUp2Date());
668 $this->assertTrue($contrib->isFee());
669 $this->assertSame('Membership', $contrib->getTypeLabel());
670 $this->assertSame('membership', $contrib->getRawType());
672 $this->contrib
->getRequired(),
674 'id_type_cotis' => 1,
677 'date_debut_cotis' => 1,
678 'date_fin_cotis' => 1,
685 * Initialize default status in database
689 protected function initStatus(): void
691 $status = new \Galette\Entity\
Status($this->zdb
);
692 if (count($status->getList()) === 0) {
693 //status are not yet instantiated.
694 $res = $status->installInit();
695 $this->assertTrue($res);
700 * Initialize default contributions types in database
704 protected function initContributionsTypes(): void
706 $ct = new \Galette\Entity\
ContributionsTypes($this->zdb
);
707 if (count($ct->getCompleteList()) === 0) {
708 //contributions types are not yet instanciated.
709 $res = $ct->installInit();
710 $this->assertTrue($res);
715 * Initialize default payment types in database
719 protected function initPaymentTypes(): void
721 $types = new \Galette\Repository\
PaymentTypes($this->zdb
, $this->preferences
, $this->login
);
722 if (count($types->getList()) === 0) {
723 //payment types are not yet instanciated.
724 $res = $types->installInit();
725 $this->assertTrue($res);
730 * Initialize default titles in database
734 protected function initTitles(): void
736 $titles = new \Galette\Repository\
Titles($this->zdb
);
737 if (count($titles->getList()) === 0) {
738 $res = $titles->installInit();
739 $this->assertTrue($res);
744 * Initialize default PDF models in database
748 protected function initModels(): void
750 $models = new \Galette\Repository\
PdfModels($this->zdb
, $this->preferences
, $this->login
);
751 $res = $models->installInit(false);
752 $this->assertTrue($res);
760 protected function cleanHistory(): void
762 $this->zdb
->db
->query(
763 'TRUNCATE TABLE ' . PREFIX_DB
. \Galette\Core\History
::TABLE
,
764 \Laminas\Db\Adapter\Adapter
::QUERY_MODE_EXECUTE
769 * Log-in as super administrator
773 protected function logSuperAdmin(): void
775 $this->login
->logAdmin('superadmin', $this->preferences
);
776 $this->assertTrue($this->login
->isLogged());
777 $this->assertTrue($this->login
->isSuperAdmin());