Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 5 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Java CI for Picke

on:
push:
branches: [ "develop", "main" ]
branches: [ "dev", "main" ]
pull_request:
branches: [ "develop", "main" ]
branches: [ "dev", "main" ]

jobs:
build:
Expand All @@ -21,6 +21,9 @@ jobs:
distribution: 'temurin'
cache: gradle # 1. 빌드 속도를 획기적으로 줄이기 위해 그래들 캐시 적용

- name: Install ffmpeg
run: sudo apt-get update && sudo apt-get install -y ffmpeg

- name: Grant execute permission for gradlew
run: chmod +x gradlew

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import com.swyp.picke.domain.notification.repository.NotificationScheduleRepository;
import com.swyp.picke.domain.notification.service.NotificationDispatchService;
import com.swyp.picke.domain.notification.service.NotificationService;
import java.time.Clock;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -24,17 +24,16 @@
@RequiredArgsConstructor
public class NotificationScheduleDispatcher {

private static final ZoneId SEOUL_ZONE = ZoneId.of("Asia/Seoul");

private final NotificationScheduleRepository notificationScheduleRepository;
private final NotificationService notificationService;
private final NotificationDispatchService notificationDispatchService;
private final Clock clock;

@Scheduled(cron = "0 * * * * *", zone = "Asia/Seoul")
@Transactional
public void dispatchDueSchedules() {
LocalTime now = LocalTime.now(SEOUL_ZONE);
LocalDate today = LocalDate.now(SEOUL_ZONE);
LocalTime now = LocalTime.now(clock);
LocalDate today = LocalDate.now(clock);

List<NotificationSchedule> dueSchedules = notificationScheduleRepository.findAllByEnabledTrue().stream()
.filter(schedule -> schedule.isDue(now, today))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.swyp.picke.global.config;

import java.time.Clock;
import java.time.ZoneId;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
Expand All @@ -20,4 +22,9 @@ public ThreadPoolTaskScheduler taskScheduler() {
scheduler.setThreadNamePrefix("credit-scheduler-");
return scheduler;
}

@Bean
public Clock clock() {
return Clock.system(ZoneId.of("Asia/Seoul"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ void createBattle_persistsAllMappedFields() throws Exception {
LocalDate targetDate = LocalDate.now();

Tag category = createTag("battle-category", TagType.CATEGORY);
Tag philosopher = createTag("battle-philosopher", TagType.PHILOSOPHER);
Tag philosopher = createTag("소크라테스", TagType.PHILOSOPHER);
Tag value = createTag("battle-value", TagType.VALUE);

Map<String, Object> payload = Map.of(
Expand Down Expand Up @@ -395,6 +395,9 @@ private User createAdminUser() {
}

private Tag createTag(String prefix, TagType type) {
if (type == TagType.PHILOSOPHER) {
return tagRepository.save(Tag.builder().name(prefix).type(type).build());
}
String normalizedPrefix = prefix.length() > 10 ? prefix.substring(0, 10) : prefix;
return tagRepository.save(
Tag.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
Expand Down Expand Up @@ -98,21 +96,14 @@ void admin_can_create_and_list_notices() throws Exception {
}

@Test
@DisplayName("admin notice page form flow persists notice and user can fetch it")
void admin_notice_page_form_flow_persists_notice_and_user_can_fetch_it() throws Exception {
@DisplayName("admin notice creation flow persists notice and user can fetch it")
void admin_notice_creation_flow_persists_notice_and_user_can_fetch_it() throws Exception {
String adminToken = createAdminToken();
String userToken = createUserToken();
String marker = UUID.randomUUID().toString().substring(0, 8);
String title = "ui-notice-" + marker;
String body = "ui-body-" + marker;

mockMvc.perform(get("/api/v1/admin/picke/notice"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("id=\"notice-form\"")))
.andExpect(content().string(containsString("id=\"notice-title\"")))
.andExpect(content().string(containsString("id=\"notice-body\"")))
.andExpect(content().string(containsString("/js/admin/notice/notice.js")));

Map<String, Object> payload = Map.of(
"category", "NOTICE",
"title", title,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ void propose_Success() {
// then
// 제안 저장 메서드가 호출되었는지 확인
verify(battleProposalRepository, times(1)).save(any());
// 크레딧 차감(-30) 로직이 호출되었는지 확인
verify(creditService, times(1)).addCredit(eq(1L), eq(CreditType.TOPIC_SUGGEST), eq(-30), any());
// 크레딧 차감(-100) 로직이 호출되었는지 확인
verify(creditService, times(1)).addCredit(eq(1L), eq(CreditType.TOPIC_SUGGEST), eq(-100), any());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,22 @@
import com.swyp.picke.domain.notification.repository.NotificationScheduleRepository;
import com.swyp.picke.domain.notification.service.NotificationDispatchService;
import com.swyp.picke.domain.notification.service.NotificationService;
import java.time.Clock;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
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;

@ExtendWith(MockitoExtension.class)
class NotificationScheduleDispatcherTest {

private static final ZoneId SEOUL_ZONE = ZoneId.of("Asia/Seoul");

@Mock
private NotificationScheduleRepository notificationScheduleRepository;

Expand All @@ -33,13 +37,21 @@ class NotificationScheduleDispatcherTest {
@Mock
private NotificationDispatchService notificationDispatchService;

@InjectMocks
private NotificationScheduleDispatcher notificationScheduleDispatcher;

@BeforeEach
void setUp() {
Clock fixedClock = Clock.fixed(
LocalTime.of(9, 0).atDate(java.time.LocalDate.now(SEOUL_ZONE)).atZone(SEOUL_ZONE).toInstant(),
SEOUL_ZONE);
notificationScheduleDispatcher = new NotificationScheduleDispatcher(
notificationScheduleRepository, notificationService, notificationDispatchService, fixedClock);
}

@Test
@DisplayName("현재 시각과 일치하는 활성 예약만 발송하고 발송일을 기록한다")
void dispatchDueSchedules_sendsOnlyMatchingEnabledSchedules() {
LocalTime now = LocalTime.now();
LocalTime now = LocalTime.of(9, 0);
NotificationSchedule due = NotificationSchedule.builder()
.title("오늘의 질문")
.subtitle("지금 확인해보세요")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ class CreditServiceTest {
@Mock
private UserService userService;

@Mock
private com.swyp.picke.domain.notification.service.NotificationService notificationService;

@Mock
private com.swyp.picke.domain.battle.repository.BattleRepository battleRepository;

@InjectMocks
private CreditService creditService;

Expand Down
16 changes: 16 additions & 0 deletions src/test/resources/application-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,29 @@ spring:
credentials:
location: file:/tmp/dummy.json

firebase:
credentials:
location: src/test/resources/firebase-test-credentials.json

admob:
app-id: dummy
reward:
unit-id:
ios: dummy
android: dummy

oauth:
kakao:
client-id: dummy
client-secret: dummy
google:
client-id: dummy
client-secret: dummy
apple:
team-id: dummy
client-id: dummy
key-id: dummy
private-key: dummy

openai:
api-key: dummy
Expand Down
13 changes: 13 additions & 0 deletions src/test/resources/firebase-test-credentials.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"type": "service_account",
"project_id": "picke-test-project",
"private_key_id": "dummykeyid1234567890",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDC8cIyqMc1Aey1\n9QVQ4QmSiWpiOPy4pFN09XmprAuZZDWbFaKD724NhbXgdtcEHNewYAWWpN3FsyBh\nh9YZPHXHa0dfdSsEKaKDxrIv/7YX8M/kRbDMSH35yDZZTIoxZnJLNZm+Zs2sB7L0\nYtzbT1ctnHV2r+zlVC29S2OV7/LY5YRbMM7fhvwu+ww+Ri6GY3Nx5dhvi/yzgeT8\njLw4hGx9uSMBZnNmpxp+hGL/wo2LSD4FQ0PrAqJTkLu90tky51XFmUGVmGqnWpRE\nuLw4C83x1xIyNayU5pR9oDTdLh+CGkVb8QOuJS95tLjqo3hx4CiQICXA+Ul8282R\nqIyrvFFbAgMBAAECggEAFLGvSMtr4i+jHim1d8F7z6dwuJ6ODVe8WEUati1CSfU+\nT4k7aEAJcbwI85wJ9TDOoLWAwl4cALmkLVZLHwCxDAtSV0rL1zRIQS7diYTeoqn4\nl6XiP71OSi67vj0GynmyllNJT9H/8Uwb7h90jH9epMPgIEpKnomSFW8kUi1XnTiI\nPCJJBzodYbSLFLpApum22E76Zjj4CDbKUnRZVYF/0GxesXhGCVlpgVi7XsuWLKvd\n7ScH9d2VxEkrvlj71Mwt2kV7791oCWIiPD/iy1i6WE3/H1YJj6LnGWPZUppvvXsH\nPfJbZNVyXMFrGJP9Ql+bAT1C0zo1z6Mudp7V8Ai3iQKBgQD/InZT5sGnLJasSEH1\njfhiGX4yyQ7Uw5Eikx6I2QQGzwxNBAu5wII8O+TryNQPu2lsBzIcvyL5LNHyNfPn\nQwTk7DWiWxZi18GBJen7IoVVDGL+bubrKSAGroNYVpsYJvJDZxV2wpXHig3wUXmP\n4Gz/FDKFHh7GAFvD0Jk+HQfpvQKBgQDDmwg6OeWnlujRGm8IGOWcu7pOEKLTxlQ9\ndB0kxRGWlslsWZtcFj/YmjExr0w3OZaHLrb+vBmFGDZqK19AUt6Nm7RGPUq6P+XG\nT1bK+Vsi3NSLYRbdLaT3s42n/8Hb+ZPx4HyQTGq/nfgciJCdvtzncDeDsgNIpxyk\n+SDNSu+89wKBgBZmdjEjn3kIByqVJYVjs50ZU+UtlenESefZNuMY+quGXjQc2NK0\nPjr/nze8aDIBaF4du56egXmTH9O+PO3fCnz26Daa/Los60Zlh8eO3ln7Pm3MWuXm\ntHMhu1J0OCXEtZyJXm8Q4omka1jgLmYddDRpF45seJM10Ni+ZdX4QouZAoGAGgCZ\n72OS69xbxrBE4kas/1DVS1taydwrhp/Q3/pyhBo3XHfs9yjeA+U7dOdgslatc/r5\nyJMosVCuqx5o4xwhCaIRLOUo8elcmigh2YmcW94PQxf8+hn/PA5aXmLZWmyrBhRZ\nerUt25scSG6/Crk8lGeOeatIVHgijquveJrlk7ECgYEAxQeYN0uzLBneootOeqLs\nGQ7ubWqJpgm0IeZST9rUA2OBjk4AwHBEC3tyayH4XEbTG259dIUYCe6xp3pjbpyH\nl/d7pFsau+D9yC72yeN+g2bZUKt7yUfmLIUDODXNF+WH9a+xFFDgL7r/zHNI0NDc\niXuwmsuc6msGwp7959s8nD0=\n-----END PRIVATE KEY-----\n",
"client_email": "test-fcm@picke-test-project.iam.gserviceaccount.com",
"client_id": "000000000000000000000",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/test-fcm%40picke-test-project.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
Loading