STT 테스트 환경 구성 및 유저스토리 업데이트

- docker-compose.test.yml 추가: 테스트용 컨테이너 환경 구성
- STT 테스트 설정 및 컨트롤러 테스트 코드 추가
- application.yml 업데이트
- 테스트 스크립트 추가
- 유저스토리 문서 업데이트

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Minseo-Jo
2025-10-24 14:50:56 +09:00
parent 694a84e4f5
commit ad8e0adbd8
7 changed files with 364 additions and 23 deletions
@@ -0,0 +1,82 @@
package com.unicorn.hgzero.stt.config;
import com.unicorn.hgzero.stt.event.publisher.EventPublisher;
import com.unicorn.hgzero.stt.repository.jpa.RecordingRepository;
import com.unicorn.hgzero.stt.repository.jpa.SpeakerRepository;
import com.unicorn.hgzero.stt.repository.jpa.TranscriptionRepository;
import com.unicorn.hgzero.stt.repository.jpa.TranscriptSegmentRepository;
import com.unicorn.hgzero.stt.service.RecordingService;
import com.unicorn.hgzero.stt.service.SpeakerService;
import com.unicorn.hgzero.stt.service.TranscriptionService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import redis.embedded.RedisServer;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import static org.mockito.Mockito.mock;
/**
* 테스트용 설정 클래스
* Embedded Redis 서버와 Mock Bean들을 제공
*/
@TestConfiguration
@EnableJpaRepositories(basePackages = "com.unicorn.hgzero.stt.repository.jpa")
@EntityScan(basePackages = "com.unicorn.hgzero.stt.repository.entity")
@ComponentScan(basePackages = {"com.unicorn.hgzero.stt.service", "com.unicorn.hgzero.stt.config"})
public class TestConfig {
private RedisServer redisServer;
@PostConstruct
public void startRedis() {
try {
redisServer = new RedisServer(6370);
redisServer.start();
} catch (Exception e) {
// Redis 서버 시작 실패 시 로그만 남기고 계속 진행
System.err.println("Failed to start embedded Redis server: " + e.getMessage());
}
}
@PreDestroy
public void stopRedis() {
if (redisServer != null && redisServer.isActive()) {
redisServer.stop();
}
}
@Bean
@Primary
public EventPublisher eventPublisher() {
return mock(EventPublisher.class);
}
@Bean
@Primary
@ConditionalOnMissingBean
public RedisConnectionFactory redisConnectionFactory() {
// Embedded Redis에 연결하는 실제 ConnectionFactory
return new LettuceConnectionFactory("localhost", 6370);
}
@Bean
@Primary
@ConditionalOnMissingBean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
return template;
}
}
@@ -0,0 +1,55 @@
package com.unicorn.hgzero.stt.config;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import redis.embedded.RedisServer;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
/**
* WebMvcTest용 최소한의 설정 클래스
* Repository는 MockBean으로 별도 처리
*/
@TestConfiguration
@ComponentScan(basePackages = "com.unicorn.hgzero.stt.config")
public class WebMvcTestConfig {
private RedisServer redisServer;
@PostConstruct
public void startRedis() {
try {
redisServer = new RedisServer(6371); // 다른 포트 사용
redisServer.start();
} catch (Exception e) {
System.err.println("Failed to start embedded Redis server: " + e.getMessage());
}
}
@PreDestroy
public void stopRedis() {
if (redisServer != null && redisServer.isActive()) {
redisServer.stop();
}
}
@Bean
@Primary
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory("localhost", 6371);
}
@Bean
@Primary
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
return template;
}
}
@@ -0,0 +1,109 @@
package com.unicorn.hgzero.stt.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.unicorn.hgzero.stt.dto.RecordingDto;
import com.unicorn.hgzero.stt.service.RecordingService;
import org.junit.jupiter.api.BeforeEach;
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.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.time.LocalDateTime;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
* 간단한 녹음 컨트롤러 테스트
*/
@WebMvcTest(RecordingController.class)
@DisplayName("간단한 녹음 컨트롤러 테스트")
class SimpleRecordingControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private RecordingService recordingService;
private RecordingDto.PrepareRequest prepareRequest;
private RecordingDto.PrepareResponse prepareResponse;
@BeforeEach
void setUp() {
prepareRequest = RecordingDto.PrepareRequest.builder()
.meetingId("MEETING-001")
.sessionId("SESSION-001")
.language("ko-KR")
.build();
prepareResponse = RecordingDto.PrepareResponse.builder()
.recordingId("REC-20250123-001")
.sessionId("SESSION-001")
.status("READY")
.streamUrl("wss://api.example.com/stt/v1/ws/stt/SESSION-001")
.storagePath("recordings/MEETING-001/SESSION-001.wav")
.estimatedInitTime(1100)
.build();
}
@Test
@DisplayName("녹음 준비 API 성공")
void prepareRecording_Success() throws Exception {
// Given
when(recordingService.prepareRecording(any(RecordingDto.PrepareRequest.class)))
.thenReturn(prepareResponse);
// When & Then
mockMvc.perform(post("/api/v1/stt/recordings/prepare")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(prepareRequest)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.data.recordingId").value("REC-20250123-001"))
.andExpect(jsonPath("$.data.sessionId").value("SESSION-001"))
.andExpect(jsonPath("$.data.status").value("READY"));
verify(recordingService).prepareRecording(any(RecordingDto.PrepareRequest.class));
}
@Test
@DisplayName("녹음 시작 API 성공")
void startRecording_Success() throws Exception {
// Given
String recordingId = "REC-20250123-001";
RecordingDto.StartRequest startRequest = RecordingDto.StartRequest.builder()
.startedBy("user001")
.build();
RecordingDto.StatusResponse statusResponse = RecordingDto.StatusResponse.builder()
.recordingId(recordingId)
.status("RECORDING")
.startTime(LocalDateTime.now())
.duration(0)
.build();
when(recordingService.startRecording(eq(recordingId), any(RecordingDto.StartRequest.class)))
.thenReturn(statusResponse);
// When & Then
mockMvc.perform(post("/api/v1/stt/recordings/{recordingId}/start", recordingId)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(startRequest)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.success").value(true))
.andExpect(jsonPath("$.data.recordingId").value(recordingId))
.andExpect(jsonPath("$.data.status").value("RECORDING"));
verify(recordingService).startRecording(eq(recordingId), any(RecordingDto.StartRequest.class));
}
}