diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
index 6e5393c3..9f915d0e 100644
--- a/.php-cs-fixer.php
+++ b/.php-cs-fixer.php
@@ -29,6 +29,7 @@
->in(__DIR__ . '/src/Services/SonetGroup/')
->in(__DIR__ . '/src/Services/IMOpenLines/')
->in(__DIR__ . '/src/Services/Landing/')
+ ->in(__DIR__ . '/src/Services/Messageservice/')
->name('*.php')
->exclude(['vendor', 'storage', 'docker', 'docs']) // Exclude directories
->ignoreDotFiles(true)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f4bc3b52..f9281944 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,14 @@
### Added
+- Added service `Services\Messageservice\Sender` and `Services\Messageservice\Message\Status` with support for `messageservice.*` methods,
+ see [messageservice.* methods](https://apidocs.bitrix24.com/api-reference/messageservice/index.html) ([#498](https://github.com/bitrix24/b24phpsdk/issues/498)):
+ - `sender.add` — register a new SMS message service provider
+ - `sender.update` — update a registered message service provider
+ - `sender.list` — get list of sender codes registered by the current application
+ - `sender.delete` — delete a registered message service provider
+ - `message.status.update` — update delivery status of a message sent via a provider
+
- Added service `Services\Landing\Site\Service\Site` with support methods,
see [landing.site.* methods](https://github.com/bitrix24/b24phpsdk/issues/267):
- `add` adds a site
diff --git a/Makefile b/Makefile
index c2fc4a9a..8af2dbc2 100644
--- a/Makefile
+++ b/Makefile
@@ -492,6 +492,18 @@ test-integration-landing-role:
test-integration-landing-repowidget:
docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_landing_repowidget
+.PHONY: test-integration-scope-messageservice
+test-integration-scope-messageservice:
+ docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_messageservice
+
+.PHONY: test-integration-messageservice-sender
+test-integration-messageservice-sender:
+ docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_messageservice_sender
+
+.PHONY: test-integration-messageservice-message-status
+test-integration-messageservice-message-status:
+ docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_messageservice_message_status
+
# work dev environment
.PHONY: php-dev-server-up
php-dev-server-up:
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index e6ce503a..0f80361e 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -37,6 +37,7 @@ parameters:
- tests/Integration/Services/CRM/Documentgenerator/Document
- tests/Integration/Services/CRM/Documentgenerator/Template
- tests/Integration/Services/Landing
+ - tests/Integration/Services/Messageservice
excludePaths:
- tests/Integration/Services/CRM/Requisites/Service/RequisiteUserfieldUseCaseTest.php
- tests/Integration/Services/CRM/Status
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index d0187ae5..c996dc29 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -262,6 +262,15 @@
./tests/Integration/Services/Landing/RepoWidget/
+
+ ./tests/Integration/Services/Messageservice/
+
+
+ ./tests/Integration/Services/Messageservice/Sender/
+
+
+ ./tests/Integration/Services/Messageservice/Message/Status/
+
diff --git a/rector.php b/rector.php
index fb38e858..7de0efd9 100644
--- a/rector.php
+++ b/rector.php
@@ -78,6 +78,8 @@
__DIR__ . '/tests/Integration/Services/CRM/Documentgenerator/Template',
__DIR__ . '/src/Services/Landing',
__DIR__ . '/tests/Integration/Services/Landing',
+ __DIR__ . '/src/Services/Messageservice',
+ __DIR__ . '/tests/Integration/Services/Messageservice',
__DIR__ . '/tests/Unit/',
])
->withCache(cacheDirectory: __DIR__ . '/var/.cache/rector')
diff --git a/src/Services/Messageservice/Message/Status/Result/MessageStatusUpdateResult.php b/src/Services/Messageservice/Message/Status/Result/MessageStatusUpdateResult.php
new file mode 100644
index 00000000..35a5a5c4
--- /dev/null
+++ b/src/Services/Messageservice/Message/Status/Result/MessageStatusUpdateResult.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Messageservice\Message\Status\Result;
+
+use Bitrix24\SDK\Core\Result\AbstractResult;
+
+class MessageStatusUpdateResult extends AbstractResult
+{
+ /**
+ * Check if message status was updated successfully.
+ *
+ * @throws \Bitrix24\SDK\Core\Exceptions\BaseException
+ */
+ public function isSuccess(): bool
+ {
+ return (bool)$this->getCoreResponse()->getResponseData()->getResult();
+ }
+}
diff --git a/src/Services/Messageservice/Message/Status/Service/MessageStatus.php b/src/Services/Messageservice/Message/Status/Service/MessageStatus.php
new file mode 100644
index 00000000..2a035d0e
--- /dev/null
+++ b/src/Services/Messageservice/Message/Status/Service/MessageStatus.php
@@ -0,0 +1,63 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Messageservice\Message\Status\Service;
+
+use Bitrix24\SDK\Attributes\ApiEndpointMetadata;
+use Bitrix24\SDK\Attributes\ApiServiceMetadata;
+use Bitrix24\SDK\Core\Credentials\Scope;
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Exceptions\TransportException;
+use Bitrix24\SDK\Services\AbstractService;
+use Bitrix24\SDK\Services\Messageservice\Message\Status\Result\MessageStatusUpdateResult;
+
+#[ApiServiceMetadata(new Scope(['messageservice']))]
+class MessageStatus extends AbstractService
+{
+ /**
+ * Update delivery status of a message sent via a message service provider.
+ *
+ * Supported status values:
+ * - queued — message is queued for sending
+ * - sent — message was sent by the provider
+ * - delivered — message was successfully delivered to the recipient
+ * - undelivered — message was not delivered to the recipient
+ * - failed — provider encountered a sending or processing error
+ *
+ * @param string $code Sender code. Can be retrieved via messageservice.sender.list
+ * @param string $messageId External message identifier received by the handler on message send
+ * @param string $status New message status
+ *
+ * @throws BaseException
+ * @throws TransportException
+ * @link https://apidocs.bitrix24.com/api-reference/messageservice/messageservice-message-status-update.html
+ */
+ #[ApiEndpointMetadata(
+ 'messageservice.message.status.update',
+ 'https://apidocs.bitrix24.com/api-reference/messageservice/messageservice-message-status-update.html',
+ 'Update delivery status of a message sent via a message service provider'
+ )]
+ public function update(
+ string $code,
+ string $messageId,
+ string $status
+ ): MessageStatusUpdateResult {
+ return new MessageStatusUpdateResult(
+ $this->core->call('messageservice.message.status.update', [
+ 'CODE' => $code,
+ 'MESSAGE_ID' => $messageId,
+ 'STATUS' => $status,
+ ])
+ );
+ }
+}
diff --git a/src/Services/Messageservice/MessageserviceServiceBuilder.php b/src/Services/Messageservice/MessageserviceServiceBuilder.php
new file mode 100644
index 00000000..241aa512
--- /dev/null
+++ b/src/Services/Messageservice/MessageserviceServiceBuilder.php
@@ -0,0 +1,48 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Messageservice;
+
+use Bitrix24\SDK\Attributes\ApiServiceBuilderMetadata;
+use Bitrix24\SDK\Core\Credentials\Scope;
+use Bitrix24\SDK\Services\AbstractServiceBuilder;
+use Bitrix24\SDK\Services\Messageservice\Message\Status\Service\MessageStatus;
+use Bitrix24\SDK\Services\Messageservice\Sender\Service\Sender;
+
+#[ApiServiceBuilderMetadata(new Scope(['messageservice']))]
+class MessageserviceServiceBuilder extends AbstractServiceBuilder
+{
+ public function sender(): Sender
+ {
+ if (!isset($this->serviceCache[__METHOD__])) {
+ $this->serviceCache[__METHOD__] = new Sender(
+ $this->core,
+ $this->log
+ );
+ }
+
+ return $this->serviceCache[__METHOD__];
+ }
+
+ public function messageStatus(): MessageStatus
+ {
+ if (!isset($this->serviceCache[__METHOD__])) {
+ $this->serviceCache[__METHOD__] = new MessageStatus(
+ $this->core,
+ $this->log
+ );
+ }
+
+ return $this->serviceCache[__METHOD__];
+ }
+}
diff --git a/src/Services/Messageservice/Sender/Result/SenderAddResult.php b/src/Services/Messageservice/Sender/Result/SenderAddResult.php
new file mode 100644
index 00000000..4aa1c1cb
--- /dev/null
+++ b/src/Services/Messageservice/Sender/Result/SenderAddResult.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Messageservice\Sender\Result;
+
+use Bitrix24\SDK\Core\Result\AbstractResult;
+
+class SenderAddResult extends AbstractResult
+{
+ /**
+ * Check if sender was registered successfully.
+ *
+ * @throws \Bitrix24\SDK\Core\Exceptions\BaseException
+ */
+ public function isSuccess(): bool
+ {
+ return (bool)$this->getCoreResponse()->getResponseData()->getResult();
+ }
+}
diff --git a/src/Services/Messageservice/Sender/Result/SenderDeleteResult.php b/src/Services/Messageservice/Sender/Result/SenderDeleteResult.php
new file mode 100644
index 00000000..d36d98de
--- /dev/null
+++ b/src/Services/Messageservice/Sender/Result/SenderDeleteResult.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Messageservice\Sender\Result;
+
+use Bitrix24\SDK\Core\Result\AbstractResult;
+
+class SenderDeleteResult extends AbstractResult
+{
+ /**
+ * Check if sender was deleted successfully.
+ *
+ * @throws \Bitrix24\SDK\Core\Exceptions\BaseException
+ */
+ public function isSuccess(): bool
+ {
+ return (bool)$this->getCoreResponse()->getResponseData()->getResult();
+ }
+}
diff --git a/src/Services/Messageservice/Sender/Result/SenderUpdateResult.php b/src/Services/Messageservice/Sender/Result/SenderUpdateResult.php
new file mode 100644
index 00000000..2d591961
--- /dev/null
+++ b/src/Services/Messageservice/Sender/Result/SenderUpdateResult.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Messageservice\Sender\Result;
+
+use Bitrix24\SDK\Core\Result\AbstractResult;
+
+class SenderUpdateResult extends AbstractResult
+{
+ /**
+ * Check if sender was updated successfully.
+ *
+ * @throws \Bitrix24\SDK\Core\Exceptions\BaseException
+ */
+ public function isSuccess(): bool
+ {
+ return (bool)$this->getCoreResponse()->getResponseData()->getResult();
+ }
+}
diff --git a/src/Services/Messageservice/Sender/Result/SendersListResult.php b/src/Services/Messageservice/Sender/Result/SendersListResult.php
new file mode 100644
index 00000000..c32788b8
--- /dev/null
+++ b/src/Services/Messageservice/Sender/Result/SendersListResult.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Messageservice\Sender\Result;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Result\AbstractResult;
+
+class SendersListResult extends AbstractResult
+{
+ /**
+ * Get list of registered sender codes.
+ *
+ * @return string[]
+ * @throws BaseException
+ */
+ public function getSenderCodes(): array
+ {
+ $result = $this->getCoreResponse()->getResponseData()->getResult();
+
+ return is_array($result) ? array_values(array_map('strval', $result)) : [];
+ }
+}
diff --git a/src/Services/Messageservice/Sender/Service/Sender.php b/src/Services/Messageservice/Sender/Service/Sender.php
new file mode 100644
index 00000000..a549c556
--- /dev/null
+++ b/src/Services/Messageservice/Sender/Service/Sender.php
@@ -0,0 +1,154 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Messageservice\Sender\Service;
+
+use Bitrix24\SDK\Attributes\ApiEndpointMetadata;
+use Bitrix24\SDK\Attributes\ApiServiceMetadata;
+use Bitrix24\SDK\Core\Credentials\Scope;
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Exceptions\TransportException;
+use Bitrix24\SDK\Services\AbstractService;
+use Bitrix24\SDK\Services\Messageservice\Sender\Result\SenderAddResult;
+use Bitrix24\SDK\Services\Messageservice\Sender\Result\SenderDeleteResult;
+use Bitrix24\SDK\Services\Messageservice\Sender\Result\SenderUpdateResult;
+use Bitrix24\SDK\Services\Messageservice\Sender\Result\SendersListResult;
+
+#[ApiServiceMetadata(new Scope(['messageservice']))]
+class Sender extends AbstractService
+{
+ /**
+ * Register a new message service provider (sender).
+ *
+ * @param string $code Sender code. Allowed characters: a-z, A-Z, 0-9, ., -, _
+ * @param string $type Provider type. Supported value: SMS
+ * @param string $handler Application handler URL called on message send
+ * @param string|array $name Provider name. Can be a string or an associative array of localized strings
+ * @param string|array|null $description Provider description. Can be a string or localized array
+ *
+ * @throws BaseException
+ * @throws TransportException
+ * @link https://apidocs.bitrix24.com/api-reference/messageservice/messageservice-sender-add.html
+ */
+ #[ApiEndpointMetadata(
+ 'messageservice.sender.add',
+ 'https://apidocs.bitrix24.com/api-reference/messageservice/messageservice-sender-add.html',
+ 'Register a new message service provider (sender)'
+ )]
+ public function add(
+ string $code,
+ string $type,
+ string $handler,
+ string|array $name,
+ string|array|null $description = null
+ ): SenderAddResult {
+ $params = [
+ 'CODE' => $code,
+ 'TYPE' => $type,
+ 'HANDLER' => $handler,
+ 'NAME' => $name,
+ ];
+
+ if ($description !== null) {
+ $params['DESCRIPTION'] = $description;
+ }
+
+ return new SenderAddResult(
+ $this->core->call('messageservice.sender.add', $params)
+ );
+ }
+
+ /**
+ * Update a registered message service provider (sender).
+ *
+ * @param string $code Sender code to update. Can be retrieved via messageservice.sender.list
+ * @param string|null $handler New application handler URL
+ * @param string|array|null $name New provider name. Can be a string or localized array
+ * @param string|array|null $description New provider description. Can be a string or localized array
+ *
+ * @throws BaseException
+ * @throws TransportException
+ * @link https://apidocs.bitrix24.com/api-reference/messageservice/messageservice-sender-update.html
+ */
+ #[ApiEndpointMetadata(
+ 'messageservice.sender.update',
+ 'https://apidocs.bitrix24.com/api-reference/messageservice/messageservice-sender-update.html',
+ 'Update a registered message service provider (sender)'
+ )]
+ public function update(
+ string $code,
+ string|null $handler = null,
+ string|array|null $name = null,
+ string|array|null $description = null
+ ): SenderUpdateResult {
+ $params = [
+ 'CODE' => $code,
+ ];
+
+ if ($handler !== null) {
+ $params['HANDLER'] = $handler;
+ }
+
+ if ($name !== null) {
+ $params['NAME'] = $name;
+ }
+
+ if ($description !== null) {
+ $params['DESCRIPTION'] = $description;
+ }
+
+ return new SenderUpdateResult(
+ $this->core->call('messageservice.sender.update', $params)
+ );
+ }
+
+ /**
+ * Get list of sender codes registered by the current application.
+ *
+ * @throws BaseException
+ * @throws TransportException
+ * @link https://apidocs.bitrix24.com/api-reference/messageservice/messageservice-sender-list.html
+ */
+ #[ApiEndpointMetadata(
+ 'messageservice.sender.list',
+ 'https://apidocs.bitrix24.com/api-reference/messageservice/messageservice-sender-list.html',
+ 'Get list of sender codes registered by the current application'
+ )]
+ public function list(): SendersListResult
+ {
+ return new SendersListResult(
+ $this->core->call('messageservice.sender.list', [])
+ );
+ }
+
+ /**
+ * Delete a registered message service provider (sender).
+ *
+ * @param string $code Sender code to delete. Can be retrieved via messageservice.sender.list
+ *
+ * @throws BaseException
+ * @throws TransportException
+ * @link https://apidocs.bitrix24.com/api-reference/messageservice/messageservice-sender-delete.html
+ */
+ #[ApiEndpointMetadata(
+ 'messageservice.sender.delete',
+ 'https://apidocs.bitrix24.com/api-reference/messageservice/messageservice-sender-delete.html',
+ 'Delete a registered message service provider (sender)'
+ )]
+ public function delete(string $code): SenderDeleteResult
+ {
+ return new SenderDeleteResult(
+ $this->core->call('messageservice.sender.delete', ['CODE' => $code])
+ );
+ }
+}
diff --git a/src/Services/ServiceBuilder.php b/src/Services/ServiceBuilder.php
index 4c1026e5..947eba39 100644
--- a/src/Services/ServiceBuilder.php
+++ b/src/Services/ServiceBuilder.php
@@ -37,6 +37,7 @@
use Bitrix24\SDK\Services\Paysystem\PaysystemServiceBuilder;
use Bitrix24\SDK\Services\SonetGroup\SonetGroupServiceBuilder;
use Bitrix24\SDK\Services\Landing\LandingServiceBuilder;
+use Bitrix24\SDK\Services\Messageservice\MessageserviceServiceBuilder;
use Psr\Log\LoggerInterface;
class ServiceBuilder extends AbstractServiceBuilder
@@ -349,4 +350,18 @@ public function getLandingScope(): LandingServiceBuilder
return $this->serviceCache[__METHOD__];
}
+
+ public function getMessageserviceScope(): MessageserviceServiceBuilder
+ {
+ if (!isset($this->serviceCache[__METHOD__])) {
+ $this->serviceCache[__METHOD__] = new MessageserviceServiceBuilder(
+ $this->core,
+ $this->batch,
+ $this->bulkItemsReader,
+ $this->log
+ );
+ }
+
+ return $this->serviceCache[__METHOD__];
+ }
}
diff --git a/tests/Integration/Services/Messageservice/Message/Status/Service/MessageStatusTest.php b/tests/Integration/Services/Messageservice/Message/Status/Service/MessageStatusTest.php
new file mode 100644
index 00000000..869a5971
--- /dev/null
+++ b/tests/Integration/Services/Messageservice/Message/Status/Service/MessageStatusTest.php
@@ -0,0 +1,81 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Tests\Integration\Services\Messageservice\Message\Status\Service;
+
+use Bitrix24\SDK\Services\Messageservice\Message\Status\Service\MessageStatus;
+use Bitrix24\SDK\Services\Messageservice\Sender\Service\Sender;
+use Bitrix24\SDK\Tests\Integration\Fabric;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\Test;
+use PHPUnit\Framework\Attributes\TestDox;
+use PHPUnit\Framework\TestCase;
+
+#[CoversClass(MessageStatus::class)]
+class MessageStatusTest extends TestCase
+{
+ private MessageStatus $messageStatus;
+
+ private Sender $sender;
+
+ private string $testSenderCode = 'test_sdk_msg_status_1';
+
+ #[\Override]
+ protected function setUp(): void
+ {
+ $messageserviceServiceBuilder = Fabric::getServiceBuilder(true)->getMessageserviceScope();
+ $this->messageStatus = $messageserviceServiceBuilder->messageStatus();
+ $this->sender = $messageserviceServiceBuilder->sender();
+
+ // Ensure cleanup before test
+ try {
+ $this->sender->delete($this->testSenderCode);
+ } catch (\Throwable) {
+ // Ignore if sender does not exist
+ }
+
+ // Register a sender to use in tests
+ $this->sender->add(
+ code: $this->testSenderCode,
+ type: 'SMS',
+ handler: 'https://provider.example/api/handler',
+ name: 'Test SDK Message Status Sender'
+ );
+ }
+
+ #[\Override]
+ protected function tearDown(): void
+ {
+ try {
+ $this->sender->delete($this->testSenderCode);
+ } catch (\Throwable) {
+ // Ignore
+ }
+ }
+
+ #[Test]
+ #[TestDox('messageservice.message.status.update returns error for unknown message id (expected API error)')]
+ public function testUpdateReturnsExpectedErrorForUnknownMessage(): void
+ {
+ // The messageservice.message.status.update requires a real MESSAGE_ID from an actual sent message.
+ // Since integration tests cannot send real SMS, we verify that the API correctly rejects
+ // an unknown message ID with the expected error code.
+ $this->expectException(\Bitrix24\SDK\Core\Exceptions\BaseException::class);
+
+ $this->messageStatus->update(
+ code: $this->testSenderCode,
+ messageId: 'nonexistent_message_id_for_testing',
+ status: 'delivered'
+ );
+ }
+}
diff --git a/tests/Integration/Services/Messageservice/Sender/Service/SenderTest.php b/tests/Integration/Services/Messageservice/Sender/Service/SenderTest.php
new file mode 100644
index 00000000..ef1f3d94
--- /dev/null
+++ b/tests/Integration/Services/Messageservice/Sender/Service/SenderTest.php
@@ -0,0 +1,123 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Tests\Integration\Services\Messageservice\Sender\Service;
+
+use Bitrix24\SDK\Services\Messageservice\Sender\Service\Sender;
+use Bitrix24\SDK\Tests\Integration\Fabric;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\Test;
+use PHPUnit\Framework\Attributes\TestDox;
+use PHPUnit\Framework\TestCase;
+
+#[CoversClass(Sender::class)]
+class SenderTest extends TestCase
+{
+ private Sender $sender;
+
+ private string $testSenderCode = 'test_sdk_sender_1';
+
+ #[\Override]
+ protected function setUp(): void
+ {
+ $this->sender = Fabric::getServiceBuilder(true)->getMessageserviceScope()->sender();
+ // Ensure cleanup before each test
+ try {
+ $this->sender->delete($this->testSenderCode);
+ } catch (\Throwable) {
+ // Ignore if sender does not exist
+ }
+ }
+
+ #[\Override]
+ protected function tearDown(): void
+ {
+ // Cleanup after each test
+ try {
+ $this->sender->delete($this->testSenderCode);
+ } catch (\Throwable) {
+ // Ignore if sender was already deleted
+ }
+ }
+
+ #[Test]
+ #[TestDox('messageservice.sender.add registers a new sender successfully')]
+ public function testAdd(): void
+ {
+ $senderAddResult = $this->sender->add(
+ code: $this->testSenderCode,
+ type: 'SMS',
+ handler: 'https://provider.example/api/handler',
+ name: 'Test SDK Sender'
+ );
+
+ $this->assertTrue($senderAddResult->isSuccess());
+ }
+
+ #[Test]
+ #[TestDox('messageservice.sender.list returns array of sender codes')]
+ public function testList(): void
+ {
+ // Register a sender first
+ $this->sender->add(
+ code: $this->testSenderCode,
+ type: 'SMS',
+ handler: 'https://provider.example/api/handler',
+ name: 'Test SDK Sender'
+ );
+
+ $sendersListResult = $this->sender->list();
+ $codes = $sendersListResult->getSenderCodes();
+
+ $this->assertIsArray($codes);
+ $this->assertContains($this->testSenderCode, $codes);
+ }
+
+ #[Test]
+ #[TestDox('messageservice.sender.update updates an existing sender successfully')]
+ public function testUpdate(): void
+ {
+ // Register a sender first
+ $this->sender->add(
+ code: $this->testSenderCode,
+ type: 'SMS',
+ handler: 'https://provider.example/api/handler',
+ name: 'Test SDK Sender'
+ );
+
+ $senderUpdateResult = $this->sender->update(
+ code: $this->testSenderCode,
+ handler: 'https://provider.example/api/new-handler',
+ name: 'Test SDK Sender Updated'
+ );
+
+ $this->assertTrue($senderUpdateResult->isSuccess());
+ }
+
+ #[Test]
+ #[TestDox('messageservice.sender.delete removes an existing sender successfully')]
+ public function testDelete(): void
+ {
+ // Register a sender first
+ $this->sender->add(
+ code: $this->testSenderCode,
+ type: 'SMS',
+ handler: 'https://provider.example/api/handler',
+ name: 'Test SDK Sender'
+ );
+
+ $senderDeleteResult = $this->sender->delete($this->testSenderCode);
+
+ $this->assertTrue($senderDeleteResult->isSuccess());
+ }
+}
diff --git a/tests/Unit/Services/Messageservice/Message/Status/Service/MessageStatusTest.php b/tests/Unit/Services/Messageservice/Message/Status/Service/MessageStatusTest.php
new file mode 100644
index 00000000..f2ac7840
--- /dev/null
+++ b/tests/Unit/Services/Messageservice/Message/Status/Service/MessageStatusTest.php
@@ -0,0 +1,63 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Tests\Unit\Services\Messageservice\Message\Status\Service;
+
+use Bitrix24\SDK\Core\Contracts\CoreInterface;
+use Bitrix24\SDK\Core\Response\Response;
+use Bitrix24\SDK\Services\Messageservice\Message\Status\Result\MessageStatusUpdateResult;
+use Bitrix24\SDK\Services\Messageservice\Message\Status\Service\MessageStatus;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\TestDox;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\NullLogger;
+
+#[CoversClass(MessageStatus::class)]
+class MessageStatusTest extends TestCase
+{
+ #[TestDox('Test MessageStatus service can be instantiated')]
+ public function testCanBeInstantiated(): void
+ {
+ $core = $this->createStub(CoreInterface::class);
+ $messageStatus = new MessageStatus($core, new NullLogger());
+ $this->assertInstanceOf(MessageStatus::class, $messageStatus);
+ }
+
+ #[TestDox('Test MessageStatus::update builds correct parameters')]
+ public function testUpdateBuildsCorrectParameters(): void
+ {
+ $core = $this->createMock(CoreInterface::class);
+ $response = $this->createStub(Response::class);
+
+ $core->expects($this->once())
+ ->method('call')
+ ->with(
+ 'messageservice.message.status.update',
+ [
+ 'CODE' => 'provider1',
+ 'MESSAGE_ID' => '65575980fa531ac284c2ee68f81ebebd',
+ 'STATUS' => 'delivered',
+ ]
+ )
+ ->willReturn($response);
+
+ $messageStatus = new MessageStatus($core, new NullLogger());
+ $messageStatusUpdateResult = $messageStatus->update(
+ code: 'provider1',
+ messageId: '65575980fa531ac284c2ee68f81ebebd',
+ status: 'delivered'
+ );
+
+ $this->assertInstanceOf(MessageStatusUpdateResult::class, $messageStatusUpdateResult);
+ }
+}
diff --git a/tests/Unit/Services/Messageservice/Sender/Service/SenderTest.php b/tests/Unit/Services/Messageservice/Sender/Service/SenderTest.php
new file mode 100644
index 00000000..12c5f94f
--- /dev/null
+++ b/tests/Unit/Services/Messageservice/Sender/Service/SenderTest.php
@@ -0,0 +1,162 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Tests\Unit\Services\Messageservice\Sender\Service;
+
+use Bitrix24\SDK\Core\Contracts\CoreInterface;
+use Bitrix24\SDK\Core\Response\Response;
+use Bitrix24\SDK\Services\Messageservice\Sender\Result\SenderAddResult;
+use Bitrix24\SDK\Services\Messageservice\Sender\Result\SenderDeleteResult;
+use Bitrix24\SDK\Services\Messageservice\Sender\Result\SenderUpdateResult;
+use Bitrix24\SDK\Services\Messageservice\Sender\Result\SendersListResult;
+use Bitrix24\SDK\Services\Messageservice\Sender\Service\Sender;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\TestDox;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\NullLogger;
+
+#[CoversClass(Sender::class)]
+class SenderTest extends TestCase
+{
+ #[TestDox('Test Sender service can be instantiated')]
+ public function testCanBeInstantiated(): void
+ {
+ $core = $this->createStub(CoreInterface::class);
+ $sender = new Sender($core, new NullLogger());
+ $this->assertInstanceOf(Sender::class, $sender);
+ }
+
+ #[TestDox('Test Sender::add builds correct parameters')]
+ public function testAddBuildsCorrectParameters(): void
+ {
+ $core = $this->createMock(CoreInterface::class);
+ $response = $this->createStub(Response::class);
+
+ $core->expects($this->once())
+ ->method('call')
+ ->with(
+ 'messageservice.sender.add',
+ [
+ 'CODE' => 'provider1',
+ 'TYPE' => 'SMS',
+ 'HANDLER' => 'https://provider.example/api/handler',
+ 'NAME' => 'Provider 1',
+ 'DESCRIPTION' => 'Main SMS provider',
+ ]
+ )
+ ->willReturn($response);
+
+ $sender = new Sender($core, new NullLogger());
+ $senderAddResult = $sender->add(
+ code: 'provider1',
+ type: 'SMS',
+ handler: 'https://provider.example/api/handler',
+ name: 'Provider 1',
+ description: 'Main SMS provider'
+ );
+
+ $this->assertInstanceOf(SenderAddResult::class, $senderAddResult);
+ }
+
+ #[TestDox('Test Sender::add without optional description')]
+ public function testAddWithoutDescription(): void
+ {
+ $core = $this->createMock(CoreInterface::class);
+ $response = $this->createStub(Response::class);
+
+ $core->expects($this->once())
+ ->method('call')
+ ->with(
+ 'messageservice.sender.add',
+ [
+ 'CODE' => 'provider1',
+ 'TYPE' => 'SMS',
+ 'HANDLER' => 'https://provider.example/api/handler',
+ 'NAME' => 'Provider 1',
+ ]
+ )
+ ->willReturn($response);
+
+ $sender = new Sender($core, new NullLogger());
+ $senderAddResult = $sender->add(
+ code: 'provider1',
+ type: 'SMS',
+ handler: 'https://provider.example/api/handler',
+ name: 'Provider 1'
+ );
+
+ $this->assertInstanceOf(SenderAddResult::class, $senderAddResult);
+ }
+
+ #[TestDox('Test Sender::update builds correct parameters')]
+ public function testUpdateBuildsCorrectParameters(): void
+ {
+ $core = $this->createMock(CoreInterface::class);
+ $response = $this->createStub(Response::class);
+
+ $core->expects($this->once())
+ ->method('call')
+ ->with(
+ 'messageservice.sender.update',
+ [
+ 'CODE' => 'provider1',
+ 'HANDLER' => 'https://provider.example/api/new-handler',
+ 'NAME' => 'Provider 1 Updated',
+ ]
+ )
+ ->willReturn($response);
+
+ $sender = new Sender($core, new NullLogger());
+ $senderUpdateResult = $sender->update(
+ code: 'provider1',
+ handler: 'https://provider.example/api/new-handler',
+ name: 'Provider 1 Updated'
+ );
+
+ $this->assertInstanceOf(SenderUpdateResult::class, $senderUpdateResult);
+ }
+
+ #[TestDox('Test Sender::list calls correct API method')]
+ public function testListCallsCorrectMethod(): void
+ {
+ $core = $this->createMock(CoreInterface::class);
+ $response = $this->createStub(Response::class);
+
+ $core->expects($this->once())
+ ->method('call')
+ ->with('messageservice.sender.list', [])
+ ->willReturn($response);
+
+ $sender = new Sender($core, new NullLogger());
+ $sendersListResult = $sender->list();
+
+ $this->assertInstanceOf(SendersListResult::class, $sendersListResult);
+ }
+
+ #[TestDox('Test Sender::delete builds correct parameters')]
+ public function testDeleteBuildsCorrectParameters(): void
+ {
+ $core = $this->createMock(CoreInterface::class);
+ $response = $this->createStub(Response::class);
+
+ $core->expects($this->once())
+ ->method('call')
+ ->with('messageservice.sender.delete', ['CODE' => 'provider1'])
+ ->willReturn($response);
+
+ $sender = new Sender($core, new NullLogger());
+ $senderDeleteResult = $sender->delete('provider1');
+
+ $this->assertInstanceOf(SenderDeleteResult::class, $senderDeleteResult);
+ }
+}