
    Ni)/                        d Z ddlZddlmc mZ ddlZddlZddl	Z	ddl
mZ ddlmZmZ ddlZej                   j#                  d e ee      j(                  j(                  j(                               	 ddlmZ ddlmZmZmZ dZej:                  j=                  e d	
      Z G d d      Z  G d d      Z!ejD                  d        Z# G d d      Z$ G d d      Z% G d d      Z& G d d      Z'y# e$ r dZY vw xY w)u   services/openai_compat_server.py 테스트 스위트

FastAPI TestClient를 사용한 엔드포인트 통합 테스트 +
Pydantic 모델 단위 테스트.
    N)Path)AnyDict)
TestClient)appChatCompletionRequestChatCompletionResponseTFzfastapi not installed)reasonc                   0    e Zd ZdZddZddZddZddZy)TestChatCompletionRequestu/   ChatCompletionRequest Pydantic 모델 테스트Nc                    t        ddddg      }|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                  }t        |      }d}||k(  }	|	s
t        j                  d|	fd||f      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t        j                  |      t        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}x}	}y)u   기본 필드로 생성gpt-4ouserHellorolecontentmodelmessages==)z-%(py2)s
{%(py2)s = %(py0)s.model
} == %(py5)sreqpy0py2py5assert %(py7)spy7N   )zN%(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.messages
})
} == %(py8)slenr   py1py3r   py8assert %(py10)spy10)r   r   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationr   r!   )selfr   @py_assert1@py_assert4@py_assert3@py_format6@py_format8@py_assert2@py_assert7@py_assert6@py_format9@py_format11s               ?/home/jay/workspace/services/tests/test_openai_compat_server.pytest_basic_creationz-TestChatCompletionRequest.test_basic_creation-   s-   #%':;
 yy$H$yH$$$$yH$$$$$$s$$$s$$$y$$$H$$$$$$$<<%s< %A% A%%%% A%%%%%%s%%%s%%%%%%3%%%3%%%<%%% %%%A%%%%%%%    c                     t        ddddg      }g }|j                  }d}||u }|}|s|j                  }t        |t              }|}|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  }
|j                  |
       |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	t	        j                        dt        j                         v st	        j                  t              rt	        j                  t              ndt	        j                        dz  }|j                  |       t	        j                  |d      i z  }dd|iz  }t        t	        j                  |            dx}x}x}x}x}x}}g }|j                  }d}||u }|}|s|j                  }t        |t              }|}|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  }
|j                  |
       |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	t	        j                  |      dt        j                         v st	        j                  t              rt	        j                  t              ndt	        j                  |      dz  }|j                  |       t	        j                  |d      i z  }dd|iz  }t        t	        j                  |            dx}x}x}x}x}x}}g }|j                   }d}||u }|}|s4|j                   }d}||u }|}|s|j                   }t        |t"              }|}|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  }
|j                  |
       |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  }|j                  |       |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	t	        j                        dt        j                         v st	        j                  t"              rt	        j                  t"              ndt	        j                        dz  }|j                  |       t	        j                  |d      i z  }d d!|iz  }t        t	        j                  |            dx}x}x}x}x}x}x}x}x}}y)"u   선택 필드 기본값 확인claude-sonnet-4-6r   hir   r   Nis)z3%(py4)s
{%(py4)s = %(py2)s.temperature
} is %(py7)sr   )r   py4r   z%(py9)spy9zV%(py17)s
{%(py17)s = %(py11)s(%(py14)s
{%(py14)s = %(py12)s.temperature
}, %(py15)s)
}
isinstancefloat)py11py12py14py15py17r    zassert %(py20)spy20)z2%(py4)s
{%(py4)s = %(py2)s.max_tokens
} is %(py7)szU%(py17)s
{%(py17)s = %(py11)s(%(py14)s
{%(py14)s = %(py12)s.max_tokens
}, %(py15)s)
}intF)z.%(py4)s
{%(py4)s = %(py2)s.stream
} is %(py7)s)z2%(py13)s
{%(py13)s = %(py11)s.stream
} is %(py16)s)rG   py13py16z%(py18)spy18zQ%(py26)s
{%(py26)s = %(py20)s(%(py23)s
{%(py23)s = %(py21)s.stream
}, %(py24)s)
}bool)rL   py21py23py24py26zassert %(py29)spy29)r   temperaturerE   rF   r(   r)   r*   r+   r,   r-   append_format_boolopr.   r/   
max_tokensrM   streamrQ   )r0   r   r1   r3   r8   @py_assert5@py_assert0@py_assert13@py_assert16r5   @py_format10@py_format18@py_format19@py_format21@py_assert12@py_assert15@py_assert14@py_assert22@py_assert25@py_format17@py_format27@py_format28@py_format30s                          r;   test_defaultsz'TestChatCompletionRequest.test_defaults6   s   #%%$78
 	MsL$L$&LS__L*_e*LL*LLLL$LLLLLLsLLLsLLLLLL$LLLLLLLLLL*LLL*LLLLLLSLLLSLLL_LLLLLLeLLLeLLL*LLLLLLLLLLLLLLHs~~HH~%HCNNHNC)HH)HHHH~HHHHHHsHHHsHHH~HHHHHHHHHHHHHHHHHHHHHHCHHHCHHHNHHHHHHCHHHCHHH)HHHHHHHHHHHHHHXszzXUXzU"XcjjXDXjD&8XszzXJzSW<XX<XXXXzUXXXXXXsXXXsXXXzXXXUXXXXXXXjDXXXXXXcXXXcXXXjXXXDXXXXXXXXXXJXXXJXXXXXXsXXXsXXXzXXXXXXSWXXXSWXXX<XXXXXXXXXXXXXXXr=   c                    t        dddddddgd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}}|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)u   모든 필드 설정zgpt-4o-minisystemzYou are helpful.r   r   zSummarize this.gffffff?i   F)r   r   rW   rZ   r[   r   )z3%(py2)s
{%(py2)s = %(py0)s.temperature
} == %(py5)sr   r   r   r   N)z2%(py2)s
{%(py2)s = %(py0)s.max_tokens
} == %(py5)srA   z.%(py2)s
{%(py2)s = %(py0)s.stream
} is %(py5)s)r   rW   r(   r)   r*   r+   r,   r-   r.   r/   rZ   r[   r0   r   r1   r2   r3   r4   r5   s          r;   test_with_all_fieldsz.TestChatCompletionRequest.test_with_all_fields@   sz   #!.@A,=> 	
 %#%#%%%%#%%%%%%s%%%s%%%%%%#%%%%%%%~~$$~$$$$~$$$$$$s$$$s$$$~$$$$$$$$$$zz"U"zU""""zU""""""s"""s"""z"""U"""""""r=   c                    t        ddddgd      }|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)u   stream=True 설정r   r   r@   r   Tr   r   r[   rA   rp   r   r   r   r   N)
r   r[   r(   r)   r*   r+   r,   r-   r.   r/   rq   s          r;   test_stream_truez*TestChatCompletionRequest.test_stream_trueP   s    #%$78

 zz!T!zT!!!!zT!!!!!!s!!!s!!!z!!!T!!!!!!!r=   returnN)__name__
__module____qualname____doc__r<   rm   rr   ru    r=   r;   r   r   *   s    9&Y# "r=   r   c                        e Zd ZdZddZddZy)TestChatCompletionResponseu0   ChatCompletionResponse Pydantic 모델 테스트Nc           
      "   t        ddt        t        j                               ddddddd	g
      }|j                  }|j                  }d} ||      }|sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }t        t        j                  |            dx}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}}|j                  }
t        |
      }d}||k(  }|s
t        j                  d|fd||f      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t        j                  |
      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}x}}y)u"   OpenAI 형식 필드 구조 확인zchatcmpl-test-123chat.completionr   r   	assistantzHello!r   stop)indexmessagefinish_reasonidobjectcreatedr   choices	chatcmpl-zdassert %(py8)s
{%(py8)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.id
}.startswith
}(%(py6)s)
}resp)r   r   rC   py6r%   Nr   )z.%(py2)s
{%(py2)s = %(py0)s.object
} == %(py5)sr   r   r   r    )zM%(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.choices
})
} == %(py8)sr!   r"   r&   r'   )r	   rM   timer   
startswithr*   r+   r(   r,   r-   r.   r/   r   r)   r   r!   )r0   r   r1   r3   r\   r7   r9   r2   r4   r5   r6   r8   r:   s                r;   test_openai_format_fieldsz4TestChatCompletionResponse.test_openai_format_fields]   s   %"$		$ (3I%+
 ww.w!!.+.!+........t...t...w...!...+..........{{///{/////{///////t///t///{///////////<<%s< %A% A%%%% A%%%%%%s%%%s%%%%%%4%%%4%%%<%%% %%%A%%%%%%%r=   c                    t        ddddg       }|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)u   usage 필드는 선택적zchatcmpl-abcr   iIr?   r   r   )z*%(py2)s
{%(py2)s = %(py0)s.id
} == %(py5)sr   r   r   r   N)
r	   r   r(   r)   r*   r+   r,   r-   r.   r/   )r0   r   r1   r2   r3   r4   r5   s          r;   test_usage_field_optionalz4TestChatCompletionResponse.test_usage_field_optionalp   s    %$%
 ww(.(w.((((w.((((((t(((t(((w(((.(((((((r=   rv   )rx   ry   rz   r{   r   r   r|   r=   r;   r~   r~   Z   s    :&&
)r=   r~   c                       t        t              S )zFastAPI TestClient fixture)r   r   r|   r=   r;   clientr      s     c?r=   c                        e Zd ZdZddZddZy)TestHealthEndpointu   GET /health 테스트Nc                    |j                  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}}y	)
u   상태 확인 - 200 OK/health   r   z3%(py2)s
{%(py2)s = %(py0)s.status_code
} == %(py5)sresponser   r   r   N
getstatus_coder(   r)   r*   r+   r,   r-   r.   r/   r0   r   r   r1   r2   r3   r4   r5   s           r;   test_health_returns_200z*TestHealthEndpoint.test_health_returns_200   s    ::i(##*s*#s****#s******x***x***#***s*******r=   c                    |j                  d      }|j                         }d}||v }|st        j                  d|fd||f      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}}|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	)u,   health 응답 본문에 status 필드 포함r   statusinz%(py1)s in %(py3)sbodyr#   r$   assert %(py5)sr   Nokr   z%(py1)s == %(py4)sr#   rC   assert %(py6)sr   
r   jsonr(   r)   r-   r*   r+   r,   r.   r/   )r0   r   r   r   r]   r6   @py_format4r4   r3   @py_format5@py_format7s              r;   test_health_response_bodyz,TestHealthEndpoint.test_health_response_body   s    ::i(}}x4x4x44H~%%~%%%%~%%%~%%%%%%%%%%r=   rv   )rx   ry   rz   r{   r   r   r|   r=   r;   r   r      s    +
&r=   r   c                   0    e Zd ZdZddZddZddZddZy)TestModelsEndpointu   GET /v1/models 테스트Nc                    |j                  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}}y	)
u   모델 목록 - 200 OK
/v1/modelsr   r   r   r   r   r   r   Nr   r   s           r;   test_models_returns_200z*TestModelsEndpoint.test_models_returns_200   s    ::l+##*s*#s****#s******x***x***#***s*******r=   c                    |j                  d      }|j                         }d}||v }|st        j                  d|fd||f      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}}|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}}d}||v }|st        j                  d|fd||f      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}}|d   }t        |t              }|sddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      d
t        j                         v st        j                  t              rt        j                  t              nd
t        j                  |      dz  }t        t        j                  |            d	x}}y	)u"   OpenAI 형식 응답 구조 확인r   r   r   r   r   r   r   r   Nlistr   r   r   r   r   data5assert %(py5)s
{%(py5)s = %(py0)s(%(py2)s, %(py3)s)
}rE   r   r   r$   r   )r   r   r(   r)   r-   r*   r+   r,   r.   r/   rE   r   )r0   r   r   r   r]   r6   r   r4   r3   r   r   r1   r2   s                r;   test_models_response_formatz.TestModelsEndpoint.test_models_response_format   s   ::l+}}x4x4x44H~''~''''~'''~''''''''''v~vvv,-z,--------z---z---,-------------------r=   c                    |j                  d      }|j                         }|d   }t        |      }d}||kD  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      t        j                  |      dz  }dd	|iz  }	t        t        j                  |	            d
x}x}x}}y
)u$   모델 목록이 비어있지 않음r   r   r   >z/%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} > %(py7)sr!   r   r   rC   r   assert %(py9)srD   N)r   r   r!   r(   r)   r*   r+   r,   r-   r.   r/   )
r0   r   r   r   r1   r3   r8   r\   r5   r`   s
             r;   test_models_list_not_emptyz-TestModelsEndpoint.test_models_list_not_empty   s    ::l+}}<$s< $1$ 1$$$$ 1$$$$$$s$$$s$$$<$$$ $$$1$$$$$$$r=   c                    |j                  d      }|j                         }|d   D ]  }d}||v }|st        j                  d|fd||f      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
)u%   각 모델 항목에 id 필드 존재r   r   r   r   r   model_entryr   r   r   Nr   )	r0   r   r   r   r   r]   r6   r   r4   s	            r;   test_model_entry_has_idz*TestModelsEndpoint.test_model_entry_has_id   s    ::l+}}< 	'K&4;&&&&4;&&&4&&&&&&;&&&;&&&&&&&	'r=   rv   )rx   ry   rz   r{   r   r   r   r   r|   r=   r;   r   r      s    "+
.%'r=   r   c                       e Zd ZdZdeeef   fdZddZddZ	ddZ
ddZdd	Zdd
ZddZddZddZddZddZddZddZddZy)TestChatCompletionsEndpointu#   POST /v1/chat/completions 테스트rw   c                     ddddgdS )Nr   r   r   r   r   r|   )r0   s    r;   _basic_payloadz*TestChatCompletionsEndpoint._basic_payload   s    "(W=>
 	
r=   Nc                    |j                  d| j                               }|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
)u   기본 요청 - 200 OK/v1/chat/completionsr   r   r   r   r   r   r   r   N)postr   r   r(   r)   r*   r+   r,   r-   r.   r/   r   s           r;   test_completions_returns_200z8TestChatCompletionsEndpoint.test_completions_returns_200   s    ;;5D<O<O<Q;R##*s*#s****#s******x***x***#***s*******r=   c                    |j                  d| j                               }|j                         }d}||v }|st        j                  d|fd||f      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}}|d   }|j                  }d} ||      }	|	stdt        j
                  |      t        j
                  |      t        j
                  |      t        j
                  |	      dz  }
t        t        j                  |
            d
x}x}x}}	y
)u   응답에 id 필드 포함r   r   r   r   r   r   r   r   r   Nr   zLassert %(py7)s
{%(py7)s = %(py3)s
{%(py3)s = %(py1)s.startswith
}(%(py5)s)
})r#   r$   r   r   )r   r   r   r(   r)   r-   r*   r+   r,   r.   r/   r   )r0   r   r   r   r]   r6   r   r4   r2   r8   r5   s              r;    test_completions_response_has_idz<TestChatCompletionsEndpoint.test_completions_response_has_id   s    ;;5D<O<O<Q;R}}tt|tttttDz1z$$1[1$[11111z111$111[1111111111r=   c                 b   |j                  d| j                               }|j                         }|j                  }d} ||      }d}||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |      t	        j                  |      t	        j                  |      dz  }	d	d
|	iz  }
t        t	        j                  |
            dx}x}x}x}}y)u'   응답 object 필드가 chat.completionr   r   r   r   r   )zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} == %(py9)sr   )r   r   rC   r   rD   zassert %(py11)srG   N)r   r   r   r   r(   r)   r*   r+   r,   r-   r.   r/   )r0   r   r   r   r1   r3   r\   @py_assert8r7   r`   @py_format12s              r;   %test_completions_response_object_typezATestChatCompletionsEndpoint.test_completions_response_object_type   s    ;;5D<O<O<Q;R}}xx66x!6%66!%66666!%6666666t666t666x666666!666%666666666r=   c                 v   |j                  d| j                               }|j                         }d}||v }|st        j                  d|fd||f      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}}|d   }t        |t              }	|	sddt        j                         v st        j                  t              rt        j
                  t              ndt        j
                  |      dt        j                         v st        j                  t              rt        j
                  t              ndt        j
                  |	      dz  }t        t        j                  |            d
x}}	|d   }t        |      }
d}|
|kD  }|st        j                  d|fd|
|f      dt        j                         v st        j                  t              rt        j
                  t              ndt        j
                  |      t        j
                  |
      t        j
                  |      dz  }dd|iz  }t        t        j                  |            d
x}x}
x}}y
)u   응답에 choices 배열 포함r   r   r   r   r   r   r   r   r   Nr   rE   r   r   r   r   r   r!   r   r   rD   )r   r   r   r(   r)   r-   r*   r+   r,   r.   r/   rE   r   r!   )r0   r   r   r   r]   r6   r   r4   r1   r2   r3   r8   r\   r5   r`   s                  r;   %test_completions_response_has_choiceszATestChatCompletionsEndpoint.test_completions_response_has_choices   s   ;;5D<O<O<Q;R}} yD    yD   y      D   D       y/0z/400000000z000z000/000000400040000000000	?'s?#'a'#a''''#a''''''s'''s'''?'''#'''a'''''''r=   c                    |j                  d| j                               }|j                         }|d   d   }d}||v }|st        j                  d|fd||f      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}}d}|d   }	||	v }|slt        j                  d|fd||	f      t        j
                  |      t        j
                  |	      dz  }
dd|
iz  }t        t        j                  |            dx}x}}	|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)u#   choices[0]에 message 필드 포함r   r   r   r   r   r   r   choicer   r   r   Nr   )z%(py1)s in %(py4)sr   r   r   r   r   r   r   r   r   r(   r)   r-   r*   r+   r,   r.   r/   )r0   r   r   r   r   r]   r6   r   r4   r3   r   r   s               r;   #test_completions_choice_has_messagez?TestChatCompletionsEndpoint.test_completions_choice_has_message   s9   ;;5D<O<O<Q;R}}i#"yF""""yF"""y""""""F"""F"""""""*	**v*****v****v***********i (7K7(K7777(K777(777K7777777r=   c                    |j                  d| j                               }|j                         }d}||v }|st        j                  d|fd||f      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
)u   응답에 model 필드 포함r   r   r   r   r   r   r   r   r   Nr   )r0   r   r   r   r]   r6   r   r4   s           r;   #test_completions_response_has_modelz?TestChatCompletionsEndpoint.test_completions_response_has_model   s    ;;5D<O<O<Q;R}}w$w$w$$r=   c                    |j                  d| j                               }|j                         }d}||v }|st        j                  d|fd||f      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}}|d   }t        |t              }	|	sddt        j                         v st        j                  t              rt        j
                  t              ndt        j
                  |      dt        j                         v st        j                  t              rt        j
                  t              ndt        j
                  |	      dz  }t        t        j                  |            d
x}}	y
)u(   응답에 created 타임스탬프 포함r   r   r   r   r   r   r   r   r   Nr   rE   rM   r   )r   r   r   r(   r)   r-   r*   r+   r,   r.   r/   rE   rM   )
r0   r   r   r   r]   r6   r   r4   r1   r2   s
             r;   %test_completions_response_has_createdzATestChatCompletionsEndpoint.test_completions_response_has_created   s    ;;5D<O<O<Q;R}} yD    yD   y      D   D       y//z/3////////z///z//////////3///3//////////r=   c                    |j                  dddi      }|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)u1   messages 없는 요청 - 422 Unprocessable Entityr   r   r   r     r   r   r   r   r   r   N
r   r   r(   r)   r*   r+   r,   r-   r.   r/   r   s           r;   1test_completions_invalid_request_missing_messageszMTestChatCompletionsEndpoint.test_completions_invalid_request_missing_messages   s    ;;"'8)<  
 ##*s*#s****#s******x***x***#***s*******r=   c                    |j                  dddddgi      }|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)u   model 없는 요청 - 422r   r   r   r@   r   r   r   r   r   r   r   r   r   Nr   r   s           r;   .test_completions_invalid_request_missing_modelzJTestChatCompletionsEndpoint.test_completions_invalid_request_missing_model   s    ;;"4@AB  
 ##*s*#s****#s******x***x***#***s*******r=   c                    dddddddgd}|j                  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}}y)u   system + user 메시지 처리r   ro   zYou are a helpful assistant.r   r   zWhat is 2+2?r   r   r   r   r   r   r   r   r   r   Nr   	r0   r   payloadr   r1   r2   r3   r4   r5   s	            r;   $test_completions_with_system_messagez@TestChatCompletionsEndpoint.test_completions_with_system_message   s     !.LMN;
 ;;5G;D##*s*#s****#s******x***x***#***s*******r=   c                    i | j                         ddi}|j                  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}}y)u   temperature 파라미터 처리rW   g      ?r   r   r   r   r   r   r   r   r   Nr   r   r   r(   r)   r*   r+   r,   r-   r.   r/   r   s	            r;   !test_completions_with_temperaturez=TestChatCompletionsEndpoint.test_completions_with_temperature  s    ?T((*?M3?;;5G;D##*s*#s****#s******x***x***#***s*******r=   c                    i | j                         ddi}|j                  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}}y)u   max_tokens 파라미터 처리rZ   d   r   r   r   r   r   r   r   r   r   Nr   r   s	            r;    test_completions_with_max_tokensz<TestChatCompletionsEndpoint.test_completions_with_max_tokens  s    >T((*>L#>;;5G;D##*s*#s****#s******x***x***#***s*******r=   c                    |j                  d| j                               }|j                         }|d   d   }d}||v }|st        j                  d|fd||f      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)u   choices[0].finish_reason 존재r   r   r   r   r   r   r   r   r   r   r   Nr   )	r0   r   r   r   r   r]   r6   r   r4   s	            r;   &test_completions_finish_reason_presentzBTestChatCompletionsEndpoint.test_completions_finish_reason_present  s    ;;5D<O<O<Q;R}}i#(&((((&(((((((((&(((&(((((((r=   c                 r   |j                  d| j                               }|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)zchoices[0].index == 0r   r   r   r   r   r   r   r   r   r   N)r   r   r   r(   r)   r-   r.   r/   )	r0   r   r   r   r]   r3   r6   r   r   s	            r;   test_completions_choice_indexz9TestChatCompletionsEndpoint.test_completions_choice_index  s    ;;5D<O<O<Q;R}}Iq!'*/a/*a////*a///*///a///////r=   rv   )rx   ry   rz   r{   r   strr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r|   r=   r;   r   r      s_    -
S#X 
+
27(80++
+++)0r=   r   c                   0    e Zd ZdZddZddZddZddZy)TestStreamEndpointu3   POST /v1/chat/completions stream=True SSE 테스트Nc                    ddddgdd}|j                  dd|	      5 }|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}}ddd       y# 1 sw Y   yxY w)u   stream=True 요청 - 200 OKr   r   r@   r   Trt   POSTr   r   r   r   r   r   r   r   r   N)
r[   r   r(   r)   r*   r+   r,   r-   r.   r/   r   s	            r;   test_stream_returns_200z*TestStreamEndpoint.test_stream_returns_200(  s     "(T:;

 ]]6#9]H 	/H''.3.'3....'3......8...8...'...3.......	/ 	/ 	/s   C	C11C:c                    ddddgdd}|j                  dd|	      5 }|j                  j                  d
d      }d}||v }|st        j                  d|fd||f      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}}ddd       y# 1 sw Y   yxY w)u4   stream=True 응답 Content-Type이 text/event-streamr   r   r@   r   Trt   r   r   r   zcontent-type ztext/event-streamr   r   content_typer   r   r   N)r[   headersr   r(   r)   r-   r*   r+   r,   r.   r/   )	r0   r   r   r   r   r]   r6   r   r4   s	            r;   %test_stream_content_type_event_streamz8TestStreamEndpoint.test_stream_content_type_event_stream2  s     "(T:;

 ]]6#9]H 	7H#++//CL&6&,6666&,666&666666,666,6666666	7 	7 	7s   CC**C3c                    ddddgdd}|j                  dd|	      5 }t        |j                               }d
j                  |      }d}||v }|st	        j
                  d|fd||f      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}}ddd       y# 1 sw Y   yxY w)u$   SSE 스트림에 data: 라인 포함r   r   r@   r   Trt   r   r   r   r   zdata:r   r   textr   r   r   Nr[   r   	iter_textjoinr(   r)   r-   r*   r+   r,   r.   r/   
r0   r   r   r   chunksr  r]   r6   r   r4   s
             r;   test_stream_contains_data_linesz2TestStreamEndpoint.test_stream_contains_data_lines=  s     "(T:;

 ]]6#9]H 	#H(,,./F776?D"7d?"""7d"""7""""""d"""d"""""""	# 	# 	#   CC88Dc                    ddddgdd}|j                  dd|	      5 }t        |j                               }d
j                  |      }d}||v }|st	        j
                  d|fd||f      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}}ddd       y# 1 sw Y   yxY w)u%   SSE 스트림이 [DONE] 으로 종료r   r   r@   r   Trt   r   r   r   r   z[DONE]r   r   r  r   r   r   Nr  r  s
             r;   test_stream_ends_with_donez-TestStreamEndpoint.test_stream_ends_with_doneI  s     "(T:;

 ]]6#9]H 	$H(,,./F776?D#8t####8t###8######t###t#######	$ 	$ 	$r  rv   )rx   ry   rz   r{   r   r   r  r
  r|   r=   r;   r   r   %  s    =/	7
#
$r=   r   )(r{   builtinsr*   _pytest.assertion.rewrite	assertionrewriter(   r   sysr   pathlibr   typingr   r   pytestpathinsertr   __file__parentfastapi.testclientr   services.openai_compat_serverr   r   r	   FASTAPI_AVAILABLEImportErrormarkskipif
pytestmarkr   r~   fixturer   r   r   r   r   r|   r=   r;   <module>r     s      
     3tH~,,33::; <	- 
 
 [["   
-" -"` )  )P  
& & ' '>k0 k0\.$ .$U  s   5C4 4C>=C>