diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index ff6973bd..bc3bb2f9 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -15,6 +15,9 @@
src
+
+
+
@@ -23,9 +26,15 @@
test/Verify
+
+ test/Account
+
test/Application
+
+ test/Conversion
+
test/Verify2
@@ -74,6 +83,9 @@
test/Users
+
+ test/Insights
+
diff --git a/src/Account/ClientFactory.php b/src/Account/ClientFactory.php
index 38695a7e..2f7e45e7 100644
--- a/src/Account/ClientFactory.php
+++ b/src/Account/ClientFactory.php
@@ -7,7 +7,6 @@
use Psr\Container\ContainerInterface;
use Vonage\Client\APIResource;
use Vonage\Client\Credentials\Handler\BasicHandler;
-use Vonage\Client\Credentials\Handler\BasicQueryHandler;
class ClientFactory
{
diff --git a/src/Client.php b/src/Client.php
index 9a62ec28..95eb3410 100644
--- a/src/Client.php
+++ b/src/Client.php
@@ -544,6 +544,17 @@ public function __get($name)
return $this->factory->get($name);
}
+ public function getDebug(): mixed
+ {
+ return $this->debug;
+ }
+
+ public function setDebug(mixed $debug): Client
+ {
+ $this->debug = $debug;
+ return $this;
+ }
+
/**
* @deprecated Use the Verify Client, this shouldn't be here and will be removed.
*/
diff --git a/src/Client/APIExceptionHandler.php b/src/Client/APIExceptionHandler.php
index 4ac58888..0581eae5 100644
--- a/src/Client/APIExceptionHandler.php
+++ b/src/Client/APIExceptionHandler.php
@@ -25,6 +25,11 @@ public function setRfc7807Format(string $format): void
$this->rfc7807Format = $format;
}
+ public function getRfc7807Format(): string
+ {
+ return $this->rfc7807Format;
+ }
+
/**
* @throws Exception\Exception
*
diff --git a/test/Account/ClientFactoryTest.php b/test/Account/ClientFactoryTest.php
index a2f9267f..06d94a4f 100644
--- a/test/Account/ClientFactoryTest.php
+++ b/test/Account/ClientFactoryTest.php
@@ -4,37 +4,30 @@
namespace VonageTest\Account;
-use VonageTest\VonageTestCase;
-use Vonage\Account\ClientFactory;
+use PHPUnit\Framework\TestCase;
use Vonage\Client;
use Vonage\Client\APIResource;
use Vonage\Client\Factory\MapFactory;
+use Vonage\Account\ClientFactory;
-class ClientFactoryTest extends VonageTestCase
+class ClientFactoryTest extends TestCase
{
- /**
- * @var MapFactory
- */
- protected $mapFactory;
-
- protected $vonageClient;
-
- public function setUp(): void
+ public function testInvokeCreatesClientWithConfiguredApiResource(): void
{
- $this->vonageClient = $this->prophesize(Client::class);
- $this->vonageClient->getRestUrl()->willReturn('https://rest.nexmo.com');
- $this->vonageClient->getApiUrl()->willReturn('https://api.nexmo.com');
+ $mockServices = [
+ 'account' => ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
- /** @noinspection PhpParamsInspection */
- $this->mapFactory = new MapFactory([APIResource::class => APIResource::class], $this->vonageClient->reveal());
- }
-
- public function testURIsAreCorrect(): void
- {
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
$factory = new ClientFactory();
- $client = $factory($this->mapFactory);
- $this->assertSame('/account', $client->getAPIResource()->getBaseUri());
- $this->assertSame('https://rest.nexmo.com', $client->getAPIResource()->getBaseUrl());
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\Account\Client::class, $result);
+ $this->assertEquals('/account', $result->getAPIResource()->getBaseUri());
+ $this->assertInstanceOf(Client\Credentials\Handler\BasicHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertFalse($result->getAPIResource()->isHAL());
}
}
diff --git a/test/Application/ClientFactoryTest.php b/test/Application/ClientFactoryTest.php
new file mode 100644
index 00000000..08ae2a1e
--- /dev/null
+++ b/test/Application/ClientFactoryTest.php
@@ -0,0 +1,33 @@
+ ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
+
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\Application\Client::class, $result);
+ $this->assertEquals('/v2/applications', $result->getAPIResource()->getBaseUri());
+ $this->assertInstanceOf(Client\Credentials\Handler\BasicHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertEquals('applications', $result->getAPIResource()->getCollectionName());
+ }
+}
diff --git a/test/Client/CallbackTest.php b/test/Client/CallbackTest.php
new file mode 100644
index 00000000..e0c42c9c
--- /dev/null
+++ b/test/Client/CallbackTest.php
@@ -0,0 +1,97 @@
+ 'value1',
+ 'key2' => 'value2',
+ ];
+
+ $_POST = [
+ 'key3' => 'value3',
+ 'key4' => 'value4',
+ ];
+ }
+
+ public function testConstructorWithMissingKeys(): void
+ {
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage('missing expected callback keys: key1, key2');
+
+ $callback = new class (['key3' => 'value3']) extends Callback {
+ protected array $expected = ['key1', 'key2'];
+ };
+ }
+
+ public function testConstructorWithAllKeys(): void
+ {
+ $callback = new class ([
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ ]) extends Callback {
+ protected array $expected = ['key1', 'key2'];
+ };
+
+ $this->assertSame([
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ ], $callback->getData());
+ }
+
+ public function testFromEnvPost(): void
+ {
+ $callback = Callback::fromEnv(Callback::ENV_POST);
+
+ $this->assertInstanceOf(Callback::class, $callback);
+ $this->assertSame([
+ 'key3' => 'value3',
+ 'key4' => 'value4',
+ ], $callback->getData());
+ }
+
+ public function testFromEnvGet(): void
+ {
+ $callback = Callback::fromEnv(Callback::ENV_GET);
+
+ $this->assertInstanceOf(Callback::class, $callback);
+ $this->assertSame([
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ ], $callback->getData());
+ }
+
+ public function testFromEnvAll(): void
+ {
+ $callback = Callback::fromEnv(Callback::ENV_ALL);
+
+ $this->assertInstanceOf(Callback::class, $callback);
+ $this->assertSame([
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ 'key3' => 'value3',
+ 'key4' => 'value4',
+ ], $callback->getData());
+ }
+
+ public function testFromEnvInvalidSource(): void
+ {
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage('invalid source: invalid');
+
+ Callback::fromEnv('invalid');
+ }
+}
diff --git a/test/ClientTest.php b/test/ClientTest.php
index e99c53ac..a8a30fbe 100644
--- a/test/ClientTest.php
+++ b/test/ClientTest.php
@@ -4,7 +4,10 @@
namespace VonageTest;
+use Lcobucci\JWT\Token\Plain;
+use Psr\Http\Client\ClientInterface;
use RuntimeException;
+use Vonage\Client\Credentials\CredentialsInterface;
use VonageTest\VonageTestCase;
use Vonage\Client;
use Vonage\Client\Credentials\Basic;
@@ -22,4 +25,90 @@ public function testCallingVideoWithoutPackageGeneratesRuntimeError(): void
$client = new Client(new Basic('abcd', '1234'));
$video = $client->video();
}
+
+ public function testConstructorWithValidClient()
+ {
+ $credentials = $this->createMock(Basic::class);
+ $httpClient = $this->createMock(ClientInterface::class);
+ $options = ['debug' => true];
+
+ $client = new Client($credentials, $options, $httpClient);
+
+ $this->assertInstanceOf(Client::class, $client);
+ $this->assertTrue($client->getDebug());
+ }
+
+ public function testConstructorWithoutHttpClientUsesDefault()
+ {
+ $credentials = $this->createMock(Basic::class);
+ $options = ['debug' => true];
+
+ $client = new Client($credentials, $options);
+
+ $this->assertInstanceOf(Client::class, $client);
+ $this->assertInstanceOf(ClientInterface::class, $client->getHttpClient());
+ }
+
+ public function testConstructorThrowsExceptionOnInvalidCredentials()
+ {
+ $invalidCredentials = $this->createMock(CredentialsInterface::class);
+
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage('unknown credentials type');
+
+ new Client($invalidCredentials);
+ }
+
+ public function testConstructorWithCustomOptions()
+ {
+ $credentials = $this->createMock(Basic::class);
+ $options = [
+ 'base_rest_url' => 'https://example-rest.com',
+ 'base_api_url' => 'https://example-api.com',
+ 'debug' => true
+ ];
+
+ $client = new Client($credentials, $options);
+
+ $this->assertEquals('https://example-rest.com', $client->getRestUrl());
+ $this->assertEquals('https://example-api.com', $client->getApiUrl());
+ $this->assertTrue($client->getDebug());
+ }
+
+ public function testConstructorHandlesDeprecationsOption()
+ {
+ $credentials = $this->createMock(Basic::class);
+ $options = ['show_deprecations' => true];
+
+ $client = new Client($credentials, $options);
+
+ // No specific assertion for error handler setup, but ensuring no exceptions occurred.
+ $this->assertInstanceOf(Client::class, $client);
+ }
+
+ public function testConstructorHandlesVideoClientFactory()
+ {
+ $credentials = $this->createMock(Basic::class);
+
+ if (class_exists('Vonage\Video\ClientFactory')) {
+ $this->markTestSkipped('Vonage Video ClientFactory class is available.');
+ }
+
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage('Please install @vonage/video to use the Video API');
+
+ $client = new Client($credentials);
+ $client->video();
+ }
+
+ public function testWillGenerateJwt()
+ {
+ $keyPath = __DIR__ . '/Client/Credentials/test.key';
+ $keyContents = file_get_contents($keyPath);
+ $credentials = new Client\Credentials\Keypair($keyContents, 'abc123');
+ $client = new Client($credentials);
+ $jwt = $client->generateJwt();
+
+ $this->assertInstanceOf(Plain::class, $jwt);
+ }
}
diff --git a/test/Conversation/ClientFactoryTest.php b/test/Conversation/ClientFactoryTest.php
new file mode 100644
index 00000000..1e791a64
--- /dev/null
+++ b/test/Conversation/ClientFactoryTest.php
@@ -0,0 +1,34 @@
+ ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
+
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\Conversation\Client::class, $result);
+ $this->assertEquals('https://api.nexmo.com/v1/conversations', $result->getAPIResource()->getBaseUrl());
+ $this->assertInstanceOf(Client\Credentials\Handler\KeypairHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertFalse($result->getAPIResource()->errorsOn200());
+ $this->assertTrue($result->getAPIResource()->isHAL());
+ }
+}
diff --git a/test/Conversion/ClientFactoryTest.php b/test/Conversion/ClientFactoryTest.php
new file mode 100644
index 00000000..95a82789
--- /dev/null
+++ b/test/Conversion/ClientFactoryTest.php
@@ -0,0 +1,32 @@
+ ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
+
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\Conversion\Client::class, $result);
+ $this->assertEquals('/conversions/', $result->getAPIResource()->getBaseUri());
+ $this->assertInstanceOf(Client\Credentials\Handler\BasicHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ }
+}
diff --git a/test/Conversion/ClientTest.php b/test/Conversion/ClientTest.php
index ca1d0b9b..0fd6dbeb 100644
--- a/test/Conversion/ClientTest.php
+++ b/test/Conversion/ClientTest.php
@@ -137,4 +137,15 @@ public function testVoiceWithoutTimestamp(): void
$this->conversionClient->voice('ABC123', true);
}
+
+ public function testExceptionHandler(): void
+ {
+ $this->expectException(Client\Exception\Request::class);
+
+ $this->vonageClient->send(Argument::that(function (RequestInterface $request) {
+ return true;
+ }))->willReturn($this->getResponse('error', 402));
+
+ $this->conversionClient->sms('ABC123', true, '123456');
+ }
}
diff --git a/test/Insights/ClientFactoryTest.php b/test/Insights/ClientFactoryTest.php
new file mode 100644
index 00000000..a03eddee
--- /dev/null
+++ b/test/Insights/ClientFactoryTest.php
@@ -0,0 +1,32 @@
+ ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
+
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\Insights\Client::class, $result);
+ $this->assertInstanceOf(Client\Credentials\Handler\BasicHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertFalse($result->getAPIResource()->isHAL());
+ }
+}
diff --git a/test/Meetings/ClientFactoryTest.php b/test/Meetings/ClientFactoryTest.php
new file mode 100644
index 00000000..a72be078
--- /dev/null
+++ b/test/Meetings/ClientFactoryTest.php
@@ -0,0 +1,35 @@
+ ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
+
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = @$factory($container);
+ $this->assertInstanceOf(\Vonage\Meetings\Client::class, $result);
+ $this->assertInstanceOf(Client\Credentials\Handler\KeypairHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertEquals('https://api-eu.vonage.com/v1/meetings/', $result->getAPIResource()->getBaseUrl());
+ $this->assertInstanceOf(ExceptionErrorHandler::class, $result->getAPIResource()->getExceptionErrorHandler());
+ }
+}
diff --git a/test/Messages/ClientFactoryTest.php b/test/Messages/ClientFactoryTest.php
new file mode 100644
index 00000000..f304e6b8
--- /dev/null
+++ b/test/Messages/ClientFactoryTest.php
@@ -0,0 +1,38 @@
+ ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
+
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\Messages\Client::class, $result);
+ $this->assertInstanceOf(Client\Credentials\Handler\KeypairHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertInstanceOf(Client\Credentials\Handler\BasicHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[1]);
+ $this->assertEquals('/v1/messages', $result->getAPIResource()->getBaseUrl());
+ $this->assertInstanceOf(ExceptionErrorHandler::class, $result->getAPIResource()->getExceptionErrorHandler());
+ $this->assertFalse($result->getAPIResource()->isHAL());
+ $this->assertFalse($result->getAPIResource()->errorsOn200());
+ }
+}
diff --git a/test/NumberVerification/ClientFactoryTest.php b/test/NumberVerification/ClientFactoryTest.php
new file mode 100644
index 00000000..7318ed38
--- /dev/null
+++ b/test/NumberVerification/ClientFactoryTest.php
@@ -0,0 +1,44 @@
+createMock(Client::class);
+
+ $mockServices = [
+ 'numberVerification' => ClientFactory::class,
+ APIResource::class => APIResource::class,
+ Client::class => fn () => $mockClient,
+ ];
+
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\NumberVerification\Client::class, $result);
+ $this->assertInstanceOf(Client\Credentials\Handler\NumberVerificationGnpHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertFalse($result->getAPIResource()->isHAL());
+ $this->assertFalse($result->getAPIResource()->errorsOn200());
+ $this->assertEquals('https://api-eu.vonage.com/camara/number-verification/v031', $result->getAPIResource()
+ ->getBaseUrl());
+
+ $this->assertEquals('https://oidc.idp.vonage.com/oauth2/auth', $result->getAPIResource()->getAuthHandlers()[0]->getBaseUrl());
+ $this->assertEquals('https://api-eu.vonage.com/oauth2/token', $result->getAPIResource()->getAuthHandlers()
+ [0]->getTokenUrl());
+ $this->assertEquals('openid+dpv:FraudPreventionAndDetection#number-verification-verify-read',
+ $result->getAPIResource()->getAuthHandlers()[0]->getScope());
+
+ }
+}
diff --git a/test/Numbers/ClientFactoryTest.php b/test/Numbers/ClientFactoryTest.php
new file mode 100644
index 00000000..480a4f7a
--- /dev/null
+++ b/test/Numbers/ClientFactoryTest.php
@@ -0,0 +1,32 @@
+ ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
+
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\Numbers\Client::class, $result);
+ $this->assertInstanceOf(Client\Credentials\Handler\BasicHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertFalse($result->getAPIResource()->isHAL());
+ }
+}
diff --git a/test/ProactiveConnect/ClientFactoryTest.php b/test/ProactiveConnect/ClientFactoryTest.php
new file mode 100644
index 00000000..9bee923e
--- /dev/null
+++ b/test/ProactiveConnect/ClientFactoryTest.php
@@ -0,0 +1,34 @@
+ ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
+
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\ProactiveConnect\Client::class, $result);
+ $this->assertInstanceOf(Client\Credentials\Handler\KeypairHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertEquals('https://api-eu.vonage.com/v0.1/bulk/', $result->getAPIResource()->getBaseUrl());
+ $this->assertFalse($result->getAPIResource()->isHAL());
+ $this->assertFalse($result->getAPIResource()->errorsOn200());
+ }
+}
diff --git a/test/Redact/ClientFactoryTest.php b/test/Redact/ClientFactoryTest.php
new file mode 100644
index 00000000..97759178
--- /dev/null
+++ b/test/Redact/ClientFactoryTest.php
@@ -0,0 +1,33 @@
+ ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
+
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\Redact\Client::class, $result);
+ $this->assertInstanceOf(Client\Credentials\Handler\BasicHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertEquals('%s - %s. See %s for more information', $result->getApiResource()
+ ->getExceptionErrorHandler()->getRfc7807Format());
+ }
+}
diff --git a/test/SMS/ClientFactoryTest.php b/test/SMS/ClientFactoryTest.php
new file mode 100644
index 00000000..38933d7e
--- /dev/null
+++ b/test/SMS/ClientFactoryTest.php
@@ -0,0 +1,36 @@
+ ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
+
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\Sms\Client::class, $result);
+ $this->assertInstanceOf(Client\Credentials\Handler\BasicHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertInstanceOf(Client\Credentials\Handler\SignatureBodyHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[1]);
+ $this->assertFalse($result->getAPIResource()->isHAL());
+ $this->assertTrue($result->getAPIResource()->errorsOn200());
+ $this->assertEquals('messages', $result->getAPIResource()->getCollectionName());
+ }
+}
diff --git a/test/Secrets/ClientFactoryTest.php b/test/Secrets/ClientFactoryTest.php
new file mode 100644
index 00000000..d1c5d42d
--- /dev/null
+++ b/test/Secrets/ClientFactoryTest.php
@@ -0,0 +1,58 @@
+createMock(FactoryInterface::class);
+
+ // Mock the APIResource
+ $apiResourceMock = $this->createMock(APIResource::class);
+
+ // Configure the factory to return the mocked APIResource
+ $factoryMock->expects($this->once())
+ ->method('make')
+ ->with(APIResource::class)
+ ->willReturn($apiResourceMock);
+
+ // Expect the methods on APIResource to be called with specific parameters
+ $apiResourceMock->expects($this->once())
+ ->method('setBaseUri')
+ ->with('/accounts')
+ ->willReturnSelf();
+
+ $apiResourceMock->expects($this->once())
+ ->method('setAuthHandlers')
+ ->with($this->isInstanceOf(BasicHandler::class))
+ ->willReturnSelf();
+
+ $apiResourceMock->expects($this->once())
+ ->method('setCollectionName')
+ ->with('secrets')
+ ->willReturnSelf();
+
+ // Create an instance of the ClientFactory
+ $clientFactory = new ClientFactory();
+
+ // Call the __invoke method and retrieve the Client
+ $client = $clientFactory($factoryMock);
+
+ // Assert that the result is an instance of the Client
+ $this->assertInstanceOf(Client::class, $client);
+
+ // Assert that the Client has the correctly configured APIResource (optional, if Client exposes it)
+ $this->assertSame($apiResourceMock, $client->getApiResource());
+ }
+}
diff --git a/test/SimSwap/ClientFactoryTest.php b/test/SimSwap/ClientFactoryTest.php
new file mode 100644
index 00000000..c894f0ad
--- /dev/null
+++ b/test/SimSwap/ClientFactoryTest.php
@@ -0,0 +1,40 @@
+createMock(Client::class);
+
+ $mockServices = [
+ 'simSwap' => ClientFactory::class,
+ APIResource::class => APIResource::class,
+ Client::class => fn () => $mockClient,
+ ];
+
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\SimSwap\Client::class, $result);
+ $this->assertInstanceOf(Client\Credentials\Handler\SimSwapGnpHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertFalse($result->getAPIResource()->isHAL());
+ $this->assertFalse($result->getAPIResource()->errorsOn200());
+ $this->assertEquals('https://api-eu.vonage.com/camara/sim-swap/v040', $result->getAPIResource()
+ ->getBaseUrl());
+ $this->assertEquals('https://api-eu.vonage.com/oauth2/bc-authorize', $result->getAPIResource()->getAuthHandlers()[0]->getBaseUrl());
+ $this->assertEquals('https://api-eu.vonage.com/oauth2/token', $result->getAPIResource()->getAuthHandlers()
+ [0]->getTokenUrl());
+ }
+}
diff --git a/test/Users/ClientFactoryTest.php b/test/Users/ClientFactoryTest.php
new file mode 100644
index 00000000..17a61069
--- /dev/null
+++ b/test/Users/ClientFactoryTest.php
@@ -0,0 +1,32 @@
+ ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
+
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\Users\Client::class, $result);
+ $this->assertInstanceOf(Client\Credentials\Handler\KeypairHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertEquals('users', $result->getAPIResource()->getCollectionName());
+ }
+}
diff --git a/test/Verify/ClientFactoryTest.php b/test/Verify/ClientFactoryTest.php
new file mode 100644
index 00000000..f1b823d9
--- /dev/null
+++ b/test/Verify/ClientFactoryTest.php
@@ -0,0 +1,35 @@
+ ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
+
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\Verify\Client::class, $result);
+ $this->assertInstanceOf(Client\Credentials\Handler\TokenBodyHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertEquals('/verify', $result->getAPIResource()->getBaseUri());
+ $this->assertTrue($result->getApiResource()->errorsOn200());
+ $this->assertInstanceOf(ExceptionErrorHandler::class, $result->getAPIResource()->getExceptionErrorHandler());
+ }
+}
diff --git a/test/Verify2/ClientFactoryTest.php b/test/Verify2/ClientFactoryTest.php
new file mode 100644
index 00000000..b5e68391
--- /dev/null
+++ b/test/Verify2/ClientFactoryTest.php
@@ -0,0 +1,36 @@
+ ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
+
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\Verify2\Client::class, $result);
+ $this->assertInstanceOf(Client\Credentials\Handler\KeypairHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertInstanceOf(Client\Credentials\Handler\BasicHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[1]);
+ $this->assertEquals('https://api.nexmo.com/v2/verify', $result->getAPIResource()->getBaseUrl());
+ $this->assertFalse($result->getApiResource()->errorsOn200());
+ $this->assertFalse($result->getApiResource()->isHAL());
+ }
+}
diff --git a/test/Verify2/VerifyObjects/TemplateFragmentTest.php b/test/Verify2/VerifyObjects/TemplateFragmentTest.php
new file mode 100644
index 00000000..0c8494d8
--- /dev/null
+++ b/test/Verify2/VerifyObjects/TemplateFragmentTest.php
@@ -0,0 +1,61 @@
+ 'value1', 'key2' => 'value2'];
+ $templateFragment = new TemplateFragment($data);
+
+ $this->assertSame($data, $templateFragment->toArray());
+ }
+
+ public function testPropertyGetAndSet()
+ {
+ $templateFragment = new TemplateFragment();
+ $templateFragment->key1 = 'value1';
+
+ $this->assertSame('value1', $templateFragment->key1);
+ $this->assertNull($templateFragment->key2);
+ }
+
+ public function testPropertyIsset()
+ {
+ $templateFragment = new TemplateFragment(['key1' => 'value1']);
+
+ $this->assertTrue(isset($templateFragment->key1));
+ $this->assertFalse(isset($templateFragment->key2));
+ }
+
+ public function testFromArrayHydratesData()
+ {
+ $data = ['key1' => 'value1', 'key2' => 'value2'];
+ $templateFragment = new TemplateFragment();
+ $templateFragment->fromArray($data);
+
+ $this->assertSame($data, $templateFragment->toArray());
+ }
+
+ public function testToArrayReturnsData()
+ {
+ $data = ['key1' => 'value1', 'key2' => 'value2'];
+ $templateFragment = new TemplateFragment($data);
+
+ $this->assertSame($data, $templateFragment->toArray());
+ }
+
+ public function testChainingWhenSettingProperties()
+ {
+ $templateFragment = new TemplateFragment();
+ $result = $templateFragment->__set('key1', 'value1');
+
+ $this->assertInstanceOf(TemplateFragment::class, $result);
+ }
+}
diff --git a/test/Verify2/VerifyObjects/TemplateTest.php b/test/Verify2/VerifyObjects/TemplateTest.php
new file mode 100644
index 00000000..524a5029
--- /dev/null
+++ b/test/Verify2/VerifyObjects/TemplateTest.php
@@ -0,0 +1,61 @@
+ 'value1', 'key2' => 'value2'];
+ $template = new Template($data);
+
+ $this->assertSame($data, $template->toArray());
+ }
+
+ public function testPropertyGetAndSet()
+ {
+ $template = new Template();
+ $template->key1 = 'value1';
+
+ $this->assertSame('value1', $template->key1);
+ $this->assertNull($template->key2);
+ }
+
+ public function testPropertyIsset()
+ {
+ $template = new Template(['key1' => 'value1']);
+
+ $this->assertTrue(isset($template->key1));
+ $this->assertFalse(isset($template->key2));
+ }
+
+ public function testFromArrayHydratesData()
+ {
+ $data = ['key1' => 'value1', 'key2' => 'value2'];
+ $template = new Template();
+ $template->fromArray($data);
+
+ $this->assertSame($data, $template->toArray());
+ }
+
+ public function testToArrayReturnsData()
+ {
+ $data = ['key1' => 'value1', 'key2' => 'value2'];
+ $template = new Template($data);
+
+ $this->assertSame($data, $template->toArray());
+ }
+
+ public function testChainingWhenSettingProperties()
+ {
+ $template = new Template();
+ $result = $template->__set('key1', 'value1');
+
+ $this->assertInstanceOf(Template::class, $result);
+ }
+}
diff --git a/test/Verify2/VerifyObjects/VerificationLocaleTest.php b/test/Verify2/VerifyObjects/VerificationLocaleTest.php
new file mode 100644
index 00000000..4dc8e4b8
--- /dev/null
+++ b/test/Verify2/VerifyObjects/VerificationLocaleTest.php
@@ -0,0 +1,39 @@
+assertSame('en-us', $locale->getCode());
+ }
+
+ public function testGetCodeReturnsCorrectValue()
+ {
+ $locale = new VerificationLocale('fr-fr');
+ $this->assertSame('fr-fr', $locale->getCode());
+ }
+
+ public function testSetCodeUpdatesCode()
+ {
+ $locale = new VerificationLocale();
+ $locale->setCode('es-es');
+
+ $this->assertSame('es-es', $locale->getCode());
+ }
+
+ public function testSetCodeReturnsSelfForChaining()
+ {
+ $locale = new VerificationLocale();
+ $result = $locale->setCode('it-it');
+
+ $this->assertInstanceOf(VerificationLocale::class, $result);
+ }
+}
\ No newline at end of file
diff --git a/test/Verify2/VerifyObjects/VerifyEventTest.php b/test/Verify2/VerifyObjects/VerifyEventTest.php
new file mode 100644
index 00000000..474db6e4
--- /dev/null
+++ b/test/Verify2/VerifyObjects/VerifyEventTest.php
@@ -0,0 +1,62 @@
+ 'completed', 'timestamp' => '2025-01-01T00:00:00Z'];
+ $event = new VerifyEvent($data);
+
+ $this->assertSame($data, $event->toArray());
+ }
+
+ public function testPropertyGetAndSet()
+ {
+ $event = new VerifyEvent(['eventType' => 'started']);
+ $event->timestamp = '2025-01-01T00:00:00Z';
+
+ $this->assertSame('started', $event->eventType);
+ $this->assertSame('2025-01-01T00:00:00Z', $event->timestamp);
+ $this->assertNull($event->unknownProperty);
+ }
+
+ public function testPropertyIsset()
+ {
+ $event = new VerifyEvent(['eventType' => 'completed']);
+
+ $this->assertTrue(isset($event->eventType));
+ $this->assertFalse(isset($event->timestamp));
+ }
+
+ public function testFromArrayHydratesData()
+ {
+ $data = ['eventType' => 'completed', 'timestamp' => '2025-01-01T00:00:00Z'];
+ $event = new VerifyEvent([]);
+ $event->fromArray($data);
+
+ $this->assertSame($data, $event->toArray());
+ }
+
+ public function testToArrayReturnsData()
+ {
+ $data = ['eventType' => 'started', 'timestamp' => '2025-01-01T00:00:00Z'];
+ $event = new VerifyEvent($data);
+
+ $this->assertSame($data, $event->toArray());
+ }
+
+ public function testChainingWhenSettingProperties()
+ {
+ $event = new VerifyEvent([]);
+ $result = $event->__set('eventType', 'completed');
+
+ $this->assertInstanceOf(VerifyEvent::class, $result);
+ }
+}
diff --git a/test/Verify2/VerifyObjects/VerifySilentAuthEventTest.php b/test/Verify2/VerifyObjects/VerifySilentAuthEventTest.php
new file mode 100644
index 00000000..80297327
--- /dev/null
+++ b/test/Verify2/VerifyObjects/VerifySilentAuthEventTest.php
@@ -0,0 +1,62 @@
+ 'completed', 'timestamp' => '2025-01-01T00:00:00Z'];
+ $event = new VerifySilentAuthEvent($data);
+
+ $this->assertSame($data, $event->toArray());
+ }
+
+ public function testPropertyGetAndSet()
+ {
+ $event = new VerifySilentAuthEvent(['eventType' => 'started']);
+ $event->timestamp = '2025-01-01T00:00:00Z';
+
+ $this->assertSame('started', $event->eventType);
+ $this->assertSame('2025-01-01T00:00:00Z', $event->timestamp);
+ $this->assertNull($event->unknownProperty);
+ }
+
+ public function testPropertyIsset()
+ {
+ $event = new VerifySilentAuthEvent(['eventType' => 'completed']);
+
+ $this->assertTrue(isset($event->eventType));
+ $this->assertFalse(isset($event->timestamp));
+ }
+
+ public function testFromArrayHydratesData()
+ {
+ $data = ['eventType' => 'completed', 'timestamp' => '2025-01-01T00:00:00Z'];
+ $event = new VerifySilentAuthEvent([]);
+ $event->fromArray($data);
+
+ $this->assertSame($data, $event->toArray());
+ }
+
+ public function testToArrayReturnsData()
+ {
+ $data = ['eventType' => 'started', 'timestamp' => '2025-01-01T00:00:00Z'];
+ $event = new VerifySilentAuthEvent($data);
+
+ $this->assertSame($data, $event->toArray());
+ }
+
+ public function testChainingWhenSettingProperties()
+ {
+ $event = new VerifySilentAuthEvent([]);
+ $result = $event->__set('eventType', 'completed');
+
+ $this->assertInstanceOf(VerifySilentAuthEvent::class, $result);
+ }
+}
diff --git a/test/Verify2/VerifyObjects/VerifyStatusUpdateTest.php b/test/Verify2/VerifyObjects/VerifyStatusUpdateTest.php
new file mode 100644
index 00000000..5913da92
--- /dev/null
+++ b/test/Verify2/VerifyObjects/VerifyStatusUpdateTest.php
@@ -0,0 +1,62 @@
+ 'completed', 'timestamp' => '2025-01-01T00:00:00Z'];
+ $event = new VerifyStatusUpdate($data);
+
+ $this->assertSame($data, $event->toArray());
+ }
+
+ public function testPropertyGetAndSet()
+ {
+ $event = new VerifyStatusUpdate(['eventType' => 'started']);
+ $event->timestamp = '2025-01-01T00:00:00Z';
+
+ $this->assertSame('started', $event->eventType);
+ $this->assertSame('2025-01-01T00:00:00Z', $event->timestamp);
+ $this->assertNull($event->unknownProperty);
+ }
+
+ public function testPropertyIsset()
+ {
+ $event = new VerifyStatusUpdate(['eventType' => 'completed']);
+
+ $this->assertTrue(isset($event->eventType));
+ $this->assertFalse(isset($event->timestamp));
+ }
+
+ public function testFromArrayHydratesData()
+ {
+ $data = ['eventType' => 'completed', 'timestamp' => '2025-01-01T00:00:00Z'];
+ $event = new VerifyStatusUpdate([]);
+ $event->fromArray($data);
+
+ $this->assertSame($data, $event->toArray());
+ }
+
+ public function testToArrayReturnsData()
+ {
+ $data = ['eventType' => 'started', 'timestamp' => '2025-01-01T00:00:00Z'];
+ $event = new VerifyStatusUpdate($data);
+
+ $this->assertSame($data, $event->toArray());
+ }
+
+ public function testChainingWhenSettingProperties()
+ {
+ $event = new VerifyStatusUpdate([]);
+ $result = $event->__set('eventType', 'completed');
+
+ $this->assertInstanceOf(VerifyStatusUpdate::class, $result);
+ }
+}
diff --git a/test/Verify2/VerifyObjects/VerifyWhatsAppInteractiveEventTest.php b/test/Verify2/VerifyObjects/VerifyWhatsAppInteractiveEventTest.php
new file mode 100644
index 00000000..b206c1b7
--- /dev/null
+++ b/test/Verify2/VerifyObjects/VerifyWhatsAppInteractiveEventTest.php
@@ -0,0 +1,62 @@
+ 'completed', 'timestamp' => '2025-01-01T00:00:00Z'];
+ $event = new VerifyWhatsAppInteractiveEvent($data);
+
+ $this->assertSame($data, $event->toArray());
+ }
+
+ public function testPropertyGetAndSet()
+ {
+ $event = new VerifyWhatsAppInteractiveEvent(['eventType' => 'started']);
+ $event->timestamp = '2025-01-01T00:00:00Z';
+
+ $this->assertSame('started', $event->eventType);
+ $this->assertSame('2025-01-01T00:00:00Z', $event->timestamp);
+ $this->assertNull($event->unknownProperty);
+ }
+
+ public function testPropertyIsset()
+ {
+ $event = new VerifyWhatsAppInteractiveEvent(['eventType' => 'completed']);
+
+ $this->assertTrue(isset($event->eventType));
+ $this->assertFalse(isset($event->timestamp));
+ }
+
+ public function testFromArrayHydratesData()
+ {
+ $data = ['eventType' => 'completed', 'timestamp' => '2025-01-01T00:00:00Z'];
+ $event = new VerifyWhatsAppInteractiveEvent([]);
+ $event->fromArray($data);
+
+ $this->assertSame($data, $event->toArray());
+ }
+
+ public function testToArrayReturnsData()
+ {
+ $data = ['eventType' => 'started', 'timestamp' => '2025-01-01T00:00:00Z'];
+ $event = new VerifyWhatsAppInteractiveEvent($data);
+
+ $this->assertSame($data, $event->toArray());
+ }
+
+ public function testChainingWhenSettingProperties()
+ {
+ $event = new VerifyWhatsAppInteractiveEvent([]);
+ $result = $event->__set('eventType', 'completed');
+
+ $this->assertInstanceOf(VerifyWhatsAppInteractiveEvent::class, $result);
+ }
+}
diff --git a/test/Voice/ClientFactoryTest.php b/test/Voice/ClientFactoryTest.php
new file mode 100644
index 00000000..e06eafcd
--- /dev/null
+++ b/test/Voice/ClientFactoryTest.php
@@ -0,0 +1,32 @@
+ ClientFactory::class,
+ APIResource::class => APIResource::class,
+ ];
+
+ $mockClient = $this->createMock(Client::class);
+ $container = new MapFactory($mockServices, $mockClient);
+ $factory = new ClientFactory();
+
+ $result = $factory($container);
+ $this->assertInstanceOf(\Vonage\voice\Client::class, $result);
+ $this->assertInstanceOf(Client\Credentials\Handler\KeypairHandler::class, $result->getAPIResource()
+ ->getAuthHandlers()[0]);
+ $this->assertEquals('/v1/calls', $result->getAPIResource()->getBaseUri());
+ $this->assertEquals('calls', $result->getAPIResource()->getCollectionName());
+ }
+}
diff --git a/test/Webhook/FactoryTest.php b/test/Webhook/FactoryTest.php
new file mode 100644
index 00000000..7c5710f5
--- /dev/null
+++ b/test/Webhook/FactoryTest.php
@@ -0,0 +1,81 @@
+concreteFactory = new class extends Factory {
+ public static function createFromArray(array $data)
+ {
+ return $data;
+ }
+ };
+ }
+
+ public function testCreateFromJsonWithValidJson(): void
+ {
+ $json = '{"key":"value"}';
+ $result = $this->concreteFactory::createFromJson($json);
+
+ $this->assertSame(['key' => 'value'], $result);
+ }
+
+ public function testCreateFromJsonWithInvalidJson(): void
+ {
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage("Invalid JSON string detected for webhook transformation");
+
+ $json = '{invalid_json}';
+ $this->concreteFactory::createFromJson($json);
+ }
+
+ public function testCreateFromRequestWithGetMethod(): void
+ {
+ $request = new ServerRequest([], [], null, 'GET', 'php://temp', [], [], ['key' => 'value']);
+
+ $result = $this->concreteFactory::createFromRequest($request);
+
+ $this->assertSame(['key' => 'value'], $result);
+ }
+
+ public function testCreateFromRequestWithPostMethodAndJson(): void
+ {
+ $body = '{"key":"value"}';
+
+ // Use a writable temporary stream for the body
+ $stream = new Stream('php://temp', 'wb+');
+ $stream->write($body);
+ $stream->rewind();
+
+ $request = new ServerRequest([], [], null, 'POST', $stream, ['Content-Type' => 'application/json'], [], []);
+
+ $result = $this->concreteFactory::createFromRequest($request);
+
+ $this->assertSame(['key' => 'value'], $result);
+ }
+
+ public function testCreateFromRequestWithInvalidMethod(): void
+ {
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage("Invalid method for incoming webhook");
+
+ $request = new ServerRequest([], [], null, 'PUT');
+ $this->concreteFactory::createFromRequest($request);
+ }
+}