Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

테스트 코드 추가 및 리팩토링 #28

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
94bf3e4
[WIP] FollowServiceTest 추가
Feb 7, 2025
cfb488e
FollowController Validate 어노테이션 추가
Feb 7, 2025
0732814
상수->변수로 이동
Feb 10, 2025
47a9a0f
FollowGroup Transactional 적용
Feb 10, 2025
c4ca026
[AUTH] token 변조 시 Exception 처리 추가
Feb 10, 2025
3f4a463
사용하지 않는 변수 정리
Feb 10, 2025
4551112
[UTIL] ParseUtil 최적화
Feb 10, 2025
5173c13
FollowPair 삭제 및 FollowDto 로 통합
Feb 10, 2025
27fa715
메인 폴더와 동일 구조로 테스트 dto 폴더 이동
Feb 11, 2025
bc3b0cb
[TEST] followServiceTest 작성
Feb 11, 2025
bf263b7
불필요한 ResponseEntity 삭제
Feb 11, 2025
f80aec2
[BUG] GetFollower 팔로워 불러오지 않는 버그 수정
Feb 11, 2025
b824978
잘못된 query 주석 수정
Feb 11, 2025
fba540b
[TEST] LoginDto test 작성
Feb 11, 2025
f0a478d
[TEST] SignupRequestDto 테스트 작성
Feb 11, 2025
2208aed
[Test] UserServiceTest 작성
Feb 11, 2025
a434685
[TEST] FollowService Test 코드 추가
Feb 11, 2025
21df289
[TEST] FollowGroupService 테스트 추가
Feb 11, 2025
9a18155
[DOMAIN] @Valid 적용
Feb 11, 2025
1320403
Valid 에러 발생 시 클라이언트로 파싱하여 반환하도록 추가
Feb 11, 2025
c5da0fd
[TEST] 도메인 누락된 테스트 추가
Feb 11, 2025
a38e1ce
[domain] 팔로워/팔로잉 불러오기 기능 및 테스트 코드 추가
Feb 11, 2025
2de0fb9
테스트코드 보완 적용
Feb 24, 2025
048223b
불필요한 입력값 없도록 오버로딩 함수 추가
Feb 24, 2025
1deb6a7
Update README.md
gru3530 Feb 24, 2025
cd409bf
회원가입 시 글자수 제한 추가
Feb 25, 2025
54b1608
Merge branch 'main' into feature/enterTestCode
gru3530 Feb 25, 2025
1eeb1c4
Github Action 적용
Feb 25, 2025
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
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.mysql:mysql-connector-j'
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
implementation 'org.springframework.boot:spring-boot-starter-validation'

compileOnly 'org.projectlombok:lombok'

Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/flab/stargram/config/KeyPairConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@

@Configuration
public class KeyPairConfig {
private static final String KEY_ALGORITHM = "RSA";
private static final int RSA_KEY_SIZE = 2048;

@Bean
public KeyPair keyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGenerator.initialize(RSA_KEY_SIZE);
return keyPairGenerator.generateKeyPair();
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package com.flab.stargram.config.exception;

import java.util.Map;

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.flab.stargram.entity.common.ApiResponseEnum;
import com.flab.stargram.entity.common.ApiResult;

import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

@RestControllerAdvice
public class GlobalApiExceptionHandler {
Expand All @@ -19,4 +24,21 @@ public ResponseEntity<ApiResult> handleBaseApiException(BaseApiException e) {
public ResponseEntity<ApiResult> handleGeneralException(Exception e) {
return ApiResult.error(ApiResponseEnum.FAILURE, e.getMessage());
}

@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<ApiResult> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
Throwable cause = ex.getCause();

if (cause instanceof InvalidFormatException invalidFormatException) {
String fieldName = invalidFormatException.getPath().get(0).getFieldName();
String invalidValue = invalidFormatException.getValue().toString();
return ApiResult.error(
ApiResponseEnum.INVALID_INPUT,
ApiResponseEnum.INVALID_INPUT.getMessage(),
Map.of(fieldName, invalidValue)
);
}

return ApiResult.error(ApiResponseEnum.INVALID_INPUT, ApiResponseEnum.INVALID_INPUT.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.flab.stargram.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -14,26 +15,26 @@
import com.flab.stargram.entity.dto.FollowRequestDto;
import com.flab.stargram.service.FollowService;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/users/{inputUserId}")
@RequiredArgsConstructor
@Validated
public class FollowController {
private final FollowService followService;

@PostMapping("/follow")
public ResponseEntity<ApiResult> followUser(@RequestBody FollowRequestDto dto, @PathVariable String inputUserId) {
dto.validateEmpty().ifInvalidThrow();
public ResponseEntity<ApiResult> followUser(@Valid @RequestBody FollowRequestDto dto, @PathVariable String inputUserId) {
FollowPair followPair = FollowPair.createFollowPairOf(inputUserId, dto);

followService.followUser(followPair);
return ApiResult.success(null);
}

@PostMapping("/unfollow")
public ResponseEntity<ApiResult> unfollowUser(@RequestBody FollowRequestDto dto, @PathVariable String inputUserId) {
dto.validateEmpty().ifInvalidThrow();
public ResponseEntity<ApiResult> unfollowUser(@Valid @RequestBody FollowRequestDto dto, @PathVariable String inputUserId) {
FollowPair followPair = FollowPair.createFollowPairOf(inputUserId, dto);

followService.unfollowUser(followPair);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class UserController {
private final AuthCookieService authCookieService;

@PostMapping("/signup")
public ResponseEntity<ApiResult> signUp(@RequestBody SignUpRequestDto dto, HttpServletResponse response) {
public ResponseEntity<ApiResult> signUp(@RequestBody SignUpRequestDto dto) {
dto.validateEmpty().ifInvalidThrow();

return ApiResult.success(userService.signUp(dto));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.SignatureException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
Expand All @@ -28,7 +30,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons

try {
request.setAttribute("userId", authService.validateToken(token).getSubject());
} catch (ExpiredJwtException e) {
} catch (SignatureException | ExpiredJwtException e) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/flab/stargram/entity/common/ApiResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@ public static ResponseEntity<ApiResult> error(ApiResponseEnum apiResponseEnum, S
ApiResult result = new ApiResult(apiResponseEnum, message, null);
return ResponseEntity.status(apiResponseEnum.getHttpStatus()).body(result);
}

public static ResponseEntity<ApiResult> error(ApiResponseEnum apiResponseEnum, String message, Object detail) {
ApiResult result = new ApiResult(apiResponseEnum, message, detail);
return ResponseEntity.status(apiResponseEnum.getHttpStatus()).body(result);
}
}
8 changes: 6 additions & 2 deletions src/main/java/com/flab/stargram/entity/common/ParseUtil.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package com.flab.stargram.entity.common;

import java.util.regex.Pattern;

import com.flab.stargram.config.exception.InvalidInputException;

public class ParseUtil {
private static final String parseLongRegex = "^[0-9]+$";
private static final Pattern parseLongPattern = Pattern.compile("^[0-9]+$");

public static Long parseToLong(String value) {
if (value.matches(parseLongRegex)) {
assert value != null : "Input value is null";

if (parseLongPattern.matcher(value).matches()) {
return Long.parseLong(value);
} else {
throw new InvalidInputException(ApiResponseEnum.INVALID_INPUT);
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/flab/stargram/entity/dto/FollowPair.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import com.flab.stargram.entity.common.ParseUtil;

import lombok.Builder;
import lombok.Getter;

@Builder
@Getter
public class FollowPair {
private Long followerId;
Expand Down
20 changes: 6 additions & 14 deletions src/main/java/com/flab/stargram/entity/dto/FollowRequestDto.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
package com.flab.stargram.entity.dto;

import com.flab.stargram.entity.common.ApiResponseEnum;
import com.flab.stargram.entity.common.BaseDto;
import com.flab.stargram.entity.common.ValidationResult;

import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Positive;
import lombok.Getter;

@Getter
public class FollowRequestDto extends BaseDto {
public class FollowRequestDto {
@NotNull(message = "팔로우할 사용자 ID는 필수 입니다.")
@Positive(message = "사용자 ID는 양수로 입력해야 합니다.")
private Long followingId;

@Override
public ValidationResult validateEmpty() {
if (isFieldEmpty(followingId)) {
validationResult.addError(ApiResponseEnum.EMPTY_FOLLOWING_ID);
}

return validationResult;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

public interface FollowGroupRepository extends JpaRepository<FollowGroup, Long> {
//SELECT * FROM follow_group WHERE user_id = ?
FollowGroup findByUserId(Long userId);
//SELECT EXISTS (SELECT 1 FROM follow_group WHERE user_id= ?);
boolean existsByUserId(Long userId);
FollowGroup findByFollowingId(Long followingId);
//SELECT EXISTS (SELECT 1 FROM follow_group WHERE following_id= ?);
boolean existsByFollowingId(Long followingId);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.flab.stargram.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.flab.stargram.entity.model.FollowGroup;
import com.flab.stargram.repository.FollowGroupRepository;
Expand All @@ -13,11 +14,12 @@ public class FollowGroupService {
private final FollowGroupRepository followGroupRepository;

public boolean hasFollow(Long userId) {
return followGroupRepository.existsByUserId(userId);
return followGroupRepository.existsByFollowingId(userId);
}

@Transactional
public FollowGroup getOrCreateFollowGroup(long followingId) {
FollowGroup followGroup = followGroupRepository.findByUserId(followingId);
FollowGroup followGroup = followGroupRepository.findByFollowingId(followingId);
if (followGroup == null) {
followGroup = createFollowGroup(followingId);
}
Expand Down
25 changes: 25 additions & 0 deletions src/test/java/com/flab/stargram/entity/common/ParseUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import com.flab.stargram.config.exception.InvalidInputException;
Expand All @@ -15,6 +16,7 @@ void parseLong() {
assertEquals(expectLong, ParseUtil.parseToLong(input));
}

@DisplayName("문자열이 들어왔을경우 에러 반환 테스트")
@Test
void parseString() {
String input = "abc";
Expand All @@ -25,6 +27,19 @@ void parseString() {

}

@DisplayName("숫자와 L이 함께 들어왔을경우 에러 반환 테스트")
@Test
void parseLongString() {
String input = "365L";

assertThrows(InvalidInputException.class, () -> {
ParseUtil.parseToLong(input);
});

}


@DisplayName("문자열과 숫자가 섞여 들어왔을경우 에러 반환 테스트")
@Test
void parseStringWithLong() {
String input = "a2b1c4";
Expand All @@ -33,4 +48,14 @@ void parseStringWithLong() {
ParseUtil.parseToLong(input);
});
}

@DisplayName("빈 문자열이 들어왔을경우 에러 반환 테스트")
@Test
void parseEmptyString() {
String input = "";

assertThrows(InvalidInputException.class, () -> {
ParseUtil.parseToLong(input);
});
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2025-02-12 at 11 03 26 PM

}
61 changes: 61 additions & 0 deletions src/test/java/com/flab/stargram/service/FollowServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.flab.stargram.service;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import com.flab.stargram.entity.dto.FollowPair;
import com.flab.stargram.entity.model.Follow;
import com.flab.stargram.entity.model.FollowGroup;
import com.flab.stargram.repository.FollowGroupRepository;
import com.flab.stargram.repository.FollowRepository;

class FollowServiceTest {
@InjectMocks
private FollowService followService;

@Mock
private UserService userService;
@Mock
private FollowGroupService followGroupService;
@Mock
private FollowRepository followRepository;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}

@Test
void followUser() {
//given
FollowPair followPair = FollowPair.builder()
.followingId(1L)
.followerId(2L)
.build();

//when
when(userService.hasUserId(followPair.getFollowerId())).thenReturn(true);
when(userService.hasUserId(followPair.getFollowingId())).thenReturn(true);
when(followGroupService.getOrCreateFollowGroup(followPair.getFollowingId())).thenReturn(mock(FollowGroup.class));
when(followRepository.existsByFollowerIdAndFollowingId(followPair.getFollowerId(), followPair.getFollowingId())).thenReturn(false);

followService.followUser(followPair);

//then
verify(followRepository, times(1)).save(any(Follow.class));
}

@Test
void unfollowUser() {
}

@Test
void getFollowers() {
}
}