btlqql opened a new pull request, #4692:
URL: https://github.com/apache/rocketmq-dashboard/pull/4692

   <!-- Base branch: `rocketmq-studio`, the RocketMQ Studio trunk. -->
   
   ### Brief Description
   
   `InstanceService` bounded the two failure texts it reports back to the 
instance page by UTF-16
   `char`:
   
   ```java
   // boundedCloudImportText, InstanceService.java:374-380 on the base
   if (singleLine.length() <= maxLength) {
       return singleLine;
   }
   return singleLine.substring(0, maxLength - 1) + "…";
   
   // batchFailureMessage, InstanceService.java:705-713 on the base
   return message.length() > MAX_BATCH_FAILURE_MESSAGE_LENGTH
           ? message.substring(0, MAX_BATCH_FAILURE_MESSAGE_LENGTH) : message;
   ```
   
   Both caps are 500 (`MAX_BATCH_FAILURE_MESSAGE_LENGTH` and
   `MAX_CLOUD_IMPORT_FAILURE_MESSAGE_LENGTH`).
   
   A supplementary character - an emoji, a CJK extension character - is two 
chars, so a cut can land
   between its high and its low surrogate and keep half of one. An unpaired 
surrogate is not a code
   point and has no UTF-8 encoding, so the value that reaches the JSON response 
carries a replacement
   character instead of the character it was cut out of. The two surfaces are 
the batch-delete result
   (`BatchDeleteResultVO.failed`) and the cloud-import result 
(`CloudImportResultVO.failed`), which the
   instance page joins into its dialogs 
(`web/src/pages/instance/index.tsx:405-416` and `:502-507`).
   Those messages are free text from the vendor SDKs and from the RocketMQ 
admin client, i.e. exactly
   where an operator sees a region name or an endpoint containing an emoji or a 
CJK character.
   
   Concretely, a 500-char `IllegalStateException` message whose char 500 is the 
high surrogate of
   `U+1F600` came back in `BatchDeleteResultVO.failed` as `"inst-a: " + 499 × 
"x"` followed by that lone
   high surrogate. The cloud-import cap applies to `target + ": " + message`, 
so a 496-code-point
   failure message prefixed with `regions: ` was cut through the emoji at code 
point 499 in the same
   way.
   
   Both caps now count code points and cut with `offsetByCodePoints`, the rule
   `MessagePropertyDisplay` already applies to message property values. Text 
within the cap is returned
   unchanged, an ASCII or BMP message that was cut before is still cut at the 
same 500-character
   boundary, and a message that contains a supplementary character now keeps 
whole characters instead
   of half of one:
   
   ```java
   if (codePointCount(singleLine) <= maxLength) {
       return singleLine;
   }
   return singleLine.substring(0, singleLine.offsetByCodePoints(0, maxLength - 
1)) + "…";
   ```
   
   No issue exists for this instance defect: the tracker has no report naming 
`InstanceService`'s
   failure messages, and searches for `surrogate`, `truncat`, `emoji` and `code 
point` only return the
   closed message-property issues (#4167, #4287, #2252) that the merged
   `fix(message): abbreviate message properties on code point boundaries` 
(#4467) fixed in
   `common/util/MessagePropertyDisplay.java`. This change touches 
`InstanceService` only, so it is a
   separate defect from that one.
   
   ### How Did You Test This Change?
   
   Two tests in `InstanceServiceTest` pin the exact cut end and assert that the 
reported detail holds no
   unpaired surrogate. The helper `hasUnpairedSurrogate` walks the UTF-16 units 
and rejects a high
   surrogate that is not followed by a low one, so half a character cannot pass 
unnoticed.
   
   Before the fix (red) - the committed test file dropped on the unmodified 
base (`7ce9a682`, only
   `InstanceServiceTest.java` modified, service untouched):
   
   ```
   $ cd server && mvn -B -ntp test 
-Dtest='InstanceServiceTest#deleteInstancesShouldCutFailureMessagesOnCodePointBoundariesTest+importCloudInstancesShouldBoundFailureMessagesOnCodePointBoundariesTest'
   [ERROR] Tests run: 2, Failures: 2, Errors: 0, Skipped: 0, Time elapsed: 
2.899 s <<< FAILURE! -- in 
org.apache.rocketmq.studio.instance.InstanceServiceTest
   [ERROR] 
org.apache.rocketmq.studio.instance.InstanceServiceTest.deleteInstancesShouldCutFailureMessagesOnCodePointBoundariesTest
 -- Time elapsed: 2.716 s <<< FAILURE!
   Expecting value to be false but was true
        at 
InstanceServiceTest.lambda$deleteInstancesShouldCutFailureMessagesOnCodePointBoundariesTest$78(InstanceServiceTest.java:1753)
   [ERROR] 
org.apache.rocketmq.studio.instance.InstanceServiceTest.importCloudInstancesShouldBoundFailureMessagesOnCodePointBoundariesTest
 -- Time elapsed: 0.091 s <<< FAILURE!
   Expecting value to be false but was true
        at 
InstanceServiceTest.lambda$importCloudInstancesShouldBoundFailureMessagesOnCodePointBoundariesTest$79(InstanceServiceTest.java:1770)
   [INFO] BUILD FAILURE
   ```
   
   The failing assertion is `hasUnpairedSurrogate(detail)`; the same run logs 
the truncated message the
   caller received, ending in the unpaired surrogate:
   
   ```
   WARN org.apache.rocketmq.studio.instance.InstanceService -- Failed to delete 
instance inst-a during
   batch operation: xxxxx...xxxxx?
   ```
   
   The console encoder cannot print a lone surrogate, so it writes `?`; the 
assertion above is the
   programmatic evidence. `detail` is 499 `x` plus the high surrogate of the 
emoji, i.e. the exact string
   the batch-delete dialog would have shown.
   
   After the fix (green) - the committed tree, the whole instance package test 
set:
   
   ```
   $ cd server && mvn -B -ntp test 
-Dtest='InstanceServiceTest,InstanceResolverTest,InstanceCapabilityServiceTest,MybatisPlusInstanceRepositoryTest,InstanceControllerTest,InstanceResourceCountRunnerTest'
   [INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0 -- in 
org.apache.rocketmq.studio.instance.InstanceCapabilityServiceTest
   [INFO] Tests run: 12, Failures: 0, Errors: 0, Skipped: 0 -- in 
org.apache.rocketmq.studio.instance.InstanceControllerTest
   [INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0 -- in 
org.apache.rocketmq.studio.instance.InstanceResolverTest
   [INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0 -- in 
org.apache.rocketmq.studio.instance.InstanceResourceCountRunnerTest
   [INFO] Tests run: 85, Failures: 0, Errors: 0, Skipped: 0 -- in 
org.apache.rocketmq.studio.instance.InstanceServiceTest
   [INFO] Tests run: 18, Failures: 0, Errors: 0, Skipped: 0 -- in 
org.apache.rocketmq.studio.instance.MybatisPlusInstanceRepositoryTest
   [INFO] Tests run: 130, Failures: 0, Errors: 0, Skipped: 0
   [INFO] BUILD SUCCESS
   [INFO] You have 0 Checkstyle violations.
   ```
   
   The two new tests also assert the positive half of the cap, so the 
truncation itself is still covered:
   the batch detail is exactly `"inst-a: " + 499 × "x" + emoji` and the import 
detail is exactly
   `"regions: " + 489 × "x" + emoji + "…"` with `codePointCount(detail) == 500`.
   
   Note on the full suite: on a clean `rocketmq-studio` checkout `mvn -B -ntp 
test` already reports
   `Tests run: 3051, Failures: 6, Errors: 25, Skipped: 4`. The 11 red classes 
are the MySQL 8 backed
   Spring integration tests (`AuthServiceBootstrapIntegrationTest`,
   `AuthServiceConcurrencyIntegrationTest`, 
`AuthServiceSessionOverviewIntegrationTest`,
   `HealthProbeIntegrationTest`, `QueryHistoryServiceIntegrationTest`,
   `NativeAlertEvaluationTransactionTest`, 
`NotificationOutboxMapperIntegrationTest`,
   `RmqAlertStateMapperIntegrationTest`, `StudioApplicationTest`) plus the 
external-CLI ones
   (`CliAgentProviderTest`, `ClaudeCodeAgentProviderTest`). None of them are 
touched by this change; the
   six classes above are the ones this change can affect.
   
   ### Checklist
   
   - [x] One coherent change; unrelated modifications are not bundled in
   - [x] Commit subject follows Conventional Commits (`fix(instance): …`)
   - [x] Tests added or updated for non-trivial changes, test methods named 
`...Test`
   - [x] New UI text has both Chinese and English entries under `web/src/i18n/` 
(no UI text in this change)
   - [x] Architecture constraints stay green (`mvn test` runs the ArchUnit 
checks)
   - [x] New source files carry the ASF license header (no new files, no 
non-ASCII source literal: the emoji is written as a `\uD83D\uDE00` escape)
   - [x] Documentation touched where behaviour changed (the two cap constants 
and both cut sites now state that they count code points; no user-visible 
contract change)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to