First bunch of unit tests and resulting fixes.
This commit is contained in:
parent
78ca2dd426
commit
3fbbdd5b9a
30 changed files with 1855 additions and 13252 deletions
MakefileVERSIONcomposer.jsoncomposer.lockphpstan.neonphpunit.xml
dev
php702
php703
php704
php800
php801
php802
php803
php804
src/Utility
tests
8
Makefile
8
Makefile
|
@ -11,6 +11,9 @@ DEV_TARGETS := install update analyse analyze
|
|||
DEV_DIR := $(realpath dev)
|
||||
DEV_DIRS := $(wildcard ${DEV_DIR}/*)
|
||||
|
||||
VENDOR := $(realpath vendor)
|
||||
VENDOR_BIN := ${VENDOR}/bin
|
||||
|
||||
# We'll use the lowest common denominator to maintain the root packages
|
||||
PHP_702 := $(shell which php7.2)
|
||||
COMPOSER := $(shell which composer)
|
||||
|
@ -28,10 +31,13 @@ install:
|
|||
update:
|
||||
${PHP_702} ${COMPOSER} update
|
||||
|
||||
tests:
|
||||
${PHP_702} ${VENDOR_BIN}/phpunit
|
||||
|
||||
fix-perms: # WSL skill issues
|
||||
${SUDO} chown -R ${USER_NAME}:${GROUP_NAME} .
|
||||
|
||||
${DEV_DIRS}:
|
||||
${MAKE} -C $@ ${MAKECMDGOALS}
|
||||
|
||||
.PHONY: ${DEV_TARGETS} ${DEV_DIRS} fix-perms
|
||||
.PHONY: ${DEV_TARGETS} ${DEV_DIRS} fix-perms tests
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
v0.3.1
|
||||
v0.3.2
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
"guzzlehttp/guzzle": "~7.9",
|
||||
"guzzlehttp/psr7": "~2.7"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~8.5"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "flashwave",
|
||||
|
@ -28,5 +31,10 @@
|
|||
"files": [
|
||||
"polyfill.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"classmap": [
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
1434
composer.lock
generated
1434
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.12",
|
||||
"phpunit/phpunit": "~8.5"
|
||||
"phpstan/phpstan": "^1.12"
|
||||
}
|
||||
}
|
||||
|
|
1441
dev/php702/composer.lock
generated
1441
dev/php702/composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.12",
|
||||
"phpunit/phpunit": "~9.6"
|
||||
"phpstan/phpstan": "^1.12"
|
||||
}
|
||||
}
|
||||
|
|
1759
dev/php703/composer.lock
generated
1759
dev/php703/composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "~9.6"
|
||||
"phpstan/phpstan": "^2.1"
|
||||
}
|
||||
}
|
||||
|
|
1761
dev/php704/composer.lock
generated
1761
dev/php704/composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "~9.6"
|
||||
"phpstan/phpstan": "^2.1"
|
||||
}
|
||||
}
|
||||
|
|
1761
dev/php800/composer.lock
generated
1761
dev/php800/composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "~10.5"
|
||||
"phpstan/phpstan": "^2.1"
|
||||
}
|
||||
}
|
||||
|
|
1644
dev/php801/composer.lock
generated
1644
dev/php801/composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "~11.5"
|
||||
"phpstan/phpstan": "^2.1"
|
||||
}
|
||||
}
|
||||
|
|
1708
dev/php802/composer.lock
generated
1708
dev/php802/composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "~12.1"
|
||||
"phpstan/phpstan": "^2.1"
|
||||
}
|
||||
}
|
||||
|
|
1602
dev/php803/composer.lock
generated
1602
dev/php803/composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "~12.1"
|
||||
"phpstan/phpstan": "^2.1"
|
||||
}
|
||||
}
|
||||
|
|
1602
dev/php804/composer.lock
generated
1602
dev/php804/composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -7,6 +7,7 @@ parameters:
|
|||
reportUnmatchedIgnoredErrors: false
|
||||
paths:
|
||||
- src
|
||||
- tests
|
||||
- polyfill.php
|
||||
bootstrapFiles:
|
||||
- vendor/autoload.php
|
||||
|
|
15
phpunit.xml
Normal file
15
phpunit.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.5/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
colors="true"
|
||||
executionOrder="depends,defects"
|
||||
failOnRisky="true"
|
||||
failOnWarning="true">
|
||||
<testsuites>
|
||||
<testsuite name="default">
|
||||
<directory suffix="Test.php">tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
|
@ -22,7 +22,7 @@ final class ClaimsUtils {
|
|||
*/
|
||||
public static function filterIntOrFloatToInt($value): int {
|
||||
if(is_float($value))
|
||||
$value = (int)floor($value);
|
||||
$value = (int)$value;
|
||||
elseif(!is_int($value))
|
||||
throw new UnexpectedValueException('Value must be an integer or a float.');
|
||||
|
||||
|
|
|
@ -41,7 +41,8 @@ final class DateTimeUtils {
|
|||
return $dti;
|
||||
}
|
||||
|
||||
$dt = DateTimeImmutable::createFromFormat('Uv', $dt->format('Uv'));
|
||||
$tz = $dt->getTimezone();
|
||||
$dt = DateTimeImmutable::createFromFormat('U.u', $dt->format('U.u'), $tz === false ? null : $tz);
|
||||
if($dt === false)
|
||||
throw new UnexpectedValueException('Date/Time representation could not be converted.');
|
||||
|
||||
|
|
43
tests/NoneJwkSetTest.php
Normal file
43
tests/NoneJwkSetTest.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php declare(strict_types=1);
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use Railgun\Jwt\{NoneJwk,NoneJwkSet};
|
||||
|
||||
final class NoneJwkSetTest extends TestCase {
|
||||
public function testJsonSerialize(): void {
|
||||
$this->assertSame('{"keys":[]}', json_encode(new NoneJwkSet));
|
||||
}
|
||||
|
||||
public function testPhpSerialize(): void {
|
||||
$orig = new NoneJwkSet;
|
||||
$serz = serialize($orig);
|
||||
$this->assertSame("O:22:\"Railgun\Jwt\NoneJwkSet\":1:{s:27:\"\0Railgun\Jwt\NoneJwkSet\0jwk\";O:19:\"Railgun\Jwt\NoneJwk\":1:{s:23:\"\0Railgun\Jwt\NoneJwk\0id\";N;}}", $serz);
|
||||
|
||||
$saisei = unserialize($serz);
|
||||
$this->assertInstanceOf(NoneJwkSet::class, $saisei);
|
||||
}
|
||||
|
||||
public function testStaticValues(): void {
|
||||
$jwks = new NoneJwkSet;
|
||||
$this->assertSame($jwks, $jwks->getPublicKeySet());
|
||||
|
||||
$keys = $jwks->getKeys();
|
||||
$this->assertCount(1, $keys);
|
||||
$this->assertInstanceOf(NoneJwk::class, $keys[0]);
|
||||
|
||||
$key1 = $jwks->getKey();
|
||||
$this->assertInstanceOf(NoneJwk::class, $key1);
|
||||
|
||||
$key2 = $jwks->getKey(null, 'none');
|
||||
$this->assertInstanceOf(NoneJwk::class, $key2);
|
||||
|
||||
$this->assertSame($key1, $key2);
|
||||
$this->assertSame($key2, $keys[0]);
|
||||
}
|
||||
|
||||
public function testGetKeyFail(): void {
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionMessage('could not find a key that matched the requested algorithm');
|
||||
(new NoneJwkSet)->getKey('the', 'ES256K');
|
||||
}
|
||||
}
|
83
tests/NoneJwkTest.php
Normal file
83
tests/NoneJwkTest.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php declare(strict_types=1);
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use Railgun\Jwt\NoneJwk;
|
||||
|
||||
final class NoneJwkTest extends TestCase {
|
||||
/**
|
||||
* @return array{?string}[]
|
||||
*/
|
||||
public function keyIdDataProvider(): array {
|
||||
return [[null], ['none'], ['test'], ['the']];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider keyIdDataProvider
|
||||
*/
|
||||
public function testKeyId(?string $keyId): void {
|
||||
$this->assertSame($keyId, (new NoneJwk($keyId))->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider keyIdDataProvider
|
||||
*/
|
||||
public function testJsonSerialize(?string $keyId): void {
|
||||
$this->assertSame('null', json_encode(new NoneJwk($keyId)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{?string, string}[]
|
||||
*/
|
||||
public function phpSerializeDataProvider(): array {
|
||||
return [
|
||||
[null, "O:19:\"Railgun\Jwt\NoneJwk\":1:{s:23:\"\0Railgun\Jwt\NoneJwk\0id\";N;}"],
|
||||
['none', "O:19:\"Railgun\Jwt\NoneJwk\":1:{s:23:\"\0Railgun\Jwt\NoneJwk\0id\";s:4:\"none\";}"],
|
||||
['test', "O:19:\"Railgun\Jwt\NoneJwk\":1:{s:23:\"\0Railgun\Jwt\NoneJwk\0id\";s:4:\"test\";}"],
|
||||
['the', "O:19:\"Railgun\Jwt\NoneJwk\":1:{s:23:\"\0Railgun\Jwt\NoneJwk\0id\";s:3:\"the\";}"],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider phpSerializeDataProvider
|
||||
*/
|
||||
public function testPhpSerialize(?string $keyId, string $expect): void {
|
||||
$orig = new NoneJwk($keyId);
|
||||
$serz = serialize($orig);
|
||||
$this->assertSame($expect, $serz);
|
||||
|
||||
$saisei = unserialize($serz);
|
||||
$this->assertInstanceOf(NoneJwk::class, $saisei);
|
||||
$this->assertSame($keyId, $saisei->getId());
|
||||
}
|
||||
|
||||
public function testStaticValues(): void {
|
||||
$jwk = new NoneJwk;
|
||||
$this->assertSame('none', $jwk->getAlgorithm());
|
||||
$this->assertSame($jwk, $jwk->getPublicKey());
|
||||
}
|
||||
|
||||
public function testSignSuccess(): void {
|
||||
$jwk = new NoneJwk;
|
||||
$this->assertSame('', $jwk->sign(random_bytes(10)));
|
||||
$this->assertSame('', $jwk->sign(random_bytes(10), 'none'));
|
||||
}
|
||||
|
||||
public function testSignFail(): void {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('$algo must be null or the string "none"');
|
||||
(new NoneJwk)->sign(random_bytes(10), 'RS256');
|
||||
}
|
||||
|
||||
public function testVerifySuccess(): void {
|
||||
$jwk = new NoneJwk;
|
||||
$this->assertTrue($jwk->verify(random_bytes(10), ''));
|
||||
$this->assertFalse($jwk->verify(random_bytes(10), 'test'));
|
||||
$this->assertFalse($jwk->verify(random_bytes(10), random_bytes(10)));
|
||||
}
|
||||
|
||||
public function testVerifyFail(): void {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('$algo must be null or the string "none"');
|
||||
(new NoneJwk)->verify(random_bytes(10), '', 'RS256');
|
||||
}
|
||||
}
|
74
tests/Utility/ClaimsUtilsTest.php
Normal file
74
tests/Utility/ClaimsUtilsTest.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php declare(strict_types=1);
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use Railgun\Jwt\UnexpectedValueExceptionWithPayload;
|
||||
use Railgun\Jwt\Utility\ClaimsUtils;
|
||||
|
||||
final class ClaimsUtilsTest extends TestCase {
|
||||
public function testFilterIntOrFloatToIntSuccess(): void {
|
||||
$this->assertSame(20, ClaimsUtils::filterIntOrFloatToInt(20));
|
||||
$this->assertSame(-34534, ClaimsUtils::filterIntOrFloatToInt(-34534));
|
||||
$this->assertSame(12345, ClaimsUtils::filterIntOrFloatToInt(12345.67890));
|
||||
$this->assertSame(-9876, ClaimsUtils::filterIntOrFloatToInt(-9876.54321));
|
||||
}
|
||||
|
||||
public function testFilterIntOrFloatToIntFail(): void {
|
||||
$this->expectException(UnexpectedValueException::class);
|
||||
ClaimsUtils::filterIntOrFloatToInt('12345');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{object, }[]
|
||||
*/
|
||||
public function ensureIdenticalAndRetrieveDataProvider(): array {
|
||||
return [
|
||||
[
|
||||
(object)['iat' => 10000, 'nbf' => 10000.34234, 'exp' => 20000],
|
||||
['iat', 'nbf'],
|
||||
[ClaimsUtils::class, 'filterIntOrFloatToInt'],
|
||||
10000,
|
||||
],
|
||||
[
|
||||
(object)['iat' => 10000, 'nbf' => 11000.34234, 'exp' => 20000],
|
||||
['iat', 'nbf'],
|
||||
[ClaimsUtils::class, 'filterIntOrFloatToInt'],
|
||||
UnexpectedValueExceptionWithPayload::class,
|
||||
],
|
||||
[
|
||||
(object)['iat' => '10000', 'nbf' => 10000.34234, 'exp' => 20000],
|
||||
['iat', 'nbf'],
|
||||
[ClaimsUtils::class, 'filterIntOrFloatToInt'],
|
||||
UnexpectedValueExceptionWithPayload::class, // it should get converted to this!
|
||||
],
|
||||
[
|
||||
(object)['iat' => 12000, 'exp' => 20000],
|
||||
['iat', 'nbf'],
|
||||
[ClaimsUtils::class, 'filterIntOrFloatToInt'],
|
||||
12000,
|
||||
],
|
||||
[
|
||||
(object)['exp' => 20000],
|
||||
['iat', 'nbf'],
|
||||
[ClaimsUtils::class, 'filterIntOrFloatToInt'],
|
||||
null,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $claims
|
||||
* @param callable(mixed, string): mixed $filter
|
||||
* @param mixed $expect
|
||||
* @dataProvider ensureIdenticalAndRetrieveDataProvider
|
||||
*/
|
||||
public function testEnsureIdenticalAndRetrieve(object $payload, array $claims, $filter, $expect): void {
|
||||
$expectIsThrowable = is_string($expect) && class_exists($expect) && is_subclass_of($expect, Throwable::class);
|
||||
if($expectIsThrowable)
|
||||
$this->expectException($expect);
|
||||
|
||||
$result = ClaimsUtils::ensureIdenticalAndRetrieve($payload, $claims, $filter);
|
||||
|
||||
if(!$expectIsThrowable)
|
||||
$this->assertSame($expect, $result);
|
||||
}
|
||||
}
|
42
tests/Utility/DateTimeUtilsTest.php
Normal file
42
tests/Utility/DateTimeUtilsTest.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php declare(strict_types=1);
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use Railgun\Jwt\Utility\DateTimeUtils;
|
||||
|
||||
final class DateTimeUtilsTest extends TestCase {
|
||||
public function testEnsureImmutable(): void {
|
||||
// passthru
|
||||
$orig = new DateTimeImmutable('now');
|
||||
$imm = DateTimeUtils::ensureImmutable($orig);
|
||||
$this->assertSame($orig, $imm);
|
||||
|
||||
// integer timestamps
|
||||
$orig = time();
|
||||
$imm = DateTimeUtils::ensureImmutable($orig);
|
||||
$this->assertInstanceOf(DateTimeImmutable::class, $imm);
|
||||
$this->assertSame(date(DATE_ATOM, $orig), $imm->format(DATE_ATOM));
|
||||
|
||||
// interface
|
||||
$orig = new DateTime('now');
|
||||
$imm = DateTimeUtils::ensureImmutable($orig);
|
||||
$this->assertNotSame($orig, $imm);
|
||||
$this->assertInstanceOf(DateTimeImmutable::class, $imm);
|
||||
$this->assertSame($orig->format('Uu'), $imm->format('Uu'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public function ensureImmutableArgNameExceptionDataProvider(): array {
|
||||
return [['dt'], ['validAfter'], ['expiredAt']];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ensureImmutableArgNameExceptionDataProvider
|
||||
*/
|
||||
public function testEnsureImmutableArgNameException(string $argName): void {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage(sprintf('$%s must be an integer or a subclass of DateTimeInterface', $argName));
|
||||
DateTimeUtils::ensureImmutable(null, $argName); // @phpstan-ignore-line
|
||||
}
|
||||
}
|
54
tests/Utility/ExceptionUtilsTest.php
Normal file
54
tests/Utility/ExceptionUtilsTest.php
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?php declare(strict_types=1);
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use Railgun\Jwt\{
|
||||
InvalidArgumentExceptionWithPayload,
|
||||
RuntimeExceptionWithPayload,
|
||||
UnexpectedValueExceptionWithPayload,
|
||||
WithPayload
|
||||
};
|
||||
use Railgun\Jwt\Utility\ExceptionUtils;
|
||||
|
||||
final class ExceptionUtilsTest extends TestCase {
|
||||
/**
|
||||
* @return array{Throwable, class-string<Throwable>, object, bool}[]
|
||||
*/
|
||||
public function ensureWithPayloadDataProvider(): array {
|
||||
$payload = new stdClass;
|
||||
$payload->hello = 'world';
|
||||
|
||||
$other = new stdClass;
|
||||
$other->goodbye = 'seeyou';
|
||||
|
||||
return [
|
||||
[new InvalidArgumentException('test', 1234), InvalidArgumentExceptionWithPayload::class, $payload, false],
|
||||
[new UnexpectedValueException('test', 5678), UnexpectedValueExceptionWithPayload::class, $payload, false],
|
||||
[new RuntimeException('test', 0xF00F), RuntimeExceptionWithPayload::class, $payload, false],
|
||||
[new LogicException('test', 0, new InvalidArgumentException('inner')), RuntimeExceptionWithPayload::class, $payload, false],
|
||||
[new Exception('test'), RuntimeExceptionWithPayload::class, $payload, false],
|
||||
[new InvalidArgumentExceptionWithPayload($payload, 'test'), InvalidArgumentExceptionWithPayload::class, $payload, true],
|
||||
[new UnexpectedValueExceptionWithPayload($payload, 'test'), UnexpectedValueExceptionWithPayload::class, $payload, true],
|
||||
[new RuntimeExceptionWithPayload($payload, 'test'), RuntimeExceptionWithPayload::class, $payload, true],
|
||||
[new RuntimeExceptionWithPayload($other, 'test'), RuntimeExceptionWithPayload::class, $payload, false],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string<Throwable> $expect
|
||||
* @dataProvider ensureWithPayloadDataProvider
|
||||
*/
|
||||
public function testEnsureWithPayload(Throwable $original, string $expect, object $payload, bool $expectPassthru): void {
|
||||
$converted = ExceptionUtils::ensureWithPayload($payload, $original);
|
||||
|
||||
$this->assertInstanceOf($expect, $converted);
|
||||
$this->assertSame($payload, $converted->getPayload());
|
||||
$this->assertSame($original->getMessage(), $converted->getMessage());
|
||||
$this->assertSame($original->getCode(), $converted->getCode());
|
||||
if($expectPassthru)
|
||||
$this->assertSame($original, $converted);
|
||||
else {
|
||||
$this->assertSame($original, $converted->getPrevious());
|
||||
$this->assertSame($original->getPrevious(), $converted->getPrevious()->getPrevious());
|
||||
}
|
||||
}
|
||||
}
|
36
tests/Utility/JsonTest.php
Normal file
36
tests/Utility/JsonTest.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php declare(strict_types=1);
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use Railgun\Jwt\Utility\Json;
|
||||
|
||||
final class JsonTest extends TestCase {
|
||||
public function testDecodeAssoc(): void {
|
||||
$this->assertIsObject(Json::decode('{"test":"obj"}', false));
|
||||
$this->assertIsArray(Json::decode('{"test":"array"}', true));
|
||||
}
|
||||
|
||||
public function testEnsureDecodeThrows(): void {
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionCode(JSON_ERROR_SYNTAX);
|
||||
Json::decode('beans');
|
||||
}
|
||||
|
||||
public function testEnsureEncodeThrowsOnResource(): void {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
Json::encode(fopen('php://memory', 'rb'));
|
||||
}
|
||||
|
||||
public function testEnsureEncodeWithoutEscapedSlashes(): void {
|
||||
$this->assertSame('"there/are/no/wrong/slashes"', Json::encode('there/are/no/wrong/slashes'));
|
||||
}
|
||||
|
||||
public function testEnsureEncodeThrows(): void {
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionCode(JSON_ERROR_RECURSION);
|
||||
|
||||
$obj = new class { /** @var object */ public $obj; };
|
||||
$obj->obj = $obj;
|
||||
|
||||
Json::encode($obj);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue