This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new 1e4b2211a fix(alert): reject notification claims with stale retry
counts (#6494)
1e4b2211a is described below
commit 1e4b2211a5a665db8c44e11787d124b938e6f26e
Author: coder999o <[email protected]>
AuthorDate: Thu Oct 15 18:12:53 2026 +0800
fix(alert): reject notification claims with stale retry counts (#6494)
---
.../ops/alert/NotificationOutboxService.java | 3 +-
.../mapper/RmqAlertNotificationOutboxMapper.java | 5 +-
.../NotificationOutboxMapperIntegrationTest.java | 107 ++++++++++++++++++++-
.../ops/alert/NotificationOutboxServiceTest.java | 35 +++----
.../ops/alert/NotificationWebhookEncodingTest.java | 3 +-
5 files changed, 131 insertions(+), 22 deletions(-)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/NotificationOutboxService.java
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/NotificationOutboxService.java
index 7e16786ff..8a94ff230 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/NotificationOutboxService.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/NotificationOutboxService.java
@@ -320,8 +320,9 @@ public class NotificationOutboxService {
// each claim so a row cannot be born with an already-expired
lease.
LocalDateTime claimStartedAt = utcNow();
LocalDateTime claimStaleBefore =
claimStartedAt.minus(claimTimeout);
+ int attemptCount = row.getAttemptCount() == null ? 0 :
row.getAttemptCount();
if (mapper.claimForDispatch(row.getId(), claimStartedAt,
claimStaleBefore, claimStartedAt,
- claimToken) != 1) {
+ claimToken, attemptCount) != 1) {
continue;
}
send(row, claimToken);
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/persistence/mapper/RmqAlertNotificationOutboxMapper.java
b/server/src/main/java/org/apache/rocketmq/studio/persistence/mapper/RmqAlertNotificationOutboxMapper.java
index c59e0a24d..3374c8e17 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/persistence/mapper/RmqAlertNotificationOutboxMapper.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/persistence/mapper/RmqAlertNotificationOutboxMapper.java
@@ -39,11 +39,12 @@ public interface RmqAlertNotificationOutboxMapper extends
BaseMapper<RmqAlertNot
@Update("UPDATE rmq_alert_notification_outbox SET status = 'SENDING',
sending_started_at = #{claimedAt}, "
+ "claim_token = #{claimToken}, gmt_modified = #{claimedAt} "
- + "WHERE id = #{id} AND ((status IN ('PENDING', 'RETRY_WAIT') AND
next_attempt_at <= #{now}) "
+ + "WHERE id = #{id} AND COALESCE(attempt_count, 0) =
#{expectedAttemptCount} "
+ + "AND ((status IN ('PENDING', 'RETRY_WAIT') AND next_attempt_at
<= #{now}) "
+ "OR (status = 'SENDING' AND (sending_started_at IS NULL OR
sending_started_at <= #{staleBefore})))")
int claimForDispatch(@Param("id") Long id, @Param("now") LocalDateTime now,
@Param("staleBefore") LocalDateTime staleBefore,
@Param("claimedAt") LocalDateTime claimedAt,
- @Param("claimToken") String claimToken);
+ @Param("claimToken") String claimToken,
@Param("expectedAttemptCount") int expectedAttemptCount);
// gmt_modified is set explicitly so the caller's clock wins over the
column's
// ON UPDATE CURRENT_TIMESTAMP default (the database session's clock).
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/NotificationOutboxMapperIntegrationTest.java
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/NotificationOutboxMapperIntegrationTest.java
index b96f8fecc..437d9df47 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/NotificationOutboxMapperIntegrationTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/NotificationOutboxMapperIntegrationTest.java
@@ -25,14 +25,30 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.test.context.bean.override.mockito.MockitoBean;
+import org.springframework.web.client.RestTemplate;
+import org.apache.rocketmq.studio.audit.OperationAuditService;
+import org.apache.rocketmq.studio.common.domain.enums.AlertLevel;
+import org.apache.rocketmq.studio.settings.GeneralSettingsVO;
+import org.apache.rocketmq.studio.settings.SettingsRepository;
import java.time.LocalDateTime;
+import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
@SpringBootTest(properties = {
"studio.auth.login-required=false",
@@ -51,6 +67,95 @@ class NotificationOutboxMapperIntegrationTest {
@Autowired
private JdbcTemplate jdbcTemplate;
+ @MockitoBean
+ private NotificationOutboxService scheduledDispatcher;
+
+ @Test
+ void staleDispatchCannotOverwriteTheRetryCountTest() {
+ Long alertId = insertAlert("Stale dispatch regression",
"stale-dispatch-" + UUID.randomUUID());
+ List<Long> alertIds = List.of(alertId);
+ RmqAlertNotificationOutboxMapper dispatcherMapper = spy(mapper);
+ SettingsRepository settings = mock(SettingsRepository.class);
+ AlertRepository alerts = mock(AlertRepository.class);
+ OperationAuditService audit = mock(OperationAuditService.class);
+ NotificationOutboxService dispatcher = new
NotificationOutboxService(dispatcherMapper, settings,
+ mock(AlertSilenceService.class), alerts, audit, new
RestTemplate(), () -> {
+ throw new IllegalStateException("Simulated email failure");
+ });
+
when(settings.loadGeneralSettings()).thenReturn(GeneralSettingsVO.builder()
+ .emailRecipients("[email protected]").build());
+
when(alerts.findAlertById(alertId)).thenReturn(java.util.Optional.of(SystemAlertVO.builder()
+
.id(alertId).level(AlertLevel.warning).title("Lag").description("High
lag").build()));
+ try {
+ Long id = insertDeliveryAt(alertId, "email", "Previous failure",
+ LocalDateTime.now(ZoneOffset.UTC).minusMinutes(1));
+ jdbcTemplate.update("UPDATE rmq_alert_notification_outbox SET
status = 'RETRY_WAIT', "
+ + "attempt_count = 3, next_attempt_at = ? WHERE id = ?",
+
MYSQL_DATE_TIME.format(LocalDateTime.now(ZoneOffset.UTC).minusMinutes(1)), id);
+ AtomicBoolean advanceOnce = new AtomicBoolean();
+ doAnswer(invocation -> {
+ List<RmqAlertNotificationOutbox> selected =
mapper.findDispatchable(invocation.getArgument(0),
+ invocation.getArgument(1), invocation.getArgument(2));
+ if (advanceOnce.compareAndSet(false, true)) {
+
assertThat(selected).extracting(RmqAlertNotificationOutbox::getAttemptCount).contains(3);
+ // Another replica fails its fourth attempt after this
batch was read.
+ jdbcTemplate.update("UPDATE rmq_alert_notification_outbox
SET attempt_count = 4, "
+ + "last_error = 'Other replica failure' WHERE id =
?", id);
+ }
+ return selected;
+
}).when(dispatcherMapper).findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), anyInt());
+
+ dispatcher.dispatch();
+
+ RmqAlertNotificationOutbox unchanged = mapper.selectById(id);
+ assertThat(unchanged.getStatus()).isEqualTo("RETRY_WAIT");
+ assertThat(unchanged.getAttemptCount()).isEqualTo(4);
+ assertThat(unchanged.getLastError()).isEqualTo("Other replica
failure");
+ assertThat(unchanged.getClaimToken()).isNull();
+ assertThat(unchanged.getSendingStartedAt()).isNull();
+ verifyNoInteractions(settings, alerts, audit);
+
+ dispatcher.dispatch();
+
+ RmqAlertNotificationOutbox exhausted = mapper.selectById(id);
+ assertThat(exhausted.getAttemptCount()).isEqualTo(5);
+ assertThat(exhausted.getStatus()).isEqualTo("FAILED");
+ dispatcher.dispatch();
+ verify(settings).loadGeneralSettings();
+ verify(audit).record("FAIL_ALERT_NOTIFICATION",
"ALERT_NOTIFICATION", String.valueOf(id), null,
+ "alertId=" + alertId + ", channel=email", "FAILED",
"Simulated email failure");
+ } finally {
+ dispatcher.closeHeartbeatExecutor();
+ cleanup(alertIds);
+ alertMapper.deleteById(alertId);
+ }
+ }
+
+ @Test
+ void dispatchClaimStillHonorsFreshAndExpiredLeasesTest() {
+ List<Long> alertIds = List.of(ALERT_ID_BASE + 301);
+ LocalDateTime now = LocalDateTime.of(2026, 1, 1, 12, 0);
+ LocalDateTime staleBefore = now.minusMinutes(1);
+ cleanup(alertIds);
+ try {
+ insert(alertIds.get(0), "email", NotificationOutboxStatus.SENDING,
null, now);
+ Long id = mapper.selectList(new
QueryWrapper<RmqAlertNotificationOutbox>()
+ .in("alert_id", alertIds)).get(0).getId();
+ jdbcTemplate.update("UPDATE rmq_alert_notification_outbox SET
attempt_count = 2, "
+ + "sending_started_at = ? WHERE id = ?",
MYSQL_DATE_TIME.format(now.minusSeconds(10)), id);
+
+ assertThat(mapper.claimForDispatch(id, now, staleBefore, now,
"new-token", 2)).isZero();
+ jdbcTemplate.update("UPDATE rmq_alert_notification_outbox SET
sending_started_at = ? WHERE id = ?",
+ MYSQL_DATE_TIME.format(staleBefore.minusSeconds(1)), id);
+ assertThat(mapper.claimForDispatch(id, now, staleBefore, now,
"stale-count-token", 1)).isZero();
+ assertThat(mapper.claimForDispatch(id, now, staleBefore, now,
"new-token", 2)).isEqualTo(1);
+ assertThat(mapper.selectById(id).getAttemptCount()).isEqualTo(2);
+
assertThat(mapper.selectById(id).getClaimToken()).isEqualTo("new-token");
+ } finally {
+ cleanup(alertIds);
+ }
+ }
+
@Test
void deliveryPageFiltersSearchAndCreationTimeWithConsistentCountTest() {
List<Long> alertIds = new ArrayList<>();
@@ -157,7 +262,7 @@ class NotificationOutboxMapperIntegrationTest {
// whole second, so a wall-clock "now" can fall a fraction before
the stored value.
LocalDateTime claimNow = LocalDateTime.now().plusMinutes(1);
assertThat(mapper.claimForDispatch(id, claimNow,
claimNow.minusMinutes(5),
- claimedAt, "token-a")).isEqualTo(1);
+ claimedAt, "token-a", 0)).isEqualTo(1);
assertThat(mapper.selectById(id).getGmtModified()).isEqualTo(claimedAt);
assertThat(mapper.renewClaim(id, "token-a",
renewedAt)).isEqualTo(1);
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/NotificationOutboxServiceTest.java
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/NotificationOutboxServiceTest.java
index 2e9ca586d..f7c2d390a 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/NotificationOutboxServiceTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/NotificationOutboxServiceTest.java
@@ -40,6 +40,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
@@ -173,7 +174,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(0);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(0);
new NotificationOutboxService(mapper,
mock(SettingsRepository.class), mock(AlertSilenceService.class),
mock(AlertRepository.class),
mock(OperationAuditService.class)).dispatch();
@@ -241,7 +242,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
LocalDateTime silenceEndsAt = LocalDateTime.now().plusHours(1);
when(alerts.findAlertById(9L)).thenReturn(Optional.of(SystemAlertVO.builder().id(9L).ruleId(4L)
.domain(AlertDomain.BUSINESS).instanceId("local").build()));
@@ -272,7 +273,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
when(mapper.update(any(), any())).thenReturn(1);
when(alerts.findAlertById(9L)).thenReturn(Optional.of(SystemAlertVO.builder().id(9L)
.level(AlertLevel.warning).title("Lag").description("high").instanceId("local")
@@ -316,7 +317,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
when(mapper.update(any(), any())).thenReturn(1);
when(alerts.findAlertById(9L)).thenReturn(Optional.of(SystemAlertVO.builder().id(9L)
.level(AlertLevel.warning).title("Lag").description("high").instanceId("local").build()));
@@ -353,7 +354,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
when(mapper.update(any(), any())).thenReturn(1);
when(alerts.findAlertById(9L)).thenReturn(Optional.of(SystemAlertVO.builder().id(9L)
.level(AlertLevel.warning).title("Lag").description("high").instanceId("local").build()));
@@ -388,7 +389,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
when(mapper.update(any(), any())).thenReturn(1);
when(alerts.findAlertById(9L)).thenReturn(Optional.of(SystemAlertVO.builder().id(9L)
.level(AlertLevel.warning).title("Lag").description("high").instanceId("local").build()));
@@ -416,7 +417,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
when(mapper.update(any(), any())).thenReturn(1);
AlertRepository alerts = mock(AlertRepository.class);
OperationAuditService audit = mock(OperationAuditService.class);
@@ -446,7 +447,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
when(mapper.update(any(), any())).thenReturn(1);
when(alerts.findAlertById(9L)).thenReturn(Optional.of(SystemAlertVO.builder().id(9L)
.level(AlertLevel.warning).title("Lag").description("high").instanceId("local").build()));
@@ -475,13 +476,13 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(0);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(0);
new NotificationOutboxService(mapper, mock(SettingsRepository.class),
mock(AlertSilenceService.class),
mock(AlertRepository.class),
mock(OperationAuditService.class)).dispatch();
verify(mapper).claimForDispatch(org.mockito.ArgumentMatchers.eq(8L),
any(LocalDateTime.class),
- any(LocalDateTime.class), any(LocalDateTime.class),
anyString());
+ any(LocalDateTime.class), any(LocalDateTime.class),
anyString(), eq(0));
}
@Test
@@ -632,7 +633,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
when(mapper.renewClaim(eq(8L), anyString(),
any(LocalDateTime.class))).thenReturn(1);
when(mapper.update(any(), any())).thenReturn(1);
when(alerts.findAlertById(9L)).thenReturn(Optional.of(SystemAlertVO.builder().id(9L)
@@ -702,7 +703,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
when(mapper.update(any(), any())).thenReturn(1);
when(alerts.findAlertById(9L)).thenReturn(Optional.of(SystemAlertVO.builder().id(9L)
.level(AlertLevel.warning).title("Lag").description("high").instanceId("local").build()));
@@ -753,7 +754,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
when(mapper.renewClaim(eq(8L), anyString(),
any(LocalDateTime.class))).thenReturn(0);
when(alerts.findAlertById(9L)).thenReturn(Optional.of(SystemAlertVO.builder().id(9L)
.level(AlertLevel.warning).title("Lag").description("high").instanceId("local").build()));
@@ -810,7 +811,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
when(mapper.update(any(), any())).thenReturn(1);
when(alerts.findAlertById(9L)).thenReturn(Optional.of(SystemAlertVO.builder().id(9L)
.level(AlertLevel.warning).title("Lag").description("high").instanceId("local").build()));
@@ -852,7 +853,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
when(mapper.update(any(), any())).thenThrow(new
IllegalStateException("outbox database unavailable"));
when(alerts.findAlertById(9L)).thenReturn(Optional.of(SystemAlertVO.builder().id(9L)
.level(AlertLevel.warning).title("Lag").description("high").instanceId("local").build()));
@@ -950,7 +951,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
when(mapper.update(any(), any())).thenReturn(1);
when(alerts.findAlertById(9L)).thenReturn(Optional.of(SystemAlertVO.builder().id(9L)
.level(AlertLevel.warning).title("Lag").description("high").instanceId("local")
@@ -987,7 +988,7 @@ class NotificationOutboxServiceTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
when(mapper.update(any(), any())).thenReturn(1);
AlertRepository alerts = mock(AlertRepository.class);
when(alerts.findAlertById(9L)).thenReturn(Optional.of(SystemAlertVO.builder().id(9L).build()));
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/NotificationWebhookEncodingTest.java
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/NotificationWebhookEncodingTest.java
index 2fb21e751..b40939eda 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/NotificationWebhookEncodingTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/NotificationWebhookEncodingTest.java
@@ -50,6 +50,7 @@ import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -109,7 +110,7 @@ class NotificationWebhookEncodingTest {
when(mapper.findDispatchable(any(LocalDateTime.class),
any(LocalDateTime.class), any(Integer.class)))
.thenReturn(List.of(row));
when(mapper.claimForDispatch(any(), any(LocalDateTime.class),
any(LocalDateTime.class),
- any(LocalDateTime.class), anyString())).thenReturn(1);
+ any(LocalDateTime.class), anyString(),
anyInt())).thenReturn(1);
when(mapper.update(any(), any())).thenReturn(1);
when(alerts.findAlertById(9L)).thenReturn(Optional.of(SystemAlertVO.builder().id(9L)
.level(AlertLevel.warning).title("Lag").description("high").instanceId("local").build()));