88use PHPForge \Helper \Exception \Message ;
99use PHPForge \Helper \Reflector ;
1010use PHPForge \Helper \Tests \Support \Attribute \{Label , Marker };
11+ use PHPForge \Helper \Tests \Support \Contract \{LeftContract , Status , UsesTimestamp };
1112use PHPForge \Helper \Tests \Support \Model \ReflectionFixture ;
1213use PHPUnit \Framework \TestCase ;
1314use ReflectionClass ;
1819 * Unit tests for the {@see Reflector} helper.
1920 *
2021 * Test coverage.
21- * - Caches reflection class instances for repeated lookups and resets the cache when its size limit is reached .
22+ * - Caches reflection class instances for repeated lookups and evicts the oldest entry at cache- size limit.
2223 * - Detects whether a property exists on the reflection target.
2324 * - Extracts class and property attributes, including filtered lookups.
2425 * - Resolves first matching property attribute instances or returns `null` when no match exists.
25- * - Resolves property type names for untyped, named, nullable, union, and intersection declarations.
26+ * - Resolves property type names for mixed, untyped, named, nullable, union, and intersection declarations.
2627 * - Returns reflection properties and throws {@see InvalidArgumentException} for missing properties.
27- * - Returns short class names for named and anonymous classes and throws.
28+ * - Returns short names for class, enum, interface, trait, and anonymous targets and throws
29+ * {@see InvalidArgumentException} for invalid targets.
2830 *
2931 * @copyright Copyright (C) 2026 Terabytesoftw.
3032 * @license https://opensource.org/license/bsd-3-clause BSD 3-Clause License.
@@ -211,7 +213,10 @@ public function testPropertyAttributeInstancesReturnsInstantiatedAttributes(): v
211213 }
212214
213215 self ::assertSame (
214- ['primary ' , 'secondary ' ],
216+ [
217+ 'primary ' ,
218+ 'secondary ' ,
219+ ],
215220 $ instanceValues ,
216221 'Should preserve declaration order for instantiated repeatable attributes. ' ,
217222 );
@@ -320,7 +325,16 @@ public function testPropertyTypeNamesReturnsExpectedNamesForUnionProperty(): voi
320325 );
321326 }
322327
323- public function testReflectionClassCacheIsResetWhenCacheSizeLimitIsReached (): void
328+ public function testPropertyTypeNamesReturnsMixedWithoutExplicitNull (): void
329+ {
330+ self ::assertSame (
331+ ['mixed ' ],
332+ Reflector::propertyTypeNames (ReflectionFixture::class, 'payload ' ),
333+ "Should return only 'mixed' without appending an explicit null type. " ,
334+ );
335+ }
336+
337+ public function testReflectionClassCacheEvictsOldestEntryWhenCacheSizeLimitIsReached (): void
324338 {
325339 $ reflectorClass = new ReflectionClass (Reflector::class);
326340
@@ -343,14 +357,19 @@ public function testReflectionClassCacheIsResetWhenCacheSizeLimitIsReached(): vo
343357 $ cacheAfterInsert = $ cacheProperty ->getValue ();
344358
345359 self ::assertCount (
346- 1 ,
360+ $ cacheLimit ,
361+ $ cacheAfterInsert ,
362+ 'Should keep cache size at the configured limit after inserting a new class. ' ,
363+ );
364+ self ::assertArrayNotHasKey (
365+ 'cached-0 ' ,
347366 $ cacheAfterInsert ,
348- 'Should reset cache and keep only the latest reflected class after limit is reached. ' ,
367+ 'Should evict the oldest cached entry when cache size limit is reached. ' ,
349368 );
350369 self ::assertArrayHasKey (
351370 ReflectionFixture::class,
352371 $ cacheAfterInsert ,
353- 'Should keep the class being reflected after cache reset . ' ,
372+ 'Should cache the newly reflected class after evicting the oldest entry . ' ,
354373 );
355374 }
356375
@@ -406,6 +425,24 @@ public function testShortNameReturnsEmptyStringForAnonymousClass(): void
406425 );
407426 }
408427
428+ public function testShortNameReturnsShortNameForEnumTarget (): void
429+ {
430+ self ::assertSame (
431+ 'Status ' ,
432+ Reflector::shortName (Status::class),
433+ 'Should return the short name for enum targets. ' ,
434+ );
435+ }
436+
437+ public function testShortNameReturnsShortNameForInterfaceTarget (): void
438+ {
439+ self ::assertSame (
440+ 'LeftContract ' ,
441+ Reflector::shortName (LeftContract::class),
442+ 'Should return the short name for interface targets. ' ,
443+ );
444+ }
445+
409446 public function testShortNameReturnsShortNameForNamedClass (): void
410447 {
411448 self ::assertSame (
@@ -415,6 +452,15 @@ public function testShortNameReturnsShortNameForNamedClass(): void
415452 );
416453 }
417454
455+ public function testShortNameReturnsShortNameForTraitTarget (): void
456+ {
457+ self ::assertSame (
458+ 'UsesTimestamp ' ,
459+ Reflector::shortName (UsesTimestamp::class),
460+ 'Should return the short name for trait targets. ' ,
461+ );
462+ }
463+
418464 public function testThrowsInvalidArgumentExceptionForInvalidReflectionTarget (): void
419465 {
420466 $ this ->expectException (InvalidArgumentException::class);
0 commit comments