diff --git a/documentation/pubsub-messaging.md b/documentation/pubsub-messaging.md index 9e7588cf..7b819dc4 100644 --- a/documentation/pubsub-messaging.md +++ b/documentation/pubsub-messaging.md @@ -4,10 +4,10 @@ Provides an integration to [Azure's Web PubSub service](https://azure.microsoft. ## Configuration -You must define a [JSON Vault item](/documentation/api-accounts.md#managing-external-api-credentials) to use this feature. The data field should be a JSON string containing `endpoint`, `hub`, `group` and `access_key`: +You must define a [JSON Vault item](/documentation/api-accounts.md#managing-external-api-credentials) to use this feature. The data field should be a JSON string containing `endpoint`, `hub`, `group` and `access_key` and optional `secondary_access_key`: ```json -{"endpoint": "", "hub": "", "group": "", "access_key": ""} +{"endpoint": "", "hub": "", "group": "", "access_key": "", "secondary_access_key": ""} ``` ## Usage @@ -85,34 +85,8 @@ $pubsub_account = [ 'hub' => '', 'group' => '', 'access_key' => '', + 'secondary_access_key' => '', ]), ]; $config['helfi_api_base.api_accounts']['vault'][] = $pubsub_account; ``` - -## Solving pubsub related problems - -If menus or news or other content doesn't update normally, you can verify that the pubsub service is working correctly - -### Artemis is not up on etusivu-instance -- See that frontpage production has artemis pod up and running - -#### If the pod is not running -- See if there is a pipeline to get it up again OR -- Contact HiQ - - -### Pubsub-process is not running -- Go to any production site's cron pod - - run `ps aux`, you should see pubsub related process on the list - -#### If the process is not running -- Short term solution is to run `drush cr` to force the site to fetch new data. -- You can run production deployment to get it running again. - - -### Bad credentials -- Go to any cron pod and look for authorization error - -#### Update the credentials -- Go and update the pubsub-vault credentials diff --git a/drush.services.yml b/drush.services.yml index 61735ee1..25017c35 100644 --- a/drush.services.yml +++ b/drush.services.yml @@ -7,12 +7,6 @@ services: - '@datetime.time' tags: - { name: drush.command } - helfi_api_base.pubsub_commands: - class: \Drupal\helfi_api_base\Commands\PubSubCommands - arguments: - - '@helfi_api_base.pubsub_manager' - tags: - - { name: drush.command } helfi_api_base.deploy_commands: class: \Drupal\helfi_api_base\Commands\DeployCommands arguments: ['@event_dispatcher'] diff --git a/helfi_api_base.services.yml b/helfi_api_base.services.yml index 1c4e2ffa..ad4d726c 100644 --- a/helfi_api_base.services.yml +++ b/helfi_api_base.services.yml @@ -108,53 +108,17 @@ services: arguments: - '@config.factory' - Drupal\helfi_api_base\Azure\PubSub\SettingsFactory: '@helfi_api_base.pubsub_settings_factory' - helfi_api_base.pubsub_settings_factory: - class: Drupal\helfi_api_base\Azure\PubSub\SettingsFactory - arguments: - - '@helfi_api_base.vault_manager' - - Drupal\helfi_api_base\Azure\PubSub\PubSubClientFactory: '@helfi_api_base.pubsub_client_factory' - helfi_api_base.pubsub_client_factory: - class: Drupal\helfi_api_base\Azure\PubSub\PubSubClientFactory - - helfi_api_base.pubsub_client: - public: false - class: \WebSocket\Client - factory: ['@helfi_api_base.pubsub_client_factory', 'create'] - arguments: - - '@helfi_api_base.pubsub_settings' - - '@datetime.time' - - Drupal\helfi_api_base\Azure\PubSub\Settings: '@helfi_api_base.pubsub_settings' - helfi_api_base.pubsub_settings: - class: Drupal\helfi_api_base\Azure\PubSub\Settings - factory: ['@helfi_api_base.pubsub_settings_factory', 'create'] - - Drupal\helfi_api_base\Azure\PubSub\PubSubManager: '@helfi_api_base.pubsub_manager' - helfi_api_base.pubsub_manager: - class: Drupal\helfi_api_base\Azure\PubSub\PubSubManager - arguments: - - '@helfi_api_base.pubsub_client' - - '@event_dispatcher' - - '@datetime.time' - - '@helfi_api_base.pubsub_settings' - - Drupal\helfi_api_base\Cache\CacheTagInvalidatorInterface: '@helfi_api_base.cache_tag_invalidator' - Drupal\helfi_api_base\Cache\CacheTagInvalidator: '@helfi_api_base.cache_tag_invalidator' - helfi_api_base.cache_tag_invalidator: - class: Drupal\helfi_api_base\Cache\CacheTagInvalidator - arguments: - - '@helfi_api_base.pubsub_manager' - - Drupal\helfi_api_base\EventSubscriber\CacheTagInvalidatorSubscriber: '@helfi_api_base.cache_tag_invalidator_subscriber' - helfi_api_base.cache_tag_invalidator_subscriber: - class: Drupal\helfi_api_base\EventSubscriber\CacheTagInvalidatorSubscriber - arguments: - - '@cache_tags.invalidator' - - '@helfi_api_base.environment_resolver' - tags: - - { name: event_subscriber } + Drupal\helfi_api_base\Azure\PubSub\PubSubClientFactory: ~ + Drupal\helfi_api_base\Azure\PubSub\PubSubClientFactoryInterface: '@Drupal\helfi_api_base\Azure\PubSub\PubSubClientFactory' + Drupal\helfi_api_base\Azure\PubSub\SettingsFactory: ~ + Drupal\helfi_api_base\Azure\PubSub\Settings: + factory: ['@Drupal\helfi_api_base\Azure\PubSub\SettingsFactory', 'create'] + Drupal\helfi_api_base\Azure\PubSub\PubSubManager: ~ + Drupal\helfi_api_base\Azure\PubSub\PubSubManagerInterface: '@Drupal\helfi_api_base\Azure\PubSub\PubSubManager' + Drupal\helfi_api_base\Cache\CacheTagInvalidatorInterface: '@Drupal\helfi_api_base\Cache\CacheTagInvalidator' + Drupal\helfi_api_base\Cache\CacheTagInvalidator: ~ + + Drupal\helfi_api_base\EventSubscriber\CacheTagInvalidatorSubscriber: ~ Drupal\helfi_api_base\Entity\Revision\RevisionManager: '@helfi_api_base.revision_manager' helfi_api_base.revision_manager: diff --git a/src/Azure/PubSub/PubSubClientFactory.php b/src/Azure/PubSub/PubSubClientFactory.php index e2351b98..bb10fb1a 100644 --- a/src/Azure/PubSub/PubSubClientFactory.php +++ b/src/Azure/PubSub/PubSubClientFactory.php @@ -11,31 +11,43 @@ /** * A Web socket client factory. */ -final class PubSubClientFactory { +final class PubSubClientFactory implements PubSubClientFactoryInterface { /** - * Constructs a new websocket client object. + * Constructs a new instance. * - * @param \Drupal\helfi_api_base\Azure\PubSub\Settings $settings - * The settings. * @param \Drupal\Component\Datetime\TimeInterface $time * The time interface. + * @param \Drupal\helfi_api_base\Azure\PubSub\Settings $settings + * The PubSub settings. + */ + public function __construct( + private readonly TimeInterface $time, + private readonly Settings $settings, + ) { + } + + /** + * Constructs a new websocket client object. + * + * @param string $accessKey + * The access key. * * @return \WebSocket\Client * The client. */ - public function create(Settings $settings, TimeInterface $time) : Client { - $url = sprintf('wss://%s/client/hubs/%s', rtrim($settings->endpoint, '/'), $settings->hub); + public function create(string $accessKey) : Client { + $url = sprintf('wss://%s/client/hubs/%s', rtrim($this->settings->endpoint, '/'), $this->settings->hub); $authorizationToken = JWT::encode([ 'aud' => $url, - 'iat' => $time->getCurrentTime(), - 'exp' => $time->getCurrentTime() + 3600, + 'iat' => $this->time->getCurrentTime(), + 'exp' => $this->time->getCurrentTime() + 3600, 'role' => [ 'webpubsub.sendToGroup', 'webpubsub.joinLeaveGroup', ], - ], $settings->accessKey, 'HS256'); + ], $accessKey, 'HS256'); return new Client($url, [ 'headers' => [ diff --git a/src/Azure/PubSub/PubSubClientFactoryInterface.php b/src/Azure/PubSub/PubSubClientFactoryInterface.php new file mode 100644 index 00000000..7e6efeba --- /dev/null +++ b/src/Azure/PubSub/PubSubClientFactoryInterface.php @@ -0,0 +1,25 @@ +joinedGroup) { + private function initializeClient() : void { + if ($this->client) { return; } - $this->client->text( - $this->encodeMessage([ - 'type' => 'joinGroup', - 'group' => $this->settings->group, - ]) - ); + $client = $exception = NULL; + + // Initialize client with primary key, fallback to secondary key. + foreach ($this->settings->accessKeys as $key) { + $exception = NULL; + + try { + $client = $this->clientFactory->create($key); + $client->text($this->encodeMessage([ + 'type' => 'joinGroup', + 'group' => $this->settings->group, + ])); + } + catch (ConnectionException $exception) { + Error::logException($this->logger, $exception); + } + } + + if ($exception instanceof ConnectionException) { + throw $exception; + } try { // Wait until we've actually joined the group. - $message = $this->decodeMessage((string) $this->client->receive()); + $message = $this->decodeMessage((string) $client->receive()); if (isset($message['event']) && $message['event'] === 'connected') { - $this->joinedGroup = TRUE; + $this->client = $client; return; } } catch (\JsonException) { } - - throw new ConnectionException('Failed to join a group.'); + throw new ConnectionException('Failed to initialize the client.'); } /** @@ -105,48 +125,20 @@ private function decodeMessage(string $message) : array { return json_decode($message, TRUE, flags: JSON_THROW_ON_ERROR); } - /** - * {@inheritdoc} - */ - public function setTimeout(int $timeout) : self { - $this->client->setTimeout($timeout); - return $this; - } - - /** - * Asserts the settings. - * - * This is used to exit early if required settings are not populated. - */ - private function assertSettings() : void { - $vars = get_object_vars($this->settings); - - foreach ($vars as $key => $value) { - if (empty($this->settings->{$key})) { - throw new ConnectionException("Azure PubSub '$key' is not configured."); - } - } - } - /** * {@inheritdoc} */ public function sendMessage(array $message) : self { - $this->assertSettings(); - $this->joinGroup(); - - $this->client - ->text( - $this->encodeMessage([ - 'type' => 'sendToGroup', - 'group' => $this->settings->group, - 'dataType' => 'json', - 'data' => $message + [ - 'timestamp' => $this->time->getCurrentTime(), - ], - ]) - ); - + $this->initializeClient(); + + $this->client->text($this->encodeMessage([ + 'type' => 'sendToGroup', + 'group' => $this->settings->group, + 'dataType' => 'json', + 'data' => $message + [ + 'timestamp' => $this->time->getCurrentTime(), + ], + ])); return $this; } @@ -154,8 +146,7 @@ public function sendMessage(array $message) : self { * {@inheritdoc} */ public function receive() : string { - $this->assertSettings(); - $this->joinGroup(); + $this->initializeClient(); $message = (string) $this->client->receive(); $json = $this->decodeMessage($message); diff --git a/src/Azure/PubSub/PubSubManagerInterface.php b/src/Azure/PubSub/PubSubManagerInterface.php index a6085ad0..a0e094fb 100644 --- a/src/Azure/PubSub/PubSubManagerInterface.php +++ b/src/Azure/PubSub/PubSubManagerInterface.php @@ -36,15 +36,4 @@ public function sendMessage(array $message): self; */ public function receive(): string; - /** - * Sets the client timeout. - * - * @param int $timeout - * The timeout in seconds. - * - * @return self - * The self. - */ - public function setTimeout(int $timeout): self; - } diff --git a/src/Azure/PubSub/Settings.php b/src/Azure/PubSub/Settings.php index 166e844b..20c6872b 100644 --- a/src/Azure/PubSub/Settings.php +++ b/src/Azure/PubSub/Settings.php @@ -18,14 +18,14 @@ final class Settings { * The group. * @param string $endpoint * The API endpoint. - * @param string $accessKey - * The API access token. + * @param array $accessKeys + * The API access keys. */ public function __construct( public readonly string $hub, public readonly string $group, public readonly string $endpoint, - public readonly string $accessKey, + public readonly array $accessKeys, ) { } diff --git a/src/Azure/PubSub/SettingsFactory.php b/src/Azure/PubSub/SettingsFactory.php index f7ef26e9..cf2cc72a 100644 --- a/src/Azure/PubSub/SettingsFactory.php +++ b/src/Azure/PubSub/SettingsFactory.php @@ -29,28 +29,26 @@ public function __construct( * The PubSub settings object. */ public function create() : Settings { - $data = [ - 'hub' => '', - 'group' => '', - 'endpoint' => '', - 'access_key' => '', - ]; - - if ($settings = $this->vaultManager->get('pubsub')) { - foreach ($data as $key => $value) { - if (!isset($settings->data()->{$key})) { - continue; - } - $data[$key] = $settings->data()->{$key}; + if (!$settings = $this->vaultManager->get('pubsub')) { + // Return an empty settings object in case PubSub is not + // configured. + return new Settings('', '', '', []); + } + $data = $settings->data(); + + $accessKeys = []; + foreach (['access_key', 'secondary_access_key'] as $key) { + if (empty($data->{$key})) { + continue; } + $accessKeys[] = $data->{$key}; } - $data = (object) $data; return new Settings( $data->hub ?: '', $data->group ?: '', $data->endpoint ?: '', - $data->access_key ?: '' + $accessKeys, ); } diff --git a/src/Commands/PubSubCommands.php b/src/Drush/Commands/PubSubCommands.php similarity index 83% rename from src/Commands/PubSubCommands.php rename to src/Drush/Commands/PubSubCommands.php index 7fd0619e..bb616690 100644 --- a/src/Commands/PubSubCommands.php +++ b/src/Drush/Commands/PubSubCommands.php @@ -2,10 +2,11 @@ declare(strict_types=1); -namespace Drupal\helfi_api_base\Commands; +namespace Drupal\helfi_api_base\Drush\Commands; use Drupal\helfi_api_base\Azure\PubSub\PubSubManagerInterface; use Drush\Attributes\Command; +use Drush\Commands\AutowireTrait; use Drush\Commands\DrushCommands; use WebSocket\TimeoutException; @@ -20,18 +21,20 @@ */ final class PubSubCommands extends DrushCommands { + use AutowireTrait; + public const MAX_MESSAGES = 100; - public const CLIENT_TIMEOUT = 120; /** * Constructs a new instance. * - * @param \Drupal\helfi_api_base\Azure\PubSub\PubSubManagerInterface $pubSubClient + * @param \Drupal\helfi_api_base\Azure\PubSub\PubSubManagerInterface $pubSubManager * The PubSub client. */ public function __construct( - private readonly PubSubManagerInterface $pubSubClient, + private readonly PubSubManagerInterface $pubSubManager, ) { + parent::__construct(); } /** @@ -42,11 +45,9 @@ public function __construct( */ #[Command(name: 'helfi:azure:pubsub-listen')] public function listen() : int { - $this->pubSubClient->setTimeout(self::CLIENT_TIMEOUT); - for ($received = 0; $received < self::MAX_MESSAGES; $received++) { try { - $message = $this->pubSubClient->receive(); + $message = $this->pubSubManager->receive(); $this->io() ->writeln(sprintf('Received message [#%d]: %s', $received, $message)); } diff --git a/tests/src/Kernel/Cache/CacheTagInvalidatorTest.php b/tests/src/Kernel/Cache/CacheTagInvalidatorTest.php index 22420dac..ed2c2a71 100644 --- a/tests/src/Kernel/Cache/CacheTagInvalidatorTest.php +++ b/tests/src/Kernel/Cache/CacheTagInvalidatorTest.php @@ -4,11 +4,15 @@ namespace Drupal\Tests\helfi_api_base\Kernel\Cache; +use Drupal\helfi_api_base\Azure\PubSub\PubSubClientFactoryInterface; use Drupal\helfi_api_base\Azure\PubSub\PubSubManager; +use Drupal\helfi_api_base\Azure\PubSub\Settings; +use Drupal\helfi_api_base\Cache\CacheTagInvalidator as CacheTagInvalidatorService; use Drupal\KernelTests\KernelTestBase; use Drupal\Tests\helfi_api_base\Traits\CacheTagInvalidator; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; +use Psr\Log\LoggerInterface; use WebSocket\Client; /** @@ -70,17 +74,21 @@ public function testInvalidateTags() : void { '{"type":"event","event":"connected"}', '{"data": {"tags":["node:123"]}}' ); + $clientFactory = $this->prophesize(PubSubClientFactoryInterface::class); + $clientFactory->create('123')->willReturn($client->reveal()); + $pubSubManager = new PubSubManager( - $client->reveal(), + $clientFactory->reveal(), $this->container->get('event_dispatcher'), $this->container->get('datetime.time'), - $this->container->get('helfi_api_base.pubsub_settings'), + $this->container->get(Settings::class), + $this->prophesize(LoggerInterface::class)->reveal(), ); - $this->container->set('helfi_api_base.pubsub_manager', $pubSubManager); + $this->container->set(PubSubManager::class, $pubSubManager); $pubSubManager->receive(); /** @var \Drupal\helfi_api_base\Cache\CacheTagInvalidator $sut */ - $sut = $this->container->get('helfi_api_base.cache_tag_invalidator'); + $sut = $this->container->get(CacheTagInvalidatorService::class); $sut->invalidateTags(['node:123']); $this->assertArrayHasKey('node:123', $cacheTagInvalidator->tags); } diff --git a/tests/src/Unit/Azure/PubSub/PubSubClientFactoryTest.php b/tests/src/Unit/Azure/PubSub/PubSubClientFactoryTest.php index 8b43c2f9..4c78f6f5 100644 --- a/tests/src/Unit/Azure/PubSub/PubSubClientFactoryTest.php +++ b/tests/src/Unit/Azure/PubSub/PubSubClientFactoryTest.php @@ -20,18 +20,17 @@ class PubSubClientFactoryTest extends UnitTestCase { use ProphecyTrait; /** - * @covers ::create - * @covers \Drupal\helfi_api_base\Azure\PubSub\Settings::__construct + * Tests client construction. */ public function testConstruct() : void { $settings = new Settings( 'hub', 'group', 'endpoint', - 'accessToken', + ['accessToken', 'secondaryAccessToken'], ); - $sut = new PubSubClientFactory(); - $client = $sut->create($settings, $this->prophesize(TimeInterface::class)->reveal()); + $sut = new PubSubClientFactory($this->prophesize(TimeInterface::class)->reveal(), $settings); + $client = $sut->create($settings->accessKeys[0]); $this->assertInstanceOf(Client::class, $client); } diff --git a/tests/src/Unit/Azure/PubSub/PubSubManagerTest.php b/tests/src/Unit/Azure/PubSub/PubSubManagerTest.php index e98aa8b7..66b56276 100644 --- a/tests/src/Unit/Azure/PubSub/PubSubManagerTest.php +++ b/tests/src/Unit/Azure/PubSub/PubSubManagerTest.php @@ -5,17 +5,20 @@ namespace Drupal\Tests\helfi_api_base\Unit\Azure\PubSub; use Drupal\Component\Datetime\TimeInterface; +use Drupal\helfi_api_base\Azure\PubSub\PubSubClientFactoryInterface; use Drupal\helfi_api_base\Azure\PubSub\PubSubManager; use Drupal\helfi_api_base\Azure\PubSub\Settings; use Drupal\Tests\UnitTestCase; use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; +use Psr\Log\LoggerInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use WebSocket\Client; use WebSocket\ConnectionException; /** - * @coversDefaultClass \Drupal\helfi_api_base\Azure\PubSub\PubSubManager + * Tests PubSub manager service. + * * @group helfi_api_base */ class PubSubManagerTest extends UnitTestCase { @@ -23,108 +26,70 @@ class PubSubManagerTest extends UnitTestCase { use ProphecyTrait; /** - * @covers ::__construct - * @covers ::assertSettings - * @covers ::receive - * @covers ::sendMessage - * @covers \Drupal\helfi_api_base\Azure\PubSub\Settings::__construct - * @dataProvider assertSettingsExceptionData + * Tests initializeClient with invalid joinGroup message. */ - public function testAssertSettingsException(string $method, array $args) : void { - $this->expectException(ConnectionException::class); - $this->expectExceptionMessage("Azure PubSub 'hub' is not configured"); - $sut = new PubSubManager( - $this->prophesize(Client::class)->reveal(), - $this->prophesize(EventDispatcherInterface::class)->reveal(), - $this->prophesize(TimeInterface::class)->reveal(), - new Settings( - '', - 'local', - 'localhost', - 'token', - ) - ); - call_user_func_array([$sut, $method], $args); - } - - /** - * Data provider for testAssertSettingsException. - * - * @return array[] - * The data. - */ - public function assertSettingsExceptionData() : array { - return [ - ['receive', []], - ['sendMessage', [['message' => 'message']]], - ]; - } + public function testInitializeClientJoinGroupException() : void { + $time = $this->prophesize(TimeInterface::class); + $time->getCurrentTime()->willReturn(1234); - /** - * @covers ::setTimeout - * @covers ::__construct - * @covers ::assertSettings - * @covers \Drupal\helfi_api_base\Azure\PubSub\Settings::__construct - */ - public function testSetTimeout() : void { $client = $this->prophesize(Client::class); - $client->setTimeout(5)->shouldBeCalled(); + $client->text('{"type":"joinGroup","group":"local"}')->shouldBeCalledTimes(1); + $client->receive()->willReturn(''); + $clientFactory = $this->prophesize(PubSubClientFactoryInterface::class); + $clientFactory->create('token') + ->willReturn($client->reveal()); + $sut = new PubSubManager( - $client->reveal(), + $clientFactory->reveal(), $this->prophesize(EventDispatcherInterface::class)->reveal(), - $this->prophesize(TimeInterface::class)->reveal(), + $time->reveal(), new Settings( 'hub', 'local', 'localhost', - 'token', - ) + ['token'], + ), + $this->prophesize(LoggerInterface::class)->reveal(), ); - $sut->setTimeout(5); + $this->expectException(ConnectionException::class); + $this->expectExceptionMessage('Failed to initialize the client.'); + $sut->sendMessage(['test' => 'something']); } /** - * @covers ::sendMessage - * @covers ::joinGroup - * @covers ::encodeMessage - * @covers ::decodeMessage - * @covers ::__construct - * @covers ::assertSettings - * @covers \Drupal\helfi_api_base\Azure\PubSub\Settings::__construct + * Tests initializeClient with invalid credentials. */ - public function testJoinGroupException() : void { + public function testInitializeClientInvalidCredentials(): void { $time = $this->prophesize(TimeInterface::class); $time->getCurrentTime()->willReturn(1234); $client = $this->prophesize(Client::class); - $client->text('{"type":"joinGroup","group":"local"}')->shouldBeCalledTimes(1); - $client->send(Argument::any())->shouldNotBeCalled(); - $client->receive()->willReturn(''); + $client->text('{"type":"joinGroup","group":"local"}') + ->shouldBeCalledTimes(1) + ->willThrow(new ConnectionException('Test exception')); + $clientFactory = $this->prophesize(PubSubClientFactoryInterface::class); + $clientFactory->create('token') + ->willReturn($client->reveal()); $sut = new PubSubManager( - $client->reveal(), + $clientFactory->reveal(), $this->prophesize(EventDispatcherInterface::class)->reveal(), $time->reveal(), new Settings( 'hub', 'local', 'localhost', - 'token', - ) + ['token'], + ), + $this->prophesize(LoggerInterface::class)->reveal(), ); $this->expectException(ConnectionException::class); - $this->expectExceptionMessage('Failed to join a group.'); + $this->expectExceptionMessage('Test exception'); $sut->sendMessage(['test' => 'something']); } /** - * @covers ::sendMessage - * @covers ::joinGroup - * @covers ::encodeMessage - * @covers ::decodeMessage - * @covers ::__construct - * @covers ::assertSettings - * @covers \Drupal\helfi_api_base\Azure\PubSub\Settings::__construct + * Tests sendMessage() and clientText() methods. */ public function testSendMessage() : void { $time = $this->prophesize(TimeInterface::class); @@ -134,17 +99,21 @@ public function testSendMessage() : void { $client->receive()->willReturn('{"type":"event","event":"connected"}'); $client->text('{"type":"joinGroup","group":"local"}')->shouldBeCalledTimes(1); $client->text('{"type":"sendToGroup","group":"local","dataType":"json","data":{"test":"something","timestamp":1234}}')->shouldBeCalledTimes(2); + $clientFactory = $this->prophesize(PubSubClientFactoryInterface::class); + $clientFactory->create('token') + ->willReturn($client->reveal()); $sut = new PubSubManager( - $client->reveal(), + $clientFactory->reveal(), $this->prophesize(EventDispatcherInterface::class)->reveal(), $time->reveal(), new Settings( 'hub', 'local', 'localhost', - 'token', - ) + ['token'], + ), + $this->prophesize(LoggerInterface::class)->reveal(), ); // Send two messages to test that we call ::joinGroup() once. $sut->sendMessage(['test' => 'something']); @@ -152,14 +121,7 @@ public function testSendMessage() : void { } /** - * @covers ::joinGroup - * @covers ::receive - * @covers ::encodeMessage - * @covers ::decodeMessage - * @covers ::__construct - * @covers ::assertSettings - * @covers \Drupal\helfi_api_base\Azure\PubSub\Settings::__construct - * @covers \Drupal\helfi_api_base\Azure\PubSub\PubSubMessage::__construct + * Tests receive() and clientReceive() methods. */ public function testReceive() : void { $expectedMessage = '{"message":"test"}'; @@ -175,17 +137,21 @@ public function testReceive() : void { ); // This called once by ::joinGroup and twice by ::receive(). $client->receive()->shouldBeCalledTimes(3); + $clientFactory = $this->prophesize(PubSubClientFactoryInterface::class); + $clientFactory->create('token') + ->willReturn($client->reveal()); $sut = new PubSubManager( - $client->reveal(), + $clientFactory->reveal(), $dispatcher->reveal(), $this->prophesize(TimeInterface::class)->reveal(), new Settings( 'hub', 'local', 'localhost', - 'token', - ) + ['token'], + ), + $this->prophesize(LoggerInterface::class)->reveal(), ); // Call twice to make sure we only join group once. $this->assertEquals($expectedMessage, $sut->receive()); diff --git a/tests/src/Unit/Azure/PubSub/SettingsTest.php b/tests/src/Unit/Azure/PubSub/SettingsTest.php index 5571f326..a2eff9d2 100644 --- a/tests/src/Unit/Azure/PubSub/SettingsTest.php +++ b/tests/src/Unit/Azure/PubSub/SettingsTest.php @@ -11,19 +11,15 @@ use Drupal\Tests\UnitTestCase; /** - * @coversDefaultClass \Drupal\helfi_api_base\Azure\PubSub\SettingsFactory + * Tests pubsub settings. + * * @group helfi_api_base */ class SettingsTest extends UnitTestCase { /** - * @covers \Drupal\helfi_api_base\Azure\PubSub\Settings::__construct - * @covers ::create - * @covers ::__construct - * @covers \Drupal\helfi_api_base\Vault\VaultManager::__construct - * @covers \Drupal\helfi_api_base\Vault\VaultManager::get - * @covers \Drupal\helfi_api_base\Vault\Json::__construct - * @covers \Drupal\helfi_api_base\Vault\Json::data + * Tests settings. + * * @dataProvider settingsData */ public function testSettings(array $values, array $expectedValues) : void { @@ -36,15 +32,11 @@ public function testSettings(array $values, array $expectedValues) : void { $this->assertSame($expectedValues['hub'], $settings->hub); $this->assertSame($expectedValues['group'], $settings->group); $this->assertSame($expectedValues['endpoint'], $settings->endpoint); - $this->assertSame($expectedValues['access_key'], $settings->accessKey); + $this->assertSame($expectedValues['access_key'], $settings->accessKeys); } /** - * @covers \Drupal\helfi_api_base\Azure\PubSub\Settings::__construct - * @covers ::create - * @covers ::__construct - * @covers \Drupal\helfi_api_base\Vault\VaultManager::__construct - * @covers \Drupal\helfi_api_base\Vault\VaultManager::get + * Tests empty settings. */ public function testEmptySettings() : void { $vaultManager = new VaultManager([]); @@ -54,7 +46,7 @@ public function testEmptySettings() : void { $this->assertSame('', $settings->hub); $this->assertSame('', $settings->group); $this->assertSame('', $settings->endpoint); - $this->assertSame('', $settings->accessKey); + $this->assertSame([], $settings->accessKeys); } /** @@ -77,7 +69,7 @@ public function settingsData() : array { 'hub' => 'hub', 'group' => 'group', 'endpoint' => 'endpoint', - 'access_key' => '123', + 'access_key' => ['123'], ], ], ]; @@ -94,7 +86,7 @@ public function settingsData() : array { 'hub' => '', 'group' => '', 'endpoint' => '', - 'access_key' => '', + 'access_key' => [], ], ]; } diff --git a/tests/src/Unit/Commands/PubSubCommandsTest.php b/tests/src/Unit/Commands/PubSubCommandsTest.php index b69b6eb9..06547e87 100644 --- a/tests/src/Unit/Commands/PubSubCommandsTest.php +++ b/tests/src/Unit/Commands/PubSubCommandsTest.php @@ -5,7 +5,7 @@ namespace Drupal\Tests\helfi_api_base\Unit\Commands; use Drupal\helfi_api_base\Azure\PubSub\PubSubManagerInterface; -use Drupal\helfi_api_base\Commands\PubSubCommands; +use Drupal\helfi_api_base\Drush\Commands\PubSubCommands; use Drupal\Tests\UnitTestCase; use Drush\Commands\DrushCommands; use Prophecy\Argument; @@ -25,8 +25,7 @@ class PubSubCommandsTest extends UnitTestCase { use ProphecyTrait; /** - * @covers ::__construct - * @covers ::listen + * Tests listen. */ public function testListen() : void { $expectedMessage = '{"message":"test"}'; @@ -39,7 +38,6 @@ public function testListen() : void { ->shouldBeCalledTimes(1); $manager = $this->prophesize(PubSubManagerInterface::class); - $manager->setTimeout(PubSubCommands::CLIENT_TIMEOUT)->shouldBeCalled(); $manager->receive()->willReturn($expectedMessage); $sut = new PubSubCommands($manager->reveal()); @@ -48,8 +46,7 @@ public function testListen() : void { } /** - * @covers ::listen - * @covers ::__construct + * Tests exception output. */ public function testExceptionOutput() : void { $output = $this->prophesize(OutputInterface::class); @@ -60,7 +57,6 @@ public function testExceptionOutput() : void { ->shouldBeCalledTimes(1); $manager = $this->prophesize(PubSubManagerInterface::class); - $manager->setTimeout(PubSubCommands::CLIENT_TIMEOUT)->shouldBeCalled(); $manager->receive()->willThrow(new \JsonException('Syntax error')); $sut = new PubSubCommands($manager->reveal()); @@ -69,8 +65,7 @@ public function testExceptionOutput() : void { } /** - * @covers ::listen - * @covers ::__construct + * Tests timeout exception. */ public function testTimeoutException() : void { $output = $this->prophesize(OutputInterface::class); @@ -80,7 +75,6 @@ public function testTimeoutException() : void { ->shouldBeCalledTimes(1); $manager = $this->prophesize(PubSubManagerInterface::class); - $manager->setTimeout(PubSubCommands::CLIENT_TIMEOUT)->shouldBeCalled(); $manager->receive()->willThrow(TimeoutException::class); $sut = new PubSubCommands($manager->reveal()); @@ -89,13 +83,11 @@ public function testTimeoutException() : void { } /** - * @covers ::listen - * @covers ::__construct + * Tests connection exception. */ public function testConnectionException() : void { $this->expectException(ConnectionException::class); $manager = $this->prophesize(PubSubManagerInterface::class); - $manager->setTimeout(PubSubCommands::CLIENT_TIMEOUT)->shouldBeCalled(); $manager->receive()->willThrow(ConnectionException::class); $sut = new PubSubCommands($manager->reveal());