diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d5338bb..fa860566 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,23 @@ All notable changes to this project will be documented in this file. This projec ## Next Major Release +### Added + +- **BREAKING**: Added `code()` method to the `ListOfErrors` interface. This returns the first error code in the list, or + `null` if there are no error codes. + ### Changed +- **BREAKING**: The constructor argument order for the `Error` class has changed. The new order is `code`, `message`, + `key`. Previously it was `key`, `message`, `code` because code was added later. However, in most instances `code` is + the most important property as it is used to programmatically detect specific error scenarios. This hopefully will not + be breaking, as the docs specified that named arguments should be used when constructing error objects. +- **BREAKING** The error and error list interfaces now accept `UnitEnum` instead of `BackedEnum` for error codes. + Although technically breaking, this will only affect your implementation if you have implemented these interfaces. All + concrete classes provided by this package have been updated. +- **BREAKING**: The key of an error can now be a enum - previously only strings were accepted. This is only breaking if + you have implemented the interface yourself. +- Updated `KeyedSetOfErrors` to handle error keys now being strings or enums. - **BREAKING**: The `Guid::make()` method will now convert a string that is a UUID to a UUID GUID. Previously it would use a string id. - **BREAKING**: Updated the `GuidTypeMap` class so that it now supports enum aliases, enum types, and UUID identifiers. diff --git a/docs/guide/toolkit/results.md b/docs/guide/toolkit/results.md index a90cdf9d..92b2f042 100644 --- a/docs/guide/toolkit/results.md +++ b/docs/guide/toolkit/results.md @@ -125,12 +125,12 @@ Alternatively, you can provide the `failed()` method with an error object or a l The error object provided by this package has the following properties: +- `code`: an enum that identifies the error and can be used to programmatically detect and handle specific errors. It + is intentionally an enum, because to programmatically detect specific errors, the codes need to be a defined list of + values. - `message`: a string message that describes the error. -- `code`: a backed enum that identifies error and can be used to programmatically detect and handle specific errors. It - is intentionally an enum, because if you need to detect specific errors, the codes need to be a defined list of - values - which is an enum. -- `key`: optionally, a key for the error. This can be used to group errors by keys, for example to group errors by - properties that exist on a command message. +- `key`: optionally, a key for the error. Use this to group errors by keys, for example to group errors by + properties that exist on a command message. The key can be a string or enum. Error objects _must_ be instantiated with either a code or a message. They are immutable, so you cannot change their properties after they have been created. @@ -181,7 +181,7 @@ The error list class is iterable, countable and has `isEmpty()` and `isNotEmpty( ### Keyed Error Sets If you are using the `key` property on error objects, we provide a keyed set of errors class that groups errors by their -key. +key. Keys can be strings or enums. To create one, provide error objects to its constructor: @@ -243,6 +243,16 @@ if ($errors->contains(CancelAttendeeTicketError::AlreadyCancelled)) { } ``` +If you only want to handle one error code at once, use the `code()` method on the errors list. This will return the first error code, or `null` if there are no error codes. + +```php +$explanation = match ($errors->code()) { + CancelAttendeeTicketError::AlreadyCancelled => 'The ticket has already been cancelled.', + CancelAttendeeTicketError::NotFound => 'The ticket was not found.', + default => null, +}; +``` + ## Exception We provide a `FailedResultException` that you can use to throw a result object: diff --git a/src/Contracts/Toolkit/Result/Error.php b/src/Contracts/Toolkit/Result/Error.php index b4455cfe..9b3239e7 100644 --- a/src/Contracts/Toolkit/Result/Error.php +++ b/src/Contracts/Toolkit/Result/Error.php @@ -12,16 +12,16 @@ namespace CloudCreativity\Modules\Contracts\Toolkit\Result; -use BackedEnum; +use UnitEnum; interface Error { /** * Get the error key. * - * @return string|null + * @return UnitEnum|string|null */ - public function key(): ?string; + public function key(): UnitEnum|string|null; /** * Get the error detail. @@ -33,15 +33,15 @@ public function message(): string; /** * Get the error code. * - * @return BackedEnum|null + * @return UnitEnum|null */ - public function code(): ?BackedEnum; + public function code(): ?UnitEnum; /** * Is the error the specified error code? * - * @param BackedEnum $code + * @param UnitEnum $code * @return bool */ - public function is(BackedEnum $code): bool; + public function is(UnitEnum $code): bool; } diff --git a/src/Contracts/Toolkit/Result/ListOfErrors.php b/src/Contracts/Toolkit/Result/ListOfErrors.php index 0044b8a6..26d35f2d 100644 --- a/src/Contracts/Toolkit/Result/ListOfErrors.php +++ b/src/Contracts/Toolkit/Result/ListOfErrors.php @@ -12,9 +12,9 @@ namespace CloudCreativity\Modules\Contracts\Toolkit\Result; -use BackedEnum; use Closure; use CloudCreativity\Modules\Contracts\Toolkit\Iterables\ListIterator; +use UnitEnum; /** * @extends ListIterator @@ -24,26 +24,33 @@ interface ListOfErrors extends ListIterator /** * Get the first error in the list, or the first matching error. * - * @param Closure(Error): bool|BackedEnum|null $matcher + * @param Closure(Error): bool|UnitEnum|null $matcher * @return Error|null */ - public function first(Closure|BackedEnum|null $matcher = null): ?Error; + public function first(Closure|UnitEnum|null $matcher = null): ?Error; /** * Does the list contain a matching error? * - * @param Closure(Error): bool|BackedEnum $matcher + * @param Closure(Error): bool|UnitEnum $matcher * @return bool */ - public function contains(Closure|BackedEnum $matcher): bool; + public function contains(Closure|UnitEnum $matcher): bool; /** * Get all the unique error codes in the list. * - * @return array + * @return array */ public function codes(): array; + /** + * Get the first error code in the list. + * + * @return UnitEnum|null + */ + public function code(): ?UnitEnum; + /** * Return a new instance with the provided error pushed on to the end of the list. * diff --git a/src/Toolkit/Loggable/ResultDecorator.php b/src/Toolkit/Loggable/ResultDecorator.php index 1ebda2c7..ea64f0bd 100644 --- a/src/Toolkit/Loggable/ResultDecorator.php +++ b/src/Toolkit/Loggable/ResultDecorator.php @@ -17,6 +17,8 @@ use CloudCreativity\Modules\Contracts\Toolkit\Result\Error; use CloudCreativity\Modules\Contracts\Toolkit\Result\Result; +use function CloudCreativity\Modules\Toolkit\enum_string; + final readonly class ResultDecorator implements ContextProvider { /** @@ -85,8 +87,10 @@ private function error(Error $error): array return $error->context(); } + $code = $error->code(); + return array_filter([ - 'code' => $error->code()?->value, + 'code' => $code ? enum_string($code) : null, 'key' => $error->key(), 'message' => $error->message(), ]); diff --git a/src/Toolkit/Result/Error.php b/src/Toolkit/Result/Error.php index 6dcc5fd5..37ad5ac0 100644 --- a/src/Toolkit/Result/Error.php +++ b/src/Toolkit/Result/Error.php @@ -12,28 +12,28 @@ namespace CloudCreativity\Modules\Toolkit\Result; -use BackedEnum; use CloudCreativity\Modules\Contracts\Toolkit\Result\Error as IError; use CloudCreativity\Modules\Toolkit\Contracts; +use UnitEnum; final readonly class Error implements IError { /** - * @var string|null + * @var UnitEnum|string|null */ - private ?string $key; + private UnitEnum|string|null $key; /** * Error constructor. * - * @param string|null $key + * @param UnitEnum|null $code * @param string $message - * @param BackedEnum|null $code + * @param UnitEnum|string|null $key */ public function __construct( - ?string $key = null, + private ?UnitEnum $code = null, private string $message = '', - private ?BackedEnum $code = null, + UnitEnum|string|null $key = null, ) { Contracts::assert(!empty($message) || $code !== null, 'Error must have a message or a code.'); $this->key = $key ?: null; @@ -42,7 +42,7 @@ public function __construct( /** * @inheritDoc */ - public function key(): ?string + public function key(): UnitEnum|string|null { return $this->key; } @@ -58,7 +58,7 @@ public function message(): string /** * @inheritDoc */ - public function code(): ?BackedEnum + public function code(): ?UnitEnum { return $this->code; } @@ -66,7 +66,7 @@ public function code(): ?BackedEnum /** * @inheritDoc */ - public function is(BackedEnum $code): bool + public function is(UnitEnum $code): bool { return $this->code === $code; } diff --git a/src/Toolkit/Result/KeyedSetOfErrors.php b/src/Toolkit/Result/KeyedSetOfErrors.php index e8dc2a78..bf77695a 100644 --- a/src/Toolkit/Result/KeyedSetOfErrors.php +++ b/src/Toolkit/Result/KeyedSetOfErrors.php @@ -16,6 +16,9 @@ use CloudCreativity\Modules\Contracts\Toolkit\Result\Error as IError; use CloudCreativity\Modules\Contracts\Toolkit\Result\ListOfErrors as IListOfErrors; use CloudCreativity\Modules\Toolkit\Iterables\IsKeyedSet; +use UnitEnum; + +use function CloudCreativity\Modules\Toolkit\enum_string; /** * @implements KeyedSet @@ -55,7 +58,7 @@ public function __construct(IError ...$errors) $this->stack[$key] = $this->get($key)->push($error); } - ksort($this->stack); + ksort($this->stack, SORT_STRING | SORT_FLAG_CASE); } /** @@ -105,11 +108,13 @@ public function keys(): array /** * Get errors by key. * - * @param string $key + * @param UnitEnum|string $key * @return IListOfErrors */ - public function get(string $key): IListOfErrors + public function get(UnitEnum|string $key): IListOfErrors { + $key = enum_string($key); + return $this->stack[$key] ?? new ListOfErrors(); } @@ -151,6 +156,8 @@ public function count(): int */ private function keyFor(IError $error): string { - return $error->key() ?? self::DEFAULT_KEY; + $key = $error->key(); + + return $key === null ? self::DEFAULT_KEY : enum_string($key); } } diff --git a/src/Toolkit/Result/ListOfErrors.php b/src/Toolkit/Result/ListOfErrors.php index 64651fba..c4bee207 100644 --- a/src/Toolkit/Result/ListOfErrors.php +++ b/src/Toolkit/Result/ListOfErrors.php @@ -12,11 +12,11 @@ namespace CloudCreativity\Modules\Toolkit\Result; -use BackedEnum; use Closure; use CloudCreativity\Modules\Contracts\Toolkit\Result\Error as IError; use CloudCreativity\Modules\Contracts\Toolkit\Result\ListOfErrors as IListOfErrors; use CloudCreativity\Modules\Toolkit\Iterables\IsList; +use UnitEnum; final class ListOfErrors implements IListOfErrors { @@ -24,17 +24,17 @@ final class ListOfErrors implements IListOfErrors use IsList; /** - * @param IListOfErrors|IError|BackedEnum|array|string $value + * @param IListOfErrors|IError|UnitEnum|array|string $value * @return self */ - public static function from(IListOfErrors|IError|BackedEnum|array|string $value): self + public static function from(IListOfErrors|IError|UnitEnum|array|string $value): self { return match(true) { $value instanceof self => $value, $value instanceof IListOfErrors, is_array($value) => new self(...$value), $value instanceof IError => new self($value), is_string($value) => new self(new Error(message: $value)), - $value instanceof BackedEnum => new self(new Error(code: $value)), + $value instanceof UnitEnum => new self(new Error(code: $value)), }; } @@ -49,13 +49,13 @@ public function __construct(IError ...$errors) /** * @inheritDoc */ - public function first(Closure|BackedEnum|null $matcher = null): ?IError + public function first(Closure|UnitEnum|null $matcher = null): ?IError { if ($matcher === null) { return $this->stack[0] ?? null; } - if ($matcher instanceof BackedEnum) { + if ($matcher instanceof UnitEnum) { $matcher = static fn (IError $error): bool => $error->is($matcher); } @@ -71,9 +71,9 @@ public function first(Closure|BackedEnum|null $matcher = null): ?IError /** * @inheritDoc */ - public function contains(Closure|BackedEnum $matcher): bool + public function contains(Closure|UnitEnum $matcher): bool { - if ($matcher instanceof BackedEnum) { + if ($matcher instanceof UnitEnum) { $matcher = static fn (IError $error): bool => $error->is($matcher); } @@ -104,6 +104,20 @@ public function codes(): array return $codes; } + /** + * @inheritDoc + */ + public function code(): ?UnitEnum + { + foreach ($this->stack as $error) { + if ($code = $error->code()) { + return $code; + } + } + + return null; + } + /** * @inheritDoc */ diff --git a/src/Toolkit/Result/Result.php b/src/Toolkit/Result/Result.php index 13236701..b8e45117 100644 --- a/src/Toolkit/Result/Result.php +++ b/src/Toolkit/Result/Result.php @@ -12,10 +12,10 @@ namespace CloudCreativity\Modules\Toolkit\Result; -use BackedEnum; use CloudCreativity\Modules\Contracts\Toolkit\Result\Error as IError; use CloudCreativity\Modules\Contracts\Toolkit\Result\ListOfErrors as IListOfErrors; use CloudCreativity\Modules\Contracts\Toolkit\Result\Result as IResult; +use UnitEnum; /** * @template TValue @@ -38,10 +38,10 @@ public static function ok(mixed $value = null): self /** * Return a failed result. * - * @param IListOfErrors|IError|BackedEnum|array|string $errorOrErrors + * @param IListOfErrors|IError|UnitEnum|array|string $errorOrErrors * @return Result */ - public static function failed(IListOfErrors|IError|BackedEnum|array|string $errorOrErrors): self + public static function failed(IListOfErrors|IError|UnitEnum|array|string $errorOrErrors): self { $errors = match(true) { $errorOrErrors instanceof IListOfErrors => $errorOrErrors, @@ -58,10 +58,10 @@ public static function failed(IListOfErrors|IError|BackedEnum|array|string $erro * * This is an alias for the `failed` method. * - * @param IListOfErrors|IError|BackedEnum|array|string $errorOrErrors + * @param IListOfErrors|IError|UnitEnum|array|string $errorOrErrors * @return Result */ - public static function fail(IListOfErrors|IError|BackedEnum|array|string $errorOrErrors): self + public static function fail(IListOfErrors|IError|UnitEnum|array|string $errorOrErrors): self { return self::failed($errorOrErrors); } diff --git a/tests/Unit/Toolkit/Loggable/ResultDecoratorTest.php b/tests/Unit/Toolkit/Loggable/ResultDecoratorTest.php index 3ba55f74..d6dfc2bb 100644 --- a/tests/Unit/Toolkit/Loggable/ResultDecoratorTest.php +++ b/tests/Unit/Toolkit/Loggable/ResultDecoratorTest.php @@ -18,12 +18,16 @@ use CloudCreativity\Modules\Contracts\Toolkit\Result\Error as IError; use CloudCreativity\Modules\Contracts\Toolkit\Result\Result as IResult; use CloudCreativity\Modules\Tests\TestBackedEnum; +use CloudCreativity\Modules\Tests\TestUnitEnum; use CloudCreativity\Modules\Toolkit\Loggable\ResultDecorator; use CloudCreativity\Modules\Toolkit\Loggable\SimpleContextFactory; use CloudCreativity\Modules\Toolkit\Result\Error; use CloudCreativity\Modules\Toolkit\Result\Result; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; +use UnitEnum; + +use function CloudCreativity\Modules\Toolkit\enum_string; /** * @extends IResult @@ -200,12 +204,12 @@ public function testFailureContextWithErrorThatOnlyHasMessage(string|Error $erro } /** - * @return array> + * @return array> */ public static function onlyCodeProvider(): array { return [ - [TestBackedEnum::Foo], + [TestUnitEnum::Baz], [new Error(code: TestBackedEnum::Bar)], ]; } @@ -215,13 +219,14 @@ public static function onlyCodeProvider(): array * @return void */ #[DataProvider('onlyCodeProvider')] - public function testFailureContextWithErrorThatOnlyHasCode(BackedEnum|Error $error): void + public function testFailureContextWithErrorThatOnlyHasCode(UnitEnum|Error $error): void { $result = Result::failed($error); + $code = $error instanceof UnitEnum ? $error : $error->code(); $expected = [ 'success' => false, - 'error' => $error instanceof BackedEnum ? $error->value : $error->code()?->value, + 'error' => enum_string($code ?? '!!'), ]; $this->assertSame($expected, (new ResultDecorator($result))->context()); diff --git a/tests/Unit/Toolkit/Result/ErrorTest.php b/tests/Unit/Toolkit/Result/ErrorTest.php index cda7b69a..046992f3 100644 --- a/tests/Unit/Toolkit/Result/ErrorTest.php +++ b/tests/Unit/Toolkit/Result/ErrorTest.php @@ -14,30 +14,25 @@ use CloudCreativity\Modules\Contracts\Toolkit\Result\Error as IError; use CloudCreativity\Modules\Tests\TestBackedEnum; +use CloudCreativity\Modules\Tests\TestUnitEnum; use CloudCreativity\Modules\Toolkit\ContractException; use CloudCreativity\Modules\Toolkit\Result\Error; use PHPUnit\Framework\TestCase; class ErrorTest extends TestCase { - /** - * @return void - */ public function test(): void { - $error = new Error(key: 'foo', message: 'Bar', code: TestBackedEnum::Foo); + $error = new Error(key: 'foo', message: 'Bar', code: TestUnitEnum::Baz); $this->assertInstanceOf(IError::class, $error); $this->assertSame('foo', $error->key()); $this->assertSame('Bar', $error->message()); - $this->assertSame(TestBackedEnum::Foo, $error->code()); - $this->assertTrue($error->is(TestBackedEnum::Foo)); - $this->assertFalse($error->is(TestBackedEnum::Bar)); + $this->assertSame(TestUnitEnum::Baz, $error->code()); + $this->assertTrue($error->is(TestUnitEnum::Baz)); + $this->assertFalse($error->is(TestUnitEnum::Bat)); } - /** - * @return void - */ public function testOnlyMessage(): void { $error = new Error(message: 'Hello World'); @@ -47,9 +42,6 @@ public function testOnlyMessage(): void $this->assertNull($error->code()); } - /** - * @return void - */ public function testOnlyCode(): void { $error = new Error(code: TestBackedEnum::Foo); @@ -59,9 +51,6 @@ public function testOnlyCode(): void $this->assertSame(TestBackedEnum::Foo, $error->code()); } - /** - * @return void - */ public function testNoMessageOrCode(): void { $this->expectException(ContractException::class); diff --git a/tests/Unit/Toolkit/Result/KeyedSetOfErrorsTest.php b/tests/Unit/Toolkit/Result/KeyedSetOfErrorsTest.php index 3ca56322..0d5f8337 100644 --- a/tests/Unit/Toolkit/Result/KeyedSetOfErrorsTest.php +++ b/tests/Unit/Toolkit/Result/KeyedSetOfErrorsTest.php @@ -12,6 +12,7 @@ namespace CloudCreativity\Modules\Tests\Unit\Toolkit\Result; +use CloudCreativity\Modules\Tests\TestUnitEnum; use CloudCreativity\Modules\Toolkit\Result\Error; use CloudCreativity\Modules\Toolkit\Result\KeyedSetOfErrors; use CloudCreativity\Modules\Toolkit\Result\ListOfErrors; @@ -25,26 +26,33 @@ class KeyedSetOfErrorsTest extends TestCase public function test(): void { $errors = new KeyedSetOfErrors( - $a = new Error('foo', 'Message A'), - $b = new Error('bar', 'Message B'), - $c = new Error('foo', 'Message C'), - $d = new Error(null, 'Message D'), - $e = new Error(null, 'Message E'), + $a = new Error(message: 'Message A', key: 'foo'), + $b = new Error(message: 'Message B', key: 'bar'), + $c = new Error(message: 'Message C', key: 'foo'), + $d = new Error(message: 'Message D'), + $e = new Error(message: 'Message E'), + $f = new Error(message: 'Message F', key: TestUnitEnum::Baz), + $g = new Error(message: 'Message G', key: TestUnitEnum::Bat), + $h = new Error(message: 'Message H', key: TestUnitEnum::Baz), ); $expected = [ '_base' => new ListOfErrors($d, $e), 'bar' => new ListOfErrors($b), + TestUnitEnum::Bat->name => new ListOfErrors($g), + TestUnitEnum::Baz->name => new ListOfErrors($f, $h), 'foo' => new ListOfErrors($a, $c), ]; $this->assertEquals($expected, iterator_to_array($errors)); $this->assertEquals($expected, $errors->all()); - $this->assertSame(['_base', 'bar', 'foo'], $errors->keys()); - $this->assertEquals(new ListOfErrors($d, $e, $b, $a, $c), $errors->toList()); - $this->assertCount(5, $errors); + $this->assertSame(['_base', 'bar', TestUnitEnum::Bat->name, TestUnitEnum::Baz->name, 'foo'], $errors->keys()); + $this->assertEquals(new ListOfErrors($d, $e, $b, $g, $f, $h, $a, $c), $errors->toList()); + $this->assertCount(8, $errors); $this->assertTrue($errors->isNotEmpty()); $this->assertFalse($errors->isEmpty()); + $this->assertEquals($expected['bar'], $errors->get('bar')); + $this->assertEquals($expected[TestUnitEnum::Baz->name], $errors->get(TestUnitEnum::Baz)); } /** @@ -65,12 +73,12 @@ public function testEmpty(): void public function testPutNewKey(): void { $original = new KeyedSetOfErrors( - $a = new Error('foo', 'Message A'), - $b = new Error('bar', 'Message B'), - $c = new Error('foo', 'Message C'), + $a = new Error(message: 'Message A', key: 'foo'), + $b = new Error(message: 'Message B', key: 'bar'), + $c = new Error(message: 'Message C', key: 'foo'), ); - $actual = $original->put($d = new Error('baz', 'Message D')); + $actual = $original->put($d = new Error(message: 'Message D', key: 'baz')); $this->assertNotSame($original, $actual); $this->assertEquals([ @@ -91,12 +99,12 @@ public function testPutNewKey(): void public function testPutExistingKey(): void { $original = new KeyedSetOfErrors( - $a = new Error('foo', 'Message A'), - $b = new Error('bar', 'Message B'), - $c = new Error('foo', 'Message C'), + $a = new Error(message: 'Message A', key: 'foo'), + $b = new Error(message: 'Message B', key: 'bar'), + $c = new Error(message: 'Message C', key: 'foo'), ); - $actual = $original->put($d = new Error('bar', 'Message D')); + $actual = $original->put($d = new Error(message: 'Message D', key: 'bar')); $this->assertNotSame($original, $actual); $this->assertEquals([ @@ -116,12 +124,12 @@ public function testPutExistingKey(): void public function testPutErrorWithoutKey1(): void { $original = new KeyedSetOfErrors( - $a = new Error('foo', 'Message A'), - $b = new Error('bar', 'Message B'), - $c = new Error('foo', 'Message C'), + $a = new Error(message: 'Message A', key: 'foo'), + $b = new Error(message: 'Message B', key: 'bar'), + $c = new Error(message: 'Message C', key: 'foo'), ); - $actual = $original->put($d = new Error(null, 'Message D')); + $actual = $original->put($d = new Error(message: 'Message D')); $this->assertNotSame($original, $actual); $this->assertEquals([ @@ -142,12 +150,12 @@ public function testPutErrorWithoutKey1(): void public function testPutErrorWithoutKey2(): void { $original = new KeyedSetOfErrors( - $a = new Error(null, 'Message A'), - $b = new Error('foo', 'Message B'), - $c = new Error(null, 'Message C'), + $a = new Error(message: 'Message A'), + $b = new Error(message: 'Message B', key: 'foo'), + $c = new Error(message: 'Message C'), ); - $actual = $original->put($d = new Error(null, 'Message D')); + $actual = $original->put($d = new Error(message: 'Message D')); $this->assertNotSame($original, $actual); $this->assertEquals([ @@ -167,16 +175,16 @@ public function testPutErrorWithoutKey2(): void public function testMerge(): void { $set1 = new KeyedSetOfErrors( - $a = new Error('foo', 'Message A'), - $b = new Error('bar', 'Message B'), - $c = new Error('foo', 'Message C'), + $a = new Error(message: 'Message A', key: 'foo'), + $b = new Error(message: 'Message B', key: 'bar'), + $c = new Error(message: 'Message C', key: 'foo'), ); $set2 = new KeyedSetOfErrors( - $d = new Error('bar', 'Message D'), - $e = new Error('baz', 'Message E'), - $f = new Error('bar', 'Message F'), - $g = new Error(null, 'Message G'), + $d = new Error(message: 'Message D', key: 'bar'), + $e = new Error(message: 'Message E', key: 'baz'), + $f = new Error(message: 'Message F', key: 'bar'), + $g = new Error(message: 'Message G'), ); $actual = $set1->merge($set2); diff --git a/tests/Unit/Toolkit/Result/ListOfErrorsTest.php b/tests/Unit/Toolkit/Result/ListOfErrorsTest.php index e405ea4a..b5da8588 100644 --- a/tests/Unit/Toolkit/Result/ListOfErrorsTest.php +++ b/tests/Unit/Toolkit/Result/ListOfErrorsTest.php @@ -15,6 +15,7 @@ use CloudCreativity\Modules\Contracts\Toolkit\Result\Error as IError; use CloudCreativity\Modules\Contracts\Toolkit\Result\ListOfErrors as IListOfErrors; use CloudCreativity\Modules\Tests\TestBackedEnum; +use CloudCreativity\Modules\Tests\TestUnitEnum; use CloudCreativity\Modules\Toolkit\Result\Error; use CloudCreativity\Modules\Toolkit\Result\KeyedSetOfErrors; use CloudCreativity\Modules\Toolkit\Result\ListOfErrors; @@ -106,14 +107,14 @@ public function testFirst(): void new Error(null, 'Message B'), $c = new Error(null, 'Message C'), new Error(null, 'Message D'), - $e = new Error(code: TestBackedEnum::Bar), + $e = new Error(code: TestUnitEnum::Bat), ); $this->assertSame($a, $errors->first()); $this->assertSame($c, $errors->first(fn (IError $error) => 'Message C' === $error->message())); - $this->assertSame($e, $errors->first(TestBackedEnum::Bar)); + $this->assertSame($e, $errors->first(TestUnitEnum::Bat)); $this->assertNull($errors->first(fn (IError $error) => 'Message E' === $error->message())); - $this->assertNull($errors->first(TestBackedEnum::Foo)); + $this->assertNull($errors->first(TestUnitEnum::Baz)); } /** @@ -125,27 +126,48 @@ public function testContains(): void new Error(message: 'Message A'), new Error(message: 'Message B'), new Error(message: 'Message C'), - new Error(message: 'Message D', code: TestBackedEnum::Foo), + new Error(message: 'Message D', code: TestUnitEnum::Baz), ); $this->assertTrue($errors->contains(fn (IError $error) => 'Message C' === $error->message())); - $this->assertTrue($errors->contains(TestBackedEnum::Foo)); + $this->assertTrue($errors->contains(TestUnitEnum::Baz)); $this->assertFalse($errors->contains(fn (IError $error) => 'Message E' === $error->message())); - $this->assertFalse($errors->contains(TestBackedEnum::Bar)); + $this->assertFalse($errors->contains(TestUnitEnum::Bat)); } - /** - * @return void - */ public function testCodes(): void { - $errors = new ListOfErrors( + $errors1 = new ListOfErrors( new Error(message: 'Message A'), new Error(message: 'Message B', code: TestBackedEnum::Foo), - new Error(message: 'Message C', code: TestBackedEnum::Bar), + new Error(message: 'Message C', code: TestUnitEnum::Baz), new Error(message: 'Message D', code: TestBackedEnum::Foo), ); - $this->assertSame([TestBackedEnum::Foo, TestBackedEnum::Bar], $errors->codes()); + $errors2 = new ListOfErrors( + new Error(message: 'Message E'), + new Error(message: 'Message F'), + ); + + $this->assertSame([TestBackedEnum::Foo, TestUnitEnum::Baz], $errors1->codes()); + $this->assertEmpty($errors2->codes()); + } + + public function testCode(): void + { + $errors1 = new ListOfErrors( + new Error(message: 'Message A'), + new Error(message: 'Message B', code: TestBackedEnum::Foo), + new Error(message: 'Message C', code: TestUnitEnum::Baz), + new Error(message: 'Message D', code: TestBackedEnum::Foo), + ); + + $errors2 = new ListOfErrors( + new Error(message: 'Message E'), + new Error(message: 'Message F'), + ); + + $this->assertSame(TestBackedEnum::Foo, $errors1->code()); + $this->assertNull($errors2->code()); } } diff --git a/tests/Unit/Toolkit/Result/ResultTest.php b/tests/Unit/Toolkit/Result/ResultTest.php index c17bf57f..95e27e74 100644 --- a/tests/Unit/Toolkit/Result/ResultTest.php +++ b/tests/Unit/Toolkit/Result/ResultTest.php @@ -15,6 +15,7 @@ use CloudCreativity\Modules\Contracts\Toolkit\Result\ListOfErrors as IListOfErrors; use CloudCreativity\Modules\Contracts\Toolkit\Result\Result as IResult; use CloudCreativity\Modules\Tests\TestBackedEnum; +use CloudCreativity\Modules\Tests\TestUnitEnum; use CloudCreativity\Modules\Toolkit\Result\Error; use CloudCreativity\Modules\Toolkit\Result\FailedResultException; use CloudCreativity\Modules\Toolkit\Result\ListOfErrors; @@ -100,7 +101,7 @@ public function testErrorWithMultipleErrors(): void { $errors = new ListOfErrors( new Error(code: TestBackedEnum::Foo), - new Error(code: TestBackedEnum::Bar), + new Error(code: TestUnitEnum::Baz), new Error(message: 'Message A'), new Error(message: 'Message B'), );