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
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ dependencies {

// 테스트
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// 부팅 회귀 테스트(ApplicationContextBootTest)에서 CircuitBreakerRegistry를 주입받기 위해 필요
testImplementation libs.resilience4j.spring.boot3
}

springBoot {
Expand Down
560 changes: 281 additions & 279 deletions app/gradle.lockfile

Large diffs are not rendered by default.

58 changes: 58 additions & 0 deletions app/src/test/java/com/icc/qasker/ApplicationContextBootTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.icc.qasker;

import static org.assertj.core.api.Assertions.assertThat;

import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ActiveProfiles;

/**
* 버전업 세이프티 넷 (RT-001 + RT-004): Spring Boot / Spring AI patch·minor 승격과 resilience4j 승격이
* ApplicationContext 부팅과 CircuitBreaker 등록 계약을 깨뜨리지 않음을 확인한다.
*
* <p>contextLoads()가 성공하면 전 모듈의 Bean 팩토리(ChatModel, Storage, DataSource 등)가 정상 구성된 것.
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@ActiveProfiles("test")
class ApplicationContextBootTest {

@Autowired private ApplicationContext applicationContext;

@Autowired private CircuitBreakerRegistry circuitBreakerRegistry;

@Test
void contextLoads() {
assertThat(applicationContext).isNotNull();
assertThat(applicationContext.getId()).isNotBlank();
}

@Test
void chatModelBeanIsPresent() {
assertThat(applicationContext.getBeanNamesForType(resolveChatModelType()))
.as("Spring AI ChatModel bean 등록 확인")
.isNotEmpty();
}

@Test
void aiServerCircuitBreakerIsRegisteredWithExpectedConfig() {
CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker("aiServer");
assertThat(cb).isNotNull();
assertThat(cb.getCircuitBreakerConfig().getFailureRateThreshold()).isEqualTo(60.0f);
assertThat(cb.getCircuitBreakerConfig().getMinimumNumberOfCalls()).isEqualTo(3);
assertThat(cb.getCircuitBreakerConfig().getSlidingWindowSize()).isEqualTo(10);
assertThat(cb.getCircuitBreakerConfig().getPermittedNumberOfCallsInHalfOpenState())
.isEqualTo(2);
}

private static Class<?> resolveChatModelType() {
try {
return Class.forName("org.springframework.ai.chat.model.ChatModel");
} catch (ClassNotFoundException e) {
throw new AssertionError("Spring AI ChatModel 클래스가 런타임 클래스패스에 없음", e);
}
}
}
18 changes: 9 additions & 9 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
[versions]
# ── BOMs ────────────────────────────────────────────────
spring-boot = "3.5.8"
spring-ai = "1.1.2"
google-cloud = "26.67.0"
spring-boot = "4.1.0"
spring-ai = "2.0.0"
google-cloud = "26.84.0"
junit = "5.14.4"

# ── Plugins ─────────────────────────────────────────────
spotless = "7.0.4"
google-java-format = "1.25.2"
dependency-graph-generator = "0.8.0"
jib = "3.4.0"
jib = "3.5.3"
foojay = "0.8.0"

# ── Libraries ───────────────────────────────────────────
jasypt = "3.0.5"
springdoc = "2.8.17"
springdoc = "3.0.3"
auth0-jwt = "4.5.0"
bucket4j = "8.19.0"
jodconverter = "4.4.9"
jodconverter = "4.4.11"
hashids = "1.0.3"
resilience4j = "2.3.0"
resilience4j = "2.4.0"
oci = "3.90.0"
pdfbox = "3.0.3"
pdfbox = "3.0.7"
httpclient5 = "5.6.1"

[libraries]
Expand All @@ -39,7 +39,7 @@ bucket4j-jdk17-core = { module = "com.bucket4j:bucket4j_jdk17-core", version.ref
jodconverter-local-lo = { module = "org.jodconverter:jodconverter-local-lo", version.ref = "jodconverter" }
jodconverter-spring-boot-starter = { module = "org.jodconverter:jodconverter-spring-boot-starter", version.ref = "jodconverter" }
hashids = { module = "org.hashids:hashids", version.ref = "hashids" }
resilience4j-spring-boot3 = { module = "io.github.resilience4j:resilience4j-spring-boot3", version.ref = "resilience4j" }
resilience4j-spring-boot3 = { module = "io.github.resilience4j:resilience4j-spring-boot4", version.ref = "resilience4j" }
oci-objectstorage = { module = "com.oracle.oci.sdk:oci-java-sdk-objectstorage", version.ref = "oci" }
oci-httpclient-jersey3 = { module = "com.oracle.oci.sdk:oci-java-sdk-common-httpclient-jersey3", version.ref = "oci" }
pdfbox = { module = "org.apache.pdfbox:pdfbox", version.ref = "pdfbox" }
Expand Down
2 changes: 1 addition & 1 deletion infra/base-image/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# eclipse-temurin:21-jdk-noble: arm64v8 공식 지원, Ubuntu 24.04(Noble) 기반
# bookworm(Debian) 태그는 deprecated → noble 사용
FROM eclipse-temurin:21-jdk-noble
FROM eclipse-temurin:25.0.2_10-jdk-noble

# ── 시스템 패키지 설치 ──────────────────────
# DEBIAN_FRONTEND=noninteractive: apt 설치 중 대화형 프롬프트 방지
Expand Down
Loading
Loading