diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml
new file mode 100644
index 0000000..4f0bed7
--- /dev/null
+++ b/.github/workflows/backend-ci.yml
@@ -0,0 +1,262 @@
+name: Backend CI
+
+on:
+ pull_request:
+ paths:
+ - "motionit/**"
+ - ".github/**"
+ push:
+ branches: [ main, develop ]
+
+permissions:
+ contents: read
+ checks: write
+ pull-requests: write
+
+jobs:
+ backend-test:
+ runs-on: ubuntu-latest
+
+ defaults:
+ run:
+ working-directory: motionit
+
+ # ๐ ์ฌ๊ธฐ์ GitHub Secrets โ Spring์ด ์ฐธ์กฐํ๋ ํ๊ฒฝ๋ณ์๋ช
์ผ๋ก ๋งคํ
+ # application.yml ์ ${...} ํค์ "์ ํํ ๊ฐ์ ์ด๋ฆ"์ผ๋ก ๋๋ฉด .env ์์ด๋ ๋์ํฉ๋๋ค.
+ env:
+ # Spring ์คํ ํ๋กํ: CI/H2์ฉ
+ SPRING_PROFILES_ACTIVE: test
+ # H2๋ก ํ
์คํธ (ํ์ ์ YourTestConfig์ ๋ง๊ฒ ์กฐ์ )
+ SPRING_DATASOURCE_URL: jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false
+ SPRING_DATASOURCE_USERNAME: sa
+ SPRING_DATASOURCE_PASSWORD: ""
+
+ # ====== app์์ ์ฐธ์กฐํ๋ ํค๋ค ======
+ AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
+ AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_KEY }}
+ AWS_S3_BUCKET_NAME: ${{ secrets.AWS_S3_BUCKET_NAME }}
+ AWS_CLOUDFRONT_DOMAIN: ${{ secrets.AWS_CLOUDFRONT_DOMAIN }}
+ AWS_CLOUDFRONT_KEY_ID: ${{ secrets.AWS_CLOUDFRONT_KEY_ID }}
+ # CloudFront ํ๋ผ์ด๋นํค๋ ๋ณดํต ํ์ผ ๊ฒฝ๋ก๊ฐ ํ์ํฉ๋๋ค.
+ # ํ
์คํธ์์๋ ์ฌ์ฉํ์ง ์๊ฒ ๋ถ๊ธฐํ๋ ํธ์ด ์์ ํ์ง๋ง,
+ # ์์๋ก /tmp/key.pem ๊ฒฝ๋ก์ ์จ๋๊ณ ๊ฒฝ๋ก๋ง ์ฃผ์
ํด๋ ๋ฉ๋๋ค. (์๋ "์ต์
: ํค ํ์ผ ์์ฑ" ์ฐธ๊ณ )
+ AWS_CLOUDFRONT_PRIVATE_KEY_PATH: /tmp/cloudfront_key.pem
+ AWS_CLOUDFRONT_PRIVATE_KEY_PEM: ${{ secrets.AWS_CLOUDFRONT_PRIVATE_KEY_PEM }}
+
+ JWT_SECRET: ${{ secrets.JWT_SECRET }}
+ JWT_ACCESS_TOKEN_EXPIRATION: "3600"
+ JWT_REFRESH_TOKEN_EXPIRATION: "1209600"
+
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
+ YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY }}
+ KAKAO_CLIENT_ID: ${{ secrets.KAKAO_CLIENT_ID }}
+
+ # ํ๋ก ํธ ๋ฆฌ๋ค์ด๋ ํธ ์ฃผ์ ๋ฑ ํ
์คํธ์ฉ ๊ธฐ๋ณธ๊ฐ
+ # ํ์์ Secrets๋ก ๋นผ๋ ๋จ
+ # app.oauth2.redirect-url์ application.yml์ ์์๋ก ์์ผ๋ ๋ณดํต ๋ถํ์
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Java 21
+ uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: 21
+
+ - name: Grant execute permission for Gradle Wrapper
+ run: chmod +x gradlew
+
+ # (์ต์
) CloudFront ํ๋ผ์ด๋น ํค ํ์ผ ์์ฑ โ ํ
์คํธ ์ฝ๋๊ฐ ํด๋น ํ์ผ์ ์ ๊ทผํ๋ค๋ฉด ํ์
+ - name: Write CloudFront private key (optional)
+ if: ${{ env.AWS_CLOUDFRONT_PRIVATE_KEY_PATH != '' && env.AWS_CLOUDFRONT_PRIVATE_KEY_PEM != '' }}
+ run: |
+ printf "%s" "$AWS_CLOUDFRONT_PRIVATE_KEY_PEM" > "$AWS_CLOUDFRONT_PRIVATE_KEY_PATH"
+ chmod 600 "$AWS_CLOUDFRONT_PRIVATE_KEY_PATH"
+
+ # ์ ์ฒด(๋จ์+ํตํฉ) ์คํ
+ - name: Run Full Test
+ run: ./gradlew clean fullTest
+
+ - name: Generate JaCoCo (fullTest)
+ run: ./gradlew jacocoFullTestReport
+
+ # ์คํจ/์ฑ๊ณต๊ณผ ๋ฌด๊ดํ๊ฒ ๋ฆฌํฌํ
๋จ๊ณ๋ ์งํ
+ - name: Publish Unit Test Results (JUnit)
+ if: always()
+ uses: EnricoMi/publish-unit-test-result-action@v2
+ with:
+ files: |
+ motionit/build/test-results/test/*.xml
+ motionit/build/test-results/fullTest/*.xml
+ check_run: true
+ comment_mode: always
+
+ - name: Upload failed-tests.txt (if exists)
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: failed-tests
+ path: motionit/build/reports/tests/failed-tests.txt
+ if-no-files-found: ignore
+
+ - name: Upsert PR comment with failed tests
+ if: always()
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const fs = require('fs');
+ const path = 'motionit/build/reports/tests/failed-tests.txt';
+ const MARK = '';
+
+ function buildBody(textBlock) {
+ return [
+ MARK,
+ '### โ Failed Tests (from Gradle summary)',
+ '',
+ 'Expand
',
+ '',
+ '```text',
+ textBlock,
+ '```',
+ '',
+ ' '
+ ].join('\n');
+ }
+
+ if (!context.payload.pull_request) {
+ core.info('Not a PR event, skip commenting');
+ return;
+ }
+
+ const { data: comments } = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.payload.pull_request.number,
+ per_page: 100
+ });
+
+ // ํ์ผ์ด ์๊ฑฐ๋, No failures๋ฉด ๊ธฐ์กด ๋ง์ปค ๋๊ธ ์ญ์
+ if (!fs.existsSync(path)) {
+ const existing = comments.find(c => c.user.type === 'Bot' && c.body.includes(MARK));
+ if (existing) {
+ await github.rest.issues.deleteComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: existing.id
+ });
+ }
+ return;
+ }
+
+ const content = fs.readFileSync(path, 'utf8').trim();
+ if (!content || content === 'No failures ๐') {
+ const existing = comments.find(c => c.user.type === 'Bot' && c.body.includes(MARK));
+ if (existing) {
+ await github.rest.issues.deleteComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: existing.id
+ });
+ }
+ return;
+ }
+
+ const body = buildBody(content);
+ const existing = comments.find(c => c.user.type === 'Bot' && c.body.includes(MARK));
+ if (existing) {
+ await github.rest.issues.updateComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: existing.id,
+ body
+ });
+ } else {
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.payload.pull_request.number,
+ body
+ });
+ }
+
+ - name: Upload JaCoCo HTML
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: jacoco-full-html
+ path: motionit/build/reports/jacocoFull/html
+ if-no-files-found: warn
+
+ - name: Install xmllint
+ if: always()
+ run: sudo apt-get update && sudo apt-get install -y libxml2-utils
+
+ - name: Compute coverage & upsert PR comment
+ if: always() && github.event_name == 'pull_request'
+ id: cov
+ run: |
+ # ํ๋์ ๋ฆฌํฌํธ๋ก ์: fullTest ๊ธฐ์ค
+ XML="motionit/build/reports/jacocoFull/xml/jacocoFullTestReport.xml"
+ if [ ! -f "$XML" ]; then
+ echo "XML not found: $XML"
+ echo "pct=0" >> $GITHUB_OUTPUT
+ exit 0
+ fi
+
+ COVERED=$(xmllint --xpath "string(sum(//counter[@type='LINE']/@covered))" "$XML")
+ MISSED=$(xmllint --xpath "string(sum(//counter[@type='LINE']/@missed))" "$XML")
+ TOTAL=$(( ${COVERED%.*} + ${MISSED%.*} ))
+ if [ "$TOTAL" -eq 0 ]; then
+ PCT=0
+ else
+ # ์์์ 2์๋ฆฌ
+ PCT=$(awk "BEGIN { printf \"%.2f\", ($COVERED/($COVERED+$MISSED))*100 }")
+ fi
+ echo "pct=$PCT" >> $GITHUB_OUTPUT
+ shell: bash
+
+ - name: Upsert PR comment (coverage)
+ if: always() && github.event_name == 'pull_request'
+ uses: actions/github-script@v7
+ env:
+ PCT: ${{ steps.cov.outputs.pct }}
+ with:
+ script: |
+ const MARK = '';
+ const pct = process.env.PCT || '0';
+ const body = `${MARK}
+ ### ๐งช JaCoCo Coverage
+ **Line Coverage:** ${pct}%`;
+
+ const { data: comments } = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.payload.pull_request.number,
+ per_page: 100
+ });
+ const existing = comments.find(c => c.user.type === 'Bot' && c.body.includes(MARK));
+ if (existing) {
+ await github.rest.issues.updateComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: existing.id,
+ body
+ });
+ } else {
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.payload.pull_request.number,
+ body
+ });
+ }
+
+ - name: Write application-test.yml from secret
+ working-directory: motionit
+ run: |
+ mkdir -p src/test/resources
+ cat > src/test/resources/application-test.yml <<'YAML'
+ ${{ secrets.APPLICATION_TEST_YML }}
+ YAML
\ No newline at end of file
diff --git a/motionit/build.gradle.kts b/motionit/build.gradle.kts
index 0563eae..f4b24a7 100644
--- a/motionit/build.gradle.kts
+++ b/motionit/build.gradle.kts
@@ -3,9 +3,9 @@ plugins {
id("org.springframework.boot") version "3.5.6"
id("io.spring.dependency-management") version "1.1.7"
checkstyle
+ jacoco
}
-
group = "com.back"
version = "0.0.1-SNAPSHOT"
description = "motionit"
@@ -35,28 +35,32 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
implementation("org.springframework.boot:spring-boot-starter-websocket")
+
compileOnly("org.projectlombok:lombok")
developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("com.h2database:h2")
runtimeOnly("com.mysql:mysql-connector-j")
annotationProcessor("org.projectlombok:lombok")
+
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.batch:spring-batch-test")
testImplementation("org.springframework.security:spring-security-test")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
+
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.13")
testImplementation("net.datafaker:datafaker:2.3.1")
testImplementation("com.jayway.jsonpath:json-path")
+
implementation("io.jsonwebtoken:jjwt-api:0.12.6")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.6")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.6")
+
implementation("com.google.apis:google-api-services-youtube:v3-rev222-1.25.0")
// AWS
implementation("software.amazon.awssdk:s3:2.27.21")
implementation("software.amazon.awssdk:auth:2.27.21")
implementation("software.amazon.awssdk:regions:2.27.21")
- implementation("software.amazon.awssdk:s3:2.27.21")
implementation("software.amazon.awssdk:cloudfront:2.27.21")
implementation("com.amazonaws:aws-java-sdk-cloudfront:1.12.782")
@@ -64,7 +68,6 @@ dependencies {
implementation("com.theokanning.openai-gpt3-java:service:0.18.2")
}
-
tasks.withType().configureEach {
options.encoding = "UTF-8"
options.compilerArgs.add("-parameters")
@@ -87,6 +90,171 @@ checkstyle {
)
}
-tasks.withType {
+/** -----------------------------
+ * JaCoCo (ํ ๊ณณ์์๋ง ์ ์ธ)
+ * ----------------------------- */
+jacoco {
+ toolVersion = "0.8.12" // Java 21 ํธํ
+}
+
+/** ๊ณตํต ์ปค๋ฒ๋ฆฌ์ง ์ ์ธ ํจํด */
+val coverageExcludes = listOf(
+ "**/*Application*",
+ "**/config/**",
+ "**/dto/**",
+ "**/exception/**",
+ "**/vo/**",
+ "**/Q*.*", // Querydsl ์์ฑ๋ฌผ
+ "**/*\$*Companion*.*" // Kotlin ๋ด๋ถ ์์ฑ๋ฌผ
+)
+
+/** -----------------------------
+ * Test ๊ณตํต ์ค์ (๋ก๊น
/์์ฝ/์คํจ์์ง)
+ * ----------------------------- */
+tasks.withType().configureEach {
+ useJUnitPlatform()
+
+ testLogging {
+ events("PASSED", "FAILED", "SKIPPED")
+ exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
+ showExceptions = true
+ showCauses = true
+ showStackTraces = true
+ showStandardStreams = true
+ }
+
+ val failed = mutableListOf>() // class, method, msg
+ addTestListener(object : org.gradle.api.tasks.testing.TestListener {
+ override fun beforeSuite(suite: TestDescriptor) {}
+ override fun beforeTest(testDescriptor: TestDescriptor) {}
+
+ override fun afterTest(desc: TestDescriptor, result: TestResult) {
+ if (result.resultType == TestResult.ResultType.FAILURE) {
+ val clazz = desc.className ?: "(unknown-class)"
+ val method = desc.name
+ val msg = result.exception?.message?.lineSequence()?.firstOrNull()
+ failed += Triple(clazz, method, msg)
+ }
+ }
+
+ override fun afterSuite(suite: TestDescriptor, result: TestResult) {
+ if (suite.parent == null) {
+ println(
+ """
+ ------------------------
+ โ
TEST RESULT SUMMARY
+ Total tests : ${result.testCount}
+ Passed : ${result.successfulTestCount}
+ Failed : ${result.failedTestCount}
+ Skipped : ${result.skippedTestCount}
+ ------------------------
+ """.trimIndent()
+ )
+ val out = layout.buildDirectory.file("reports/tests/failed-tests.txt").get().asFile
+ out.parentFile.mkdirs()
+
+ if (failed.isNotEmpty()) {
+ val RED = "\u001B[31m"
+ val RESET = "\u001B[0m"
+ println("โ FAILED TESTS (${failed.size})")
+ failed.forEachIndexed { i, (c, m, msg) ->
+ println("${RED}${i + 1}. $c#$m${if (msg != null) " โ $msg" else ""}${RESET}")
+ }
+ out.printWriter().use { pw ->
+ pw.println("FAILED TESTS (${failed.size})")
+ failed.forEach { (c, m, msg) ->
+ pw.println("$c#$m${if (msg != null) " โ $msg" else ""}")
+ }
+ pw.println()
+ pw.println("Patterns for --tests:")
+ failed.forEach { (c, m, _) -> pw.println("--tests \"$c.$m\"") }
+ }
+ println("๐ Saved failed list -> ${out.absolutePath}")
+ } else {
+ out.writeText("No failures ๐")
+ }
+ }
+ }
+ })
+}
+
+/** -----------------------------
+ * ๊ธฐ๋ณธ test ํ์คํฌ (unit ๊ธฐ๋ณธ, -PincludeIntegration=true ์ ํตํฉ ํฌํจ)
+ * ----------------------------- */
+tasks.named("test") {
+ if (project.findProperty("includeIntegration") == "true") {
+ systemProperty("junit.platform.tags.includes", "integration,unit")
+ } else {
+ systemProperty("junit.platform.tags.excludes", "integration")
+ }
+ finalizedBy(tasks.jacocoTestReport)
+}
+
+/** -----------------------------
+ * fullTest: ๋จ์+ํตํฉ ๋ชจ๋ ์คํ (CI์์ ์ฌ์ฉ)
+ * ----------------------------- */
+tasks.register("fullTest") {
+ description = "Run unit + integration tests"
+ group = "verification"
+
+ val testSourceSet = sourceSets.named("test").get()
+ testClassesDirs = testSourceSet.output.classesDirs
+ classpath = testSourceSet.runtimeClasspath
+
useJUnitPlatform()
+ systemProperty("junit.platform.tags.includes", "integration,unit")
+
+ shouldRunAfter(tasks.named("test"))
+ finalizedBy(tasks.named("jacocoFullTestReport"))
}
+
+/** -----------------------------
+ * JaCoCo ๋ฆฌํฌํธ (test)
+ * ----------------------------- */
+tasks.jacocoTestReport {
+ dependsOn(tasks.named("test"))
+ reports {
+ xml.required.set(true)
+ // PR ์ฝ๋ฉํธ ์ก์
์์ ์ฌ์ฉํ๊ธฐ ์ฌ์ด ๊ณ ์ ๊ฒฝ๋ก
+ xml.outputLocation.set(layout.buildDirectory.file("reports/jacoco/xml/jacocoTestReport.xml"))
+ html.required.set(true)
+ csv.required.set(false)
+ html.outputLocation.set(layout.buildDirectory.dir("reports/jacoco/html"))
+ }
+ classDirectories.setFrom(
+ files(
+ classDirectories.files.map {
+ fileTree(it) { exclude(coverageExcludes) }
+ }
+ )
+ )
+}
+
+/** -----------------------------
+ * JaCoCo ๋ฆฌํฌํธ (fullTest)
+ * ----------------------------- */
+tasks.register("jacocoFullTestReport") {
+ dependsOn(tasks.named("fullTest"))
+
+ // fullTest์ ์คํ ๊ฒฐ๊ณผ(Exec)๋ฅผ ํ์คํฌ ์ฐธ์กฐ๋ก ์์ ํ๊ฒ ์์ง
+ executionData(tasks.named("fullTest"))
+
+ reports {
+ xml.required.set(true)
+ // PR ์ฝ๋ฉํธ ์ก์
์์ ์ฌ์ฉํ๊ธฐ ์ฌ์ด ๊ณ ์ ๊ฒฝ๋ก
+ xml.outputLocation.set(layout.buildDirectory.file("reports/jacocoFull/xml/jacocoFullTestReport.xml"))
+ html.required.set(true)
+ csv.required.set(false)
+ html.outputLocation.set(layout.buildDirectory.dir("reports/jacocoFull/html"))
+ }
+
+ val main = sourceSets.named("main").get()
+ sourceDirectories.setFrom(main.allSource.srcDirs)
+ classDirectories.setFrom(
+ files(
+ main.output.classesDirs.files.map {
+ fileTree(it) { exclude(coverageExcludes) }
+ }
+ )
+ )
+}
\ No newline at end of file
diff --git a/motionit/src/main/java/com/back/motionit/global/service/CloudFrontCookieService.java b/motionit/src/main/java/com/back/motionit/global/service/CloudFrontCookieService.java
index 65a7f3f..449acc3 100644
--- a/motionit/src/main/java/com/back/motionit/global/service/CloudFrontCookieService.java
+++ b/motionit/src/main/java/com/back/motionit/global/service/CloudFrontCookieService.java
@@ -8,6 +8,7 @@
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;
@@ -19,6 +20,7 @@
import jakarta.servlet.http.HttpServletResponse;
@Component
+@Profile("!test")
public class CloudFrontCookieService {
@Value("${aws.cloudfront.domain}")
private String cloudFrontDomain;
diff --git a/motionit/src/test/java/com/back/motionit/MotionitApplicationTests.java b/motionit/src/test/java/com/back/motionit/MotionitApplicationTests.java
index c77739e..9f6d900 100644
--- a/motionit/src/test/java/com/back/motionit/MotionitApplicationTests.java
+++ b/motionit/src/test/java/com/back/motionit/MotionitApplicationTests.java
@@ -1,9 +1,11 @@
package com.back.motionit;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
+@Disabled("CI์์ ๋ถํ์ํ ์ ์ฒด ์ปจํ
์คํธ ๋ถํ
๋ฐฉ์ง")
class MotionitApplicationTests {
@Test
diff --git a/motionit/src/test/java/com/back/motionit/domain/auth/service/AuthTokenServiceTest.java b/motionit/src/test/java/com/back/motionit/domain/auth/service/AuthTokenServiceTest.java
index 229d9ca..6a9b627 100644
--- a/motionit/src/test/java/com/back/motionit/domain/auth/service/AuthTokenServiceTest.java
+++ b/motionit/src/test/java/com/back/motionit/domain/auth/service/AuthTokenServiceTest.java
@@ -8,10 +8,11 @@
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.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.bean.override.mockito.MockitoBean;
import com.back.motionit.domain.auth.dto.TokenRefreshResponse;
import com.back.motionit.domain.user.entity.User;
@@ -21,20 +22,20 @@
import com.back.motionit.global.request.RequestContext;
import com.back.motionit.security.jwt.JwtTokenProvider;
-@SpringBootTest
+@ExtendWith(MockitoExtension.class)
@ActiveProfiles("test")
class AuthTokenServiceTest {
- @Autowired
+ @InjectMocks
private AuthTokenService authTokenService;
- @MockitoBean
+ @Mock
private JwtTokenProvider jwtTokenProvider;
- @MockitoBean
+ @Mock
private UserRepository userRepository;
- @MockitoBean
+ @Mock
private RequestContext requestContext;
private static final String VALID_RT = "valid_refresh_token";
diff --git a/motionit/src/test/java/com/back/motionit/domain/challenge/comment/controller/CommentControllerTest.java b/motionit/src/test/java/com/back/motionit/domain/challenge/comment/controller/CommentControllerTest.java
index 42ce09d..7f4b15e 100644
--- a/motionit/src/test/java/com/back/motionit/domain/challenge/comment/controller/CommentControllerTest.java
+++ b/motionit/src/test/java/com/back/motionit/domain/challenge/comment/controller/CommentControllerTest.java
@@ -9,6 +9,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
@@ -33,6 +34,7 @@
@SpringBootTest
@AutoConfigureMockMvc(addFilters = false)
@ActiveProfiles("test")
+@Tag("integration")
@TestPropertySource(properties = "spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration")
class CommentControllerIntegrationTest {
diff --git a/motionit/src/test/java/com/back/motionit/domain/challenge/like/controller/CommentLikeControllerTest.java b/motionit/src/test/java/com/back/motionit/domain/challenge/like/controller/CommentLikeControllerTest.java
index c875d19..91fe036 100644
--- a/motionit/src/test/java/com/back/motionit/domain/challenge/like/controller/CommentLikeControllerTest.java
+++ b/motionit/src/test/java/com/back/motionit/domain/challenge/like/controller/CommentLikeControllerTest.java
@@ -16,9 +16,11 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -49,6 +51,8 @@
@SpringBootTest
@ActiveProfiles("test")
@AutoConfigureMockMvc(addFilters = false)
+@WebMvcTest(CommentLikeController.class)
+@Tag("integration")
@Transactional
public class CommentLikeControllerTest {
diff --git a/motionit/src/test/java/com/back/motionit/domain/challenge/participant/controller/ChallengeParticipantControllerTest.java b/motionit/src/test/java/com/back/motionit/domain/challenge/participant/controller/ChallengeParticipantControllerTest.java
index 0edee5a..48ed6c2 100644
--- a/motionit/src/test/java/com/back/motionit/domain/challenge/participant/controller/ChallengeParticipantControllerTest.java
+++ b/motionit/src/test/java/com/back/motionit/domain/challenge/participant/controller/ChallengeParticipantControllerTest.java
@@ -10,9 +10,11 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -38,6 +40,8 @@
@SpringBootTest
@ActiveProfiles("test")
@AutoConfigureMockMvc(addFilters = false)
+@WebMvcTest(ChallengeParticipantController.class)
+@Tag("integration")
@Transactional
public class ChallengeParticipantControllerTest {
diff --git a/motionit/src/test/java/com/back/motionit/domain/challenge/room/controller/ChallengeRoomControllerTest.java b/motionit/src/test/java/com/back/motionit/domain/challenge/room/controller/ChallengeRoomControllerTest.java
index af517a6..a9d18f5 100644
--- a/motionit/src/test/java/com/back/motionit/domain/challenge/room/controller/ChallengeRoomControllerTest.java
+++ b/motionit/src/test/java/com/back/motionit/domain/challenge/room/controller/ChallengeRoomControllerTest.java
@@ -12,9 +12,11 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -50,6 +52,8 @@
@SpringBootTest
@ActiveProfiles("test")
@AutoConfigureMockMvc(addFilters = false)
+@WebMvcTest(ChallengeRoomController.class)
+@Tag("integration")
@Transactional
public class ChallengeRoomControllerTest {
diff --git a/motionit/src/test/java/com/back/motionit/domain/challenge/video/controller/ChallengeVideoControllerTest.java b/motionit/src/test/java/com/back/motionit/domain/challenge/video/controller/ChallengeVideoControllerTest.java
index 66582a3..1741d21 100644
--- a/motionit/src/test/java/com/back/motionit/domain/challenge/video/controller/ChallengeVideoControllerTest.java
+++ b/motionit/src/test/java/com/back/motionit/domain/challenge/video/controller/ChallengeVideoControllerTest.java
@@ -8,9 +8,11 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -40,6 +42,8 @@
@SpringBootTest
@ActiveProfiles("test")
@AutoConfigureMockMvc(addFilters = false)
+@WebMvcTest(ChallengeVideoController.class)
+@Tag("integration")
@Transactional
class ChallengeVideoControllerTest {