feat(notification-service): adapt to Go backend /api/v1/rooms/high-co2 endpoint
Replace SensorReading model with HighCo2Room matching the Go API response
{room, is_high, co2}. Update AirQualityService to call /api/v1/rooms/high-co2.
Simplify scheduler: backend already filters alertable rooms, recovery detected
by absence from response. Adapt Telegram message and all unit tests (20 passing).
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
package ch.hesso.pi.notification.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public record HighCo2Room(
|
||||
String room,
|
||||
@JsonProperty("is_high") boolean isHigh,
|
||||
int co2
|
||||
) {}
|
||||
@@ -1,13 +0,0 @@
|
||||
package ch.hesso.pi.notification.model;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public record SensorReading(
|
||||
String roomId,
|
||||
String roomName,
|
||||
int co2,
|
||||
double temperature,
|
||||
int humidity,
|
||||
String windowState,
|
||||
Instant timestamp
|
||||
) {}
|
||||
@@ -2,7 +2,7 @@ package ch.hesso.pi.notification.scheduler;
|
||||
|
||||
import ch.hesso.pi.notification.config.NotificationProperties;
|
||||
import ch.hesso.pi.notification.model.Co2Level;
|
||||
import ch.hesso.pi.notification.model.SensorReading;
|
||||
import ch.hesso.pi.notification.model.HighCo2Room;
|
||||
import ch.hesso.pi.notification.service.AirQualityService;
|
||||
import ch.hesso.pi.notification.service.TelegramNotificationService;
|
||||
import org.slf4j.Logger;
|
||||
@@ -10,7 +10,6 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -20,17 +19,16 @@ public class AirQualityScheduler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AirQualityScheduler.class);
|
||||
|
||||
private static final List<SensorReading> MOCK_READINGS = List.of(
|
||||
new SensorReading("A1", "Salle A1", 2150, 23.5, 55, "closed", Instant.now()),
|
||||
new SensorReading("A2", "Salle A2", 1520, 22.0, 48, "open", Instant.now()),
|
||||
new SensorReading("B1", "Salle B1", 780, 21.0, 42, "closed", Instant.now())
|
||||
private static final List<HighCo2Room> MOCK_ROOMS = List.of(
|
||||
new HighCo2Room("A1", true, 2150),
|
||||
new HighCo2Room("A2", true, 1520)
|
||||
);
|
||||
|
||||
private final AirQualityService airQualityService;
|
||||
private final TelegramNotificationService telegramService;
|
||||
private final NotificationProperties props;
|
||||
|
||||
// last alerted level per room — null means no alert is active for that room
|
||||
// last alerted level per room — cleared on recovery
|
||||
private final Map<String, Co2Level> lastAlertedLevel = new ConcurrentHashMap<>();
|
||||
|
||||
public AirQualityScheduler(AirQualityService airQualityService,
|
||||
@@ -45,32 +43,37 @@ public class AirQualityScheduler {
|
||||
initialDelayString = "5000")
|
||||
public void checkAirQuality() {
|
||||
boolean mockMode = props.getAirQuality().isMockMode();
|
||||
log.debug("Polling air quality data… (mock={})", mockMode);
|
||||
log.debug("Polling high-CO₂ rooms… (mock={})", mockMode);
|
||||
|
||||
List<SensorReading> readings = mockMode
|
||||
? MOCK_READINGS
|
||||
: airQualityService.fetchLatestReadings();
|
||||
List<HighCo2Room> rooms = mockMode
|
||||
? MOCK_ROOMS
|
||||
: airQualityService.fetchHighCo2Rooms();
|
||||
|
||||
if (readings.isEmpty()) {
|
||||
log.warn("No readings returned — skipping this cycle");
|
||||
// rooms not in the response have recovered — clear their deduplication state
|
||||
if (!mockMode) {
|
||||
var activeRooms = rooms.stream().map(HighCo2Room::room).collect(java.util.stream.Collectors.toSet());
|
||||
lastAlertedLevel.keySet().removeIf(roomId -> {
|
||||
if (!activeRooms.contains(roomId)) {
|
||||
log.info("Room {} recovered — resetting alert state", roomId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
if (rooms.isEmpty()) {
|
||||
log.debug("No high-CO₂ rooms — skipping this cycle");
|
||||
return;
|
||||
}
|
||||
|
||||
for (SensorReading reading : readings) {
|
||||
Co2Level level = airQualityService.resolveLevel(reading.co2());
|
||||
|
||||
if (airQualityService.isAlertable(level)) {
|
||||
Co2Level previous = lastAlertedLevel.get(reading.roomId());
|
||||
for (HighCo2Room room : rooms) {
|
||||
Co2Level level = airQualityService.resolveLevel(room.co2());
|
||||
Co2Level previous = lastAlertedLevel.get(room.room());
|
||||
if (level != previous) {
|
||||
log.info("Alert level changed: room={} {} -> {}", reading.roomId(), previous, level);
|
||||
telegramService.sendAlert(reading, level);
|
||||
lastAlertedLevel.put(reading.roomId(), level);
|
||||
}
|
||||
} else {
|
||||
// room recovered — reset so next alert triggers a new notification
|
||||
if (lastAlertedLevel.remove(reading.roomId()) != null) {
|
||||
log.info("Room {} recovered to {}", reading.roomId(), level);
|
||||
}
|
||||
log.info("Alert level changed: room={} {} -> {} ({} ppm)",
|
||||
room.room(), previous, level, room.co2());
|
||||
telegramService.sendAlert(room, level);
|
||||
lastAlertedLevel.put(room.room(), level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package ch.hesso.pi.notification.service;
|
||||
|
||||
import ch.hesso.pi.notification.config.NotificationProperties;
|
||||
import ch.hesso.pi.notification.model.Co2Level;
|
||||
import ch.hesso.pi.notification.model.SensorReading;
|
||||
import ch.hesso.pi.notification.model.HighCo2Room;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
@@ -30,11 +30,11 @@ public class AirQualityService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the latest sensor reading for every room from the backend API.
|
||||
* Fetches rooms whose CO₂ exceeds 1400 ppm from GET /api/v1/rooms/high-co2.
|
||||
* Returns an empty list on error so the scheduler can continue safely.
|
||||
*/
|
||||
public List<SensorReading> fetchLatestReadings() {
|
||||
String url = props.getAirQuality().getApiUrl() + "/sensors/latest";
|
||||
public List<HighCo2Room> fetchHighCo2Rooms() {
|
||||
String url = props.getAirQuality().getApiUrl() + "/api/v1/rooms/high-co2";
|
||||
String username = props.getAirQuality().getApiUsername();
|
||||
String password = props.getAirQuality().getApiPassword();
|
||||
|
||||
@@ -42,21 +42,21 @@ public class AirQualityService {
|
||||
var request = restClient.get().uri(url);
|
||||
|
||||
if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
|
||||
String encoded = Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
|
||||
String encoded = Base64.getEncoder()
|
||||
.encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
|
||||
request = request.header(HttpHeaders.AUTHORIZATION, "Basic " + encoded);
|
||||
}
|
||||
|
||||
List<SensorReading> readings = request
|
||||
List<HighCo2Room> rooms = request
|
||||
.retrieve()
|
||||
.body(new ParameterizedTypeReference<>() {});
|
||||
return readings != null ? readings : List.of();
|
||||
return rooms != null ? rooms : List.of();
|
||||
} catch (RestClientException e) {
|
||||
log.error("Failed to fetch latest readings from {}: {}", url, e.getMessage());
|
||||
log.error("Failed to fetch high-CO₂ rooms from {}: {}", url, e.getMessage());
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
/** Resolves the CO₂ level enum from a raw ppm value using configured thresholds. */
|
||||
public Co2Level resolveLevel(int ppm) {
|
||||
NotificationProperties.Thresholds t = props.getAirQuality().getThresholds();
|
||||
if (ppm < 800) return Co2Level.EXCELLENT;
|
||||
@@ -65,19 +65,4 @@ public class AirQualityService {
|
||||
if (ppm < t.getCritical()) return Co2Level.POOR;
|
||||
return Co2Level.CRITICAL;
|
||||
}
|
||||
|
||||
/** Returns true if the given level meets or exceeds the configured alert threshold. */
|
||||
public boolean isAlertable(Co2Level level) {
|
||||
Co2Level floor = parseAlertFromLevel(props.getAirQuality().getAlertFromLevel());
|
||||
return level.ordinal() >= floor.ordinal();
|
||||
}
|
||||
|
||||
private Co2Level parseAlertFromLevel(String value) {
|
||||
try {
|
||||
return Co2Level.valueOf(value.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Unknown alert-from-level '{}', defaulting to POOR", value);
|
||||
return Co2Level.POOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package ch.hesso.pi.notification.service;
|
||||
|
||||
import ch.hesso.pi.notification.config.NotificationProperties;
|
||||
import ch.hesso.pi.notification.model.Co2Level;
|
||||
import ch.hesso.pi.notification.model.SensorReading;
|
||||
import ch.hesso.pi.notification.model.HighCo2Room;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -11,16 +11,12 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class TelegramNotificationService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TelegramNotificationService.class);
|
||||
private static final DateTimeFormatter TIME_FMT =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
|
||||
|
||||
private final RestClient restClient;
|
||||
private final NotificationProperties props;
|
||||
@@ -30,17 +26,17 @@ public class TelegramNotificationService {
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
public void sendAlert(SensorReading reading, Co2Level level) {
|
||||
public void sendAlert(HighCo2Room room, Co2Level level) {
|
||||
String token = props.getTelegram().getBotToken();
|
||||
String chatId = props.getTelegram().getChatId();
|
||||
|
||||
if (!StringUtils.hasText(token) || !StringUtils.hasText(chatId)) {
|
||||
log.warn("Telegram credentials not configured — skipping alert for room {}", reading.roomId());
|
||||
log.warn("Telegram credentials not configured — skipping alert for room {}", room.room());
|
||||
return;
|
||||
}
|
||||
|
||||
String url = "https://api.telegram.org/bot" + token + "/sendMessage";
|
||||
String text = buildMessage(reading, level);
|
||||
String text = buildMessage(room, level);
|
||||
|
||||
try {
|
||||
restClient.post()
|
||||
@@ -50,32 +46,20 @@ public class TelegramNotificationService {
|
||||
.retrieve()
|
||||
.toBodilessEntity();
|
||||
|
||||
log.info("Telegram alert sent for room {} — {} ({} ppm)", reading.roomId(), level.getLabel(), reading.co2());
|
||||
log.info("Telegram alert sent for room {} — {} ({} ppm)", room.room(), level.getLabel(), room.co2());
|
||||
} catch (RestClientException e) {
|
||||
log.error("Failed to send Telegram alert for room {}: {}", reading.roomId(), e.getMessage());
|
||||
log.error("Failed to send Telegram alert for room {}: {}", room.room(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String buildMessage(SensorReading reading, Co2Level level) {
|
||||
String timestamp = reading.timestamp() != null
|
||||
? TIME_FMT.format(reading.timestamp())
|
||||
: "—";
|
||||
|
||||
private String buildMessage(HighCo2Room room, Co2Level level) {
|
||||
return String.format(
|
||||
"<b>%s — Air Quality Alert</b>\n\n" +
|
||||
"<b>Room:</b> %s\n" +
|
||||
"<b>CO₂:</b> %d ppm\n" +
|
||||
"<b>Temperature:</b> %.1f °C\n" +
|
||||
"<b>Humidity:</b> %d %%\n" +
|
||||
"<b>Windows:</b> %s\n" +
|
||||
"<b>Time:</b> %s",
|
||||
"<b>CO₂:</b> %d ppm",
|
||||
level.getLabel(),
|
||||
reading.roomName(),
|
||||
reading.co2(),
|
||||
reading.temperature(),
|
||||
reading.humidity(),
|
||||
reading.windowState(),
|
||||
timestamp
|
||||
room.room(),
|
||||
room.co2()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package ch.hesso.pi.notification.service;
|
||||
|
||||
import ch.hesso.pi.notification.config.NotificationProperties;
|
||||
import ch.hesso.pi.notification.model.Co2Level;
|
||||
import ch.hesso.pi.notification.model.SensorReading;
|
||||
import ch.hesso.pi.notification.model.HighCo2Room;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -14,7 +14,6 @@ import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -38,7 +37,7 @@ class AirQualityServiceTest {
|
||||
service = new AirQualityService(restClient, props);
|
||||
}
|
||||
|
||||
// ── resolveLevel ─────────────────────────────────────────────────────────
|
||||
// ── resolveLevel ──────────────────────────────────────────────────────────
|
||||
|
||||
@ParameterizedTest(name = "{0} ppm -> {1}")
|
||||
@CsvSource({
|
||||
@@ -57,70 +56,61 @@ class AirQualityServiceTest {
|
||||
assertThat(service.resolveLevel(ppm)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
// ── isAlertable ───────────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
void isAlertable_defaultThresholdIsPoor() {
|
||||
assertThat(service.isAlertable(Co2Level.EXCELLENT)).isFalse();
|
||||
assertThat(service.isAlertable(Co2Level.GOOD)).isFalse();
|
||||
assertThat(service.isAlertable(Co2Level.MODERATE)).isFalse();
|
||||
assertThat(service.isAlertable(Co2Level.POOR)).isTrue();
|
||||
assertThat(service.isAlertable(Co2Level.CRITICAL)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAlertable_respectsConfiguredLevel() {
|
||||
props.getAirQuality().setAlertFromLevel("moderate");
|
||||
|
||||
assertThat(service.isAlertable(Co2Level.GOOD)).isFalse();
|
||||
assertThat(service.isAlertable(Co2Level.MODERATE)).isTrue();
|
||||
assertThat(service.isAlertable(Co2Level.POOR)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAlertable_unknownLevelDefaultsToPoor() {
|
||||
props.getAirQuality().setAlertFromLevel("invalid");
|
||||
|
||||
assertThat(service.isAlertable(Co2Level.MODERATE)).isFalse();
|
||||
assertThat(service.isAlertable(Co2Level.POOR)).isTrue();
|
||||
}
|
||||
|
||||
// ── fetchLatestReadings ───────────────────────────────────────────────────
|
||||
// ── fetchHighCo2Rooms ─────────────────────────────────────────────────────
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
void fetchLatestReadings_returnsReadingsOnSuccess() {
|
||||
List<SensorReading> expected = List.of(
|
||||
new SensorReading("A1", "Salle A1", 1500, 22.0, 50, "closed", Instant.now())
|
||||
void fetchHighCo2Rooms_returnsRoomsOnSuccess() {
|
||||
List<HighCo2Room> expected = List.of(
|
||||
new HighCo2Room("A2", true, 1718),
|
||||
new HighCo2Room("A3", true, 1653)
|
||||
);
|
||||
|
||||
doReturn(uriSpec).when(restClient).get();
|
||||
doReturn(uriSpec).when(uriSpec).uri("http://test-api/sensors/latest");
|
||||
doReturn(uriSpec).when(uriSpec).uri("http://test-api/api/v1/rooms/high-co2");
|
||||
doReturn(responseSpec).when(uriSpec).retrieve();
|
||||
doReturn(expected).when(responseSpec).body(any(ParameterizedTypeReference.class));
|
||||
|
||||
assertThat(service.fetchLatestReadings()).isEqualTo(expected);
|
||||
assertThat(service.fetchHighCo2Rooms()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
void fetchLatestReadings_returnsEmptyListOnApiError() {
|
||||
void fetchHighCo2Rooms_returnsEmptyListOnApiError() {
|
||||
doReturn(uriSpec).when(restClient).get();
|
||||
doReturn(uriSpec).when(uriSpec).uri("http://test-api/sensors/latest");
|
||||
doReturn(uriSpec).when(uriSpec).uri("http://test-api/api/v1/rooms/high-co2");
|
||||
doReturn(responseSpec).when(uriSpec).retrieve();
|
||||
doThrow(new RestClientException("connection refused")).when(responseSpec).body(any(ParameterizedTypeReference.class));
|
||||
doThrow(new RestClientException("connection refused"))
|
||||
.when(responseSpec).body(any(ParameterizedTypeReference.class));
|
||||
|
||||
assertThat(service.fetchLatestReadings()).isEmpty();
|
||||
assertThat(service.fetchHighCo2Rooms()).isEmpty();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
void fetchLatestReadings_returnsEmptyListWhenApiReturnsNull() {
|
||||
void fetchHighCo2Rooms_returnsEmptyListWhenApiReturnsNull() {
|
||||
doReturn(uriSpec).when(restClient).get();
|
||||
doReturn(uriSpec).when(uriSpec).uri("http://test-api/sensors/latest");
|
||||
doReturn(uriSpec).when(uriSpec).uri("http://test-api/api/v1/rooms/high-co2");
|
||||
doReturn(responseSpec).when(uriSpec).retrieve();
|
||||
doReturn(null).when(responseSpec).body(any(ParameterizedTypeReference.class));
|
||||
|
||||
assertThat(service.fetchLatestReadings()).isEmpty();
|
||||
assertThat(service.fetchHighCo2Rooms()).isEmpty();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
void fetchHighCo2Rooms_sendsBasicAuthWhenCredentialsConfigured() {
|
||||
props.getAirQuality().setApiUsername("user");
|
||||
props.getAirQuality().setApiPassword("pass");
|
||||
|
||||
doReturn(uriSpec).when(restClient).get();
|
||||
doReturn(uriSpec).when(uriSpec).uri(anyString());
|
||||
doReturn(uriSpec).when(uriSpec).header(anyString(), anyString());
|
||||
doReturn(responseSpec).when(uriSpec).retrieve();
|
||||
doReturn(List.of()).when(responseSpec).body(any(ParameterizedTypeReference.class));
|
||||
|
||||
service.fetchHighCo2Rooms();
|
||||
|
||||
verify(uriSpec).header(eq("Authorization"), any(String.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package ch.hesso.pi.notification.service;
|
||||
|
||||
import ch.hesso.pi.notification.config.NotificationProperties;
|
||||
import ch.hesso.pi.notification.model.Co2Level;
|
||||
import ch.hesso.pi.notification.model.SensorReading;
|
||||
import ch.hesso.pi.notification.model.HighCo2Room;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.HttpMethod;
|
||||
@@ -10,8 +10,6 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.*;
|
||||
|
||||
@@ -21,9 +19,7 @@ class TelegramNotificationServiceTest {
|
||||
private TelegramNotificationService service;
|
||||
private NotificationProperties props;
|
||||
|
||||
private static final SensorReading READING = new SensorReading(
|
||||
"A1", "Salle A1", 1500, 22.5, 55, "closed", Instant.parse("2026-01-01T10:00:00Z")
|
||||
);
|
||||
private static final HighCo2Room ROOM = new HighCo2Room("A2", true, 1718);
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
@@ -44,7 +40,7 @@ class TelegramNotificationServiceTest {
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||
.andRespond(withSuccess());
|
||||
|
||||
service.sendAlert(READING, Co2Level.POOR);
|
||||
service.sendAlert(ROOM, Co2Level.POOR);
|
||||
|
||||
server.verify();
|
||||
}
|
||||
@@ -54,11 +50,11 @@ class TelegramNotificationServiceTest {
|
||||
server.expect(requestTo("https://api.telegram.org/bottest-token/sendMessage"))
|
||||
.andExpect(jsonPath("$.chat_id").value("123456789"))
|
||||
.andExpect(jsonPath("$.parse_mode").value("HTML"))
|
||||
.andExpect(jsonPath("$.text").value(org.hamcrest.Matchers.containsString("Salle A1")))
|
||||
.andExpect(jsonPath("$.text").value(org.hamcrest.Matchers.containsString("1500")))
|
||||
.andExpect(jsonPath("$.text").value(org.hamcrest.Matchers.containsString("A2")))
|
||||
.andExpect(jsonPath("$.text").value(org.hamcrest.Matchers.containsString("1718")))
|
||||
.andRespond(withSuccess());
|
||||
|
||||
service.sendAlert(READING, Co2Level.POOR);
|
||||
service.sendAlert(ROOM, Co2Level.POOR);
|
||||
|
||||
server.verify();
|
||||
}
|
||||
@@ -67,18 +63,18 @@ class TelegramNotificationServiceTest {
|
||||
void sendAlert_skipsWhenTokenMissing() {
|
||||
props.getTelegram().setBotToken("");
|
||||
|
||||
service.sendAlert(READING, Co2Level.POOR);
|
||||
service.sendAlert(ROOM, Co2Level.POOR);
|
||||
|
||||
server.verify(); // expects no requests
|
||||
server.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void sendAlert_skipsWhenChatIdMissing() {
|
||||
props.getTelegram().setChatId("");
|
||||
|
||||
service.sendAlert(READING, Co2Level.POOR);
|
||||
service.sendAlert(ROOM, Co2Level.POOR);
|
||||
|
||||
server.verify(); // expects no requests
|
||||
server.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -86,7 +82,6 @@ class TelegramNotificationServiceTest {
|
||||
server.expect(requestTo("https://api.telegram.org/bottest-token/sendMessage"))
|
||||
.andRespond(withServerError());
|
||||
|
||||
// must not propagate the exception
|
||||
service.sendAlert(READING, Co2Level.CRITICAL);
|
||||
service.sendAlert(ROOM, Co2Level.CRITICAL);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user