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]