Issues (19)

tests/AnnotatedMapTest.php (6 issues)

1
<?php declare(strict_types=1);
2
/**
3
 * This file is part of the daikon-cqrs/data-structure project.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace Daikon\Tests\DataStructure;
10
11
use Daikon\Interop\InvalidArgumentException;
12
use Daikon\Tests\DataStructure\Fixture\AnnotatedMap;
13
use Daikon\Tests\DataStructure\Fixture\DatetimeList;
14
use DateTime;
15
use DateTimeImmutable;
16
use DateTimeInterface;
17
use PHPUnit\Framework\TestCase;
18
use stdClass;
19
20
final class AnnotatedMapTest extends TestCase
21
{
22 1
    public function testConstructWithoutParams(): void
23
    {
24 1
        $this->assertInstanceOf(AnnotatedMap::class, new AnnotatedMap);
25 1
    }
26
27 1
    public function testConstructWithParams(): void
28
    {
29 1
        $map = new AnnotatedMap(['now' => new DateTime, 'nower' => new DateTimeImmutable]);
30
        /** @psalm-suppress RedundantCondition */
31 1
        $this->assertInstanceOf(AnnotatedMap::class, $map);
32 1
    }
33
34 1
    public function testConstructWithIndexedParams(): void
35
    {
36 1
        $map = new AnnotatedMap(['a1337' => new DateTime, 'yes' => new DateTimeImmutable]);
37 1
        $this->assertCount(2, $map);
38 1
        $this->assertTrue($map->has('yes'));
39 1
        $this->assertTrue($map->has('a1337'));
40 1
    }
41
42 1
    public function testConstructWithIntegerStringAsKeyThrowsBecausePhp(): void
43
    {
44 1
        $this->expectException(InvalidArgumentException::class);
45 1
        $this->expectExceptionCode(16);
46 1
        $this->expectExceptionMessage('Key must be a valid string.');
47 1
        new AnnotatedMap(['1337' => new DateTime]);
48
    }
49
50 1
    public function testConstructFailsOnInvalidIndex(): void
51
    {
52 1
        $d0 = new DateTime;
53 1
        $this->expectException(InvalidArgumentException::class);
54 1
        $this->expectExceptionCode(16);
55 1
        $this->expectExceptionMessage('Key must be a valid string.');
56 1
        new AnnotatedMap([123 => $d0]);
57
    }
58
59 1
    public function testKeys(): void
60
    {
61 1
        $map = new AnnotatedMap(['a' => new DateTime, 'b' => new DateTimeImmutable]);
62 1
        $this->assertSame(['a', 'b'], $map->keys());
63 1
    }
64
65 1
    public function testEmpty(): void
66
    {
67 1
        $map = new AnnotatedMap(['a' => new DateTime, 'b' => new DateTimeImmutable]);
68 1
        $empty = $map->empty();
69 1
        $this->assertNotSame($map, $empty);
70 1
        $this->assertCount(0, $empty);
71 1
        $this->assertTrue($empty->isEmpty());
72 1
    }
73
74 1
    public function testHas(): void
75
    {
76 1
        $map = new AnnotatedMap(['a' => new DateTime, 'b' => new DateTimeImmutable]);
77 1
        $this->assertTrue($map->has('a'));
78 1
        $this->assertTrue($map->has('b'));
79 1
        $this->assertFalse($map->has('A'));
80 1
        $this->assertFalse($map->has('B'));
81 1
    }
82
83 1
    public function testGet(): void
84
    {
85 1
        $d1 = new DateTime;
86 1
        $map = new AnnotatedMap(['a' => $d1, 'b' => new DateTimeImmutable]);
87 1
        $unwrappedMap = $map->unwrap();
88 1
        $this->assertNotSame($d1, $unwrappedMap['a']);
89 1
        $this->assertEquals($d1, $unwrappedMap['a']);
90 1
        $this->assertNotSame($unwrappedMap['a'], $map->get('a'));
91 1
        $this->assertEquals($unwrappedMap['a'], $map->get('a'));
92 1
    }
93
94 1
    public function testGetWithDefault(): void
95
    {
96 1
        $d1 = new DateTime;
97 1
        $default = new DateTime('@1234567');
98 1
        $map = new AnnotatedMap(['a' => $d1]);
99 1
        $this->assertNotSame($default, $map->get('x', $default));
100 1
        $this->assertEquals($default, $map->get('x', $default));
101 1
    }
102
103 1
    public function testGetWithNullDefault(): void
104
    {
105 1
        $d1 = new DateTime;
106 1
        $map = new AnnotatedMap(['a' => $d1]);
107 1
        $this->assertNull($map->get('x', null));
108 1
    }
109
110 1
    public function testGetWithInvalidDefault(): void
111
    {
112 1
        $d1 = new DateTime;
113 1
        $map = new AnnotatedMap(['a' => $d1]);
114 1
        $this->expectException(InvalidArgumentException::class);
115 1
        $this->expectExceptionCode(32);
116 1
        $this->expectExceptionMessage(
117
            "Invalid object type given to 'Daikon\Tests\DataStructure\Fixture\AnnotatedMap', ".
118 1
            "expected one of [DateTime, DateTimeImmutable] but was given 'stdClass'."
119
        );
120 1
        $map->get('x', new stdClass);
121
    }
122
123 1
    public function testGetWithNoDefault(): void
124
    {
125 1
        $map = new AnnotatedMap;
126 1
        $this->expectException(InvalidArgumentException::class);
127 1
        $this->expectExceptionCode(217);
128 1
        $this->expectExceptionMessage("Key 'x' not found and no default provided.");
129 1
        $map->x;
0 ignored issues
show
Bug Best Practice introduced by
The property x does not exist on Daikon\Tests\DataStructure\Fixture\AnnotatedMap. Since you implemented __get, consider adding a @property annotation.
Loading history...
130
    }
131
132 1
    public function testGetThrowsForInternalProperties(): void
133
    {
134 1
        $map = new AnnotatedMap(['a' => new Datetime]);
135 1
        $this->expectException(InvalidArgumentException::class);
136 1
        $this->expectExceptionCode(217);
137 1
        $map->validTypes;
0 ignored issues
show
Bug Best Practice introduced by
The property $validTypes is declared protected in Daikon\DataStructure\TypedMap. Since you implement __get, consider adding a @property or @property-read.
Loading history...
138
    }
139
140 1
    public function testWith(): void
141
    {
142 1
        $d0 = new DateTime;
143 1
        $d1 = new DateTimeImmutable;
144 1
        $map = new AnnotatedMap(['a' => $d0]);
145 1
        $unwrappedMap = $map->with('b', $d1)->unwrap();
146 1
        $this->assertNotSame($d0, $unwrappedMap['a']);
147 1
        $this->assertEquals($d0, $unwrappedMap['a']);
148 1
        $this->assertNotSame($d1, $unwrappedMap['b']);
149 1
        $this->assertEquals($d1, $unwrappedMap['b']);
150 1
        $this->assertCount(2, $unwrappedMap);
151 1
    }
152
153 1
    public function testWithFailsOnUnacceptableType(): void
154
    {
155 1
        $d0 = new DateTime;
156 1
        $d1 = new stdClass;
157 1
        $map = new AnnotatedMap(['a' => $d0]);
158 1
        $this->expectException(InvalidArgumentException::class);
159 1
        $this->expectExceptionCode(32);
160 1
        $this->expectExceptionMessage(
161
            "Invalid object type given to 'Daikon\Tests\DataStructure\Fixture\AnnotatedMap', ".
162 1
            "expected one of [DateTime, DateTimeImmutable] but was given 'stdClass'."
163
        );
164 1
        $map->with('b', $d1);
165
    }
166
167 1
    public function testWithout(): void
168
    {
169 1
        $d0 = new DateTimeImmutable;
170 1
        $d1 = new DateTimeImmutable;
171 1
        $d2 = new DateTime;
172 1
        $map = new AnnotatedMap(['a' => $d0, 'b' => $d1, 'c' => $d2]);
173 1
        $unwrappedMap = $map->unwrap();
174 1
        $prunedMap = $map->without('b')->unwrap();
175 1
        $this->assertNotSame($unwrappedMap['a'], $prunedMap['a']);
176 1
        $this->assertEquals($unwrappedMap['a'], $prunedMap['a']);
177 1
        $this->assertCount(2, $prunedMap);
178 1
        $this->assertNotSame($d0, $prunedMap['a']);
179 1
        $this->assertEquals($d0, $prunedMap['a']);
180 1
        $this->assertNotSame($d2, $prunedMap['c']);
181 1
        $this->assertEquals($d2, $prunedMap['c']);
182 1
        $this->assertArrayNotHasKey('b', $prunedMap);
183 1
    }
184
185 1
    public function testWithoutWithNotExistentKey(): void
186
    {
187 1
        $d0 = new DateTimeImmutable;
188 1
        $d1 = new DateTime;
189 1
        $map = new AnnotatedMap(['a' => $d0, 'b' => $d1]);
190 1
        $this->expectException(InvalidArgumentException::class);
191 1
        $this->expectExceptionCode(217);
192 1
        $this->expectExceptionMessage("Key 'c' not found.");
193 1
        $map->without('c');
194
    }
195
196 1
    public function testFind(): void
197
    {
198 1
        $d1 = new DateTimeImmutable('-1 minute');
199 1
        $map = new AnnotatedMap(['a' => new DateTimeImmutable, 'b' => $d1]);
200 1
        $unwrappedMap = $map->unwrap();
201 1
        $this->assertNotSame($d1, $unwrappedMap['b']);
202 1
        $this->assertEquals($d1, $unwrappedMap['b']);
203 1
        $this->assertEquals('b', $map->find($d1));
204 1
    }
205
206 1
    public function testFirst(): void
207
    {
208 1
        $d1 = new DateTimeImmutable;
209 1
        $map = new AnnotatedMap(['a' => $d1, 'b' => new DateTimeImmutable]);
210 1
        $unwrappedMap = $map->unwrap();
211 1
        $this->assertNotSame($d1, $unwrappedMap['a']);
212 1
        $this->assertEquals($d1, $unwrappedMap['a']);
213 1
        $this->assertNotSame($unwrappedMap['a'], $map->first());
214 1
        $this->assertEquals($unwrappedMap['a'], $map->first());
215 1
    }
216
217 1
    public function testLast(): void
218
    {
219 1
        $d1 = new DateTimeImmutable;
220 1
        $d2 = new DateTimeImmutable;
221 1
        $map = new AnnotatedMap(['a' => $d1, 'b' => $d2]);
222 1
        $unwrappedMap = $map->unwrap();
223 1
        $this->assertNotSame($d2, $unwrappedMap['b']);
224 1
        $this->assertEquals($d2, $unwrappedMap['b']);
225 1
        $this->assertNotSame($unwrappedMap['b'], $map->last());
226 1
        $this->assertEquals($unwrappedMap['b'], $map->last());
227 1
    }
228
229 1
    public function testIsEmpty(): void
230
    {
231 1
        $map = new AnnotatedMap(['a' => new DateTime, 'b' => new DateTimeImmutable]);
232 1
        $this->assertFalse($map->isEmpty());
233 1
        $map = new AnnotatedMap;
234 1
        $this->assertTrue($map->isEmpty());
235 1
    }
236
237 1
    public function testMerge(): void
238
    {
239 1
        $d0 = new DateTime;
240 1
        $d1 = new DateTimeImmutable;
241 1
        $d2 = new DateTimeImmutable('@1234567');
242 1
        $d3 = new DateTimeImmutable('@7654321');
243 1
        $map0 = new AnnotatedMap(['a' => $d0, 'c' => $d3]);
244 1
        $map1 = new AnnotatedMap(['a' => $d1, 'b' => $d2]);
245 1
        $unwrappedMap0 = $map0->unwrap();
246 1
        $mergedMap = $map0->merge($map1)->unwrap();
247 1
        $this->assertCount(3, $mergedMap);
248 1
        $this->assertNotSame($unwrappedMap0['c'], $mergedMap['c']);
249 1
        $this->assertEquals($unwrappedMap0['c'], $mergedMap['c']);
250 1
        $this->assertNotSame($d1, $mergedMap['a']);
251 1
        $this->assertEquals($d1, $mergedMap['a']);
252 1
        $this->assertNotSame($d2, $mergedMap['b']);
253 1
        $this->assertEquals($d2, $mergedMap['b']);
254 1
        $this->assertNotSame($d3, $mergedMap['c']);
255 1
        $this->assertEquals($d3, $mergedMap['c']);
256 1
    }
257
258 1
    public function testMergeWithInvalidParam(): void
259
    {
260 1
        $map = new AnnotatedMap;
261 1
        $list = new DatetimeList;
262 1
        $this->expectException(InvalidArgumentException::class);
263 1
        $this->expectExceptionCode(28);
264 1
        $this->expectExceptionMessage(
265 1
            "Map operation must be on same type as 'Daikon\Tests\DataStructure\Fixture\AnnotatedMap'."
266
        );
267
        /** @psalm-suppress InvalidArgument */
268 1
        $map->merge($list);
0 ignored issues
show
$list of type Daikon\Tests\DataStructure\Fixture\DatetimeList is incompatible with the type Daikon\DataStructure\TypedMap expected by parameter $map of Daikon\DataStructure\TypedMap::merge(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

268
        $map->merge(/** @scrutinizer ignore-type */ $list);
Loading history...
269
    }
270
271 1
    public function testIntersect(): void
272
    {
273 1
        $d0 = new DateTime;
274 1
        $d1 = new DateTimeImmutable;
275 1
        $d2 = new DateTimeImmutable('@1234567');
276 1
        $map0 = new AnnotatedMap(['a' => $d0]);
277 1
        $map1 = new AnnotatedMap(['a' => $d1, 'b' => $d2]);
278 1
        $unwrappedMap0 = $map0->unwrap();
279 1
        $intersectedMap = $map0->intersect($map1)->unwrap();
280 1
        $this->assertNotSame($unwrappedMap0['a'], $intersectedMap['a']);
281 1
        $this->assertEquals($unwrappedMap0['a'], $intersectedMap['a']);
282 1
        $this->assertCount(1, $intersectedMap);
283 1
        $this->assertNotSame($d0, $intersectedMap['a']);
284 1
        $this->assertEquals($d0, $intersectedMap['a']);
285 1
    }
286
287 1
    public function testIntersectWithInvalidParam(): void
288
    {
289 1
        $map = new AnnotatedMap;
290 1
        $list = new DatetimeList;
291 1
        $this->expectException(InvalidArgumentException::class);
292 1
        $this->expectExceptionCode(28);
293 1
        $this->expectExceptionMessage(
294 1
            "Map operation must be on same type as 'Daikon\Tests\DataStructure\Fixture\AnnotatedMap'."
295
        );
296
        /** @psalm-suppress InvalidArgument */
297 1
        $map->intersect($list);
0 ignored issues
show
$list of type Daikon\Tests\DataStructure\Fixture\DatetimeList is incompatible with the type Daikon\DataStructure\TypedMap expected by parameter $map of Daikon\DataStructure\TypedMap::intersect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

297
        $map->intersect(/** @scrutinizer ignore-type */ $list);
Loading history...
298
    }
299
300 1
    public function testDiff(): void
301
    {
302 1
        $d0 = new DateTime;
303 1
        $d1 = new DateTimeImmutable;
304 1
        $d2 = new DateTimeImmutable('@1234567');
305 1
        $map0 = new AnnotatedMap(['a' => $d1, 'b' => $d2]);
306 1
        $map1 = new AnnotatedMap(['a' => $d0]);
307 1
        $unwrappedMap0 = $map0->unwrap();
308 1
        $diffedMap = $map0->diff($map1)->unwrap();
309 1
        $this->assertNotSame($unwrappedMap0['b'], $diffedMap['b']);
310 1
        $this->assertEquals($unwrappedMap0['b'], $diffedMap['b']);
311 1
        $this->assertCount(1, $diffedMap);
312 1
        $this->assertNotSame($d2, $diffedMap['b']);
313 1
        $this->assertEquals($d2, $diffedMap['b']);
314 1
    }
315
316 1
    public function testDiffWithInvalidParam(): void
317
    {
318 1
        $map = new AnnotatedMap;
319 1
        $list = new DatetimeList;
320 1
        $this->expectException(InvalidArgumentException::class);
321 1
        $this->expectExceptionCode(28);
322 1
        $this->expectExceptionMessage(
323 1
            "Map operation must be on same type as 'Daikon\Tests\DataStructure\Fixture\AnnotatedMap'."
324
        );
325
        /** @psalm-suppress InvalidArgument */
326 1
        $map->diff($list);
0 ignored issues
show
$list of type Daikon\Tests\DataStructure\Fixture\DatetimeList is incompatible with the type Daikon\DataStructure\TypedMap expected by parameter $map of Daikon\DataStructure\TypedMap::diff(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

326
        $map->diff(/** @scrutinizer ignore-type */ $list);
Loading history...
327
    }
328
329 1
    public function testFilter(): void
330
    {
331 1
        $d0 = new DateTimeImmutable('@7654321');
332 1
        $d1 = new DateTimeImmutable('@1234567');
333 1
        $map = new AnnotatedMap(['a' => $d0, 'b' => $d1]);
334 1
        $unwrappedMap = $map->unwrap();
335 1
        $filteredMap = $map->filter(
336 1
            fn(string $key, DateTimeInterface $d): bool => $d > new DateTimeImmutable('@4444444')
337 1
        )->unwrap();
338 1
        $this->assertNotSame($unwrappedMap['a'], $filteredMap['a']);
339 1
        $this->assertEquals($unwrappedMap['a'], $filteredMap['a']);
340 1
        $this->assertCount(1, $filteredMap);
341 1
        $this->assertNotSame($d0, $filteredMap['a']);
342 1
        $this->assertEquals($d0, $filteredMap['a']);
343 1
    }
344
345 1
    public function testFilterEmpty(): void
346
    {
347 1
        $map = new AnnotatedMap;
348 1
        $filteredList = $map->filter(fn(): bool => true);
349 1
        $this->assertNotSame($map, $filteredList);
350 1
    }
351
352 1
    public function testSearch(): void
353
    {
354 1
        $d1 = new DateTimeImmutable('@1234567');
355 1
        $map = new AnnotatedMap(['a' => new DateTimeImmutable, 'b' => $d1]);
356 1
        $unwrappedMap = $map->unwrap();
357 1
        $this->assertNotSame($d1, $unwrappedMap['b']);
358 1
        $this->assertEquals($d1, $unwrappedMap['b']);
359 1
        $predicate = fn(DateTimeInterface $object): bool => $d1->getTimestamp() === $object->getTimestamp();
360 1
        $this->assertEquals('b', $map->search($predicate));
361 1
    }
362
363 1
    public function testMap(): void
364
    {
365 1
        $d0 = new DateTimeImmutable;
366 1
        $d1 = new DateTimeImmutable('@1234567');
367 1
        $map = new AnnotatedMap(['a' => $d0, 'b' => $d1]);
368 1
        $unwrappedMap = $map->unwrap();
369 1
        $appliedMap = $map->map(
370 1
            fn(string $key, DateTimeInterface $item) => $item
371 1
        )->unwrap();
372 1
        $this->assertNotSame($unwrappedMap['a'], $appliedMap['a']);
373 1
        $this->assertEquals($unwrappedMap['a'], $appliedMap['a']);
374 1
        $this->assertCount(2, $appliedMap);
375 1
        $this->assertNotSame($d0, $appliedMap['a']);
376 1
        $this->assertEquals($d0, $appliedMap['a']);
377 1
        $this->assertNotSame($d1, $appliedMap['b']);
378 1
        $this->assertEquals($d1, $appliedMap['b']);
379 1
    }
380
381 1
    public function testReduce(): void
382
    {
383 1
        $d0 = new DateTimeImmutable;
384 1
        $d1 = new DateTimeImmutable('@1234567');
385 1
        $map = new AnnotatedMap(['a' => $d0, 'b' => $d1]);
386 1
        $result = $map->reduce(fn(string $carry, string $key): string => $key, 'a');
387 1
        $this->assertEquals('b', $result);
388 1
    }
389
390 1
    public function testGetValidTypes(): void
391
    {
392 1
        $this->assertEquals([DateTime::class, DateTimeImmutable::class], (new AnnotatedMap)->getValidTypes());
393 1
    }
394
395 1
    public function testunwrap(): void
396
    {
397 1
        $d0 = new DateTime;
398 1
        $d1 = new DateTimeImmutable;
399 1
        $a = ['a' => $d0, 'b' => $d1];
400 1
        $map = new AnnotatedMap($a);
401 1
        $b = $map->unwrap();
402 1
        $this->assertNotSame($a, $b);
403 1
        $this->assertEquals($a, $b);
404 1
        $this->assertNotSame($a['a'], $b['a']);
405 1
        $this->assertEquals($a['a'], $b['a']);
406 1
        $this->assertNotSame($a['b'], $b['b']);
407 1
        $this->assertEquals($a['b'], $b['b']);
408 1
    }
409
410 1
    public function testIterator(): void
411
    {
412 1
        $d0 = new DateTime;
413 1
        $d1 = new DateTimeImmutable;
414 1
        $state = ['a' => $d0, 'b' => $d1];
415 1
        $map = new AnnotatedMap($state);
416 1
        $unwrappedMap = $map->unwrap();
417 1
        foreach ($map as $key => $current) {
418 1
            $this->assertNotSame($unwrappedMap[$key], $current);
419 1
            $this->assertEquals($unwrappedMap[$key], $current);
420 1
            $this->assertNotSame($state[$key], $current);
421 1
            $this->assertEquals($state[$key], $current);
422
        }
423 1
    }
424
425 1
    public function testImplicitGet(): void
426
    {
427 1
        $d1 = new DateTime;
428 1
        $map = new AnnotatedMap(['a' => $d1, 'b' => new DateTimeImmutable]);
429 1
        $this->assertNotSame($d1, $map->a);
0 ignored issues
show
Bug Best Practice introduced by
The property a does not exist on Daikon\Tests\DataStructure\Fixture\AnnotatedMap. Since you implemented __get, consider adding a @property annotation.
Loading history...
430 1
        $this->assertEquals($d1, $map->a);
431 1
    }
432
433 1
    public function testImplicitGetForWeirdKey(): void
434
    {
435 1
        $d1 = new DateTime;
436 1
        $key = '_a.b.123-456';
437 1
        $map = new AnnotatedMap([$key => $d1]);
438 1
        $this->assertNotSame($d1, $map->{'_a.b.123-456'});
439 1
        $this->assertEquals($d1, $map->$key);
440 1
    }
441
442 1
    public function testCount(): void
443
    {
444 1
        $map = new AnnotatedMap(['a' => new DateTime, 'b' => new DateTimeImmutable]);
445 1
        $this->assertCount(2, $map);
446 1
    }
447
448 1
    public function testClone(): void
449
    {
450 1
        $d0 = new DateTime;
451 1
        $d1 = new DateTimeImmutable;
452 1
        $a = ['a' => $d0, 'b' => $d1];
453 1
        $map = new AnnotatedMap($a);
454 1
        $unwrappedMap = $map->unwrap();
455 1
        $clonedMap = clone $map;
456 1
        $unwrappedClone = $clonedMap->unwrap();
457 1
        $this->assertSame($map->getValidTypes(), $clonedMap->getValidTypes());
458 1
        $this->assertNotSame($map->getIterator(), $clonedMap->getIterator());
459 1
        $this->assertEquals($map->getIterator(), $clonedMap->getIterator());
460 1
        $this->assertNotSame($unwrappedMap['a'], $unwrappedClone['a']);
461 1
        $this->assertEquals($unwrappedMap['a'], $unwrappedClone['a']);
462 1
    }
463
}
464