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 99f14ad6 fix: validate operational API requests (#939)
99f14ad6 is described below
commit 99f14ad6a1788aea45e80738ddda09a96a64e460
Author: aias00 <[email protected]>
AuthorDate: Tue Aug 4 03:05:59 2026 -0700
fix: validate operational API requests (#939)
* [ISSUE #831] Validate proxy address format
* [ISSUE #844] Validate null datasource requests
* [ISSUE #846] Validate null alert rule requests
* [ISSUE #854] Validate null DLQ resend requests
* [ISSUE #856] Validate null system alert acknowledge requests
---
.../studio/cluster/proxy/ProxyAddressService.java | 18 +++++++++++++-
.../studio/instance/dlq/DLQController.java | 10 +++++++-
.../studio/ops/alert/AlertRuleController.java | 16 +++++++++---
.../rocketmq/studio/ops/alert/AlertService.java | 8 +++++-
.../studio/ops/alert/SystemAlertController.java | 11 +++++++-
.../studio/settings/SettingsController.java | 16 +++++++++---
.../rocketmq/studio/settings/SettingsService.java | 6 +++++
.../cluster/proxy/ProxyAddressServiceTest.java | 29 ++++++++++++++++++++++
.../studio/instance/dlq/DLQControllerTest.java | 12 +++++++++
.../studio/ops/alert/AlertRuleControllerTest.java | 24 ++++++++++++++++++
.../studio/ops/alert/AlertServiceTest.java | 20 +++++++++++++++
.../ops/alert/SystemAlertControllerTest.java | 12 +++++++++
.../studio/settings/SettingsControllerTest.java | 24 ++++++++++++++++++
.../studio/settings/SettingsServiceTest.java | 23 +++++++++++++++++
14 files changed, 217 insertions(+), 12 deletions(-)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/cluster/proxy/ProxyAddressService.java
b/server/src/main/java/org/apache/rocketmq/studio/cluster/proxy/ProxyAddressService.java
index 5495a573..3b6271d1 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/cluster/proxy/ProxyAddressService.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/cluster/proxy/ProxyAddressService.java
@@ -25,11 +25,18 @@ import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
@Slf4j
@Service
public class ProxyAddressService {
+ private static final Pattern PROXY_ADDR_PATTERN =
+
Pattern.compile("^(\\[[0-9a-fA-F:.]+]|[A-Za-z0-9._-]+):(\\d{1,5})$");
+ private static final int MIN_PORT = 1;
+ private static final int MAX_PORT = 65535;
+
private final Set<String> proxyAddrs = new
LinkedHashSet<>(List.of("127.0.0.1:8081"));
private String currentProxyAddr = "127.0.0.1:8081";
@@ -64,6 +71,15 @@ public class ProxyAddressService {
if (proxyAddr == null || proxyAddr.trim().isEmpty()) {
throw new BusinessException(400, fieldName + " is required");
}
- return proxyAddr.trim();
+ String normalized = proxyAddr.trim();
+ Matcher matcher = PROXY_ADDR_PATTERN.matcher(normalized);
+ if (!matcher.matches()) {
+ throw new BusinessException(400, fieldName + " must be in
host:port or [ipv6]:port format");
+ }
+ int port = Integer.parseInt(matcher.group(2));
+ if (port < MIN_PORT || port > MAX_PORT) {
+ throw new BusinessException(400, fieldName + " port must be
between 1 and 65535");
+ }
+ return normalized;
}
}
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQController.java
b/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQController.java
index 4015e1de..5b5517f7 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQController.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/instance/dlq/DLQController.java
@@ -17,6 +17,7 @@
package org.apache.rocketmq.studio.instance.dlq;
import org.apache.rocketmq.studio.common.domain.Result;
+import org.apache.rocketmq.studio.common.exception.BusinessException;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
@@ -41,9 +42,16 @@ public class DLQController {
}
@PostMapping("/resend")
- public Result<Void> resendMessages(@Valid @RequestBody DLQResendRequestDTO
request) {
+ public Result<Void> resendMessages(@Valid @RequestBody(required = false)
DLQResendRequestDTO request) {
+ requireRequest(request);
dlqService.resendMessages(
request.getGroupName(), request.getStartTime(),
request.getEndTime(), request.getTargetTopic());
return Result.ok();
}
+
+ private void requireRequest(DLQResendRequestDTO request) {
+ if (request == null) {
+ throw new BusinessException(400, "DLQ resend request is required");
+ }
+ }
}
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRuleController.java
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRuleController.java
index 25a5e9ab..d7613a5f 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRuleController.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRuleController.java
@@ -17,6 +17,7 @@
package org.apache.rocketmq.studio.ops.alert;
import org.apache.rocketmq.studio.common.domain.Result;
+import org.apache.rocketmq.studio.common.exception.BusinessException;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
@@ -40,13 +41,13 @@ public class AlertRuleController {
}
@PostMapping("/create")
- public Result<AlertRuleVO> createRule(@RequestBody AlertRuleVO rule) {
- return Result.ok(alertService.createRule(rule));
+ public Result<AlertRuleVO> createRule(@RequestBody(required = false)
AlertRuleVO rule) {
+ return Result.ok(alertService.createRule(requireAlertRule(rule)));
}
@PostMapping("/update")
- public Result<AlertRuleVO> updateRule(@RequestBody AlertRuleVO rule) {
- return Result.ok(alertService.updateRule(rule));
+ public Result<AlertRuleVO> updateRule(@RequestBody(required = false)
AlertRuleVO rule) {
+ return Result.ok(alertService.updateRule(requireAlertRule(rule)));
}
@PostMapping("/toggle")
@@ -59,4 +60,11 @@ public class AlertRuleController {
alertService.deleteRule(request.getId());
return Result.ok();
}
+
+ private AlertRuleVO requireAlertRule(AlertRuleVO rule) {
+ if (rule == null) {
+ throw new BusinessException(400, "Alert rule request is required");
+ }
+ return rule;
+ }
}
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertService.java
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertService.java
index cd405ebc..82c67312 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertService.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertService.java
@@ -67,6 +67,9 @@ public class AlertService {
public AlertRuleVO createRule(AlertRuleVO rule) {
+ if (rule == null) {
+ throw new BusinessException(400, "Alert rule request is required");
+ }
log.info("Creating alert rule: {}", rule.getName());
rule.setId(UUID.randomUUID().toString());
return alertRepository.saveRule(rule);
@@ -74,7 +77,10 @@ public class AlertService {
public AlertRuleVO updateRule(AlertRuleVO rule) {
- String id = rule == null ? null : rule.getId();
+ if (rule == null) {
+ throw new BusinessException(400, "Alert rule request is required");
+ }
+ String id = rule.getId();
log.info("Updating alert rule: {}", id);
validateRuleId(id);
if (!alertRepository.replaceRule(rule)) {
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/SystemAlertController.java
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/SystemAlertController.java
index 7dda1bf9..b1ef42a6 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/SystemAlertController.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/SystemAlertController.java
@@ -17,6 +17,7 @@
package org.apache.rocketmq.studio.ops.alert;
import org.apache.rocketmq.studio.common.domain.Result;
+import org.apache.rocketmq.studio.common.exception.BusinessException;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
@@ -43,7 +44,9 @@ public class SystemAlertController {
}
@PostMapping("/acknowledge")
- public Result<SystemAlertVO> acknowledgeAlert(@Valid @RequestBody
AcknowledgeSystemAlertDTO request) {
+ public Result<SystemAlertVO> acknowledgeAlert(
+ @Valid @RequestBody(required = false) AcknowledgeSystemAlertDTO
request) {
+ requireAcknowledgeRequest(request);
return Result.ok(alertService.acknowledgeAlert(request.getId()));
}
@@ -52,4 +55,10 @@ public class SystemAlertController {
int cleared = alertService.clearAcknowledged();
return Result.ok(Map.of("cleared", cleared));
}
+
+ private void requireAcknowledgeRequest(AcknowledgeSystemAlertDTO request) {
+ if (request == null) {
+ throw new BusinessException(400, "System alert acknowledge request
is required");
+ }
+ }
}
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/settings/SettingsController.java
b/server/src/main/java/org/apache/rocketmq/studio/settings/SettingsController.java
index 7be4fad3..40d79332 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/settings/SettingsController.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/settings/SettingsController.java
@@ -17,6 +17,7 @@
package org.apache.rocketmq.studio.settings;
import org.apache.rocketmq.studio.common.domain.Result;
+import org.apache.rocketmq.studio.common.exception.BusinessException;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
@@ -52,13 +53,13 @@ public class SettingsController {
}
@PostMapping("/datasources/create")
- public Result<DataSourceVO> createDataSource(@Valid @RequestBody
DataSourceVO dataSource) {
- return Result.ok(settingsService.createDataSource(dataSource));
+ public Result<DataSourceVO> createDataSource(@Valid @RequestBody(required
= false) DataSourceVO dataSource) {
+ return
Result.ok(settingsService.createDataSource(requireDataSource(dataSource)));
}
@PostMapping("/datasources/update")
- public Result<DataSourceVO> updateDataSource(@Valid @RequestBody
DataSourceVO dataSource) {
- return Result.ok(settingsService.updateDataSource(dataSource));
+ public Result<DataSourceVO> updateDataSource(@Valid @RequestBody(required
= false) DataSourceVO dataSource) {
+ return
Result.ok(settingsService.updateDataSource(requireDataSource(dataSource)));
}
@PostMapping("/datasources/delete")
@@ -71,4 +72,11 @@ public class SettingsController {
public Result<DataSourceTestResultVO> testDataSource(@Valid @RequestBody
DataSourceTestDTO request) {
return Result.ok(settingsService.testDataSource(request));
}
+
+ private DataSourceVO requireDataSource(DataSourceVO dataSource) {
+ if (dataSource == null) {
+ throw new BusinessException(400, "Data source request is
required");
+ }
+ return dataSource;
+ }
}
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/settings/SettingsService.java
b/server/src/main/java/org/apache/rocketmq/studio/settings/SettingsService.java
index a4bd5068..0d8295dc 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/settings/SettingsService.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/settings/SettingsService.java
@@ -99,6 +99,9 @@ public class SettingsService {
public DataSourceVO createDataSource(DataSourceVO dataSource) {
+ if (dataSource == null) {
+ throw new BusinessException(400, "Data source request is
required");
+ }
log.info("Creating data source: {}", dataSource.getName());
dataSource.setKey(UUID.randomUUID().toString());
return settingsRepository.saveDataSource(dataSource);
@@ -106,6 +109,9 @@ public class SettingsService {
public DataSourceVO updateDataSource(DataSourceVO dataSource) {
+ if (dataSource == null) {
+ throw new BusinessException(400, "Data source request is
required");
+ }
String key = normalizeDataSourceKey(dataSource.getKey());
dataSource.setKey(key);
log.info("Updating data source: {}", key);
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/cluster/proxy/ProxyAddressServiceTest.java
b/server/src/test/java/org/apache/rocketmq/studio/cluster/proxy/ProxyAddressServiceTest.java
index 81a647b2..82648842 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/cluster/proxy/ProxyAddressServiceTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/cluster/proxy/ProxyAddressServiceTest.java
@@ -20,6 +20,8 @@ package org.apache.rocketmq.studio.cluster.proxy;
import org.apache.rocketmq.studio.common.exception.BusinessException;
import org.junit.jupiter.api.Test;
+import java.util.List;
+
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -53,6 +55,33 @@ class ProxyAddressServiceTest {
.satisfies(ex -> assertThat(((BusinessException)
ex).getCode()).isEqualTo(400));
}
+ @Test
+ void addProxyAddrShouldAcceptBracketedIpv6Address() {
+ proxyAddressService.addProxyAddr(" [::1]:8081 ");
+
+ ProxyHomeVO home = proxyAddressService.getHomePage();
+ assertThat(home.getProxyAddrList()).containsExactly("127.0.0.1:8081",
"[::1]:8081");
+ }
+
+ @Test
+ void addProxyAddrShouldRejectInvalidAddressFormats() {
+ List<String> invalidProxyAddrs = List.of(
+ "10.0.0.1",
+ "10.0.0.1:abc",
+ "10.0.0.1:0",
+ "10.0.0.1:65536",
+ "http://10.0.0.1:8081",
+ "10.0.0.1:8081/path"
+ );
+
+ for (String invalidProxyAddr : invalidProxyAddrs) {
+ assertThatThrownBy(() ->
proxyAddressService.addProxyAddr(invalidProxyAddr))
+ .as("invalid proxy address %s", invalidProxyAddr)
+ .isInstanceOf(BusinessException.class)
+ .satisfies(ex -> assertThat(((BusinessException)
ex).getCode()).isEqualTo(400));
+ }
+ }
+
@Test
void removeProxyAddrShouldTrimAndRemoveAddress() {
proxyAddressService.addProxyAddr("10.0.0.1:8081");
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQControllerTest.java
b/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQControllerTest.java
index 6c23be58..2071c348 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQControllerTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/instance/dlq/DLQControllerTest.java
@@ -124,6 +124,18 @@ class DLQControllerTest {
eq("test-group"), isNull(), isNull(), eq("target-topic"));
}
+ @Test
+ void resendMessagesShouldRejectNullRequestBody() throws Exception {
+ mockMvc.perform(post("/api/dlq/resend")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("null"))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.code").value(400))
+ .andExpect(jsonPath("$.message").value("DLQ resend request is
required"));
+
+ verifyNoInteractions(dlqService);
+ }
+
@Test
void resendMessagesShouldRejectMissingGroupName() throws Exception {
Map<String, Object> body = Map.of(
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertRuleControllerTest.java
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertRuleControllerTest.java
index d8c3aeba..057b04ba 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertRuleControllerTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertRuleControllerTest.java
@@ -91,6 +91,30 @@ class AlertRuleControllerTest {
.andExpect(jsonPath("$.data.name").value("High Lag"));
}
+ @Test
+ void createRuleShouldRejectNullRequestBody() throws Exception {
+ mockMvc.perform(post("/api/alert-rules/create")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("null"))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.code").value(400))
+ .andExpect(jsonPath("$.message").value("Alert rule request is
required"));
+
+ verifyNoInteractions(alertService);
+ }
+
+ @Test
+ void updateRuleShouldRejectNullRequestBody() throws Exception {
+ mockMvc.perform(post("/api/alert-rules/update")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("null"))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.code").value(400))
+ .andExpect(jsonPath("$.message").value("Alert rule request is
required"));
+
+ verifyNoInteractions(alertService);
+ }
+
@Test
void toggleRuleShouldPassValidatedRequest() throws Exception {
AlertRuleVO toggled = AlertRuleVO.builder()
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertServiceTest.java
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertServiceTest.java
index 3b11ad02..58209e02 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertServiceTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertServiceTest.java
@@ -250,6 +250,16 @@ class AlertServiceTest {
assertThat(result1.getId()).isNotEqualTo(result2.getId());
}
+ @Test
+ void createRuleShouldRejectNullRequest() {
+ assertThatThrownBy(() -> alertService.createRule(null))
+ .isInstanceOf(BusinessException.class)
+ .hasMessage("Alert rule request is required")
+ .satisfies(ex -> assertThat(((BusinessException)
ex).getCode()).isEqualTo(400));
+
+ verify(alertRepository, never()).saveRule(any());
+ }
+
@Test
void updateRuleShouldUpdateExistingRule() {
AlertRuleVO update = AlertRuleVO.builder().id("rule-1").name("CPU
Alert").threshold(90.0).build();
@@ -262,6 +272,16 @@ class AlertServiceTest {
verify(alertRepository).replaceRule(update);
}
+ @Test
+ void updateRuleShouldRejectNullRequest() {
+ assertThatThrownBy(() -> alertService.updateRule(null))
+ .isInstanceOf(BusinessException.class)
+ .hasMessage("Alert rule request is required")
+ .satisfies(ex -> assertThat(((BusinessException)
ex).getCode()).isEqualTo(400));
+
+ verify(alertRepository, never()).replaceRule(any());
+ }
+
@Test
void updateRuleShouldRejectNullId() {
AlertRuleVO update = AlertRuleVO.builder().name("CPU Alert").build();
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/SystemAlertControllerTest.java
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/SystemAlertControllerTest.java
index ca655a62..2bac8166 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/SystemAlertControllerTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/SystemAlertControllerTest.java
@@ -91,6 +91,18 @@ class SystemAlertControllerTest {
verify(alertService).acknowledgeAlert("alert-1");
}
+ @Test
+ void acknowledgeAlertShouldRejectNullRequestBody() throws Exception {
+ mockMvc.perform(post("/api/system-alerts/acknowledge")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("null"))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.code").value(400))
+ .andExpect(jsonPath("$.message").value("System alert
acknowledge request is required"));
+
+ verifyNoInteractions(alertService);
+ }
+
@Test
void acknowledgeAlertShouldRejectBlankId() throws Exception {
mockMvc.perform(post("/api/system-alerts/acknowledge")
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/settings/SettingsControllerTest.java
b/server/src/test/java/org/apache/rocketmq/studio/settings/SettingsControllerTest.java
index b6f4f7d7..e56a7495 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/settings/SettingsControllerTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/settings/SettingsControllerTest.java
@@ -222,6 +222,18 @@ class SettingsControllerTest {
verifyNoInteractions(settingsService);
}
+ @Test
+ void createDataSourceShouldRejectNullRequestBody() throws Exception {
+ mockMvc.perform(post("/api/settings/datasources/create")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("null"))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.code", is(400)))
+ .andExpect(jsonPath("$.message", is("Data source request is
required")));
+
+ verifyNoInteractions(settingsService);
+ }
+
@Test
void updateDataSourceShouldReturnUpdatedSource() throws Exception {
DataSourceVO input = DataSourceVO.builder().key("ds-1").name("Updated
DS").type("rocketmq")
@@ -255,6 +267,18 @@ class SettingsControllerTest {
verifyNoInteractions(settingsService);
}
+ @Test
+ void updateDataSourceShouldRejectNullRequestBody() throws Exception {
+ mockMvc.perform(post("/api/settings/datasources/update")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("null"))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("$.code", is(400)))
+ .andExpect(jsonPath("$.message", is("Data source request is
required")));
+
+ verifyNoInteractions(settingsService);
+ }
+
@Test
void deleteDataSourceShouldReturnSuccess() throws Exception {
doNothing().when(settingsService).deleteDataSource("ds-1");
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/settings/SettingsServiceTest.java
b/server/src/test/java/org/apache/rocketmq/studio/settings/SettingsServiceTest.java
index 785f3d5b..5a4de053 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/settings/SettingsServiceTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/settings/SettingsServiceTest.java
@@ -42,6 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
@@ -214,6 +215,17 @@ class SettingsServiceTest {
verify(settingsRepository).saveDataSource(input);
}
+ @Test
+ void createDataSourceShouldRejectNullRequest() {
+ assertThatThrownBy(() -> settingsService.createDataSource(null))
+ .isInstanceOf(BusinessException.class)
+ .hasMessage("Data source request is required")
+ .extracting("code")
+ .isEqualTo(400);
+
+ verifyNoInteractions(settingsRepository);
+ }
+
@Test
void updateDataSourceShouldDelegateToRepository() {
DataSourceVO input = DataSourceVO.builder().key("ds-1").name("Updated
DS").type("rocketmq")
@@ -227,6 +239,17 @@ class SettingsServiceTest {
verify(settingsRepository).replaceDataSource(input);
}
+ @Test
+ void updateDataSourceShouldRejectNullRequest() {
+ assertThatThrownBy(() -> settingsService.updateDataSource(null))
+ .isInstanceOf(BusinessException.class)
+ .hasMessage("Data source request is required")
+ .extracting("code")
+ .isEqualTo(400);
+
+ verifyNoInteractions(settingsRepository);
+ }
+
@Test
void updateDataSourceShouldRejectUnknownKey() {
SettingsService service = new SettingsService(settingsRepository,
RestClient.builder(), new ObjectMapper(), operationAuditService);