From d0e28d5ac41bdcb6dbd722af19a6a6cb840fbefa Mon Sep 17 00:00:00 2001 From: Antonio Scardace <33966051+antonioscardace@users.noreply.github.com> Date: Tue, 11 Jun 2024 15:58:10 +0200 Subject: [PATCH] add: Monitor microservice --- monitor/pom.xml | 85 ++++++++++++++++ .../netwatch/monitor/MonitorApplication.java | 21 ++++ .../monitor/config/RabbitMqConfig.java | 20 ++++ .../netwatch/monitor/config/RedisConfig.java | 46 +++++++++ .../netwatch/monitor/entities/Contact.java | 21 ++++ .../monitor/entities/Observation.java | 25 +++++ .../monitor/entities/ObservationId.java | 22 +++++ .../netwatch/monitor/entities/Utility.java | 22 +++++ .../netwatch/monitor/requests/IRequest.java | 9 ++ .../monitor/requests/PingRequest.java | 34 +++++++ .../monitor/requests/RequestCreator.java | 18 ++++ .../monitor/services/MonitorService.java | 97 +++++++++++++++++++ .../services/RabbitMqProducerService.java | 31 ++++++ .../monitor/services/RedisService.java | 41 ++++++++ .../netwatch/monitor/utils/AlertMessage.java | 53 ++++++++++ .../src/main/resources/application.properties | 8 ++ .../monitor/requests/PingRequestTest.java | 40 ++++++++ .../monitor/requests/RequestCreatorTest.java | 26 +++++ 18 files changed, 619 insertions(+) create mode 100644 monitor/pom.xml create mode 100644 monitor/src/main/java/netwatch/monitor/MonitorApplication.java create mode 100644 monitor/src/main/java/netwatch/monitor/config/RabbitMqConfig.java create mode 100644 monitor/src/main/java/netwatch/monitor/config/RedisConfig.java create mode 100644 monitor/src/main/java/netwatch/monitor/entities/Contact.java create mode 100644 monitor/src/main/java/netwatch/monitor/entities/Observation.java create mode 100644 monitor/src/main/java/netwatch/monitor/entities/ObservationId.java create mode 100644 monitor/src/main/java/netwatch/monitor/entities/Utility.java create mode 100644 monitor/src/main/java/netwatch/monitor/requests/IRequest.java create mode 100644 monitor/src/main/java/netwatch/monitor/requests/PingRequest.java create mode 100644 monitor/src/main/java/netwatch/monitor/requests/RequestCreator.java create mode 100644 monitor/src/main/java/netwatch/monitor/services/MonitorService.java create mode 100644 monitor/src/main/java/netwatch/monitor/services/RabbitMqProducerService.java create mode 100644 monitor/src/main/java/netwatch/monitor/services/RedisService.java create mode 100644 monitor/src/main/java/netwatch/monitor/utils/AlertMessage.java create mode 100644 monitor/src/main/resources/application.properties create mode 100644 monitor/src/test/java/netwatch/monitor/requests/PingRequestTest.java create mode 100644 monitor/src/test/java/netwatch/monitor/requests/RequestCreatorTest.java diff --git a/monitor/pom.xml b/monitor/pom.xml new file mode 100644 index 0000000..efd7dc3 --- /dev/null +++ b/monitor/pom.xml @@ -0,0 +1,85 @@ + + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.0 + + + + netwatch + monitor + 0.0.1-SNAPSHOT + monitor + Microservice for Checking Services Health. + + + 21 + + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.amqp + spring-rabbit-test + test + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-starter-amqp + + + org.springframework.boot + spring-boot-starter-webflux + + + org.junit.jupiter + junit-jupiter-engine + 5.10.1 + test + + + org.mockito + mockito-core + 5.3.1 + test + + + org.json + json + 20231013 + + + org.projectlombok + lombok + 1.18.30 + provided + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + 21 + 21 + UTF-8 + + + + + + \ No newline at end of file diff --git a/monitor/src/main/java/netwatch/monitor/MonitorApplication.java b/monitor/src/main/java/netwatch/monitor/MonitorApplication.java new file mode 100644 index 0000000..4befe4b --- /dev/null +++ b/monitor/src/main/java/netwatch/monitor/MonitorApplication.java @@ -0,0 +1,21 @@ +package netwatch.monitor; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.web.client.RestTemplate; + +@EnableScheduling +@SpringBootApplication +public class MonitorApplication { + + public static void main(String[] args) { + SpringApplication.run(MonitorApplication.class, args); + } + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} \ No newline at end of file diff --git a/monitor/src/main/java/netwatch/monitor/config/RabbitMqConfig.java b/monitor/src/main/java/netwatch/monitor/config/RabbitMqConfig.java new file mode 100644 index 0000000..294596b --- /dev/null +++ b/monitor/src/main/java/netwatch/monitor/config/RabbitMqConfig.java @@ -0,0 +1,20 @@ +package netwatch.monitor.config; + +import org.springframework.amqp.core.FanoutExchange; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +// This class configures a Fanout Exchange for broadcasting messages to all bound queues. +// @author Antonio Scardace +// @version 1.0 + +@Configuration +public class RabbitMqConfig { + + private static final String EXCHANGE_NAME = System.getenv("RABBITMQ_EXCHANGE"); + + @Bean + public FanoutExchange fanoutExchange() { + return new FanoutExchange(EXCHANGE_NAME); + } +} \ No newline at end of file diff --git a/monitor/src/main/java/netwatch/monitor/config/RedisConfig.java b/monitor/src/main/java/netwatch/monitor/config/RedisConfig.java new file mode 100644 index 0000000..7e676db --- /dev/null +++ b/monitor/src/main/java/netwatch/monitor/config/RedisConfig.java @@ -0,0 +1,46 @@ +package netwatch.monitor.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import netwatch.monitor.entities.ObservationId; + +// Configuration class for connecting to Redis and for using RedisTemplate. +// @author Antonio Scardace +// @version 1.0 + +@Configuration +public class RedisConfig { + + @Value("${spring.redis.host}") + private String redisHost; + + @Value("${spring.redis.port}") + private int redisPort; + + @Value("${spring.redis.password}") + private String redisPassword; + + @Bean + public LettuceConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(redisHost, redisPort); + config.setPassword(redisPassword); + return new LettuceConnectionFactory(config); + } + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(connectionFactory); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(ObservationId.class)); + return redisTemplate; + } +} \ No newline at end of file diff --git a/monitor/src/main/java/netwatch/monitor/entities/Contact.java b/monitor/src/main/java/netwatch/monitor/entities/Contact.java new file mode 100644 index 0000000..12a7ec9 --- /dev/null +++ b/monitor/src/main/java/netwatch/monitor/entities/Contact.java @@ -0,0 +1,21 @@ +package netwatch.monitor.entities; + +import java.io.Serializable; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +// This class represents the "contacts" entity in the database. +// The Primary Key is the field "value". +// @author Antonio Scardace +// @version 1.0 + +@NoArgsConstructor +@AllArgsConstructor +@Data +public class Contact implements Serializable { + + private String value; + private String type; +} \ No newline at end of file diff --git a/monitor/src/main/java/netwatch/monitor/entities/Observation.java b/monitor/src/main/java/netwatch/monitor/entities/Observation.java new file mode 100644 index 0000000..6becd2c --- /dev/null +++ b/monitor/src/main/java/netwatch/monitor/entities/Observation.java @@ -0,0 +1,25 @@ +package netwatch.monitor.entities; + +import java.io.Serializable; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +// This class represents the "observes" entity in the database. +// The Primary Key is the composite field "observationId" defined by the Embedded class {@link ObservationId}. +// It represents the Many-To-Many association table between "utilities" and "contacts" tables. +// @author Antonio Scardace +// @version 1.0 + +@NoArgsConstructor +@AllArgsConstructor +@Data +public class Observation implements Serializable { + + private ObservationId observationId; + + private Utility utility; + private Contact contact; + private String environment; +} \ No newline at end of file diff --git a/monitor/src/main/java/netwatch/monitor/entities/ObservationId.java b/monitor/src/main/java/netwatch/monitor/entities/ObservationId.java new file mode 100644 index 0000000..ea34cca --- /dev/null +++ b/monitor/src/main/java/netwatch/monitor/entities/ObservationId.java @@ -0,0 +1,22 @@ +package netwatch.monitor.entities; + +import java.io.Serializable; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +// It represents a composite Primary Key for the entity {@link Observation} of the table "observes". +// It is composed by "address" and "contact" fields. They are both Foreign Keys. +// Respectively, they are the utility address and its referent contact. +// @author Antonio Scardace +// @version 1.0 + +@NoArgsConstructor +@AllArgsConstructor +@Data +public class ObservationId implements Serializable { + + private String address; + private String contact; +} \ No newline at end of file diff --git a/monitor/src/main/java/netwatch/monitor/entities/Utility.java b/monitor/src/main/java/netwatch/monitor/entities/Utility.java new file mode 100644 index 0000000..7a8bbb2 --- /dev/null +++ b/monitor/src/main/java/netwatch/monitor/entities/Utility.java @@ -0,0 +1,22 @@ +package netwatch.monitor.entities; + +import java.io.Serializable; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +// This class represents the "utilities" entity in the database. +// The Primary Key is the field "address". +// @author Antonio Scardace +// @version 1.0 + +@NoArgsConstructor +@AllArgsConstructor +@Data +public class Utility implements Serializable { + + private String address; + private String name; + private String type; +} \ No newline at end of file diff --git a/monitor/src/main/java/netwatch/monitor/requests/IRequest.java b/monitor/src/main/java/netwatch/monitor/requests/IRequest.java new file mode 100644 index 0000000..1e4932c --- /dev/null +++ b/monitor/src/main/java/netwatch/monitor/requests/IRequest.java @@ -0,0 +1,9 @@ +package netwatch.monitor.requests; + +// Interface defining methods to handle address-related operations. +// @author Antonio Scardace +// @version 1.0 + +public interface IRequest { + public Boolean request(String address); +} \ No newline at end of file diff --git a/monitor/src/main/java/netwatch/monitor/requests/PingRequest.java b/monitor/src/main/java/netwatch/monitor/requests/PingRequest.java new file mode 100644 index 0000000..c6f55ed --- /dev/null +++ b/monitor/src/main/java/netwatch/monitor/requests/PingRequest.java @@ -0,0 +1,34 @@ +package netwatch.monitor.requests; + +import java.io.IOException; +import java.net.InetAddress; + +import lombok.extern.java.Log; + +// Implementation of {@link IRequest} for handling ping-related operations. +// It supports IPv4 and IPv6 addresses. The use of their machine name is discouraged. +// The timeout (the time before the call aborts) is set to 4000ms (4 seconds). +// @author Antonio Scardace +// @version 1.0 + +@Log +public class PingRequest implements IRequest { + + private final Integer timeoutMs; + + public PingRequest() { + this.timeoutMs = 4000; + } + + @Override + public Boolean request(String ip) { + try { + InetAddress address = InetAddress.getByName(ip); + return address.isReachable(this.timeoutMs); + } + catch(IOException e) { + log.warning(e.getMessage()); + return false; + } + } +} \ No newline at end of file diff --git a/monitor/src/main/java/netwatch/monitor/requests/RequestCreator.java b/monitor/src/main/java/netwatch/monitor/requests/RequestCreator.java new file mode 100644 index 0000000..f528cb3 --- /dev/null +++ b/monitor/src/main/java/netwatch/monitor/requests/RequestCreator.java @@ -0,0 +1,18 @@ +package netwatch.monitor.requests; + +// Factory class to create {@link IRequest} instances based on the type. +// Factory-Method Design Pattern used to make the Request component modular. +// @author Antonio Scardace +// @version 1.0 + +public class RequestCreator { + + private RequestCreator() { + + } + + public static IRequest getRequest(String type) { + if (type.equals("ip")) return new PingRequest(); + throw new IllegalArgumentException("Unknown Request type " + type); + } +} \ No newline at end of file diff --git a/monitor/src/main/java/netwatch/monitor/services/MonitorService.java b/monitor/src/main/java/netwatch/monitor/services/MonitorService.java new file mode 100644 index 0000000..ea99cd5 --- /dev/null +++ b/monitor/src/main/java/netwatch/monitor/services/MonitorService.java @@ -0,0 +1,97 @@ +package netwatch.monitor.services; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import lombok.extern.java.Log; +import netwatch.monitor.entities.Observation; +import netwatch.monitor.entities.ObservationId; +import netwatch.monitor.entities.Utility; +import netwatch.monitor.requests.IRequest; +import netwatch.monitor.requests.RequestCreator; +import netwatch.monitor.utils.AlertMessage; + +// This class is responsible for monitoring the state of utilities. +// It scans periodically (each second) a set of utilities and, based on detected events, sends notifications. +// Notifications are sent to the interested microservices by RabbitMQ. +// @author Antonio Scardace +// @version 1.0 + +@Log +@Service +public class MonitorService { + + private static final String SET_NAME = "offline-utilities"; + + private final RestTemplate restTemplate; + private final RabbitMqProducerService rabbitManager; + private final RedisService redisService; + + @Autowired + public MonitorService(RestTemplate restTemplate, RabbitMqProducerService rabbitManager, RedisService redisService) { + this.restTemplate = restTemplate; + this.rabbitManager = rabbitManager; + this.redisService = redisService; + } + + private Boolean checkUtility(Utility utility) { + IRequest req = RequestCreator.getRequest(utility.getType()); + return req.request(utility.getAddress()); + } + + private void updateOfflineUtilities(ObservationId observation, Boolean backOnline) { + if (Boolean.TRUE.equals(backOnline)) this.redisService.delete(SET_NAME, observation); + else this.redisService.insert(SET_NAME, observation); + } + + // Fetches utility observations from the external API specified by the MANAGER_API_URL environment variable. + // Logs a warning if the API endpoint cannot be reached and return an empty list. + // Returns the list of observations, if any. + + private List fetchUtilities() { + try { + String urlApi = System.getenv("MANAGER_API_URL"); + ResponseEntity responseEntity = restTemplate.getForEntity(urlApi, Observation[].class); + Observation[] observationsArray = responseEntity.getBody(); + return observationsArray == null ? Collections.emptyList() : Arrays.asList(observationsArray); + } + catch (RestClientException e) { + log.warning("API endpoint cannot be reached."); + return Collections.emptyList(); + } + } + + // Performs a single scan for a given utility observation, checking its connectivity status. + // If the utility is online and was offline in the previous scans, a Solve Message is send. + // If the utility is offline and was not offline in the previous scans, a Resolution Message is send. + + private void singleScan(Observation observation) { + Boolean isOnline = this.checkUtility(observation.getUtility()); + Boolean wasOffline = this.redisService.contains(SET_NAME, observation.getObservationId()); + + if (Boolean.compare(isOnline, wasOffline) == 0) { + this.rabbitManager.send(AlertMessage.generate(observation, isOnline)); + this.updateOfflineUtilities(observation.getObservationId(), wasOffline); + } + } + + // Scheduled method to periodically scan utility observations for connectivity status. + // Fetches utility observations and performs a single scan for each observation concurrently. + + @Scheduled(fixedDelay = 1000) + public void scanUtilities() { + CompletableFuture[] futures = this.fetchUtilities().stream() + .map(utility -> CompletableFuture.runAsync(() -> singleScan(utility))) + .toArray(CompletableFuture[]::new); + CompletableFuture.allOf(futures).join(); + } +} \ No newline at end of file diff --git a/monitor/src/main/java/netwatch/monitor/services/RabbitMqProducerService.java b/monitor/src/main/java/netwatch/monitor/services/RabbitMqProducerService.java new file mode 100644 index 0000000..6846d88 --- /dev/null +++ b/monitor/src/main/java/netwatch/monitor/services/RabbitMqProducerService.java @@ -0,0 +1,31 @@ +package netwatch.monitor.services; + +import org.springframework.amqp.core.AmqpTemplate; +import org.springframework.amqp.core.FanoutExchange; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import lombok.extern.java.Log; + +// This class is a simple RabbitMQ Producer (Publisher) to a Fanout Exchange for broadcasting to all bound queues. +// @author Antonio Scardace +// @version 1.0 + +@Log +@Service +public class RabbitMqProducerService { + + private final AmqpTemplate rabbitTemplate; + private final FanoutExchange fanoutExchange; + + @Autowired + public RabbitMqProducerService(AmqpTemplate rabbitTemplate, FanoutExchange fanoutExchange) { + this.rabbitTemplate = rabbitTemplate; + this.fanoutExchange = fanoutExchange; + } + + public void send(String message) { + rabbitTemplate.convertAndSend(this.fanoutExchange.getName(), "", message); + log.info("Message sent: " + message); + } +} \ No newline at end of file diff --git a/monitor/src/main/java/netwatch/monitor/services/RedisService.java b/monitor/src/main/java/netwatch/monitor/services/RedisService.java new file mode 100644 index 0000000..9f58171 --- /dev/null +++ b/monitor/src/main/java/netwatch/monitor/services/RedisService.java @@ -0,0 +1,41 @@ +package netwatch.monitor.services; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import netwatch.monitor.entities.ObservationId; + +import java.util.Set; + +// This class is a service component responsible for managing the interaction with Redis for storing and retrieving data. +// The Redis data type handled by the class is the Set. +// @author Antonio Scardace +// @version 1.0 + +@Service +public class RedisService { + + private RedisTemplate redisTemplate; + + @Autowired + public RedisService(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + public Boolean contains(String setName, ObservationId value) { + return this.redisTemplate.opsForSet().isMember(setName, value); + } + + public Boolean insert(String setName, ObservationId value) { + return this.redisTemplate.opsForSet().add(setName, value) != null; + } + + public Boolean delete(String setName, ObservationId value) { + return this.redisTemplate.opsForSet().remove(setName, value) != null; + } + + public Set getAllByName(String setName) { + return this.redisTemplate.opsForSet().members(setName); + } +} \ No newline at end of file diff --git a/monitor/src/main/java/netwatch/monitor/utils/AlertMessage.java b/monitor/src/main/java/netwatch/monitor/utils/AlertMessage.java new file mode 100644 index 0000000..1f52296 --- /dev/null +++ b/monitor/src/main/java/netwatch/monitor/utils/AlertMessage.java @@ -0,0 +1,53 @@ +package netwatch.monitor.utils; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +import org.json.JSONObject; + +import netwatch.monitor.entities.Observation; + +// This static class provides methods for creating the alert message on two events. +// It can be an error message if the utility goes offline. +// It can be a resolution message if the utility returns online. +// This message is then attached with the utility referent contact info. +// @author Antonio Scardace +// @version 1.0 + +public class AlertMessage { + + private AlertMessage() { + + } + + private static String getErrorMessage(String address, String name, String timestamp) { + return String.format("Hi 👋🏻, an error occurred on utility %s (%s) on %s ⚠️", name, address, timestamp); + } + + private static String getResolutionMessage(String address, String name, String timestamp) { + return String.format("Hi 👋🏻, the utility %s (%s) returned online on %s ✅", name, address, timestamp); + } + + private static String getUtcTimestamp() { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + return dateFormat.format(new Date()) + " UTC"; + } + + public static String generate(Observation observation, Boolean backOnline) { + String address = observation.getUtility().getAddress(); + String name = observation.getUtility().getName(); + String timestamp = AlertMessage.getUtcTimestamp(); + + JSONObject message = new JSONObject(); + message.put("contact", observation.getContact().getValue()); + message.put("contact_type", observation.getContact().getType()); + message.put("message", + Boolean.TRUE.equals(backOnline) + ? AlertMessage.getResolutionMessage(address, name, timestamp) + : AlertMessage.getErrorMessage(address, name, timestamp)); + + return message.toString(); + } +} \ No newline at end of file diff --git a/monitor/src/main/resources/application.properties b/monitor/src/main/resources/application.properties new file mode 100644 index 0000000..301eaed --- /dev/null +++ b/monitor/src/main/resources/application.properties @@ -0,0 +1,8 @@ +spring.rabbitmq.host=${RABBITMQ_HOST} +spring.rabbitmq.port=${RABBITMQ_PORT} +spring.rabbitmq.username=${RABBITMQ_USER} +spring.rabbitmq.password=${RABBITMQ_PSW} + +spring.redis.host=${REDIS_HOST} +spring.redis.port=${REDIS_PORT} +spring.redis.password=${REDIS_PSW} \ No newline at end of file diff --git a/monitor/src/test/java/netwatch/monitor/requests/PingRequestTest.java b/monitor/src/test/java/netwatch/monitor/requests/PingRequestTest.java new file mode 100644 index 0000000..16958f6 --- /dev/null +++ b/monitor/src/test/java/netwatch/monitor/requests/PingRequestTest.java @@ -0,0 +1,40 @@ +package netwatch.monitor.requests; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +// Unit tests for the {@link PingRequest} class. +// The tests examine both successful and unsuccessful scenarios. +// @author Antonio Scardace +// @version 1.0 + +class PingRequestTest { + + @Test + void testRequest_Successful_withLocalhost() { + PingRequest pingRequest = new PingRequest(); + String ipAddress = "127.0.0.1"; + assertTrue(pingRequest.request(ipAddress)); + } + + @Test + void testRequest_Successful_withName() { + PingRequest pingRequest = new PingRequest(); + String ipAddress = "google.com"; + assertTrue(pingRequest.request(ipAddress)); + } + + @Test + void testRequest_Unsuccessful_withUnvalidIp() { + PingRequest pingRequest = new PingRequest(); + String invalidIpAddress = "1.1."; + assertFalse(pingRequest.request(invalidIpAddress)); + } + + @Test + void testRequest_Unsuccessful_withUnvalidName() { + PingRequest pingRequest = new PingRequest(); + String invalidIpAddress = "https://google.com"; + assertFalse(pingRequest.request(invalidIpAddress)); + } +} diff --git a/monitor/src/test/java/netwatch/monitor/requests/RequestCreatorTest.java b/monitor/src/test/java/netwatch/monitor/requests/RequestCreatorTest.java new file mode 100644 index 0000000..3dad7fd --- /dev/null +++ b/monitor/src/test/java/netwatch/monitor/requests/RequestCreatorTest.java @@ -0,0 +1,26 @@ +package netwatch.monitor.requests; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +// Unit tests for the {@link RequestCreator} class. +// The tests examine both successful and unsuccessful scenarios. +// @author Antonio Scardace +// @version 1.0 + +class RequestCreatorTest { + + @Test + void testGetRequest_Successful() { + IRequest request = RequestCreator.getRequest("ip"); + assertNotNull(request); + assertTrue(request instanceof PingRequest); + } + + @Test + void testGetRequest_UnknownType_ExceptionThrown() { + String unknownType = "unknown"; + assertThrows(IllegalArgumentException.class, () -> RequestCreator.getRequest(unknownType)); + } +} \ No newline at end of file