
    i"b                     n   d Z ddlZddlZddlZddlmZmZmZ ddlm	Z	 ddl
Z
 e	e      j                  j                  Zej                  j                  d ee             edz  Zej$                  j'                  de      ZeJ ej$                  j+                  e      Zej.                  J ej.                  j1                  e       de	de	fd	Zde	d
ede	fdZde	de	fdZde	dedede	fdZ G d d      Z G d d      Z G d d      Z  G d d      Z! G d d      Z" G d d      Z# G d d      Z$y)u:  
test_pattern_detector.py

scripts/pattern-detector.py 단위 테스트 (TDD)

테스트 항목:
1. 각 패턴 타입 감지 (보고서 내용 모킹)
   - test_missing 감지
   - pyright_error 감지
   - scope_exceeded 감지
   - qc_fail 감지
   - regression 감지
2. 팀 추출 로직 테스트
   - 보고서 내용에서 팀 추출
   - task-timers.json 역참조
   - 팀 추출 실패 시 "unknown"
3. risk_score 계산 테스트
4. 빈 보고서 디렉토리 graceful 처리
5. --days 필터링 테스트
6. team-patterns.json 없는 경우 (신규 생성)
    N)datetime	timedeltatimezone)Pathzpattern-detector.pypattern_detectortmp_pathreturnc                 <    | dz  dz  }|j                  dd       |S )NmemoryreportsTparentsexist_okmkdir)r   reports_dirs     T/home/jay/workspace/.worktrees/task-2116-dev1/scripts/tests/test_pattern_detector.pymake_reports_dirr   0   *    X%	1KdT2    tasksc                     | dz  }|j                  dd       |dz  }|j                  t        j                  d|i      d       | S )Nr   Tr   ztask-timers.jsonr   utf-8encoding)r   
write_textjsondumps)r   r   
memory_dirfs       r   make_task_timersr!   6   sM    H$JTD1''ALLWe,-L@Or   c                 <    | dz  dz  }|j                  dd       |S )Nr   whisperTr   r   )r   whisper_dirs     r   make_whisper_dirr%   >   r   r   r   filenamecontentc                 6    | |z  }|j                  |d       |S )Nr   r   )r   )r   r&   r'   r    s       r   write_reportr)   D   s!    hALL7L+Hr   c                   @    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zy
)TestPatternDetectionu"   각 패턴 타입 감지 테스트c                 `    g d}|D ]%  }t         j                  |      }d|v rJ d|        y)u!   테스트 미작성 패턴 감지)u*   작업 완료. 테스트 없이 배포함.u$   이번 작업은 test 없이 진행.u$   테스트 미작성 상태로 제출u&   일정 문제로 테스트 SKIP 처리u$   시간 부족으로 테스트 생략test_missingztest_missing not detected in: Nr   detect_patternsselfcontentsr'   patternss       r   !test_detect_test_missing_keywordsz6TestPatternDetection.test_detect_test_missing_keywordsR   sH    
   	\G'77@H!X-[1OPW{/[[-	\r   c                 `    g d}|D ]%  }t         j                  |      }d|v rJ d|        y)u#   pyright 에러 반복 패턴 감지)u   pyright 에러 5건 발생z!pyright error: Cannot find moduleu(   타입 에러 발생으로 빌드 실패pyright_errorzpyright_error not detected in: Nr.   r0   s       r   "test_detect_pyright_error_keywordsz7TestPatternDetection.test_detect_pyright_error_keywords_   sI    

   	^G'77@H"h.]2QRYQ\0]].	^r   c                 `    g d}|D ]%  }t         j                  |      }d|v rJ d|        y)u   scope 초과 패턴 감지)u$   scope 초과로 추가 작업 필요u0   범위 초과 발생, 다음 태스크로 이월u%   예상치 못한 추가 작업 발생u   작업이 예상보다 커짐scope_exceededz scope_exceeded not detected in: Nr.   r0   s       r   #test_detect_scope_exceeded_keywordsz8TestPatternDetection.test_detect_scope_exceeded_keywordsj   sI    
   	`G'77@H#x/_3ST[S^1__/	`r   c                 `    g d}|D ]%  }t         j                  |      }d|v rJ d|        y)u   QC FAIL 패턴 감지)u   QC FAIL — 재작업 필요zqc_verify: FAILu   검증 결과: FAILqc_failzqc_fail not detected in: Nr.   r0   s       r   test_detect_qc_fail_keywordsz1TestPatternDetection.test_detect_qc_fail_keywordsv   sG    

   	RG'77@H(Q,Eg[*QQ(	Rr   c                 `    g d}|D ]%  }t         j                  |      }d|v rJ d|        y)u   회귀 발생 패턴 감지)u   배포 후 회귀 발생 확인u'   regression 발생, 기존 기능 깨짐u   기존 테스트 실패 발생
regressionzregression not detected in: Nr.   r0   s       r   test_detect_regression_keywordsz4TestPatternDetection.test_detect_regression_keywords   sG    

   	XG'77@H8+W/KG;-WW+	Xr   c                 b    d}t         j                  |      }t        |      dk(  s
J d|        y)u'   정상 보고서에서 패턴 미감지u   
# task-100.1 완료 보고서

## 테스트 결과
모든 테스트 통과: 45/45 PASSED
pyright: 에러 없음
QC 통과
회귀 없음
r   zFalse positives detected: N)r   r/   lenr1   r'   r3   s      r   test_no_false_positivesz,TestPatternDetection.test_no_false_positives   s:     $33G<8}!J%?z#JJ!r   c                 V    d}t         j                  |      }d|v sJ d|v sJ d|v sJ y)u   복수 패턴 동시 감지u?   테스트 없이 배포. 또한 pyright 에러 발생. QC FAIL.r-   r6   r<   Nr.   rC   s      r   test_detect_multiple_patternsz2TestPatternDetection.test_detect_multiple_patterns   sA    S#33G<)))(***H$$$r   c                 >    d}t         j                  |      }d|v sJ y)u0   qc_fail: qc_verify.*FAIL 정규식 패턴 감지u(   qc_verify: status=FAIL, 재시도 필요r<   Nr.   rC   s      r   $test_detect_case_insensitive_qc_failz9TestPatternDetection.test_detect_case_insensitive_qc_fail   s%    <#33G<H$$$r   N)__name__
__module____qualname____doc__r4   r7   r:   r=   r@   rD   rF   rH    r   r   r+   r+   O   s3    ,\	^
`	R	XK%%r   r+   c                   :    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
y	)
TestTeamExtractionu+   보고서에서 팀 추출 로직 테스트c                 D    d}t         j                  |di       }|dk(  sJ y)u/   보고서 내용에서 팀 추출 (팀: 패턴)    **팀:** dev1-team
작업 완료task-100.1.md	dev1-teamNr   extract_team_from_contentr1   r'   teams      r   %test_extract_team_from_report_contentz8TestTeamExtraction.test_extract_team_from_report_content   *    599'?TVW{"""r   c                 D    d}t         j                  |di       }|dk(  sJ y)u-   보고서 내용에서 team_id: 패턴 추출u    team_id: dev2-team
작업 내용rR   	dev2-teamNrT   rV   s      r   $test_extract_team_from_team_id_fieldz7TestTeamExtraction.test_extract_team_from_team_id_field   rY   r   c                 R    ddddi}d}t         j                  |d|      }|dk(  sJ y)u(   task-timers.json에서 team_id 역참조task-200	dev3-teamtask_idteam_idu   일반 보고서 내용task-200.1.mdNrT   r1   r   r'   rW   s       r   "test_extract_team_from_task_timersz5TestTeamExtraction.test_extract_team_from_task_timers   sC     %&
 ,99'?TYZ{"""r   c                 D    d}t         j                  |di       }|dk(  sJ y)u$   팀 추출 실패 시 unknown 반환u   팀 정보 없는 보고서ztask-999.1.mdunknownNrT   rV   s      r   "test_extract_team_unknown_fallbackz5TestTeamExtraction.test_extract_team_unknown_fallback   s*    /99'?TVWy   r   c                 R    ddddi}d}t         j                  |d|      }|dk(  sJ y)u:   보고서 내용의 팀 정보가 task-timers보다 우선ztask-300r_   r`   rQ   ztask-300.1.mdrS   NrT   rd   s       r   .test_extract_team_priority_content_over_timerszATestTeamExtraction.test_extract_team_priority_content_over_timers   sC     %&
 699'?TYZ{"""r   c                     t         j                  d      dk(  sJ t         j                  d      dk(  sJ t         j                  d      J y)u   파일명에서 task-id 추출rR   ztask-100ztask-200.2.mdr^   zrandom-report.mdN)r   extract_task_id_from_filename)r1   s    r   "test_extract_task_id_from_filenamez5TestTeamExtraction.test_extract_task_id_from_filename   sN    ==oNR\\\\==oNR\\\\==>PQYYYr   c                 x    g d}|D ]1  \  }}t         j                  |di       }||k(  r#J d|d|d|        y)u"   다양한 팀 표기 형식 추출))u   **팀:** dev2-teamr[   )u   팀: dev3-teamr_   )zteam: dev1-teamrS   zx.mdz	Expected z from z, got NrT   )r1   contents_teamsr'   expectedrW   s        r   !test_extract_team_various_formatsz4TestTeamExtraction.test_extract_team_various_formats   s]    

 "0 	]GX#==gvrRD8#\yF7+VTXS[%\\#	]r   N)rI   rJ   rK   rL   rX   r\   re   rh   rj   rm   rq   rM   r   r   rO   rO      s*    5##
#!
#Z	]r   rO   c                   4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	TestRiskScoreCalculationu   risk_score 계산 테스트c                 ^    ddi}t         j                  |d      }t        |dz
        dk  sJ y)u,   QC FAIL 1건, 보고서 10건 시 risk_scorer<      
   gQ?&.>Nr   calculate_risk_scoreabsr1   pattern_countsscores      r   test_risk_score_single_qc_failz7TestRiskScoreCalculation.test_risk_score_single_qc_fail   s6     $Q 55nbI54< 4'''r   c                 b    dddd}t         j                  |d      }t        |dz
        dk  sJ y)	u   복수 패턴 risk_score 계산      ru   )r<   r-   r6   rv   g{Gz?rw   Nrx   r{   s      r   !test_risk_score_multiple_patternsz:TestRiskScoreCalculation.test_risk_score_multiple_patterns   s9     &'AN 55nbI54< 4'''r   c                 >    t         j                  i d      }|dk(  sJ y)u    패턴 없을 때 risk_score = 0rv           Nr   ry   r1   r}   s     r   %test_risk_score_zero_when_no_patternsz>TestRiskScoreCalculation.test_risk_score_zero_when_no_patterns  s      55b"=||r   c                 B    t         j                  ddid      }|dk(  sJ y)u8   보고서 없을 때 risk_score = 0 (0 나누기 방지)r<      r   r   Nr   r   s     r   $test_risk_score_zero_when_no_reportsz=TestRiskScoreCalculation.test_risk_score_zero_when_no_reports
  s$     55y!naH||r   c                     t         j                  }|d   dk(  sJ |d   dk(  sJ |d   dk(  sJ |d   dk(  sJ |d	   d
k(  sJ y)u%   모든 패턴 타입 가중치 검증r<   g333333?r-   g      ?r6   g?r9   g333333?r?   g?N)r   PATTERN_WEIGHTS)r1   weightss     r   #test_risk_score_all_pattern_weightsz<TestRiskScoreCalculation.test_risk_score_all_pattern_weights  sm    "22y!S(((~&$...'3...'(D000|$+++r   c                 f    dddddd}t         j                  |d      }t        |dz
        dk  sJ y)u8   극단적 케이스에서 risk_score가 합리적 범위rv   )r<   r-   r6   r9   r?   ru   g      $@rw   Nrx   r{   s      r   %test_risk_score_clamped_or_reasonablez>TestRiskScoreCalculation.test_risk_score_clamped_or_reasonable  sE      
 !55naH54< 4'''r   N)
rI   rJ   rK   rL   r~   r   r   r   r   r   rM   r   r   rs   rs      s#    %((

,(r   rs   c                   (    e Zd ZdZd Zd Zd Zd Zy)TestEmptyReportsDirectoryu,   빈 디렉토리 및 에러 처리 테스트c                     t        |      }t        |i        t        |       t        j	                  t        |      d      }|d   d   dk(  sJ |d   d   dk(  sJ |d   i k(  sJ y)	u!   빈 보고서 디렉토리 처리   	workspacedayssummarytotal_reports_analyzedr   total_patterns_foundteamsN)r   r!   r%   r   analyze_reportsstrr1   r   r   results       r   +test_empty_reports_dir_returns_empty_resultzETestEmptyReportsDirectory.test_empty_reports_dir_returns_empty_result/  s    &x02&"!11(m 2 

 i !9:a???i !78A===g"$$$r   c                     t        |i        t        |       t        j                  t	        |      d      }|d   d   dk(  sJ y)u4   보고서 디렉토리가 없을 때 graceful 처리r   r   r   r   r   N)r!   r%   r   r   r   )r1   r   r   s      r   %test_nonexistent_reports_dir_gracefulz?TestEmptyReportsDirectory.test_nonexistent_reports_dir_graceful>  sN    2&"!11(m 2 

 i !9:a???r   c                     t        |      }t        |i        t        |       t        |dd       t        |dd       t        j                  t        |      d      }|d   d   d	k\  sJ y
)uJ   읽기 실패 보고서는 건너뜀 (디렉토리를 파일처럼 생성)rR   u'   정상 보고서. 테스트 SKIP 처리task-101.1.md r   r   r   r   ru   Nr   r!   r%   r)   r   r   r   r   s       r   test_unreadable_report_skippedz8TestEmptyReportsDirectory.test_unreadable_report_skippedJ  ss    &x02&" 	[/3\][/26!11(m 2 
 i !9:a???r   c                     t        |      }t        |       t        |dd       t        j	                  t        |      d      }|d   d   dk\  sJ y)	u*   task-timers.json 없어도 graceful 처리rR   u#   **팀:** dev1-team
정상 보고서r   r   r   r   ru   N)r   r%   r)   r   r   r   r   s       r   %test_nonexistent_task_timers_gracefulz?TestEmptyReportsDirectory.test_nonexistent_task_timers_graceful\  s[    &x0"[/3YZ!11(m 2 

 i !9:a???r   N)rI   rJ   rK   rL   r   r   r   r   rM   r   r   r   r   ,  s    6%
@@$@r   r   c                   "    e Zd ZdZd Zd Zd Zy)TestDaysFilteringu   최근 N일 필터링 테스트c                 F   ddl }ddl}t        |      }t        |i        t	        |       t        |dd      }t        |dd      }|j                         dz
  }|j                  t        |      ||f       t        j                  t        |      d	      }|d
   d   dk(  sJ y)u   30일 이전 파일 제외r   Ntask-new.mdu!   **팀:** dev1-team
테스트 SKIPtask-old.md   **팀:** dev1-team
QC FAILi O r   r   r   r   ru   
ostimer   r!   r%   r)   utimer   r   r   )	r1   r   r   r   r   
new_report
old_reportold_timer   s	            r   %test_days_filter_excludes_old_reportsz7TestDaysFiltering.test_days_filter_excludes_old_reportsr  s    &x02&" "0

 ")


 99;.1
Z8X"67!11(m 2 
 i !9:a???r   c                 F   ddl }ddl}t        |      }t        |i        t	        |       t        |dd       t        |dd      }|j                         dz
  }|j                  t        |      ||f       t        j                  t        |      d      }|d	   d
   dk(  sJ y)u(   days=60이면 60일 이내 파일 포함r   Nr      **팀:** dev1-team
정상r   iS; <   r   r   r   r   r   )r1   r   r   r   r   r   r   r   s           r   (test_days_filter_includes_recent_reportsz:TestDaysFiltering.test_days_filter_includes_recent_reports  s    &x02&" 	[-1MN "(


 99;.1
Z8X"67!11(m 2 

 i !9:a???r   c                 F   ddl }ddl}t        |      }t        |i        t	        |       t        |dd       t        |dd      }|j                         dz
  }|j                  t        |      ||f       t        j                  t        |      d      }|d	   d
   dk(  sJ y)u!   days=0이면 모든 파일 포함r   Nz	task-1.mdr   z	task-2.mdu   **팀:** dev2-team
정상i3r   r   r   r   r   )r1   r   r   r   r   very_oldr   r   s           r   test_days_filter_zero_means_allz1TestDaysFiltering.test_days_filter_zero_means_all  s    &x02&"[+/KL[:VW99;/2
X8 45!11(m 2 

 i !9:a???r   N)rI   rJ   rK   rL   r   r   r   rM   r   r   r   r   o  s    )@B@8@r   r   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestOutputFileu#   team-patterns.json 저장 테스트c                    t        |      }t        |i        t        |      }t        |dd       t        j                  t        |      d      }|dz  }t        j                  |t        |             |j                         sJ t        j                  |j                  d            }|d   d	k(  sJ d
|v sJ d|v sJ d|v sJ y)u-   team-patterns.json이 없으면 신규 생성rR   u(   **팀:** dev1-team
테스트 SKIP 처리r   r   team-patterns.jsonr   r   versionru   last_updatedr   r   N)r   r!   r%   r)   r   r   r   save_resultsexistsr   loads	read_text)r1   r   r   r$   r   output_pathdatas          r   #test_creates_new_team_patterns_jsonz2TestOutputFile.test_creates_new_team_patterns_json  s    &x02&&x07	
 "11(m 2 

 "$88%%fc+.>?!!###zz+///ABI!###%%%$D   r   c                    t        |      }t        |i        t        |       t        |dd       t        j                  t        |      d      }|d   dk(  sJ d|v sJ d|v sJ d	|v sJ d
|d	   v sJ d|d	   v sJ d|d	   v sJ y)u   출력 JSON 구조 검증rR   u/   **팀:** dev1-team
QC FAIL — 재작업 필요r   r   r   ru   r   r   r   r   r   highest_risk_teamNr   r   s       r   )test_output_structure_has_required_fieldsz8TestOutputFile.test_output_structure_has_required_fields  s    &x02&">	
 "11(m 2 

 i A%%%'''&   F"""'6)+<<<<%	)::::"fY&7777r   c                 0   t        |      }t        |i        t        |       t        |dd       t        j                  t        |      d      }d|d   v sJ |d   d   }d|v sJ d|v sJ t        |d   t              sJ t        |d   t              sJ y	)
u   팀 항목 구조 검증rR   r   r   r   rS   r   r3   
risk_scoreN)
r   r!   r%   r)   r   r   r   
isinstancelistfloat)r1   r   r   r   	team_datas        r   test_team_entry_structurez(TestOutputFile.test_team_entry_structure  s    &x02&")	
 "11(m 2 

 fWo---7OK0	Y&&&y((()J/666)L15999r   c                 6   t        |      }t        |i        t        |       t        |dd       t        j                  t        |      d      }|d   d   }t        |d         dkD  sJ |d   d   }d	|v sJ d
|v sJ d|v sJ t        |d   t              sJ y)u   패턴 항목 구조 검증rR   r   r   r   r   rS   r3   r   typecountrecent_reportsN)
r   r!   r%   r)   r   r   r   rB   r   r   )r1   r   r   r   r   pattern_entrys         r   test_pattern_entry_structurez+TestOutputFile.test_pattern_entry_structure  s    &x02&")	
 "11(m 2 

 7OK0	9Z()A---!*-a0&&&-'''=000-(894@@@r   c                     t        |      }t        |i        |dz  dz  dz  }t        j                  t	        |      d      }t        j                  |t	        |             |j                         sJ y)u/   whisper 디렉토리가 없으면 자동 생성r   r#   r   r   r   N)r   r!   r   r   r   r   r   )r1   r   r   r   r   s        r   #test_whisper_dir_created_if_missingz2TestOutputFile.test_whisper_dir_created_if_missing2  su    &x02& )I58LL!11(m 2 
 	%%fc+.>?!!###r   N)	rI   rJ   rK   rL   r   r   r   r   r   rM   r   r   r   r     s    -!682:0A2$r   r   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestIntegrationu    analyze_reports 통합 테스트c                    t        |      }t        |ddddi       t        |       t        |dd       t        |dd       t        |dd	       t        j                  t        |      d
      }|d   d   dk(  sJ d|d   v sJ |d   d   d   D ci c]  }|d   |d    }}|j                  dd      dk\  sJ |j                  dd      dk\  sJ |j                  dd      dk\  sJ yc c}w )u#   복수 팀, 복수 보고서 분석r^   r[   r`   rR   u>   **팀:** dev1-team
QC FAIL — 재작업 필요
테스트 SKIPr   u-   **팀:** dev1-team
pyright 에러 3건 발생rc       **팀:** dev2-team
정상 완료r   r   r   r   r   rS   r   r3   r   r   r<   r   ru   r-   r6   N)r   r!   r%   r)   r   r   r   get)r1   r   r   r   pdev1_patternss         r   !test_full_analysis_multiple_teamsz1TestIntegration.test_full_analysis_multiple_teamsJ  s=   &x0
{K	
 	"N	

 	<	

 	/	
 "11(m 2 

 i !9:a???fWo--- 9?w8TU_8`a16AgJ.aa  A.!333  3q888  !4999 bs   C*c                     t        |      }t        |i        t        |       t        |dd       t        |dd       t        j                  t        |      d      }|d   d   d	k(  sJ y
)u1   highest_risk_team이 가장 높은 risk_score 팀rR   u8   **팀:** dev1-team
QC FAIL
테스트 SKIP
pyright 에러rc   r   r   r   r   r   rS   Nr   r   s       r   !test_highest_risk_team_in_summaryz1TestIntegration.test_highest_risk_team_in_summarys  s~    &x02&"I	

 	/	
 "11(m 2 

 i !45DDDr   c                     t        |      }t        |i        t        |       t        |dd       t        j                  t        |      d      }|d   d   dk\  sJ y)	u%   total_patterns_found 합산 정확성rR   u)   **팀:** dev1-team
QC FAIL
테스트 SKIPr   r   r   r   r   Nr   r   s       r   test_total_patterns_countz)TestIntegration.test_total_patterns_count  sm    &x02&" 	9	
 "11(m 2 

 i !78A===r   c                     t        |      }t        |i        t        |       t        |dd       t        j                  t        |      d      }|d   d   d   }t        d |D        d	      }|J d|d
   v sJ y	)u5   recent_reports에 파일명이 포함되는지 확인rR   r   r   r   r   rS   r3   c              3   2   K   | ]  }|d    dk(  s|  yw)r   r<   NrM   ).0r   s     r   	<genexpr>zFTestIntegration.test_recent_reports_list_in_pattern.<locals>.<genexpr>  s     NqyI7M1Ns   Nr   )r   r!   r%   r)   r   r   r   next)r1   r   r   r   r   
qc_patterns         r   #test_recent_reports_list_in_patternz3TestIntegration.test_recent_reports_list_in_pattern  s    &x02&")	
 "11(m 2 

 w4Z@NmNPTU
%%%*-=">>>>r   c                     t        |      }t        |ddddi       t        |       t        |dd       t        j                  t        |      d      }d|d   v sJ y	)
u(   task-timers.json 역참조로 팀 식별ztask-500r_   r`   ztask-500.1.mdu   작업 완료. 테스트 SKIP.r   r   r   Nr   r   s       r   test_task_timers_team_lookupz,TestIntegration.test_task_timers_team_lookup  sx    &x0
{K	
 	" 	,	
 "11(m 2 

 fWo---r   N)	rI   rJ   rK   rL   r   r   r   r   r   rM   r   r   r   r   G  s     *':RE0>(?,.r   r   )%rL   importlib.util	importlibr   sysr   r   r   pathlibr   pytest__file__parent_SCRIPTS_DIRpathinsertr   _MODULE_PATHutilspec_from_file_locationspecmodule_from_specr   loaderexec_moduler   dictr!   r%   r)   r+   rO   rs   r   r   r   r   rM   r   r   <module>r     sr  ,   
 2 2   H~$$++ 3|$ % 33~~--.@,O >>2248 {{    ( )t  t D T t  d c C D W% W%~<] <]H2( 2(t;@ ;@FS@ S@vu$ u$zE. E.r   