Skip to content

Commit fd502ed

Browse files
committed
test: TTS 콜백 및 메세징 설정 테스트 추가
1 parent ee0912d commit fd502ed

6 files changed

Lines changed: 155 additions & 3 deletions

File tree

backend/src/test/java/com/stackup/stackup/common/messaging/RabbitMessagePublisherTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ private RabbitMqProperties rabbitMqProperties() {
9797
"core.callback.analysis",
9898
"core.callback.questions",
9999
"core.callback.feedback",
100-
"core.callback.voice"
100+
"core.callback.voice",
101+
"core.callback.tts"
101102
)
102103
),
103104
new RabbitMqProperties.RoutingKeyProperties(
@@ -111,6 +112,7 @@ private RabbitMqProperties rabbitMqProperties() {
111112
"callback.questions",
112113
"callback.feedback",
113114
"callback.voice",
115+
"callback.tts",
114116
"realtime.session.notify",
115117
"realtime.user.notify",
116118
"realtime.document.notify"

backend/src/test/java/com/stackup/stackup/common/messaging/RabbitMqConfigTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ void allWorkQueues_routeToDeadLetterExchange() {
3737
.containsEntry("x-dead-letter-routing-key", "dlq.core.callback.analysis");
3838
assertThat(config.coreCallbackQuestionsQueue().getArguments())
3939
.containsEntry("x-dead-letter-routing-key", "dlq.core.callback.questions");
40+
assertThat(config.coreCallbackTtsQueue().getArguments())
41+
.containsEntry("x-dead-letter-routing-key", "dlq.core.callback.tts");
4042
}
4143

4244
private RabbitMqProperties rabbitMqProperties() {
@@ -64,7 +66,8 @@ private RabbitMqProperties rabbitMqProperties() {
6466
"core.callback.analysis",
6567
"core.callback.questions",
6668
"core.callback.feedback",
67-
"core.callback.voice"
69+
"core.callback.voice",
70+
"core.callback.tts"
6871
)
6972
),
7073
new RabbitMqProperties.RoutingKeyProperties(
@@ -78,6 +81,7 @@ private RabbitMqProperties rabbitMqProperties() {
7881
"callback.questions",
7982
"callback.feedback",
8083
"callback.voice",
84+
"callback.tts",
8185
"realtime.session.notify",
8286
"realtime.user.notify",
8387
"realtime.document.notify"

backend/src/test/java/com/stackup/stackup/session/application/SessionFeedbackRequesterTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ private RabbitMqProperties.RoutingKeyProperties mockRoutingKeys() {
8484
"analyze.resume", "analyze.repository",
8585
"generate.questions", "generate.followup", "generate.feedback", "analyze.voice",
8686
"callback.analysis", "callback.questions", "callback.feedback", "callback.voice",
87+
"callback.tts",
8788
"realtime.session.notify", "realtime.user.notify", "realtime.document.notify");
8889
}
8990
}

backend/src/test/java/com/stackup/stackup/session/application/SessionQuestionsRequesterTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ private RabbitMqProperties.RoutingKeyProperties mockRoutingKeys() {
6363
"analyze.resume", "analyze.repository",
6464
"generate.questions", "generate.followup", "generate.feedback", "analyze.voice",
6565
"callback.analysis", "callback.questions", "callback.feedback", "callback.voice",
66+
"callback.tts",
6667
"realtime.session.notify", "realtime.user.notify", "realtime.document.notify");
6768
}
6869
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package com.stackup.stackup.session.application;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.mockito.ArgumentMatchers.any;
5+
import static org.mockito.ArgumentMatchers.eq;
6+
import static org.mockito.Mockito.never;
7+
import static org.mockito.Mockito.verify;
8+
import static org.mockito.Mockito.when;
9+
10+
import com.fasterxml.jackson.databind.ObjectMapper;
11+
import com.stackup.stackup.common.messaging.domain.ProcessedMessage;
12+
import com.stackup.stackup.common.messaging.domain.ProcessedMessageRepository;
13+
import com.stackup.stackup.common.sse.SseEventPublisher;
14+
import com.stackup.stackup.common.sse.SseEventType;
15+
import com.stackup.stackup.session.application.dto.MessageResult;
16+
import com.stackup.stackup.session.application.dto.TtsCallbackEnvelope;
17+
import com.stackup.stackup.session.application.dto.TtsCallbackPayload;
18+
import com.stackup.stackup.session.domain.InterviewMessage;
19+
import com.stackup.stackup.session.domain.InterviewMessageRepository;
20+
import com.stackup.stackup.session.domain.InterviewSession;
21+
import com.stackup.stackup.session.domain.JobCategory;
22+
import com.stackup.stackup.session.domain.SessionMode;
23+
import com.stackup.stackup.session.domain.TtsStatus;
24+
import com.stackup.stackup.user.domain.User;
25+
import java.util.Optional;
26+
import org.junit.jupiter.api.Test;
27+
import org.junit.jupiter.api.extension.ExtendWith;
28+
import org.mockito.InjectMocks;
29+
import org.mockito.Mock;
30+
import org.mockito.junit.jupiter.MockitoExtension;
31+
import org.springframework.test.util.ReflectionTestUtils;
32+
33+
@ExtendWith(MockitoExtension.class)
34+
class TtsCallbackServiceTest {
35+
36+
private static final ObjectMapper JSON = new ObjectMapper();
37+
38+
@Mock InterviewMessageRepository messageRepository;
39+
@Mock ProcessedMessageRepository processedMessageRepository;
40+
@Mock SseEventPublisher sseEventPublisher;
41+
@InjectMocks TtsCallbackService service;
42+
43+
@Test
44+
void apply_successStoresTtsFieldsWithoutChangingMessageStatus() {
45+
InterviewSession session = sessionFixture(50L);
46+
InterviewMessage question = InterviewMessage.interviewer(session, 1, "Q?");
47+
ReflectionTestUtils.setField(question, "id", 100L);
48+
49+
when(processedMessageRepository.existsById("tts-1")).thenReturn(false);
50+
when(messageRepository.findById(100L)).thenReturn(Optional.of(question));
51+
52+
service.apply(envelope("tts-1", new TtsCallbackPayload(
53+
50L, 100L, "SUCCEEDED", "interview/tts/50/100.mp3", 2.4, null
54+
)));
55+
56+
assertThat(question.getTtsStatus()).isEqualTo(TtsStatus.SUCCEEDED);
57+
assertThat(question.getTtsAudioPath()).isEqualTo("interview/tts/50/100.mp3");
58+
assertThat(question.getTtsDurationSec()).isEqualTo(2.4);
59+
assertThat(MessageResult.of(question).ttsAudioPath()).isEqualTo("interview/tts/50/100.mp3");
60+
verify(sseEventPublisher).publishToSession(eq(50L), eq(SseEventType.SESSION_MESSAGE), any());
61+
verify(sseEventPublisher).publishToUser(eq(1L), eq(SseEventType.SESSION_MESSAGE), any());
62+
verify(processedMessageRepository).save(any(ProcessedMessage.class));
63+
}
64+
65+
@Test
66+
void apply_failedMarksOnlyTtsFailedAndLeavesQuestionUsable() {
67+
InterviewSession session = sessionFixture(50L);
68+
InterviewMessage question = InterviewMessage.interviewer(session, 1, "Q?");
69+
ReflectionTestUtils.setField(question, "id", 100L);
70+
71+
when(processedMessageRepository.existsById("tts-fail")).thenReturn(false);
72+
when(messageRepository.findById(100L)).thenReturn(Optional.of(question));
73+
74+
service.apply(envelope("tts-fail", new TtsCallbackPayload(
75+
50L, 100L, "FAILED", null, null, "TTS_PROVIDER_DOWN"
76+
)));
77+
78+
assertThat(question.getTtsStatus()).isEqualTo(TtsStatus.FAILED);
79+
assertThat(question.getContent()).isEqualTo("Q?");
80+
assertThat(question.getTtsAudioPath()).isNull();
81+
verify(sseEventPublisher).publishToSession(eq(50L), eq(SseEventType.SESSION_MESSAGE), any());
82+
verify(processedMessageRepository).save(any(ProcessedMessage.class));
83+
}
84+
85+
@Test
86+
void apply_ignoresIntervieweeMessages() {
87+
InterviewSession session = sessionFixture(50L);
88+
InterviewMessage answer = InterviewMessage.interviewee(session, 2, "A", null, null);
89+
ReflectionTestUtils.setField(answer, "id", 200L);
90+
91+
when(processedMessageRepository.existsById("tts-answer")).thenReturn(false);
92+
when(messageRepository.findById(200L)).thenReturn(Optional.of(answer));
93+
94+
service.apply(envelope("tts-answer", new TtsCallbackPayload(
95+
50L, 200L, "SUCCEEDED", "interview/tts/50/200.mp3", 1.1, null
96+
)));
97+
98+
assertThat(answer.getTtsStatus()).isEqualTo(TtsStatus.NOT_REQUESTED);
99+
verify(sseEventPublisher, never()).publishToSession(any(), any(), any());
100+
verify(processedMessageRepository).save(any(ProcessedMessage.class));
101+
}
102+
103+
@Test
104+
void apply_skipsDuplicateMessageId() {
105+
when(processedMessageRepository.existsById("dup")).thenReturn(true);
106+
107+
service.apply(envelope("dup", new TtsCallbackPayload(
108+
50L, 100L, "SUCCEEDED", "interview/tts/50/100.mp3", 2.4, null
109+
)));
110+
111+
verify(messageRepository, never()).findById(any());
112+
}
113+
114+
@Test
115+
void payload_acceptsAudioUrlAliasAndLowercaseStatus() throws Exception {
116+
TtsCallbackPayload payload = JSON.readValue("""
117+
{
118+
"sessionId": 50,
119+
"messageId": 100,
120+
"status": "succeeded",
121+
"audioUrl": "interview/tts/50/100.mp3",
122+
"durationSec": 2.4
123+
}
124+
""", TtsCallbackPayload.class);
125+
126+
assertThat(payload.isSucceeded()).isTrue();
127+
assertThat(payload.audioKey()).isEqualTo("interview/tts/50/100.mp3");
128+
}
129+
130+
private TtsCallbackEnvelope envelope(String messageId, TtsCallbackPayload payload) {
131+
return new TtsCallbackEnvelope(messageId, "callback.tts", "1", "t",
132+
null, "ai", payload, null);
133+
}
134+
135+
private InterviewSession sessionFixture(Long id) {
136+
User user = User.createGithubUser(1L, "u", null, null, "t");
137+
ReflectionTestUtils.setField(user, "id", 1L);
138+
InterviewSession s = InterviewSession.create(user, "t", null, SessionMode.TECHNICAL, JobCategory.BACKEND, 5, 30);
139+
ReflectionTestUtils.setField(s, "id", id);
140+
return s;
141+
}
142+
}

backend/src/test/java/com/stackup/stackup/session/application/VoiceAnswerUploadServiceTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,8 @@ private RabbitMqProperties rabbitMqProperties() {
216216
"core.callback.analysis",
217217
"core.callback.questions",
218218
"core.callback.feedback",
219-
"core.callback.voice"
219+
"core.callback.voice",
220+
"core.callback.tts"
220221
)),
221222
new RabbitMqProperties.RoutingKeyProperties(
222223
"analyze.resume",
@@ -229,6 +230,7 @@ private RabbitMqProperties rabbitMqProperties() {
229230
"callback.questions",
230231
"callback.feedback",
231232
"callback.voice",
233+
"callback.tts",
232234
"session.notify",
233235
"realtime.user.notify",
234236
"realtime.document.notify"

0 commit comments

Comments
 (0)