diff --git a/src/Mixins/Expectation.php b/src/Mixins/Expectation.php index 926200a8..4522155e 100644 --- a/src/Mixins/Expectation.php +++ b/src/Mixins/Expectation.php @@ -1142,6 +1142,22 @@ final class Expectation return $this; } + /** + * Asserts that the value is a ULID. + * + * @return self + */ + public function toBeUlid(string $message = ''): self + { + if (! is_string($this->value)) { + InvalidExpectationValue::expected('string'); + } + + Assert::assertTrue(Str::isUlid($this->value), $message); + + return $this; + } + /** * Asserts that the value is between 2 specified values * diff --git a/src/Support/Str.php b/src/Support/Str.php index 04f4b1fd..85ba4115 100644 --- a/src/Support/Str.php +++ b/src/Support/Str.php @@ -98,6 +98,14 @@ final class Str return preg_match('/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iD', $value) > 0; } + /** + * Determine if a given value is a valid ULID. + */ + public static function isUlid(string $value): bool + { + return preg_match('/^[0-9A-HJKMNP-TV-Z]{26}$/', $value) > 0; + } + /** * Creates a describe block as `$describeDescription` → `$testDescription` format. * diff --git a/tests/Features/Expect/toBeUlid.php b/tests/Features/Expect/toBeUlid.php new file mode 100644 index 00000000..0f4a0e67 --- /dev/null +++ b/tests/Features/Expect/toBeUlid.php @@ -0,0 +1,26 @@ +toBeUlid(); +})->throws(InvalidExpectationValue::class, 'Invalid expectation value type. Expected [string].'); + +test('pass', function () { + expect('01ARZ3NDEKTSV4RRFFQ69G5FAV')->toBeUlid(); + expect('01BX5ZZKBKACTAV9WEVGEMMVRE')->toBeUlid(); + expect('7ZZZZZZZZZ0000000000000000')->toBeUlid(); +}); + +test('failures', function () { + expect('foo')->toBeUlid(); +})->throws(ExpectationFailedException::class); + +test('failures with message', function () { + expect('bar')->toBeUlid('oh no!'); +})->throws(ExpectationFailedException::class, 'oh no!'); + +test('not failures', function () { + expect('foo')->not->toBeUlid(); +});