mirror of
https://github.com/pestphp/pest.git
synced 2026-07-21 17:10:03 +02:00
feat: validation expectations (#1762)
* feat: add validation expectations for IP, MAC, hostname, domain, base64, and hexadecimal * improvements * improvements
This commit is contained in:
@@ -1219,4 +1219,104 @@ final class Expectation
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the value is an IP address.
|
||||||
|
*
|
||||||
|
* @return self<TValue>
|
||||||
|
*/
|
||||||
|
public function toBeIpAddress(string $message = ''): self
|
||||||
|
{
|
||||||
|
if (! is_string($this->value)) {
|
||||||
|
InvalidExpectationValue::expected('string');
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert::assertTrue((bool) filter_var($this->value, FILTER_VALIDATE_IP), $message);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the value is a MAC address.
|
||||||
|
*
|
||||||
|
* @return self<TValue>
|
||||||
|
*/
|
||||||
|
public function toBeMacAddress(string $message = ''): self
|
||||||
|
{
|
||||||
|
if (! is_string($this->value)) {
|
||||||
|
InvalidExpectationValue::expected('string');
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert::assertTrue((bool) filter_var($this->value, FILTER_VALIDATE_MAC), $message);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the value is a hostname.
|
||||||
|
*
|
||||||
|
* @return self<TValue>
|
||||||
|
*/
|
||||||
|
public function toBeHostname(string $message = ''): self
|
||||||
|
{
|
||||||
|
if (! is_string($this->value)) {
|
||||||
|
InvalidExpectationValue::expected('string');
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert::assertTrue((bool) filter_var($this->value, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME), $message);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the value is a domain name.
|
||||||
|
*
|
||||||
|
* @return self<TValue>
|
||||||
|
*/
|
||||||
|
public function toBeDomain(string $message = ''): self
|
||||||
|
{
|
||||||
|
if (! is_string($this->value)) {
|
||||||
|
InvalidExpectationValue::expected('string');
|
||||||
|
}
|
||||||
|
|
||||||
|
$isValid = filter_var($this->value, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) !== false
|
||||||
|
&& str_contains($this->value, '.');
|
||||||
|
|
||||||
|
Assert::assertTrue($isValid, $message);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the value is a base64-encoded string.
|
||||||
|
*
|
||||||
|
* @return self<TValue>
|
||||||
|
*/
|
||||||
|
public function toBeBase64(string $message = ''): self
|
||||||
|
{
|
||||||
|
if (! is_string($this->value)) {
|
||||||
|
InvalidExpectationValue::expected('string');
|
||||||
|
}
|
||||||
|
|
||||||
|
$decoded = base64_decode($this->value, true);
|
||||||
|
Assert::assertTrue($decoded !== false && base64_encode($decoded) === $this->value, $message);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the value is a hexadecimal string.
|
||||||
|
*
|
||||||
|
* @return self<TValue>
|
||||||
|
*/
|
||||||
|
public function toBeHexadecimal(string $message = ''): self
|
||||||
|
{
|
||||||
|
if (! is_string($this->value)) {
|
||||||
|
InvalidExpectationValue::expected('string');
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert::assertTrue(ctype_xdigit($this->value), $message);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Pest\Exceptions\InvalidExpectationValue;
|
||||||
|
use PHPUnit\Framework\ExpectationFailedException;
|
||||||
|
|
||||||
|
test('pass', function (): void {
|
||||||
|
expect('Zm9v')->toBeBase64() // 'foo' (no padding)
|
||||||
|
->and('Zm9vYg==')->toBeBase64() // 'foob' (with padding)
|
||||||
|
->and('Zm9vYmE=')->toBeBase64() // 'fooba' (with padding)
|
||||||
|
->and('Zm9vYmFy')->toBeBase64() // 'foobar' (no padding)
|
||||||
|
->and('')->toBeBase64(); // empty string
|
||||||
|
});
|
||||||
|
|
||||||
|
test('failures', function (): void {
|
||||||
|
expect('not-base64!')->toBeBase64();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
|
|
||||||
|
test('failures with invalid padding', function (): void {
|
||||||
|
expect('AAA')->toBeBase64();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
|
|
||||||
|
test('failures with padding only', function (): void {
|
||||||
|
expect('====')->toBeBase64();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
|
|
||||||
|
test('failures with malformed input', function (): void {
|
||||||
|
expect('!!invalid!!')->toBeBase64();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
|
|
||||||
|
test('failures with invalid type', function (): void {
|
||||||
|
expect([])->toBeBase64();
|
||||||
|
})->throws(InvalidExpectationValue::class, 'Invalid expectation value type. Expected [string].');
|
||||||
|
|
||||||
|
test('failures with custom message', function (): void {
|
||||||
|
expect('!!invalid!!')->toBeBase64('oh no!');
|
||||||
|
})->throws(ExpectationFailedException::class, 'oh no!');
|
||||||
|
|
||||||
|
test('not failures', function (): void {
|
||||||
|
expect('Zm9v')->not->toBeBase64();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Pest\Exceptions\InvalidExpectationValue;
|
||||||
|
use PHPUnit\Framework\ExpectationFailedException;
|
||||||
|
|
||||||
|
test('pass', function (): void {
|
||||||
|
expect('example.com')->toBeDomain() // standard domain
|
||||||
|
->and('sub.example.com')->toBeDomain() // subdomain
|
||||||
|
->and('my-host.io')->toBeDomain() // with hyphen
|
||||||
|
->and('example.co.uk')->toBeDomain(); // multi-level TLD
|
||||||
|
});
|
||||||
|
|
||||||
|
test('failures', function (): void {
|
||||||
|
expect('example')->toBeDomain();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
|
|
||||||
|
test('failures with leading dot', function (): void {
|
||||||
|
expect('.example.com')->toBeDomain();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
|
|
||||||
|
test('failures with invalid type', function (): void {
|
||||||
|
expect([])->toBeDomain();
|
||||||
|
})->throws(InvalidExpectationValue::class, 'Invalid expectation value type. Expected [string].');
|
||||||
|
|
||||||
|
test('failures with custom message', function (): void {
|
||||||
|
expect('example')->toBeDomain('oh no!');
|
||||||
|
})->throws(ExpectationFailedException::class, 'oh no!');
|
||||||
|
|
||||||
|
test('not failures', function (): void {
|
||||||
|
expect('example.com')->not->toBeDomain();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Pest\Exceptions\InvalidExpectationValue;
|
||||||
|
use PHPUnit\Framework\ExpectationFailedException;
|
||||||
|
|
||||||
|
test('pass', function (): void {
|
||||||
|
expect('abcdef')->toBeHexadecimal() // lowercase
|
||||||
|
->and('ABCDEF')->toBeHexadecimal() // uppercase
|
||||||
|
->and('aBcDeF')->toBeHexadecimal() // mixed case
|
||||||
|
->and('1234567890')->toBeHexadecimal() // numeric
|
||||||
|
->and('deadbeef')->toBeHexadecimal(); // alphanumeric
|
||||||
|
});
|
||||||
|
|
||||||
|
test('failures', function (): void {
|
||||||
|
expect('xyz')->toBeHexadecimal();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
|
|
||||||
|
test('failures with invalid characters', function (): void {
|
||||||
|
expect('ghijkl')->toBeHexadecimal();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
|
|
||||||
|
test('failures with zero-x prefix', function (): void {
|
||||||
|
expect('0x1a')->toBeHexadecimal();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
|
|
||||||
|
test('failures with empty string', function (): void {
|
||||||
|
expect('')->toBeHexadecimal();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
|
|
||||||
|
test('failures with invalid type', function (): void {
|
||||||
|
expect([])->toBeHexadecimal();
|
||||||
|
})->throws(InvalidExpectationValue::class, 'Invalid expectation value type. Expected [string].');
|
||||||
|
|
||||||
|
test('failures with custom message', function (): void {
|
||||||
|
expect('xyz')->toBeHexadecimal('oh no!');
|
||||||
|
})->throws(ExpectationFailedException::class, 'oh no!');
|
||||||
|
|
||||||
|
test('not failures', function (): void {
|
||||||
|
expect('abcdef')->not->toBeHexadecimal();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Pest\Exceptions\InvalidExpectationValue;
|
||||||
|
use PHPUnit\Framework\ExpectationFailedException;
|
||||||
|
|
||||||
|
test('pass', function (): void {
|
||||||
|
expect('example')->toBeHostname() // single label
|
||||||
|
->and('example.com')->toBeHostname() // multiple labels
|
||||||
|
->and('sub.example.com')->toBeHostname() // subdomain
|
||||||
|
->and('my-host')->toBeHostname(); // with hyphen
|
||||||
|
});
|
||||||
|
|
||||||
|
test('failures', function (): void {
|
||||||
|
expect('-example')->toBeHostname();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
|
|
||||||
|
test('failures with trailing hyphen', function (): void {
|
||||||
|
expect('example-')->toBeHostname();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
|
|
||||||
|
test('failures with invalid type', function (): void {
|
||||||
|
expect([])->toBeHostname();
|
||||||
|
})->throws(InvalidExpectationValue::class, 'Invalid expectation value type. Expected [string].');
|
||||||
|
|
||||||
|
test('failures with custom message', function (): void {
|
||||||
|
expect('-example')->toBeHostname('oh no!');
|
||||||
|
})->throws(ExpectationFailedException::class, 'oh no!');
|
||||||
|
|
||||||
|
test('not failures', function (): void {
|
||||||
|
expect('example.com')->not->toBeHostname();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Pest\Exceptions\InvalidExpectationValue;
|
||||||
|
use PHPUnit\Framework\ExpectationFailedException;
|
||||||
|
|
||||||
|
test('pass', function (): void {
|
||||||
|
expect('192.168.1.1')->toBeIpAddress()
|
||||||
|
->and('::1')->toBeIpAddress()
|
||||||
|
->and('2001:db8::1')->toBeIpAddress();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('failures', function (): void {
|
||||||
|
expect('not-an-ip')->toBeIpAddress();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
|
|
||||||
|
test('failures with invalid type', function (): void {
|
||||||
|
expect([])->toBeIpAddress();
|
||||||
|
})->throws(InvalidExpectationValue::class, 'Invalid expectation value type. Expected [string].');
|
||||||
|
|
||||||
|
test('failures with custom message', function (): void {
|
||||||
|
expect('not-an-ip')->toBeIpAddress('oh no!');
|
||||||
|
})->throws(ExpectationFailedException::class, 'oh no!');
|
||||||
|
|
||||||
|
test('not failures', function (): void {
|
||||||
|
expect('192.168.1.1')->not->toBeIpAddress();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Pest\Exceptions\InvalidExpectationValue;
|
||||||
|
use PHPUnit\Framework\ExpectationFailedException;
|
||||||
|
|
||||||
|
test('pass', function (): void {
|
||||||
|
expect('00:1a:2b:3c:4d:5e')->toBeMacAddress() // colon-separated
|
||||||
|
->and('00-1a-2b-3c-4d-5e')->toBeMacAddress() // hyphen-separated
|
||||||
|
->and('ff:ff:ff:ff:ff:ff')->toBeMacAddress();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('failures', function (): void {
|
||||||
|
expect('not-a-mac')->toBeMacAddress();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
|
|
||||||
|
test('failures with invalid type', function (): void {
|
||||||
|
expect([])->toBeMacAddress();
|
||||||
|
})->throws(InvalidExpectationValue::class, 'Invalid expectation value type. Expected [string].');
|
||||||
|
|
||||||
|
test('failures with custom message', function (): void {
|
||||||
|
expect('not-a-mac')->toBeMacAddress('oh no!');
|
||||||
|
})->throws(ExpectationFailedException::class, 'oh no!');
|
||||||
|
|
||||||
|
test('not failures', function (): void {
|
||||||
|
expect('00:1a:2b:3c:4d:5e')->not->toBeMacAddress();
|
||||||
|
})->throws(ExpectationFailedException::class);
|
||||||
Reference in New Issue
Block a user