From f16b3b4d6e855057c9c4c29238a5d94638b61c5c Mon Sep 17 00:00:00 2001 From: dino Date: Thu, 12 Nov 2026 15:31:32 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat(messages):=20=EB=B0=9C=EC=86=A1=C2=B7?= =?UTF-8?q?=EA=B7=B8=EB=A3=B9=20=EC=A1=B0=ED=9A=8C=EB=A5=BC=20=EA=B3=A0?= =?UTF-8?q?=EC=86=8D=EC=A0=91=EC=88=98(fast)=20=EC=97=94=EB=93=9C=ED=8F=AC?= =?UTF-8?q?=EC=9D=B8=ED=8A=B8=EB=A1=9C=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Models/Response/GroupCountForCharge.php | 4 +- src/Models/Response/GroupMessageResponse.php | 4 + src/Services/SolapiMessageService.php | 6 +- .../Response/ResponseConstructorTest.php | 134 +++++++++++++ tests/Services/SolapiMessageServiceTest.php | 180 +++++++++++++++++- tests/assets/fast-send-response.json | 147 ++++++++++++++ 6 files changed, 460 insertions(+), 15 deletions(-) create mode 100644 tests/assets/fast-send-response.json diff --git a/src/Models/Response/GroupCountForCharge.php b/src/Models/Response/GroupCountForCharge.php index bd3bffc..2b64a85 100644 --- a/src/Models/Response/GroupCountForCharge.php +++ b/src/Models/Response/GroupCountForCharge.php @@ -69,8 +69,8 @@ public function __construct($value = null) $this->lms = $value->lms ?? null; $this->mms = $value->mms ?? null; $this->ata = $value->ata ?? null; - $this->cta = $value->cta ?? null; - $this->cti = $value->cti ?? null; + $this->cta = $value->cta ?? (object) []; + $this->cti = $value->cti ?? (object) []; $this->nsa = $value->nsa ?? null; $this->rcs_sms = $value->rcs_sms ?? null; $this->rcs_lms = $value->rcs_lms ?? null; diff --git a/src/Models/Response/GroupMessageResponse.php b/src/Models/Response/GroupMessageResponse.php index 62efd3b..a69e97f 100644 --- a/src/Models/Response/GroupMessageResponse.php +++ b/src/Models/Response/GroupMessageResponse.php @@ -107,6 +107,10 @@ public function __construct($value = null) $this->balance = ResponseMapper::mapObject($value->balance ?? null, CommonCashResponse::class); $this->point = ResponseMapper::mapObject($value->point ?? null, CommonCashResponse::class); $this->app = $value->app ?? null; + if (isset($this->app->profit) && is_object($this->app->profit)) { + $this->app->profit->cta = $this->app->profit->cta ?? 0; + $this->app->profit->cti = $this->app->profit->cti ?? 0; + } $this->log = $value->log ?? null; $this->status = $value->status ?? null; $this->allowDuplicates = $value->allowDuplicates ?? null; diff --git a/src/Services/SolapiMessageService.php b/src/Services/SolapiMessageService.php index 95bf5f1..d5cbb24 100644 --- a/src/Services/SolapiMessageService.php +++ b/src/Services/SolapiMessageService.php @@ -51,7 +51,7 @@ public function send($messages, ?DateTime $scheduledDateTime = null): SendRespon $messages = array($messages); } $requestParameter = new SendRequest($messages, $scheduledDateTime); - $result = $this->fetcherInstance->request("POST", "/messages/v4/send-many/detail", $requestParameter); + $result = $this->fetcherInstance->request("POST", "/messages/v4/send-many/fast", $requestParameter); $response = new SendResponse($result); $count = $response->groupInfo !== null ? $response->groupInfo->count : null; @@ -140,7 +140,7 @@ public function getGroups(?GetGroupsRequest $parameter = null): ?GetGroupsRespon public function getGroup(string $groupId): ?GroupMessageResponse { try { - $result = $this->fetcherInstance->request("GET", "/messages/v4/groups/$groupId"); + $result = $this->fetcherInstance->request("GET", "/messages/v4/groups/fast/$groupId"); return $result !== null ? new GroupMessageResponse($result) : null; } catch (Exception $exception) { return null; @@ -156,7 +156,7 @@ public function getGroup(string $groupId): ?GroupMessageResponse public function getGroupMessages(string $groupId, ?GetGroupMessagesRequest $parameter = null): ?GetGroupMessagesResponse { try { - $result = $this->fetcherInstance->request("GET", "/messages/v4/groups/$groupId/messages", $parameter); + $result = $this->fetcherInstance->request("GET", "/messages/v4/groups/fast/$groupId/messages", $parameter); return $result !== null ? new GetGroupMessagesResponse($result) : null; } catch (Exception $exception) { return null; diff --git a/tests/Models/Response/ResponseConstructorTest.php b/tests/Models/Response/ResponseConstructorTest.php index 8c79db1..a22a319 100644 --- a/tests/Models/Response/ResponseConstructorTest.php +++ b/tests/Models/Response/ResponseConstructorTest.php @@ -385,4 +385,138 @@ public function testSendResponseJsonSerializeRoundTrip(): void $this->assertArrayHasKey('groupInfo', $decoded); $this->assertArrayHasKey('failedMessageList', $decoded); } + + // ---------- Fast intake (send-many/fast, groups/fast): deprecated cta/cti keys are absent ---------- + + private function fastSendResponseFixture(): stdClass + { + return json_decode(file_get_contents(__DIR__ . '/../../assets/fast-send-response.json')); + } + + public function testGroupCountForChargeDefaultsAbsentCtaCtiToEmptyObject(): void + { + $g = new GroupCountForCharge($this->obj(['sms' => ['82' => 1], 'lms' => []])); + + $this->assertInstanceOf(stdClass::class, $g->cta); + $this->assertInstanceOf(stdClass::class, $g->cti); + $this->assertSame([], get_object_vars($g->cta)); + $this->assertSame([], get_object_vars($g->cti)); + } + + public function testGroupCountForChargeKeepsCtaCtiWhenServerReturnsThem(): void + { + $g = new GroupCountForCharge($this->obj(['cta' => ['82' => 3], 'cti' => ['82' => 2]])); + + $this->assertSame(3, $g->cta->{'82'}); + $this->assertSame(2, $g->cti->{'82'}); + } + + public function testGroupCountForChargeDefaultsCtaCtiEvenWhenValueIsEmptyObject(): void + { + $g = new GroupCountForCharge($this->obj([])); + + $this->assertSame([], get_object_vars($g->cta)); + $this->assertSame([], get_object_vars($g->cti)); + $this->assertNull($g->sms); + } + + public function testGroupMessageResponseDefaultsAbsentAppProfitCtaCtiToZero(): void + { + $r = new GroupMessageResponse($this->obj([ + 'app' => ['profit' => ['sms' => 0, 'lms' => 0], 'appId' => null, 'version' => null], + ])); + + $this->assertSame(0, $r->app->profit->cta); + $this->assertSame(0, $r->app->profit->cti); + $this->assertSame(0, $r->app->profit->sms); + $this->assertNull($r->app->appId); + } + + public function testGroupMessageResponseKeepsAppProfitCtaCtiWhenPresent(): void + { + $r = new GroupMessageResponse($this->obj([ + 'app' => ['profit' => ['sms' => 0, 'cta' => 7, 'cti' => 9]], + ])); + + $this->assertSame(7, $r->app->profit->cta); + $this->assertSame(9, $r->app->profit->cti); + } + + /** + * @return array + */ + public function provideAppShapesWithoutProfitObject(): array + { + return [ + 'app absent' => [[]], + 'app null' => [['app' => null]], + 'app without profit' => [['app' => ['appId' => 'A1', 'version' => null]]], + 'profit null' => [['app' => ['profit' => null]]], + 'profit scalar' => [['app' => ['profit' => 5]]], + ]; + } + + /** + * @dataProvider provideAppShapesWithoutProfitObject + */ + public function testGroupMessageResponseLeavesAppUntouchedWhenProfitIsNotAnObject(array $data): void + { + $errors = []; + set_error_handler(static function ($severity, $message) use (&$errors) { + $errors[] = $message; + return true; + }); + + try { + $r = new GroupMessageResponse($this->obj($data)); + } finally { + restore_error_handler(); + } + + $this->assertSame([], $errors); + $expected = $this->obj($data); + $this->assertEquals($expected->app ?? null, $r->app); + } + + public function testSendResponseDecodesFastSendFixtureWithoutPhpNotices(): void + { + $errors = []; + set_error_handler(static function ($severity, $message) use (&$errors) { + $errors[] = $message; + return true; + }); + + try { + $r = new SendResponse($this->fastSendResponseFixture()); + } finally { + restore_error_handler(); + } + + $this->assertSame([], $errors); + $this->assertSame('SEND-REQUESTED', $r->groupInfo->status); + $this->assertSame('G4V20260101000000TESTGROUP00000', $r->groupInfo->groupId); + $this->assertSame(1, $r->groupInfo->count->sentPending); + $this->assertSame([], $r->failedMessageList); + } + + public function testSendResponseFillsFastSendDefaultsForDeprecatedCtaCti(): void + { + $r = new SendResponse($this->fastSendResponseFixture()); + + $this->assertSame([], get_object_vars($r->groupInfo->countForCharge->cta)); + $this->assertSame([], get_object_vars($r->groupInfo->countForCharge->cti)); + $this->assertSame(1, $r->groupInfo->countForCharge->sms->{'82'}); + $this->assertSame(0, $r->groupInfo->app->profit->cta); + $this->assertSame(0, $r->groupInfo->app->profit->cti); + } + + public function testSendResponseJsonSerializeIncludesDefaultedCtaCtiKeys(): void + { + $decoded = json_decode(json_encode(new SendResponse($this->fastSendResponseFixture())), true); + + $this->assertSame([], $decoded['groupInfo']['countForCharge']['cta']); + $this->assertSame([], $decoded['groupInfo']['countForCharge']['cti']); + $this->assertSame(0, $decoded['groupInfo']['app']['profit']['cta']); + $this->assertSame(0, $decoded['groupInfo']['app']['profit']['cti']); + } } diff --git a/tests/Services/SolapiMessageServiceTest.php b/tests/Services/SolapiMessageServiceTest.php index 4b1ebc1..997df66 100644 --- a/tests/Services/SolapiMessageServiceTest.php +++ b/tests/Services/SolapiMessageServiceTest.php @@ -53,14 +53,14 @@ public function provideGetMethodsRegression(): array 'getGroup returns GroupMessageResponse, not stdClass' => [ 'getGroup', 'GET', - '/messages/v4/groups/G4V01', + '/messages/v4/groups/fast/G4V01', '{"groupId":"G4V01","status":"COMPLETE","accountId":"129"}', GroupMessageResponse::class, ], 'getGroupMessages returns GetGroupMessagesResponse, not stdClass' => [ 'getGroupMessages', 'GET', - '/messages/v4/groups/G4V01/messages', + '/messages/v4/groups/fast/G4V01/messages', '{"limit":20,"messageList":{},"startKey":null,"nextKey":"abc"}', GetGroupMessagesResponse::class, ], @@ -115,7 +115,7 @@ public function testGetGroupConvertsNestedCountIntoGroupCountInstance(): void 'registeredSuccess' => 8, ], ]); - $this->http->respondTo('GET', '/messages/v4/groups/G4V01', 200, $body); + $this->http->respondTo('GET', '/messages/v4/groups/fast/G4V01', 200, $body); $response = $this->service->getGroup('G4V01'); @@ -165,7 +165,7 @@ public function testGetBalanceHandlesMissingFieldsAsNull(): void public function testGetGroupHandlesMissingNestedObjectsAsNull(): void { - $this->http->respondTo('GET', '/messages/v4/groups/G4V01', 200, '{"groupId":"G4V01"}'); + $this->http->respondTo('GET', '/messages/v4/groups/fast/G4V01', 200, '{"groupId":"G4V01"}'); $response = $this->service->getGroup('G4V01'); @@ -236,8 +236,8 @@ public function provideHttpErrors(): array return [ 'getBalance 4xx returns null' => ['getBalance', '/cash/v1/balance', 400, '{"errorCode":"E400","errorMessage":"bad"}'], 'getBalance 5xx returns null' => ['getBalance', '/cash/v1/balance', 500, ''], - 'getGroup 404 returns null' => ['getGroup', '/messages/v4/groups/G4V01', 404, '{"errorCode":"NotFound","errorMessage":"missing"}'], - 'getGroupMessages 4xx null' => ['getGroupMessages', '/messages/v4/groups/G4V01/messages', 401, '{"errorCode":"Unauthorized","errorMessage":"x"}'], + 'getGroup 404 returns null' => ['getGroup', '/messages/v4/groups/fast/G4V01', 404, '{"errorCode":"NotFound","errorMessage":"missing"}'], + 'getGroupMessages 4xx null' => ['getGroupMessages', '/messages/v4/groups/fast/G4V01/messages', 401, '{"errorCode":"Unauthorized","errorMessage":"x"}'], 'getStatistics 5xx null' => ['getStatistics', '/messages/v4/statistics', 502, ''], 'getMessages 4xx null' => ['getMessages', '/messages/v4/list', 403, '{"errorCode":"Forbidden","errorMessage":"x"}'], 'getGroups 5xx null' => ['getGroups', '/messages/v4/groups', 500, ''], @@ -311,7 +311,7 @@ public function testSendDoesNotThrowWhenApiResponseOmitsGroupInfo(): void $this->http->respondTo( 'POST', - '/messages/v4/send-many/detail', + '/messages/v4/send-many/fast', 200, '{"failedMessageList":[]}' ); @@ -337,7 +337,7 @@ public function testSendThrowsMessageNotReceivedExceptionOnlyWhenAllRegistration ['to' => '01000000000', 'statusCode' => 'N000'], ], ]); - $this->http->respondTo('POST', '/messages/v4/send-many/detail', 200, $body); + $this->http->respondTo('POST', '/messages/v4/send-many/fast', 200, $body); $this->expectException(\Nurigo\Solapi\Exceptions\MessageNotReceivedException::class); @@ -361,13 +361,13 @@ public function testGetBalanceIssuesGetRequestToCashBalanceEndpoint(): void public function testGetGroupMessagesAppendsGroupIdToPath(): void { - $this->http->respondTo('GET', '/messages/v4/groups/G4V123/messages', 200, '{"limit":20,"messageList":{}}'); + $this->http->respondTo('GET', '/messages/v4/groups/fast/G4V123/messages', 200, '{"limit":20,"messageList":{}}'); $this->service->getGroupMessages('G4V123'); $this->assertCount(1, $this->http->receivedRequests); $this->assertSame( - '/messages/v4/groups/G4V123/messages', + '/messages/v4/groups/fast/G4V123/messages', $this->http->receivedRequests[0]->getUri()->getPath() ); } @@ -385,4 +385,164 @@ public function testGetBalanceIsIdempotentWhenApiResponseUnchanged(): void $this->assertEquals($a, $b); $this->assertNotSame($a, $b, 'Each call should return a fresh instance'); } + + // ---------- Fast intake routing: send / getGroup / getGroupMessages use the fast endpoints ---------- + + private function fastSendResponseBody(): string + { + return file_get_contents(__DIR__ . '/../assets/fast-send-response.json'); + } + + public function testSendIssuesPostToSendManyFastEndpoint(): void + { + $message = new \Nurigo\Solapi\Models\Message(); + $message->setTo('01000000000')->setFrom('01087654321')->setText('hi'); + $this->http->respondTo('POST', '/messages/v4/send-many/fast', 200, $this->fastSendResponseBody()); + + $this->service->send($message); + + $this->assertCount(1, $this->http->receivedRequests); + $req = $this->http->receivedRequests[0]; + $this->assertSame('POST', $req->getMethod()); + $this->assertSame('/messages/v4/send-many/fast', $req->getUri()->getPath()); + $this->assertSame('', $req->getUri()->getQuery()); + $body = json_decode((string) $req->getBody(), true); + $this->assertSame('01000000000', $body['messages'][0]['to']); + } + + public function testSendDecodesFastResponseWithoutCtaCtiAndReportsSendRequested(): void + { + $message = new \Nurigo\Solapi\Models\Message(); + $message->setTo('01000000000')->setFrom('01087654321')->setText('hi'); + $this->http->respondTo('POST', '/messages/v4/send-many/fast', 200, $this->fastSendResponseBody()); + + $errors = []; + set_error_handler(static function ($severity, $message) use (&$errors) { + $errors[] = $message; + return true; + }); + try { + $response = $this->service->send($message); + } finally { + restore_error_handler(); + } + + $this->assertSame([], $errors); + $this->assertSame('SEND-REQUESTED', $response->groupInfo->status); + $this->assertSame(1, $response->groupInfo->count->sentPending); + $this->assertSame([], get_object_vars($response->groupInfo->countForCharge->cta)); + $this->assertSame([], get_object_vars($response->groupInfo->countForCharge->cti)); + $this->assertSame(0, $response->groupInfo->app->profit->cta); + $this->assertSame(0, $response->groupInfo->app->profit->cti); + $this->assertSame([], $response->failedMessageList); + } + + public function testSendDoesNotFallBackToDetailEndpointWhenFastEndpointFails(): void + { + $message = new \Nurigo\Solapi\Models\Message(); + $message->setTo('01000000000')->setFrom('01087654321')->setText('hi'); + $this->http->respondTo('POST', '/messages/v4/send-many/detail', 200, $this->fastSendResponseBody()); + $this->http->respondTo('POST', '/messages/v4/send-many/fast', 400, '{"errorCode":"ValidationError","errorMessage":"bad"}'); + + try { + $this->service->send($message); + $this->fail('send() must surface the fast endpoint error'); + } catch (\Nurigo\Solapi\Exceptions\BaseException $e) { + $this->assertSame('ValidationError', $e->errorCode); + } + + $this->assertCount(1, $this->http->receivedRequests); + $this->assertSame('/messages/v4/send-many/fast', $this->http->receivedRequests[0]->getUri()->getPath()); + } + + public function testGetGroupIssuesGetToFastGroupEndpoint(): void + { + $groupId = 'G4V20260101000000TESTGROUP00000'; + $groupBody = json_encode(json_decode($this->fastSendResponseBody())->groupInfo); + $this->http->respondTo('GET', "/messages/v4/groups/fast/$groupId", 200, $groupBody); + + $response = $this->service->getGroup($groupId); + + $this->assertCount(1, $this->http->receivedRequests); + $req = $this->http->receivedRequests[0]; + $this->assertSame('GET', $req->getMethod()); + $this->assertSame("/messages/v4/groups/fast/$groupId", $req->getUri()->getPath()); + $this->assertSame('', $req->getUri()->getQuery()); + $this->assertSame('SEND-REQUESTED', $response->status); + $this->assertSame([], get_object_vars($response->countForCharge->cta)); + $this->assertSame(0, $response->app->profit->cti); + } + + public function testGetGroupReturnsNullForNonExistentGroupOnFastEndpoint(): void + { + $this->http->respondTo( + 'GET', + '/messages/v4/groups/fast/G4VNOPE', + 404, + '{"errorCode":"ResourceNotFound","errorMessage":"리소스를 찾을 수 없습니다."}' + ); + + $this->assertNull($this->service->getGroup('G4VNOPE')); + $this->assertSame('/messages/v4/groups/fast/G4VNOPE', $this->http->receivedRequests[0]->getUri()->getPath()); + } + + public function testGetGroupMessagesIssuesGetToFastGroupMessagesEndpointWithUnchangedQuery(): void + { + $this->http->respondTo('GET', '/messages/v4/groups/fast/G4V123/messages', 200, '{"limit":2,"messageList":{},"startKey":"M4V01","nextKey":null}'); + $parameter = new \Nurigo\Solapi\Models\Request\GetGroupMessagesRequest(); + $parameter->setStartKey('M4V01')->setLimit(2); + + $response = $this->service->getGroupMessages('G4V123', $parameter); + + $this->assertCount(1, $this->http->receivedRequests); + $req = $this->http->receivedRequests[0]; + $this->assertSame('GET', $req->getMethod()); + $this->assertSame('/messages/v4/groups/fast/G4V123/messages', $req->getUri()->getPath()); + parse_str($req->getUri()->getQuery(), $query); + $this->assertSame(['startKey' => 'M4V01', 'limit' => '2'], $query); + $this->assertSame(2, $response->limit); + $this->assertSame('M4V01', $response->startKey); + } + + public function testGetGroupMessagesReturnsEmptyListForNonExistentGroupOnFastEndpoint(): void + { + $this->http->respondTo('GET', '/messages/v4/groups/fast/G4VNOPE/messages', 200, '{"startKey":null,"limit":20,"messageList":{}}'); + + $response = $this->service->getGroupMessages('G4VNOPE'); + + $this->assertInstanceOf(GetGroupMessagesResponse::class, $response); + $this->assertSame([], $response->messageList); + } + + public function testGetGroupMessagesExposesCustomFieldsOnFastItems(): void + { + $body = json_encode([ + 'limit' => 20, + 'messageList' => [ + 'M4V01' => ['messageId' => 'M4V01', 'statusCode' => '2000', 'customFields' => ['orderId' => '42']], + ], + ]); + $this->http->respondTo('GET', '/messages/v4/groups/fast/G4V123/messages', 200, $body); + + $response = $this->service->getGroupMessages('G4V123'); + + $this->assertCount(1, $response->messageList); + $this->assertSame('42', $response->messageList[0]->customFields->orderId); + } + + public function testGetMessagesAndStatisticsStillUseLegacyEndpoints(): void + { + $this->http->respondTo('GET', '/messages/v4/list', 200, '{"limit":20,"messageList":{}}'); + $this->http->respondTo('GET', '/messages/v4/groups', 200, '{"limit":20,"groupList":{}}'); + $this->http->respondTo('GET', '/messages/v4/statistics', 200, '{"balance":0,"point":0}'); + + $this->service->getMessages(); + $this->service->getGroups(); + $this->service->getStatistics(); + + $paths = array_map(static function ($req) { + return $req->getUri()->getPath(); + }, $this->http->receivedRequests); + $this->assertSame(['/messages/v4/list', '/messages/v4/groups', '/messages/v4/statistics'], $paths); + } } diff --git a/tests/assets/fast-send-response.json b/tests/assets/fast-send-response.json new file mode 100644 index 0000000..c4bc007 --- /dev/null +++ b/tests/assets/fast-send-response.json @@ -0,0 +1,147 @@ +{ + "messageList": [ + { + "messageId": "M4V20260101000000TESTMESSAGE000", + "statusCode": "2000", + "statusMessage": "정상 접수(이통사로 접수 예정) ", + "customFields": {} + } + ], + "failedMessageList": [], + "groupInfo": { + "deposit": { + "requested": 0, + "replacement": 0, + "additional": 0, + "refund": 0, + "sum": 0 + }, + "isRefunded": false, + "workspaceId": null, + "_id": "G4V20260101000000TESTGROUP00000", + "groupId": "G4V20260101000000TESTGROUP00000", + "serviceMethod": "MT", + "accountId": "12345678901234", + "apiVersion": "v4-fast", + "sdkVersion": "php/probe", + "osPlatform": "probe", + "count": { + "total": 1, + "sentTotal": 0, + "sentFailed": 0, + "sentSuccess": 0, + "sentPending": 1, + "sentReplacement": 0, + "refund": 0, + "registeredFailed": 0, + "registeredSuccess": 1 + }, + "countForCharge": { + "sms": { + "82": 1 + }, + "lms": {}, + "mms": {}, + "ata": {}, + "nsa": {}, + "rcs_sms": {}, + "rcs_lms": {}, + "rcs_mms": {}, + "rcs_tpl": {}, + "rcs_itpl": {}, + "rcs_ltpl": {}, + "fax": {}, + "voice": {}, + "bms_text": {}, + "bms_image": {}, + "bms_wide": {}, + "bms_wide_item_list": {}, + "bms_carousel_feed": {}, + "bms_premium_video": {}, + "bms_commerce": {}, + "bms_carousel_commerce": {}, + "bms_free": {} + }, + "log": [ + { + "createAt": "2026-01-01T00:00:00.000Z", + "message": "메시지 그룹이 생성되었습니다." + } + ], + "status": "SEND-REQUESTED", + "dateSent": "2026-01-01T00:00:00.000Z", + "scheduledDate": null, + "dateCompleted": null, + "balance": { + "requested": 0, + "replacement": 0, + "additional": 0, + "refund": 0, + "sum": 0 + }, + "point": { + "requested": 13, + "replacement": 0, + "additional": 0, + "refund": 0, + "sum": 13 + }, + "clusterKey": null, + "app": { + "profit": { + "sms": 0, + "lms": 0, + "mms": 0, + "ata": 0, + "nsa": 0, + "rcs_sms": 0, + "rcs_lms": 0, + "rcs_mms": 0, + "rcs_tpl": 0, + "rcs_itpl": 0, + "rcs_ltpl": 0, + "fax": 0, + "voice": 0, + "bms_text": 0, + "bms_image": 0, + "bms_wide": 0, + "bms_wide_item_list": 0, + "bms_carousel_feed": 0, + "bms_premium_video": 0, + "bms_commerce": 0, + "bms_carousel_commerce": 0, + "bms_free": 0 + }, + "appId": null, + "version": null + }, + "price": { + "82": { + "sms": 13, + "lms": 29, + "mms": 60, + "ata": 8, + "cta": 14, + "cti": 22, + "nsa": 6, + "rcs_sms": 11, + "rcs_lms": 28, + "rcs_mms": 58, + "rcs_tpl": 8, + "bms_free": 100, + "fax": 100, + "voice": 200 + } + }, + "flagUpdated": false, + "customFields": {}, + "prepaid": true, + "strict": false, + "hint": {}, + "masterAccountId": null, + "allowDuplicates": false, + "originCode": null, + "dateCreated": "2026-01-01T00:00:00.000Z", + "dateUpdated": "2026-01-01T00:00:00.000Z" + } +} From 00f2c18575735711b2190b0499fdb1172a9ad8c5 Mon Sep 17 00:00:00 2001 From: dino Date: Thu, 12 Nov 2026 15:31:32 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix(models):=20GroupCount.sendTotal?= =?UTF-8?q?=EC=9D=84=20=EC=84=9C=EB=B2=84=20=EC=9D=91=EB=8B=B5=20=ED=82=A4?= =?UTF-8?q?=20sentTotal=EC=97=90=EC=84=9C=20=EB=A7=A4=ED=95=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Models/Response/GroupCount.php | 2 +- .../Response/ResponseConstructorTest.php | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Models/Response/GroupCount.php b/src/Models/Response/GroupCount.php index 6d17cca..61debac 100644 --- a/src/Models/Response/GroupCount.php +++ b/src/Models/Response/GroupCount.php @@ -55,7 +55,7 @@ class GroupCount public function __construct($value = null) { $this->total = $value->total ?? null; - $this->sendTotal = $value->sendTotal ?? null; + $this->sendTotal = $value->sentTotal ?? $value->sendTotal ?? null; $this->sentFailed = $value->sentFailed ?? null; $this->sentSuccess = $value->sentSuccess ?? null; $this->sentPending = $value->sentPending ?? null; diff --git a/tests/Models/Response/ResponseConstructorTest.php b/tests/Models/Response/ResponseConstructorTest.php index a22a319..7695ce6 100644 --- a/tests/Models/Response/ResponseConstructorTest.php +++ b/tests/Models/Response/ResponseConstructorTest.php @@ -519,4 +519,37 @@ public function testSendResponseJsonSerializeIncludesDefaultedCtaCtiKeys(): void $this->assertSame(0, $decoded['groupInfo']['app']['profit']['cta']); $this->assertSame(0, $decoded['groupInfo']['app']['profit']['cti']); } + + // ---------- GroupCount: server key is sentTotal, SDK property is sendTotal ---------- + + /** + * @return array + */ + public function provideSentTotalKeyVariants(): array + { + return [ + 'server sentTotal lands in sendTotal' => [['sentTotal' => 7], 7], + 'legacy sendTotal still accepted' => [['sendTotal' => 4], 4], + 'sentTotal wins over sendTotal when both present' => [['sentTotal' => 7, 'sendTotal' => 4], 7], + 'sentTotal zero is kept as zero' => [['sentTotal' => 0], 0], + 'neither key gives null' => [['total' => 1], null], + ]; + } + + /** + * @dataProvider provideSentTotalKeyVariants + */ + public function testGroupCountPopulatesSendTotalFromServerSentTotalKey(array $data, ?int $expected): void + { + $c = new GroupCount($this->obj($data)); + + $this->assertSame($expected, $c->sendTotal); + } + + public function testSendResponseFixtureSentTotalReachesGroupCountSendTotal(): void + { + $r = new SendResponse($this->fastSendResponseFixture()); + + $this->assertSame(0, $r->groupInfo->count->sendTotal); + } }