Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
73 / 73
100.00% covered (success)
100.00%
1 / 1
CRAP
n/a
0 / 0
givenBookExists
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\Library\Domain\Book;
11use Phexium\Domain\Id\TimestampId;
12use Phexium\Domain\Specification\AlwaysFalseSpecification;
13use Phexium\Domain\Specification\AlwaysTrueSpecification;
14use Tests\AppDemo\Fixture\BookMother;
15
16if (!function_exists('givenBookExists')) {
17    function givenBookExists(object $repository, int $id, string $isbn): void
18    {
19        $repository->save(
20            BookMother::create(id: $id, title: 'Title#'.$id, author: 'Author#'.$id, isbn: $isbn)
21        );
22    }
23}
24
25describe('Persistence', function (): void {
26    it('saves and retrieves a book', function (): void {
27        $bookId = 12345;
28        givenBookExists($this->repository, $bookId, '9780134494166');
29
30        $foundBook = $this->repository->getById(new TimestampId($bookId));
31
32        expect($foundBook)->not->toBeNull()
33            ->and($foundBook->getId()->getValue())->toBe($bookId)
34        ;
35    });
36
37    it('throws exception for non-existing id via getById', function (): void {
38        expect(fn () => $this->repository->getById(new TimestampId(99999)))->toThrow(InvalidArgumentException::class);
39    });
40
41    it('checks for book existence', function (): void {
42        givenBookExists($this->repository, 12345, '9780134494166');
43
44        expect($this->repository->exists(new TimestampId(12345)))->toBeTrue();
45        expect($this->repository->exists(new TimestampId(99999)))->toBeFalse();
46    });
47});
48
49describe('Querying', function (): void {
50    it('returns all books via findAll', function (): void {
51        givenBookExists($this->repository, 1, '9780134494166');
52        givenBookExists($this->repository, 2, '9780201633610');
53
54        expect($this->repository->findAll())->toHaveCount(2);
55    });
56
57    it('returns matching books via findBy', function (): void {
58        givenBookExists($this->repository, 1, '9780134494166');
59        givenBookExists($this->repository, 2, '9780201633610');
60
61        expect($this->repository->findBy(new AlwaysTrueSpecification()))->toHaveCount(2);
62        expect($this->repository->findBy(new AlwaysFalseSpecification()))->toHaveCount(0);
63    });
64
65    it('returns book or null via findById', function (): void {
66        givenBookExists($this->repository, 12345, '9780134494166');
67
68        expect($this->repository->findById(new TimestampId(12345)))->toBeInstanceOf(Book::class);
69        expect($this->repository->findById(new TimestampId(99999)))->toBeNull();
70    });
71
72    it('returns first matching book or null via findOneBy', function (): void {
73        givenBookExists($this->repository, 12345, '9780134494166');
74
75        expect($this->repository->findOneBy(new AlwaysTrueSpecification()))->toBeInstanceOf(Book::class);
76        expect($this->repository->findOneBy(new AlwaysFalseSpecification()))->toBeNull();
77    });
78});
79
80describe('Counting', function (): void {
81    it('counts all books with an always-true specification', function (): void {
82        givenBookExists($this->repository, 1, '9780134494166');
83        givenBookExists($this->repository, 2, '9780201633610');
84
85        expect($this->repository->countBy(new AlwaysTrueSpecification()))->toBe(2);
86    });
87
88    it('returns zero with an always-false specification', function (): void {
89        givenBookExists($this->repository, 1, '9780134494166');
90
91        expect($this->repository->countBy(new AlwaysFalseSpecification()))->toBe(0);
92    });
93});
94
95describe('Deletion', function (): void {
96    it('deletes a book and returns affected rows', function (): void {
97        givenBookExists($this->repository, 12345, '9780134494166');
98        $book = $this->repository->getById(new TimestampId(12345));
99
100        $affectedRows = $this->repository->delete($book);
101
102        expect($affectedRows)->toBe(1)
103            ->and($this->repository->exists(new TimestampId(12345)))->toBeFalse()
104        ;
105    });
106
107    it('deletes a book by id and returns affected rows', function (): void {
108        givenBookExists($this->repository, 12345, '9780134494166');
109
110        $affectedRows = $this->repository->deleteById(new TimestampId(12345));
111
112        expect($affectedRows)->toBe(1)
113            ->and($this->repository->exists(new TimestampId(12345)))->toBeFalse()
114        ;
115    });
116});