This commit is contained in:
@@ -17,7 +17,6 @@ jobs:
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
cache: gradle
|
||||
|
||||
- name: Make Gradle wrapper executable
|
||||
run: chmod +x ./gradlew
|
||||
|
||||
@@ -42,6 +42,15 @@ dependencies {
|
||||
implementation("org.projectlombok:lombok:1.18.42")
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.42'
|
||||
implementation 'com.google.firebase:firebase-admin:9.9.0'
|
||||
|
||||
|
||||
|
||||
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testImplementation("org.testcontainers:postgresql:1.21.3")
|
||||
testImplementation("org.testcontainers:junit-jupiter:1.21.4")
|
||||
testImplementation("org.mockito:mockito-inline:5.2.0")
|
||||
testImplementation 'org.mockito:mockito-core'
|
||||
}
|
||||
|
||||
generateJava {
|
||||
@@ -52,4 +61,8 @@ generateJava {
|
||||
|
||||
tasks.named('test') {
|
||||
useJUnitPlatform()
|
||||
|
||||
jvmArgs += [
|
||||
"-javaagent:${configurations.testRuntimeClasspath.find { it.name.contains('byte-buddy-agent') }}"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package dev.ksan.etfoglasiserver.config;
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import com.google.firebase.FirebaseApp;
|
||||
import com.google.firebase.FirebaseOptions;
|
||||
import com.google.firebase.messaging.FirebaseMessaging;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
@@ -28,4 +29,9 @@ public class FirebaseConfig {
|
||||
}
|
||||
return FirebaseApp.getInstance();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FirebaseMessaging firebaseMessaging(FirebaseApp firebaseApp) {
|
||||
return FirebaseMessaging.getInstance(firebaseApp);
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ public class JwtFilter extends OncePerRequestFilter {
|
||||
String token = null;
|
||||
String username = null;
|
||||
|
||||
|
||||
if (authHeader != null && authHeader.startsWith("Bearer ")) {
|
||||
token = authHeader.substring(7);
|
||||
username = jwtService.extractEmail(token);
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
@@ -61,4 +62,10 @@ public class SecurityConfig {
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+6
-18
@@ -1,12 +1,8 @@
|
||||
package dev.ksan.etfoglasiserver.controller;
|
||||
|
||||
import dev.ksan.etfoglasiserver.model.DeviceToken;
|
||||
import dev.ksan.etfoglasiserver.model.RegisterTokenRequest;
|
||||
import dev.ksan.etfoglasiserver.model.User;
|
||||
import dev.ksan.etfoglasiserver.repository.DeviceTokenRepo;
|
||||
import dev.ksan.etfoglasiserver.repository.UserRepo;
|
||||
import dev.ksan.etfoglasiserver.service.DeviceTokenService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
@@ -18,24 +14,16 @@ import org.springframework.web.bind.annotation.*;
|
||||
@RequiredArgsConstructor
|
||||
public class DeviceTokenController {
|
||||
|
||||
private final DeviceTokenRepo deviceTokenRepo;
|
||||
private final UserRepo userRepo;
|
||||
private final DeviceTokenService deviceTokenService;
|
||||
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity<?> register(
|
||||
public ResponseEntity<Void> register(
|
||||
@RequestBody RegisterTokenRequest request,
|
||||
@AuthenticationPrincipal UserDetails principal) {
|
||||
|
||||
User user = userRepo.findByEmail(principal.getUsername())
|
||||
.orElseThrow(() -> new RuntimeException("User not found"));
|
||||
|
||||
DeviceToken deviceToken = deviceTokenRepo
|
||||
.findByFcmToken(request.getToken())
|
||||
.orElse(new DeviceToken());
|
||||
|
||||
deviceToken.setUser(user);
|
||||
deviceToken.setFcmToken(request.getToken());
|
||||
deviceTokenRepo.save(deviceToken);
|
||||
deviceTokenService.registerToken(
|
||||
principal.getUsername(),
|
||||
request.getToken());
|
||||
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class UserController {
|
||||
@Autowired UserService service;
|
||||
|
||||
|
||||
@GetMapping("users/me")
|
||||
@GetMapping("/users/me")
|
||||
public ResponseEntity<UserDTO> getUserById(Authentication authentication) {
|
||||
String email = authentication.getName();
|
||||
User user = service.getAccountByEmail(email)
|
||||
@@ -46,7 +46,7 @@ public class UserController {
|
||||
|
||||
|
||||
|
||||
@PostMapping("users/me/notification-type")
|
||||
@PostMapping("/users/me/notification-type")
|
||||
public ResponseEntity<?> updateNotificationType(
|
||||
@RequestBody Map<String, String> request,
|
||||
Authentication authentication) {
|
||||
@@ -82,19 +82,32 @@ public class UserController {
|
||||
return ResponseEntity.ok(updatedUser);
|
||||
}
|
||||
|
||||
// not the best, should fix this later
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<JwtResponseDTO> login(@RequestBody UserLoginDTO user) {
|
||||
JwtResponseDTO userVer = service.verify(user);
|
||||
try {
|
||||
JwtResponseDTO userVer = service.verify(user);
|
||||
|
||||
if (userVer != null) return new ResponseEntity<>(userVer, HttpStatus.OK);
|
||||
}catch (Exception e) {
|
||||
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (userVer != null) return new ResponseEntity<>(userVer, HttpStatus.OK);
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
// not the best, should fix this later too
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity<UserDTO> register(@RequestBody UserCreationDTO user) {
|
||||
|
||||
UserDTO newUser = service.register(user);
|
||||
if (newUser != null) return new ResponseEntity<>(newUser, HttpStatus.CREATED);
|
||||
try {
|
||||
UserDTO newUser = service.register(user);
|
||||
if (newUser != null) return new ResponseEntity<>(newUser, HttpStatus.CREATED);
|
||||
}catch (Exception e) {
|
||||
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@ public class UserCreationDTO {
|
||||
private Set<Subject> subjectSet;
|
||||
private String notification;
|
||||
|
||||
public UserCreationDTO(String mail, String password) {
|
||||
this.email = mail;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package dev.ksan.etfoglasiserver.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class UserLoginDTO {
|
||||
|
||||
private String email;
|
||||
|
||||
@@ -1,4 +1,31 @@
|
||||
package dev.ksan.etfoglasiserver.service;
|
||||
|
||||
import dev.ksan.etfoglasiserver.model.DeviceToken;
|
||||
import dev.ksan.etfoglasiserver.model.User;
|
||||
import dev.ksan.etfoglasiserver.repository.DeviceTokenRepo;
|
||||
import dev.ksan.etfoglasiserver.repository.UserRepo;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DeviceTokenService {
|
||||
}
|
||||
|
||||
private final DeviceTokenRepo deviceTokenRepo;
|
||||
private final UserRepo userRepo;
|
||||
|
||||
public void registerToken(String email, String token) {
|
||||
|
||||
User user = userRepo.findByEmail(email)
|
||||
.orElseThrow(() -> new RuntimeException("User not found"));
|
||||
|
||||
DeviceToken deviceToken = deviceTokenRepo
|
||||
.findByFcmToken(token)
|
||||
.orElse(new DeviceToken());
|
||||
|
||||
deviceToken.setUser(user);
|
||||
deviceToken.setFcmToken(token);
|
||||
|
||||
deviceTokenRepo.save(deviceToken);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
package dev.ksan.etfoglasiserver.service;
|
||||
|
||||
import com.google.firebase.messaging.FirebaseMessaging;
|
||||
import com.google.firebase.messaging.FirebaseMessagingException;
|
||||
import com.google.firebase.messaging.Message;
|
||||
import com.google.firebase.messaging.Notification;
|
||||
import com.google.firebase.messaging.*;
|
||||
import dev.ksan.etfoglasiserver.model.DeviceToken;
|
||||
import dev.ksan.etfoglasiserver.model.User;
|
||||
import dev.ksan.etfoglasiserver.repository.DeviceTokenRepo;
|
||||
@@ -16,45 +13,32 @@ import java.util.List;
|
||||
@RequiredArgsConstructor
|
||||
public class NotificationService {
|
||||
|
||||
private final DeviceTokenRepo deviceTokenRepository;
|
||||
private final DeviceTokenRepo deviceTokenRepo;
|
||||
private final FirebaseMessaging firebaseMessaging;
|
||||
|
||||
|
||||
public void sendToUser(User user, String title, String body) {
|
||||
public void sendToUser(User user, String title, String body)
|
||||
throws FirebaseMessagingException {
|
||||
|
||||
List<DeviceToken> tokens =
|
||||
deviceTokenRepository.findByUserId(user.getId());
|
||||
deviceTokenRepo.findByUserId(user.getId());
|
||||
|
||||
for (DeviceToken token : tokens) {
|
||||
|
||||
Message message = Message.builder()
|
||||
.setToken(token.getFcmToken())
|
||||
.putData("title", title)
|
||||
.putData("body", body)
|
||||
.build();
|
||||
|
||||
try {
|
||||
Message message = Message.builder()
|
||||
.setToken(token.getFcmToken())
|
||||
.setNotification(
|
||||
Notification.builder()
|
||||
.setTitle(title)
|
||||
.setBody(body)
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
firebaseMessaging.send(message);
|
||||
} catch (FirebaseMessagingException ex) {
|
||||
|
||||
FirebaseMessaging.getInstance().send(message);
|
||||
|
||||
} catch (FirebaseMessagingException e) {
|
||||
|
||||
String errorCode = e.getMessagingErrorCode() != null
|
||||
? e.getMessagingErrorCode().name()
|
||||
: "";
|
||||
|
||||
System.out.println("Failed token: " + token.getFcmToken());
|
||||
|
||||
if (errorCode.equals("UNREGISTERED")
|
||||
|| errorCode.equals("INVALID_ARGUMENT")) {
|
||||
|
||||
deviceTokenRepository.delete(token);
|
||||
System.out.println("Deleted dead token");
|
||||
if (ex.getMessagingErrorCode()
|
||||
== MessagingErrorCode.UNREGISTERED) {
|
||||
deviceTokenRepo.delete(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.ksan.etfoglasiserver.service;
|
||||
|
||||
import com.google.firebase.messaging.FirebaseMessagingException;
|
||||
import dev.ksan.etfoglasiserver.model.Entry;
|
||||
import dev.ksan.etfoglasiserver.model.NotificationMethod;
|
||||
import dev.ksan.etfoglasiserver.model.Subject;
|
||||
@@ -83,7 +84,12 @@ public class SubjectService {
|
||||
if (user.getNotificationMethod() != NotificationMethod.PUSH_NOTIFICATION)
|
||||
continue;
|
||||
|
||||
notificationService.sendToUser(user, entry.getTitle(), entry.getParagraph());
|
||||
try{
|
||||
|
||||
notificationService.sendToUser(user, entry.getTitle(), entry.getParagraph());
|
||||
}catch (FirebaseMessagingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
@@ -147,28 +148,27 @@ public class UserService {
|
||||
throw new RuntimeException("Invalid email");
|
||||
}
|
||||
|
||||
public JwtResponseDTO verify(UserLoginDTO user) {
|
||||
public JwtResponseDTO verify(UserLoginDTO user) {
|
||||
|
||||
Authentication authentication = authManager.authenticate(
|
||||
new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword())
|
||||
);
|
||||
authManager.authenticate(
|
||||
new UsernamePasswordAuthenticationToken(
|
||||
user.getEmail(),
|
||||
user.getPassword()
|
||||
)
|
||||
);
|
||||
|
||||
if (!authentication.isAuthenticated()) {
|
||||
throw new BadCredentialsException("Invalid username or password");
|
||||
}
|
||||
User foundUser = userRepo.findByEmail(user.getEmail())
|
||||
.orElseThrow(() -> new RuntimeException("User not found"));
|
||||
|
||||
String token = jwtService.generateToken(user.getEmail());
|
||||
String token = jwtService.generateToken(user.getEmail());
|
||||
|
||||
User foundUser = userRepo.findByEmail(user.getEmail())
|
||||
.orElseThrow(() -> new RuntimeException("User not found after authentication"));
|
||||
|
||||
return new JwtResponseDTO(
|
||||
token,
|
||||
foundUser.getEmail(),
|
||||
foundUser.getId().toString(),
|
||||
HttpStatus.OK.toString()
|
||||
);
|
||||
}
|
||||
return new JwtResponseDTO(
|
||||
token,
|
||||
foundUser.getEmail(),
|
||||
foundUser.getId().toString(),
|
||||
HttpStatus.OK.toString()
|
||||
);
|
||||
}
|
||||
|
||||
public Optional<User> getAccountByEmail(String email) {
|
||||
return userRepo.findByEmail(email);
|
||||
|
||||
+42
-2
@@ -1,4 +1,44 @@
|
||||
package dev.ksan.etfoglasiserver.controller;
|
||||
|
||||
public class DeviceTokenControllerTest {
|
||||
}
|
||||
import dev.ksan.etfoglasiserver.model.RegisterTokenRequest;
|
||||
import dev.ksan.etfoglasiserver.service.DeviceTokenService;
|
||||
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 org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DeviceTokenControllerTest {
|
||||
|
||||
@Mock
|
||||
DeviceTokenService deviceTokenService;
|
||||
|
||||
@InjectMocks
|
||||
DeviceTokenController controller;
|
||||
|
||||
@Test
|
||||
void register_savesNewToken() {
|
||||
|
||||
UserDetails principal = mock(UserDetails.class);
|
||||
when(principal.getUsername()).thenReturn("test@test.com");
|
||||
|
||||
RegisterTokenRequest req = new RegisterTokenRequest();
|
||||
req.setToken("fcm-123");
|
||||
|
||||
ResponseEntity<Void> res = controller.register(req, principal);
|
||||
|
||||
assertEquals(204, res.getStatusCode().value());
|
||||
|
||||
verify(deviceTokenService).registerToken(
|
||||
"test@test.com",
|
||||
"fcm-123"
|
||||
);
|
||||
}
|
||||
}
|
||||
+225
-2
@@ -1,4 +1,227 @@
|
||||
package dev.ksan.etfoglasiserver.controller;
|
||||
|
||||
public class UserControllerIntegrationTest {
|
||||
}
|
||||
import dev.ksan.etfoglasiserver.dto.JwtResponseDTO;
|
||||
import dev.ksan.etfoglasiserver.dto.UserCreationDTO;
|
||||
import dev.ksan.etfoglasiserver.dto.UserDTO;
|
||||
import dev.ksan.etfoglasiserver.dto.UserLoginDTO;
|
||||
import dev.ksan.etfoglasiserver.model.NotificationMethod;
|
||||
import dev.ksan.etfoglasiserver.model.User;
|
||||
import dev.ksan.etfoglasiserver.repository.UserRepo;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@Import(UserController.class)
|
||||
class UserControllerIntegrationTest {
|
||||
|
||||
@Autowired TestRestTemplate restTemplate;
|
||||
@Autowired UserRepo userRepo;
|
||||
@Autowired
|
||||
PasswordEncoder passwordEncoder;
|
||||
|
||||
private String token;
|
||||
private User testUser;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
userRepo.deleteAll();
|
||||
|
||||
testUser = new User();
|
||||
testUser.setEmail("test@test.com");
|
||||
testUser.setPassword(passwordEncoder.encode("password"));
|
||||
testUser.setNotificationMethod(NotificationMethod.PUSH_NOTIFICATION);
|
||||
userRepo.save(testUser);
|
||||
|
||||
ResponseEntity<JwtResponseDTO> res = restTemplate.postForEntity(
|
||||
"/api/login",
|
||||
new UserLoginDTO("test@test.com", "password"),
|
||||
JwtResponseDTO.class
|
||||
);
|
||||
token = res.getBody().getToken();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void login_returnsToken_withValidCredentials() {
|
||||
ResponseEntity<JwtResponseDTO> res = restTemplate.postForEntity(
|
||||
"/api/login",
|
||||
new UserLoginDTO("test@test.com", "password"),
|
||||
JwtResponseDTO.class
|
||||
);
|
||||
|
||||
assertEquals(200, res.getStatusCode().value());
|
||||
assertNotNull(res.getBody().getToken());
|
||||
}
|
||||
|
||||
@Test
|
||||
void login_returns400_withWrongPassword() {
|
||||
ResponseEntity<?> res = restTemplate.postForEntity(
|
||||
"/api/login",
|
||||
new UserLoginDTO("test@test.com", "wrongpassword"),
|
||||
Object.class
|
||||
);
|
||||
|
||||
assertEquals(400, res.getStatusCode().value());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void register_createsUser_andReturns201() {
|
||||
ResponseEntity<UserDTO> res = restTemplate.postForEntity(
|
||||
"/api/register",
|
||||
new UserCreationDTO("new@test.com", "password123"),
|
||||
UserDTO.class
|
||||
);
|
||||
|
||||
assertEquals(201, res.getStatusCode().value());
|
||||
assertNotNull(res.getBody());
|
||||
assertEquals("new@test.com", res.getBody().getEmail());
|
||||
}
|
||||
|
||||
@Test
|
||||
void register_returns400_withInvalidData() {
|
||||
ResponseEntity<?> res = restTemplate.postForEntity(
|
||||
"/api/register",
|
||||
new UserCreationDTO("", ""),
|
||||
Object.class
|
||||
);
|
||||
|
||||
assertEquals(400, res.getStatusCode().value());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void getMe_returnsUser_withValidToken() {
|
||||
ResponseEntity<UserDTO> res = restTemplate.exchange(
|
||||
"/api/users/me",
|
||||
HttpMethod.GET,
|
||||
withAuth(null),
|
||||
UserDTO.class
|
||||
);
|
||||
|
||||
assertEquals(200, res.getStatusCode().value());
|
||||
assertEquals("test@test.com", res.getBody().getEmail());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMe_returns401_withoutToken() {
|
||||
ResponseEntity<?> res = restTemplate.getForEntity("/api/users/me", Object.class);
|
||||
assertEquals(401, res.getStatusCode().value());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void updateUser_updatesEmail() {
|
||||
UserCreationDTO update = new UserCreationDTO("updated@test.com", null);
|
||||
|
||||
ResponseEntity<UserDTO> res = restTemplate.exchange(
|
||||
"/api/users/me",
|
||||
HttpMethod.PUT,
|
||||
withAuth(update),
|
||||
UserDTO.class
|
||||
);
|
||||
|
||||
assertEquals(200, res.getStatusCode().value());
|
||||
assertEquals("updated@test.com", res.getBody().getEmail());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateUser_returns401_withoutToken() {
|
||||
ResponseEntity<?> res = restTemplate.exchange(
|
||||
"/api/users/me",
|
||||
HttpMethod.PUT,
|
||||
new HttpEntity<>(new UserCreationDTO("x@x.com", null)),
|
||||
Object.class
|
||||
);
|
||||
|
||||
assertEquals(401, res.getStatusCode().value());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void updateNotificationType_setsNoNotification() {
|
||||
ResponseEntity<?> res = restTemplate.exchange(
|
||||
"/api/users/me/notification-type",
|
||||
HttpMethod.POST,
|
||||
withAuth(Map.of("notificationType", "NO_NOTIFICATION")),
|
||||
Object.class
|
||||
);
|
||||
|
||||
assertEquals(200, res.getStatusCode().value());
|
||||
|
||||
User updated = userRepo.findByEmail("test@test.com").get();
|
||||
assertEquals(NotificationMethod.NO_NOTIFICATION, updated.getNotificationMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateNotificationType_setsPushNotification() {
|
||||
// set to push first
|
||||
testUser.setNotificationMethod(NotificationMethod.PUSH_NOTIFICATION);
|
||||
userRepo.save(testUser);
|
||||
|
||||
restTemplate.exchange(
|
||||
"/api/users/me/notification-type",
|
||||
HttpMethod.POST,
|
||||
withAuth(Map.of("notificationType", "PUSH_NOTIFICATION")),
|
||||
Object.class
|
||||
);
|
||||
|
||||
User updated = userRepo.findByEmail("test@test.com").get();
|
||||
assertEquals(NotificationMethod.PUSH_NOTIFICATION, updated.getNotificationMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateNotificationType_returns401_withoutToken() {
|
||||
ResponseEntity<?> res = restTemplate.postForEntity(
|
||||
"/api/users/me/notification-type",
|
||||
Map.of("notificationType", "NO_NOTIFICATION"),
|
||||
Object.class
|
||||
);
|
||||
|
||||
assertEquals(401, res.getStatusCode().value());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void deleteUser_returns204_andRemovesUser() {
|
||||
ResponseEntity<Void> res = restTemplate.exchange(
|
||||
"/api/users/me",
|
||||
HttpMethod.DELETE,
|
||||
withAuth(null),
|
||||
Void.class
|
||||
);
|
||||
|
||||
assertEquals(204, res.getStatusCode().value());
|
||||
assertTrue(userRepo.findByEmail("test@test.com").isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteUser_returns401_withoutToken() {
|
||||
ResponseEntity<?> res = restTemplate.exchange(
|
||||
"/api/users/me",
|
||||
HttpMethod.DELETE,
|
||||
HttpEntity.EMPTY,
|
||||
Object.class
|
||||
);
|
||||
|
||||
assertEquals(401, res.getStatusCode().value());
|
||||
}
|
||||
|
||||
|
||||
private <T> HttpEntity<T> withAuth(T body) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(token);
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
return new HttpEntity<>(body, headers);
|
||||
}
|
||||
}
|
||||
+71
-2
@@ -1,4 +1,73 @@
|
||||
package dev.ksan.etfoglasiserver.service;
|
||||
|
||||
public class NotificationServiceTest {
|
||||
}
|
||||
import com.google.firebase.messaging.FirebaseMessaging;
|
||||
import com.google.firebase.messaging.FirebaseMessagingException;
|
||||
import com.google.firebase.messaging.Message;
|
||||
import com.google.firebase.messaging.MessagingErrorCode;
|
||||
import dev.ksan.etfoglasiserver.model.DeviceToken;
|
||||
import dev.ksan.etfoglasiserver.model.User;
|
||||
import dev.ksan.etfoglasiserver.repository.DeviceTokenRepo;
|
||||
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.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class NotificationServiceTest {
|
||||
|
||||
@Mock
|
||||
DeviceTokenRepo deviceTokenRepo;
|
||||
@Mock
|
||||
FirebaseMessaging firebaseMessaging;
|
||||
@InjectMocks
|
||||
NotificationService notificationService;
|
||||
|
||||
@Test
|
||||
void sendToUser_sendsMessageForEachToken() throws FirebaseMessagingException {
|
||||
User user = new User();
|
||||
DeviceToken t1 = new DeviceToken(); t1.setFcmToken("token-1");
|
||||
DeviceToken t2 = new DeviceToken(); t2.setFcmToken("token-2");
|
||||
|
||||
when(deviceTokenRepo.findByUserId(user.getId()))
|
||||
.thenReturn(List.of(t1, t2));
|
||||
when(firebaseMessaging.send(any())).thenReturn("msg-id");
|
||||
|
||||
notificationService.sendToUser(user, "Hello", "World");
|
||||
|
||||
verify(firebaseMessaging, times(2)).send(any(Message.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sendToUser_deletesStaleToken_whenUnregistered() throws FirebaseMessagingException {
|
||||
User user = new User();
|
||||
DeviceToken token = new DeviceToken();
|
||||
token.setFcmToken("stale-token");
|
||||
|
||||
when(deviceTokenRepo.findByUserId(user.getId()))
|
||||
.thenReturn(List.of(token));
|
||||
|
||||
FirebaseMessagingException ex = mock(FirebaseMessagingException.class);
|
||||
when(ex.getMessagingErrorCode()).thenReturn(MessagingErrorCode.UNREGISTERED);
|
||||
when(firebaseMessaging.send(any())).thenThrow(ex);
|
||||
|
||||
notificationService.sendToUser(user, "Hello", "World");
|
||||
|
||||
verify(deviceTokenRepo).delete(token);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sendToUser_doesNothing_whenUserHasNoTokens() throws FirebaseMessagingException {
|
||||
User user = new User();
|
||||
when(deviceTokenRepo.findByUserId(user.getId())).thenReturn(List.of());
|
||||
|
||||
notificationService.sendToUser(user, "Hello", "World");
|
||||
|
||||
verify(firebaseMessaging, never()).send(any());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user