resolver = new EnumResolver(); }); describe('resolve()', function () { it('converts string value to enum', function () { // Create a test enum if (!enum_exists('TestEnum')) { eval('enum TestEnum: string { case FOO = "foo"; case BAR = "bar"; }'); } $default = TestEnum::BAR; $result = $this->resolver->resolve('foo', TestEnum::class, $default); expect($result)->toBe(TestEnum::FOO); }); it('converts int value to enum', function () { // Create a test enum with int backing if (!enum_exists('IntTestEnum')) { eval('enum IntTestEnum: int { case ONE = 1; case TWO = 2; }'); } $default = IntTestEnum::TWO; $result = $this->resolver->resolve(1, IntTestEnum::class, $default); expect($result)->toBe(IntTestEnum::ONE); }); it('returns default when value cannot be converted', function () { if (!enum_exists('TestEnum')) { eval('enum TestEnum: string { case FOO = "foo"; case BAR = "bar"; }'); } $default = TestEnum::BAR; $result = $this->resolver->resolve('invalid', TestEnum::class, $default); expect($result)->toBe($default); }); it('returns default when value is null', function () { if (!enum_exists('TestEnum')) { eval('enum TestEnum: string { case FOO = "foo"; case BAR = "bar"; }'); } $default = TestEnum::BAR; $result = $this->resolver->resolve(null, TestEnum::class, $default); expect($result)->toBe($default); }); it('throws exception when default is not instance of enum class', function () { if (!enum_exists('TestEnum')) { eval('enum TestEnum: string { case FOO = "foo"; case BAR = "bar"; }'); } if (!enum_exists('AnotherEnum')) { eval('enum AnotherEnum: string { case BAZ = "baz"; }'); } $wrongDefault = AnotherEnum::BAZ; expect(function () use ($wrongDefault) { $this->resolver->resolve('foo', TestEnum::class, $wrongDefault); })->toThrow(\InvalidArgumentException::class); }); }); describe('caching', function () { it('caches enum conversion results', function () { if (!enum_exists('TestEnum')) { eval('enum TestEnum: string { case FOO = "foo"; case BAR = "bar"; }'); } $default = TestEnum::BAR; // First call - should convert $enum1 = $this->resolver->resolve('foo', TestEnum::class, $default); expect($enum1)->toBe(TestEnum::FOO); // Second call to same resolver instance should use cache // (We can't directly verify cache, but we can verify it returns the same instance) $enum2 = $this->resolver->resolve('foo', TestEnum::class, $default); expect($enum2)->toBe(TestEnum::FOO); expect($enum1)->toBe($enum2); }); it('caches different enum classes separately', function () { if (!enum_exists('TestEnum')) { eval('enum TestEnum: string { case FOO = "foo"; case BAR = "bar"; }'); } if (!enum_exists('AnotherTestEnum')) { eval('enum AnotherTestEnum: string { case FOO = "foo"; case BAR = "bar"; }'); } $default1 = TestEnum::BAR; $default2 = AnotherTestEnum::BAR; $enum1 = $this->resolver->resolve('foo', TestEnum::class, $default1); $enum2 = $this->resolver->resolve('foo', AnotherTestEnum::class, $default2); expect($enum1)->toBe(TestEnum::FOO); expect($enum2)->toBe(AnotherTestEnum::FOO); expect($enum1)->not->toBe($enum2); // Different enum classes }); it('caches different values separately', function () { if (!enum_exists('TestEnum')) { eval('enum TestEnum: string { case FOO = "foo"; case BAR = "bar"; }'); } $default = TestEnum::BAR; $enum1 = $this->resolver->resolve('foo', TestEnum::class, $default); $enum2 = $this->resolver->resolve('bar', TestEnum::class, $default); expect($enum1)->toBe(TestEnum::FOO); expect($enum2)->toBe(TestEnum::BAR); }); it('caches default values', function () { if (!enum_exists('TestEnum')) { eval('enum TestEnum: string { case FOO = "foo"; case BAR = "bar"; }'); } $default = TestEnum::BAR; // First call with null $enum1 = $this->resolver->resolve(null, TestEnum::class, $default); expect($enum1)->toBe($default); // Second call with null should return cached default $enum2 = $this->resolver->resolve(null, TestEnum::class, $default); expect($enum2)->toBe($default); expect($enum1)->toBe($enum2); }); }); });