
    LDiT                     D   d Z ddlZddlZddlmZ ddlmZ ddlZ ee      j                  j                  Z
 ee
      ej                  vr"ej                  j                  d ee
             e
dz  Z ee      ej                  vr"ej                  j                  d ee             	 ddlmZ dZej&                  j)                  e d	
      ZdddgddiddidddZdddZdddZdZdZ	 d8dddddddddd	dededededededed edz  d!edz  d"edz  d#edz  d$eeef   fd%Z G d& d'      Z G d( d)      Z  G d* d+      Z! G d, d-      Z" G d. d/      Z# G d0 d1      Z$ G d2 d3      Z% G d4 d5      Z& G d6 d7      Z'y# e$ r 	 ddlZdZn# e$ r dZY nw xY wY w xY w)9u  
test_schema_contract.py - schema_contract verifier 단위 테스트 (헤임달 작성)

테스트 항목:
1. 정상 케이스 - 모든 SC 항목 PASS
2. SC-1: models.py 누락
3. SC-2: tests/test_contract.py 누락
4. SC-3: shared/schemas/{name}.schema.json 누락
5. SC-4/SC-5: sample 데이터가 JSON Schema와 불일치
6. SC-6: Pydantic 모델 필드와 JSON Schema 필드 불일치 (mock 사용)
7. workers/ 디렉토리 자체 없음 → SKIP
8. jsonschema 라이브러리 부재 → SC-4/SC-5 SKIP + WARN

구현 참고 사항:
- sample 파일 경로: schemas_dir/{name}.sample.normal.json, {name}.sample.edge.json
- SC-1 누락 시 Worker 자체가 감지되지 않으므로 → SKIP
  (schema_contract는 models.py 존재 여부로 Worker 디렉토리를 감지)
  따라서 SC-1 FAIL은 Worker를 먼저 정상 감지한 후 SC-1 로직이 실행될 때 발생함.
  실제 구현에서는 _find_worker_dirs()가 models.py 기준으로 탐색하므로
  models.py 없으면 Worker 자체를 못 찾아 SKIP 반환.
  → SC-1 테스트는 "models.py 없는 Worker" 대신 "SC-1 FAIL 메시지 직접 발생" 시나리오로 구성.
- jsonschema 미설치 시: 전체 SKIP이 아닌 WARN + SC-4/SC-5 개별 SKIP
    N)Path)mock	verifiers)schema_contractTFuW   schema_contract 모듈이 아직 구현되지 않음 — 구현 후 테스트 활성화)reasonobjectkeywordkeyword_counttypestringintegerr	   r
   )r   required
propertiesadditionalPropertiestest*   edgezhfrom pydantic import BaseModel

class KeywordResult(BaseModel):
    keyword: str
    keyword_count: int
z+def test_keyword_result_schema():
    pass
)	include_modelsinclude_test_contractinclude_schemainclude_normal_sampleinclude_edge_samplenormal_sampleedge_sampleschema_contentmodels_contentbaseworker_namer   r   r   r   r   r   r   r   r   returnc       	         p   | dz  }||z  }|dz  }| dz  }|j                  dd       |j                  dd       |j                  dd       |r |
|
nt        }|dz  j                  |d       |r|d	z  j                  t        d       |	|	nt        }|r,|| d
z  j                  t        j                  |      d       |r6||nt        }|| dz  j                  t        j                  |      d       |r6||nt        }|| dz  j                  t        j                  |      d       ||fS )u   
    tmp_path 아래에 테스트용 Worker 구조를 생성하고
    (workers_base_dir, schemas_dir) 경로를 반환합니다.
    workerstestsschemasTparentsexist_ok	models.pyutf-8encodingtest_contract.py.schema.json.sample.normal.json.sample.edge.json)	mkdir_MODELS_PY_CONTENT
write_text_TEST_CONTRACT_CONTENT_VALID_SCHEMAjsondumps_VALID_NORMAL_SAMPLE_VALID_EDGE_SAMPLE)r   r   r   r   r   r   r   r   r   r   r   workers_base
worker_dir	tests_dirschemas_dircontentschemansess                      ?/home/jay/workspace/teams/dev1/qc/tests/test_schema_contract.py_make_worker_dirrB   j   sr   $ )#L+-J'I)#KTD1OOD4O0dT2$2$>.DV	k	!--g-H	'	'334JU\3]-9^}F	+l3	3??JJv 	@ 	
 +7]=Q	+&9:	:FFJJrNW 	G 	
 '3[9K	+&78	8DDJJrNW 	E 	
 $$    c                   :    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
y	)
TestSchemaContractNormalCaseu=   모든 SC 항목이 충족되면 PASS를 반환해야 한다.c                     t        |      \  }}t        j                  dt        |      t        |            }|d   dk(  s
J d|        y )Ntask-test-normaltask_idworkers_base_dirr<   statusPASSu&   정상 환경에서 PASS 기대, got: rB   r   verifystrselftmp_pathr9   r<   results        rA   test_sc_all_pass_statusz4TestSchemaContractNormalCase.test_sc_all_pass_status   sZ    $4X$>!k ''& .K(

 h6) 	
4VH=	
)rC   c                     t        |      \  }}t        j                  dt        |      t        |            }d|v sJ d       d|v sJ d       t	        |d   t
              sJ d       y )NrG   rH   rK   u    결과에 'status' 키가 없음detailsu!   결과에 'details' 키가 없음u   'details'는 list여야 함rB   r   rN   rO   
isinstancelistrP   s        rA    test_sc_result_has_required_keysz=TestSchemaContractNormalCase.test_sc_result_has_required_keys   sy    $4X$>!k ''& .K(

 6!E#EE!F"G$GG"&+T2Q4QQ2rC   c                     t        |      \  }}t        j                  dt        |      t        |            }t	        |d         dkD  sJ d       y )NrG   rH   rV   r   u/   PASS여도 details에 최소 1개 항목 필요)rB   r   rN   rO   lenrP   s        rA   test_sc_pass_details_not_emptyz;TestSchemaContractNormalCase.test_sc_pass_details_not_empty   sT    $4X$>!k ''& .K(

 6)$%)\+\\)rC   c                     t        |      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d|v sd|v s
J d|        y y )NrG   rH   
rV   rL   OKu7   PASS 상태인데 details에 PASS/OK 문자열 없음: rB   r   rN   rO   joinrQ   rR   r9   r<   rS   details_strs         rA   )test_sc_pass_details_contain_pass_keywordzFTestSchemaContractNormalCase.test_sc_pass_details_contain_pass_keyword   su    $4X$>!k ''& .K(

 iiy 12$(; 	
Ek]S	
;(;$rC   c                     t        |      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d|v s
J d|        y )NrG   rH   r_   rV   z	SC-1 PASSu&   정상 케이스에 SC-1 PASS 없음: ra   rc   s         rA    test_sc_details_contain_sc1_passz=TestSchemaContractNormalCase.test_sc_details_contain_sc1_pass   f    $4X$>!k ''& .K(

 iiy 12k)a-ST_S`+aa)rC   c                     t        |      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d|v s
J d|        y )NrG   rH   r_   rV   z	SC-2 PASSu&   정상 케이스에 SC-2 PASS 없음: ra   rc   s         rA    test_sc_details_contain_sc2_passz=TestSchemaContractNormalCase.test_sc_details_contain_sc2_pass   rh   rC   c                     t        |      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d|v s
J d|        y )NrG   rH   r_   rV   z	SC-3 PASSu&   정상 케이스에 SC-3 PASS 없음: ra   rc   s         rA    test_sc_details_contain_sc3_passz=TestSchemaContractNormalCase.test_sc_details_contain_sc3_pass   rh   rC   N)__name__
__module____qualname____doc__rT   rZ   r]   re   rg   rj   rl    rC   rA   rE   rE      s-    G	
	R]

bbbrC   rE   c                   :    e Zd ZdZd
dedefdZd Zd Zd Z	d Z
y	)TestSC1ModelsMissinguD   Worker 디렉토리에 models.py가 없으면 FAIL + SC-1 메시지.r   r   c                    |dz  }||z  }|dz  }|dz  }|j                  dd       |j                  dd       |j                  dd       |dz  j                  t        d       || d	z  j                  t        j                  t
              d       || d
z  j                  t        j                  t              d       || dz  j                  t        j                  t              d       |||fS )uY   models.py 없는 Worker 디렉토리를 생성하고, workers_base와 schemas_dir 반환.r"   r#   r$   Tr%   r,   r)   r*   r-   r.   r/   )r0   r2   r3   r5   r6   r4   r7   r8   )rQ   r   r   r9   r:   r;   r<   s          rA   _make_worker_without_modelsz0TestSC1ModelsMissing._make_worker_without_models   s   i'#k1
!G+	i'5t4$6 
'	'334JU\3]	+l3	3??JJ}% 	@ 	
 
+&9:	:FFJJ+,w 	G 	
 
+&78	8DDJJ)*W 	E 	
 [*44rC   c                 :   | j                  |      \  }}}t        |      ddg}t        j                  j	                  t
        d|      5  t        j                  dt        |      t        |            }ddd       d   d	k(  s
J d
|        y# 1 sw Y   xY w)uE   _find_worker_dirs를 mock하여 SC-1 FAIL 시나리오 강제 구성.keyword_workerdirname_find_worker_dirsreturn_valuetask-test-sc1rH   NrK   FAILu'   models.py 누락 시 FAIL 기대, got: )ru   rO   r   patchr   r   rN   )rQ   rR   r9   r<   r:   fake_workersrS   s          rA   test_sc1_status_fail_via_mockz2TestSC1ModelsMissing.test_sc1_status_fail_via_mock  s    040P0PQY0Z-k: !$J9IJKZZ0CR^_ 	$++'!$\!2,F	 h6) 	
5fX>	
)	 	s   +BBc                 Z   | j                  |      \  }}}t        |      ddg}t        j                  j	                  t
        d|      5  t        j                  dt        |      t        |            }d d d        dj                  d         }d	|v s
J d
|        y # 1 sw Y   ,xY w)Nrw   rx   r{   r|   r~   rH   r_   rV   zSC-1u$   details에 'SC-1' 문자열 없음: ru   rO   r   r   r   r   rN   rb   rQ   rR   r9   r<   r:   r   rS   rd   s           rA   %test_sc1_details_contain_sc1_via_mockz:TestSC1ModelsMissing.test_sc1_details_contain_sc1_via_mock)  s    040P0PQY0Z-k: #J9IJKZZ0CR^_ 	$++'!$\!2,F	 iiy 12$Z(L[M&ZZ$	 	   +B!!B*c                 Z   | j                  |      \  }}}t        |      ddg}t        j                  j	                  t
        d|      5  t        j                  dt        |      t        |            }d d d        dj                  d         }d	|v s
J d
|        y # 1 sw Y   ,xY w)Nrw   rx   r{   r|   r~   rH   r_   rV   r(   u&   details에 'models.py' 언급 없음: r   r   s           rA   (test_sc1_details_mention_models_via_mockz=TestSC1ModelsMissing.test_sc1_details_mention_models_via_mock5  s    040P0PQY0Z-k: #J9IJKZZ0CR^_ 	$++'!$\!2,F	 iiy 12k) 	
4[MB	
)	 	r   c                     | j                  |      \  }}}t        j                  dt        |      t        |            }|d   dk(  s
J d|        y)u   
        models.py 없으면 _find_worker_dirs가 Worker를 찾지 못하므로
        실제 verify() 호출 시 SKIP이 반환됨을 확인.
        ztask-test-sc1-skiprH   rK   SKIPu;   models.py 없어서 Worker 미감지 → SKIP 기대, got: N)ru   r   rN   rO   )rQ   rR   r9   r<   _rS   s         rA   'test_sc1_no_models_directory_gives_skipz<TestSC1ModelsMissing.test_sc1_no_models_directory_gives_skipC  sd    
 (,'G'G'Q$k1 ''( .K(

 h6) 	
I&R	
)rC   Nrw   )rm   rn   ro   rp   r   rO   ru   r   r   r   r   rq   rC   rA   rs   rs      s-    N5 53 50
"
[

rC   rs   c                   (    e Zd ZdZd Zd Zd Zd Zy)TestSC2TestContractMissinguE   Worker 내 tests/test_contract.py가 없으면 FAIL + SC-2 메시지.c                     t        |d      \  }}t        j                  dt        |      t        |            }|d   dk(  s
J d|        y )NFr   task-test-sc2rH   rK   r   u.   test_contract.py 누락 시 FAIL 기대, got: rM   rP   s        rA   test_sc2_status_failz/TestSC2TestContractMissing.test_sc2_status_failZ  sa    $4E%
!k !''# .K(

 h6) 	
<VHE	
)rC   c                     t        |d      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d|v s
J d|        y )	NFr   r   rH   r_   rV   zSC-2u$   details에 'SC-2' 문자열 없음: ra   rc   s         rA   test_sc2_details_contain_sc2z7TestSC2TestContractMissing.test_sc2_details_contain_sc2g  so    $4E%
!k !''# .K(

 iiy 12$ 	
2;-@	
$rC   c                     t        |d      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d|v s
J d|        y )	NFr   r   rH   r_   rV   r,   u-   details에 'test_contract.py' 언급 없음: ra   rc   s         rA   &test_sc2_details_mention_test_contractzATestSC2TestContractMissing.test_sc2_details_mention_test_contractu  so    $4E%
!k !''# .K(

 iiy 12![0 	
;K=I	
0rC   c                     t        |d      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d|v sd|v s
J d	|        y y )
NFr   r   rH   r_   rV   MISSINGr   u,   SC-2 FAIL 메시지에 MISSING/FAIL 없음: ra   rc   s         rA   &test_sc2_fail_message_includes_missingzATestSC2TestContractMissing.test_sc2_fail_message_includes_missing  s|    $4E%
!k !''# .K(

 iiy 12K'6[+@ 	
:;-H	
@+@'rC   N)rm   rn   ro   rp   r   r   r   r   rq   rC   rA   r   r   W  s    O



rC   r   c                   (    e Zd ZdZd Zd Zd Zd Zy)TestSC3SchemaMissinguL   shared/schemas/{name}.schema.json 파일이 없으면 FAIL + SC-3 메시지.c                     t        |d      \  }}t        j                  dt        |      t        |            }|d   dk(  s
J d|        y )NFr   task-test-sc3rH   rK   r   u)   schema.json 누락 시 FAIL 기대, got: rM   rP   s        rA   test_sc3_status_failz)TestSC3SchemaMissing.test_sc3_status_fail  sa    $4U%
!k !''# .K(

 h6) 	
7x@	
)rC   c                     t        |d      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d|v s
J d|        y )	NFr   r   rH   r_   rV   zSC-3u$   details에 'SC-3' 문자열 없음: ra   rc   s         rA   test_sc3_details_contain_sc3z1TestSC3SchemaMissing.test_sc3_details_contain_sc3  so    $4U%
!k !''# .K(

 iiy 12$ 	
2;-@	
$rC   c                     t        |d      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d|v sd|j                         v s
J d	|        y y )
NFr   r   rH   r_   rV   zschema.jsonr>   u(   details에 schema 관련 언급 없음: )rB   r   rN   rO   rb   lowerrc   s         rA   $test_sc3_details_mention_schema_jsonz9TestSC3SchemaMissing.test_sc3_details_mention_schema_json  s    $4U%
!k !''# .K(

 iiy 12+x;;L;L;N/N 	
6{mD	
N/N+rC   c                     t        |d      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d|v sd|v s
J d	|        y
y
)u7   SC-3 실패 시 SC-4, SC-5는 SKIP 처리돼야 한다.Fr   ztask-test-sc3-cascaderH   r_   rV   	SC-4 SKIP	SC-4 FAILu?   SC-3 실패 후 SC-4가 SKIP 또는 cascade FAIL이어야 함: Nra   rc   s         rA   !test_sc3_fail_causes_sc4_sc5_skipz6TestSC3SchemaMissing.test_sc3_fail_causes_sc4_sc5_skip  s|    $4U%
!k !''+ .K(

 iiy 12k)[K-G 	
Mk][	
G-G)rC   N)rm   rn   ro   rp   r   r   r   r   rq   rC   rA   r   r     s    V



rC   r   c                   @    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zy
)TestSC4SC5SampleSchemaMismatchu   
    sample.normal.json 또는 sample.edge.json이 schema.json을 위반하면
    FAIL + SC-4 혹은 SC-5 메시지가 포함돼야 한다.

    sample 파일 위치: schemas_dir/{worker_name}.sample.normal.json
    c                     ddd}t        ||      \  }}t        j                  dt        |      t        |            }|d   dk(  s
J d	|        y
)uH   normal sample의 keyword_count가 integer가 아닌 string → SC-4 FAILr   
not-an-intr   r   task-test-sc4rH   rK   r   u5   normal sample 타입 불일치 시 FAIL 기대, got: NrM   rQ   rR   
bad_normalr9   r<   rS   s         rA   )test_sc4_normal_sample_type_mismatch_failzHTestSC4SC5SampleSchemaMismatch.test_sc4_normal_sample_type_mismatch_fail  sk    !',G
$4J%
!k !''# .K(

 h6) 	
CF8L	
)rC   c                     ddd}t        ||      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d	|v s
J d
|        y )Nr   r   r   r   r   rH   r_   rV   zSC-4u6   normal sample 불일치 시 details에 'SC-4' 없음: ra   rQ   rR   r   r9   r<   rS   rd   s          rA   test_sc4_details_contain_sc4z;TestSC4SC5SampleSchemaMismatch.test_sc4_details_contain_sc4  sy    !',G
$4J%
!k !''# .K(

 iiy 12$ 	
D[MR	
$rC   c                     ddd}t        ||      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d	|v s
J d
|        y )Nr   r   r   r   r   rH   r_   rV   r   u.   SC-4 실패 시 'SC-4 FAIL' 메시지 없음: ra   r   s          rA   %test_sc4_details_contain_fail_messagezDTestSC4SC5SampleSchemaMismatch.test_sc4_details_contain_fail_message  sy    !',G
$4J%
!k !''# .K(

 iiy 12k) 	
<[MJ	
)rC   c                     ddi}t        ||      \  }}t        j                  dt        |      t        |            }|d   dk(  s
J d|        y	)
uE   edge sample에서 required 필드(keyword_count) 누락 → SC-5 FAILr	   	edge-onlyr   task-test-sc5rH   rK   r   u0   edge sample 필드 누락 시 FAIL 기대, got: NrM   )rQ   rR   bad_edger9   r<   rS   s         rA   0test_sc5_edge_sample_missing_required_field_failzOTestSC4SC5SampleSchemaMismatch.test_sc5_edge_sample_missing_required_field_fail  sk    {+$4(%
!k !''# .K(

 h6) 	
>vhG	
)rC   c                     ddi}t        ||      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d|v s
J d	|        y )
Nr	   r   r   r   rH   r_   rV   zSC-5u4   edge sample 불일치 시 details에 'SC-5' 없음: ra   )rQ   rR   r   r9   r<   rS   rd   s          rA   test_sc5_details_contain_sc5z;TestSC4SC5SampleSchemaMismatch.test_sc5_details_contain_sc5  sy    {+$4(%
!k !''# .K(

 iiy 12$ 	
B;-P	
$rC   c                     t        |d      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d|v s
J d|        |d	   d
v sJ d|d	           y)uG   edge sample 파일 자체가 없으면 FAIL이 아닌 WARN (SC-5 WARN).F)r   ztask-test-sc5-missingrH   r_   rV   z	SC-5 WARNu/   edge sample 파일 없으면 SC-5 WARN 기대: rK   )WARNrL   u-   edge sample 누락은 WARN/PASS 기대, got: Nra   rc   s         rA   &test_sc5_edge_sample_not_found_is_warnzETestSC4SC5SampleSchemaMismatch.test_sc5_edge_sample_not_found_is_warn*  s    $4%%
!k !''+ .K(

 iiy 12k) 	
=k]K	
) h#33 	
;F8<L;MN	
3rC   c                     dddd}t        ||      \  }}t        j                  dt        |      t        |            }|d   d	k(  s
J d
|        y)u[   additionalProperties: false 스키마에서 extra 필드 포함 normal sample → SC-4 FAILr      oops)r	   r
   extra_fieldr   ztask-test-sc4-extrarH   rK   r   uU   extra 필드 포함 normal sample + additionalProperties:false → FAIL 기대, got: NrM   r   s         rA   9test_sc4_normal_sample_extra_field_fails_if_no_additionalzXTestSC4SC5SampleSchemaMismatch.test_sc4_normal_sample_extra_field_fails_if_no_additional>  so    !'!FS
$4J%
!k !'') .K(
 h6) 	
cdjckl	
)rC   c                     t        |d      \  }}t        j                  dt        |      t        |            }dj	                  |d         }d|v s
J d|        |d	   d
k(  sJ y)u3   normal sample 파일 자체가 없으면 SC-4 FAIL.F)r   ztask-test-sc4-no-filerH   r_   rV   r   u*   normal sample 없으면 SC-4 FAIL 기대: rK   r   Nra   rc   s         rA   +test_sc4_normal_sample_missing_file_is_failzJTestSC4SC5SampleSchemaMismatch.test_sc4_normal_sample_missing_file_is_failN  s    $4E%
!k !''+ .K(

 iiy 12k) 	
8F	
) h6)))rC   N)rm   rn   ro   rp   r   r   r   r   r   r   r   r   rq   rC   rA   r   r     s/    





(
 *rC   r   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)"TestSC6PydanticSchemaFieldMismatchu  
    Pydantic 모델에 정의된 필드와 JSON Schema에 정의된 필드가 다를 때
    FAIL + SC-6 메시지가 있어야 한다.

    _get_pydantic_fields()를 mock하여 실제 동적 임포트 없이 시뮬레이션한다.
    반환값: (fields_set | None, detail_msg)
    c                    t        |      \  }}h ddf}t        j                  j                  t        d|      5  t	        j
                  dt        |      t        |            }ddd       d   d	k(  s
J d
|        y# 1 sw Y   xY w)uK   Pydantic 모델에 JSON Schema에 없는 extra 필드 존재 → SC-6 FAIL.>   r	   r
   extra_pydantic_fieldmock import OK_get_pydantic_fieldsr|   ztask-test-sc6-extrarH   NrK   r   u=   Pydantic 모델 extra 필드 불일치 시 FAIL 기대, got: rB   r   r   r   r   rN   rO   rQ   rR   r9   r<   mock_fieldsrS   s         rA   )test_sc6_pydantic_extra_field_status_failzLTestSC6PydanticSchemaFieldMismatch.test_sc6_pydantic_extra_field_status_faill  s    $4X$>!k LM]^ZZ0FU`a 	$++-!$\!2,F	 h6) 	
KF8T	
)	 	s   +BB
c                 V   t        |      \  }}h ddf}t        j                  j                  t        d|      5  t	        j
                  dt        |      t        |            }ddd       dj                  d	         }d
|v s
J d|        d|v s
J d|        y# 1 sw Y   :xY w)u,   SC-6 FAIL 시 details에 'SC-6 FAIL' 포함.>   r	   r
   r   r   r   r|   ztask-test-sc6rH   Nr_   rV   zSC-6u1   Pydantic 불일치 시 details에 'SC-6' 없음: r   u,   SC-6 불일치 시 'FAIL' 문자열 없음: rB   r   r   r   r   rN   rO   rb   rQ   rR   r9   r<   r   rS   rd   s          rA   1test_sc6_pydantic_extra_field_details_contain_sc6zTTestSC6PydanticSchemaFieldMismatch.test_sc6_pydantic_extra_field_details_contain_sc6~  s    $4X$>!kKM]^ZZ0FU`a 	$++'!$\!2,F	 iiy 12$ 	
?}M	
$ $ 	
:;-H	
$	 	s   +BB(c                    t        |      \  }}dhdf}t        j                  j                  t        d|      5  t	        j
                  dt        |      t        |            }ddd       d   d	k(  s
J d
|        y# 1 sw Y   xY w)uH   JSON Schema에 있는 필드가 Pydantic 모델에 없음 → SC-6 FAIL.r	   r   r   r|   ztask-test-sc6-missingrH   NrK   r   u4   Pydantic 모델 필드 부족 시 FAIL 기대, got: r   r   s         rA   )test_sc6_schema_missing_field_status_failzLTestSC6PydanticSchemaFieldMismatch.test_sc6_schema_missing_field_status_fail  s    $4X$>!k "{$45ZZ0FU`a 	$++/!$\!2,F	 h6) 	
B6(K	
)	 	s   +B  B	c                 :   t        |      \  }}ddhdf}t        j                  j                  t        d|      5  t	        j
                  dt        |      t        |            }ddd       d	j                  d
         }d|v s
J d|        y# 1 sw Y   ,xY w)uJ   Pydantic 필드가 JSON Schema 필드와 완전히 일치하면 SC-6 PASS.r	   r
   r   r   r|   ztask-test-sc6-passrH   Nr_   rV   z	SC-6 PASSu-   Pydantic 필드 일치 시 SC-6 PASS 기대: r   r   s          rA   test_sc6_matching_fields_passz@TestSC6PydanticSchemaFieldMismatch.test_sc6_matching_fields_pass  s    $4X$>!k "?35EFZZ0FU`a 	$++,!$\!2,F	 iiy 12k) 	
;K=I	
)	 	s   +BBc                 F   t        |      \  }}d}t        j                  j                  t        d|      5  t	        j
                  dt        |      t        |            }ddd       dj                  d         }d	|v s
J d
|        |d   dk(  sJ y# 1 sw Y   6xY w)u6   Pydantic 모델 동적 임포트 실패 시 SC-6 FAIL.)Nz Dynamic import FAILED: SomeErrorr   r|   ztask-test-sc6-import-failrH   Nr_   rV   z	SC-6 FAILu0   Pydantic 임포트 실패 시 SC-6 FAIL 기대: rK   r   r   r   s          rA   test_sc6_import_failure_is_failzBTestSC6PydanticSchemaFieldMismatch.test_sc6_import_failure_is_fail  s    $4X$>!k AZZ0FU`a 	$++3!$\!2,F	 iiy 12k) 	
>{mL	
) h6)))	 	s   +BB N)	rm   rn   ro   rp   r   r   r   r   r   rq   rC   rA   r   r   c  s     
$
$
"
$*rC   r   c                   (    e Zd ZdZd Zd Zd Zd Zy)TestWorkersDirectoryMissinguF   workers/ 디렉토리가 아예 없으면 SKIP을 반환해야 한다.c                     |dz  }|j                  d       t        j                  dt        |dz        t        |            }|d   dk(  s
J d	|        y
)u/   workers/ 디렉토리 자체가 없으면 SKIP.r$   Tr&   task-test-no-workersr"   rH   rK   r   u2   workers/ 디렉토리 없으면 SKIP 기대, got: Nr0   r   rN   rO   rQ   rR   r<   rS   s       rA   test_no_workers_dir_status_skipz;TestWorkersDirectoryMissing.test_no_workers_dir_status_skip  sk    *$' ''* I!56K(

 h6) 	
@I	
)rC   c                 "   |dz  }|j                  d       t        j                  dt        |dz        t        |            }dj	                  |d         t        |d         d	kD  sJ d
       t        fddD              s
J d        y)u6   SKIP 시 details에 그 이유가 명시돼야 한다.r$   Tr   r   r"   rH   r_   rV   r   u&   SKIP이어도 details에 이유 필요c              3   B   K   | ]  }|j                         v   y wNr   .0kwrd   s     rA   	<genexpr>zYTestWorkersDirectoryMissing.test_no_workers_dir_details_mention_reason.<locals>.<genexpr>  '      
 +##%%
   )z	not foundu   없skipmissingr"   z
no workersu"   SKIP 이유가 details에 없음: N)r0   r   rN   rO   rb   r\   anyrQ   rR   r<   rS   rd   s       @rA   *test_no_workers_dir_details_mention_reasonzFTestWorkersDirectoryMissing.test_no_workers_dir_details_mention_reason  s    *$' ''* I!56K(

 iiy 126)$%)S+SS) 
V
 
 	> 0}=	> 
rC   c                     |dz  }|j                  d       |dz  }|j                  d       t        j                  dt        |      t        |            }|d   dk(  s
J d	|        y
)u]   workers/ 디렉토리가 있지만 models.py를 가진 하위 디렉토리가 없으면 SKIP.r"   Tr   r$   ztask-test-empty-workersrH   rK   r   u4   models.py 없는 빈 workers/ → SKIP 기대, got: Nr   rP   s        rA   "test_empty_workers_dir_status_skipz>TestWorkersDirectoryMissing.test_empty_workers_dir_status_skip  s    )+4(*$' ''- .K(

 h6) 	
B6(K	
)rC   c                     |dz  }|j                  d       t        j                  dt        |dz        t        |            }dj	                  |d         }d	|v s
J d
|        y)u3   SKIP 반환 시 details에 'SKIP' 문자열 포함.r$   Tr   ztask-test-no-workers-skip-msgnonexistentrH   r_   rV   r   u*   SKIP 반환 시 details에 'SKIP' 없음: N)r0   r   rN   rO   rb   r   s        rA   0test_no_workers_dir_details_contain_skip_messagezLTestWorkersDirectoryMissing.test_no_workers_dir_details_contain_skip_message  sw    *$' ''3 M!9:K(

 iiy 12$`(RS^R_&``$rC   N)rm   rn   ro   rp   r   r   r   r   rq   rC   rA   r   r     s    P
>$
 arC   r   c                   D    e Zd ZdZdedefdZd Zd Zd Z	d Z
d	 Zd
 Zy)TestJsonschemaMissinguL   jsonschema 패키지가 없을 때 SC-4/SC-5 SKIP + 설치 안내 메시지.rR   r    c                     t        |      \  }}t        j                  j                  t        dd      5  t	        j
                  dt        |      t        |            cddd       S # 1 sw Y   yxY w)u4   jsonschema를 None으로 mock하고 verify() 호출._try_import_jsonschemaNr|   ztask-test-no-jsonschemarH   r   )rQ   rR   r9   r<   s       rA   _verify_without_jsonschemaz0TestJsonschemaMissing._verify_without_jsonschema  si    $4X$>!kZZ$  
 		
 #))1!$\!2,		 		 		s   *A))A2c                 j    | j                  |      }dj                  |d         }d|v s
J d|        y)u   jsonschema 없으면 SC-4 SKIP.r_   rV   r   u'   jsonschema 없으면 SC-4 SKIP 기대: Nr   rb   rQ   rR   rS   rd   s       rA   test_no_jsonschema_sc4_skippedz4TestJsonschemaMissing.test_no_jsonschema_sc4_skipped%  D    00:iiy 12k) 	
5k]C	
)rC   c                 j    | j                  |      }dj                  |d         }d|v s
J d|        y)u   jsonschema 없으면 SC-5 SKIP.r_   rV   z	SC-5 SKIPu'   jsonschema 없으면 SC-5 SKIP 기대: Nr  r  s       rA   test_no_jsonschema_sc5_skippedz4TestJsonschemaMissing.test_no_jsonschema_sc5_skipped-  r  rC   c                     | j                  |      }dj                  |d         t        fddD              s
J d        y)uR   설치 안내 메시지(pip install jsonschema)가 details에 포함돼야 한다.r_   rV   c              3   B   K   | ]  }|j                         v   y wr   r   r   s     rA   r   zYTestJsonschemaMissing.test_no_jsonschema_details_contain_install_guide.<locals>.<genexpr>9  r   r   )zpip install jsonschema
jsonschemainstallu    설치 안내 메시지 없음: N)r   rb   r   r  s      @rA   0test_no_jsonschema_details_contain_install_guidezFTestJsonschemaMissing.test_no_jsonschema_details_contain_install_guide5  sV    00:iiy 12 
I
 
 	< .k];	< 
rC   c                 V    | j                  |      }t        |d         dkD  sJ d       y)u;   jsonschema 없어도 details에 내용이 있어야 한다.rV   r   u   details가 비어있음N)r   r\   rQ   rR   rS   s      rA   $test_no_jsonschema_details_not_emptyz:TestJsonschemaMissing.test_no_jsonschema_details_not_empty>  s0    00:6)$%)D+DD)rC   c                 N    | j                  |      }|d   dv sJ d|d           y)u   
        jsonschema 없으면 SC-4/SC-5는 SKIP이지 FAIL이 아니므로
        다른 항목이 모두 PASS면 전체 status는 PASS 또는 WARN.
        rK   )rL   r   uI   jsonschema 없을 때 SC-4/SC-5 SKIP이면 전체 PASS/WARN 기대, got: N)r   r  s      rA   /test_no_jsonschema_status_not_fail_from_sc4_sc5zETestJsonschemaMissing.test_no_jsonschema_status_not_fail_from_sc4_sc5C  sA    
 00:h#33 	
WX^_gXhWij	
3rC   c                 j    | j                  |      }dj                  |d         }d|v s
J d|        y)uJ   jsonschema 없으면 WARN 경고가 details 상단에 표시돼야 한다.r_   rV   r   u,   jsonschema 없으면 WARN 메시지 기대: Nr  r  s       rA   *test_no_jsonschema_warn_message_in_detailsz@TestJsonschemaMissing.test_no_jsonschema_warn_message_in_detailsM  sD    00:iiy 12$ 	
:;-H	
$rC   N)rm   rn   ro   rp   r   dictr   r  r  r  r  r  r  rq   rC   rA   r   r     s7    V4 D 

<E


rC   r   c                   :    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
y	)
TestReturnFormatContractuO   verify() 반환 딕셔너리가 항상 규약된 형식을 따르는지 확인.c                     t        |      \  }}t        j                  dt        |      t        |            }|d   dv sJ d|d           y)u6   status는 PASS|FAIL|WARN|SKIP 중 하나여야 한다.task-format-checkrH   rK   >   r   rL   r   r   u(   status가 유효한 enum 값이 아님: NrM   rP   s        rA   test_status_is_valid_enumz2TestReturnFormatContract.test_status_is_valid_enum]  sa    $4X$>!k ''' .K(

 h#CC 	
6vh7G6HI	
CrC   c                     t        |      \  }}t        j                  dt        |      t        |            }t	        |d   t
              sJ d       |d   D ]  }t	        |t              rJ d|        y)u,   details는 문자열 리스트여야 한다.r  rH   rV   u   details가 list가 아님u!   details 항목이 str이 아님: NrW   )rQ   rR   r9   r<   rS   items         rA   test_details_is_list_of_stringsz8TestReturnFormatContract.test_details_is_list_of_stringsi  s    $4X$>!k ''' .K(

 &+T2O4OO29% 	WDdC(V,MdX*VV(	WrC   c                     t        |      \  }}t        j                  dt        |      t        |            }d|v sJ y )Nr  rH   rK   rM   rP   s        rA   test_has_status_keyz,TestReturnFormatContract.test_has_status_keyu  sD    $4X$>!k ''' .K(

 6!!!rC   c                     t        |      \  }}t        j                  dt        |      t        |            }d|v sJ y )Nr  rH   rV   rM   rP   s        rA   test_has_details_keyz-TestReturnFormatContract.test_has_details_key~  sD    $4X$>!k ''' .K(

 F"""rC   c                     t        |      \  }}t        j                  dt        |      t        |            }h d}t	        |j                               |z
  }|r
J d|        y)uh   반환 딕셔너리에는 status, details 외 추가 키가 없거나 알려진 키만 있어야 한다.r  rH   >   errorsrK   rV   summarychecked_workersu   예상치 못한 키 발견: N)rB   r   rN   rO   setkeys)rQ   rR   r9   r<   rS   allowed_keys
unexpecteds          rA   test_no_extra_unexpected_keysz6TestReturnFormatContract.test_no_extra_unexpected_keys  sh    $4X$>!k ''' .K(

 U',6
K!>zlKK~:rC   c                     |dz  }|j                          t        j                  dt        |dz        t        |            }|d   dk(  sJ y)u;   workers가 없으면 반드시 SKIP이 반환돼야 한다.r$   ztask-format-skipr   rH   rK   r   Nr   r   s       rA    test_skip_status_when_no_workersz9TestReturnFormatContract.test_skip_status_when_no_workers  sU    * ''& M!9:K(

 h6)))rC   c                     t        |d      \  }}t        j                  dt        |      t        |            }|d   dk(  sJ d|d           y)	uE   Worker 하나라도 FAIL이면 전체 status가 FAIL이어야 한다.Fr   ztask-format-fail-propagaterH   rK   r   u)   Worker FAIL → 전체 FAIL 기대, got: NrM   rP   s        rA   'test_fail_status_propagates_from_workerz@TestReturnFormatContract.test_fail_status_propagates_from_worker  sg    $4E%
!k !''0 .K(

 h6) 	
7x8H7IJ	
)rC   N)rm   rn   ro   rp   r  r  r  r  r(  r*  r,  rq   rC   rA   r  r  Z  s*    Y


W"#
L	*
rC   r  r   )(rp   r5   syspathlibr   unittestr   pytest__file__parent_QC_DIRrO   pathinsert_VERIFIERS_DIRr   r   _MODULE_AVAILABLEImportErrormarkskipif
pytestmarkr4   r7   r8   r1   r3   boolr  tuplerB   rE   rs   r   r   r   r   r   r   r  rq   rC   rA   <module>r>     ss  0  
    x.


&
&w<sxxHHOOAs7|$ ;&~chh&HHOOAs>*+") [[d   
 O,H% ), " $*B? #)A>   . (4%  "&"& $!%#"&!%4%
4%4% 	4%
  4% 4%  4% 4% $;4% 4% 4K4% $J4% 4:4%vJb JbpS
 S
t8
 8
~:
 :
BE* E*Xa* a*P>a >aR?
 ?
LP
 P
Y  ""  "!"	"s6   )F FFFFFFFF