'stun:stun.relay.metered.ca:80', ], [ 'urls' => [ 'turn:standard.relay.metered.ca:80', 'turns:standard.relay.metered.ca:443', ], 'username' => 'testuser', 'credential' => 'testpass', ], ]; $validated = IceResponseValidator::validate($payload, 'Metered'); $this->assertCount(2, $validated); $this->assertEquals('stun:stun.relay.metered.ca:80', $validated[0]['urls']); $this->assertEquals('testuser', $validated[1]['username']); } public function test_validates_wrapped_ice_servers_payload(): void { $payload = [ 'iceServers' => [ [ 'urls' => 'stun:stun.convexsol.com:3478', ], ], ]; $validated = IceResponseValidator::validate($payload, 'ConvexSol'); $this->assertCount(1, $validated); $this->assertEquals('stun:stun.convexsol.com:3478', $validated[0]['urls']); } public function test_throws_exception_for_non_array_payload(): void { $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Metered API returned an invalid non-array response.'); IceResponseValidator::validate('invalid-string', 'Metered'); } public function test_throws_exception_for_empty_array_payload(): void { $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Metered API returned an empty or invalid list of ICE servers.'); IceResponseValidator::validate([], 'Metered'); } public function test_throws_exception_for_missing_urls_field(): void { $this->expectException(RuntimeException::class); $this->expectExceptionMessage("Metered server entry at index [0] is missing 'urls'."); IceResponseValidator::validate([ ['username' => 'someuser'], ], 'Metered'); } public function test_throws_exception_for_invalid_url_protocol(): void { $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Metered invalid ICE URL scheme [http://invalid.url] at index [0].'); IceResponseValidator::validate([ ['urls' => 'http://invalid.url'], ], 'Metered'); } }