Skip to content

Commit

Permalink
Write unhappy tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bigfoot90 committed Mar 25, 2024
1 parent 1a7fa0e commit 8187fbe
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 23 deletions.
74 changes: 51 additions & 23 deletions src/Types/EnumType.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
use Doctrine\DBAL\Types\Exception\InvalidType;
use Doctrine\DBAL\Types\Exception\ValueNotConvertible;
use ReflectionClass;
use StringBackedEnum;
use Throwable;
use UnitEnum;
use ValueError;

use function array_map;
use function class_exists;
use function enum_exists;
use function implode;
use function is_object;
use function is_string;
use function sprintf;

Expand Down Expand Up @@ -69,14 +70,34 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform): ?str
return null;
}

if ($value instanceof UnitEnum) {
if ($value instanceof BackedEnum) {
if ($this->enumClassname === null) {
if (! is_string($value)) {
throw InvalidType::new($value, $this->name, ['null', 'string']);
}

if (! in_array($value, $this->members)) {
throw InvalidType::new($value, $this->name, ['null', 'string']);
}

return $value;
}

if (enum_exists($this->enumClassname)) {
if (! $value instanceof UnitEnum) {
throw InvalidType::new($value, $this->name, ['null', $this->enumClassname]);

Check warning on line 87 in src/Types/EnumType.php

View check run for this annotation

Codecov / codecov/patch

src/Types/EnumType.php#L87

Added line #L87 was not covered by tests
}

if ($value instanceof \BackedEnum) {
return $value->value;
}

return $value->name;
}

if (! (is_object($value) && $value::class === $this->enumClassname)) {
throw InvalidType::new($value, $this->name, ['null', $this->enumClassname]);

Check warning on line 98 in src/Types/EnumType.php

View check run for this annotation

Codecov / codecov/patch

src/Types/EnumType.php#L98

Added line #L98 was not covered by tests
}

return (string) $value;
}

Expand All @@ -90,32 +111,39 @@ public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mix
throw InvalidType::new($value, $this->name, ['null', 'string']);

Check warning on line 111 in src/Types/EnumType.php

View check run for this annotation

Codecov / codecov/patch

src/Types/EnumType.php#L111

Added line #L111 was not covered by tests
}

if ($this->enumClassname) {
if (enum_exists($this->enumClassname)) {
try {
foreach ($this->enumClassname::cases() as $case) {
if (($case instanceof BackedEnum && $value === $case->value) || $value === $case->name) {
return $case;
}
}

throw new ValueError(sprintf("'%s' is not a valid backing value for enum %s", $value, $this->enumClassname));
} catch (Throwable $e) {
throw ValueNotConvertible::new($value, $this->name, $e->getMessage(), $e);
if ($this->enumClassname === null) {
return $value;
}

if (enum_exists($this->enumClassname)) {
foreach ($this->enumClassname::cases() as $case) {
if (($case instanceof BackedEnum && $value === $case->value) || $value === $case->name) {
return $case;
}
}

if (class_exists($this->enumClassname)) {
$refl = new ReflectionClass($this->enumClassname);
throw ValueNotConvertible::new($value, $this->getInternalDocrineType());

Check warning on line 125 in src/Types/EnumType.php

View check run for this annotation

Codecov / codecov/patch

src/Types/EnumType.php#L125

Added line #L125 was not covered by tests
}

if (class_exists($this->enumClassname)) {
$refl = new ReflectionClass($this->enumClassname);

try {
return $refl->newInstance($value);
} catch (Throwable $e) {
throw ValueNotConvertible::new($value, $this->name, $e->getMessage(), $e);
}
try {
return $refl->newInstance($value);
} catch (Throwable $e) {
throw ValueNotConvertible::new($value, $this->getInternalDocrineType(), $e->getMessage(), $e);

Check warning on line 134 in src/Types/EnumType.php

View check run for this annotation

Codecov / codecov/patch

src/Types/EnumType.php#L133-L134

Added lines #L133 - L134 were not covered by tests
}
}

return $value;
throw new ConversionException(sprintf('Class %s does not exists', $this->enumClassname));

Check warning on line 138 in src/Types/EnumType.php

View check run for this annotation

Codecov / codecov/patch

src/Types/EnumType.php#L138

Added line #L138 was not covered by tests
}

private function getInternalDocrineType(): string

Check warning on line 141 in src/Types/EnumType.php

View check run for this annotation

Codecov / codecov/patch

src/Types/EnumType.php#L141

Added line #L141 was not covered by tests
{
if ($this->enumClassname) {
return sprintf('%s(%s)', $this->name, $this->enumClassname);

Check warning on line 144 in src/Types/EnumType.php

View check run for this annotation

Codecov / codecov/patch

src/Types/EnumType.php#L143-L144

Added lines #L143 - L144 were not covered by tests
}

return sprintf('%s(%s)', $this->name, 'string');

Check warning on line 147 in src/Types/EnumType.php

View check run for this annotation

Codecov / codecov/patch

src/Types/EnumType.php#L147

Added line #L147 was not covered by tests
}
}
105 changes: 105 additions & 0 deletions tests/Types/EnumTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
namespace Doctrine\DBAL\Tests\Types;

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\EnumType;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Stringable;
Expand Down Expand Up @@ -62,6 +64,8 @@ public function testConvertToPHPObject(): void

public function testConvertStringToDatabaseValue(): void
{
$this->type->members = ['a', 'b'];

self::assertSame('a', $this->type->convertToDatabaseValue('a', $this->platform));
self::assertNull($this->type->convertToDatabaseValue(null, $this->platform));
}
Expand Down Expand Up @@ -89,6 +93,98 @@ public function testConvertObjectToDatabaseValue(): void
self::assertSame('a', $this->type->convertToDatabaseValue(new EnumClass('a'), $this->platform));
self::assertNull($this->type->convertToDatabaseValue(null, $this->platform));
}

#[DataProvider('provideInvalidDataForEnumStringToDatabaseValueConversion')]
public function testEnumStringDoesNotSupportInvalidValuesToDatabaseValueConversion($value): void
{
$this->expectException(ConversionException::class);

$this->type->convertToDatabaseValue($value, $this->platform);
}

public static function provideInvalidDataForEnumStringToDatabaseValueConversion()
{
return [
'boolean true' => [true],
'boolean false' => [false],
'integer' => [17],
'string' => ['not_in_members'],
'array' => [['']],
'enum' => [EnumPhp::A],
'enum backed' => [EnumPhpBacked::A],
'object' => [new \stdClass()],
'stringable' => [new class() { function __toString() { return 'a'; }}],
];
}

#[DataProvider('provideInvalidDataForEnumPhpToDatabaseValueConversion')]
public function testEnumPhpDoesNotSupportInvalidValuesToDatabaseValueConversion($value): void
{
$this->expectException(ConversionException::class);

$this->type->convertToDatabaseValue($value, $this->platform);
}

public static function provideInvalidDataForEnumPhpToDatabaseValueConversion()
{
return [
'boolean true' => [true],
'boolean false' => [false],
'integer' => [17],
'string' => ['a'],
'array' => [['']],
'enum' => [WrongEnumPhp::WRONG],
'enum backed' => [EnumPhpBacked::A],
'object' => [new \stdClass()],
'stringable' => [new class() { function __toString() { return 'a'; }}],
];
}

#[DataProvider('provideInvalidDataForEnumPhpBackedToDatabaseValueConversion')]
public function testEnumPhpBackedDoesNotSupportInvalidValuesToDatabaseValueConversion($value): void
{
$this->expectException(ConversionException::class);

$this->type->convertToDatabaseValue($value, $this->platform);
}

public static function provideInvalidDataForEnumPhpBackedToDatabaseValueConversion()
{
return [
'boolean true' => [true],
'boolean false' => [false],
'integer' => [17],
'string' => ['a'],
'array' => [['']],
'enum' => [EnumPhp::A],
'enum backed' => [WrongEnumPhpBacked::WRONG],
'object' => [new \stdClass()],
'stringable' => [new class() { function __toString() { return 'a'; }}],
];
}

#[DataProvider('provideInvalidDataForEnumObjectToDatabaseValueConversion')]
public function testEnumObjectDoesNotSupportInvalidValuesToDatabaseValueConversion($value): void
{
$this->expectException(ConversionException::class);

$this->type->convertToDatabaseValue($value, $this->platform);
}

public static function provideInvalidDataForEnumObjectToDatabaseValueConversion()
{
return [
'boolean true' => [true],
'boolean false' => [false],
'integer' => [17],
'string' => ['a'],
'array' => [['']],
'enum' => [EnumPhp::A],
'enum backed' => [EnumPhpBacked::A],
'object' => [new \stdClass()],
'stringable' => [new class() { function __toString() { return 'a'; }}],
];
}
}

enum EnumPhp
Expand All @@ -97,13 +193,22 @@ enum EnumPhp
case B;
}

enum WrongEnumPhp
{
case WRONG;
}

enum EnumPhpBacked: string
{
case A = 'a';
case B = 'b';
}

enum WrongEnumPhpBacked: string
{
case WRONG = 'wrong';
}

final class EnumClass implements Stringable
{
public function __construct(
Expand Down

0 comments on commit 8187fbe

Please sign in to comment.