
    ui                       d Z ddlmZ ddlZddlmc mZ ddl	Z	ddl
mZ ddlmZ ddlmZmZmZ ddlZ ee      j)                         j*                  d   Zedz  Z ee      e	j2                  vr"e	j2                  j5                  d ee             dd	Zd
e	j8                  vr e       e	j8                  d
<   ddlmZmZ dddZ ddZ! G d d      Z" G d d      Z# G d d      Z$y)zaTests for TTSClient.

All aiohttp calls are mocked so these tests run without aiohttp installed.
    )annotationsN)Path)
ModuleType)	AsyncMock	MagicMockpatch   srcc                 `    t        d      }  G d d      } G d d      }|| _        || _        | S )z<Build a minimal aiohttp stub sufficient for TTSClient tests.aiohttpc                      e Zd ZddZy))_make_fake_aiohttp.<locals>.ClientTimeoutc                :    | j                   j                  |       y )N)__dict__update)selfkwargss     R/home/nelsen/Projects/kognitive/orchestrator/src/services/tests/test_tts_client.py__init__z2_make_fake_aiohttp.<locals>.ClientTimeout.__init__   s    MM  (    N)r   objectreturnNone)__name__
__module____qualname__r    r   r   ClientTimeoutr      s    	)r   r   c                      e Zd ZdZy))_make_fake_aiohttp.<locals>.ClientSessionz!Stub replaced per-test via patch.N)r   r   r   __doc__r   r   r   ClientSessionr    !   s    /r   r"   )r   r   r"   )faker   r"   s      r   _make_fake_aiohttpr$      s7    i D) )0 0 'D&DKr   r   )	TTSClient	TTSConfigc                    t               }| |_        t        |      |_        d }t               |_        ||j                  _        t        |      |_        t        d      |_        |S )zHBuild an async context manager mock that looks like an aiohttp response.return_valuec                   K   d y w)N
   audio_datar   r   r   r   	_iter_anyz&_make_mock_response.<locals>._iter_any8   s     s   	F)r   statusr   textcontentiter_any
__aenter__	__aexit__)r-   bodyrespr,   s       r   _make_mock_responser5   1   sW    ;DDKt,DI ;DL%DLL  T2DOE2DNKr   c                Z    t               }t        |       |_        t               |_        |S )zFBuild a mock aiohttp.ClientSession whose post() returns mock_response.r(   )r   postr   close)mock_responsesessions     r   _make_mock_sessionr;   D   s#    kG-8GLKGMNr   c                  0    e Zd ZdZddZddZddZddZy)	TestTTSConfigz-TTSConfig default values and field behaviour.c                   |j                  dd       t               }|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }d	d
|iz  }t        t        j                  |            d x}x}}y )NTTS_ENDPOINTF)raisingz!http://kluster.klass.dev:8200/tts==z0%(py2)s
{%(py2)s = %(py0)s.endpoint
} == %(py5)sconfigpy0py2py5assert %(py7)spy7)delenvr&   endpoint
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationr   monkeypatchrD   @py_assert1@py_assert4@py_assert3@py_format6@py_format8s           r   )test_default_endpoint_uses_env_or_klusterz7TestTTSConfig.test_default_endpoint_uses_env_or_klusterR   s    >59E"EE"EEEEE"EEEEEEEvEEEvEEEEEE"EEEEEEEEr   c                   |j                  dd       t               }|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y )	Nr?   zhttp://myhost:9000/ttsrA   rC   rD   rE   rI   rJ   )setenvr&   rL   rM   rN   rO   rP   rQ   rR   rS   rT   rU   s           r   test_endpoint_from_envz$TestTTSConfig.test_endpoint_from_envW   s    >+CD:"::":::::":::::::v:::v::::::"::::::::r   c                   t               }|j                  }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y )N   rA   z1%(py2)s
{%(py2)s = %(py0)s.timeout_s
} == %(py5)srD   rE   rI   rJ   )
r&   	timeout_srM   rN   rO   rP   rQ   rR   rS   rT   r   rD   rW   rX   rY   rZ   r[   s          r   test_default_timeoutz"TestTTSConfig.test_default_timeout\   s{    %2%2%%%%2%%%%%%v%%%v%%%%%%2%%%%%%%r   c                >   t        dd      }|j                  }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            d x}x}}|j                  }d}||k(  }|st        j                  d|fd
||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            d x}x}}y )Nzhttp://custom:1234/tts   )rL   rc   rA   rC   rD   rE   rI   rJ   rb   )r&   rL   rM   rN   rO   rP   rQ   rR   rS   rT   rc   rd   s          r   test_custom_valuesz TestTTSConfig.test_custom_values`   s    $<K:"::":::::":::::::v:::v::::::"::::::::%2%2%%%%2%%%%%%v%%%v%%%%%%2%%%%%%%r   N)rV   zpytest.MonkeyPatchr   r   r   r   )r   r   r   r!   r\   r_   re   rh   r   r   r   r=   r=   O   s    7F
;
&&r   r=   c                     e Zd ZdZej
                  j                  d
d       Zej
                  j                  d
d       Zej
                  j                  d
d       Z	ej
                  j                  d
d       Z
ej
                  j                  d
d       Zej
                  j                  d
d       Zej
                  j                  d
d       Zy	)TestTTSClientSpeakzTTSClient.speak() behaviour.c                  K   t        d      }t        |      }t        d      }t        |      |_        |j                  d       d{   }t        |t              }|sddt        j                         v st        j                  t              rt        j                  t              ndd	t        j                         v st        j                  |      rt        j                  |      nd	d
t        j                         v st        j                  t              rt        j                  t              nd
t        j                  |      dz  }t        t        j                  |            d}d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}}|j                  j                   j#                          |j                  j                   j$                  }|j&                  d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j(                  d   }ddd}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y7 'w)u;   HTTP 200 with streaming body → speak() returns WAV bytes.http://fake:8200/ttsrL      r-   Hello worldN5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstanceresultbytesrF   py1rG   py4r+   rA   )z%(py0)s == %(py3)srF   py3assert %(py5)srH   r   z%(py1)s == %(py4)srw   rx   assert %(py6)spy6jsoni>  )r.   sample_rate)r&   r%   r5   r;   _sessionspeakrs   ru   rO   rP   rM   rQ   rR   rS   rT   rN   r7   assert_called_once	call_argsargsr   )r   rD   client	mock_resprt   rY   @py_format5@py_assert2rW   @py_format4rZ   r   @py_assert0@py_format7s                 r   $test_speak_success_returns_wav_bytesz7TestTTSClientSpeak.test_speak_success_returns_wav_bytesi   s     $:;6"'s3	,Y7||M22&%((((((((z(((z((((((&(((&((((((%(((%((((((((((&&v&&&&v&&&&&&v&&&v&&&&&&&&&&//1OO((22	~~a :$:: $::::: $:::: :::$::::::::'XMRW+XX'+XXXXX'+XXXX'XXX+XXXXXXXX 3s   AM4
M1L'M4c                6  K   t        d      }t        |      }t        t        d            |_        |j                  dd       d {   }t        |t              }|sdd	t        j                         v st        j                  t              rt        j                  t              nd	d
t        j                         v st        j                  |      rt        j                  |      nd
dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            d }|j                  j                  j                   \  }}|d   d   }d}||k(  }	|	slt        j"                  d|	fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }
t        t        j                  |
            d x}x}	}y 7 ̭w)Nrm   rn   ro   rp   Testi"V  )r   rr   rs   rt   ru   rv   r   r   rA   r|   r}   r~   r   )r&   r%   r;   r5   r   r   rs   ru   rO   rP   rM   rQ   rR   rS   rT   r7   r   rN   )r   rD   r   rt   rY   r   _call_kwargsr   r   r   s              r   %test_speak_success_custom_sample_ratez8TestTTSClientSpeak.test_speak_success_custom_sample_rate{   s?    $:;6",-@-LM||F|>>&%((((((((z(((z((((((&(((&((((((%(((%((((((((((--77;6"=1:U:1U::::1U:::1:::U:::::::	 ?s   AH
HGHc                  K   t        d      }t        |      }t        t        dd            |_        |j                  d       d{   }d}||u }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y7 w)u*   Non-200 response → speak() returns None.rm   rn   i  zInternal Server Errorr-   r3   rq   Nisz%(py0)s is %(py3)srt   ry   r{   rH   r&   r%   r;   r5   r   r   rM   rN   rO   rP   rQ   rR   rS   rT   r   rD   r   rt   r   rW   r   rZ   s           r   "test_speak_server_500_returns_nonez5TestTTSClientSpeak.test_speak_server_500_returns_none   s      $:;6",s1HI
 ||M22v~vvv 3   AC6	C4
B+C6c                  K   t        d      }t        |      }t        t        dd            |_        |j                  d       d {   }d }||u }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}}y 7 w)Nrm   rn   i  zService Unavailabler   r   r   r   rt   ry   r{   rH   r   r   s           r   "test_speak_server_503_returns_nonez5TestTTSClientSpeak.test_speak_server_503_returns_none   s     $:;6",s1FG
 ||F++v~vvv ,r   c                  K   t        d      }t        |      }t               }t        t        d            |_        ||_        |j                  d       d{   }d}||u }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}}y7 w)u?   Connection-level exception → speak() returns None (no raise).zhttp://unreachable:8200/ttsrn   refused)side_effectHelloNr   r   rt   ry   r{   rH   )r&   r%   r   ConnectionRefusedErrorr7   r   r   rM   rN   rO   rP   rQ   rR   rS   rT   )	r   rD   r   r:   rt   r   rW   r   rZ   s	            r   %test_speak_network_error_returns_nonez8TestTTSClientSpeak.test_speak_network_error_returns_none   s      $AB6"+ -CI-NO!||G,,v~vvv -s   ADDB+Dc                2  K   t        t        d            }t        t        d            |_        |j                  d       d{   }d}||u }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}}|j                  j                  j                          y7 έw)z,Empty text is rejected before any HTTP call.rm   rn   ro   rp    Nr   r   rt   ry   r{   rH   )r%   r&   r;   r5   r   r   rM   rN   rO   rP   rQ   rR   rS   rT   r7   assert_not_called)r   r   rt   r   rW   r   rZ   s          r   "test_speak_empty_text_returns_nonez5TestTTSClientSpeak.test_speak_empty_text_returns_none   s      9.DEF,-@-LM||B''v~vvv..0 (s   ADDCDc                  K   t        d      }t        |      }|j                  }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            dx}x}}t        d
      }t        |      }	ddl}
t        j                  |
d|	      5  |j!                  d       d{   }ddd       t#        t$              }|sddt        j                         v st        j                  t"              rt        j                  t"              nddt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  t$              rt        j                  t$              ndt        j                  |      dz  }t        t        j                  |            d}|j                  }||	u }|st        j                  d|fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  |	      rt        j                  |	      nddz  }dd|iz  }t        t        j                  |            dx}}y7 # 1 sw Y   xY ww)z9If _session is None, speak() creates a new ClientSession.rm   rn   Nr   z0%(py2)s
{%(py2)s = %(py0)s._session
} is %(py5)sr   rE   rI   rJ   ro   rp   r   r"   r(   r   rr   rs   rt   ru   rv   )z0%(py2)s
{%(py2)s = %(py0)s._session
} is %(py4)smock_session)rF   rG   rx   r~   r   )r&   r%   r   rM   rN   rO   rP   rQ   rR   rS   rT   r5   r;   r   r   r   r   rs   ru   )r   rD   r   rW   rX   rY   rZ   r[   r   r   r   rt   r   r   s                 r   "test_speak_creates_session_if_nonez5TestTTSClientSpeak.test_speak_creates_session_if_none   s     $:;6"&$&$&&&&$&&&&&&v&&&v&&&&&&$&&&&&&&'s3	))4 	\\'?N 	1!<<00F	1 &%((((((((z(((z((((((&(((&((((((%(((%((((((((((.,....,......v...v.........,...,....... 1	1 	1s1   DMM
)M*M
.HMM

MMNri   )r   r   r   r!   pytestmarkasyncior   r   r   r   r   r   r   r   r   r   rk   rk   f   s    &[[Y Y" [[	; 	; [[
 
 [[	 	 [[  [[1 1 [[/ /r   rk   c                      e Zd ZdZej
                  j                  dd       Zej
                  j                  dd       Zy)TestTTSClientClosezTTSClient.close() behaviour.c                l  K   t        t        d            }t               }t               |_        ||_        |j	                          d {    |j                  j                          |j
                  }d }||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}}y 7 w)	Nrm   rn   r   r   r   rE   rI   rJ   )r%   r&   r   r   r8   r   assert_awaited_oncerM   rN   rO   rP   rQ   rR   rS   rT   )r   r   r   rW   rX   rY   rZ   r[   s           r   test_close_calls_session_closez1TestTTSClientClose.test_close_calls_session_close   s     9.DEF {&[&lln..0&$&$&&&&$&&&&&&v&&&v&&&&&&$&&&&&&& 	s   A	D4D2C'D4c                T   K   t               }|j                          d{    y7 w)z'close() with no session does not raise.N)r%   r8   )r   r   s     r   test_close_no_session_is_noopz0TestTTSClientClose.test_close_no_session_is_noop   s      llns   (&(Nri   )	r   r   r   r!   r   r   r   r   r   r   r   r   r   r      s?    &[[	' 	' [[ r   r   )r   r   )r   )r-   intr3   strr   r   )r9   r   r   r   )%r!   
__future__r   builtinsrO   _pytest.assertion.rewrite	assertionrewriterM   syspathlibr   typesr   unittest.mockr   r   r   r   __file__resolveparentsROOTSRCr   pathinsertr$   modulesservices.tts_clientr%   r&   r5   r;   r=   rk   r   r   r   r   <module>r      s   
 #   
   5 5  H~''*
Uls8388HHOOAs3x   CKK/1CKK	 4
&& &.c/ c/L r   