diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..590e812 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,37 @@ +name: ci pipeline + +on: + pull_request: + branches: + - dev + - main + paths-ignore: + - 'README.md' + +jobs: + ci: + runs-on: ubuntu-latest + + steps: + - name: checkout code + uses: actions/checkout@v4 + + - name: set up jdk 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + + - name: set up gradle + uses: gradle/actions/setup-gradle@v3 + + - name: grant permission to gradlew + run: chmod +x gradlew + + - name: test for dev + if: github.event.pull_request.base.ref == 'dev' + run: ./gradlew test + + - name: test for main + if: github.event.pull_request.base.ref == 'main' + run: ./gradlew clean build diff --git a/.gitignore b/.gitignore index a304872..972315f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ HELP.md .gradle build/ -!gradle/wrapper/gradle-wrapper.jar + !**/src/main/**/build/ !**/src/test/**/build/ @@ -182,7 +182,9 @@ replay_pid* .DS_Store -src/main/resources/application-dev.properties +src/main/resources/application-prod.properties loki/data/ +!**/gradle/wrapper/gradle-wrapper.jar +!**/gradle/wrapper/gradle-wrapper.properties \ No newline at end of file diff --git a/build.gradle b/build.gradle index 02c2e56..054cdc0 100644 --- a/build.gradle +++ b/build.gradle @@ -36,6 +36,9 @@ dependencies { // test testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + testImplementation 'org.testcontainers:testcontainers:1.19.3' + testImplementation 'org.testcontainers:junit-jupiter:1.19.3' + testImplementation 'org.testcontainers:mysql' // lombok compileOnly 'org.projectlombok:lombok' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..8bdaf60 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e2847c8..2e11132 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f5feea6..adff685 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -115,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -173,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -206,15 +203,14 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9d21a21..c4bdd3a 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/promtail/positions/positions.yaml b/promtail/positions/positions.yaml index 663641d..95a9509 100644 --- a/promtail/positions/positions.yaml +++ b/promtail/positions/positions.yaml @@ -1,5 +1,5 @@ positions: - /logs/app.log: "5166" + /logs/app.log: "5165" /var/log/alternatives.log: "4682" /var/log/bootstrap.log: "61237" /var/log/dpkg.log: "171709" diff --git a/src/main/java/knu_chatbot/entity/Member.java b/src/main/java/knu_chatbot/entity/Member.java index 53f58a4..2aeacee 100644 --- a/src/main/java/knu_chatbot/entity/Member.java +++ b/src/main/java/knu_chatbot/entity/Member.java @@ -26,6 +26,7 @@ public class Member extends DateTimeEntity { private String nickname; + @Builder.Default private int questionCount = 0; @OneToMany(mappedBy = "member", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties new file mode 100644 index 0000000..2be981c --- /dev/null +++ b/src/main/resources/application-dev.properties @@ -0,0 +1,7 @@ +MYSQL_HOST=localhost +MYSQL_PORT=3306 +MYSQL_USER=knu-chatbot_user +MYSQL_PASSWORD=knu-chatbot123 +MYSQL_DATABASE=knu-chatbot + +DISCORD_WEBHOOK_URL=discord_webhook_url \ No newline at end of file diff --git a/src/test/java/knu_chatbot/KnuChatbotApplicationTests.java b/src/test/java/knu_chatbot/KnuChatbotApplicationTests.java index 8a6d3be..01df4ab 100644 --- a/src/test/java/knu_chatbot/KnuChatbotApplicationTests.java +++ b/src/test/java/knu_chatbot/KnuChatbotApplicationTests.java @@ -1,10 +1,11 @@ package knu_chatbot; +import knu_chatbot.config.WithContainerTest; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -class KnuChatbotApplicationTests { +class KnuChatbotApplicationTests extends WithContainerTest { @Test void contextLoads() { diff --git a/src/test/java/knu_chatbot/config/WithContainerTest.java b/src/test/java/knu_chatbot/config/WithContainerTest.java new file mode 100644 index 0000000..1efd718 --- /dev/null +++ b/src/test/java/knu_chatbot/config/WithContainerTest.java @@ -0,0 +1,21 @@ +package knu_chatbot.config; + +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.MySQLContainer; + +public class WithContainerTest { + + protected static MySQLContainer mySQLContainer = new MySQLContainer("mysql:8"); + + static { + mySQLContainer.start(); + } + + @DynamicPropertySource + static void properties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", mySQLContainer::getJdbcUrl); + registry.add("spring.datasource.username", mySQLContainer::getUsername); + registry.add("spring.datasource.password", mySQLContainer::getPassword); + } +} diff --git a/src/test/java/knu_chatbot/controller/HistoryControllerTest.java b/src/test/java/knu_chatbot/controller/HistoryControllerTest.java index 900f7ff..32e87e3 100644 --- a/src/test/java/knu_chatbot/controller/HistoryControllerTest.java +++ b/src/test/java/knu_chatbot/controller/HistoryControllerTest.java @@ -1,6 +1,7 @@ package knu_chatbot.controller; import com.fasterxml.jackson.databind.ObjectMapper; +import io.micrometer.core.instrument.MeterRegistry; import knu_chatbot.controller.request.UpdateHistoryNameRequest; import knu_chatbot.controller.response.AnswerResponse; import knu_chatbot.controller.response.HistoryResponse; @@ -10,6 +11,7 @@ import knu_chatbot.exception.KnuChatbotException; import knu_chatbot.service.HistoryService; import knu_chatbot.service.request.UpdateHistoryNameServiceRequest; +import knu_chatbot.util.DiscordClient; import knu_chatbot.util.SessionConst; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -39,6 +41,12 @@ class HistoryControllerTest { @Autowired private ObjectMapper objectMapper; + @MockitoBean + DiscordClient discordClient; + + @MockitoBean + MeterRegistry meterRegistry; + @MockitoBean private HistoryService historyService; diff --git a/src/test/java/knu_chatbot/controller/MemberControllerTest.java b/src/test/java/knu_chatbot/controller/MemberControllerTest.java index ab34817..920e542 100644 --- a/src/test/java/knu_chatbot/controller/MemberControllerTest.java +++ b/src/test/java/knu_chatbot/controller/MemberControllerTest.java @@ -1,6 +1,7 @@ package knu_chatbot.controller; import com.fasterxml.jackson.databind.ObjectMapper; +import io.micrometer.core.instrument.MeterRegistry; import knu_chatbot.controller.request.MemberEmailCheckRequest; import knu_chatbot.controller.request.MemberLoginRequest; import knu_chatbot.controller.request.MemberSignupRequest; @@ -9,6 +10,7 @@ import knu_chatbot.service.request.MemberEmailCheckServiceRequest; import knu_chatbot.service.request.MemberLoginServiceRequest; import knu_chatbot.service.response.MemberResponse; +import knu_chatbot.util.DiscordClient; import knu_chatbot.util.SessionConst; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -37,6 +39,12 @@ class MemberControllerTest { @Autowired private ObjectMapper objectMapper; + @MockitoBean + DiscordClient discordClient; + + @MockitoBean + MeterRegistry meterRegistry; + @MockitoBean private MemberService memberService; diff --git a/src/test/java/knu_chatbot/repository/HistoryRepositoryTest.java b/src/test/java/knu_chatbot/repository/HistoryRepositoryTest.java index 1f96077..7a6b124 100644 --- a/src/test/java/knu_chatbot/repository/HistoryRepositoryTest.java +++ b/src/test/java/knu_chatbot/repository/HistoryRepositoryTest.java @@ -1,10 +1,12 @@ package knu_chatbot.repository; +import knu_chatbot.config.WithContainerTest; import knu_chatbot.entity.History; import knu_chatbot.entity.Member; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import java.util.ArrayList; @@ -13,7 +15,8 @@ import static org.assertj.core.api.Assertions.assertThat; @DataJpaTest -class HistoryRepositoryTest { +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +class HistoryRepositoryTest extends WithContainerTest { @Autowired private MemberRepository memberRepository; diff --git a/src/test/java/knu_chatbot/repository/MemberRepositoryTest.java b/src/test/java/knu_chatbot/repository/MemberRepositoryTest.java index e33a950..6f7325b 100644 --- a/src/test/java/knu_chatbot/repository/MemberRepositoryTest.java +++ b/src/test/java/knu_chatbot/repository/MemberRepositoryTest.java @@ -1,11 +1,13 @@ package knu_chatbot.repository; +import knu_chatbot.config.WithContainerTest; import knu_chatbot.entity.History; import knu_chatbot.entity.Member; import knu_chatbot.entity.Question; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import java.util.Optional; @@ -13,7 +15,8 @@ import static org.assertj.core.api.Assertions.assertThat; @DataJpaTest -class MemberRepositoryTest { +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +class MemberRepositoryTest extends WithContainerTest { @Autowired private MemberRepository memberRepository; diff --git a/src/test/java/knu_chatbot/service/HistoryServiceTest.java b/src/test/java/knu_chatbot/service/HistoryServiceTest.java index 4334664..9f686cc 100644 --- a/src/test/java/knu_chatbot/service/HistoryServiceTest.java +++ b/src/test/java/knu_chatbot/service/HistoryServiceTest.java @@ -1,6 +1,7 @@ package knu_chatbot.service; import jakarta.persistence.EntityManager; +import knu_chatbot.config.WithContainerTest; import knu_chatbot.controller.response.HistoryResponse; import knu_chatbot.controller.response.QuestionAndAnswerResponse; import knu_chatbot.entity.*; @@ -20,9 +21,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -@Transactional @SpringBootTest -class HistoryServiceTest { +@Transactional +class HistoryServiceTest extends WithContainerTest { @Autowired HistoryService historyService; diff --git a/src/test/java/knu_chatbot/service/MemberServiceTest.java b/src/test/java/knu_chatbot/service/MemberServiceTest.java index e0738f6..db13ec7 100644 --- a/src/test/java/knu_chatbot/service/MemberServiceTest.java +++ b/src/test/java/knu_chatbot/service/MemberServiceTest.java @@ -1,21 +1,20 @@ package knu_chatbot.service; +import knu_chatbot.config.WithContainerTest; import knu_chatbot.entity.History; import knu_chatbot.entity.Member; import knu_chatbot.entity.Question; import knu_chatbot.exception.KnuChatbotException; -import knu_chatbot.repository.HistoryRepository; import knu_chatbot.repository.MemberRepository; -import knu_chatbot.repository.QuestionRepository; import knu_chatbot.service.request.MemberEmailCheckServiceRequest; import knu_chatbot.service.request.MemberLoginServiceRequest; import knu_chatbot.service.request.MemberSignupServiceRequest; import knu_chatbot.service.response.MemberResponse; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; import java.util.Optional; @@ -23,7 +22,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows; @SpringBootTest -class MemberServiceTest { +@Transactional +class MemberServiceTest extends WithContainerTest { @Autowired private MemberService memberService; @@ -31,19 +31,6 @@ class MemberServiceTest { @Autowired private MemberRepository memberRepository; - @Autowired - private QuestionRepository questionRepository; - - @Autowired - private HistoryRepository historyRepository; - - @AfterEach - void tearDown() { - questionRepository.deleteAllInBatch(); - historyRepository.deleteAllInBatch(); - memberRepository.deleteAllInBatch(); - } - @DisplayName("이미 존재하는 이메일 확인 시 예외가 발생한다.") @Test void emailCheckTest() { @@ -201,7 +188,7 @@ void getMyInfoWithExistMemberId() { // then assertThat(myInfo).isNotNull() .extracting("email", "questionCount") - .contains(email, 1000); + .contains(email, 0); } @DisplayName("계정 탈퇴시 멤버 정보가 삭제되어야 한다.") diff --git a/src/test/java/knu_chatbot/util/EncryptionManagerTest.java b/src/test/java/knu_chatbot/util/EncryptionManagerTest.java index 660e3d1..640c564 100644 --- a/src/test/java/knu_chatbot/util/EncryptionManagerTest.java +++ b/src/test/java/knu_chatbot/util/EncryptionManagerTest.java @@ -2,16 +2,16 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; import static org.assertj.core.api.Assertions.assertThat; -@SpringBootTest class EncryptionManagerTest { - @Autowired - private EncryptionManager encryptionManager; + private final EncryptionManager encryptionManager; + + public EncryptionManagerTest() { + this.encryptionManager = new EncryptionManager(); + } @DisplayName("EncryptionManager에서 암호화시 기존의 password와 다른 문자가 나와야 한다.") @Test