yyqdbngt opened a new pull request, #4687:
URL: https://github.com/apache/rocketmq-dashboard/pull/4687
<!-- Base branch: `rocketmq-studio`, the RocketMQ Studio trunk. -->
### Brief Description
`AiConversationService.deriveTitle` and `AiConversationService.capTitle`
capped the conversation title
by UTF-16 `char`:
```java
return folded.substring(0, TITLE_MAX_CHARS);
// 40
return title.length() <= TITLE_COLUMN_MAX_CHARS ? title : title.substring(0,
TITLE_COLUMN_MAX_CHARS);
```
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 database and
the client holds a
replacement character instead of the character it was cut out of. The title
is both
`rmq_ai_conversation.title` and the label of the conversation list, i.e. the
first thing the user
sees about a conversation, and the *first* message that produces it is
exactly the free-text field a
user is most likely to put an emoji or a Chinese/CJK character in.
Concretely, `deriveTitle("a" * 39 + "🚀" + "tail")` used to return 39 `a`s
plus the lone high
surrogate `U+D83D`, and `capTitle(511 × "b" + "🚀" + "tail")` did the same
against the 512-char column.
Both caps now count code points and cut with `offsetByCodePoints`, which is
the rule
`MessagePropertyDisplay` already applies to message property values (its
constant is
`MAX_PROPERTY_VALUE_CODE_POINTS` for that reason). The two constants keep
their names but their caps
are code-point budgets, so a 40-emoji message now keeps 40 emoji instead of
20 — the ceiling the
javadoc always described.
### How Did You Test This Change?
Extended
`AiConversationServiceTest#deriveTitleShouldFoldWhitespaceAndCapTheLengthTest`
(the test that
already pins the 40/512 caps) with the boundary cases: 39 ASCII chars + an
astral character + trailing
text, the same for the 512-char rename cap, and a title made only of astral
characters.
Before the fix (red) — the cut kept the high surrogate of the emoji and
dropped its low surrogate, so
the value no longer equals the string it was cut out of:
```
$ cd server && mvn -B -ntp test
-Dtest='AiConversationServiceTest#deriveTitleShouldFoldWhitespaceAndCapTheLengthTest'
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed:
2.756 s <<< FAILURE! -- in
org.apache.rocketmq.studio.ops.ai.conversation.AiConversationServiceTest
org.opentest4j.AssertionFailedError:
expected: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa🚀"
but was: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
```
The `but was:` line is the 39 `a`s followed by the unpaired high surrogate
`U+D83D`; JUnit stops
printing there because AssertJ renders that lone surrogate as a replacement
character. The full text
from `target/surefire-reports/...AiConversationServiceTest.txt` is the
assertion above.
After the fix (green) — the focused class plus the neighbouring conversation
classes:
```
$ cd server && mvn -B -ntp test
-Dtest='AiConversationServiceTest,AiConversationControllerTest,AiConversationPersistenceIntegrationTest,AiConversationVoAssemblerTest,AiRunExecutorTest,AiRunServiceTest'
[INFO] You have 0 Checkstyle violations.
[INFO] Tests run: 18, Failures: 0, Errors: 0, Skipped: 0 -- in
org.apache.rocketmq.studio.ops.ai.conversation.AiConversationServiceTest
[INFO] Tests run: 108, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS
```
The pre-existing cap assertions (`deriveTitle("x".repeat(120)).hasSize(40)`,
`capTitle("y".repeat(600)).hasSize(512)`) are unchanged and still pass, so
an ASCII title behaves
exactly as before.
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.
### Checklist
- [x] One coherent change; unrelated modifications are not bundled in
- [x] Commit subject follows Conventional Commits (`fix(ai): …`)
- [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: the astral character is written as a `\uD83D\uDE80` escape,
as the checkstyle config requires)
- [x] Documentation touched where behaviour changed (the two caps are
documented as code-point budgets)
--
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]