Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
76 / 76
100.00% covered (success)
100.00%
1 / 1
CRAP
n/a
0 / 0
givenUserExists
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3// ╔════════════════════════════════════════════════════════════╗
4// ║ MIT Licence (#Expat) - https://opensource.org/licenses/MIT ║
5// ║ Copyright 2026 Frederic Poeydomenge <dyno@phexium.com>     ║
6// ╚════════════════════════════════════════════════════════════╝
7
8declare(strict_types=1);
9
10use AppDemo\User\Domain\Email;
11use AppDemo\User\Domain\User;
12use AppDemo\User\Domain\UserGroup;
13use Phexium\Domain\Id\TimestampId;
14use Phexium\Domain\Specification\AlwaysFalseSpecification;
15use Phexium\Domain\Specification\AlwaysTrueSpecification;
16use Tests\AppDemo\Fixture\UserMother;
17
18if (!function_exists('givenUserExists')) {
19    function givenUserExists(object $repository, int $id, string $email): void
20    {
21        $repository->save(
22            UserMother::create(id: $id, email: $email)
23        );
24    }
25}
26
27describe('Persistence', function (): void {
28    it('saves and retrieves a user', function (): void {
29        $userId = 12345;
30        givenUserExists($this->repository, $userId, 'john.doe@example.com');
31
32        $foundUser = $this->repository->getById(new TimestampId($userId));
33
34        expect($foundUser)->not->toBeNull()
35            ->and($foundUser->getId()->getValue())->toBe($userId)
36        ;
37    });
38
39    it('throws exception for non-existing id via getById', function (): void {
40        expect(fn () => $this->repository->getById(new TimestampId(99999)))->toThrow(InvalidArgumentException::class);
41    });
42
43    it('checks for user existence', function (): void {
44        givenUserExists($this->repository, 12345, 'john.doe@example.com');
45
46        expect($this->repository->exists(new TimestampId(12345)))->toBeTrue();
47        expect($this->repository->exists(new TimestampId(99999)))->toBeFalse();
48    });
49});
50
51describe('Querying', function (): void {
52    it('returns all users via findAll', function (): void {
53        givenUserExists($this->repository, 1, 'john.doe@example.com');
54        givenUserExists($this->repository, 2, 'jane.doe@example.com');
55
56        expect($this->repository->findAll())->toHaveCount(2);
57    });
58
59    it('returns matching users via findBy', function (): void {
60        givenUserExists($this->repository, 1, 'john.doe@example.com');
61        givenUserExists($this->repository, 2, 'jane.doe@example.com');
62
63        expect($this->repository->findBy(new AlwaysTrueSpecification()))->toHaveCount(2);
64        expect($this->repository->findBy(new AlwaysFalseSpecification()))->toHaveCount(0);
65    });
66
67    it('returns user or null via findById', function (): void {
68        givenUserExists($this->repository, 12345, 'john.doe@example.com');
69
70        expect($this->repository->findById(new TimestampId(12345)))->toBeInstanceOf(User::class);
71        expect($this->repository->findById(new TimestampId(99999)))->toBeNull();
72    });
73
74    it('returns first matching user or null via findOneBy', function (): void {
75        givenUserExists($this->repository, 12345, 'john.doe@example.com');
76
77        expect($this->repository->findOneBy(new AlwaysTrueSpecification()))->toBeInstanceOf(User::class);
78        expect($this->repository->findOneBy(new AlwaysFalseSpecification()))->toBeNull();
79    });
80
81    it('finds a user by email', function (): void {
82        $email = 'john.doe@example.com';
83        givenUserExists($this->repository, 12345, $email);
84
85        $user = $this->repository->findByEmail(Email::fromString($email));
86
87        expect($user)->not->toBeNull()
88            ->and($user->getId()->getValue())->toBe(12345)
89            ->and($user->getEmail()->getValue())->toBe($email)
90            ->and($user->getGroup())->toBe(UserGroup::User)
91        ;
92    });
93
94    it('returns null for non-existing email via findByEmail', function (): void {
95        $user = $this->repository->findByEmail(Email::fromString('not.found@example.com'));
96
97        expect($user)->toBeNull();
98    });
99});
100
101describe('Deletion', function (): void {
102    it('deletes a user and returns affected rows', function (): void {
103        givenUserExists($this->repository, 12345, 'john.doe@example.com');
104        $user = $this->repository->getById(new TimestampId(12345));
105
106        $affectedRows = $this->repository->delete($user);
107
108        expect($affectedRows)->toBe(1)
109            ->and($this->repository->exists(new TimestampId(12345)))->toBeFalse()
110        ;
111    });
112
113    it('deletes a user by id and returns affected rows', function (): void {
114        givenUserExists($this->repository, 12345, 'john.doe@example.com');
115
116        $affectedRows = $this->repository->deleteById(new TimestampId(12345));
117
118        expect($affectedRows)->toBe(1)
119            ->and($this->repository->exists(new TimestampId(12345)))->toBeFalse()
120        ;
121    });
122});