RockteMQ-AI commented on code in PR #3579:
URL:
https://github.com/apache/rocketmq-dashboard/pull/3579#discussion_r3941671062
##########
server/src/test/java/org/apache/rocketmq/studio/ops/alert/NativeAlertRulePolicyTest.java:
##########
@@ -20,104 +20,153 @@
import org.junit.jupiter.api.Test;
import java.util.List;
-import java.util.Locale;
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
class NativeAlertRulePolicyTest {
Review Comment:
The PR description says this adds the 'first dedicated unit test suite', but
the diff replaces an existing 12-test AssertJ suite with a 10-test JUnit
Jupiter suite — net branch coverage decreases (see findings above). The rewrite
also drops the legacy Prometheus-metric compatibility test
(rocketmq_consumer_lag_messages), though unknownMetricSkipsNativeChecks does
exercise the same metricDomain == null early return, so that is mainly a loss
of documented intent. Please update the description and prefer extending the
existing suite over rewriting it, including keeping the AssertJ style used by
the file.
##########
server/src/test/java/org/apache/rocketmq/studio/ops/alert/NativeAlertRulePolicyTest.java:
##########
@@ -20,104 +20,153 @@
import org.junit.jupiter.api.Test;
import java.util.List;
-import java.util.Locale;
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
class NativeAlertRulePolicyTest {
- @Test
- void acceptsScopedBusinessRuleTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS, "consumer.lag.total")
-
.instanceId("local").consumerGroup("orders").consecutiveSamples(2).build()))
- .doesNotThrowAnyException();
+ private AlertRuleVO validNativeRule() {
+ return AlertRuleVO.builder()
+ .domain(AlertDomain.CLUSTER)
+ .metric("broker.availability")
+ .operator("UNAVAILABLE")
+ .threshold(0)
+ .duration("5m")
+ .instanceId("inst-1")
+ .channels(List.of("email"))
+ .build();
}
- @Test
- void acceptsConsumerDelayForAConsumerGroupTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS,
"consumer.delay.seconds")
-
.instanceId("local").consumerGroup("orders").build())).doesNotThrowAnyException();
+ private BusinessException rejectionOf(AlertRuleVO rule) {
+ return assertThrows(BusinessException.class, () ->
NativeAlertRulePolicy.validate(rule));
}
@Test
- void acceptsTopicSelectorOnlyForTopicBacklogTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS, "topic.backlog.total")
-
.instanceId("local").consumerGroup("orders").topic("orders-topic").build()))
- .doesNotThrowAnyException();
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS, "consumer.lag.total")
- .instanceId("local").topic("orders-topic").build()))
-
.isInstanceOf(BusinessException.class).hasMessageContaining("topic is not
supported");
+ void acceptsValidNativeAvailabilityRule() {
+ assertDoesNotThrow(() ->
NativeAlertRulePolicy.validate(validNativeRule()));
}
@Test
- void rejectsNativeRuleWithoutInstanceScopeTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "broker.availability")
-
.build())).isInstanceOf(BusinessException.class).hasMessageContaining("instanceId");
+ void unknownMetricSkipsNativeChecks() {
+ AlertRuleVO rule = AlertRuleVO.builder()
+ .domain(AlertDomain.BUSINESS)
+ .metric("custom.app.latency")
+ .operator(">")
+ .threshold(200)
+ .duration("5m")
+ .channels(List.of("email"))
+ .build();
+
+ assertDoesNotThrow(() -> NativeAlertRulePolicy.validate(rule));
}
@Test
- void rejectsNativeMetricInWrongDomainTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "consumer.lag.total")
-
.instanceId("local").build())).isInstanceOf(BusinessException.class).hasMessageContaining("BUSINESS");
+ void rejectsUnsupportedChannel() {
+ AlertRuleVO rule = validNativeRule();
+ rule.setChannels(List.of("slack"));
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertEquals(400, ex.getCode());
+ assertTrue(ex.getMessage().contains("Unsupported notification
channel"));
}
@Test
- void acceptsProxyAvailabilityAsAClusterMetricTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "proxy.availability")
- .instanceId("local").build())).doesNotThrowAnyException();
+ void rejectsDomainMismatchForNativeMetric() {
+ AlertRuleVO rule = validNativeRule();
+ rule.setDomain(AlertDomain.BUSINESS);
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertTrue(ex.getMessage().contains("belongs to the CLUSTER alert
domain"));
}
@Test
- void acceptsExplicitUnavailableAvailabilityRuleTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "broker.availability")
-
.instanceId("local").operator("UNAVAILABLE").build())).doesNotThrowAnyException();
+ void rejectsMissingInstanceIdForNativeMetric() {
+ AlertRuleVO rule = validNativeRule();
+ rule.setInstanceId(null);
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertTrue(ex.getMessage().contains("instanceId is required for native
alert rules"));
}
@Test
- void rejectsUnavailableForNonAvailabilityMetricTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER,
-
"broker.disk.usage_ratio").instanceId("local").operator("UNAVAILABLE").build()))
-
.isInstanceOf(BusinessException.class).hasMessageContaining("only supported");
+ void rejectsUnavailableOperatorOnNonAvailabilityMetric() {
+ AlertRuleVO rule = AlertRuleVO.builder()
+ .domain(AlertDomain.CLUSTER)
+ .metric("broker.disk.usage_ratio")
+ .operator("UNAVAILABLE")
+ .threshold(0)
+ .duration("5m")
+ .instanceId("inst-1")
+ .build();
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertTrue(ex.getMessage().contains("UNAVAILABLE is only supported for
native availability metrics"));
}
@Test
- void leavesLegacyPrometheusRulesCompatibleTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS,
-
"rocketmq_consumer_lag_messages").build())).doesNotThrowAnyException();
+ void rejectsConsumerGroupOnNonGroupScopedMetric() {
+ AlertRuleVO rule = AlertRuleVO.builder()
+ .domain(AlertDomain.CLUSTER)
+ .metric("broker.disk.usage_ratio")
+ .operator(">")
+ .threshold(0.8)
+ .duration("5m")
+ .instanceId("inst-1")
+ .consumerGroup("cg-1")
+ .build();
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertTrue(ex.getMessage().contains("consumerGroup is not supported
for metric"));
}
@Test
- void rejectsUnsupportedNotificationChannelsOutsideTheHttpApiTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS,
-
"rocketmq_consumer_lag_messages").channels(List.of("webhook")).build()))
-
.isInstanceOf(BusinessException.class).hasMessageContaining("Unsupported
notification channel");
+ void rejectsTopicOnNonTopicScopedMetric() {
+ AlertRuleVO rule = AlertRuleVO.builder()
+ .domain(AlertDomain.CLUSTER)
+ .metric("broker.disk.usage_ratio")
+ .operator(">")
+ .threshold(0.8)
+ .duration("5m")
+ .instanceId("inst-1")
+ .topic("order-topic")
+ .build();
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertTrue(ex.getMessage().contains("topic is not supported for
metric"));
}
@Test
- void acceptsNotificationChannelsIndependentlyOfTheDefaultLocaleTest() {
- Locale previous = Locale.getDefault();
- try {
- Locale.setDefault(Locale.forLanguageTag("tr-TR"));
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS,
- "rocketmq_consumer_lag_messages").channels(List.of("
DINGTALK ")).build()))
- .doesNotThrowAnyException();
- } finally {
- Locale.setDefault(previous);
- }
+ void acceptsConsumerLagRuleWithGroupScopedChecks() {
+ AlertRuleVO rule = AlertRuleVO.builder()
+ .domain(AlertDomain.BUSINESS)
+ .metric("consumer.lag.total")
+ .operator(">")
+ .threshold(1000)
+ .duration("5m")
+ .instanceId("inst-1")
+ .consumerGroup("cg-1")
+ .build();
+
+ assertDoesNotThrow(() -> NativeAlertRulePolicy.validate(rule));
}
@Test
- void rejectsOverflowingNativeRuleDurationsBeforePersistenceTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "broker.availability")
- .instanceId("local").duration("9223372036854775807y").build()))
-
.isInstanceOf(BusinessException.class).hasMessageContaining("Invalid alert
duration");
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "broker.availability")
-
.instanceId("local").reminderInterval("9223372036854775807y").build()))
-
.isInstanceOf(BusinessException.class).hasMessageContaining("Invalid alert
duration");
- }
+ void rejectsMalformedDuration() {
+ AlertRuleVO rule = validNativeRule();
Review Comment:
Coverage regression: the deleted
rejectsOverflowingNativeRuleDurationsBeforePersistenceTest exercised the
ArithmeticException overflow guard in AlertRuleDuration.parse
(Math.multiplyExact for 'w'/'y' units, AlertRuleDuration.java:64, caught at
:48) via duration "9223372036854775807y". The new rejectsMalformedDuration uses
"5x", which only hits the regex/no-match rejection path
(AlertRuleDuration.java:44) — a different branch. Please restore the overflow
case so the multiplyExact guard stays covered.
##########
server/src/test/java/org/apache/rocketmq/studio/ops/alert/NativeAlertRulePolicyTest.java:
##########
@@ -20,104 +20,153 @@
import org.junit.jupiter.api.Test;
import java.util.List;
-import java.util.Locale;
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
class NativeAlertRulePolicyTest {
- @Test
- void acceptsScopedBusinessRuleTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS, "consumer.lag.total")
-
.instanceId("local").consumerGroup("orders").consecutiveSamples(2).build()))
- .doesNotThrowAnyException();
+ private AlertRuleVO validNativeRule() {
+ return AlertRuleVO.builder()
+ .domain(AlertDomain.CLUSTER)
+ .metric("broker.availability")
+ .operator("UNAVAILABLE")
+ .threshold(0)
+ .duration("5m")
+ .instanceId("inst-1")
+ .channels(List.of("email"))
+ .build();
}
- @Test
- void acceptsConsumerDelayForAConsumerGroupTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS,
"consumer.delay.seconds")
-
.instanceId("local").consumerGroup("orders").build())).doesNotThrowAnyException();
+ private BusinessException rejectionOf(AlertRuleVO rule) {
+ return assertThrows(BusinessException.class, () ->
NativeAlertRulePolicy.validate(rule));
}
@Test
- void acceptsTopicSelectorOnlyForTopicBacklogTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS, "topic.backlog.total")
-
.instanceId("local").consumerGroup("orders").topic("orders-topic").build()))
- .doesNotThrowAnyException();
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS, "consumer.lag.total")
- .instanceId("local").topic("orders-topic").build()))
-
.isInstanceOf(BusinessException.class).hasMessageContaining("topic is not
supported");
+ void acceptsValidNativeAvailabilityRule() {
+ assertDoesNotThrow(() ->
NativeAlertRulePolicy.validate(validNativeRule()));
}
@Test
- void rejectsNativeRuleWithoutInstanceScopeTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "broker.availability")
-
.build())).isInstanceOf(BusinessException.class).hasMessageContaining("instanceId");
+ void unknownMetricSkipsNativeChecks() {
+ AlertRuleVO rule = AlertRuleVO.builder()
+ .domain(AlertDomain.BUSINESS)
+ .metric("custom.app.latency")
+ .operator(">")
+ .threshold(200)
+ .duration("5m")
+ .channels(List.of("email"))
+ .build();
+
+ assertDoesNotThrow(() -> NativeAlertRulePolicy.validate(rule));
}
@Test
- void rejectsNativeMetricInWrongDomainTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "consumer.lag.total")
-
.instanceId("local").build())).isInstanceOf(BusinessException.class).hasMessageContaining("BUSINESS");
+ void rejectsUnsupportedChannel() {
Review Comment:
Regression test removed:
acceptsNotificationChannelsIndependentlyOfTheDefaultLocaleTest guarded the
channel whitelist against locale-sensitive lowercasing by validating " DINGTALK
" under Locale tr-TR. validateChannels relies on toLowerCase(Locale.ROOT)
(NativeAlertRulePolicy.java:90); if that ever regresses to default-locale
toLowerCase(), Turkish locales map I to dotless ı and channel matching breaks.
rejectsUnsupportedChannel("slack") cannot catch that. Please keep the tr-TR
happy-path test.
##########
server/src/test/java/org/apache/rocketmq/studio/ops/alert/NativeAlertRulePolicyTest.java:
##########
@@ -20,104 +20,153 @@
import org.junit.jupiter.api.Test;
import java.util.List;
-import java.util.Locale;
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
class NativeAlertRulePolicyTest {
- @Test
- void acceptsScopedBusinessRuleTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS, "consumer.lag.total")
-
.instanceId("local").consumerGroup("orders").consecutiveSamples(2).build()))
- .doesNotThrowAnyException();
+ private AlertRuleVO validNativeRule() {
+ return AlertRuleVO.builder()
+ .domain(AlertDomain.CLUSTER)
+ .metric("broker.availability")
+ .operator("UNAVAILABLE")
+ .threshold(0)
+ .duration("5m")
+ .instanceId("inst-1")
+ .channels(List.of("email"))
+ .build();
}
- @Test
- void acceptsConsumerDelayForAConsumerGroupTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS,
"consumer.delay.seconds")
-
.instanceId("local").consumerGroup("orders").build())).doesNotThrowAnyException();
+ private BusinessException rejectionOf(AlertRuleVO rule) {
+ return assertThrows(BusinessException.class, () ->
NativeAlertRulePolicy.validate(rule));
}
@Test
- void acceptsTopicSelectorOnlyForTopicBacklogTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS, "topic.backlog.total")
-
.instanceId("local").consumerGroup("orders").topic("orders-topic").build()))
- .doesNotThrowAnyException();
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS, "consumer.lag.total")
- .instanceId("local").topic("orders-topic").build()))
-
.isInstanceOf(BusinessException.class).hasMessageContaining("topic is not
supported");
+ void acceptsValidNativeAvailabilityRule() {
+ assertDoesNotThrow(() ->
NativeAlertRulePolicy.validate(validNativeRule()));
}
@Test
- void rejectsNativeRuleWithoutInstanceScopeTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "broker.availability")
-
.build())).isInstanceOf(BusinessException.class).hasMessageContaining("instanceId");
+ void unknownMetricSkipsNativeChecks() {
+ AlertRuleVO rule = AlertRuleVO.builder()
+ .domain(AlertDomain.BUSINESS)
+ .metric("custom.app.latency")
+ .operator(">")
+ .threshold(200)
+ .duration("5m")
+ .channels(List.of("email"))
+ .build();
+
+ assertDoesNotThrow(() -> NativeAlertRulePolicy.validate(rule));
}
@Test
- void rejectsNativeMetricInWrongDomainTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "consumer.lag.total")
-
.instanceId("local").build())).isInstanceOf(BusinessException.class).hasMessageContaining("BUSINESS");
+ void rejectsUnsupportedChannel() {
+ AlertRuleVO rule = validNativeRule();
+ rule.setChannels(List.of("slack"));
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertEquals(400, ex.getCode());
+ assertTrue(ex.getMessage().contains("Unsupported notification
channel"));
}
@Test
- void acceptsProxyAvailabilityAsAClusterMetricTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "proxy.availability")
- .instanceId("local").build())).doesNotThrowAnyException();
+ void rejectsDomainMismatchForNativeMetric() {
+ AlertRuleVO rule = validNativeRule();
+ rule.setDomain(AlertDomain.BUSINESS);
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertTrue(ex.getMessage().contains("belongs to the CLUSTER alert
domain"));
}
@Test
- void acceptsExplicitUnavailableAvailabilityRuleTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "broker.availability")
-
.instanceId("local").operator("UNAVAILABLE").build())).doesNotThrowAnyException();
+ void rejectsMissingInstanceIdForNativeMetric() {
+ AlertRuleVO rule = validNativeRule();
+ rule.setInstanceId(null);
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertTrue(ex.getMessage().contains("instanceId is required for native
alert rules"));
}
@Test
- void rejectsUnavailableForNonAvailabilityMetricTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER,
-
"broker.disk.usage_ratio").instanceId("local").operator("UNAVAILABLE").build()))
-
.isInstanceOf(BusinessException.class).hasMessageContaining("only supported");
+ void rejectsUnavailableOperatorOnNonAvailabilityMetric() {
+ AlertRuleVO rule = AlertRuleVO.builder()
+ .domain(AlertDomain.CLUSTER)
+ .metric("broker.disk.usage_ratio")
+ .operator("UNAVAILABLE")
+ .threshold(0)
+ .duration("5m")
+ .instanceId("inst-1")
+ .build();
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertTrue(ex.getMessage().contains("UNAVAILABLE is only supported for
native availability metrics"));
}
@Test
- void leavesLegacyPrometheusRulesCompatibleTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS,
-
"rocketmq_consumer_lag_messages").build())).doesNotThrowAnyException();
+ void rejectsConsumerGroupOnNonGroupScopedMetric() {
+ AlertRuleVO rule = AlertRuleVO.builder()
+ .domain(AlertDomain.CLUSTER)
+ .metric("broker.disk.usage_ratio")
+ .operator(">")
+ .threshold(0.8)
+ .duration("5m")
+ .instanceId("inst-1")
+ .consumerGroup("cg-1")
+ .build();
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertTrue(ex.getMessage().contains("consumerGroup is not supported
for metric"));
}
@Test
- void rejectsUnsupportedNotificationChannelsOutsideTheHttpApiTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS,
-
"rocketmq_consumer_lag_messages").channels(List.of("webhook")).build()))
-
.isInstanceOf(BusinessException.class).hasMessageContaining("Unsupported
notification channel");
+ void rejectsTopicOnNonTopicScopedMetric() {
+ AlertRuleVO rule = AlertRuleVO.builder()
Review Comment:
The topic-scoped happy path was dropped: the old suite verified
topic.backlog.total with a topic selector is accepted (TOPIC_SCOPED_METRICS,
NativeAlertRulePolicy.java:74). Only the rejection side (topic on
broker.disk.usage_ratio) remains. Consider adding a positive topic-scoped rule
test next to this one.
##########
server/src/test/java/org/apache/rocketmq/studio/ops/alert/NativeAlertRulePolicyTest.java:
##########
@@ -20,104 +20,153 @@
import org.junit.jupiter.api.Test;
import java.util.List;
-import java.util.Locale;
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
class NativeAlertRulePolicyTest {
- @Test
- void acceptsScopedBusinessRuleTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS, "consumer.lag.total")
-
.instanceId("local").consumerGroup("orders").consecutiveSamples(2).build()))
- .doesNotThrowAnyException();
+ private AlertRuleVO validNativeRule() {
+ return AlertRuleVO.builder()
+ .domain(AlertDomain.CLUSTER)
+ .metric("broker.availability")
+ .operator("UNAVAILABLE")
+ .threshold(0)
+ .duration("5m")
+ .instanceId("inst-1")
+ .channels(List.of("email"))
+ .build();
}
- @Test
- void acceptsConsumerDelayForAConsumerGroupTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS,
"consumer.delay.seconds")
-
.instanceId("local").consumerGroup("orders").build())).doesNotThrowAnyException();
+ private BusinessException rejectionOf(AlertRuleVO rule) {
+ return assertThrows(BusinessException.class, () ->
NativeAlertRulePolicy.validate(rule));
}
@Test
- void acceptsTopicSelectorOnlyForTopicBacklogTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS, "topic.backlog.total")
-
.instanceId("local").consumerGroup("orders").topic("orders-topic").build()))
- .doesNotThrowAnyException();
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS, "consumer.lag.total")
- .instanceId("local").topic("orders-topic").build()))
-
.isInstanceOf(BusinessException.class).hasMessageContaining("topic is not
supported");
+ void acceptsValidNativeAvailabilityRule() {
+ assertDoesNotThrow(() ->
NativeAlertRulePolicy.validate(validNativeRule()));
}
@Test
- void rejectsNativeRuleWithoutInstanceScopeTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "broker.availability")
-
.build())).isInstanceOf(BusinessException.class).hasMessageContaining("instanceId");
+ void unknownMetricSkipsNativeChecks() {
+ AlertRuleVO rule = AlertRuleVO.builder()
+ .domain(AlertDomain.BUSINESS)
+ .metric("custom.app.latency")
+ .operator(">")
+ .threshold(200)
+ .duration("5m")
+ .channels(List.of("email"))
+ .build();
+
+ assertDoesNotThrow(() -> NativeAlertRulePolicy.validate(rule));
}
@Test
- void rejectsNativeMetricInWrongDomainTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "consumer.lag.total")
-
.instanceId("local").build())).isInstanceOf(BusinessException.class).hasMessageContaining("BUSINESS");
+ void rejectsUnsupportedChannel() {
+ AlertRuleVO rule = validNativeRule();
+ rule.setChannels(List.of("slack"));
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertEquals(400, ex.getCode());
+ assertTrue(ex.getMessage().contains("Unsupported notification
channel"));
}
@Test
- void acceptsProxyAvailabilityAsAClusterMetricTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "proxy.availability")
- .instanceId("local").build())).doesNotThrowAnyException();
+ void rejectsDomainMismatchForNativeMetric() {
+ AlertRuleVO rule = validNativeRule();
+ rule.setDomain(AlertDomain.BUSINESS);
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertTrue(ex.getMessage().contains("belongs to the CLUSTER alert
domain"));
}
@Test
- void acceptsExplicitUnavailableAvailabilityRuleTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "broker.availability")
-
.instanceId("local").operator("UNAVAILABLE").build())).doesNotThrowAnyException();
+ void rejectsMissingInstanceIdForNativeMetric() {
+ AlertRuleVO rule = validNativeRule();
+ rule.setInstanceId(null);
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertTrue(ex.getMessage().contains("instanceId is required for native
alert rules"));
}
@Test
- void rejectsUnavailableForNonAvailabilityMetricTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER,
-
"broker.disk.usage_ratio").instanceId("local").operator("UNAVAILABLE").build()))
-
.isInstanceOf(BusinessException.class).hasMessageContaining("only supported");
+ void rejectsUnavailableOperatorOnNonAvailabilityMetric() {
+ AlertRuleVO rule = AlertRuleVO.builder()
+ .domain(AlertDomain.CLUSTER)
+ .metric("broker.disk.usage_ratio")
+ .operator("UNAVAILABLE")
+ .threshold(0)
+ .duration("5m")
+ .instanceId("inst-1")
+ .build();
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertTrue(ex.getMessage().contains("UNAVAILABLE is only supported for
native availability metrics"));
}
@Test
- void leavesLegacyPrometheusRulesCompatibleTest() {
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS,
-
"rocketmq_consumer_lag_messages").build())).doesNotThrowAnyException();
+ void rejectsConsumerGroupOnNonGroupScopedMetric() {
+ AlertRuleVO rule = AlertRuleVO.builder()
+ .domain(AlertDomain.CLUSTER)
+ .metric("broker.disk.usage_ratio")
+ .operator(">")
+ .threshold(0.8)
+ .duration("5m")
+ .instanceId("inst-1")
+ .consumerGroup("cg-1")
+ .build();
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertTrue(ex.getMessage().contains("consumerGroup is not supported
for metric"));
}
@Test
- void rejectsUnsupportedNotificationChannelsOutsideTheHttpApiTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS,
-
"rocketmq_consumer_lag_messages").channels(List.of("webhook")).build()))
-
.isInstanceOf(BusinessException.class).hasMessageContaining("Unsupported
notification channel");
+ void rejectsTopicOnNonTopicScopedMetric() {
+ AlertRuleVO rule = AlertRuleVO.builder()
+ .domain(AlertDomain.CLUSTER)
+ .metric("broker.disk.usage_ratio")
+ .operator(">")
+ .threshold(0.8)
+ .duration("5m")
+ .instanceId("inst-1")
+ .topic("order-topic")
+ .build();
+
+ BusinessException ex = rejectionOf(rule);
+
+ assertTrue(ex.getMessage().contains("topic is not supported for
metric"));
}
@Test
- void acceptsNotificationChannelsIndependentlyOfTheDefaultLocaleTest() {
- Locale previous = Locale.getDefault();
- try {
- Locale.setDefault(Locale.forLanguageTag("tr-TR"));
- assertThatCode(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.BUSINESS,
- "rocketmq_consumer_lag_messages").channels(List.of("
DINGTALK ")).build()))
- .doesNotThrowAnyException();
- } finally {
- Locale.setDefault(previous);
- }
+ void acceptsConsumerLagRuleWithGroupScopedChecks() {
+ AlertRuleVO rule = AlertRuleVO.builder()
+ .domain(AlertDomain.BUSINESS)
+ .metric("consumer.lag.total")
+ .operator(">")
+ .threshold(1000)
+ .duration("5m")
+ .instanceId("inst-1")
+ .consumerGroup("cg-1")
+ .build();
+
+ assertDoesNotThrow(() -> NativeAlertRulePolicy.validate(rule));
}
@Test
- void rejectsOverflowingNativeRuleDurationsBeforePersistenceTest() {
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "broker.availability")
- .instanceId("local").duration("9223372036854775807y").build()))
-
.isInstanceOf(BusinessException.class).hasMessageContaining("Invalid alert
duration");
- assertThatThrownBy(() ->
NativeAlertRulePolicy.validate(rule(AlertDomain.CLUSTER, "broker.availability")
-
.instanceId("local").reminderInterval("9223372036854775807y").build()))
-
.isInstanceOf(BusinessException.class).hasMessageContaining("Invalid alert
duration");
- }
+ void rejectsMalformedDuration() {
+ AlertRuleVO rule = validNativeRule();
Review Comment:
Coverage regression: no remaining test sets reminderInterval, so the parse
call at NativeAlertRulePolicy.java:78 is now uncovered. The old suite validated
reminderInterval("9223372036854775807y") alongside duration. Please add a
reminderInterval rejection case (e.g. invalid or overflowing value).
--
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]