+
    _*j  c                      R t ^ RIHt ^ RIt^ RIHu Ht ^ RI	t	^ RI
t
^ RIHt ]
P                  R 4       tRR R lltR tR tR	 tR
 tR tR tR tR# )u  BDD tests for runtime/api/voice.py.

Given: compiled test-persona fixture + FakeProvider + FakeSTT (FAKE_PROVIDER=1)
When:  WebSocket voice conversation flow
Then:  correct protocol frames in correct order

All offline — no model downloads, no real API calls.
)annotationsN)
TestClientc                ~    VP                  R R4       VP                  R\        V 4      4       ^ RIHp \	        V4      # )FAKE_PROVIDER1MENTORCORE_PERSONAS_ROOT)app)setenvstrruntime.api.appr   r   )examples_persona_rootmonkeypatchr   s   && E/Users/jameslopez/projects/MentorCore/tests/runtime/test_voice_api.pyclientr      s6    ,137L3MN#c?    c                    V ^8  d   QhRRRR/# )   audio_bytesbytesreturnzlist[dict | bytes] )formats   "r   __annotate__r      s      E >P r   c                   V P                  \        P                  ! RR/4      4       V P                  V4       V P                  \        P                  ! RR/4      4       . p V P	                  4       pVP                  R4      '       d   VP                  VR,          4       KB  VP                  R4      '       g   K[  \        P                  ! VR,          4      pVP                  V4       VP                  R4      R8X  g   K   V# )z5Send one voice turn and collect all server responses.typestartstopr   textdone)	send_textjsondumps
send_bytesreceivegetappendloads)wsr   framesdatamsgs   &&   r   _ws_voice_turnr*      s    LLVW-./MM+LLVV,-.!#F
zz|88GMM$w-(XXf**T&\*CMM#wwv&(Mr   c           	     `   V P                  R4      ;_uu_ 4       p\        P                  ! VP                  4       4      pVR,          pRqCV8H  qU'       gy   \        P
                  ! RV3RW434      R\        P                  ! V4      R\        P                  ! V4      /,          pRRV/,          p\        \        P                  ! V4      4      hR;p;rTR	q3V9   qU'       g   \        P
                  ! RV3RW234      R\        P                  ! V4      R
R\        P                  ! 4       9   g   \        P                  ! V4      '       d   \        P                  ! V4      MR/,          pRRV/,          p	\        \        P                  ! V	4      4      hR;r5RRR4       R#   + '       g   i     R# ; i)zDGiven: test-persona. When: WS connect. Then: session frame received./api/voice/test-personar   sessionpy1py4assert %(py6)spy6N
session_idpy3r)   zassert %(py5)spy5==z%(py1)s == %(py4)sin)z%(py1)s in %(py3)s)websocket_connectr   r%   receive_text
@pytest_ar_call_reprcompare	_safereprAssertionError_format_explanation@py_builtinslocals_should_repr_global_name)
r   r&   r)   @py_assert0@py_assert3@py_assert2@py_format5@py_format7@py_format4@py_format6s
   &         r   (test_voice_ws_connects_and_sends_sessionrK   1   s    		!	!";	<	<jj*+6{'i'i'''''{'''{'''i''''''''"s"""""|"""|""""""s""""s"""""""" 
=	<	<	<s   D+FAFF-	c           	        V P                  R4      ;_uu_ 4       p\        P                  ! VP                  4       4      pVR,          pRqCV8H  qU'       gy   \        P
                  ! R	V3R
W434      R\        P                  ! V4      R\        P                  ! V4      /,          pRRV/,          p\        \        P                  ! V4      4      hR;p;rTRRR4       R#   + '       g   i     R# ; i)z;Given: unknown slug. When: WS connect. Then: error + close.z/api/voice/nonexistentr   errorr.   r/   r0   r1   Nr5   r7   )	r:   r   r%   r;   r<   r=   r>   r?   r@   )r   r&   r)   rD   rE   rF   rG   rH   s   &       r   $test_voice_ws_unknown_persona_closesrN   9   s    		!	!":	;	;rjj*+6{%g%g%%%%%{%%%{%%%g%%%%%%%% 
<	;	;	;s   B8CC-	c                   V P                  R4      ;_uu_ 4       pVP                  4        \        V4      pV Uu. uF5  p\        V\        4      '       g   K  VP                  R4      R8X  g   K3  VNK7  	  pp\        V4      p^qeV8H  qw'       Eg   \        P                  ! RV3RWV34      RR\        P                  ! 4       9   g!   \        P                  ! \        4      '       d   \        P                  ! \        4      MRRR\        P                  ! 4       9   g   \        P                  ! V4      '       d   \        P                  ! V4      MRR\        P                  ! V4      R	\        P                  ! V4      /,          pR
RV/,          p	\        \        P                  ! V	4      4      hR;p;rvRq^ ,          qV9   qU'       gy   \        P                  ! RV3RW34      R\        P                  ! V
4      R\        P                  ! V4      /,          pRR	V/,          p\        \        P                  ! V4      4      hR;p
;r[RRR4       R# u upi   + '       g   i     R# ; i)z8Given: audio bytes. Then: transcript frame before audio.r,   r   
transcriptpy0lenr.   transcript_framesr3   r1   assert %(py8)spy8Nr   r/   r0   r5   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sr8   )z%(py1)s in %(py4)sr:   r;   r*   
isinstancedictr#   rR   r<   r=   rA   rB   rC   r>   r?   r@   )r   r&   r'   frS   rF   @py_assert5@py_assert4rH   @py_format9rD   rE   rG   s   &            r    test_voice_turn_sends_transcriptr^   C   sk   		!	!";	<	<
#
!At!4Av,9VAAv 	 
 $%*******%******s****s*******$****$***%***********-1--------v---v------------ 
=	<
 
=	<	<s7    III/I5A/I%AI7DIII#	c                x   V P                  R4      ;_uu_ 4       pVP                  4        \        V4      pV Uu. uF  p\        V\        4      '       g   K  VNK  	  pp\        V4      p^ qeV8  qw'       Eg   \        P                  ! RV3RWV34      RR\        P                  ! 4       9   g!   \        P                  ! \
        4      '       d   \        P                  ! \
        4      MRRR\        P                  ! 4       9   g   \        P                  ! V4      '       d   \        P                  ! V4      MRR\        P                  ! V4      R\        P                  ! V4      /,          pRR	V/,          p	\        \        P                  ! V	4      4      hR
;p;rvR
R
R
4       R
# u upi   + '       g   i     R
# ; i)zAGiven: non-empty transcript. Then: at least one binary PCM frame.r,   rQ   rR   r.   binary_framesr3   r1   rT   rU   N)>)z/%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} > %(py6)s)r:   r;   r*   rX   r   rR   r<   r=   rA   rB   rC   r>   r?   r@   )
r   r&   r'   rZ   r`   rF   r[   r\   rH   r]   s
   &         r   "test_voice_turn_sends_binary_audiorb   O   s    		!	!";	<	<
#$*CFqjE.BFC=!%A%A%%%%%!%%%%%%s%%%%s%%%%%%%=%%%%=%%%!%%%A%%%%%%%%	 
=	< D 
=	<	<s1    F(F#F#A/F(AF(A:F(#F((F9	c           	     8   V P                  R4      ;_uu_ 4       pVP                  4        \        V4      pV Uu. uF  p\        V\        4      '       g   K  VNK  	  ppVR	,          R,          pRqeV8H  qw'       gy   \
        P                  ! R
V3RWV34      R\
        P                  ! V4      R\
        P                  ! V4      /,          pRRV/,          p	\        \
        P                  ! V	4      4      hR;p;rvRRR4       R# u upi   + '       g   i     R# ; i)z&Then: last text frame is {type: done}.r,   r   r   r.   r/   r0   r1   Nr5   r7   )
r:   r;   r*   rX   rY   r<   r=   r>   r?   r@   )
r   r&   r'   rZ   text_framesrD   rE   rF   rG   rH   s
   &         r   test_voice_turn_ends_with_donerf   X   s    		!	!";	<	<
#"(@&QJq$,?qq&@2v&0&0&00000&000&000&00000000	 
=	< A 
=	<	<s#    DDDBDDD	c           
     T   V P                  R4      ;_uu_ 4       pVP                  4        \        V4      p\        V4       UUu. uF   w  r4\	        V\
        4      '       g   K  W43NK"  	  ppp\        R V 4       4      p\        R V 4       4      pWg8  q'       g   \        P                  ! RV3RWg34      RR\        P                  ! 4       9   g   \        P                  ! V4      '       d   \        P                  ! V4      MRRR\        P                  ! 4       9   g   \        P                  ! V4      '       d   \        P                  ! V4      MR/,          p	RR	V	/,          p
\        \        P                  ! V
4      4      hR
pR
R
R
4       R
# u uppi   + '       g   i     R
# ; i)u4   Protocol order: transcript → (binary…) → done.r,   c              3  Z   "   T F!  w  rVP                  R 4      R8X  g   K  Vx  K#  	  R# 5i)r   rP   Nr#   .0irZ   s   &  r   	<genexpr>)test_voice_frame_order.<locals>.<genexpr>h   s#     YKDA155=L;XaaK   +
+c              3  Z   "   T F!  w  rVP                  R 4      R8X  g   K  Vx  K#  	  R# 5i)r   r   Nri   rj   s   &  r   rm   rn   i   s#     MkdaQUU6]f5Lkro   rQ   transcript_idxpy2done_idxzassert %(py4)sr/   N)<)z%(py0)s < %(py2)s)r:   r;   r*   	enumeraterX   rY   nextr<   r=   rA   rB   rC   r>   r?   r@   )r   r&   r'   rl   rZ   re   rq   rs   @py_assert1@py_format3rG   s   &          r   test_voice_frame_orderry   a   s   		!	!";	<	<
#*3F*;S*;$!z!T?Rvv*;SYKYYMkMM(((((~((((((~((((~((((((((((((((((((( 
=	< T	 
=	<	<s2   *FF
#F
*BF-AF;AFFF'	c                   V P                  R4      ;_uu_ 4       pVP                  4        \        V4       \        V4      pV Uu. uF5  p\        V\        4      '       g   K  VP                  R4      R8X  g   K3  VNK7  	  pp\        V4      p^qeV8H  qw'       Eg   \        P                  ! RV3RWV34      RR\        P                  ! 4       9   g!   \        P                  ! \        4      '       d   \        P                  ! \        4      MRRR\        P                  ! 4       9   g   \        P                  ! V4      '       d   \        P                  ! V4      MRR\        P                  ! V4      R	\        P                  ! V4      /,          pR
RV/,          p	\        \        P                  ! V	4      4      hR;p;rvRRR4       R# u upi   + '       g   i     R# ; i)z6Two turns in same WS connection share session history.r,   r   r   rQ   rR   r.   done_framesr3   r1   rT   rU   Nr5   rV   rW   )
r   r&   frames2rZ   r{   rF   r[   r\   rH   r]   s
   &         r   (test_voice_multiturn_accumulates_sessionr}   n   s   		!	!";	<	<
r $")]'QZ4-@qQUU6]V\E\qq'];$1$1$$$$$$$$$$$s$$$$s$$$$$$$;$$$$;$$$$$$1$$$$$$$$ 
=	<
 ^ 
=	<	<s8   +GG"G:G A/G0AGA:GGG	)sd                                                                                                       )__doc__
__future__r   builtinsrA   _pytest.assertion.rewrite	assertionrewriter<   r   pytestfastapi.testclientr   fixturer   r*   rK   rN   r^   rb   rf   ry   r}   r   r   r   <module>r      s\    #     )  ,#&	.&1
)%r   