Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
132 / 132
n/a
0 / 0
CRAP
n/a
0 / 0
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
10pest()->group('unit');
11
12use Phexium\Plugin\Clock\Adapter\FrozenClock;
13use Phexium\Plugin\Clock\Adapter\OffsetClock;
14
15describe('Time retrieval', function (): void {
16    it('returns inner clock time without offset', function (): void {
17        $inner = new FrozenClock('2025-01-01T12:00:00+00:00');
18        $clock = new OffsetClock($inner);
19
20        $result = $clock->now();
21
22        expect($result->format(DateTimeInterface::ATOM))->toBe('2025-01-01T12:00:00+00:00');
23    });
24});
25
26describe('Offset', function (): void {
27    it('advances time by seconds', function (): void {
28        $inner = new FrozenClock('2025-01-01T12:00:00+00:00');
29        $clock = new OffsetClock($inner);
30
31        $clock->advanceSeconds(60);
32
33        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-01-01T12:01:00+00:00');
34    });
35
36    it('advances time with DateInterval', function (): void {
37        $inner = new FrozenClock('2025-01-01T12:00:00+00:00');
38        $clock = new OffsetClock($inner);
39
40        $interval = new DateInterval('P1DT2H30M45S');
41        $clock->advance($interval);
42
43        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-01-02T14:30:45+00:00');
44    });
45
46    it('advances time with inverted DateInterval', function (): void {
47        $inner = new FrozenClock('2025-01-01T12:00:00+00:00');
48        $clock = new OffsetClock($inner);
49
50        $interval = new DateInterval('P1DT2H30M45S');
51        $interval->invert = 1;
52        $clock->advance($interval);
53
54        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2024-12-31T09:29:15+00:00');
55    });
56
57    it('accumulates multiple advances', function (): void {
58        $inner = new FrozenClock('2025-01-01T12:00:00+00:00');
59        $clock = new OffsetClock($inner);
60
61        $clock->advanceSeconds(30);
62        $clock->advanceSeconds(30);
63
64        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-01-01T12:01:00+00:00');
65    });
66
67    it('rewinds time by seconds', function (): void {
68        $inner = new FrozenClock('2025-01-01T12:00:00+00:00');
69        $clock = new OffsetClock($inner);
70
71        $clock->rewindSeconds(60);
72
73        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-01-01T11:59:00+00:00');
74    });
75
76    it('rewinds time with DateInterval', function (): void {
77        $inner = new FrozenClock('2025-01-01T12:00:00+00:00');
78        $clock = new OffsetClock($inner);
79
80        $interval = new DateInterval('P1DT2H30M45S');
81        $clock->rewind($interval);
82
83        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2024-12-31T09:29:15+00:00');
84    });
85
86    it('rewinds time with inverted DateInterval', function (): void {
87        $inner = new FrozenClock('2025-01-01T12:00:00+00:00');
88        $clock = new OffsetClock($inner);
89
90        $interval = new DateInterval('P1DT2H30M45S');
91        $interval->invert = 1;
92        $clock->rewind($interval);
93
94        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-01-02T14:30:45+00:00');
95    });
96
97    it('accumulates multiple rewinds', function (): void {
98        $inner = new FrozenClock('2025-01-01T12:00:00+00:00');
99        $clock = new OffsetClock($inner);
100
101        $clock->rewindSeconds(30);
102        $clock->rewindSeconds(30);
103
104        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-01-01T11:59:00+00:00');
105    });
106
107    it('combines advance and rewind', function (): void {
108        $inner = new FrozenClock('2025-01-01T12:00:00+00:00');
109        $clock = new OffsetClock($inner);
110
111        $clock->advanceSeconds(120);
112        $clock->rewindSeconds(60);
113
114        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-01-01T12:01:00+00:00');
115    });
116
117    it('resets offset to zero', function (): void {
118        $inner = new FrozenClock('2025-01-01T12:00:00+00:00');
119        $clock = new OffsetClock($inner);
120
121        $clock->advanceSeconds(3600);
122        $clock->reset();
123
124        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-01-01T12:00:00+00:00');
125    });
126
127    it('advances by large amounts', function (): void {
128        $inner = new FrozenClock('2025-01-01T12:00:00+00:00');
129        $clock = new OffsetClock($inner);
130
131        $clock->advanceSeconds(24 * 60 * 60);
132
133        expect($clock->now()->format('Y-m-d'))->toBe('2025-01-02');
134    });
135
136    it('advances with days in DateInterval', function (): void {
137        $inner = new FrozenClock('2025-01-01T12:00:00+00:00');
138        $clock = new OffsetClock($inner);
139
140        $clock->advance(new DateInterval('P7D'));
141
142        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-01-08T12:00:00+00:00');
143    });
144});
145
146describe('Calendar offset', function (): void {
147    it('advances by one month', function (): void {
148        $inner = new FrozenClock('2025-01-15T12:00:00+00:00');
149        $clock = new OffsetClock($inner);
150
151        $clock->advance(new DateInterval('P1M'));
152
153        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-02-15T12:00:00+00:00');
154    });
155
156    it('advances by one year', function (): void {
157        $inner = new FrozenClock('2024-01-15T12:00:00+00:00');
158        $clock = new OffsetClock($inner);
159
160        $clock->advance(new DateInterval('P1Y'));
161
162        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-01-15T12:00:00+00:00');
163    });
164
165    it('applies months with calendar length, not a fixed duration', function (): void {
166        $inner = new FrozenClock('2025-01-31T12:00:00+00:00');
167        $clock = new OffsetClock($inner);
168
169        $clock->advance(new DateInterval('P1M'));
170
171        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-03-03T12:00:00+00:00');
172    });
173
174    it('combines months and seconds from a single interval', function (): void {
175        $inner = new FrozenClock('2025-01-15T12:00:00+00:00');
176        $clock = new OffsetClock($inner);
177
178        $clock->advance(new DateInterval('P1M2DT3H'));
179
180        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-02-17T15:00:00+00:00');
181    });
182
183    it('rewinds by one month', function (): void {
184        $inner = new FrozenClock('2025-03-15T12:00:00+00:00');
185        $clock = new OffsetClock($inner);
186
187        $clock->rewind(new DateInterval('P1M'));
188
189        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-02-15T12:00:00+00:00');
190    });
191
192    it('resets the month offset to zero', function (): void {
193        $inner = new FrozenClock('2025-01-15T12:00:00+00:00');
194        $clock = new OffsetClock($inner);
195
196        $clock->advance(new DateInterval('P1Y2M'));
197        $clock->reset();
198
199        expect($clock->now()->format(DateTimeInterface::ATOM))->toBe('2025-01-15T12:00:00+00:00');
200    });
201});