diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/alerting/storage/PostgresAlertInstanceRepository.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/alerting/storage/PostgresAlertInstanceRepository.java index c2cfdd60..1243499d 100644 --- a/cameleer-server-app/src/main/java/com/cameleer/server/app/alerting/storage/PostgresAlertInstanceRepository.java +++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/alerting/storage/PostgresAlertInstanceRepository.java @@ -235,6 +235,8 @@ public class PostgresAlertInstanceRepository implements AlertInstanceRepository rs.getString("acked_by"), resolvedAt == null ? null : resolvedAt.toInstant(), lastNotifiedAt == null ? null : lastNotifiedAt.toInstant(), + null, // readAt — TODO Task 4: read from DB column + null, // deletedAt — TODO Task 4: read from DB column rs.getBoolean("silenced"), currentValue, threshold, diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/AlertingFullLifecycleIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/AlertingFullLifecycleIT.java index 2fa3c742..8919d86d 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/AlertingFullLifecycleIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/AlertingFullLifecycleIT.java @@ -243,11 +243,11 @@ class AlertingFullLifecycleIT extends AbstractPostgresIT { assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK); JsonNode body = objectMapper.readTree(resp.getBody()); - assertThat(body.path("state").asText()).isEqualTo("ACKNOWLEDGED"); + assertThat(body.path("state").asText()).isEqualTo("FIRING"); // TODO Task 4: ack no longer changes state // DB state AlertInstance updated = instanceRepo.findById(instanceId).orElseThrow(); - assertThat(updated.state()).isEqualTo(AlertState.ACKNOWLEDGED); + assertThat(updated.state()).isEqualTo(AlertState.FIRING); // TODO Task 4: ack is orthogonal to state } @Test diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/controller/AlertControllerIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/controller/AlertControllerIT.java index 4b866321..b6cf7611 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/controller/AlertControllerIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/controller/AlertControllerIT.java @@ -138,7 +138,7 @@ class AlertControllerIT extends AbstractPostgresIT { assertThat(ack.getStatusCode()).isEqualTo(HttpStatus.OK); JsonNode body = objectMapper.readTree(ack.getBody()); - assertThat(body.path("state").asText()).isEqualTo("ACKNOWLEDGED"); + assertThat(body.path("state").asText()).isEqualTo("FIRING"); // TODO Task 4: ack is orthogonal to state } @Test @@ -192,7 +192,7 @@ class AlertControllerIT extends AbstractPostgresIT { AlertInstance instance = new AlertInstance( UUID.randomUUID(), null, null, envId, AlertState.FIRING, AlertSeverity.WARNING, - Instant.now(), null, null, null, null, false, + Instant.now(), null, null, null, null, null, null, false, 42.0, 1000.0, null, "Test alert", "Something happened", List.of("test-operator"), List.of(), List.of()); return instanceRepo.save(instance); 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 index 766af9db..6abbb100 100644 --- 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 @@ -175,7 +175,7 @@ class AlertNotificationControllerIT extends AbstractPostgresIT { AlertInstance instance = new AlertInstance( UUID.randomUUID(), null, null, envId, AlertState.FIRING, AlertSeverity.WARNING, - Instant.now(), null, null, null, null, false, + Instant.now(), null, null, 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); diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/eval/AlertStateTransitionsTest.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/eval/AlertStateTransitionsTest.java index 29d07a81..17c3b6c7 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/eval/AlertStateTransitionsTest.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/eval/AlertStateTransitionsTest.java @@ -34,7 +34,7 @@ class AlertStateTransitionsTest { return new AlertInstance( UUID.randomUUID(), UUID.randomUUID(), Map.of(), UUID.randomUUID(), state, AlertSeverity.WARNING, - firedAt, null, ackedBy, null, null, false, + firedAt, null, ackedBy, null, null, null, null, false, 1.0, null, Map.of(), "title", "msg", List.of(), List.of(), List.of()); } @@ -71,7 +71,8 @@ class AlertStateTransitionsTest { @Test void ackedInstanceClearsToResolved() { - var acked = openInstance(AlertState.ACKNOWLEDGED, NOW.minusSeconds(30), "alice"); + var acked = openInstance(AlertState.FIRING, NOW.minusSeconds(30), null) + .withAck("alice", Instant.parse("2026-04-19T11:55:00Z")); var next = AlertStateTransitions.apply(acked, EvalResult.Clear.INSTANCE, ruleWith(0), NOW); assertThat(next).hasValueSatisfying(i -> { assertThat(i.state()).isEqualTo(AlertState.RESOLVED); @@ -131,7 +132,7 @@ class AlertStateTransitionsTest { } // ------------------------------------------------------------------------- - // Firing branch — already open FIRING / ACKNOWLEDGED + // Firing branch — already open FIRING (with or without ack) // ------------------------------------------------------------------------- @Test @@ -142,9 +143,13 @@ class AlertStateTransitionsTest { } @Test - void firingWhenAcknowledgedIsNoOp() { - var acked = openInstance(AlertState.ACKNOWLEDGED, NOW.minusSeconds(30), "alice"); - var next = AlertStateTransitions.apply(acked, FIRING_RESULT, ruleWith(0), NOW); + void firing_with_ack_stays_firing_on_next_firing_tick() { + // Pre-redesign this was the "ACKNOWLEDGED stays ACK" case. Post-redesign, + // ack is orthogonal; an acked FIRING row stays FIRING and no update is needed. + var current = openInstance(AlertState.FIRING, NOW.minusSeconds(30), null) + .withAck("alice", Instant.parse("2026-04-21T10:00:00Z")); + var next = AlertStateTransitions.apply( + current, new EvalResult.Firing(1.0, null, Map.of()), ruleWith(0), NOW); assertThat(next).isEmpty(); } diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/NotificationContextBuilderTest.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/NotificationContextBuilderTest.java index 9d33073e..e67c4834 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/NotificationContextBuilderTest.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/NotificationContextBuilderTest.java @@ -75,7 +75,7 @@ class NotificationContextBuilderTest { INST_ID, RULE_ID, Map.of(), ENV_ID, AlertState.FIRING, AlertSeverity.CRITICAL, Instant.parse("2026-04-19T10:00:00Z"), - null, null, null, null, + null, null, null, null, null, null, false, 0.95, 0.1, ctx, "Alert fired", "Some message", List.of(), List.of(), List.of() diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/NotificationDispatchJobIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/NotificationDispatchJobIT.java index b843fc4b..9f96ee49 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/NotificationDispatchJobIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/NotificationDispatchJobIT.java @@ -89,7 +89,7 @@ class NotificationDispatchJobIT extends AbstractPostgresIT { instanceRepo.save(new AlertInstance( instanceId, ruleId, Map.of(), envId, AlertState.FIRING, AlertSeverity.WARNING, - Instant.now(), null, null, null, null, false, + Instant.now(), null, null, null, null, null, null, false, null, null, Map.of(), "title", "msg", List.of(), List.of(), List.of())); diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/SilenceMatcherServiceTest.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/SilenceMatcherServiceTest.java index aed812d7..bc86435a 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/SilenceMatcherServiceTest.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/SilenceMatcherServiceTest.java @@ -30,7 +30,7 @@ class SilenceMatcherServiceTest { return new AlertInstance( INST_ID, RULE_ID, Map.of(), ENV_ID, AlertState.FIRING, AlertSeverity.WARNING, - Instant.now(), null, null, null, null, + Instant.now(), null, null, null, null, null, null, false, 1.5, 1.0, Map.of(), "title", "msg", List.of(), List.of(), List.of() @@ -85,7 +85,7 @@ class SilenceMatcherServiceTest { var inst = new AlertInstance( INST_ID, null, Map.of(), ENV_ID, AlertState.FIRING, AlertSeverity.WARNING, - Instant.now(), null, null, null, null, + Instant.now(), null, null, null, null, null, null, false, null, null, Map.of(), "t", "m", List.of(), List.of(), List.of() @@ -99,7 +99,7 @@ class SilenceMatcherServiceTest { var inst = new AlertInstance( INST_ID, null, Map.of(), ENV_ID, AlertState.FIRING, AlertSeverity.WARNING, - Instant.now(), null, null, null, null, + Instant.now(), null, null, null, null, null, null, false, null, null, Map.of(), "t", "m", List.of(), List.of(), List.of() diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/WebhookDispatcherIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/WebhookDispatcherIT.java index cd83c44e..b26a4616 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/WebhookDispatcherIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/alerting/notify/WebhookDispatcherIT.java @@ -188,7 +188,7 @@ class WebhookDispatcherIT { return new AlertInstance( UUID.randomUUID(), UUID.randomUUID(), Map.of(), UUID.randomUUID(), AlertState.FIRING, AlertSeverity.WARNING, - Instant.now(), null, null, null, null, false, + Instant.now(), null, null, null, null, null, null, false, null, null, Map.of(), "Alert", "Message", List.of(), List.of(), List.of()); } 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 35997a9f..877cf26a 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 @@ -176,7 +176,7 @@ class PostgresAlertInstanceRepositoryIT extends AbstractPostgresIT { repo.ack(inst.id(), userId, when); var found = repo.findById(inst.id()).orElseThrow(); - assertThat(found.state()).isEqualTo(AlertState.ACKNOWLEDGED); + assertThat(found.state()).isEqualTo(AlertState.FIRING); // TODO Task 4: ack no longer changes state assertThat(found.ackedBy()).isEqualTo(userId); assertThat(found.ackedAt()).isNotNull(); } @@ -325,7 +325,7 @@ class PostgresAlertInstanceRepositoryIT extends AbstractPostgresIT { return new AlertInstance( UUID.randomUUID(), ruleId, Map.of(), envId, AlertState.FIRING, severity, - Instant.now(), null, null, null, null, + Instant.now(), null, null, null, null, null, null, false, null, null, Map.of(), "title", "message", userIds, groupIds, roleNames); @@ -341,7 +341,7 @@ class PostgresAlertInstanceRepositoryIT extends AbstractPostgresIT { return new AlertInstance( UUID.randomUUID(), ruleId, Map.of(), envId, AlertState.FIRING, AlertSeverity.WARNING, - Instant.now(), null, null, null, null, + Instant.now(), null, null, null, null, null, null, false, null, null, Map.of("exchange", Map.of("id", exchangeId)), "title", "message",