Skip to content

Commit ed87ee7

Browse files
authored
Merge pull request #44 from thughari/unit-test/coverage
Unit test/coverage
2 parents 2edd6c2 + 578a52c commit ed87ee7

File tree

8 files changed

+466
-0
lines changed

8 files changed

+466
-0
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package com.thughari.jobtrackerpro.controller;
2+
3+
import com.thughari.jobtrackerpro.dto.JobDTO;
4+
import com.thughari.jobtrackerpro.entity.User;
5+
import com.thughari.jobtrackerpro.interfaces.GeminiService;
6+
import com.thughari.jobtrackerpro.repo.UserRepository;
7+
import com.thughari.jobtrackerpro.service.EmailService;
8+
import com.thughari.jobtrackerpro.service.JobService;
9+
import org.junit.jupiter.api.Test;
10+
import org.junit.jupiter.api.extension.ExtendWith;
11+
import org.mockito.InjectMocks;
12+
import org.mockito.Mock;
13+
import org.mockito.junit.jupiter.MockitoExtension;
14+
15+
import java.util.HashMap;
16+
import java.util.Map;
17+
import java.util.Optional;
18+
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.mockito.ArgumentMatchers.anyString;
21+
import static org.mockito.Mockito.*;
22+
23+
@ExtendWith(MockitoExtension.class)
24+
class WebhookControllerTest {
25+
26+
@Mock private JobService jobService;
27+
@Mock private UserRepository userRepository;
28+
@Mock private GeminiService geminiService;
29+
@Mock private EmailService emailService;
30+
31+
@InjectMocks
32+
private WebhookController webhookController;
33+
34+
@Test
35+
void returnsInvalidPayloadWhenHeadersMissing() {
36+
var response = webhookController.handleInboundEmail(Map.of("plain", "x"));
37+
assertEquals("Invalid Payload", response.getBody());
38+
}
39+
40+
@Test
41+
void handlesGoogleForwardingVerification() {
42+
Map<String, Object> headers = new HashMap<>();
43+
headers.put("from", "mailer-daemon@google.com");
44+
headers.put("subject", "Forwarding Confirmation");
45+
46+
String plain = "user@example.com has requested\nConfirmation code: 123456";
47+
48+
Map<String, Object> payload = new HashMap<>();
49+
payload.put("headers", headers);
50+
payload.put("plain", plain);
51+
52+
var response = webhookController.handleInboundEmail(payload);
53+
54+
assertEquals("Verification Forwarded", response.getBody());
55+
verify(emailService).sendForwardingHelper("user@example.com", "123456", null);
56+
}
57+
58+
@Test
59+
void ignoresSystemEmails() {
60+
Map<String, Object> headers = new HashMap<>();
61+
headers.put("from", "foo@bar.com");
62+
headers.put("subject", "Please verify your address");
63+
64+
Map<String, Object> payload = new HashMap<>();
65+
payload.put("headers", headers);
66+
payload.put("plain", "any body");
67+
68+
var response = webhookController.handleInboundEmail(payload);
69+
70+
assertEquals("Ignored System Email", response.getBody());
71+
verifyNoInteractions(geminiService, jobService);
72+
}
73+
74+
@Test
75+
void processesKnownUserAndCreatesJob() {
76+
Map<String, Object> headers = new HashMap<>();
77+
headers.put("from", "Recruiter <hr@example.com>");
78+
headers.put("subject", "Application update");
79+
80+
Map<String, Object> payload = new HashMap<>();
81+
payload.put("headers", headers);
82+
payload.put("plain", "hello");
83+
84+
User user = new User();
85+
user.setEmail("Candidate@Example.com");
86+
when(userRepository.findByEmail("hr@example.com")).thenReturn(Optional.of(user));
87+
88+
JobDTO jobDTO = new JobDTO();
89+
jobDTO.setCompany("Acme");
90+
when(geminiService.extractJobFromEmail(anyString(), anyString(), anyString())).thenReturn(jobDTO);
91+
92+
var response = webhookController.handleInboundEmail(payload);
93+
94+
assertEquals("Processed", response.getBody());
95+
verify(jobService).createOrUpdateJob(jobDTO, "candidate@example.com");
96+
}
97+
98+
@Test
99+
void returnsSkippedWhenGeminiReturnsNull() {
100+
Map<String, Object> headers = new HashMap<>();
101+
headers.put("from", "hr@example.com");
102+
headers.put("subject", "Update");
103+
Map<String, Object> payload = new HashMap<>();
104+
payload.put("headers", headers);
105+
payload.put("plain", "content");
106+
107+
User user = new User();
108+
user.setEmail("u@example.com");
109+
when(userRepository.findByEmail("hr@example.com")).thenReturn(Optional.of(user));
110+
when(geminiService.extractJobFromEmail(anyString(), anyString(), anyString())).thenReturn(null);
111+
112+
var response = webhookController.handleInboundEmail(payload);
113+
assertEquals("Skipped", response.getBody());
114+
}
115+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.thughari.jobtrackerpro.exception;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.springframework.http.HttpStatus;
5+
import org.springframework.web.multipart.MaxUploadSizeExceededException;
6+
7+
import static org.junit.jupiter.api.Assertions.*;
8+
9+
class GlobalExceptionHandlerTest {
10+
11+
private final GlobalExceptionHandler handler = new GlobalExceptionHandler();
12+
13+
@Test
14+
void handlesBadRequestExceptions() {
15+
var response = handler.handleBadRequest(new IllegalArgumentException("bad"));
16+
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
17+
assertEquals("bad", response.getBody().getMessage());
18+
}
19+
20+
@Test
21+
void handlesNotFound() {
22+
var response = handler.handleNotFound(new ResourceNotFoundException("missing"));
23+
assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
24+
assertEquals("missing", response.getBody().getMessage());
25+
}
26+
27+
@Test
28+
void handlesPayloadTooLarge() {
29+
var response = handler.handleMaxSizeException(new MaxUploadSizeExceededException(10));
30+
assertEquals(HttpStatus.PAYLOAD_TOO_LARGE, response.getStatusCode());
31+
assertTrue(response.getBody().getMessage().contains("File size exceeds"));
32+
}
33+
34+
@Test
35+
void handlesFallbackException() {
36+
var response = handler.handleGeneralException(new RuntimeException("oops"));
37+
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
38+
assertEquals("An unexpected error occurred.", response.getBody().getMessage());
39+
}
40+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.thughari.jobtrackerpro.scheduler;
2+
3+
import com.thughari.jobtrackerpro.service.JobService;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static org.mockito.Mockito.*;
7+
8+
class JobSchedulerTest {
9+
10+
@Test
11+
void runStaleJobCleanupInvokesService() {
12+
JobService jobService = mock(JobService.class);
13+
JobScheduler scheduler = new JobScheduler(jobService);
14+
15+
scheduler.runStaleJobCleanup();
16+
17+
verify(jobService).cleanupStaleApplications();
18+
}
19+
20+
@Test
21+
void runStaleJobCleanupHandlesServiceException() {
22+
JobService jobService = mock(JobService.class);
23+
doThrow(new RuntimeException("boom")).when(jobService).cleanupStaleApplications();
24+
JobScheduler scheduler = new JobScheduler(jobService);
25+
26+
scheduler.runStaleJobCleanup();
27+
28+
verify(jobService).cleanupStaleApplications();
29+
}
30+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.thughari.jobtrackerpro.security;
2+
3+
import jakarta.servlet.FilterChain;
4+
import org.junit.jupiter.api.AfterEach;
5+
import org.junit.jupiter.api.Test;
6+
import org.junit.jupiter.api.extension.ExtendWith;
7+
import org.mockito.Mock;
8+
import org.mockito.junit.jupiter.MockitoExtension;
9+
import org.springframework.mock.web.MockHttpServletRequest;
10+
import org.springframework.mock.web.MockHttpServletResponse;
11+
import org.springframework.security.core.context.SecurityContextHolder;
12+
13+
import static org.junit.jupiter.api.Assertions.*;
14+
import static org.mockito.Mockito.*;
15+
16+
@ExtendWith(MockitoExtension.class)
17+
class JwtAuthenticationFilterTest {
18+
19+
@Mock
20+
private JwtUtils jwtUtils;
21+
22+
@Mock
23+
private FilterChain filterChain;
24+
25+
@AfterEach
26+
void tearDown() {
27+
SecurityContextHolder.clearContext();
28+
}
29+
30+
@Test
31+
void skipsWhenNoBearerHeader() throws Exception {
32+
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(jwtUtils);
33+
MockHttpServletRequest request = new MockHttpServletRequest();
34+
MockHttpServletResponse response = new MockHttpServletResponse();
35+
36+
filter.doFilter(request, response, filterChain);
37+
38+
assertNull(SecurityContextHolder.getContext().getAuthentication());
39+
verify(filterChain).doFilter(request, response);
40+
}
41+
42+
@Test
43+
void setsAuthenticationWhenTokenValid() throws Exception {
44+
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(jwtUtils);
45+
MockHttpServletRequest request = new MockHttpServletRequest();
46+
request.addHeader("Authorization", "Bearer token123");
47+
MockHttpServletResponse response = new MockHttpServletResponse();
48+
49+
when(jwtUtils.validateToken("token123")).thenReturn(true);
50+
when(jwtUtils.getEmailFromToken("token123")).thenReturn("user@example.com");
51+
52+
filter.doFilter(request, response, filterChain);
53+
54+
assertNotNull(SecurityContextHolder.getContext().getAuthentication());
55+
assertEquals("user@example.com", SecurityContextHolder.getContext().getAuthentication().getPrincipal());
56+
verify(filterChain).doFilter(request, response);
57+
}
58+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.thughari.jobtrackerpro.security;
2+
3+
import org.junit.jupiter.api.BeforeEach;
4+
import org.junit.jupiter.api.Test;
5+
import org.springframework.test.util.ReflectionTestUtils;
6+
7+
import static org.junit.jupiter.api.Assertions.*;
8+
9+
class JwtUtilsTest {
10+
11+
private JwtUtils jwtUtils;
12+
13+
@BeforeEach
14+
void setUp() {
15+
jwtUtils = new JwtUtils();
16+
ReflectionTestUtils.setField(jwtUtils, "jwtSecret", "01234567890123456789012345678901");
17+
ReflectionTestUtils.setField(jwtUtils, "refreshJwtSecret", "abcdefghijklmnopqrstuvwxyz123456");
18+
ReflectionTestUtils.setField(jwtUtils, "jwtExpirationMs", 60_000);
19+
ReflectionTestUtils.setField(jwtUtils, "refreshJwtExpirationMs", 120_000L);
20+
ReflectionTestUtils.setField(jwtUtils, "activeProfile", "test");
21+
}
22+
23+
@Test
24+
void accessTokenRoundTrip() {
25+
String token = jwtUtils.generateAccessToken("user@example.com");
26+
assertTrue(jwtUtils.validateAccessToken(token));
27+
assertEquals("user@example.com", jwtUtils.getEmailFromToken(token));
28+
}
29+
30+
@Test
31+
void refreshTokenRoundTrip() {
32+
String refreshToken = jwtUtils.generateRefreshToken("user@example.com");
33+
assertTrue(jwtUtils.validateRefreshToken(refreshToken));
34+
assertEquals("user@example.com", jwtUtils.getEmailFromRefreshToken(refreshToken));
35+
}
36+
37+
@Test
38+
void rejectsInvalidToken() {
39+
assertFalse(jwtUtils.validateAccessToken("not-a-token"));
40+
assertFalse(jwtUtils.validateRefreshToken("not-a-token"));
41+
}
42+
43+
@Test
44+
void generateTokenDelegatesToAccessToken() {
45+
String token = jwtUtils.generateToken("mail@example.com");
46+
assertTrue(jwtUtils.validateToken(token));
47+
assertEquals("mail@example.com", jwtUtils.getEmailFromToken(token));
48+
}
49+
}

0 commit comments

Comments
 (0)