테스트·문제 해결¶
검증 계층¶
단위·통합 테스트¶
순수 Go 경로는 기본 테스트로, 실제 Opus encoder 경로는 opusenc tag와 libopus가 있는 환경에서
검증합니다.
기본 build는 SDP, 인증 선행, ICE, 이벤트, resampler와 lifecycle을 폭넓게 검사하지만 실제 아웃바운드 Opus encode까지 성공한다는 보장은 없습니다. production image와 같은 cgo/libopus 환경에서 tag 빌드 및 테스트가 반드시 통과해야 합니다.
로컬 E2E¶
examples/local-e2e harness는 mock /sohri, Gateway, Pion probe를 함께 실행해 다음을 검증합니다.
- 비-트리클 offer/answer와 세션 WSS
session.created데이터채널 왕복- TTS raw PCM/WAV → Opus 트랙
sohri.tts_stream_start/end/aborted순서와 상관 ID
Probe에는 유효한 진폭의 sine wave를 사용합니다. 모두 0인 입력이나 channel 설정이 맞지 않는 fixture는 파이프라인 결함처럼 보이는 무음 결과를 만들 수 있습니다.
부하 테스트¶
sohri-load는 실제 session contract를 따르는 부하 생성기입니다. 단순 SDP 성공률 외에 다음을
함께 수집해야 합니다.
- session setup 단계별 실패율과 연결 시간
- RTP packet loss, RTCP RTT/jitter
- DataChannel event 순서
tts_duck_appliedlatency와tts_playback_statetrue/false 균형- queue full·stale generation·half-duplex drop 비율
- 종료 후 active session 회수
증상별 진단¶
SDP POST가 401¶
Authorization: Bearer형식과 토큰 앞뒤 공백을 확인합니다.- API key는
sr_, JWT는eyJ...3분절 형태인지 확인합니다. - Gateway의 형태 필터인지
/sohri의401/403인지 setup label과 bridge 로그로 구분합니다. - JWT issuer·서명키·만료, API key workspace 상태는 socket-backend에서 확인합니다.
Gateway는 credential을 자체 검증하지 않으며 백엔드 거절을 401로 매핑합니다.
브라우저에서 네트워크 오류¶
curl은 성공하지만 브라우저만 실패하면 CORS를 먼저 확인합니다.
- Origin의 scheme/host/port가
CORS_ALLOWED_ORIGINS와 정확히 일치하는가 - OPTIONS가
204인가;403이면 Origin/요청 메서드 오류,405이면 CORS 비활성 - 오류 POST에도
Access-Control-Allow-Origin이 있는가
/v1/realtime/client_secrets는 제거된 경로이므로 404가 정상입니다.
answer는 왔지만 ICE가 연결되지 않음¶
- 양쪽 SDP에 candidate가 포함되었는지 확인합니다. trickle candidate를 나중에 보낼 API는 없습니다.
- answer의 host/srflx IP가 외부에서 도달 가능한지 확인합니다.
NAT_1TO1_IP, UDP 포트 범위, LB backend와 방화벽 범위를 대조합니다.- 직접 UDP가 차단된 망이면 클라이언트·Gateway에 실제 relay candidate가 생성되는 coturn 구성을 검증합니다. coturn 배포만으로는 충분하지 않습니다.
연결됐지만 마이크가 백엔드에 도착하지 않음¶
- remote audio track codec이 Opus 48 kHz인지 확인합니다.
- inbound RTP/decoded/PCM byte 메트릭을 차례로 비교합니다.
BARGE_IN_ENABLED=false이고 TTS가 재생 중이면 입력 폐기는 정상입니다.- 100 ms에 못 미치는 마지막 PCM 조각은 전송하지 않습니다.
- packet loss, empty payload, timestamp jump와 decode error를 확인합니다.
TTS 텍스트는 오지만 소리가 없음¶
- production binary가
-tags opusenc와 libopus로 빌드되었는지 확인합니다. - WAV header 또는
tts_stream_start.format이 mono s16le, 16/24/48 kHz인지 확인합니다. - 활성 response가 있었는지와
tts_discarded_total{reason}을 봅니다. - stale generation, queue full, pause/duck gain, half-duplex 상태를 확인합니다.
tts_playback_state{active:true}가 없으면 첫 가청 frame까지 도달하지 못한 것입니다.
음성이 끊기거나 이전 답변이 다시 들림¶
stale_generationdrop이 없다면 cancel과 enqueue의 세대 상관관계를 확인합니다.- streaming 청크 사이에서 playback state가 반복되면 true/false를 청크마다 내는 회귀입니다.
- queue full과 deadline lateness가 증가하면 backend burst와 20 ms pacer 처리량을 비교합니다.
- underrun gap은 OpenAI PCM 공급 지연이며 삽입 무음 누계를 함께 확인합니다.
barge-in이 너무 느리거나 봇이 복귀하지 않음¶
speech_candidate도착 자체가 늦으면 Gateway 전단 EPD 지연입니다.- duck ACK latency가 1초 이내인지 확인합니다.
- 취소된 후보의
duck_duration{reason="cancelled"}만 최소 hold 검증에 사용합니다. pause_duration{end_reason="max"}는 pause 고착 또는 긴 STT 지연 조사 신호입니다.- 확정·재생 종료로 짧게 끝난 duck은 정상이며 최소 hold 위반으로 보면 안 됩니다.
일시적 망 전환에서 세션이 종료됨¶
Peer 상태와 PEER_DISCONNECTED_GRACE를 확인합니다. 기본 10초 안에 Connected로 복귀하면
세션을 유지하지만 Failed·Closed는 즉시 종료합니다. 현재 API에는 ICE restart가 없으므로
유예를 넘긴 연결은 새 SDP 세션을 만들어야 합니다.
배포 전 최소 승인 기준¶
- 유효/무효 credential에서 인증 선행 테스트 통과
- 실제 외부 네트워크에서 host/srflx와 필요 시 relay ICE 성공
- 마이크 3,200 B/100 ms WSS framing 확인
- 16/24/48 kHz 샘플의 Opus 20 ms playout 확인
- cancel·duck·pause·half-duplex와 의도적 drop 확인
- disconnected 10초 복구와 shutdown terminal event 확인
- 부하 종료 후 active session, goroutine, UDP socket 회수 확인