Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package com.thughari.jobtrackerpro.controller;

import com.thughari.jobtrackerpro.dto.JobDTO;
import com.thughari.jobtrackerpro.entity.User;
import com.thughari.jobtrackerpro.interfaces.GeminiService;
import com.thughari.jobtrackerpro.repo.UserRepository;
import com.thughari.jobtrackerpro.service.EmailService;
import com.thughari.jobtrackerpro.service.JobService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class WebhookControllerTest {

@Mock private JobService jobService;
@Mock private UserRepository userRepository;
@Mock private GeminiService geminiService;
@Mock private EmailService emailService;

@InjectMocks
private WebhookController webhookController;

@Test
void returnsInvalidPayloadWhenHeadersMissing() {
var response = webhookController.handleInboundEmail(Map.of("plain", "x"));
assertEquals("Invalid Payload", response.getBody());
}

@Test
void handlesGoogleForwardingVerification() {
Map<String, Object> headers = new HashMap<>();
headers.put("from", "mailer-daemon@google.com");
headers.put("subject", "Forwarding Confirmation");

String plain = "user@example.com has requested\nConfirmation code: 123456";

Map<String, Object> payload = new HashMap<>();
payload.put("headers", headers);
payload.put("plain", plain);

var response = webhookController.handleInboundEmail(payload);

assertEquals("Verification Forwarded", response.getBody());
verify(emailService).sendForwardingHelper("user@example.com", "123456", null);
}

@Test
void ignoresSystemEmails() {
Map<String, Object> headers = new HashMap<>();
headers.put("from", "foo@bar.com");
headers.put("subject", "Please verify your address");

Map<String, Object> payload = new HashMap<>();
payload.put("headers", headers);
payload.put("plain", "any body");

var response = webhookController.handleInboundEmail(payload);

assertEquals("Ignored System Email", response.getBody());
verifyNoInteractions(geminiService, jobService);
}

@Test
void processesKnownUserAndCreatesJob() {
Map<String, Object> headers = new HashMap<>();
headers.put("from", "Recruiter <hr@example.com>");
headers.put("subject", "Application update");

Map<String, Object> payload = new HashMap<>();
payload.put("headers", headers);
payload.put("plain", "hello");

User user = new User();
user.setEmail("Candidate@Example.com");
when(userRepository.findByEmail("hr@example.com")).thenReturn(Optional.of(user));

JobDTO jobDTO = new JobDTO();
jobDTO.setCompany("Acme");
when(geminiService.extractJobFromEmail(anyString(), anyString(), anyString())).thenReturn(jobDTO);

var response = webhookController.handleInboundEmail(payload);

assertEquals("Processed", response.getBody());
verify(jobService).createOrUpdateJob(jobDTO, "candidate@example.com");
}

@Test
void returnsSkippedWhenGeminiReturnsNull() {
Map<String, Object> headers = new HashMap<>();
headers.put("from", "hr@example.com");
headers.put("subject", "Update");
Map<String, Object> payload = new HashMap<>();
payload.put("headers", headers);
payload.put("plain", "content");

User user = new User();
user.setEmail("u@example.com");
when(userRepository.findByEmail("hr@example.com")).thenReturn(Optional.of(user));
when(geminiService.extractJobFromEmail(anyString(), anyString(), anyString())).thenReturn(null);

var response = webhookController.handleInboundEmail(payload);
assertEquals("Skipped", response.getBody());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.thughari.jobtrackerpro.exception;

import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import static org.junit.jupiter.api.Assertions.*;

class GlobalExceptionHandlerTest {

private final GlobalExceptionHandler handler = new GlobalExceptionHandler();

@Test
void handlesBadRequestExceptions() {
var response = handler.handleBadRequest(new IllegalArgumentException("bad"));
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
assertEquals("bad", response.getBody().getMessage());
}

@Test
void handlesNotFound() {
var response = handler.handleNotFound(new ResourceNotFoundException("missing"));
assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
assertEquals("missing", response.getBody().getMessage());
}

@Test
void handlesPayloadTooLarge() {
var response = handler.handleMaxSizeException(new MaxUploadSizeExceededException(10));
assertEquals(HttpStatus.PAYLOAD_TOO_LARGE, response.getStatusCode());
assertTrue(response.getBody().getMessage().contains("File size exceeds"));
}

@Test
void handlesFallbackException() {
var response = handler.handleGeneralException(new RuntimeException("oops"));
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
assertEquals("An unexpected error occurred.", response.getBody().getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.thughari.jobtrackerpro.scheduler;

import com.thughari.jobtrackerpro.service.JobService;
import org.junit.jupiter.api.Test;

import static org.mockito.Mockito.*;

class JobSchedulerTest {

@Test
void runStaleJobCleanupInvokesService() {
JobService jobService = mock(JobService.class);
JobScheduler scheduler = new JobScheduler(jobService);

scheduler.runStaleJobCleanup();

verify(jobService).cleanupStaleApplications();
}

@Test
void runStaleJobCleanupHandlesServiceException() {
JobService jobService = mock(JobService.class);
doThrow(new RuntimeException("boom")).when(jobService).cleanupStaleApplications();
JobScheduler scheduler = new JobScheduler(jobService);

scheduler.runStaleJobCleanup();

verify(jobService).cleanupStaleApplications();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.thughari.jobtrackerpro.security;

import jakarta.servlet.FilterChain;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.core.context.SecurityContextHolder;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class JwtAuthenticationFilterTest {

@Mock
private JwtUtils jwtUtils;

@Mock
private FilterChain filterChain;

@AfterEach
void tearDown() {
SecurityContextHolder.clearContext();
}

@Test
void skipsWhenNoBearerHeader() throws Exception {
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(jwtUtils);
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();

filter.doFilter(request, response, filterChain);

assertNull(SecurityContextHolder.getContext().getAuthentication());
verify(filterChain).doFilter(request, response);
}

@Test
void setsAuthenticationWhenTokenValid() throws Exception {
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(jwtUtils);
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Authorization", "Bearer token123");
MockHttpServletResponse response = new MockHttpServletResponse();

when(jwtUtils.validateToken("token123")).thenReturn(true);
when(jwtUtils.getEmailFromToken("token123")).thenReturn("user@example.com");

filter.doFilter(request, response, filterChain);

assertNotNull(SecurityContextHolder.getContext().getAuthentication());
assertEquals("user@example.com", SecurityContextHolder.getContext().getAuthentication().getPrincipal());
verify(filterChain).doFilter(request, response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.thughari.jobtrackerpro.security;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;

import static org.junit.jupiter.api.Assertions.*;

class JwtUtilsTest {

private JwtUtils jwtUtils;

@BeforeEach
void setUp() {
jwtUtils = new JwtUtils();
ReflectionTestUtils.setField(jwtUtils, "jwtSecret", "01234567890123456789012345678901");
ReflectionTestUtils.setField(jwtUtils, "refreshJwtSecret", "abcdefghijklmnopqrstuvwxyz123456");
ReflectionTestUtils.setField(jwtUtils, "jwtExpirationMs", 60_000);
ReflectionTestUtils.setField(jwtUtils, "refreshJwtExpirationMs", 120_000L);
ReflectionTestUtils.setField(jwtUtils, "activeProfile", "test");
}

@Test
void accessTokenRoundTrip() {
String token = jwtUtils.generateAccessToken("user@example.com");
assertTrue(jwtUtils.validateAccessToken(token));
assertEquals("user@example.com", jwtUtils.getEmailFromToken(token));
}

@Test
void refreshTokenRoundTrip() {
String refreshToken = jwtUtils.generateRefreshToken("user@example.com");
assertTrue(jwtUtils.validateRefreshToken(refreshToken));
assertEquals("user@example.com", jwtUtils.getEmailFromRefreshToken(refreshToken));
}

@Test
void rejectsInvalidToken() {
assertFalse(jwtUtils.validateAccessToken("not-a-token"));
assertFalse(jwtUtils.validateRefreshToken("not-a-token"));
}

@Test
void generateTokenDelegatesToAccessToken() {
String token = jwtUtils.generateToken("mail@example.com");
assertTrue(jwtUtils.validateToken(token));
assertEquals("mail@example.com", jwtUtils.getEmailFromToken(token));
}
}
Loading