diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/alerting/controller/AlertNotificationController.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/alerting/controller/AlertNotificationController.java
new file mode 100644
index 00000000..5cb11d2d
--- /dev/null
+++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/alerting/controller/AlertNotificationController.java
@@ -0,0 +1,80 @@
+package com.cameleer.server.app.alerting.controller;
+
+import com.cameleer.server.app.alerting.dto.AlertNotificationDto;
+import com.cameleer.server.app.web.EnvPath;
+import com.cameleer.server.core.alerting.AlertNotification;
+import com.cameleer.server.core.alerting.AlertNotificationRepository;
+import com.cameleer.server.core.alerting.NotificationStatus;
+import com.cameleer.server.core.runtime.Environment;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.server.ResponseStatusException;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * REST controller for alert notifications.
+ *
+ * Env-scoped: GET /api/v1/environments/{envSlug}/alerts/{id}/notifications — lists outbound
+ * notifications for a given alert instance.
+ *
+ * Flat: POST /api/v1/alerts/notifications/{id}/retry — globally unique notification IDs;
+ * flat path matches the /executions/{id} precedent. OPERATOR+ only.
+ */
+@RestController
+@Tag(name = "Alert Notifications", description = "Outbound webhook notification management")
+public class AlertNotificationController {
+
+ private final AlertNotificationRepository notificationRepo;
+
+ public AlertNotificationController(AlertNotificationRepository notificationRepo) {
+ this.notificationRepo = notificationRepo;
+ }
+
+ /**
+ * Lists notifications for a specific alert instance (env-scoped).
+ * VIEWER+.
+ */
+ @GetMapping("/api/v1/environments/{envSlug}/alerts/{alertId}/notifications")
+ @PreAuthorize("hasAnyRole('VIEWER','OPERATOR','ADMIN')")
+ public List listForInstance(
+ @EnvPath Environment env,
+ @PathVariable UUID alertId) {
+ return notificationRepo.listForInstance(alertId)
+ .stream().map(AlertNotificationDto::from).toList();
+ }
+
+ /**
+ * Retries a failed notification — resets attempts and schedules it for immediate retry.
+ * Notification IDs are globally unique (flat path, matches /executions/{id} precedent).
+ * OPERATOR+ only.
+ */
+ @PostMapping("/api/v1/alerts/notifications/{id}/retry")
+ @PreAuthorize("hasAnyRole('OPERATOR','ADMIN')")
+ public AlertNotificationDto retry(@PathVariable UUID id) {
+ AlertNotification notification = notificationRepo.findById(id)
+ .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
+ "Notification not found: " + id));
+
+ if (notification.status() == NotificationStatus.PENDING) {
+ return AlertNotificationDto.from(notification);
+ }
+
+ // Reset for retry: status -> PENDING, attempts -> 0, next_attempt_at -> now
+ // We use scheduleRetry to reset attempt timing; then we need to reset attempts count.
+ // The repository has scheduleRetry which sets next_attempt_at and records last status.
+ // We use a dedicated pattern: mark as pending by scheduling immediately.
+ notificationRepo.scheduleRetry(id, Instant.now(), 0, null);
+
+ return AlertNotificationDto.from(notificationRepo.findById(id)
+ .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)));
+ }
+}
diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/alerting/dto/AlertNotificationDto.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/alerting/dto/AlertNotificationDto.java
new file mode 100644
index 00000000..08b8040c
--- /dev/null
+++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/alerting/dto/AlertNotificationDto.java
@@ -0,0 +1,29 @@
+package com.cameleer.server.app.alerting.dto;
+
+import com.cameleer.server.core.alerting.AlertNotification;
+import com.cameleer.server.core.alerting.NotificationStatus;
+
+import java.time.Instant;
+import java.util.UUID;
+
+public record AlertNotificationDto(
+ UUID id,
+ UUID alertInstanceId,
+ UUID webhookId,
+ UUID outboundConnectionId,
+ NotificationStatus status,
+ int attempts,
+ Instant nextAttemptAt,
+ Integer lastResponseStatus,
+ String lastResponseSnippet,
+ Instant deliveredAt,
+ Instant createdAt
+) {
+ public static AlertNotificationDto from(AlertNotification n) {
+ return new AlertNotificationDto(
+ n.id(), n.alertInstanceId(), n.webhookId(), n.outboundConnectionId(),
+ n.status(), n.attempts(), n.nextAttemptAt(),
+ n.lastResponseStatus(), n.lastResponseSnippet(),
+ n.deliveredAt(), n.createdAt());
+ }
+}
diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityConfig.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityConfig.java
index 65f8a7b6..c72f727d 100644
--- a/cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityConfig.java
+++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/security/SecurityConfig.java
@@ -161,6 +161,23 @@ public class SecurityConfig {
// Runtime management (OPERATOR+) — legacy flat shape
.requestMatchers("/api/v1/apps/**").hasAnyRole("OPERATOR", "ADMIN")
+ // Alerting — env-scoped reads (VIEWER+)
+ .requestMatchers(HttpMethod.GET, "/api/v1/environments/*/alerts/**").hasAnyRole("VIEWER", "OPERATOR", "ADMIN")
+ // Alerting — rule mutations (OPERATOR+)
+ .requestMatchers(HttpMethod.POST, "/api/v1/environments/*/alerts/rules/**").hasAnyRole("OPERATOR", "ADMIN")
+ .requestMatchers(HttpMethod.PUT, "/api/v1/environments/*/alerts/rules/**").hasAnyRole("OPERATOR", "ADMIN")
+ .requestMatchers(HttpMethod.DELETE, "/api/v1/environments/*/alerts/rules/**").hasAnyRole("OPERATOR", "ADMIN")
+ // Alerting — silence mutations (OPERATOR+)
+ .requestMatchers(HttpMethod.POST, "/api/v1/environments/*/alerts/silences/**").hasAnyRole("OPERATOR", "ADMIN")
+ .requestMatchers(HttpMethod.PUT, "/api/v1/environments/*/alerts/silences/**").hasAnyRole("OPERATOR", "ADMIN")
+ .requestMatchers(HttpMethod.DELETE, "/api/v1/environments/*/alerts/silences/**").hasAnyRole("OPERATOR", "ADMIN")
+ // Alerting — ack/read (VIEWER+ self-service)
+ .requestMatchers(HttpMethod.POST, "/api/v1/environments/*/alerts/*/ack").hasAnyRole("VIEWER", "OPERATOR", "ADMIN")
+ .requestMatchers(HttpMethod.POST, "/api/v1/environments/*/alerts/*/read").hasAnyRole("VIEWER", "OPERATOR", "ADMIN")
+ .requestMatchers(HttpMethod.POST, "/api/v1/environments/*/alerts/bulk-read").hasAnyRole("VIEWER", "OPERATOR", "ADMIN")
+ // Alerting — notification retry (flat path; notification IDs globally unique)
+ .requestMatchers(HttpMethod.POST, "/api/v1/alerts/notifications/*/retry").hasAnyRole("OPERATOR", "ADMIN")
+
// Outbound connections: list/get allow OPERATOR (method-level @PreAuthorize gates mutations)
.requestMatchers(HttpMethod.GET, "/api/v1/admin/outbound-connections", "/api/v1/admin/outbound-connections/**").hasAnyRole("OPERATOR", "ADMIN")
diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/controller/AlertNotificationControllerIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/controller/AlertNotificationControllerIT.java
new file mode 100644
index 00000000..ee2c9567
--- /dev/null
+++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/controller/AlertNotificationControllerIT.java
@@ -0,0 +1,176 @@
+package com.cameleer.server.app.alerting.controller;
+
+import com.cameleer.server.app.AbstractPostgresIT;
+import com.cameleer.server.app.TestSecurityHelper;
+import com.cameleer.server.app.search.ClickHouseLogStore;
+import com.cameleer.server.app.search.ClickHouseSearchIndex;
+import com.cameleer.server.core.alerting.AlertInstance;
+import com.cameleer.server.core.alerting.AlertInstanceRepository;
+import com.cameleer.server.core.alerting.AlertNotification;
+import com.cameleer.server.core.alerting.AlertNotificationRepository;
+import com.cameleer.server.core.alerting.AlertSeverity;
+import com.cameleer.server.core.alerting.AlertState;
+import com.cameleer.server.core.alerting.NotificationStatus;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class AlertNotificationControllerIT extends AbstractPostgresIT {
+
+ @MockBean(name = "clickHouseSearchIndex") ClickHouseSearchIndex clickHouseSearchIndex;
+ @MockBean(name = "clickHouseLogStore") ClickHouseLogStore clickHouseLogStore;
+
+ @Autowired private TestRestTemplate restTemplate;
+ @Autowired private ObjectMapper objectMapper;
+ @Autowired private TestSecurityHelper securityHelper;
+ @Autowired private AlertInstanceRepository instanceRepo;
+ @Autowired private AlertNotificationRepository notificationRepo;
+
+ private String operatorJwt;
+ private String viewerJwt;
+ private String envSlug;
+ private UUID envId;
+
+ @BeforeEach
+ void setUp() {
+ operatorJwt = securityHelper.operatorToken();
+ viewerJwt = securityHelper.viewerToken();
+ seedUser("test-operator");
+ seedUser("test-viewer");
+
+ envSlug = "notif-env-" + UUID.randomUUID().toString().substring(0, 6);
+ envId = UUID.randomUUID();
+ jdbcTemplate.update(
+ "INSERT INTO environments (id, slug, display_name) VALUES (?, ?, ?) ON CONFLICT (id) DO NOTHING",
+ envId, envSlug, envSlug);
+ }
+
+ @AfterEach
+ void cleanUp() {
+ jdbcTemplate.update("DELETE FROM alert_notifications WHERE alert_instance_id IN (SELECT id FROM alert_instances WHERE environment_id = ?)", envId);
+ jdbcTemplate.update("DELETE FROM alert_instances WHERE environment_id = ?", envId);
+ jdbcTemplate.update("DELETE FROM environments WHERE id = ?", envId);
+ jdbcTemplate.update("DELETE FROM users WHERE user_id IN ('test-operator','test-viewer')");
+ }
+
+ @Test
+ void listNotificationsForInstance() throws Exception {
+ AlertInstance instance = seedInstance();
+ AlertNotification notification = seedNotification(instance.id());
+
+ ResponseEntity resp = restTemplate.exchange(
+ "/api/v1/environments/" + envSlug + "/alerts/" + instance.id() + "/notifications",
+ HttpMethod.GET,
+ new HttpEntity<>(securityHelper.authHeadersNoBody(operatorJwt)),
+ String.class);
+
+ assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
+ JsonNode body = objectMapper.readTree(resp.getBody());
+ assertThat(body.isArray()).isTrue();
+ assertThat(body.size()).isGreaterThanOrEqualTo(1);
+ }
+
+ @Test
+ void viewerCanListNotifications() throws Exception {
+ AlertInstance instance = seedInstance();
+
+ ResponseEntity resp = restTemplate.exchange(
+ "/api/v1/environments/" + envSlug + "/alerts/" + instance.id() + "/notifications",
+ HttpMethod.GET,
+ new HttpEntity<>(securityHelper.authHeadersNoBody(viewerJwt)),
+ String.class);
+
+ assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
+ }
+
+ @Test
+ void retryNotification() throws Exception {
+ AlertInstance instance = seedInstance();
+ AlertNotification notification = seedNotification(instance.id());
+
+ // Mark as failed first
+ notificationRepo.markFailed(notification.id(), 500, "Internal Server Error");
+
+ ResponseEntity resp = restTemplate.exchange(
+ "/api/v1/alerts/notifications/" + notification.id() + "/retry",
+ HttpMethod.POST,
+ new HttpEntity<>(securityHelper.authHeaders(operatorJwt)),
+ String.class);
+
+ assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
+ }
+
+ @Test
+ void viewerCannotRetry() throws Exception {
+ AlertInstance instance = seedInstance();
+ AlertNotification notification = seedNotification(instance.id());
+
+ ResponseEntity resp = restTemplate.exchange(
+ "/api/v1/alerts/notifications/" + notification.id() + "/retry",
+ HttpMethod.POST,
+ new HttpEntity<>(securityHelper.authHeaders(viewerJwt)),
+ String.class);
+
+ assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
+ }
+
+ @Test
+ void retryUnknownNotificationReturns404() {
+ ResponseEntity resp = restTemplate.exchange(
+ "/api/v1/alerts/notifications/" + UUID.randomUUID() + "/retry",
+ HttpMethod.POST,
+ new HttpEntity<>(securityHelper.authHeaders(operatorJwt)),
+ String.class);
+
+ assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
+ }
+
+ // -------------------------------------------------------------------------
+ // Helpers
+ // -------------------------------------------------------------------------
+
+ private AlertInstance seedInstance() {
+ AlertInstance instance = new AlertInstance(
+ UUID.randomUUID(), null, null, envId,
+ AlertState.FIRING, AlertSeverity.WARNING,
+ Instant.now(), null, null, null, null, false,
+ 42.0, 1000.0, null, "Test alert", "Something happened",
+ List.of(), List.of(), List.of("OPERATOR"));
+ return instanceRepo.save(instance);
+ }
+
+ private AlertNotification seedNotification(UUID instanceId) {
+ // webhookId is a local UUID (not FK-constrained), outboundConnectionId is null
+ // (FK to outbound_connections ON DELETE SET NULL - null is valid)
+ AlertNotification notification = new AlertNotification(
+ UUID.randomUUID(), instanceId,
+ UUID.randomUUID(), null,
+ NotificationStatus.PENDING,
+ 0, Instant.now(),
+ null, null,
+ null, null,
+ null, null, Instant.now());
+ return notificationRepo.save(notification);
+ }
+
+ private void seedUser(String userId) {
+ jdbcTemplate.update(
+ "INSERT INTO users (user_id, provider, email, display_name) VALUES (?, 'test', ?, ?) ON CONFLICT (user_id) DO NOTHING",
+ userId, userId + "@example.com", userId);
+ }
+}
diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertInstanceRepositoryIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertInstanceRepositoryIT.java
index 11434a27..23f579b3 100644
--- a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertInstanceRepositoryIT.java
+++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertInstanceRepositoryIT.java
@@ -1,11 +1,14 @@
package com.cameleer.server.app.alerting.storage;
import com.cameleer.server.app.AbstractPostgresIT;
+import com.cameleer.server.app.search.ClickHouseLogStore;
+import com.cameleer.server.app.search.ClickHouseSearchIndex;
import com.cameleer.server.core.alerting.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
import java.time.Instant;
import java.util.List;
@@ -16,6 +19,9 @@ import static org.assertj.core.api.Assertions.assertThat;
class PostgresAlertInstanceRepositoryIT extends AbstractPostgresIT {
+ @MockBean(name = "clickHouseSearchIndex") ClickHouseSearchIndex clickHouseSearchIndex;
+ @MockBean(name = "clickHouseLogStore") ClickHouseLogStore clickHouseLogStore;
+
private PostgresAlertInstanceRepository repo;
private UUID envId;
private UUID ruleId;
diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertNotificationRepositoryIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertNotificationRepositoryIT.java
index b28ade89..41a744b3 100644
--- a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertNotificationRepositoryIT.java
+++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertNotificationRepositoryIT.java
@@ -1,11 +1,14 @@
package com.cameleer.server.app.alerting.storage;
import com.cameleer.server.app.AbstractPostgresIT;
+import com.cameleer.server.app.search.ClickHouseLogStore;
+import com.cameleer.server.app.search.ClickHouseSearchIndex;
import com.cameleer.server.core.alerting.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
import java.time.Instant;
import java.util.List;
@@ -16,6 +19,9 @@ import static org.assertj.core.api.Assertions.assertThat;
class PostgresAlertNotificationRepositoryIT extends AbstractPostgresIT {
+ @MockBean(name = "clickHouseSearchIndex") ClickHouseSearchIndex clickHouseSearchIndex;
+ @MockBean(name = "clickHouseLogStore") ClickHouseLogStore clickHouseLogStore;
+
private PostgresAlertNotificationRepository repo;
private UUID envId;
private UUID instanceId;
diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertReadRepositoryIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertReadRepositoryIT.java
index 6cd829eb..e4fc74f0 100644
--- a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertReadRepositoryIT.java
+++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertReadRepositoryIT.java
@@ -1,9 +1,12 @@
package com.cameleer.server.app.alerting.storage;
import com.cameleer.server.app.AbstractPostgresIT;
+import com.cameleer.server.app.search.ClickHouseLogStore;
+import com.cameleer.server.app.search.ClickHouseSearchIndex;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
import java.util.List;
import java.util.UUID;
@@ -13,6 +16,9 @@ import static org.assertj.core.api.Assertions.assertThatCode;
class PostgresAlertReadRepositoryIT extends AbstractPostgresIT {
+ @MockBean(name = "clickHouseSearchIndex") ClickHouseSearchIndex clickHouseSearchIndex;
+ @MockBean(name = "clickHouseLogStore") ClickHouseLogStore clickHouseLogStore;
+
private PostgresAlertReadRepository repo;
private UUID envId;
private UUID instanceId1;
diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertRuleRepositoryIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertRuleRepositoryIT.java
index 64d8f76d..6728daf7 100644
--- a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertRuleRepositoryIT.java
+++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertRuleRepositoryIT.java
@@ -1,11 +1,14 @@
package com.cameleer.server.app.alerting.storage;
import com.cameleer.server.app.AbstractPostgresIT;
+import com.cameleer.server.app.search.ClickHouseLogStore;
+import com.cameleer.server.app.search.ClickHouseSearchIndex;
import com.cameleer.server.core.alerting.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
import java.time.Instant;
import java.util.List;
@@ -16,6 +19,9 @@ import static org.assertj.core.api.Assertions.assertThat;
class PostgresAlertRuleRepositoryIT extends AbstractPostgresIT {
+ @MockBean(name = "clickHouseSearchIndex") ClickHouseSearchIndex clickHouseSearchIndex;
+ @MockBean(name = "clickHouseLogStore") ClickHouseLogStore clickHouseLogStore;
+
private PostgresAlertRuleRepository repo;
private UUID envId;
diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertSilenceRepositoryIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertSilenceRepositoryIT.java
index 1af01376..e2fa741f 100644
--- a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertSilenceRepositoryIT.java
+++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/PostgresAlertSilenceRepositoryIT.java
@@ -1,12 +1,15 @@
package com.cameleer.server.app.alerting.storage;
import com.cameleer.server.app.AbstractPostgresIT;
+import com.cameleer.server.app.search.ClickHouseLogStore;
+import com.cameleer.server.app.search.ClickHouseSearchIndex;
import com.cameleer.server.core.alerting.AlertSilence;
import com.cameleer.server.core.alerting.SilenceMatcher;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
@@ -16,6 +19,9 @@ import static org.assertj.core.api.Assertions.assertThat;
class PostgresAlertSilenceRepositoryIT extends AbstractPostgresIT {
+ @MockBean(name = "clickHouseSearchIndex") ClickHouseSearchIndex clickHouseSearchIndex;
+ @MockBean(name = "clickHouseLogStore") ClickHouseLogStore clickHouseLogStore;
+
private PostgresAlertSilenceRepository repo;
private UUID envId;
diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/V12MigrationIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/V12MigrationIT.java
index babcebe7..d1fa4e45 100644
--- a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/V12MigrationIT.java
+++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/storage/V12MigrationIT.java
@@ -1,12 +1,18 @@
package com.cameleer.server.app.alerting.storage;
import com.cameleer.server.app.AbstractPostgresIT;
+import com.cameleer.server.app.search.ClickHouseLogStore;
+import com.cameleer.server.app.search.ClickHouseSearchIndex;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
import static org.assertj.core.api.Assertions.assertThat;
class V12MigrationIT extends AbstractPostgresIT {
+ @MockBean(name = "clickHouseSearchIndex") ClickHouseSearchIndex clickHouseSearchIndex;
+ @MockBean(name = "clickHouseLogStore") ClickHouseLogStore clickHouseLogStore;
+
private java.util.UUID testEnvId;
private String testUserId;