diff --git a/web-backend/src/main/java/com/statusneo/vms/service/GraphEmailService.java b/web-backend/src/main/java/com/statusneo/vms/service/GraphEmailService.java index 67bee2b..a4755be 100644 --- a/web-backend/src/main/java/com/statusneo/vms/service/GraphEmailService.java +++ b/web-backend/src/main/java/com/statusneo/vms/service/GraphEmailService.java @@ -22,6 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -41,7 +42,7 @@ * Service responsible for handling all email communications in the Visitor Management System. */ @Service -@Profile({"prod", "default", "sqlite"}) +@Profile({"prod", "default", "sqlite", "test"}) public class GraphEmailService implements EmailService { private static final Logger logger = LoggerFactory.getLogger(GraphEmailService.class); @@ -49,6 +50,9 @@ public class GraphEmailService implements EmailService { private final RestClient restClient; private final OAuth2AuthorizedClientManager authorizedClientManager; + @Value("${graph.api.base-url}") + private String graphApiBaseUrl; + @Autowired public GraphEmailService(OAuth2AuthorizedClientManager authorizedClientManager, RestClient restClient) { @@ -72,7 +76,7 @@ public String getAccessToken() { @Override public boolean sendEmail(Email email) { String accessToken = getAccessToken(); - String endpointUsers = String.format("https://graph.microsoft.com/v1.0/users/%s/sendMail", email.from()); + String endpointUsers = String.format("%s/users/%s/sendMail", graphApiBaseUrl, email.from()); Map emailData = new HashMap<>(); Map message = new HashMap<>(); diff --git a/web-backend/src/main/java/com/statusneo/vms/service/NotificationService.java b/web-backend/src/main/java/com/statusneo/vms/service/NotificationService.java index 8940d39..52ed4b0 100644 --- a/web-backend/src/main/java/com/statusneo/vms/service/NotificationService.java +++ b/web-backend/src/main/java/com/statusneo/vms/service/NotificationService.java @@ -46,14 +46,17 @@ public class NotificationService { private final EmailService emailService; private final TemplateEngine templateEngine; + private final String systemFrom; // Use the env-backed VMS_SYSTEM_EMAIL (fallback to vms.system-email or default noreply) - @Value("${VMS_SYSTEM_EMAIL:${vms.system-email:noreply@company.com}}") - private String systemFrom; - - public NotificationService(EmailService emailService, TemplateEngine templateEngine) { + public NotificationService( + EmailService emailService, + TemplateEngine templateEngine, + @Value("${VMS_SYSTEM_EMAIL:${vms.system-email:noreply@company.com}}") String systemFrom + ) { this.emailService = emailService; this.templateEngine = templateEngine; + this.systemFrom = systemFrom; } public void sendVisitorConfirmationEmail(Visitor visitor) { diff --git a/web-backend/src/main/java/com/statusneo/vms/service/OtpService.java b/web-backend/src/main/java/com/statusneo/vms/service/OtpService.java index aa02a2b..be24d68 100644 --- a/web-backend/src/main/java/com/statusneo/vms/service/OtpService.java +++ b/web-backend/src/main/java/com/statusneo/vms/service/OtpService.java @@ -71,7 +71,7 @@ public OtpService(OtpRepository otpRepository, EmailService emailService, this.systemFrom = systemFrom; } - private static final int OTP_EXPIRATION_MINUTES = 10; + private static final int OTP_EXPIRATION_MINUTES = 3; private static final int MAX_OTP_ATTEMPTS = 2; private static final int MAX_RESEND_COUNT = 2; private static final int RESEND_COOLDOWN_MINUTES = 2; diff --git a/web-backend/src/main/java/com/statusneo/vms/service/ScheduledTasks.java b/web-backend/src/main/java/com/statusneo/vms/service/ScheduledTasks.java index 121912e..70aaa11 100644 --- a/web-backend/src/main/java/com/statusneo/vms/service/ScheduledTasks.java +++ b/web-backend/src/main/java/com/statusneo/vms/service/ScheduledTasks.java @@ -26,7 +26,7 @@ import org.springframework.stereotype.Component; @Component -@Profile("prod") +@Profile({"prod","test"}) public class ScheduledTasks { private final ExcelService excelService; @@ -34,12 +34,15 @@ public class ScheduledTasks { private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class); - public ScheduledTasks(ExcelService excelService, GraphDirectoryService graphDirectoryService) { this.excelService = excelService; this.graphDirectoryService = graphDirectoryService; } + @Scheduled( + fixedRateString = "${vms.scheduled.report.rate:43200000}", + initialDelayString = "${vms.scheduled.report.initialDelay:PT2H}" + ) @Scheduled(fixedRateString = "${vms.scheduled.report.rate:43200000}", initialDelayString = "PT2H") // Runs every 12 hours by default public void sendVisitorReport() { excelService.sendVisitorReport(); diff --git a/web-backend/src/main/java/com/statusneo/vms/service/VisitService.java b/web-backend/src/main/java/com/statusneo/vms/service/VisitService.java index b75a9e5..f57e1c1 100644 --- a/web-backend/src/main/java/com/statusneo/vms/service/VisitService.java +++ b/web-backend/src/main/java/com/statusneo/vms/service/VisitService.java @@ -56,23 +56,6 @@ public VisitService(OtpService otpService, VisitorRepository visitorRepository, this.visitRepository = visitRepository; } - - // Send OTP -// notificationService.sendOtp(visitor.getEmail(), visitor.getOtp()); -// } -// -// @Transactional -// public Visitor saveVisitor(Visitor visitor) { -// Visitor savedVisitor = visitorRepository.save(visitor); -// emailService.sendVisitorEmail(savedVisitor); -// otpService.sendOtp(savedVisitor.getEmail()); -// return savedVisitor; -// } - - -// return otpService.sendOtp(visit.getVisitor().getEmail(), visit); -// } - /** * Registers a new visit and generates an OTP for visitor verification. * The OTP is associated with the visit and sent to the visitor's email. diff --git a/web-backend/src/main/java/com/statusneo/vms/service/WiremockMailServiceImpl.java b/web-backend/src/main/java/com/statusneo/vms/service/WiremockMailServiceImpl.java deleted file mode 100644 index 2fbf273..0000000 --- a/web-backend/src/main/java/com/statusneo/vms/service/WiremockMailServiceImpl.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package com.statusneo.vms.service; - -import com.statusneo.vms.model.Email; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Profile; -import org.springframework.http.*; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; - -import java.util.List; -import java.util.Map; - -@Service -@Profile({"dev", "test"}) -public class WiremockMailServiceImpl implements EmailService{ - - private final RestTemplate restTemplate; - - @Value("${mail.url}") - private String wiremockMailUrl; - - public WiremockMailServiceImpl(RestTemplate restTemplate) { - this.restTemplate = restTemplate; - } - /** - * Sends a simple email for local/dev testing. - * - * */ - public boolean sendEmail(Email email) { - String endpoint = String.format(wiremockMailUrl, email.from()); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - - headers.setBearerAuth("dummy-token"); - Map payload = Map.of( - "message", Map.of( - "subject", email.subject(), - "body", Map.of( - "contentType", "Text", - "content", email.body() - ), - "toRecipients", List.of( - Map.of("emailAddress", Map.of("address", email.to())) - ) - ), - "saveToSentItems", true - ); - - HttpEntity> request = new HttpEntity<>(payload, headers); - ResponseEntity response = restTemplate.postForEntity(endpoint, request, String.class); - return response.getStatusCode().equals(HttpStatus.ACCEPTED); - } -} diff --git a/web-backend/src/main/resources/application.yml b/web-backend/src/main/resources/application.yml index 96d581b..f283eae 100644 --- a/web-backend/src/main/resources/application.yml +++ b/web-backend/src/main/resources/application.yml @@ -47,8 +47,7 @@ vms: sync: onstartup: enabled: false -wiremock: - server: - port: 8081 -mail: - url: http://localhost:${wiremock.server.port}/v1.0/users/%s/sendMail + +graph: + api: + base-url: https://graph.microsoft.com/v1.0 \ No newline at end of file diff --git a/web-backend/src/test/java/com/statusneo/vms/VmsApplicationTests.java b/web-backend/src/test/java/com/statusneo/vms/VmsApplicationTests.java index b4b5092..81ab964 100644 --- a/web-backend/src/test/java/com/statusneo/vms/VmsApplicationTests.java +++ b/web-backend/src/test/java/com/statusneo/vms/VmsApplicationTests.java @@ -2,14 +2,13 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; -@Import(TestcontainersConfiguration.class) @SpringBootTest +@ActiveProfiles("test") class VmsApplicationTests { @Test void contextLoads() { } - } diff --git a/web-backend/src/test/java/com/statusneo/vms/config/TestConfig.java b/web-backend/src/test/java/com/statusneo/vms/config/TestConfig.java new file mode 100644 index 0000000..daea6b5 --- /dev/null +++ b/web-backend/src/test/java/com/statusneo/vms/config/TestConfig.java @@ -0,0 +1,18 @@ +package com.statusneo.vms.config; +import com.statusneo.vms.service.EmailService; +import com.statusneo.vms.service.GraphDirectoryService; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import static org.mockito.Mockito.mock; + +@TestConfiguration +public class TestConfig { + @Bean + public GraphDirectoryService graphDirectoryService() { + return mock(GraphDirectoryService.class); + } + @Bean + public EmailService emailService() { + return mock(EmailService.class); + } +} \ No newline at end of file diff --git a/web-backend/src/test/java/com/statusneo/vms/service/ExcelServiceITest.java b/web-backend/src/test/java/com/statusneo/vms/service/ExcelServiceITest.java index 66eb1f0..d6ceb66 100644 --- a/web-backend/src/test/java/com/statusneo/vms/service/ExcelServiceITest.java +++ b/web-backend/src/test/java/com/statusneo/vms/service/ExcelServiceITest.java @@ -1,6 +1,5 @@ package com.statusneo.vms.service; -import com.statusneo.vms.TestcontainersConfiguration; import com.statusneo.vms.model.Visitor; import com.statusneo.vms.repository.VisitorRepository; import org.junit.jupiter.api.BeforeEach; @@ -8,8 +7,8 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -19,9 +18,11 @@ @SpringBootTest @Disabled("Enable this test with real credentials and configuration for full integration testing.") @ActiveProfiles("test") -@Import(TestcontainersConfiguration.class) class ExcelServiceITest { + @MockitoBean + private EmailService emailService; + @Autowired private ExcelService excelService; diff --git a/web-backend/src/test/java/com/statusneo/vms/service/GraphEmailServiceIntegrationTest.java b/web-backend/src/test/java/com/statusneo/vms/service/GraphEmailServiceIntegrationTest.java index b85feb7..87984c8 100644 --- a/web-backend/src/test/java/com/statusneo/vms/service/GraphEmailServiceIntegrationTest.java +++ b/web-backend/src/test/java/com/statusneo/vms/service/GraphEmailServiceIntegrationTest.java @@ -1,13 +1,11 @@ package com.statusneo.vms.service; -import com.statusneo.vms.TestcontainersConfiguration; import com.statusneo.vms.model.Email; import com.statusneo.vms.model.Attachment; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; import org.springframework.test.context.ActiveProfiles; import java.util.List; @@ -16,7 +14,6 @@ @SpringBootTest @ActiveProfiles("test") -@Import(TestcontainersConfiguration.class) class GraphEmailServiceIntegrationTest { @Autowired diff --git a/web-backend/src/test/java/com/statusneo/vms/service/GraphEmailServiceTest.java b/web-backend/src/test/java/com/statusneo/vms/service/GraphEmailServiceTest.java index 1ad31ac..af3e564 100644 --- a/web-backend/src/test/java/com/statusneo/vms/service/GraphEmailServiceTest.java +++ b/web-backend/src/test/java/com/statusneo/vms/service/GraphEmailServiceTest.java @@ -7,15 +7,24 @@ import org.mockito.MockitoAnnotations; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.client.RestClient; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import com.statusneo.vms.model.Email; +import java.time.Instant; +import java.util.Map; + + +@ActiveProfiles("test") class GraphEmailServiceTest { @Mock @@ -33,9 +42,23 @@ class GraphEmailServiceTest { @InjectMocks private GraphEmailService graphEmailService; + @Mock + private OAuth2AuthorizedClientManager authorizedClientManager; + @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); + OAuth2AccessToken accessToken = new OAuth2AccessToken( + OAuth2AccessToken.TokenType.BEARER, + "dummy-token", + Instant.now(), + Instant.now().plusSeconds(3600) + ); + + OAuth2AuthorizedClient authorizedClient = mock(OAuth2AuthorizedClient.class); + when(authorizedClient.getAccessToken()).thenReturn(accessToken); + when(authorizedClientManager.authorize(any())).thenReturn(authorizedClient); + ReflectionTestUtils.setField(graphEmailService, "graphApiBaseUrl", "https://graph.microsoft.com/v1.0"); } @Test @@ -47,7 +70,6 @@ void testSendEmail_Success() { String body = "Test Body"; ResponseEntity responseEntity = new ResponseEntity<>(HttpStatus.ACCEPTED); - mockRestClientResponse(responseEntity); // Act @@ -68,7 +90,6 @@ void testSendEmail_Failure() { String body = "Test Body"; ResponseEntity responseEntity = new ResponseEntity<>(HttpStatus.BAD_REQUEST); - mockRestClientResponse(responseEntity); // Act @@ -84,8 +105,24 @@ private void mockRestClientResponse(ResponseEntity responseEntity) { when(restClient.post()).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.uri(anyString())).thenReturn(requestBodySpec); when(requestBodySpec.headers(any())).thenReturn(requestBodySpec); - when(requestBodySpec.body(any())).thenReturn(requestBodySpec); + when(requestBodySpec.body(any(Map.class))).thenReturn(requestBodySpec); when(requestBodySpec.retrieve()).thenReturn(responseSpec); when(responseSpec.toBodilessEntity()).thenReturn(responseEntity); } + + @Test + void testSendEmail_AccessTokenFailure() { + when(authorizedClientManager.authorize(any())).thenReturn(null); + + String fromEmail = "system-user@example.com"; + String toEmail = "recipient@example.com"; + String subject = "Test Subject"; + String body = "Test Body"; + + RuntimeException exception = assertThrows(RuntimeException.class, () -> + graphEmailService.sendEmail(Email.of(fromEmail, toEmail, subject, body)) + ); + + assertEquals("Failed to obtain access token", exception.getMessage()); + } } \ No newline at end of file diff --git a/web-backend/src/test/java/com/statusneo/vms/service/GraphEmailServiceWireMockTest.java b/web-backend/src/test/java/com/statusneo/vms/service/GraphEmailServiceWireMockTest.java new file mode 100644 index 0000000..75c3971 --- /dev/null +++ b/web-backend/src/test/java/com/statusneo/vms/service/GraphEmailServiceWireMockTest.java @@ -0,0 +1,108 @@ +package com.statusneo.vms.service; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.jupiter.api.Assertions.*; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import com.statusneo.vms.model.Email; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.web.client.HttpClientErrorException; + +@SpringBootTest +@ActiveProfiles("test") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class GraphEmailServiceWireMockTest { + + private static final WireMockServer wireMockServer; + + static { + wireMockServer = new WireMockServer(WireMockConfiguration.options().dynamicPort()); + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + stubFor(post(urlEqualTo("/oauth2/token")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody("{\"access_token\":\"dummy-access-token\",\"token_type\":\"Bearer\",\"expires_in\":3600}"))); + + stubFor(post(urlMatching("/v1.0/users/.*/sendMail")) + .willReturn(aResponse().withStatus(202))); + } + + @DynamicPropertySource + static void overrideProperties(DynamicPropertyRegistry registry) { + registry.add("spring.security.oauth2.client.provider.azure.token-uri", + () -> "http://localhost:" + wireMockServer.port() + "/oauth2/token"); + registry.add("graph.api.base-url", + () -> "http://localhost:" + wireMockServer.port() + "/v1.0"); + } + + @Autowired + private GraphEmailService graphEmailService; + + @Test + void testSendEmail_Success() { + boolean result = graphEmailService.sendEmail( + Email.of("system-user@example.com", "recipient@example.com", "Subject", "Body") + ); + + assertTrue(result, "Email should be sent successfully"); + + System.out.println("==== WireMock Recorded Requests ===="); + wireMockServer.getAllServeEvents().forEach(event -> { + System.out.println("Request URL: " + event.getRequest().getUrl()); + System.out.println("Headers: " + event.getRequest().getHeaders()); + System.out.println("Body: " + event.getRequest().getBodyAsString()); + }); + + WireMock.configureFor("localhost", wireMockServer.port()); + + verify(postRequestedFor(urlMatching("/v1.0/users/.*/sendMail")) + .withHeader("Authorization", matching("Bearer .*"))); + } + + + @Test + void testSendEmail_FailureFromGraphAPI() { + // Override stub for failure + stubFor(post(urlMatching("/v1.0/users/.*/sendMail")) + .willReturn(aResponse().withStatus(400))); + + Exception exception = assertThrows(HttpClientErrorException.class, () -> + graphEmailService.sendEmail( + Email.of("system-user@example.com", "recipient@example.com", "Subject", "Body") + ) + ); + + assertEquals(400, ((HttpClientErrorException) exception).getStatusCode().value(), + "Should throw 400 Bad Request"); + } + + @Test + void testSendEmail_TokenEndpointFailure() { + stubFor(post(urlEqualTo("/oauth2/token")) + .willReturn(aResponse().withStatus(500))); + + Exception exception = assertThrows(HttpClientErrorException.class, () -> + graphEmailService.sendEmail( + Email.of("system-user@example.com", "recipient@example.com", "Subject", "Body") + ) + ); + + assertEquals(400, ((HttpClientErrorException) exception).getStatusCode().value(), + "Should fail due to invalid token causing bad request"); + } + + @AfterAll + void stopWireMock() { + wireMockServer.stop(); + } +} diff --git a/web-backend/src/test/java/com/statusneo/vms/service/NotificationServiceTest.java b/web-backend/src/test/java/com/statusneo/vms/service/NotificationServiceTest.java index 30c7fc3..b7696a4 100644 --- a/web-backend/src/test/java/com/statusneo/vms/service/NotificationServiceTest.java +++ b/web-backend/src/test/java/com/statusneo/vms/service/NotificationServiceTest.java @@ -26,7 +26,7 @@ public class NotificationServiceTest { void setUp() { emailService = mock(EmailService.class); templateEngine = mock(TemplateEngine.class); - notificationService = new NotificationService(emailService, templateEngine); + notificationService = new NotificationService(emailService, templateEngine, "noreply@company.com"); } @Test diff --git a/web-backend/src/test/java/com/statusneo/vms/service/ScheduledTasksSchedulingITest.java b/web-backend/src/test/java/com/statusneo/vms/service/ScheduledTasksSchedulingITest.java index 02dd321..f8a4092 100644 --- a/web-backend/src/test/java/com/statusneo/vms/service/ScheduledTasksSchedulingITest.java +++ b/web-backend/src/test/java/com/statusneo/vms/service/ScheduledTasksSchedulingITest.java @@ -1,6 +1,7 @@ package com.statusneo.vms.service; -import com.statusneo.vms.TestcontainersConfiguration; +import com.statusneo.vms.config.TestConfig; +import com.statusneo.vms.dto.DirectorySyncResult; import org.awaitility.Awaitility; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -8,24 +9,31 @@ import org.springframework.context.annotation.Import; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import java.util.concurrent.TimeUnit; import static org.mockito.Mockito.*; -@SpringBootTest(properties = "vms.scheduled.report.rate=500") // 0.5 seconds +@SpringBootTest(properties = {"vms.scheduled.report.rate=500","vms.scheduled.report.initialDelay=PT0S"}) // 0.5 seconds @EnableScheduling +@Import(TestConfig.class) @ActiveProfiles("test") -@Import(TestcontainersConfiguration.class) class ScheduledTasksSchedulingITest { @MockitoSpyBean private ScheduledTasks scheduledTasks; + @MockitoBean + private GraphDirectoryService graphDirectoryService; + + @BeforeEach void setUp() { clearInvocations(scheduledTasks); + DirectorySyncResult mockResult = new DirectorySyncResult(0, "dummy-delta-link"); + when(graphDirectoryService.syncAllUsersToEmployees()).thenReturn(mockResult); } @Test diff --git a/web-backend/src/test/java/com/statusneo/vms/service/VisitServiceIntegrationTest.java b/web-backend/src/test/java/com/statusneo/vms/service/VisitServiceIntegrationTest.java index 28573b8..cad0bab 100644 --- a/web-backend/src/test/java/com/statusneo/vms/service/VisitServiceIntegrationTest.java +++ b/web-backend/src/test/java/com/statusneo/vms/service/VisitServiceIntegrationTest.java @@ -17,6 +17,7 @@ */ package com.statusneo.vms.service; +import com.statusneo.vms.config.TestConfig; import com.statusneo.vms.dto.VerificationResult; import com.statusneo.vms.model.Visit; import com.statusneo.vms.model.Visitor; @@ -27,12 +28,9 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.bean.override.mockito.MockitoBean; -import org.testcontainers.containers.PostgreSQLContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; import java.time.LocalDateTime; import java.util.Optional; @@ -43,27 +41,14 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @SpringBootTest -@Testcontainers +@Import(TestConfig.class) +@ActiveProfiles("test") public class VisitServiceIntegrationTest { - @Container - public static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer<>("postgres:latest") - .withDatabaseName("test") - .withUsername("test") - .withPassword("test"); - - @DynamicPropertySource - static void postgresqlProperties(DynamicPropertyRegistry registry) { - registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl); - registry.add("spring.datasource.username", postgreSQLContainer::getUsername); - registry.add("spring.datasource.password", postgreSQLContainer::getPassword); - } - @Autowired private VisitService visitService; diff --git a/web-backend/src/test/java/com/statusneo/vms/service/VisitServiceTest.java b/web-backend/src/test/java/com/statusneo/vms/service/VisitServiceTest.java index de9bd61..38feb5a 100644 --- a/web-backend/src/test/java/com/statusneo/vms/service/VisitServiceTest.java +++ b/web-backend/src/test/java/com/statusneo/vms/service/VisitServiceTest.java @@ -1,5 +1,6 @@ package com.statusneo.vms.service; +import com.statusneo.vms.config.TestConfig; import com.statusneo.vms.dto.VerificationResult; import com.statusneo.vms.model.Visit; import com.statusneo.vms.model.Visitor; @@ -8,12 +9,9 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.bean.override.mockito.MockitoBean; -import org.testcontainers.containers.PostgreSQLContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -26,17 +24,10 @@ import static org.mockito.ArgumentMatchers.eq; @SpringBootTest -@Testcontainers +@Import(TestConfig.class) +@ActiveProfiles("test") public class VisitServiceTest { - @Container - public static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer<>("postgres:latest") - .withDatabaseName("test") - .withUsername("test") - .withPassword("test"); - @Autowired - private VisitService visitorService; - @Autowired private VisitService visitService; @@ -46,12 +37,9 @@ public class VisitServiceTest { @MockitoBean private OtpService otpService; - @DynamicPropertySource - static void postgresqlProperties(DynamicPropertyRegistry registry) { - registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl); - registry.add("spring.datasource.username", postgreSQLContainer::getUsername); - registry.add("spring.datasource.password", postgreSQLContainer::getPassword); - } + @MockitoBean + private NotificationService notificationService; + @Test public void testRegisterVisitor() { @@ -63,36 +51,33 @@ public void testRegisterVisitor() { visit.setHost("Host Name"); visitor.setAddress("123 Street, City, Country"); visit.setVisitDate(LocalDateTime.parse("2022-01-01T00:00:00", DateTimeFormatter.ISO_LOCAL_DATE_TIME)); - -// Visit registeredVisitor = visitorService.registerVisit(visit); - -// assertNotNull(registeredVisitor); - } - - @Test - public void testConfirmVisit_Success() { - Visitor visitor = new Visitor(); - visitor.setName("Anurag Sharma"); - visitor.setEmail("anurag@gmail.com"); - visitor.setPhoneNumber("9999999999"); - visitor.setAddress("123 Delhi Address"); - - Visit visit = visitService.registerVisit(visitor); - String dummyOtp = "123456"; - - VerificationResult verificationResult = new VerificationResult(true, false, "OTP verified successfully"); - - Mockito.when(otpService.validateOtp(any(Visit.class), eq(dummyOtp))) - .thenReturn(verificationResult); - - VerificationResult result = visitService.confirmVisit(visit.getId(), dummyOtp); - - assertNotNull(result); - assertTrue(result.success(), "OTP verification should be successful"); - assertFalse(result.reattempt(), "Reattempt should be false on success"); - assertEquals("OTP verified successfully", result.message()); - - Visit updatedVisit = visitRepository.findById(visit.getId()).orElseThrow(); - assertTrue(updatedVisit.getIsApproved(), "Visit should be approved after successful OTP verification"); } +@Test +public void testConfirmVisit_Success() { + Visitor visitor = new Visitor(); + visitor.setName("Anurag Sharma"); + visitor.setEmail("anurag@gmail.com"); + visitor.setPhoneNumber("9999999999"); + visitor.setAddress("123 Delhi Address"); + + Visit visit = visitService.registerVisit(visitor); + + String dummyOtp = "123456"; + visit.setOtp(dummyOtp); + visitRepository.save(visit); + + VerificationResult verificationResult = new VerificationResult(true, false, "OTP verified successfully"); + Mockito.when(otpService.validateOtp(any(Visit.class), eq(dummyOtp))) + .thenReturn(verificationResult); + + VerificationResult result = visitService.confirmVisit(visit.getId(), dummyOtp); + + assertNotNull(result); + assertTrue(result.success(), "OTP verification should be successful"); + assertFalse(result.reattempt(), "Reattempt should be false on success"); + assertEquals("OTP verified successfully", result.message()); + + Visit updatedVisit = visitRepository.findById(visit.getId()).orElseThrow(); + assertTrue(updatedVisit.getIsApproved(), "Visit should be approved after successful OTP verification"); +} } \ No newline at end of file diff --git a/web-backend/src/test/java/com/statusneo/vms/service/WiremockEmailServiceTest.java b/web-backend/src/test/java/com/statusneo/vms/service/WiremockEmailServiceTest.java deleted file mode 100644 index 283cc2f..0000000 --- a/web-backend/src/test/java/com/statusneo/vms/service/WiremockEmailServiceTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.statusneo.vms.service; - -import static com.github.tomakehurst.wiremock.client.WireMock.*; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.github.tomakehurst.wiremock.WireMockServer; -import com.github.tomakehurst.wiremock.junit5.WireMockExtension; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.statusneo.vms.TestcontainersConfiguration; -import com.statusneo.vms.model.Email; -import org.junit.jupiter.api.*; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; -import org.wiremock.spring.EnableWireMock; -import org.wiremock.spring.InjectWireMock; - -@SpringBootTest -@Import(TestcontainersConfiguration.class) -@ActiveProfiles("test") -@EnableWireMock -@WireMockTest(httpPort = 0) -public class WiremockEmailServiceTest { - - @InjectWireMock - private WireMockServer wiremock; - - @Autowired - private WiremockMailServiceImpl wiremockMailService; - - @Test - void shouldSendEmailSuccessfully() { - wiremock.stubFor(post(urlMatching("/v1\\.0/users/[^/]+@[^/]+/sendMail")) - .willReturn(aResponse() - .withStatus(202) - .withHeader("Content-Type", "application/json") - .withBody("{\"message\": \"Email accepted by mock server\"}"))); - - - boolean result = wiremockMailService.sendEmail(Email.of( - "sender@sender.com", - "recipient@recipient.com", - "Integration Test Subject", - "This is a test email." - )); - - assertTrue(result, "Email should be sent successfully"); - } -} diff --git a/web-backend/src/test/resources/application-test.yml b/web-backend/src/test/resources/application-test.yml new file mode 100644 index 0000000..d814cb3 --- /dev/null +++ b/web-backend/src/test/resources/application-test.yml @@ -0,0 +1,27 @@ +spring: + datasource: + url: jdbc:sqlite:memory:testdb?cache=shared + driver-class-name: org.sqlite.JDBC + jpa: + hibernate: + ddl-auto: create-drop + show-sql: true + flyway: + enabled: false + security: + oauth2: + client: + registration: + azure: + client-id: dummy-client-id + client-secret: dummy-client-secret + authorization-grant-type: client_credentials + scope: https://graph.microsoft.com/.default + provider: + azure: {} +graph: + api: {} +logging: + level: + org.springframework.security: DEBUG + org.springframework.web.client.RestTemplate: DEBUG \ No newline at end of file