$headers * @param array|null $json */ public function __construct( public readonly int $status, public readonly array $headers, public readonly ?array $json, public readonly string $body, ) { } public function assertStatus(int $expectedStatus): self { Assert::assertSame($expectedStatus, $this->status, $this->body); return $this; } public function assertEnvelope(): self { Assert::assertIsArray($this->json, 'Expected a JSON response body. Raw body: ' . $this->body); Assert::assertArrayHasKey('success', $this->json); Assert::assertArrayHasKey('data', $this->json); Assert::assertArrayHasKey('meta', $this->json); Assert::assertArrayHasKey('includes', $this->json); Assert::assertArrayHasKey('content-type', $this->headers); Assert::assertStringContainsString('application/json', strtolower($this->headers['content-type'])); return $this; } public function assertSuccess(bool $expected = true): self { $this->assertEnvelope(); Assert::assertSame($expected, $this->json['success']); return $this; } public function assertError(): self { return $this->assertSuccess(false); } public function assertMessage(string $expectedMessage): self { $this->assertEnvelope(); Assert::assertIsArray($this->json['data']); Assert::assertSame($expectedMessage, $this->json['data']['message'] ?? null, $this->body); return $this; } public function assertMessageContains(string $expectedFragment): self { $this->assertEnvelope(); Assert::assertIsArray($this->json['data']); Assert::assertIsString($this->json['data']['message'] ?? null, $this->body); Assert::assertStringContainsString($expectedFragment, $this->json['data']['message'], $this->body); return $this; } /** * @param array $expectedPermissions */ public function assertMissingPermissions(array $expectedPermissions): self { $this->assertEnvelope(); Assert::assertIsArray($this->json['data']); Assert::assertSame('Missing permission(s)', $this->json['data']['message'] ?? null, $this->body); $actual = $this->json['data']['permissions'] ?? null; Assert::assertIsArray($actual); $expected = $expectedPermissions; sort($expected); sort($actual); Assert::assertSame($expected, $actual, $this->body); return $this; } public function data(): mixed { return $this->json['data'] ?? null; } public function meta(): array { $meta = $this->json['meta'] ?? []; return is_array($meta) ? $meta : []; } }