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

[test]: 테스트 작성 #174

Merged
merged 30 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
182dd1b
refactor: FCM 로직 수정
kanguk01 Nov 13, 2024
7274b64
refactor: GPT 로직 더 보완
kanguk01 Nov 14, 2024
ac87762
deploy: 일단 배포
kanguk01 Nov 14, 2024
fb27d23
refactor: plan 에서 의도치 않게 필요한 부분이 리턴되지 않는 부분 수정
kanguk01 Nov 14, 2024
a6ec343
refactor: GPT 로직 보완
kanguk01 Nov 14, 2024
4e97993
refactor: FCM 토큰 수정 API 분리
kanguk01 Nov 14, 2024
b3fb589
refactor: FCM 토큰 클릭 시 로직 추가를 위한 수정
kanguk01 Nov 14, 2024
0e89e97
bug: 팀 서비스 api 수정(delete 문제)
kimsongmok Nov 14, 2024
c3af011
refactor: 디버깅을 위해 추가했던 부분 삭제
kanguk01 Nov 14, 2024
a800384
[merge]: FCM 토큰 로직 수정 및 GPT api 보완 (#169)
kanguk01 Nov 14, 2024
91373ee
[merge]: 팀 삭제 버그 수정 (#168)
kanguk01 Nov 14, 2024
088f0cf
feat: 코드 커버리지 측정 CI에 추가
kanguk01 Nov 14, 2024
ef7e41d
feat: JaCoCo 설정 수정
kanguk01 Nov 14, 2024
96ce1df
feat: 다른 형식으로 나오도록 시도해봄
kanguk01 Nov 14, 2024
70ce818
feat: 커버리지 측정 필터링하고
kanguk01 Nov 14, 2024
3390d85
feat: 커버리지 결과가 PR에 남도록 한다.
kanguk01 Nov 14, 2024
de81f2f
[merge]: 코드 커버리지 측정 자동화 (#171)
kanguk01 Nov 15, 2024
5d84ddf
refactor: cd 수정
kimsongmok Nov 10, 2024
44cb710
test: 친구플랜조회_후_댓글작성 테스트 작성
ez23re Nov 14, 2024
a894b65
test: 팀플랜_생성후_수정후_삭제 테스트 작성
ez23re Nov 14, 2024
7cf04f3
test: 팀플랜_생성후_수정후_삭제 테스트 작성
ez23re Nov 14, 2024
932be0b
test: 팀생성후_유저초대_유저가_수락후_내보내기 테스트 작성
ez23re Nov 14, 2024
294f985
refactor: 클래스 리네이밍
ez23re Nov 14, 2024
2905a4e
test: 내가_친구_요청_보내면_친구가_수락하기 테스트 작성
ez23re Nov 14, 2024
445f134
chore: splanet-test.log 왜 자꾸 뜨지
ez23re Nov 14, 2024
2a00660
chore: splanet-test.log 계속 뜸
ez23re Nov 14, 2024
14f6a28
chore: 브랜치 옮겨서 rm --cached를 했는데됴..!!
ez23re Nov 14, 2024
52df8fe
test: 플랜카드_생성후_조회수_수정후_삭제 테스트 작성
ez23re Nov 14, 2024
b5912ed
refactor: 메소드명 typo
ez23re Nov 14, 2024
9d5c9ab
test: 구독_생성후_조회 테스트 작성
ez23re Nov 14, 2024
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
47 changes: 35 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ on:
- develop
- 'weekly/**'

permissions:
contents: write
issues: write
pull-requests: write

jobs:
build-and-test:
runs-on: ubuntu-22.04
Expand Down Expand Up @@ -36,17 +41,6 @@ jobs:
- name: Checkout Repository
uses: actions/checkout@v2

# - name: Cache Gradle dependencies
# uses: actions/cache@v3
# with:
# path: |
# ~/.gradle/caches/modules-2/files-2.1
# ~/.gradle/wrapper
# key: gradle-${{ runner.os }}-${{ hashFiles('build.gradle', 'settings.gradle') }}
# restore-keys: |
# gradle-${{ runner.os }}-


- name: Decode env.properties from GitHub Secrets
run: |
echo "${{ secrets.ENV_FILE }}" | base64 --decode > ./src/main/resources/env.properties
Expand Down Expand Up @@ -87,4 +81,33 @@ jobs:
run: ./gradlew clean build -Dspring.profiles.active=test

- name: Run Tests
run: ./gradlew test -Dspring.profiles.active=test
run: ./gradlew test -Dspring.profiles.active=test

- name: Generate JaCoCo Coverage Report
run: ./gradlew jacocoTestReport

- name: Generate Coverage Badge and Summary
uses: cicirello/jacoco-badge-generator@v2
with:
jacoco-csv-file: 'build/reports/jacoco/test/jacocoTestReport.csv'
badges-directory: 'badges'
generate-branches-badge: true
generate-summary: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Display Coverage Summary JSON
run: |
cat badges/coverage-summary.json

- name: Extract Coverage Information
if: ${{ github.event_name == 'pull_request' }}
run: |
COVERAGE=$(jq '.coverage' badges/coverage-summary.json)
BRANCH_COVERAGE=$(jq '.branches' badges/coverage-summary.json)
FORMATTED_COVERAGE=$(printf "%.2f" $COVERAGE)
FORMATTED_BRANCH_COVERAGE=$(printf "%.2f" $BRANCH_COVERAGE)
curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-X POST \
-d "{\"body\": \"### 코드 커버리지\n- Coverage: ${FORMATTED_COVERAGE}%\n- Branches: ${FORMATTED_BRANCH_COVERAGE}%\n\"}" \
"https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ splanet-db
### env ###
.env
/src/main/resources/env.properties
/src/main/resources/env.properties.base64
splanet-firebase.json
31 changes: 30 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ plugins {
id 'org.springframework.boot' version '3.3.3'
id 'io.spring.dependency-management' version '1.1.6'
id 'com.google.protobuf' version '0.9.4'

id 'jacoco'
}


group = 'com.splanet'
version = '0.0.1-SNAPSHOT'

Expand All @@ -18,6 +19,7 @@ java {
test {
useJUnitPlatform()
systemProperty 'spring.profiles.active', 'test'
finalizedBy jacocoTestReport
}

configurations {
Expand Down Expand Up @@ -93,6 +95,7 @@ dependencies {

tasks.named('test') {
useJUnitPlatform()
finalizedBy jacocoTestReport
}

protobuf {
Expand All @@ -119,4 +122,30 @@ sourceSets {
srcDirs 'build/generated/source/proto/main/java', 'build/generated/source/proto/main/grpc'
}
}
}

jacoco {
toolVersion = "0.8.10"
}

jacocoTestReport {
dependsOn test
reports {
xml.required.set(true)
html.required.set(true)
csv.required.set(true)
}
classDirectories.setFrom(
files(classDirectories.files.collect {
fileTree(dir: it, exclude: [
'**/dto/**', // dto 패키지 제외
'**/config/**', // config 패키지 제외
'**/exception/**', // exception 패키지 제외
'**/core/**', // core 패키지 제외
'**/mapper/**', // mapper 패키지 제외
'**/grpc/**', // grpc 패키지 제외
'**/log/**', // log 패키지 제외
])
})
)
}
503 changes: 503 additions & 0 deletions splanet-test.log

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public enum ErrorCode {
// redis
REDIS_SCAN_FAILED("Redis 키 스캔 중 오류가 발생했습니다.", HttpStatus.SERVICE_UNAVAILABLE),

// fcm
TOKEN_NOT_FOUND("해당 FCM 토큰을 찾을 수 없습니다.", HttpStatus.NOT_FOUND),

// ect
INVALID_PLAN_FORMAT("", HttpStatus.BAD_REQUEST);


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@

import java.util.List;


@Repository
public interface FriendRepository extends JpaRepository<Friend, Long> {
List<Friend> findByUserId(Long userId);

boolean existsByUserIdAndFriendId(Long userId, Long friendId);

@Modifying
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@ public ResponseEntity<List<PlanResponseDto>> getFriendPlan(Long friendId, Long u
return ResponseEntity.ok(planResponseDtos);
}

// 친구 삭제(취소)하기

// 친구 삭제(취소)하기
@Transactional
public ResponseEntity<Map<String, String>> unfriend(Long friendId, Long userId) {
Expand Down
12 changes: 7 additions & 5 deletions src/main/java/com/splanet/splanet/gpt/service/GptService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.splanet.splanet.core.properties.GptProperties;
import com.splanet.splanet.plan.dto.PlanResponseDto;
import com.splanet.splanet.plan.dto.PlanTimeDto;
import com.splanet.splanet.plan.service.PlanService;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.http.ResponseEntity;
Expand All @@ -25,9 +26,9 @@ public class GptService {

private static final double RESPONSE_TEMPERATURE = 0.8;
private static final Map<Integer, String> PROMPT_TEMPLATES = Map.of(
3, "사용자 입력: \"%s\" (deviceId: %s) (groupId: %s) 기존 일정이 있다면 해당 시간과는 겹치지 않게 해 줘 기존일정:%s 현재 시간 이후로 가능한 자주 반복하여 짧고 집중적으로 일정을 완수할 수 있도록 계획을 세워줘. 시험이 포함된 경우, 시험 당일이 아닌 전날까지 준비가 완료되도록 해줘 (%s 기준). 모든 일정은 한국 시간(UTC+9)을 기준으로 설정해줘.",
2, "사용자 입력: \"%s\" (deviceId: %s) (groupId: %s) 기존 일정이 있다면 해당 시간과는 겹치지 않게 해 줘 기존일정:%s 현재 시간 이후로 적당한 간격을 두고 모든 일정을 완수할 수 있도록 계획해줘. 시험이 포함된 경우, 시험 당일이 아닌 전날까지 준비가 완료되도록 해줘 (%s 기준). 모든 일정은 한국 시간(UTC+9)을 기준으로 설정해줘.",
1, "사용자 입력: \"%s\" (deviceId: %s) (groupId: %s) 기존 일정이 있다면 해당 시간과는 겹치지 않게 해 줘 기존일정:%s 현재 시간 이후로 여유 있게 모든 일정을 완수할 수 있도록 계획해줘. 시험이 포함된 경우, 시험 당일이 아닌 전날까지 준비가 완료되도록 해줘 (%s 기준). 모든 일정은 한국 시간(UTC+9)을 기준으로 설정해줘."
3, "사용자 입력: \"%s\" (deviceId: %s) (groupId: %s) 제공된 가이드라인을 따르지 않으면 페널티가 부과될 것입니다. 모든 지침을 주의깊게 읽고 그에 따라 행동하세요. 기존 startDate와 endDate사이에 일정을 생성하지 말아줘. 기존 startDate, endDate:%s 현재 시간 이후로 가능한 자주 반복하여 짧고 집중적으로 일정을 완수할 수 있도록 계획을 세워줘. 현재 시간 : (%s 기준). 모든 일정은 한국 시간(UTC+9)을 기준으로 설정해줘. 또한, 기존 일정을 생각하고 새로운 일정을 적당한 간격을 두고 배치해줘.",
2, "사용자 입력: \"%s\" (deviceId: %s) (groupId: %s) 제공된 가이드라인을 따르지 않으면 페널티가 부과될 것입니다. 모든 지침을 주의깊게 읽고 그에 따라 행동하세요. 기존 startDate와 endDate사이에 일정을 생성하지 말아줘. 기존 startDate, endDate:%s 현재 시간 이후로 적당한 간격을 두고 모든 일정을 완수할 수 있도록 계획해줘. 현재 시간 : (%s 기준). 모든 일정은 한국 시간(UTC+9)을 기준으로 설정해줘. 또한, 기존 일정을 생각하고 새로운 일정을 적당한 간격을 두고 배치해줘.",
1, "사용자 입력: \"%s\" (deviceId: %s) (groupId: %s) 제공된 가이드라인을 따르지 않으면 페널티가 부과될 것입니다. 모든 지침을 주의깊게 읽고 그에 따라 행동하세요. 기존 startDate와 endDate사이에 일정을 생성하지 말아줘. 기존 startDate, endDate:%s 현재 시간 이후로 여유 있게 모든 일정을 완수할 수 있도록 계획해줘. 현재 시간 : (%s 기준). 모든 일정은 한국 시간(UTC+9)을 기준으로 설정해줘. 또한, 기존 일정을 생각하고 새로운 일정을 적당한 간격을 두고 배치해줘."
);

public GptService(OpenAiApi openAiApi, GptProperties gptProperties, PlanService planService, ObjectMapper objectMapper) {
Expand All @@ -39,7 +40,7 @@ public GptService(OpenAiApi openAiApi, GptProperties gptProperties, PlanService

public String generateResponse(String userInput, Long userId, String deviceId, int groupId) {
String currentTime = getCurrentTime();
List<PlanResponseDto> futurePlans = (userId != null) ? planService.getAllFuturePlansByUserId(userId) : List.of();
List<PlanTimeDto> futurePlans = (userId != null) ? planService.getAllFuturePlanTimesByUserId(userId) : List.of();
String planJson = convertPlansToJson(futurePlans);
String promptTemplate = PROMPT_TEMPLATES.get(groupId);
String fullPrompt = String.format(promptTemplate, userInput, deviceId, groupId, planJson, currentTime);
Expand All @@ -54,7 +55,7 @@ private String getCurrentTime() {
return LocalDateTime.now(ZoneId.of("Asia/Seoul")).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}

private String convertPlansToJson(List<PlanResponseDto> futurePlans) {
private String convertPlansToJson(List<PlanTimeDto> futurePlans) {
try {
return objectMapper.writeValueAsString(futurePlans);
} catch (JsonProcessingException e) {
Expand Down Expand Up @@ -85,3 +86,4 @@ private String getGptResponse(OpenAiApi.ChatCompletionRequest chatRequest) {
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,25 @@
public interface FcmTokenApi {

@PostMapping("/register")
@Operation(summary = "FCM 토큰 등록", description = "유저가 FCM 토큰을 등록합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "FCM 토큰이 성공적으로 등록되었습니다."),
@ApiResponse(responseCode = "404", description = "유저를 찾을 수 없습니다.", content = @Content)
})
ResponseEntity<String> registerFcmToken(
@AuthenticationPrincipal Long userId,
@RequestBody FcmTokenRequest fcmTokenRequest
);

@PutMapping("/update")
@Operation(summary = "FCM 토큰 설정 수정", description = "알림 설정 및 알림 오프셋을 수정합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "FCM 토큰 설정이 성공적으로 수정되었습니다."),
@ApiResponse(responseCode = "404", description = "유저를 찾을 수 없습니다.", content = @Content)
})
ResponseEntity<String> updateFcmTokenSettings(
@AuthenticationPrincipal Long userId,
@RequestBody FcmTokenUpdateRequest fcmTokenUpdateRequest
@PutMapping("/update/notification-enabled")
ResponseEntity<String> updateNotificationEnabled(
@RequestParam String token,
@RequestParam Boolean isNotificationEnabled
);

@PutMapping("/update/notification-offset")
ResponseEntity<String> updateNotificationOffset(
@RequestParam String token,
@RequestParam Integer notificationOffset
);

@DeleteMapping("/delete")
@Operation(summary = "FCM 토큰 삭제", description = "유저의 FCM 토큰을 삭제합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "FCM 토큰이 성공적으로 삭제되었습니다."),
@ApiResponse(responseCode = "404", description = "해당 토큰을 찾을 수 없습니다.", content = @Content)
})
ResponseEntity<String> deleteFcmToken(
@AuthenticationPrincipal Long userId,
@RequestParam String token
);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.splanet.splanet.notification.controller;

import com.splanet.splanet.notification.dto.FcmTokenRequest;
import com.splanet.splanet.notification.dto.FcmTokenUpdateRequest;
import com.splanet.splanet.notification.service.FcmTokenService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
Expand All @@ -21,14 +20,20 @@ public ResponseEntity<String> registerFcmToken(Long userId, FcmTokenRequest fcmT
}

@Override
public ResponseEntity<String> updateFcmTokenSettings(Long userId, FcmTokenUpdateRequest fcmTokenUpdateRequest) {
fcmTokenService.updateFcmTokenSettings(userId, fcmTokenUpdateRequest);
return ResponseEntity.ok("FCM token 수정 완료");
public ResponseEntity<String> updateNotificationEnabled(String token, Boolean isNotificationEnabled) {
fcmTokenService.updateNotificationEnabled(token, isNotificationEnabled);
return ResponseEntity.ok("FCM 알림 여부 수정 완료");
}

@Override
public ResponseEntity<String> deleteFcmToken(Long userId, String token) {
fcmTokenService.deleteFcmToken(userId, token);
public ResponseEntity<String> updateNotificationOffset(String token, Integer notificationOffset) {
fcmTokenService.updateNotificationOffset(token, notificationOffset);
return ResponseEntity.ok("FCM 알림 오프셋 수정 완료");
}

@Override
public ResponseEntity<String> deleteFcmToken(String token) {
fcmTokenService.deleteFcmToken(token);
return ResponseEntity.ok("FCM token 삭제 완료");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ public interface FcmTokenRepository extends JpaRepository<FcmToken, Long> {
Optional<FcmToken> findByUserIdAndToken(Long userId, String token);
List<FcmToken> findByUserId(Long userId);
List<FcmToken> findByUserIdIn(Collection<Long> userIds);
Optional<FcmToken> findByToken(String token);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// src/main/java/com/splanet/splanet/notification/service/FcmTokenService.java
package com.splanet.splanet.notification.service;

import com.splanet.splanet.core.exception.BusinessException;
Expand Down Expand Up @@ -34,26 +33,33 @@ public void registerFcmToken(Long userId, String token) {
}

@Transactional
public void updateFcmTokenSettings(Long userId, FcmTokenUpdateRequest request) {
FcmToken fcmToken = fcmTokenRepository.findByUserIdAndToken(userId, request.token())
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
public void updateNotificationEnabled(String token, Boolean isNotificationEnabled) {
FcmToken fcmToken = fcmTokenRepository.findByToken(token)
.orElseThrow(() -> new BusinessException(ErrorCode.TOKEN_NOT_FOUND));

fcmToken = fcmToken.toBuilder()
.isNotificationEnabled(request.isNotificationEnabled() != null
? request.isNotificationEnabled()
: fcmToken.getIsNotificationEnabled())
.notificationOffset(request.notificationOffset() != null
? request.notificationOffset()
: fcmToken.getNotificationOffset())
.isNotificationEnabled(isNotificationEnabled != null ? isNotificationEnabled : fcmToken.getIsNotificationEnabled())
.build();

fcmTokenRepository.save(fcmToken);
}

@Transactional
public void deleteFcmToken(Long userId, String token) {
FcmToken fcmToken = fcmTokenRepository.findByUserIdAndToken(userId, token)
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
public void updateNotificationOffset(String token, Integer notificationOffset) {
FcmToken fcmToken = fcmTokenRepository.findByToken(token)
.orElseThrow(() -> new BusinessException(ErrorCode.TOKEN_NOT_FOUND));

fcmToken = fcmToken.toBuilder()
.notificationOffset(notificationOffset != null ? notificationOffset : fcmToken.getNotificationOffset())
.build();

fcmTokenRepository.save(fcmToken);
}

@Transactional
public void deleteFcmToken(String token) {
FcmToken fcmToken = fcmTokenRepository.findByToken(token)
.orElseThrow(() -> new BusinessException(ErrorCode.TOKEN_NOT_FOUND));
fcmTokenRepository.delete(fcmToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,16 @@ public void sendNotification(FcmToken fcmToken, Plan plan) {

Notification notification = new Notification(title, body);

Message message = Message.builder().setToken(fcmToken.getToken()).setNotification(notification).putData("title", plan.getTitle()).putData("title", plan.getDescription()).putData("startDate", plan.getStartDate().toString()).build();
String clickActionUrl = "https://www.splanet.co.kr";

Message message = Message.builder().setToken(fcmToken.getToken())
.setNotification(notification)
.putData("click_action", clickActionUrl)
.putData("title", plan.getTitle())
.putData("description", plan.getDescription())
.putData("startDate", plan.getStartDate().toString())
.build();

try {
String response = firebaseMessaging.send(message);
log.info("알림을 정상적으로 전송하였습니다. : {}", response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ public class PlanResponseDto {
private String description;
private LocalDateTime startDate;
private LocalDateTime endDate;

@JsonIgnore
private Boolean accessibility;
@JsonIgnore
private Boolean isCompleted;

@JsonIgnore
private LocalDateTime createdAt;
@JsonIgnore
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/com/splanet/splanet/plan/dto/PlanTimeDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.splanet.splanet.plan.dto;

import java.time.LocalDateTime;

public class PlanTimeDto {
private LocalDateTime startDate;
private LocalDateTime endDate;

public PlanTimeDto(LocalDateTime startDate, LocalDateTime endDate) {
this.startDate = startDate;
this.endDate = endDate;
}

public LocalDateTime getStartDate() {
return startDate;
}

public LocalDateTime getEndDate() {
return endDate;
}
}
Loading
Loading