Skip to content

Commit 49d661c

Browse files
committed
Add integration tests for Post and User controllers, and configure test database
- Created PostControllerTest2 with tests for adding, updating, retrieving, and deleting posts. - Created UserControllerTest2 with tests for user management, including adding users, checking availability, and updating user information. - Added application-test.yml for H2 in-memory database configuration for testing.
1 parent dee01bc commit 49d661c

22 files changed

+2563
-20
lines changed

pom.xml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@
3939
<scope>runtime</scope>
4040
</dependency>
4141

42+
<!-- H2 Database for testing -->
43+
<dependency>
44+
<groupId>com.h2database</groupId>
45+
<artifactId>h2</artifactId>
46+
<scope>test</scope>
47+
</dependency>
48+
4249
<dependency>
4350
<groupId>org.projectlombok</groupId>
4451
<artifactId>lombok</artifactId>
@@ -155,7 +162,13 @@
155162
<artifactId>commons-lang3</artifactId>
156163
</dependency>
157164

158-
</dependencies>
165+
166+
<dependency>
167+
<groupId>org.springframework.security</groupId>
168+
<artifactId>spring-security-test</artifactId>
169+
<version>4.0.4.RELEASE</version>
170+
</dependency>
171+
</dependencies>
159172

160173
<build>
161174
<plugins>
@@ -182,7 +195,7 @@
182195
<goals>
183196
<goal>prepare-agent</goal>
184197
</goals>
185-
</execution>
198+
</execution>
186199
<execution>
187200
<id>report</id>
188201
<phase>test</phase>

src/main/java/com/sopromadze/blogapi/config/AuditingConfig.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,16 @@ public Optional<Long> getCurrentAuditor() {
3131
return Optional.empty();
3232
}
3333

34-
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
34+
Object principal = authentication.getPrincipal();
35+
36+
// Handle both UserPrincipal and standard Spring Security User objects
37+
if (principal instanceof UserPrincipal) {
38+
return Optional.ofNullable(((UserPrincipal) principal).getId());
39+
} else if (principal instanceof org.springframework.security.core.userdetails.User) {
40+
// For @WithMockUser, return null to skip auditing
41+
return Optional.empty();
42+
}
3543

36-
return Optional.ofNullable(userPrincipal.getId());
44+
return Optional.empty();
3745
}
3846
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.sopromadze.blogapi.config;
2+
3+
import com.sopromadze.blogapi.model.user.User;
4+
import com.sopromadze.blogapi.repository.UserRepository;
5+
import com.sopromadze.blogapi.security.CurrentUser;
6+
import com.sopromadze.blogapi.security.UserPrincipal;
7+
import org.springframework.core.MethodParameter;
8+
import org.springframework.security.core.Authentication;
9+
import org.springframework.security.core.context.SecurityContextHolder;
10+
import org.springframework.security.core.userdetails.UserDetails;
11+
import org.springframework.stereotype.Component;
12+
import org.springframework.web.bind.support.WebDataBinderFactory;
13+
import org.springframework.web.context.request.NativeWebRequest;
14+
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
15+
import org.springframework.web.method.support.ModelAndViewContainer;
16+
17+
import java.util.Optional;
18+
19+
/**
20+
* Custom argument resolver for @CurrentUser annotation.
21+
* This resolver extracts the current authenticated user from the security context
22+
* and converts it to a UserPrincipal object for controller methods.
23+
*/
24+
@Component
25+
public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {
26+
27+
private final UserRepository userRepository;
28+
29+
public CurrentUserArgumentResolver(UserRepository userRepository) {
30+
this.userRepository = userRepository;
31+
}
32+
33+
/**
34+
* Determines if this resolver supports the given method parameter.
35+
* Returns true if the parameter is annotated with @CurrentUser.
36+
*/
37+
@Override
38+
public boolean supportsParameter(MethodParameter parameter) {
39+
return parameter.hasParameterAnnotation(CurrentUser.class);
40+
}
41+
42+
/**
43+
* Resolves the method parameter by extracting the current user from the security context.
44+
* If the user is found in the database, it creates a UserPrincipal from the User entity.
45+
* If not found, it returns null which will cause a NullPointerException in the controller.
46+
*/
47+
@Override
48+
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
49+
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
50+
51+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
52+
53+
if (authentication == null || !authentication.isAuthenticated()) {
54+
return null;
55+
}
56+
57+
Object principal = authentication.getPrincipal();
58+
59+
if (principal instanceof UserDetails) {
60+
String username = ((UserDetails) principal).getUsername();
61+
62+
// Find the user in the database
63+
Optional<User> userOptional = userRepository.findByUsername(username);
64+
65+
if (userOptional.isPresent()) {
66+
User user = userOptional.get();
67+
return UserPrincipal.create(user);
68+
}
69+
}
70+
71+
return null;
72+
}
73+
}

src/main/java/com/sopromadze/blogapi/config/WebMvcConfig.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
11
package com.sopromadze.blogapi.config;
22

3+
import com.sopromadze.blogapi.config.CurrentUserArgumentResolver;
34
import org.springframework.beans.factory.annotation.Value;
45
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
57
import org.springframework.web.servlet.config.annotation.CorsRegistry;
68
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
79

10+
import java.util.List;
11+
812
@Configuration
913
public class WebMvcConfig implements WebMvcConfigurer {
1014

1115
@Value("cors.allowedOrings")
1216
private String allowedOrigins;
1317

18+
private final CurrentUserArgumentResolver currentUserArgumentResolver;
19+
20+
public WebMvcConfig(CurrentUserArgumentResolver currentUserArgumentResolver) {
21+
this.currentUserArgumentResolver = currentUserArgumentResolver;
22+
}
23+
24+
@Override
1425
public void addCorsMappings(CorsRegistry registry) {
1526
final long MAX_AGE_SECS = 3600;
1627

@@ -20,4 +31,10 @@ public void addCorsMappings(CorsRegistry registry) {
2031
.allowedHeaders("*")
2132
.maxAge(MAX_AGE_SECS);
2233
}
34+
35+
@Override
36+
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
37+
// Add custom argument resolvers here
38+
resolvers.add(currentUserArgumentResolver);
39+
}
2340
}

src/main/java/com/sopromadze/blogapi/exception/ResourceNotFoundException.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public ResourceNotFoundException(String resourceName, String fieldName, Object f
1919
this.resourceName = resourceName;
2020
this.fieldName = fieldName;
2121
this.fieldValue = fieldValue;
22+
this.setApiResponse();
2223
}
2324

2425
public String getResourceName() {

src/main/java/com/sopromadze/blogapi/model/user/User.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ public User(String firstName, String lastName, String username, String email, St
116116
this.username = username;
117117
this.email = email;
118118
this.password = password;
119+
this.roles = new ArrayList<>();
119120
}
120121

121122

@@ -164,17 +165,11 @@ public void setPosts(List<Post> posts) {
164165
}
165166

166167
public List<Role> getRoles() {
167-
168-
return roles == null ? null : new ArrayList<>(roles);
168+
return roles;
169169
}
170170

171171
public void setRoles(List<Role> roles) {
172-
173-
if (roles == null) {
174-
this.roles = null;
175-
} else {
176-
this.roles = Collections.unmodifiableList(roles);
177-
}
172+
this.roles = roles;
178173
}
179174

180175
public List<Comment> getComments() {

src/main/java/com/sopromadze/blogapi/repository/AlbumRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88

99
@Repository
1010
public interface AlbumRepository extends JpaRepository<Album, Long> {
11-
Page<Album> findByCreatedBy(Long userId, Pageable pageable);
11+
Page<Album> findByUser_Id(Long userId, Pageable pageable);
1212
}

src/main/java/com/sopromadze/blogapi/repository/PostRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
public interface PostRepository extends JpaRepository<Post, Long> {
1414
Page<Post> findByCreatedBy(Long userId, Pageable pageable);
1515

16-
Page<Post> findByCategory(Long categoryId, Pageable pageable);
16+
Page<Post> findByCategoryId(Long categoryId, Pageable pageable);
1717

1818
Page<Post> findByTags(List<Tag> tags, Pageable pageable);
1919

src/main/java/com/sopromadze/blogapi/service/impl/AlbumServiceImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public PagedResponse<Album> getUserAlbums(String username, int page, int size) {
124124

125125
Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, CREATED_AT);
126126

127-
Page<Album> albums = albumRepository.findByCreatedBy(user.getId(), pageable);
127+
Page<Album> albums = albumRepository.findByUser_Id(user.getId(), pageable);
128128

129129
List<Album> content = albums.getNumberOfElements() > 0 ? albums.getContent() : Collections.emptyList();
130130

src/main/java/com/sopromadze/blogapi/service/impl/PhotoServiceImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public PhotoResponse updatePhoto(Long id, PhotoRequest photoRequest, UserPrincip
7979
if (photo.getAlbum().getUser().getId().equals(currentUser.getId())
8080
|| currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))) {
8181
photo.setTitle(photoRequest.getTitle());
82+
photo.setUrl(photoRequest.getUrl());
8283
photo.setThumbnailUrl(photoRequest.getThumbnailUrl());
8384
photo.setAlbum(album);
8485
Photo updatedPhoto = photoRepository.save(photo);

0 commit comments

Comments
 (0)