
    oiK0              	          U d Z ddlZddlZddlZdZdZdZdZdede	fd	Z
d
edeee   ee   f   fdZdedee   dedz  fdZdee   defdZ	 	 d+dedee   dz  dedefdZedk(  r ddlZ ej(                  dd      Zej-                  dd       ej-                  dddd       ej-                  dddd e d!       ej/                         ZdZee   dz  ed"<   ej6                  rHej6                  j9                  d#      D  cg c]#  } | j;                         s| j;                         % c} Z eej<                  eej>                  $      Z  e! ejD                  e d%d&'              ejF                  e d(   d)v rdnd*       yyc c} w ),u<  
tdd_check.py - TDD 순서 검증 verifier

audit-trail.jsonl에서 파일 수정 순서를 분석하여 테스트 파일이
구현 파일보다 먼저 작성되었는지(Test-Driven Development) 확인합니다.

task_id 필드가 audit-trail에 없는 경우 check_files 기반 fallback 검증을 수행합니다.
    Nz1/home/jay/workspace/memory/logs/audit-trail.jsonl)test_)z.test.tsz	.test.tsxz.spec.tsz	.spec.tsx)z/tests/z/test/filepathreturnc                     t         j                  j                  |       }t        D ]  }|j	                  |      s y t
        D ]  }|j                  |      s y | j                  dd      }t        D ]  }||v s y y)u  
    주어진 파일 경로가 테스트 파일인지 판별합니다.

    판별 기준:
    - 파일명이 'test_'로 시작하는 경우 (Python unittest 관례)
    - 파일명이 '.test.ts', '.test.tsx', '.spec.ts', '.spec.tsx'로 끝나는 경우
    - 경로에 '/tests/' 또는 '/test/' 디렉토리가 포함된 경우

    Args:
        filepath: 검사할 파일 절대경로 또는 상대경로

    Returns:
        테스트 파일이면 True, 아니면 False
    T\/F)	ospathbasenameTEST_PREFIXES
startswithTEST_SUFFIXESendswithreplace	TEST_DIRS)r   r   prefixsuffix
normalizedtest_dirs         </home/jay/workspace/teams/dev5/qc/verifiers.bak/tdd_check.py_is_test_filer      s     ww)H v&   V$
 !!$,J z!     
trail_pathc           	      ,   g }g }	 t        | dd      5 }t        |d      D ]=  \  }}|j                         }|s	 |j                  t	        j
                  |             ? 	 ddd       ||fS # t        j                  $ r!}|j                  d| d|        Y d}~|d}~ww xY w# 1 sw Y   DxY w# t        $ r |j                  d	|         Y ||fS t        $ r7}|j                  d
t        |      j                   d|        Y d}~||fS d}~ww xY w)un  
    audit-trail.jsonl 파일을 읽어 엔트리 목록과 파싱 에러 메시지를 반환합니다.

    Args:
        trail_path: audit-trail.jsonl 파일 경로

    Returns:
        (entries, error_messages) 튜플.
        entries: 파싱에 성공한 JSON 객체 목록
        error_messages: 파싱 실패 또는 파일 읽기 오류 메시지 목록
    rzutf-8)encoding   )startzJSON parse error at line z: Nzaudit-trail not found: zFailed to read audit-trail: )open	enumeratestripappendjsonloadsJSONDecodeErrorFileNotFoundErrorOSErrortype__name__)r   entrieserrorsflinenolinees          r   _load_audit_entriesr0   8   s5    GFN*cG4 	M )!1 5 Mzz|MNN4::d#34M	M F? ++ MMM$=fXRs"KLLM	M 	M  >/
|<= F?  N4T!W5E5E4FbLMMF?Ns]   B2 'B&$A/ B&#B2 /B#BB&B##B&&B/+B2 2DD*DDtask_idr*   c           	      v   |D cg c]  }|j                  d      | k(  s| }}|syh d}|D cg c]*  }|j                  d      |v s|j                  d      s)|, }}|sdd|  dt        |       d	gd
S i }|D ]@  }t        j                  j	                  |d         }|j                  dd      }	||vs<|	||<   B |j                         D 
	ci c]  \  }
}	t        |
      s|
|	 }}
}	|j                         D 
	ci c]  \  }
}	t        |
      r|
|	 }}
}	d|  ddt        |       dt        |       dt        |       dg}|s!|s	d|dgz   d
S |j                  d       d|d
S |sN|j                  d       t        |j                               D ]  \  }
}	|j                  d|	 d|
         d|d
S t        |j                               }t        |j                               }t        |j                         d       D ]  \  }
}	|j                  d|	 d|
         t        |j                         d       D ]  \  }
}	|j                  d|	 d|
         ||k  r|j                  d| d| d        d|d
S |j                  d!| d"| d#       d$|d
S c c}w c c}w c c}	}
w c c}	}
w )%u  
    audit-trail 엔트리에서 task_id로 필터링하여 TDD 순서를 검증합니다.

    task_id 필드가 존재하는 엔트리가 없으면 None을 반환하여
    fallback 검증으로 위임합니다.

    Args:
        task_id: 검사 대상 task ID
        entries: audit-trail에서 로드한 전체 엔트리 목록 (시간순 정렬 가정)

    Returns:
        검증 결과 dict, 또는 task_id 매칭 엔트리가 없으면 None
    r1   N>   EditWriteNotebookEdittoolfileSKIPz	task_id='u   ' 엔트리 uC   개 발견, 그러나 파일 변경(Edit/Write/NotebookEdit) 없음statusdetailsts u$   audit-trail 기반 검증 (task_id='z')u   변경 파일 총    개: 테스트    개, 구현    개u   변경된 파일 없음u@   구현 파일 변경 없음, 테스트 파일만 존재 → PASSPASSu;   테스트 파일 없이 구현 파일만 변경됨 → FAILz	  IMPL  [z] FAILc                     | d   S Nr    xs    r   <lambda>z(_verify_by_audit_trail.<locals>.<lambda>   
    1Q4 r   )keyz	  TEST  [c                     | d   S rD   rE   rF   s    r   rH   z(_verify_by_audit_trail.<locals>.<lambda>   rI   r   u   테스트 먼저 수정 (u   ) → 구현 (u
   ) → PASSu   구현 먼저 수정 (u   ) → 테스트 (u   ) → WARN (TDD 순서 위반)WARN)getlenr	   r
   normpathitemsr   r"   sortedminvalues)r1   r*   r/   task_entrieswrite_toolschangedfirst_tsentryr   r<   fp
test_files
impl_filesr;   earliest_test_tsearliest_impl_tss                   r   _verify_by_audit_trailr^   Y   s.   "  'F!!%%	*:g*EAFLF 4K&2eaeeFm{6RWXW\W\]cWd1eGeG9L\1B0C DJ J
 	
  "H $77##E&M2YYtR 8#!#HX	$ (0~~'7MVR=;L"b&MJM'/~~'7QVR}R?P"b&QJQ /wir:
S]O?3z?:K<X[\fXgWhhklG
 $<U;V1VWWYZ W55TUZ--/0 	3FBNNYrd"RD12	3 W55 :,,./:,,./ ))+@ /B2$b-./))+@ /B2$b-./ ++23C2DNScRddnop W55/0@/AARScRd  eC  D  	E W55 G f& NQs3   J%J%J*	J*J*J/'J/J5J5check_filesc           	         | sddgdS | D cg c]  }t        |      s| }}| D cg c]  }t        |      r| }}ddt        |        dt        |       dt        |       dg}|s|s	d|d	gz   dS |s:t        |      D ]  }|j                  d
|         |j                  d       d|dS |s:t        |      D ]  }|j                  d|         |j                  d       d|dS t        |      D ]  }|j                  d
|         t        |      D ]  }|j                  d|         |j                  d       d|dS c c}w c c}w )u  
    check_files 목록에서 테스트 파일과 구현 파일을 분리하여 TDD 준수 여부를 검증합니다.

    audit-trail에 task_id가 없는 경우의 fallback 검증입니다.
    파일 수정 순서는 알 수 없으므로 테스트 파일 존재 여부만 확인합니다.

    Args:
        check_files: 검사 대상 파일 경로 목록

    Returns:
        {"status": "PASS"|"FAIL"|"SKIP", "details": [...]}
    r8   u'   check_files 미제공 — 검증 불가r9   u$   check_files 기반 검증 (fallback)u   파일 총 r>   r?   r@   u   검사할 파일 없음z  TEST  u9   구현 파일 없음, 테스트 파일만 존재 → PASSrA   z  IMPL  uC   구현 파일 존재, 대응하는 테스트 파일 없음 → FAILrB   uR   테스트 파일과 구현 파일 모두 존재 (수정 순서 미확인) → PASS)r   rN   rQ   r"   )r_   r,   rZ   r[   r;   s        r   _verify_by_check_filesra      s    AB
 	

 )=M!,<!=J=(Aa0@!AJA 	/
c+&'s:6G|TWXbTcSddghG
 j W8Q7R-RSS
# 	+ANNXaS>*	+RS W55
# 	+ANNXaS>*	+\] W55 J '!~&'J '!~&'NNgh11? >As   EEEEr=   audit_trail_pathc                 r   |r|nt         }g }t        |      \  }}|rG|s4|j                  |       |rt        |      }||d   z   |d<   |S d|dgz   dS |j                  |       t	        | |      }||r||d   z   |d<   |S |j                  d|  d       |rt        |      }||d   z   |d<   |S d|dgz   dS )u  
    TDD(Test-Driven Development) 준수 여부를 검증합니다.

    검증 우선순위:
    1. audit-trail.jsonl에 task_id 필드가 있는 엔트리가 존재하면
       파일 수정 타임스탬프 순서를 기반으로 검증합니다.
    2. task_id 매칭 엔트리가 없으면 check_files 목록을 기반으로
       테스트 파일 존재 여부를 확인합니다.

    판정 기준:
    - PASS : 테스트 파일이 구현 파일보다 먼저 수정됨,
             또는 테스트 파일만 존재하거나 check_files에 테스트+구현 모두 존재
    - FAIL : 테스트 파일 없이 구현 파일만 존재
    - WARN : 구현 파일이 테스트 파일보다 먼저 수정됨 (TDD 순서 위반이지만 테스트 존재)
    - SKIP : 검사 대상 파일이 없거나 audit-trail을 읽을 수 없고 check_files도 없는 경우

    Args:
        task_id: 검사 대상 task ID
        check_files: fallback 검증에 사용할 파일 경로 목록 (선택)
        audit_trail_path: audit-trail.jsonl 경로
                          (기본값: DEFAULT_AUDIT_TRAIL_PATH)

    Returns:
        {"status": "PASS"|"FAIL"|"WARN"|"SKIP", "details": [...]}
    r;   r8   u*   check_files도 미제공 — 검증 불가r9   u   audit-trail에 task_id='u2   ' 매칭 엔트리 없음 — check_files fallback)DEFAULT_AUDIT_TRAIL_PATHr0   extendra   r^   r"   )	r1   r_   rb   r   details_prefixr*   load_errorsresultaudit_results	            r   verifyrj      s   < &6!;SJ "N /z:G[!!+./<$2VI5F$Fy! )-Y,ZZ 
 	k* *'7;L&4|I7N&NL# 4WI=opq'4*VI->>y !%Q$RR r   __main__u   TDD 순서 검증 verifieruG   예시: tdd_check.py task-42 --check-files src/foo.py,tests/test_foo.py)descriptionepilogu   검사 대상 task ID)helpz--check-fileszFILE1,FILE2,...u=   쉼표로 구분된 파일 경로 목록 (fallback 검증용))metavardefaultrn   z--audit-trailPATHu%   audit-trail.jsonl 경로 (기본값: )cf,)r1   r_   rb   F   )ensure_asciiindentr:   )rA   r8   r   )Nr=   )$__doc__r#   r	   sysrd   r   r   r   strboolr   tuplelistdictr0   r^   ra   rj   r)   argparseArgumentParserparseradd_argument
parse_argsargsrs   __annotations__r_   splitr!   r1   audit_trailrh   printdumpsexit)r,   s   0r   <module>r      s*    	 
N  B!	C D DC E$t*d3i2G,H BP6P6$ZP6 
D[P6f22S	 22d 22n %)HHcT!H H 
	HV z$X$$0XF 	(?@
!L	   45M4NaP	   DBS	D!%!1!1!7!7!<JA	aggiJ))F
 
*$**V%
:;CHH&"&66QA>C 2 Ks   F
F
