
    ^i;                        d Z ddlZddlmc mZ ddlZddlm	Z	 ddl
Z
ej                  j                  d e e	e      j                  j                               ddlmZmZmZ  G d d      ZdZdZ G d	 d
      Z G d d      Zy)uL  
test_report_utils.py

scripts/report_utils.py 단위 테스트 (TDD RED 단계)

테스트 항목:
1. SCQA 형식 보고서 - **A** 부분 정확 추출
2. SCQA 형식 - A 뒤에 **C** 등 다른 Bold 패턴에서 끊김
3. SCQA 형식 - A 뒤에 --- 에서 끊김
4. 비SCQA 형식 - 첫 ## 섹션 본문 추출
5. 아무 패턴도 없는 경우 - 첫 10줄 반환
6. 파일 없는 경우 - None 반환
7. 빈 파일 - None 또는 빈 문자열 처리
8. 500자 초과 truncation
9. max_chars 커스텀 값
10. **A.** 패턴 (마침표 포함)
11. 멀티라인 A 내용
12. 존재하지 않는 경로 → 에러 없이 None 반환
13. extract_report_metadata - test_summary, files_count, unresolved, team_id, duration 추출
14. format_notification_message - 알림 메시지 포맷
15. extract_report_summary max_chars 기본값 1000 변경
    N)Path)extract_report_metadataextract_report_summaryformat_notification_messagec                      e Zd ZdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfd	Z	deddfd
Z
deddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZy)TestExtractReportSummaryu-   report_utils.extract_report_summary 테스트tmp_pathreturnNc                    |dz  }|j                  dd       t        |      }d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}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>   **A**: 패턴에서 A 내용을 정확히 추출해야 한다.	report.mdu   **S**: 상황 설명

**C**: 복잡한 문제 발생

**Q**: 어떻게 해결할까?

**A**: 로그 분석 도구를 도입하여 에러 추적을 자동화했다.
utf-8encodingNis notz%(py0)s is not %(py3)sresultpy0py3assert %(py5)spy5u   로그 분석 도구inz%(py1)s in %(py3)spy1r   

write_textr   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation	selfr	   reportr   @py_assert2@py_assert1@py_format4@py_format6@py_assert0s	            6/home/jay/workspace/scripts/tests/test_report_utils.py!test_scqa_a_colon_extracts_answerz:TestExtractReportSummary.test_scqa_a_colon_extracts_answer*   s    K'^  	 	
 (/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!%/%////%///%////////////////    c                 (   |dz  }|j                  dd       t        |      }d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}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}||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)uJ   **A**: 내용 이후 **Q**: 등 다른 Bold 패턴에서 끊겨야 한다.r   uS   **A**: 내용은 여기까지입니다.

**Q**: 이건 포함되면 안 됩니다.
r   r   Nr   r   r   r   r   r   u    내용은 여기까지입니다.r   r   r   "   이건 포함되면 안 됩니다.not inz%(py1)s not in %(py3)sr   r(   s	            r0   &test_scqa_a_stops_at_next_bold_patternz?TestExtractReportSummary.test_scqa_a_stops_at_next_bold_pattern>   sB   K'g 	 	

 (/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!1;1V;;;;1V;;;1;;;;;;V;;;V;;;;;;;3A36AAAA36AAA3AAAAAA6AAA6AAAAAAAr2   c                 (   |dz  }|j                  dd       t        |      }d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}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}||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:   **A**: 내용 이후 --- 구분선에서 끊겨야 한다.r   u]   **A**: 해결책은 캐시를 적용하는 것이다.
---
이건 포함되면 안 됩니다.
r   r   Nr   r   r   r   r   r   u.   해결책은 캐시를 적용하는 것이다.r   r   r   r4   r5   r7   r   r(   s	            r0   $test_scqa_a_stops_at_horizontal_rulez=TestExtractReportSummary.test_scqa_a_stops_at_horizontal_ruleP   sB   K't 	 	

 (/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!?I?6IIII?6III?IIIIII6III6IIIIIII3A36AAAA36AAA3AAAAAA6AAA6AAAAAAAr2   c                    |dz  }|j                  dd       t        |      }d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}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)uC   SCQA 패턴 없을 때 첫 ## 섹션 본문을 추출해야 한다.r   ua   # 작업 보고서

## 작업 내용

버그를 수정했습니다.

## 결과

테스트 통과
r   r   Nr   r   r   r   r   r   u   버그를 수정했습니다.r   r   r   r   r(   s	            r0   )test_non_scqa_extracts_first_section_bodyzBTestExtractReportSummary.test_non_scqa_extracts_first_section_bodyb   s    K' E 	 	

 (/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!.8.&8888.&888.888888&888&8888888r2   c                 (   |dz  }|j                  dd       t        |      }d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}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}||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)uY   첫 ## 섹션 본문 추출 시 다음 ## 섹션 내용은 포함되지 않아야 한다.r   uy   ## 요약

데이터베이스 연결 문제를 해결했습니다.

## 상세 내용

이건 포함되면 안 됩니다.
r   r   Nr   r   r   r   r   r   u7   데이터베이스 연결 문제를 해결했습니다.r   r   r   r4   r5   r7   r   r(   s	            r0   0test_non_scqa_section_body_excludes_next_sectionzITestExtractReportSummary.test_non_scqa_section_body_excludes_next_sectiono   sE   K' X 	 	

 (/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!HRHFRRRRHFRRRHRRRRRRFRRRFRRRRRRR3A36AAAA36AAA3AAAAAA6AAA6AAAAAAAr2   c                    t        d      D cg c]  }d|dz    d }}|dz  }|j                  dj                  |      d       t        |      }d	}||u}|st	        j
                  d
|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            d	x}}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}
|
|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}
|
|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	c c}w )uT   SCQA도 ## 섹션도 없는 플레인 텍스트는 첫 10줄을 반환해야 한다.      줄    u    : 작업 내용 설명입니다.r   
r   r   Nr   r   r   r   r   r   u   줄 1:r   r   r   u   줄 10:u   줄 11:r5   r7   )ranger   joinr   r    r!   r"   r#   r$   r%   r&   r'   )r)   r	   ilinesr*   r   r+   r,   r-   r.   r/   s              r0   &test_no_pattern_returns_first_10_linesz?TestExtractReportSummary.test_no_pattern_returns_first_10_lines   s   GLRyQ!4!u<=QQK'$))E*W='/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!!x6!!!!x6!!!x!!!!!!6!!!6!!!!!!!"yF""""yF"""y""""""F"""F"""""""&y&&&&y&&&y&&&&&&&&&&&&&&&& Rs   K'c                 (   |dz  }|j                  dd       t        |      }d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}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}||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)uI   10줄 미만 플레인 텍스트는 모든 내용을 반환해야 한다.r   u-   첫 번째 줄
두 번째 줄
세 번째 줄
r   r   Nr   r   r   r   r   r   u   첫 번째 줄r   r   r   u   세 번째 줄r   r(   s	            r0   /test_no_pattern_fewer_than_10_lines_returns_allzHTestExtractReportSummary.test_no_pattern_fewer_than_10_lines_returns_all   s<   K'LW^_'/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!)6))))6)))))))))6)))6))))))))6))))6)))))))))6)))6)))))))r2   c                 n   |dz  }t        |      }d}||u }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)	uA   존재하지 않는 파일 경로는 None을 반환해야 한다.zno_such_file.mdNisz%(py0)s is %(py3)sr   r   r   r   	r   r    r!   r"   r#   r$   r%   r&   r'   r)   r	   missingr   r+   r,   r-   r.   s           r0   "test_nonexistent_file_returns_nonez;TestExtractReportSummary.test_nonexistent_file_returns_none   so    ..'0v~vvvr2   c                    |dz  }	 t        |      }d}||u }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y# t        $ r"}t        j                  d	|        Y d}~yd}~ww xY w)
uU   존재하지 않는 파일 경로 호출 시 예외가 발생하지 않아야 한다.zdoes_not_exist.mdNrL   rN   r   r   r   r      예외가 발생했습니다: )r   r    r!   r"   r#   r$   r%   r&   r'   	Exceptionpytestfail	r)   r	   rQ   r   r+   r,   r-   r.   es	            r0   "test_nonexistent_file_no_exceptionz;TestExtractReportSummary.test_nonexistent_file_no_exception   s    00	>+G4F!!6T>!!!6T!!!!!!6!!!6!!!T!!!!!!! 	>KK8<==	>s   B0B8 8	C#CC#c                 "   |dz  }|j                  dd       t        |      }| }|sedddt        j                         v st	        j
                  |      rt	        j                  |      ndiz  }t        t	        j                  |            d}y)	uF   0 byte 빈 파일은 None 또는 빈 문자열을 반환해야 한다.empty.md r   r   zassert not %(py0)sr   r   N)	r   r   r"   r#   r    r$   r%   r&   r'   )r)   r	   emptyr   r,   @py_format2s         r0   %test_empty_file_returns_none_or_emptyz>TestExtractReportSummary.test_empty_file_returns_none_or_empty   sc    :%g.'. zz66r2   c                    d}|dz  }|j                  d| dd       t        |      }d}||u}|st        j                  d|fd	||f      d
t	        j
                         v st        j                  |      rt        j                  |      nd
t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}t        |      }d}	||	k  }
|
st        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                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	y)uO   1200자짜리 A 내용은 기본 max_chars(1000)자 이내로 잘려야 한다.u  가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가가r   **A**: rC   r   r   Nr   r   r   r   r   r     <=z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} <= %(py6)slenr   r   r   py6assert %(py8)spy8r   r   r    r!   r"   r#   r$   r%   r&   r'   rg   r)   r	   long_contentr*   r   r+   r,   r-   r.   @py_assert5@py_assert4@py_format7@py_format9s                r0   /test_long_answer_truncated_to_default_max_charszHTestExtractReportSummary.test_long_answer_truncated_to_default_max_chars   s   #K'GL>4wG'/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!6{"d"{d""""{d""""""s"""s""""""6"""6"""{"""d"""""""r2   c                 <   d}|dz  }|j                  d| dd       t        |      }d}||u}|st        j                  d|fd	||f      d
t	        j
                         v st        j                  |      rt        j                  |      nd
t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}||v }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndd
t	        j
                         v st        j                  |      rt        j                  |      nd
dz  }	dd|	iz  }
t        t        j                  |
            d}y)u9   500자 이하 내용은 그대로 반환되어야 한다.u   짧은 해결 방법입니다.r   rb   rC   r   r   Nr   r   r   r   r   r   r   )z%(py0)s in %(py2)scontent)r   py2zassert %(py4)spy4r   )r)   r	   ru   r*   r   r+   r,   r-   r.   @py_format3@py_format5s              r0   test_short_answer_not_truncatedz8TestExtractReportSummary.test_short_answer_not_truncated   s    2K'GG9B/'B'/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!&    w&      w   w      &   &       r2   c                    d}|dz  }|j                  d| dd       t        |d      }d	}||u}|st        j                  d
|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}t        |      }d}	||	k  }
|
st        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                  |	      dz  }dd|iz  }t        t        j                  |            d	x}x}
}	y	)u;   max_chars=100 전달 시 100자 이내로 잘려야 한다.u  나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나나r   rb   rC   r   r   d   	max_charsNr   r   r   r   r   r   rd   rf   rg   rh   rj   rk   rl   rm   s                r0   test_custom_max_chars_100z2TestExtractReportSummary.test_custom_max_chars_100   s   "K'GL>4wG'#>!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!6{!c!{c!!!!{c!!!!!!s!!!s!!!!!!6!!!6!!!{!!!c!!!!!!!r2   c                    d}|dz  }|j                  d| dd       t        |d      }d	}||u}|st        j                  d
|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}t        |      }d}	||	k  }
|
st        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                  |	      dz  }dd|iz  }t        t        j                  |            d	x}x}
}	y	)u9   max_chars=50 전달 시 50자 이내로 잘려야 한다.uX  다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다다r   rb   rC   r   r   2   r}   Nr   r   r   r   r   r   rd   rf   rg   rh   rj   rk   rl   r)   r	   ru   r*   r   r+   r,   r-   r.   ro   rp   rq   rr   s                r0   test_custom_max_chars_50z1TestExtractReportSummary.test_custom_max_chars_50   s   K'GG9B/'B'"=!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!6{ b {b    {b      s   s      6   6   {   b       r2   c                    d}|dz  }|j                  d| dd       t        |d      }d	}||u}|st        j                  d
|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}t        |      }d}	||	k\  }
|
st        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                  |	      dz  }dd|iz  }t        t        j                  |            d	x}x}
}	y	)uF   max_chars=1000 전달 시 600자 내용은 잘리지 않아야 한다.u  라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라라r   rb   rC   r   r   rc   r}   Nr   r   r   r   r   r   X  >=z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} >= %(py6)srg   rh   rj   rk   rl   r   s                r0   /test_custom_max_chars_1000_returns_full_contentzHTestExtractReportSummary.test_custom_max_chars_1000_returns_full_content   s   K'GG9B/'B'$?!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!6{!c!{c!!!!{c!!!!!!s!!!s!!!!!!6!!!6!!!{!!!c!!!!!!!r2   c                    |dz  }|j                  dd       t        |      }d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}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)uJ   **A.** 패턴(마침표 포함)도 정상적으로 추출되어야 한다.r   uO   **S.** 상황

**A.** 해결 방법은 인덱스를 추가하는 것입니다.
r   r   Nr   r   r   r   r   r   u8   해결 방법은 인덱스를 추가하는 것입니다.r   r   r   r   r(   s	            r0   test_a_dot_pattern_extractedz5TestExtractReportSummary.test_a_dot_pattern_extracted  s    K'c 	 	

 (/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!ISIVSSSSIVSSSISSSSSSVSSSVSSSSSSSr2   c                 (   |dz  }|j                  dd       t        |      }d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}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}||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)uN   **A.** 패턴 이후 **Q**: 등 다른 Bold 섹션 키에서 끊겨야 한다.r   uM   **A.** 이것이 정답입니다.

**Q**: 이건 포함되면 안 됩니다.
r   r   Nr   r   r   r   r   r   u   이것이 정답입니다.r   r   r   r4   r5   r7   r   r(   s	            r0   %test_a_dot_pattern_stops_at_next_boldz>TestExtractReportSummary.test_a_dot_pattern_stops_at_next_bold  sD   K' 	a 	 	

 (/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!+5+v5555+v555+555555v555v55555553A36AAAA36AAA3AAAAAA6AAA6AAAAAAAr2   c                 r   |dz  }|j                  dd       t        |      }d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}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}||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}||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[   **A**: 이후 멀티라인 내용이 다음 섹션 전까지 모두 포함되어야 한다.r   u>   **A**: 첫 줄
두번째 줄
세번째 줄

**다음 섹션**
r   r   Nr   r   r   r   r   r   u   첫 줄r   r   r   u   두번째 줄u   세번째 줄r   r(   s	            r0   +test_multiline_a_content_all_lines_includedzDTestExtractReportSummary.test_multiline_a_content_all_lines_included#  s   K'] 	 	

 (/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!"yF""""yF"""y""""""F"""F"""""""(&((((&(((((((((&(((&((((((((&((((&(((((((((&(((&(((((((r2   c                    |dz  }|j                  dd       t        |      }d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}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)uY   멀티라인 A 내용 추출 시 다음 **Bold** 섹션은 포함되지 않아야 한다.r   uS   **A**: 첫 줄
두번째 줄

**다음 섹션**
이건 포함되면 안 됩니다.
r   r   Nr   r   r   r   r   r   r4   r5   r7   r   r   r(   s	            r0   &test_multiline_a_excludes_next_sectionz?TestExtractReportSummary.test_multiline_a_excludes_next_section2  s    K' 	r 	 	

 (/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!3A36AAAA36AAA3AAAAAA6AAA6AAAAAAAr2   c                 t   |dz  dz  }t        |      }d}||u }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }dd	|iz  }t        t        j                  |            dx}}y)
uN   존재하지 않는 .done 경로는 예외 없이 None을 반환해야 한다.reportsztask-999.doneNrL   rN   r   r   r   r   rO   )r)   r	   missing_doner   r+   r,   r-   r.   s           r0   0test_nonexistent_done_path_returns_none_no_errorzITestExtractReportSummary.test_nonexistent_done_path_returns_none_no_errorE  ss    )+o='5v~vvvr2   c                    |dz  dz  dz  dz  }t        |      }d}||u }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}y)uK   중첩 경로의 존재하지 않는 파일도 None을 반환해야 한다.abcr   NrL   rN   r   r   r   r   rO   rP   s           r0   0test_deeply_nested_nonexistent_path_returns_nonezITestExtractReportSummary.test_deeply_nested_nonexistent_path_returns_noneM  s|    S.3&,{:'0v~vvvr2   c                    |dz  }|j                  dd       t        |      }d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}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)uK   **A** 패턴이 있으면 ## 섹션보다 우선하여 추출해야 한다.r   uV   ## 작업 내용

섹션 본문 내용입니다.

**A**: SCQA 답변 내용입니다.
r   r   Nr   r   r   r   r   r   u   SCQA 답변 내용입니다.r   r   r   r   r(   s	            r0   %test_scqa_takes_priority_over_sectionz>TestExtractReportSummary.test_scqa_takes_priority_over_sectionY  s    K'o 	 	

 (/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!-7-7777-777-7777777777777777r2   c                 D   dj                  t        d      D cg c]  }d| 	 c}      }|dz  }|j                  | dd       t        |      }d}||u}|st	        j
                  d	|fd
||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }	t        t	        j                  |	            dx}}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c c}w )uH   ## 섹션이 있으면 첫 10줄보다 우선하여 추출해야 한다.rC      rA   r   u,   

## 요약 섹션

섹션 본문입니다.
r   r   Nr   r   r   r   r   r   u   섹션 본문입니다.r   r   r   )rE   rD   r   r   r    r!   r"   r#   r$   r%   r&   r'   )r)   r	   rF   lines_beforer*   r   r+   r,   r-   r.   r/   s              r0   /test_section_takes_priority_over_first_10_lineszHTestExtractReportSummary.test_section_takes_priority_over_first_10_linesf  s   yyeAh!?D*!?@K'nTT 	 	

 (/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!(2(F2222(F222(222222F222F2222222 "@s   Fc                 (   |dz  }|j                  dd       t        |      }d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}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}||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)uI   한글 SCQA 보고서에서 A 내용이 정확히 추출되어야 한다.r   u/  **S**: 배포 이후 응답 속도가 현저히 느려졌습니다.

**C**: 데이터베이스 쿼리 최적화가 되지 않아 병목이 발생했습니다.

**Q**: 어떻게 성능을 개선할 수 있을까?

**A**: 인덱스 추가와 N+1 쿼리 제거로 응답 속도를 3배 개선했습니다.
r   r   Nr   r   r   r   r   r   u   인덱스 추가r   r   r   u   응답 속도를 3배 개선r   r(   s	            r0   test_korean_content_in_scqaz4TestExtractReportSummary.test_korean_content_in_scqax  sG   K'j  	 	
 (/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!!+!V++++!V+++!++++++V+++V+++++++-7-7777-777-7777777777777777r2   c                    |dz  }|j                  dd       t        |      }d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}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)uL   한글 ## 섹션 보고서에서 본문이 정확히 추출되어야 한다.r   u   # 2026-03-24 작업 보고서

## 작업 요약

인증 모듈의 토큰 만료 처리 버그를 수정했습니다.
JWT 검증 로직을 개선하여 보안을 강화했습니다.

## 테스트 결과

모든 테스트 케이스 통과
r   r   Nr   r   r   r   r   r   uC   인증 모듈의 토큰 만료 처리 버그를 수정했습니다.r   r   r   r   r(   s	            r0   test_korean_content_in_sectionz7TestExtractReportSummary.test_korean_content_in_section  s    K'2  	 	
 (/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!T^TX^^^^^TX^^^^T^^^^^^X^^^^X^^^^^^^^r2   c                    d}|dz  }|j                  d| dd       t        |      }d}||u}|st        j                  d|fd	||f      d
t	        j
                         v st        j                  |      rt        j                  |      nd
t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}t        |      }d}	||	k\  }
|
st        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                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	y)uQ   기본값이 1000으로 변경되어 600자 내용이 잘리지 않아야 한다.u  마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마마r   rb   rC   r   r   Nr   r   r   r   r   r   r   r   r   rg   rh   rj   rk   rl   r   s                r0   test_default_max_chars_is_1000z7TestExtractReportSummary.test_default_max_chars_is_1000  s   K'GG9B/'B (/!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!6{!c!{c!!!!{c!!!!!!s!!!s!!!!!!6!!!6!!!{!!!c!!!!!!!r2   )__name__
__module____qualname____doc__r   r1   r8   r:   r<   r>   rH   rJ   rR   rZ   r`   rs   rz   r   r   r   r   r   r   r   r   r   r   r   r   r   r    r2   r0   r   r   #   s   70$ 04 0(Bt B B$BT Bd B$9$ 94 9B BRV B$'t ' '	* 	*QU 	*4 D >4 >D >d t 	# 	#QU 	#	! 	! 	!	"$ 	"4 	"	! 	!$ 	!
" 
"QU 
" TT Td TBd Bt B()D )T )Bt B B& RV  RV 8d 8t 83 3QU 3$8D 8T 8"_t _ _,"t " "r2   r   ui  # task-906.1 완료 보고서

**담당**: 오딘 (dev2-team 팀장)

---

## SCQA

**A**: auto_orch.py (591 LOC) + team_lock.py (75 LOC) 구현 완료. pytest 64건 전건 통과. pyright 0 에러.

---

## 생성/수정 파일 목록

- `orchestrator/auto_orch.py` — 메인 오케스트레이터
- `orchestrator/team_lock.py` — 팀별 뮤텍스
- `orchestrator/tests/test_phase3.py` — 테스트
- `~/.config/systemd/user/auto-orch.timer` — 타이머
- `~/.config/systemd/user/auto-orch.service` — 서비스
- `orchestrator/health.json` — 헬스 파일

---

## 테스트 결과 (정량적 증거)

**Phase 3 pytest**: 32/32 passed (0.45s)

**pyright**: 0 errors, 0 warnings

---

## 발견 이슈 및 해결

### 범위 외 미해결 (1건)

1. **qc_verify.py pyright 환경 이슈** — 범위 외 사유: qc_verify.py 자체 수정은 본 작업 범위 외
uO  # task-924.1 InfoKeyword 키워드 우선순위 평가 기능 추가

**담당**: 헤르메스 (dev1-team 팀장)
소요시간: 6분 12초

---

## SCQA

**A**: `prioritize_results()` 함수를 추가하여 백엔드에서 정렬 후 반환하도록 개선.
1. 정보성 키워드: 검색량 높은 순으로 정렬
2. 홍보성 키워드: `informational_count` 높은 순 → 동일 시 검색량 순
3. 각 결과에 `priority` + `priority_reason` 필드 추가
pytest 28건 통과, pyright 에러 0건

---

## 생성/수정 파일 목록

- `workers/info_keyword/prioritizer.py` — 우선순위 평가 함수
- `workers/info_keyword/models.py` — priority 필드 추가
- `workers/info_keyword/tests/test_prioritizer.py` — 테스트
- `workers/info_keyword/tests/test_contract.py` — 계약 테스트 갱신

---

## 발견 이슈 및 해결

### 자체 해결 (3건)
1. **black/isort 일부 불일치** — 재실행으로 해결
2. **total_blogs 미존재 가능성** — .get() 기본값 처리
3. **priority=0 falsy 문제** — 해당 없음 확인

### 범위 외 미해결 (0건)
없음
c                       e Zd ZdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfd	Z	deddfd
Z
deddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZy)TestExtractReportMetadatau.   report_utils.extract_report_metadata 테스트r	   r
   Nc                 D   |dz  }|j                  dd       t        |      }|d   }d}||u}|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}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   }||v }|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}}y)uP   SCQA A 절에서 pytest 결과 패턴이 test_summary로 추출되어야 한다.r   u9   **A**: 모듈 구현 완료. pytest 64건 전건 통과.
r   r   test_summaryNr   z%(py1)s is not %(py4)sr   rw   assert %(py6)sri   rV   r   z%(py1)s in %(py4)s64r   r   r    r!   r%   r&   r'   	r)   r	   r*   r   r/   @py_assert3r+   ry   rq   s	            r0    test_test_summary_pytest_patternz:TestExtractReportMetadata.test_test_summary_pytest_pattern  s'   K'H 	 	

 )0n%1T1%T1111%T111%111T111111116.11x11111x1111x11111111111-vn--t-----t----t-----------r2   c                 F   |dz  }|j                  dd       t        |      }|d   }d}||u}|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}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}}y)uB   test_summary에 pyright 0 에러 결과가 포함되어야 한다.r   uD   **A**: 구현 완료. pytest 64건 전건 통과. pyright 0 에러.
r   r   r   Nr   r   r   r   ri   pyrightr   r   r   r   s	            r0   "test_test_summary_includes_pyrightz<TestExtractReportMetadata.test_test_summary_includes_pyright  s    K'S 	 	

 )0n%1T1%T1111%T111%111T11111112F>22y22222y2222y22222222222r2   c                 J   |dz  }|j                  dd       t        |      }|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_   '생성/수정 파일 목록' 섹션에서 bullet(-) 수를 files_count로 추출해야 한다.r   uq   ## 생성/수정 파일 목록

- `file_a.py` — 설명 A
- `file_b.py` — 설명 B
- `file_c.py` — 설명 C
r   r   files_count   ==z%(py1)s == %(py4)sr   r   ri   Nr   r   s	            r0   !test_files_count_from_bullet_listz;TestExtractReportMetadata.test_files_count_from_bullet_list+  s    K'+  	 	
 )0m$))$))))$)))$))))))))))r2   c                 "   |dz  }|j                  dd       t        |      }|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   }	t        |	      }d}
||
k(  }|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}}
d}|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}}y)uc   '범위 외 미해결' 섹션의 항목들이 unresolved_items 리스트로 추출되어야 한다.r   um   ### 범위 외 미해결 (1건)

1. **qc_verify.py pyright 환경 이슈** — 범위 외 사유: 범위 외
r   r   unresolved_countrB   r   r   r   r   ri   Nunresolved_itemsz0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)srg   r   rv   rw   py7assert %(py9)spy9"   qc_verify.py pyright 환경 이슈r   r   r   )r   r   r    r!   r%   r&   r'   rg   r"   r#   r$   )r)   r	   r*   r   r/   r   r+   ry   rq   r,   @py_assert6ro   @py_format8@py_format10s                 r0   test_unresolved_items_extractedz9TestExtractReportMetadata.test_unresolved_items_extracted>  sl   K' B 	 	

 )0().Q.)Q....)Q...)...Q.......,-3s-.3!3.!3333.!333333s333s333-333.333!33333333Tv>P7QRS7TT37TTTTT37TTTT3TTT7TTTTTTTTr2   c                 J   |dz  }|j                  dd       t        |      }|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   }g }||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}}y)uc   '범위 외 미해결' 섹션이 없으면 unresolved_count=0, unresolved_items=[]이어야 한다.r   u:   ## 발견 이슈 및 해결

모든 이슈 해결 완료.
r   r   r   r   r   r   r   r   ri   Nr   r   r   s	            r0   .test_no_unresolved_returns_zero_and_empty_listzHTestExtractReportMetadata.test_no_unresolved_returns_zero_and_empty_listP  s    K'N 	 	

 )0().Q.)Q....)Q...)...Q.......()/R/)R////)R///)///R///////r2   c                 F   |dz  }|j                  dd       t        |      }|d   }d}||u}|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}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}}y)uR   '담당' 줄에서 괄호 안 'dev2-team' 형태 팀 ID가 추출되어야 한다.r   u&   **담당**: 오딘 (dev2-team 팀장)
r   r   team_idNr   r   r   r   ri   	dev2-teamr   r   r   r   s	            r0   %test_team_id_extracted_korean_patternz?TestExtractReportMetadata.test_team_id_extracted_korean_patterna  s    K'5 	 	

 )0i ,, ,,,, ,,, ,,,,,,,,,,/fY//{/////{////{///////////r2   c                 F   |dz  }|j                  dd       t        |      }|d   }d}||u}|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}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}}y)u6   'team:' 패턴에서 팀 ID가 추출되어야 한다.r   u&   team: dev3-team

작업 내용 설명
r   r   r   Nr   r   r   r   ri   z	dev3-teamr   r   r   r   s	            r0   &test_team_id_extracted_english_patternz@TestExtractReportMetadata.test_team_id_extracted_english_patternn  s    K': 	 	

 )0i ,, ,,,, ,,, ,,,,,,,,,,/fY//{/////{////{///////////r2   c                 F   |dz  }|j                  dd       t        |      }|d   }d}||u}|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}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}}y)u?   '소요시간' 패턴에서 duration이 추출되어야 한다.r   u)   소요시간: 12분 30초

작업 내용
r   r   durationNr   r   r   r   ri   u   12분 30초r   r   r   r   s	            r0   test_duration_extractedz1TestExtractReportMetadata.test_duration_extracted  s    K'= 	 	

 )0j!--!----!---!----------2z 22} 22222} 2222}222 22222222r2   c                 H   |dz  }|j                  dd       t        |      }|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}}y)uD   '소요시간' 패턴이 없으면 duration은 None이어야 한다.r   u(   ## 작업 내용

버그 수정 완료.
r   r   r   NrL   z%(py1)s is %(py4)sr   r   ri   r   r   s	            r0   #test_duration_none_when_not_presentz=TestExtractReportMetadata.test_duration_none_when_not_present  s    K'< 	 	

 )0j!)T)!T))))!T)))!)))T)))))))r2   c                 	   |dz  }t        |      }t        |t              }|sddt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            d}|d   }d}||u }|slt        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}g }	|d   }d}
||
u }|}|s|d   }d}||k(  }|}|st        j                  d	|fd||
f      t        j                  |      t        j                  |
      dz  }dd|iz  }|	j                  |       |s_t        j                  dfdf      t        j                  |      t        j                  |      dz  }dd|iz  }|	j                  |       t        j                  |	d      i z  }dd|iz  }t        t        j                  |            dx}x}	x}x}x}
x}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   }g }||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d   }d}||u }|slt        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|d    }d}||u }|slt        j                  d	|fd
||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)!ub   존재하지 않는 파일은 모든 필드가 None/0/빈리스트인 dict를 반환해야 한다.no_such_report.md5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancer   dictr   r   rv   rw   Nr   rL   r   r   r   ri   r   r   z%(py3)s is %(py6)sr   ri   %(py8)srk   r   z%(py11)s == %(py14)spy11py14%(py16)spy16rB   assert %(py19)spy19r   r   r   r   r   )r   r   r   r"   r#   r    r$   r%   r&   r'   r!   append_format_boolop)r)   r	   rQ   r   r   ry   r/   r+   rq   r,   ro   rp   @py_assert10@py_assert13@py_assert12rr   @py_format15@py_format17@py_format18@py_format20s                       r0   ,test_nonexistent_file_returns_empty_metadatazFTestExtractReportMetadata.test_nonexistent_file_returns_empty_metadata  s   00(1&$''''''''z'''z''''''&'''&''''''$'''$''''''''''n%--%----%---%----------Jvm$JJ$,J}0EJJ0E0JJJJ$JJJ$JJJJJJJJJJ0EJJJ0EJJJJJJJJJJJJJJJJJ().Q.)Q....)Q...)...Q.......()/R/)R////)R///)///R///////i (D( D(((( D((( (((D(((((((j!)T)!T))))!T)))!)))T)))))))r2   c                 	   |dz  }|j                  dd       t        |      }t        |t              }|sddt	        j
                         v st        j                  t              rt        j                  t              nddt	        j
                         v st        j                  |      rt        j                  |      nddt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      d	z  }t        t        j                  |            d
}|d   }d
}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d
x}x}}g }	|d   }d
}
||
u }|}|s|d   }d}||k(  }|}|st        j                  d|fd||
f      t        j                  |      t        j                  |
      dz  }dd|iz  }|	j                  |       |s_t        j                  dfdf      t        j                  |      t        j                  |      dz  }dd|iz  }|	j                  |       t        j                  |	d      i z  }dd|iz  }t        t        j                  |            d
x}x}	x}x}x}
x}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!   }g }||k(  }|slt        j                  d|fd ||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d
x}x}}|d"   }d
}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d
x}x}}|d#   }d
}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d
x}x}}y
)$uR   빈 파일은 모든 필드가 None/0/빈리스트인 dict를 반환해야 한다.r\   r]   r   r   r   r   r   r   r   Nr   rL   r   r   r   ri   r   r   r   r   r   rk   r   r   r   r   r   rB   r   r   r   r   r   r   r   )r   r   r   r   r"   r#   r    r$   r%   r&   r'   r!   r   r   )r)   r	   r^   r   r   ry   r/   r+   rq   r,   ro   rp   r   r   r   rr   r   r   r   r   s                       r0   &test_empty_file_returns_empty_metadataz@TestExtractReportMetadata.test_empty_file_returns_empty_metadata  s   :%g.(/&$''''''''z'''z''''''&'''&''''''$'''$''''''''''n%--%----%---%----------Jvm$JJ$,J}0EJJ0E0JJJJ$JJJ$JJJJJJJJJJ0EJJJ0EJJJJJJJJJJJJJJJJJ().Q.)Q....)Q...)...Q.......()/R/)R////)R///)///R///////i (D( D(((( D((( (((D(((((((j!)T)!T))))!T)))!)))T)))))))r2   c                    |dz  }|j                  t        d       t        |      }t        |t              }|sddt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            d	}|d
   }d	}||u}|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}g }	d}|d
   }
||
v }|}|sd}|d
   }||v }|}|st        j                  d|fd||
f      t        j                  |      t        j                  |
      dz  }dd|iz  }|	j                  |       |s_t        j                  dfdf      t        j                  |      t        j                  |      dz  }dd|iz  }|	j                  |       t        j                  |	d      i z  }dd|iz  }t        t        j                  |            d	x}x}	x}x}x}
x}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"   }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#   }	t        |	      }d}||k(  }
|
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}
}d)}|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	}||u}|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}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}}y	).uH   task-906.1 형태 실제 보고서 전체 파싱이 정확해야 한다.task-906.1.mdr   r   r   r   r   r   r   Nr   r   r   r   r   ri   rV   r   r   )z%(py3)s in %(py6)sr   r   rk   )z%(py11)s in %(py14)sr   r   r   rB   r   r   r      r   r   r   r   r   rg   r   r   r   zqc_verify.pyr   r   r   r   )r   TASK_906_1_REPORTr   r   r   r"   r#   r    r$   r%   r&   r'   r!   r   r   rg   )r)   r	   r*   r   r   ry   r/   r+   rq   r,   ro   rp   r   r   r   rr   r   r   r   r   r   r   r   s                          r0   test_task_906_1_full_parsingz6TestExtractReportMetadata.test_task_906_1_full_parsing  s   O++g>(0&$''''''''z'''z''''''&'''&''''''$'''$''''''''''n%1T1%T1111%T111%111T1111111XxX6.1Xx11XYX&BXXYBX5XXXXx1XXXxXXX1XXXXXXXYBXXXXYXXXBXXXXXXXXXXXXXXXm$))$))))$)))$))))))))))().Q.)Q....)Q...)...Q.......,-3s-.3!3.!3333.!333333s333s333-333.333!3333333>(:!;A!>>~!>>>>>~!>>>>~>>>!>>>>>>>>i ,, ,,,, ,,, ,,,,,,,,,,/fY//{/////{////{///////////r2   c                 R   |dz  }|j                  t        d       t        |      }|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)uV   보고서 첫 # 헤더에서 task ID 이후 텍스트를 title로 추출해야 한다.task-924.1.mdr   r   titleu7   InfoKeyword 키워드 우선순위 평가 기능 추가r   r   r   r   ri   N)r   TASK_924_1_REPORTr   r    r!   r%   r&   r'   r   s	            r0   'test_title_extracted_from_first_headingzATestExtractReportMetadata.test_title_extracted_from_first_heading  s|    O++g>(0g["[["[[[[["[[[[[[["[[[[[[[[r2   c                 H   |dz  }|j                  dd       t        |      }|d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}}y)u4   # 헤더가 없으면 title은 None이어야 한다.zno_heading.mdu@   **담당**: 테스트 (dev1-team 팀장)

**A**: 작업 완료.
r   r   r  NrL   r   r   r   ri   r   r   s	            r0   test_title_none_when_no_headingz9TestExtractReportMetadata.test_title_none_when_no_heading  s    O+Q 	 	

 )0g&$&$&&&&$&&&&&&$&&&&&&&r2   c                    |dz  }|j                  dd       t        |      }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        |      }	d}
|	|
k(  }|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}}
|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}}	|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}}	|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}}	|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) u^   ### 자체 해결 섹션에서 이슈 목록이 구조화된 형태로 추출되어야 한다.z	issues.mdu   ### 자체 해결 (2건)
1. **black/isort 불일치** — 재실행으로 해결
2. **total_blogs 미존재** — .get() 기본값 처리
r   r   issues_resolvedr   r   r   r   r   r   N   r   r   rg   r   r   r   r   summaryu   black/isort 불일치r   r   r   ri   
resolutionu   재실행으로 해결rB   u   total_blogs 미존재u   .get() 기본값 처리r   r   r    r!   r%   r"   r#   r$   r&   r'   rg   r)   r	   r*   r   r/   r+   r-   r.   r,   r   r   ro   r   r   ry   rq   s                   r0   test_issues_resolved_extractedz8TestExtractReportMetadata.test_issues_resolved_extracted  s   K'I 	 	 	
 )0 * F**** F*** ******F***F*******+,2s,-22-2222-222222s222s222,222-2222222222'(+I6Q:QQ6:QQQQQ6:QQQQ6QQQ:QQQQQQQQ'(+L9U=UU9=UUUUU9=UUUU9UUU=UUUUUUUU'(+I6Q:QQ6:QQQQQ6:QQQQ6QQQ:QQQQQQQQ'(+L9V=VV9=VVVVV9=VVVV9VVV=VVVVVVVVr2   c                    |dz  }|j                  dd       t        |      }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   }g }||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}y)uT   ### 자체 해결 섹션이 없으면 issues_resolved는 빈 리스트여야 한다.zno_resolved.mduN   **A**: 작업 완료.

## 생성/수정 파일 목록

- `file.py` — 설명
r   r   r  r   r   r   r   r   r   Nr   r   r   r   ri   )
r   r   r    r!   r%   r"   r#   r$   r&   r'   )r)   r	   r*   r   r/   r+   r-   r.   r   ry   rq   s              r0   *test_issues_resolved_empty_when_no_sectionzDTestExtractReportMetadata.test_issues_resolved_empty_when_no_section  s    ,,a 	 	

 )0 * F**** F*** ******F***F*******'(.B.(B....(B...(...B.......r2   c                 ~   |dz  }|j                  dd       t        |      }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        |      }	d}
|	|
k(  }|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}}
|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}}	d}|d   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}}	y)u^   ### 범위 외 미해결 섹션에서 이슈가 구조화된 형태로 추출되어야 한다.zunresolved.mduw   ### 범위 외 미해결 (1건)
1. **qc_verify.py pyright 환경 이슈** — 범위 외 사유: 본 작업 범위 외
r   r   issues_unresolvedr   r   r   r   r   r   NrB   r   r   rg   r   r   r   r   r
  r   r   r   r   ri   u
   범위 외r  r   r  r  s                   r0    test_issues_unresolved_extractedz:TestExtractReportMetadata.test_issues_unresolved_extracted  s   O+g 	 	
 )0","f,,,,"f,,,",,,,,,f,,,f,,,,,,,-.4s./414/14444/1444444s444s444.444/44414444444)*1-i8`<``8<`````8<````8```<````````Kv&9:1=lKK|KKKKK|KKKK|KKKKKKKKKKKr2   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r   r2   r0   r   r     sU   8. .$ .$34 3D 3"*$ *4 *&U U U$0t 0PT 0"0d 0t 00t 0 0"3 3 3
*D 
*T 
* *T *d *$*t * *&0T 0d 02\ \ \
' 
' 
'Wt W W&/4 /D /L L$ Lr2   r   c                      e Zd ZdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfd	Z	deddfd
Z
deddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZy)TestFormatNotificationMessageu2   report_utils.format_notification_message 테스트r	   r
   Nc                    |dz  }|j                  t        d       t        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)u6   반환 메시지에 task_id가 포함되어야 한다.r   r   r   
task-906.1r   r   r   r   r   r   Nr   r   r   r    r!   r%   r"   r#   r$   r&   r'   r)   r	   r*   r   r/   r+   r-   r.   s           r0   "test_basic_format_contains_task_idz@TestFormatNotificationMessage.test_basic_format_contains_task_id,  s    O++g>,\6B%|v%%%%|v%%%|%%%%%%v%%%v%%%%%%%r2   c                    |dz  }|j                  t        d       t        d|      }d}||u}|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}}t        |      }d}||kD  }	|	st        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                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}y)uH   반환 메시지에 보고서 요약(summary)이 포함되어야 한다.r   r   r   r  Nr   r   r   r   r   r   r   )>)z/%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} > %(py6)srg   rh   rj   rk   )r   r   r   r    r!   r"   r#   r$   r%   r&   r'   rg   )r)   r	   r*   r   r+   r,   r-   r.   ro   rp   rq   rr   s               r0   "test_basic_format_contains_summaryz@TestFormatNotificationMessage.test_basic_format_contains_summary5  s   O++g>,\6B!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!6{Q{Q{Qss66{Qr2   c                    |dz  }|j                  t        d       t        d|      }d|v xs d|v }d|v xs
 d|v xs d	|v }g }|}|s|}|sd
ddt        j                         v st        j                  |      rt        j                  |      ndiz  }|j                  |       |sXdddt        j                         v st        j                  |      rt        j                  |      ndiz  }	|j                  |	       t        j                  |d      i z  }
dd|
iz  }t        t        j                  |            dx}}y)u`   반환 메시지에 메타데이터(파일 수, 테스트 결과 등)가 포함되어야 한다.r   r   r   r  6   파일rV   	   테스트r   z%(py2)srv   	has_filesz%(py4)srw   has_testrB   zassert %(py7)sr   N)r   r   r   r"   r#   r    r$   r%   r   r   r&   r'   )r)   r	   r*   r   r"  r#  r,   r/   rx   ry   r.   r   s               r0   #test_basic_format_contains_metadatazATestFormatNotificationMessage.test_basic_format_contains_metadata?  s    O++g>,\6B 6M7X%7	v%U)>U)vBU$y$yH$$$$$$y$$$y$$$$y$$$$$H$$$H$$$$$$$$$$$$r2   c                    |dz  }|j                  t        d       ddd}t        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)uF   done_data에 team_id가 있으면 메시지에 포함되어야 한다.r   r   r   r     r   duration_secondsr  	done_datar   r   r   r   r   r   Nr  	r)   r	   r*   r*  r   r/   r+   r-   r.   s	            r0   !test_done_data_team_id_in_messagez?TestFormatNotificationMessage.test_done_data_team_id_in_messageO  s    O++g> +E	,\6YW${f$$$${f$$${$$$$$$f$$$f$$$$$$$r2   c                    |dz  }|j                  t        d       ddd}t        d||      }g }d	}||v }|}|sd
}	|	|v }
|
}|
s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  }|j                  |       |st        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  }|j                  |       |
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  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}	x}
x}}y)uq   done_data에 duration_seconds가 있으면 사람이 읽기 좋은 형태로 메시지에 포함되어야 한다.r   r   r   r   r&  r'  r  r)  u   12분750u   분r   z%(py3)s in %(py5)sr   r   r   %(py7)sr   z%(py10)s in %(py12)spy10py12%(py14)sr   z%(py17)s in %(py19)spy17r   %(py21)spy21rB   assert %(py24)spy24Nr   r   r   r    r!   r%   r"   r#   r$   r   r   r&   r'   )r)   r	   r*   r*  r   r,   r+   rp   r/   @py_assert9@py_assert11@py_assert16@py_assert18r.   r   @py_format13r   r   @py_format22@py_format23@py_format25s                        r0   *test_done_data_duration_seconds_in_messagezHTestFormatNotificationMessage.test_done_data_duration_seconds_in_messageY  sV   O++g> +E	,\6YW 	GwFw& FEFEVOFuFuFFFFw&FFFwFFFFFF&FFF&FFFFFFFFEVFFFEFFFFFFVFFFVFFFFFFFuFFFuFFFFFFFFFFFFFFFFFFFFFFFFr2   c                    |dz  }t        d|      }d}||u}|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }dd	|iz  }t        t        j                  |            dx}}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)uT   보고서 파일이 없어도 예외 없이 기본 메시지를 반환해야 한다.r   
task-999.1Nr   r   r   r   r   r   r   r   r   )	r   r    r!   r"   r#   r$   r%   r&   r'   )	r)   r	   rQ   r   r+   r,   r-   r.   r/   s	            r0   )test_missing_report_returns_basic_messagezGTestFormatNotificationMessage.test_missing_report_returns_basic_messageh  s    00,\7C!!vT!!!!vT!!!!!!v!!!v!!!T!!!!!!!%|v%%%%|v%%%|%%%%%%v%%%v%%%%%%%r2   c                    |dz  }	 t        d|      }d}||u}|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }dd	|iz  }t        t        j                  |            dx}}y# t        $ r"}t        j                  d
|        Y d}~yd}~ww xY w)uG   보고서 파일이 없을 때 예외가 발생하지 않아야 한다.r   rI  Nr   r   r   r   r   r   rT   )r   r    r!   r"   r#   r$   r%   r&   r'   rU   rV   rW   rX   s	            r0    test_missing_report_no_exceptionz>TestFormatNotificationMessage.test_missing_report_no_exceptionq  s    00	>0wGF!%%6%%%%6%%%%%%6%%%6%%%%%%%%%% 	>KK8<==	>s   B1B9 9	C$CC$c                    |dz  }|j                  t        d       ddd}t        d||      }t        |      }d	}||k  }|st	        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                  |      dz  }dd|iz  }	t        t	        j                  |	            dx}x}}y)u@   반환 메시지가 Telegram 4096자 제한 이내여야 한다.r   r   r   r   r&  r'  r  r)     rd   rf   rg   r   rh   rj   rk   N)r   r   r   rg   r    r!   r"   r#   r$   r%   r&   r'   
r)   r	   r*   r*  r   r+   ro   rp   rq   rr   s
             r0   "test_message_within_telegram_limitz@TestFormatNotificationMessage.test_message_within_telegram_limit  s    O++g> +E	,\6YW6{"d"{d""""{d""""""s"""s""""""6"""6"""{"""d"""""""r2   c                    ddz  }|dz  }|j                  d| ddj                  t        d      D cg c]  }d| d	
 c}      z   d
       t        d|      }t	        |      }d}||k  }|st        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                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}yc c}w )uH   매우 긴 보고서도 4096자 제한을 초과하지 않아야 한다.u   가i  zlong_report.mdrb   u"   

## 생성/수정 파일 목록

r]   r   z- `file_u   .py` — 설명
r   r   ztask-long.1rN  rd   rf   rg   r   rh   rj   rk   N)r   rE   rD   r   rg   r    r!   r"   r#   r$   r%   r&   r'   )r)   r	   rn   r*   rF   r   r+   ro   rp   rq   rr   s              r0   .test_long_report_message_within_telegram_limitzLTestFormatNotificationMessage.test_long_report_message_within_telegram_limit  s   t|,,l^ $1 13577ejkmen;o`ahqcIZ<[;o3pq 	 	
 -]FC6{"d"{d""""{d""""""s"""s""""""6"""6"""{"""d""""""" <ps   E,c                     |dz  }|j                  t        d       	 t        d|d      }d}||u}|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}}y# t        $ r"}t        j                  d|        Y d}~yd}~ww xY w)uJ   done_data=None이어도 예외 없이 메시지가 반환되어야 한다.r   r   r   r  Nr)  r   r   r   r   r   r   rT   )r   r   r   r    r!   r"   r#   r$   r%   r&   r'   rU   rV   rW   )	r)   r	   r*   r   r+   r,   r-   r.   rY   s	            r0    test_none_done_data_no_exceptionz>TestFormatNotificationMessage.test_none_done_data_no_exception  s    O++g>	>0vQUVF!%%6%%%%6%%%%%%6%%%6%%%%%%%%%% 	>KK8<==	>s   B3C 	C=C88C=c                    |dz  }|j                  t        d       t        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)uG   done_data=None이어도 task_id가 메시지에 포함되어야 한다.r   r   r   r  Nr)  r   r   r   r   r   r   r  r  s           r0   $test_none_done_data_contains_task_idzBTestFormatNotificationMessage.test_none_done_data_contains_task_id  s    O++g>,\6TR%|v%%%%|v%%%|%%%%%%v%%%v%%%%%%%r2   c                 J   |dz  }|j                  t        d       t        d|      }g }d}||v }|}|sd}||v }	|	}|sXt        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  }|j                  |       |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  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}}	y)un   미해결 이슈가 있으면 메시지에 경고 표시(⚠️ 또는 '미해결')가 포함되어야 한다.r   r   r   r     ⚠️u	   미해결r   r/  r   r0  r1  r   r2  r3  r6  r   rB   assert %(py17)sr9  Nr>  r)   r	   r*   r   r,   r+   rp   r/   r?  r@  r.   r   rC  r   @py_format16r   s                   r0   (test_unresolved_issues_marked_in_messagezFTestFormatNotificationMessage.test_unresolved_issues_marked_in_message  s    O++g>,\6B:x:x6!:[:[F%:::::x6:::x::::::6:::6:::::::[F:::[::::::F:::F::::::::::::::r2   c                 J   |dz  }|j                  t        d       t        d|      }g }d}||v }|}|sd}||v }	|	}|sXt        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  }|j                  |       |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  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}}	y)u>   미해결 건수(1건)가 메시지에 표시되어야 한다.r   r   r   r  u   1건1r   r/  r   r0  r1  r   r2  r3  r6  r   rB   rY  r9  Nr>  rZ  s                   r0   &test_unresolved_count_shown_in_messagezDTestFormatNotificationMessage.test_unresolved_count_shown_in_message  s    O++g>,\6B0v0v0303&=0000v000v00000000000000003&0003000000&000&00000000000000r2   c                    |dz  }|j                  t        d       t        d|      }g }d}||v }|}|sd}||v }	|	}|	s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  }|j                  |       |st        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  }|j                  |       |	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  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}x}	x}
}y)uk   테스트 결과가 있으면 메시지에 🧪 표시 또는 '테스트' 문구가 포함되어야 한다.r   r   r   r     🧪r!  rV   r   r/  r   r0  r1  r   r2  r3  r6  r   r7  r8  r:  r;  rB   r<  r=  Nr>  )r)   r	   r*   r   r,   r+   rp   r/   r?  r@  rA  rB  r.   r   rC  r   r   rD  rE  rF  s                       r0   "test_test_result_marked_in_messagez@TestFormatNotificationMessage.test_test_result_marked_in_message  sI   O++g>,\6BNvNvN;N;&#8NHNH<NNNNNvNNNvNNNNNNNNNNNNNNNNN;&NNN;NNNNNN&NNN&NNNNNNNHNNNHNNNNNNNNNNNNNNNNNNNNNNNNr2   c                 J   |dz  }|j                  t        d       t        d|      }g }d}||v }|}|sd}||v }	|	}|sXt        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  }|j                  |       |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  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}x}}	y)ub   파일 수가 있으면 메시지에 📁 표시 또는 '파일' 문구가 포함되어야 한다.r   r   r   r     📁r   r   r/  r   r0  r1  r   r2  r3  r6  r   rB   rY  r9  Nr>  rZ  s                   r0   "test_files_count_marked_in_messagez@TestFormatNotificationMessage.test_files_count_marked_in_message  s    O++g>,\6B5v5v5858v#55555v555v55555555555555558v5558555555v555v55555555555555r2   c                    |dz  }|j                  t        d       t        d|      }|j                  }d} ||      }|sddt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      d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}}	y	)u<   첫 줄이 **task-XXX.X 완료 보고** 형태여야 한다.r  r   r   
task-924.1z**task-zLassert %(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.startswith
}(%(py4)s)
}r   )r   rv   rw   ri   Nu   완료 보고**r   r   r   r   r   )r   r  r   
startswithr"   r#   r    r$   r%   r&   r'   r!   )r)   r	   r*   r   r,   r   ro   rq   r/   r+   r-   r.   s               r0   test_new_format_bold_headerz9TestFormatNotificationMessage.test_new_format_bold_header  s    O++g>,\6B  ++ ++++++++v+++v+++ +++++++++++++ * F**** F*** ******F***F*******r2   c                    |dz  }|j                  t        d       t        d|      }|j                         }t	        |      }d}||k\  }|st        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                  |      d
z  }dd|iz  }	t        t        j                  |	            dx}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}}y)uX   보고서 제목(task ID 제거 후 텍스트)이 2번째 줄에 포함되어야 한다.r   r   r   r  r	  r   r   rg   rG   rh   rj   rk   Nu   완료 보고서rB   r   r   r   r   ri   )r   r   r   
splitlinesrg   r    r!   r"   r#   r$   r%   r&   r'   )r)   r	   r*   r   rG   r+   ro   rp   rq   rr   r/   r   ry   s                r0   test_new_format_includes_titlez<TestFormatNotificationMessage.test_new_format_includes_title  s   O++g>,\6B!!#5zQzQzQss55zQ!-U1X-!X----!X---!---X-------r2   c                    |dz  }|j                  t        d       ddi}t        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)ug   done_data에 duration_seconds가 있으면 '6분 12초' 형태로 제목 옆에 표시되어야 한다.r  r   r   r(  t  rg  r)  u
   6분 12초r   r   r   r   r   r   Nr   r  r   r    r!   r%   r"   r#   r$   r&   r'   r+  s	            r0   !test_new_format_includes_durationz?TestFormatNotificationMessage.test_new_format_includes_duration  s    O++g>'-	,\6YW%|v%%%%|v%%%|%%%%%%v%%%v%%%%%%%r2   c                    |dz  }|j                  t        d       t        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@   **핵심 결과** 섹션이 메시지에 포함되어야 한다.r  r   r   rg  u   **핵심 결과**r   r   r   r   r   r   Nro  r  s           r0   $test_new_format_core_results_sectionzBTestFormatNotificationMessage.test_new_format_core_results_section  s    O++g>,\6B","f,,,,"f,,,",,,,,,f,,,f,,,,,,,r2   c                    |dz  }|j                  t        d       t        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)ug   자체 해결 섹션이 있으면 **발견/해결 이슈 섹션이 메시지에 포함되어야 한다.r  r   r   rg  u   **발견/해결 이슈r   r   r   r   r   r   Nro  r  s           r0   test_new_format_issues_sectionz<TestFormatNotificationMessage.test_new_format_issues_section  s    O++g>,\6B'1'61111'6111'111111611161111111r2   c                    |dz  }|j                  t        d       t        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}||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}||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}||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}||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)uL   새 포맷에서 이모지(✅📁🧪⚠️📄)가 제거되어야 한다.r  r   r   rg  u   ✅r5   r7   r   r   r   r   Nrd  ra  rX  u   📄ro  r  s           r0   test_new_format_no_emojisz7TestFormatNotificationMessage.test_new_format_no_emojis  s   O++g>,\6B"uF""""uF"""u""""""F"""F"""""""#vV####vV###v######V###V########vV####vV###v######V###V#######%xv%%%%xv%%%x%%%%%%v%%%v%%%%%%%#vV####vV###v######V###V#######r2   c                    |dz  }|j                  t        d       t        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)uM   핵심 결과가 번호 리스트(1. 2. 3.) 형태로 표시되어야 한다.r  r   r   rg  z1.r   r   r   r   r   r   Nro  r  s           r0   (test_new_format_numbered_list_in_resultszFTestFormatNotificationMessage.test_new_format_numbered_list_in_results!  s    O++g>,\6Btv~tvtvvr2   c                    |dz  }|j                  t        d       ddi}t        d||      }t        |      }d}||k  }|st	        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                  |      dz  }dd|iz  }	t        t	        j                  |	            dx}x}}y)u=   새 포맷도 Telegram 4096자 제한을 준수해야 한다.r  r   r   r(  rn  rg  r)  rN  rd   rf   rg   r   rh   rj   rk   N)r   r  r   rg   r    r!   r"   r#   r$   r%   r&   r'   rO  s
             r0   test_new_format_telegram_limitz<TestFormatNotificationMessage.test_new_format_telegram_limit*  s    O++g>'-	,\6YW6{"d"{d""""{d""""""s"""s""""""6"""6"""{"""d"""""""r2   )r   r   r   r   r   r  r  r$  r,  rG  rJ  rL  rP  rR  rT  rV  r\  r_  rb  re  ri  rl  rp  rr  rt  rv  rx  rz  r   r2   r0   r  r  %  s   <&4 &D &4 D 
%D 
%T 
% %$ %4 %	G4 	GD 	G&$ &4 &> >$ >#4 #D ##t #PT #$	> 	>$ 	>&T &d &; ;$ ;1t 1 1O4 OD O64 6D 6+D +T +	.t 	. 	.&$ &4 &-T -d -2t 2 2$$ $4 $ $ #t # #r2   r  )r   builtinsr"   _pytest.assertion.rewrite	assertionrewriter    syspathlibr   rV   pathinsertstr__file__parentreport_utilsr   r   r   r   r   r  r   r  r   r2   r0   <module>r     s   .  
   3tH~,,334 5 e eG" G"^% P$ N\L \LH	M# M#r2   