Skip to content

Commit e9fc6cb

Browse files
committed
test: add toObject equivalence tests against json_encode/json_decode
Verify that the toObject() helper produces structurally identical output to json_decode(json_encode()) across 17 edge cases: scalars, empty arrays, sequential/associative arrays, non-sequential integer keys, deeply nested structures, and mixed types.
1 parent 7fcbf72 commit e9fc6cb

1 file changed

Lines changed: 57 additions & 0 deletions

File tree

tests/Unit/OpenApiResponseValidatorTest.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,19 @@
44

55
namespace Studio\OpenApiContractTesting\Tests\Unit;
66

7+
use const JSON_THROW_ON_ERROR;
8+
79
use InvalidArgumentException;
10+
use PHPUnit\Framework\Attributes\DataProvider;
811
use PHPUnit\Framework\Attributes\Test;
912
use PHPUnit\Framework\TestCase;
13+
use ReflectionMethod;
1014
use Studio\OpenApiContractTesting\OpenApiResponseValidator;
1115
use Studio\OpenApiContractTesting\OpenApiSpecLoader;
1216

1317
use function array_map;
1418
use function count;
19+
use function json_encode;
1520
use function range;
1621

1722
class OpenApiResponseValidatorTest extends TestCase
@@ -32,6 +37,42 @@ protected function tearDown(): void
3237
parent::tearDown();
3338
}
3439

40+
// ========================================
41+
// toObject equivalence tests
42+
// ========================================
43+
44+
/**
45+
* @return iterable<string, array{mixed}>
46+
*/
47+
public static function provideTo_object_matches_json_roundtripCases(): iterable
48+
{
49+
yield 'null' => [null];
50+
yield 'string' => ['hello'];
51+
yield 'integer' => [42];
52+
yield 'float' => [3.14];
53+
yield 'boolean true' => [true];
54+
yield 'boolean false' => [false];
55+
yield 'empty array' => [[]];
56+
yield 'sequential array' => [[1, 2, 3]];
57+
yield 'associative array' => [['key' => 'value', 'num' => 1]];
58+
yield 'nested associative' => [['a' => ['b' => ['c' => 'deep']]]];
59+
yield 'list of objects' => [[['id' => 1, 'name' => 'a'], ['id' => 2, 'name' => 'b']]];
60+
yield 'non-sequential int keys' => [[1 => 'a', 3 => 'b']];
61+
yield 'mixed nested' => [
62+
[
63+
'users' => [
64+
['id' => 1, 'tags' => ['admin', 'user'], 'meta' => ['active' => true]],
65+
],
66+
'total' => 1,
67+
'filters' => [],
68+
],
69+
];
70+
yield 'numeric string keys' => [['200' => ['description' => 'OK']]];
71+
yield 'deeply nested list' => [[[['a']]]];
72+
yield 'null in array' => [[null, 'a', null]];
73+
yield 'empty nested object' => [['data' => []]];
74+
}
75+
3576
// ========================================
3677
// OAS 3.0 tests
3778
// ========================================
@@ -744,4 +785,20 @@ public function v30_strip_prefixes_applied(): void
744785
$this->assertTrue($result->isValid());
745786
$this->assertSame('/v1/pets', $result->matchedPath());
746787
}
788+
789+
#[Test]
790+
#[DataProvider('provideTo_object_matches_json_roundtripCases')]
791+
public function to_object_matches_json_roundtrip(mixed $input): void
792+
{
793+
$method = new ReflectionMethod(OpenApiResponseValidator::class, 'toObject');
794+
795+
$actual = $method->invoke(null, $input);
796+
797+
// Re-encode both to JSON to compare structural equivalence
798+
// without relying on object identity (assertSame fails on stdClass).
799+
$expectedJson = json_encode($input, JSON_THROW_ON_ERROR);
800+
$actualJson = (string) json_encode($actual, JSON_THROW_ON_ERROR);
801+
802+
$this->assertSame($expectedJson, $actualJson);
803+
}
747804
}

0 commit comments

Comments
 (0)