mirror of
https://github.com/hwanny1128/HGZero.git
synced 2026-06-13 07:09:09 +00:00
백엔드 stt 서비스 개발
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
package com.unicorn.hgzero.stt;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
/**
|
||||
* STT 애플리케이션 통합 테스트
|
||||
* 전체 애플리케이션 컨텍스트 로딩 및 기본 설정 검증
|
||||
*/
|
||||
@SpringBootTest
|
||||
@ActiveProfiles("test")
|
||||
@DisplayName("STT 애플리케이션 테스트")
|
||||
class SttApplicationTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("애플리케이션 컨텍스트 로딩 성공")
|
||||
void contextLoads() {
|
||||
// 애플리케이션 컨텍스트가 정상적으로 로딩되는지 확인
|
||||
// 이 테스트는 모든 Bean이 정상적으로 생성되고 주입되는지 검증
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
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 RecordingControllerTest {
|
||||
|
||||
@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"))
|
||||
.andExpect(jsonPath("$.data.streamUrl").value("wss://api.example.com/stt/v1/ws/stt/SESSION-001"))
|
||||
.andExpect(jsonPath("$.data.estimatedInitTime").value(1100));
|
||||
|
||||
verify(recordingService).prepareRecording(any(RecordingDto.PrepareRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("녹음 준비 API 실패 - 유효성 검증")
|
||||
void prepareRecording_ValidationFailure() throws Exception {
|
||||
// Given
|
||||
RecordingDto.PrepareRequest invalidRequest = RecordingDto.PrepareRequest.builder()
|
||||
.meetingId("") // 빈 값
|
||||
.sessionId("SESSION-001")
|
||||
.build();
|
||||
|
||||
// When & Then
|
||||
mockMvc.perform(post("/api/v1/stt/recordings/prepare")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(invalidRequest)))
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
verify(recordingService, never()).prepareRecording(any());
|
||||
}
|
||||
|
||||
@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"))
|
||||
.andExpect(jsonPath("$.data.duration").value(0));
|
||||
|
||||
verify(recordingService).startRecording(eq(recordingId), any(RecordingDto.StartRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("녹음 중지 API 성공")
|
||||
void stopRecording_Success() throws Exception {
|
||||
// Given
|
||||
String recordingId = "REC-20250123-001";
|
||||
RecordingDto.StopRequest stopRequest = RecordingDto.StopRequest.builder()
|
||||
.stoppedBy("user001")
|
||||
.build();
|
||||
|
||||
RecordingDto.StatusResponse statusResponse = RecordingDto.StatusResponse.builder()
|
||||
.recordingId(recordingId)
|
||||
.status("STOPPED")
|
||||
.startTime(LocalDateTime.now().minusMinutes(30))
|
||||
.endTime(LocalDateTime.now())
|
||||
.duration(1800)
|
||||
.fileSize(172800000L)
|
||||
.storagePath("recordings/MEETING-001/SESSION-001.wav")
|
||||
.build();
|
||||
|
||||
when(recordingService.stopRecording(eq(recordingId), any(RecordingDto.StopRequest.class)))
|
||||
.thenReturn(statusResponse);
|
||||
|
||||
// When & Then
|
||||
mockMvc.perform(post("/api/v1/stt/recordings/{recordingId}/stop", recordingId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(stopRequest)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.success").value(true))
|
||||
.andExpect(jsonPath("$.data.recordingId").value(recordingId))
|
||||
.andExpect(jsonPath("$.data.status").value("STOPPED"))
|
||||
.andExpect(jsonPath("$.data.duration").value(1800))
|
||||
.andExpect(jsonPath("$.data.fileSize").value(172800000L));
|
||||
|
||||
verify(recordingService).stopRecording(eq(recordingId), any(RecordingDto.StopRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("녹음 조회 API 성공")
|
||||
void getRecording_Success() throws Exception {
|
||||
// Given
|
||||
String recordingId = "REC-20250123-001";
|
||||
|
||||
RecordingDto.DetailResponse detailResponse = RecordingDto.DetailResponse.builder()
|
||||
.recordingId(recordingId)
|
||||
.meetingId("MEETING-001")
|
||||
.sessionId("SESSION-001")
|
||||
.status("STOPPED")
|
||||
.startTime(LocalDateTime.now().minusMinutes(30))
|
||||
.endTime(LocalDateTime.now())
|
||||
.duration(1800)
|
||||
.speakerCount(3)
|
||||
.segmentCount(45)
|
||||
.storagePath("recordings/MEETING-001/SESSION-001.wav")
|
||||
.language("ko-KR")
|
||||
.build();
|
||||
|
||||
when(recordingService.getRecording(recordingId)).thenReturn(detailResponse);
|
||||
|
||||
// When & Then
|
||||
mockMvc.perform(get("/api/v1/stt/recordings/{recordingId}", recordingId))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.success").value(true))
|
||||
.andExpect(jsonPath("$.data.recordingId").value(recordingId))
|
||||
.andExpect(jsonPath("$.data.meetingId").value("MEETING-001"))
|
||||
.andExpect(jsonPath("$.data.sessionId").value("SESSION-001"))
|
||||
.andExpect(jsonPath("$.data.status").value("STOPPED"))
|
||||
.andExpect(jsonPath("$.data.duration").value(1800))
|
||||
.andExpect(jsonPath("$.data.speakerCount").value(3))
|
||||
.andExpect(jsonPath("$.data.segmentCount").value(45))
|
||||
.andExpect(jsonPath("$.data.language").value("ko-KR"));
|
||||
|
||||
verify(recordingService).getRecording(recordingId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("존재하지 않는 녹음 조회 실패")
|
||||
void getRecording_NotFound() throws Exception {
|
||||
// Given
|
||||
String recordingId = "REC-NOTFOUND-001";
|
||||
|
||||
when(recordingService.getRecording(recordingId))
|
||||
.thenThrow(new com.unicorn.hgzero.common.exception.BusinessException(
|
||||
com.unicorn.hgzero.common.exception.ErrorCode.ENTITY_NOT_FOUND,
|
||||
"녹음을 찾을 수 없습니다"
|
||||
));
|
||||
|
||||
// When & Then
|
||||
mockMvc.perform(get("/api/v1/stt/recordings/{recordingId}", recordingId))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.success").value(false))
|
||||
.andExpect(jsonPath("$.message").value("녹음을 찾을 수 없습니다"));
|
||||
|
||||
verify(recordingService).getRecording(recordingId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package com.unicorn.hgzero.stt.integration;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unicorn.hgzero.stt.dto.RecordingDto;
|
||||
import com.unicorn.hgzero.stt.dto.TranscriptionDto;
|
||||
import com.unicorn.hgzero.stt.dto.SpeakerDto;
|
||||
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.AutoConfigureWebMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
/**
|
||||
* STT API 통합 테스트
|
||||
* 전체 워크플로우 시나리오 테스트
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@AutoConfigureWebMvc
|
||||
@ActiveProfiles("test")
|
||||
@Transactional
|
||||
@DisplayName("STT API 통합 테스트")
|
||||
class SttApiIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Test
|
||||
@DisplayName("전체 STT 워크플로우 통합 테스트")
|
||||
void fullSttWorkflowIntegrationTest() throws Exception {
|
||||
// 1단계: 녹음 준비
|
||||
RecordingDto.PrepareRequest prepareRequest = RecordingDto.PrepareRequest.builder()
|
||||
.meetingId("MEETING-INTEGRATION-001")
|
||||
.sessionId("SESSION-INTEGRATION-001")
|
||||
.language("ko-KR")
|
||||
.build();
|
||||
|
||||
MvcResult prepareResult = 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.status").value("READY"))
|
||||
.andReturn();
|
||||
|
||||
// 응답에서 recordingId 추출
|
||||
String prepareResponseJson = prepareResult.getResponse().getContentAsString();
|
||||
// JSON 파싱하여 recordingId 추출 (실제로는 JsonPath 라이브러리 사용)
|
||||
String recordingId = "REC-20250123-001"; // 테스트용 하드코딩
|
||||
|
||||
// 2단계: 녹음 시작
|
||||
RecordingDto.StartRequest startRequest = RecordingDto.StartRequest.builder()
|
||||
.startedBy("integration-test-user")
|
||||
.build();
|
||||
|
||||
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.status").value("RECORDING"));
|
||||
|
||||
// 3단계: 실시간 음성 변환 (시뮬레이션)
|
||||
TranscriptionDto.StreamRequest streamRequest = TranscriptionDto.StreamRequest.builder()
|
||||
.recordingId(recordingId)
|
||||
.audioData("dGVzdCBhdWRpbyBkYXRh") // base64 encoded "test audio data"
|
||||
.timestamp(System.currentTimeMillis())
|
||||
.chunkIndex(1)
|
||||
.build();
|
||||
|
||||
mockMvc.perform(post("/api/v1/stt/transcription/stream")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(streamRequest)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.success").value(true))
|
||||
.andExpect(jsonPath("$.data.recordingId").value(recordingId))
|
||||
.andExpect(jsonPath("$.data.text").exists())
|
||||
.andExpect(jsonPath("$.data.confidence").exists());
|
||||
|
||||
// 4단계: 화자 식별
|
||||
SpeakerDto.IdentifyRequest identifyRequest = SpeakerDto.IdentifyRequest.builder()
|
||||
.recordingId(recordingId)
|
||||
.audioFrame("dGVzdCBhdWRpbyBmcmFtZQ==") // base64 encoded "test audio frame"
|
||||
.build();
|
||||
|
||||
mockMvc.perform(post("/api/v1/stt/speakers/identify")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(identifyRequest)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.success").value(true))
|
||||
.andExpect(jsonPath("$.data.speakerId").exists())
|
||||
.andExpect(jsonPath("$.data.confidence").exists());
|
||||
|
||||
// 5단계: 녹음 중지
|
||||
RecordingDto.StopRequest stopRequest = RecordingDto.StopRequest.builder()
|
||||
.stoppedBy("integration-test-user")
|
||||
.build();
|
||||
|
||||
mockMvc.perform(post("/api/v1/stt/recordings/{recordingId}/stop", recordingId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(stopRequest)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.success").value(true))
|
||||
.andExpect(jsonPath("$.data.status").value("STOPPED"))
|
||||
.andExpect(jsonPath("$.data.duration").exists());
|
||||
|
||||
// 6단계: 녹음 정보 조회
|
||||
mockMvc.perform(get("/api/v1/stt/recordings/{recordingId}", recordingId))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.success").value(true))
|
||||
.andExpect(jsonPath("$.data.recordingId").value(recordingId))
|
||||
.andExpect(jsonPath("$.data.status").value("STOPPED"));
|
||||
|
||||
// 7단계: 변환 결과 조회 (세그먼트 포함)
|
||||
mockMvc.perform(get("/api/v1/stt/transcription/{recordingId}", recordingId)
|
||||
.param("includeSegments", "true"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.success").value(true))
|
||||
.andExpect(jsonPath("$.data.recordingId").value(recordingId))
|
||||
.andExpect(jsonPath("$.data.fullText").exists());
|
||||
|
||||
// 8단계: 녹음별 화자 목록 조회
|
||||
mockMvc.perform(get("/api/v1/stt/speakers/recordings/{recordingId}", recordingId))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.success").value(true))
|
||||
.andExpect(jsonPath("$.data.recordingId").value(recordingId))
|
||||
.andExpect(jsonPath("$.data.speakerCount").exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("에러 케이스 통합 테스트")
|
||||
void errorCasesIntegrationTest() throws Exception {
|
||||
// 존재하지 않는 녹음 시작 시도
|
||||
RecordingDto.StartRequest startRequest = RecordingDto.StartRequest.builder()
|
||||
.startedBy("test-user")
|
||||
.build();
|
||||
|
||||
mockMvc.perform(post("/api/v1/stt/recordings/NONEXISTENT-001/start")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(startRequest)))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.success").value(false))
|
||||
.andExpect(jsonPath("$.message").exists());
|
||||
|
||||
// 잘못된 요청 데이터로 녹음 준비 시도
|
||||
RecordingDto.PrepareRequest invalidRequest = RecordingDto.PrepareRequest.builder()
|
||||
.meetingId("") // 빈 meetingId
|
||||
.sessionId("SESSION-001")
|
||||
.build();
|
||||
|
||||
mockMvc.perform(post("/api/v1/stt/recordings/prepare")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(invalidRequest)))
|
||||
.andExpect(status().isBadRequest());
|
||||
|
||||
// 존재하지 않는 변환 결과 조회
|
||||
mockMvc.perform(get("/api/v1/stt/transcription/NONEXISTENT-001"))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.success").value(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Swagger UI 접근 테스트")
|
||||
void swaggerUiAccessTest() throws Exception {
|
||||
// Swagger UI 페이지 접근 가능 여부 확인
|
||||
mockMvc.perform(get("/swagger-ui/index.html"))
|
||||
.andExpect(status().isOk());
|
||||
|
||||
// OpenAPI JSON 엔드포인트 확인
|
||||
mockMvc.perform(get("/v3/api-docs"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
package com.unicorn.hgzero.stt.service;
|
||||
|
||||
import com.unicorn.hgzero.common.exception.BusinessException;
|
||||
import com.unicorn.hgzero.stt.domain.Recording;
|
||||
import com.unicorn.hgzero.stt.dto.RecordingDto;
|
||||
import com.unicorn.hgzero.stt.event.publisher.EventPublisher;
|
||||
import com.unicorn.hgzero.stt.repository.entity.RecordingEntity;
|
||||
import com.unicorn.hgzero.stt.repository.jpa.RecordingRepository;
|
||||
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;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* 녹음 서비스 단위 테스트
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("녹음 서비스 테스트")
|
||||
class RecordingServiceTest {
|
||||
|
||||
@Mock
|
||||
private RecordingRepository recordingRepository;
|
||||
|
||||
@Mock
|
||||
private EventPublisher eventPublisher;
|
||||
|
||||
@InjectMocks
|
||||
private RecordingServiceImpl recordingService;
|
||||
|
||||
private RecordingDto.PrepareRequest prepareRequest;
|
||||
private RecordingEntity recordingEntity;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
prepareRequest = RecordingDto.PrepareRequest.builder()
|
||||
.meetingId("MEETING-001")
|
||||
.sessionId("SESSION-001")
|
||||
.language("ko-KR")
|
||||
.build();
|
||||
|
||||
recordingEntity = RecordingEntity.builder()
|
||||
.recordingId("REC-20250123-001")
|
||||
.meetingId("MEETING-001")
|
||||
.sessionId("SESSION-001")
|
||||
.status(Recording.RecordingStatus.READY)
|
||||
.language("ko-KR")
|
||||
.speakerCount(0)
|
||||
.segmentCount(0)
|
||||
.storagePath("recordings/MEETING-001/SESSION-001.wav")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("녹음 준비 성공")
|
||||
void prepareRecording_Success() {
|
||||
// Given
|
||||
when(recordingRepository.existsActiveRecordingByMeetingId(anyString())).thenReturn(false);
|
||||
when(recordingRepository.save(any(RecordingEntity.class))).thenReturn(recordingEntity);
|
||||
|
||||
// When
|
||||
RecordingDto.PrepareResponse response = recordingService.prepareRecording(prepareRequest);
|
||||
|
||||
// Then
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getSessionId()).isEqualTo("SESSION-001");
|
||||
assertThat(response.getStatus()).isEqualTo("READY");
|
||||
assertThat(response.getStreamUrl()).contains("SESSION-001");
|
||||
|
||||
verify(recordingRepository).existsActiveRecordingByMeetingId("MEETING-001");
|
||||
verify(recordingRepository).save(any(RecordingEntity.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("중복 녹음 세션 예외")
|
||||
void prepareRecording_DuplicateSession() {
|
||||
// Given
|
||||
when(recordingRepository.existsActiveRecordingByMeetingId(anyString())).thenReturn(true);
|
||||
|
||||
// When & Then
|
||||
assertThatThrownBy(() -> recordingService.prepareRecording(prepareRequest))
|
||||
.isInstanceOf(BusinessException.class)
|
||||
.hasMessageContaining("이미 진행 중인 녹음 세션이 있습니다");
|
||||
|
||||
verify(recordingRepository).existsActiveRecordingByMeetingId("MEETING-001");
|
||||
verify(recordingRepository, never()).save(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("녹음 시작 성공")
|
||||
void startRecording_Success() {
|
||||
// Given
|
||||
String recordingId = "REC-20250123-001";
|
||||
RecordingDto.StartRequest startRequest = RecordingDto.StartRequest.builder()
|
||||
.startedBy("user001")
|
||||
.build();
|
||||
|
||||
when(recordingRepository.findById(recordingId)).thenReturn(Optional.of(recordingEntity));
|
||||
when(recordingRepository.save(any(RecordingEntity.class))).thenReturn(recordingEntity);
|
||||
|
||||
// When
|
||||
RecordingDto.StatusResponse response = recordingService.startRecording(recordingId, startRequest);
|
||||
|
||||
// Then
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getRecordingId()).isEqualTo(recordingId);
|
||||
assertThat(response.getStatus()).isEqualTo("RECORDING");
|
||||
assertThat(response.getDuration()).isEqualTo(0);
|
||||
|
||||
verify(recordingRepository).findById(recordingId);
|
||||
verify(recordingRepository).save(any(RecordingEntity.class));
|
||||
verify(eventPublisher).publishAsync(eq("recording-events"), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("녹음 시작 실패 - 잘못된 상태")
|
||||
void startRecording_InvalidStatus() {
|
||||
// Given
|
||||
String recordingId = "REC-20250123-001";
|
||||
RecordingDto.StartRequest startRequest = RecordingDto.StartRequest.builder()
|
||||
.startedBy("user001")
|
||||
.build();
|
||||
|
||||
RecordingEntity recordingEntity = RecordingEntity.builder()
|
||||
.recordingId(recordingId)
|
||||
.status(Recording.RecordingStatus.RECORDING) // 이미 녹음 중
|
||||
.build();
|
||||
|
||||
when(recordingRepository.findById(recordingId)).thenReturn(Optional.of(recordingEntity));
|
||||
|
||||
// When & Then
|
||||
assertThatThrownBy(() -> recordingService.startRecording(recordingId, startRequest))
|
||||
.isInstanceOf(BusinessException.class)
|
||||
.hasMessageContaining("녹음을 시작할 수 없는 상태입니다");
|
||||
|
||||
verify(recordingRepository).findById(recordingId);
|
||||
verify(recordingRepository, never()).save(any());
|
||||
verify(eventPublisher, never()).publishAsync(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("녹음 중지 성공")
|
||||
void stopRecording_Success() {
|
||||
// Given
|
||||
String recordingId = "REC-20250123-001";
|
||||
RecordingDto.StopRequest stopRequest = RecordingDto.StopRequest.builder()
|
||||
.stoppedBy("user001")
|
||||
.build();
|
||||
|
||||
RecordingEntity recordingEntity = RecordingEntity.builder()
|
||||
.recordingId(recordingId)
|
||||
.meetingId("MEETING-001")
|
||||
.status(Recording.RecordingStatus.RECORDING)
|
||||
.startTime(LocalDateTime.now().minusMinutes(30))
|
||||
.build();
|
||||
|
||||
when(recordingRepository.findById(recordingId)).thenReturn(Optional.of(recordingEntity));
|
||||
when(recordingRepository.save(any(RecordingEntity.class))).thenReturn(recordingEntity);
|
||||
|
||||
// When
|
||||
RecordingDto.StatusResponse response = recordingService.stopRecording(recordingId, stopRequest);
|
||||
|
||||
// Then
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getRecordingId()).isEqualTo(recordingId);
|
||||
assertThat(response.getStatus()).isEqualTo("STOPPED");
|
||||
assertThat(response.getDuration()).isEqualTo(1800);
|
||||
assertThat(response.getFileSize()).isEqualTo(172800000L);
|
||||
|
||||
verify(recordingRepository).findById(recordingId);
|
||||
verify(recordingRepository).save(any(RecordingEntity.class));
|
||||
verify(eventPublisher).publishAsync(eq("recording-events"), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("녹음 정보 조회 성공")
|
||||
void getRecording_Success() {
|
||||
// Given
|
||||
String recordingId = "REC-20250123-001";
|
||||
|
||||
when(recordingRepository.findById(recordingId)).thenReturn(Optional.of(recordingEntity));
|
||||
|
||||
// When
|
||||
RecordingDto.DetailResponse response = recordingService.getRecording(recordingId);
|
||||
|
||||
// Then
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getRecordingId()).isEqualTo(recordingId);
|
||||
assertThat(response.getMeetingId()).isEqualTo("MEETING-001");
|
||||
assertThat(response.getSessionId()).isEqualTo("SESSION-001");
|
||||
assertThat(response.getStatus()).isEqualTo("READY");
|
||||
assertThat(response.getLanguage()).isEqualTo("ko-KR");
|
||||
|
||||
verify(recordingRepository).findById(recordingId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("존재하지 않는 녹음 조회 예외")
|
||||
void getRecording_NotFound() {
|
||||
// Given
|
||||
String recordingId = "REC-NOTFOUND-001";
|
||||
|
||||
when(recordingRepository.findById(recordingId)).thenReturn(Optional.empty());
|
||||
|
||||
// When & Then
|
||||
assertThatThrownBy(() -> recordingService.getRecording(recordingId))
|
||||
.isInstanceOf(BusinessException.class)
|
||||
.hasMessageContaining("녹음을 찾을 수 없습니다");
|
||||
|
||||
verify(recordingRepository).findById(recordingId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
package com.unicorn.hgzero.stt.service;
|
||||
|
||||
import com.unicorn.hgzero.common.exception.BusinessException;
|
||||
import com.unicorn.hgzero.stt.dto.TranscriptionDto;
|
||||
import com.unicorn.hgzero.stt.dto.TranscriptSegmentDto;
|
||||
import com.unicorn.hgzero.stt.event.publisher.EventPublisher;
|
||||
import com.unicorn.hgzero.stt.repository.entity.RecordingEntity;
|
||||
import com.unicorn.hgzero.stt.repository.entity.TranscriptSegmentEntity;
|
||||
import com.unicorn.hgzero.stt.repository.entity.TranscriptionEntity;
|
||||
import com.unicorn.hgzero.stt.repository.jpa.RecordingRepository;
|
||||
import com.unicorn.hgzero.stt.repository.jpa.TranscriptSegmentRepository;
|
||||
import com.unicorn.hgzero.stt.repository.jpa.TranscriptionRepository;
|
||||
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;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* 음성 변환 서비스 단위 테스트
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("음성 변환 서비스 테스트")
|
||||
class TranscriptionServiceTest {
|
||||
|
||||
@Mock
|
||||
private TranscriptionRepository transcriptionRepository;
|
||||
|
||||
@Mock
|
||||
private TranscriptSegmentRepository segmentRepository;
|
||||
|
||||
@Mock
|
||||
private RecordingRepository recordingRepository;
|
||||
|
||||
@Mock
|
||||
private EventPublisher eventPublisher;
|
||||
|
||||
@InjectMocks
|
||||
private TranscriptionServiceImpl transcriptionService;
|
||||
|
||||
private RecordingEntity recordingEntity;
|
||||
private TranscriptionDto.StreamRequest streamRequest;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
recordingEntity = RecordingEntity.builder()
|
||||
.recordingId("REC-20250123-001")
|
||||
.meetingId("MEETING-001")
|
||||
.sessionId("SESSION-001")
|
||||
.build();
|
||||
|
||||
streamRequest = TranscriptionDto.StreamRequest.builder()
|
||||
.recordingId("REC-20250123-001")
|
||||
.audioData("base64-encoded-audio-data")
|
||||
.timestamp(System.currentTimeMillis())
|
||||
.chunkIndex(1)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("실시간 음성 변환 성공")
|
||||
void processAudioStream_Success() {
|
||||
// Given
|
||||
when(recordingRepository.findById(anyString())).thenReturn(Optional.of(recordingEntity));
|
||||
when(segmentRepository.save(any(TranscriptSegmentEntity.class))).thenReturn(any());
|
||||
when(segmentRepository.getSpeakerStatisticsByRecording(anyString())).thenReturn(List.of());
|
||||
when(segmentRepository.countByRecordingId(anyString())).thenReturn(1L);
|
||||
when(recordingRepository.save(any(RecordingEntity.class))).thenReturn(recordingEntity);
|
||||
|
||||
// When
|
||||
TranscriptSegmentDto.Response response = transcriptionService.processAudioStream(streamRequest);
|
||||
|
||||
// Then
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getRecordingId()).isEqualTo("REC-20250123-001");
|
||||
assertThat(response.getText()).isNotEmpty();
|
||||
assertThat(response.getConfidence()).isGreaterThan(0.8);
|
||||
assertThat(response.getSpeakerId()).isNotEmpty();
|
||||
|
||||
verify(recordingRepository).findById("REC-20250123-001");
|
||||
verify(segmentRepository).save(any(TranscriptSegmentEntity.class));
|
||||
verify(eventPublisher).publishAsync(eq("transcription-events"), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("실시간 음성 변환 실패 - 녹음 없음")
|
||||
void processAudioStream_RecordingNotFound() {
|
||||
// Given
|
||||
when(recordingRepository.findById(anyString())).thenReturn(Optional.empty());
|
||||
|
||||
// When & Then
|
||||
assertThatThrownBy(() -> transcriptionService.processAudioStream(streamRequest))
|
||||
.isInstanceOf(BusinessException.class)
|
||||
.hasMessageContaining("녹음을 찾을 수 없습니다");
|
||||
|
||||
verify(recordingRepository).findById("REC-20250123-001");
|
||||
verify(segmentRepository, never()).save(any());
|
||||
verify(eventPublisher, never()).publishAsync(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("저신뢰도 세그먼트 경고 이벤트 발행")
|
||||
void processAudioStream_LowConfidenceWarning() {
|
||||
// Given
|
||||
when(recordingRepository.findById(anyString())).thenReturn(Optional.of(recordingEntity));
|
||||
when(segmentRepository.save(any(TranscriptSegmentEntity.class))).thenReturn(any());
|
||||
when(segmentRepository.getSpeakerStatisticsByRecording(anyString())).thenReturn(List.of());
|
||||
when(segmentRepository.countByRecordingId(anyString())).thenReturn(1L);
|
||||
when(recordingRepository.save(any(RecordingEntity.class))).thenReturn(recordingEntity);
|
||||
|
||||
// When
|
||||
TranscriptSegmentDto.Response response = transcriptionService.processAudioStream(streamRequest);
|
||||
|
||||
// Then
|
||||
// 저신뢰도인 경우 경고 이벤트도 발행되는지 확인
|
||||
if (response.getWarningFlag()) {
|
||||
verify(eventPublisher, times(2)).publishAsync(eq("transcription-events"), any());
|
||||
} else {
|
||||
verify(eventPublisher, times(1)).publishAsync(eq("transcription-events"), any());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("배치 음성 변환 작업 시작 성공")
|
||||
void transcribeAudioBatch_Success() {
|
||||
// Given
|
||||
TranscriptionDto.BatchRequest batchRequest = TranscriptionDto.BatchRequest.builder()
|
||||
.recordingId("REC-20250123-001")
|
||||
.callbackUrl("https://api.example.com/callback")
|
||||
.build();
|
||||
|
||||
MockMultipartFile audioFile = new MockMultipartFile(
|
||||
"audioFile", "test.wav", "audio/wav", "test audio content".getBytes()
|
||||
);
|
||||
|
||||
when(recordingRepository.findById(anyString())).thenReturn(Optional.of(recordingEntity));
|
||||
|
||||
// When
|
||||
TranscriptionDto.BatchResponse response = transcriptionService.transcribeAudioBatch(batchRequest, audioFile);
|
||||
|
||||
// Then
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getRecordingId()).isEqualTo("REC-20250123-001");
|
||||
assertThat(response.getStatus()).isEqualTo("PROCESSING");
|
||||
assertThat(response.getJobId()).isNotEmpty();
|
||||
assertThat(response.getCallbackUrl()).isEqualTo("https://api.example.com/callback");
|
||||
assertThat(response.getEstimatedCompletionTime()).isAfter(LocalDateTime.now());
|
||||
|
||||
verify(recordingRepository).findById("REC-20250123-001");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("배치 변환 완료 콜백 처리 성공")
|
||||
void processBatchCallback_Success() {
|
||||
// Given
|
||||
List<TranscriptSegmentDto.Detail> segments = List.of(
|
||||
TranscriptSegmentDto.Detail.builder()
|
||||
.transcriptId("TRS-001")
|
||||
.text("안녕하세요")
|
||||
.speakerId("SPK-001")
|
||||
.speakerName("화자-001")
|
||||
.timestamp(System.currentTimeMillis())
|
||||
.duration(2.5)
|
||||
.confidence(0.95)
|
||||
.build(),
|
||||
TranscriptSegmentDto.Detail.builder()
|
||||
.transcriptId("TRS-002")
|
||||
.text("회의를 시작하겠습니다")
|
||||
.speakerId("SPK-002")
|
||||
.speakerName("화자-002")
|
||||
.timestamp(System.currentTimeMillis() + 3000)
|
||||
.duration(3.2)
|
||||
.confidence(0.92)
|
||||
.build()
|
||||
);
|
||||
|
||||
TranscriptionDto.BatchCallbackRequest callbackRequest = TranscriptionDto.BatchCallbackRequest.builder()
|
||||
.jobId("JOB-20250123-001")
|
||||
.status("COMPLETED")
|
||||
.segments(segments)
|
||||
.build();
|
||||
|
||||
when(segmentRepository.save(any(TranscriptSegmentEntity.class))).thenReturn(any());
|
||||
when(transcriptionRepository.save(any(TranscriptionEntity.class))).thenReturn(any());
|
||||
|
||||
// When
|
||||
TranscriptionDto.CompleteResponse response = transcriptionService.processBatchCallback(callbackRequest);
|
||||
|
||||
// Then
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getJobId()).isEqualTo("JOB-20250123-001");
|
||||
assertThat(response.getStatus()).isEqualTo("COMPLETED");
|
||||
assertThat(response.getSegmentCount()).isEqualTo(2);
|
||||
assertThat(response.getTotalDuration()).isEqualTo(5); // 2.5 + 3.2 반올림
|
||||
assertThat(response.getAverageConfidence()).isEqualTo(0.935); // (0.95 + 0.92) / 2
|
||||
|
||||
verify(segmentRepository, times(2)).save(any(TranscriptSegmentEntity.class));
|
||||
verify(transcriptionRepository).save(any(TranscriptionEntity.class));
|
||||
verify(eventPublisher).publishAsync(eq("transcription-events"), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("변환 결과 조회 성공")
|
||||
void getTranscription_Success() {
|
||||
// Given
|
||||
String recordingId = "REC-20250123-001";
|
||||
|
||||
TranscriptionEntity transcriptionEntity = TranscriptionEntity.builder()
|
||||
.transcriptId("TRS-FULL-001")
|
||||
.recordingId(recordingId)
|
||||
.fullText("안녕하세요, 회의를 시작하겠습니다.")
|
||||
.segmentCount(2)
|
||||
.totalDuration(300)
|
||||
.averageConfidence(0.92)
|
||||
.speakerCount(2)
|
||||
.build();
|
||||
|
||||
when(transcriptionRepository.findByRecordingId(recordingId))
|
||||
.thenReturn(Optional.of(transcriptionEntity));
|
||||
|
||||
// When
|
||||
TranscriptionDto.Response response = transcriptionService.getTranscription(recordingId, false, null);
|
||||
|
||||
// Then
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getRecordingId()).isEqualTo(recordingId);
|
||||
assertThat(response.getFullText()).isEqualTo("안녕하세요, 회의를 시작하겠습니다.");
|
||||
assertThat(response.getSegmentCount()).isEqualTo(2);
|
||||
assertThat(response.getTotalDuration()).isEqualTo(300);
|
||||
assertThat(response.getAverageConfidence()).isEqualTo(0.92);
|
||||
assertThat(response.getSpeakerCount()).isEqualTo(2);
|
||||
assertThat(response.getSegments()).isNull(); // includeSegments = false
|
||||
|
||||
verify(transcriptionRepository).findByRecordingId(recordingId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("세그먼트 포함 변환 결과 조회 성공")
|
||||
void getTranscription_WithSegments_Success() {
|
||||
// Given
|
||||
String recordingId = "REC-20250123-001";
|
||||
|
||||
TranscriptionEntity transcriptionEntity = TranscriptionEntity.builder()
|
||||
.transcriptId("TRS-FULL-001")
|
||||
.recordingId(recordingId)
|
||||
.fullText("안녕하세요, 회의를 시작하겠습니다.")
|
||||
.segmentCount(2)
|
||||
.totalDuration(300)
|
||||
.averageConfidence(0.92)
|
||||
.speakerCount(2)
|
||||
.build();
|
||||
|
||||
List<TranscriptSegmentEntity> segmentEntities = List.of(
|
||||
TranscriptSegmentEntity.builder()
|
||||
.segmentId("SEG-001")
|
||||
.recordingId(recordingId)
|
||||
.text("안녕하세요")
|
||||
.speakerId("SPK-001")
|
||||
.speakerName("화자-001")
|
||||
.timestamp(System.currentTimeMillis())
|
||||
.duration(2.5)
|
||||
.confidence(0.95)
|
||||
.build()
|
||||
);
|
||||
|
||||
when(transcriptionRepository.findByRecordingId(recordingId))
|
||||
.thenReturn(Optional.of(transcriptionEntity));
|
||||
when(segmentRepository.findByRecordingIdOrderByTimestamp(recordingId))
|
||||
.thenReturn(segmentEntities);
|
||||
|
||||
// When
|
||||
TranscriptionDto.Response response = transcriptionService.getTranscription(recordingId, true, null);
|
||||
|
||||
// Then
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getSegments()).isNotNull();
|
||||
assertThat(response.getSegments()).hasSize(1);
|
||||
assertThat(response.getSegments().get(0).getText()).isEqualTo("안녕하세요");
|
||||
|
||||
verify(transcriptionRepository).findByRecordingId(recordingId);
|
||||
verify(segmentRepository).findByRecordingIdOrderByTimestamp(recordingId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("변환 결과 조회 실패 - 결과 없음")
|
||||
void getTranscription_NotFound() {
|
||||
// Given
|
||||
String recordingId = "REC-NOTFOUND-001";
|
||||
|
||||
when(transcriptionRepository.findByRecordingId(recordingId)).thenReturn(Optional.empty());
|
||||
|
||||
// When & Then
|
||||
assertThatThrownBy(() -> transcriptionService.getTranscription(recordingId, false, null))
|
||||
.isInstanceOf(BusinessException.class)
|
||||
.hasMessageContaining("변환 결과를 찾을 수 없습니다");
|
||||
|
||||
verify(transcriptionRepository).findByRecordingId(recordingId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
# STT 서비스 테스트 설정
|
||||
spring:
|
||||
profiles:
|
||||
active: test
|
||||
|
||||
# 데이터베이스 설정 (H2 인메모리)
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb
|
||||
driver-class-name: org.h2.Driver
|
||||
username: sa
|
||||
password:
|
||||
|
||||
# JPA 설정
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: create-drop
|
||||
show-sql: true
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.H2Dialect
|
||||
format_sql: true
|
||||
|
||||
# Redis 설정 (임베디드)
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6370
|
||||
timeout: 2000ms
|
||||
|
||||
# JWT 설정
|
||||
security:
|
||||
jwt:
|
||||
secret: test-secret-key-for-jwt-token-generation-test
|
||||
expiration: 86400
|
||||
|
||||
# Azure 서비스 설정 (테스트용 더미)
|
||||
azure:
|
||||
speech:
|
||||
subscription-key: test-key
|
||||
region: koreacentral
|
||||
endpoint: https://test.cognitiveservices.azure.com/
|
||||
|
||||
storage:
|
||||
connection-string: DefaultEndpointsProtocol=https;AccountName=testaccount;AccountKey=testkey;EndpointSuffix=core.windows.net
|
||||
container-name: test-recordings
|
||||
|
||||
event-hubs:
|
||||
connection-string: Endpoint=sb://test-eventhub.servicebus.windows.net/;SharedAccessKeyName=test;SharedAccessKey=test
|
||||
consumer-group: test-group
|
||||
|
||||
# 로깅 설정
|
||||
logging:
|
||||
level:
|
||||
com.unicorn.hgzero.stt: DEBUG
|
||||
org.springframework.web: DEBUG
|
||||
org.hibernate.SQL: DEBUG
|
||||
Reference in New Issue
Block a user