
    Ri-                     0   d Z ddlZddlZddlZddlmZ ej                  j                  d e ee	      j                  j                                ej                  d      ZdZdZdZdZd	Z ej$                  e      Z ej$                  e      Z ej$                  e      Z ej$                  e      Z ej$                  e      Zd
ededefdZd
ededefdZ G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z y)z2Tests for token-tracker.py (TDD - RED phase first)    N)Pathztoken-trackerz{"type":"assistant","sessionId":"s1","message":{"model":"claude-sonnet-4-6","role":"assistant","usage":{"input_tokens":1000,"cache_creation_input_tokens":500,"cache_read_input_tokens":2000,"output_tokens":300}},"timestamp":"2026-03-25T10:00:00Z"}u   {"type":"user","sessionId":"s1","message":{"role":"user","content":"작업 ID: task-930.1\n팀: dev1-team\n수행하세요."},"timestamp":"2026-03-25T09:59:00Z"}u   {"type":"user","sessionId":"s2","message":{"role":"user","content":[{"type":"text","text":"작업 ID: task-930.2\n팀: dev2-team"}]},"timestamp":"2026-03-25T10:01:00Z"}z{"type":"assistant","sessionId":"s1","message":{"model":"claude-sonnet-4-6","role":"assistant","usage":{"input_tokens":200,"cache_creation_input_tokens":0,"cache_read_input_tokens":100,"output_tokens":50}},"timestamp":"2026-03-25T10:01:00Z"}zk{"type":"user","sessionId":"x","message":{"role":"user","content":"hi"},"timestamp":"2026-03-25T10:00:00Z"}tmplinesreturnc                 t    | dz  }|j                  dj                  d |D              d       t        |      S )Ns.jsonl
c              3   J   K   | ]  }t        j                  |d         yw)F)ensure_asciiNjsondumps).0ls     Q/home/jay/workspace/.worktrees/task-2117-dev1/scripts/tests/test_token_tracker.py	<genexpr>zsess.<locals>.<genexpr>   s     L4::ae<<Ls   !#zutf-8)encoding)
write_textjoinstr)r   r   ps      r   sessr      s4    iALLLeLLW^L_q6M    tasksc           	          t        d |j                         D              }t        |      }| dz  }|r||z  nd}|j                  t	        j
                  ||||dd             t        |      S )Nc              3   &   K   | ]	  }|d      yw)total_tokensN )r   ts     r   r   zledger.<locals>.<genexpr>!   s     :a.!:s   l.jsonr   )r   total_tasksavg_tokens_per_taskr   summary)sumvalueslenr   r   r   r   )r   r   totalnr   avgs         r   ledgerr+       sh    :5<<>::EE
AhA%!)aCLL

UVWps/tuv q6Mr   c                   L    e 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y)TestParseSessiontmp_pathr   Nc                     t         j                  t        |t        t                    }|d   |d   |d   |d   |d   fdk(  sJ y )Ninput_tokenscache_creation_tokenscache_read_tokensoutput_tokensmessage_count)    i  i,     ttparse_sessionr   UAselfr.   rs      r   test_aggregationz!TestParseSession.test_aggregation,   s^    T(Aq12n%&!"oo


 	
 
r   c                     t         j                  t        |t        t        t
                    }|d   dk(  r|d   dk(  sJ y )Nr0   i  r3   i^  )r9   r:   r   r;   r<   A2r=   s      r   test_multi_assistantz%TestParseSession.test_multi_assistant<   s?    T(Aq"56 D(Q-?3-FFF-Fr   c                     t         j                  t        |t        t                    t        fddD              sJ y )Nc              3   &   K   | ]  }|v  
 y w)Nr   )r   kr?   s     r   r   z6TestParseSession.test_required_keys.<locals>.<genexpr>B   s      
 F
s   )	r0   r1   r2   r3   r4   model
session_idtask_id	timestamp)r9   r:   r   r;   r<   allr=   s     @r   test_required_keysz#TestParseSession.test_required_keys@   s=    T(Aq12 


 
 	
 
r   c                 |    |dz  }|j                  d       t        j                  t        |            d   dk(  sJ y )Nze.jsonl r4   r   )r   r9   r:   r   r>   r.   r   s      r   test_empty_filez TestParseSession.test_empty_fileQ   s:    y 	RA'8A===r   )__name__
__module____qualname__r   r@   rC   rL   rP   r   r   r   r-   r-   +   sQ    
 
$ 
 GT Gd G
4 
D 
"> > >r   r-   c                   \    e 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y)	TestTaskMappingr.   r   Nc                 d    t         j                  t        |t        t                    d   dk(  sJ y )NrI   
task-930.1r8   r>   r.   s     r   test_task_id_stringz#TestTaskMapping.test_task_id_stringX   s)    Xq! 45i@LPPPr   c                 d    t         j                  t        |t        t                    d   dk(  sJ y )NrI   z
task-930.2)r9   r:   r   ULr<   rX   s     r   test_task_id_listz!TestTaskMapping.test_task_id_list[   s)    Xr1 56yA\QQQr   c                 d    t         j                  t        |t        t                    d   dk(  sJ y )NrI   rN   )r9   r:   r   UXr<   rX   s     r   test_no_task_idzTestTaskMapping.test_no_task_id^   s)    Xr1 56yARGGGr   c                 d    t         j                  t        |t        t                    d   dk(  sJ y )NrG   claude-sonnet-4-6r8   rX   s     r   
test_modelzTestTaskMapping.test_modela   s*    Xq! 45g>BUUUUr   c                 d    t         j                  t        |t        t                    d   dk(  sJ y )NrJ   z2026-03-25T09:59:00Zr8   rX   s     r   test_timestampzTestTaskMapping.test_timestampd   s*    Xq! 45kBF\\\\r   )	rQ   rR   rS   r   rY   r\   r_   rb   rd   r   r   r   rU   rU   W   sp    QD QT QR$ R4 RH H HV4 VD V]t ] ]r   rU   c                   Z    e Zd Zddededededef
dZddZdd	Zdd
ZddZ	ddZ
ddZy)TestComputeCosticrccor   c                     ||||dS )N)r0   r2   r1   r3   r   )r>   rg   rh   ri   rj   s        r   uzTestComputeCost.ui   s     !UWjkllr   Nc                 t    t        t        j                  | j                  dd      d      dz
        dk  sJ y )N@B rj   ra   g      2@MbP?absr9   compute_costrl   r>   s    r   test_sonnetzTestComputeCost.test_sonnetl   s5    2??466)y6#ACVWZ^^_bggggr   c                 t    t        t        j                  | j                  dd      d      dz
        dk  sJ y )Nrn   ro   zclaude-opus-4-6g     V@rp   rq   rt   s    r   	test_opuszTestComputeCost.test_opuso   s5    2??466)y6#ACTUX\\]`eeeer   c                 t    t        t        j                  | j                  dd      d      dz
        dk  sJ y )Nrn   ro   zclaude-haiku-4-5-20251001g333333@rp   rq   rt   s    r   
test_haikuzTestComputeCost.test_haikur   s5    2??466)y6#AC^_beefinnnnr   c                 r    t        t        j                  | j                  d      d      dz
        dk  sJ y )Nrn   )rh   ra   g333333?rp   rq   rt   s    r   test_cache_readzTestComputeCost.test_cache_readu   s3    2??466Y6#79LMPSSTW\\\\r   c                 r    t        t        j                  | j                  d      d      dz
        dk  sJ y )Nrn   )ri   ra   g      ?rp   rq   rt   s    r   test_cache_creationz#TestComputeCost.test_cache_creationx   s3    2??466Y6#79LMPTTUX]]]]r   c                 \    t         j                  | j                  dd      d      dk(  sJ y )Nrn   ro   unknowng        )r9   rs   rl   rt   s    r   test_unknown_modelz"TestComputeCost.test_unknown_model{   s(    tvvi9v=yISPPPr   )r   r   r   r   )r   N)rQ   rR   rS   intdictrl   ru   rw   ry   r{   r}   r   r   r   r   rf   rf   h   sT    m3 m mS m mT mhfo]^Qr   rf   c                   <    e Zd ZdeddfdZdeddfdZdeddfdZy)TestDetectAnomalyr.   r   Nc           	          dddddddddd}t        d t        j                  t        ||            D              sJ y )Nd   {Gz?r   cost_estimate_usdr6   g?t1t2t3c              3   ,   K   | ]  }|d    dk(    yw)rI   r   Nr   )r   as     r   r   z2TestDetectAnomaly.test_above_2x.<locals>.<genexpr>   s     XA1Y<4'Xs   )anyr9   detect_anomalyr+   r>   r.   r   s      r   test_above_2xzTestDetectAnomaly.test_above_2x   sL     $'TB#&TB#&TB

 X1B1B6(TUCV1WXXXXr   c                 b    ddddddd}t         j                  t        ||            g k(  sJ y )Nr   r   r   n   )r   r   )r9   r   r+   r   s      r   test_uniform_no_anomalyz)TestDetectAnomaly.test_uniform_no_anomaly   s;    #&TB#&TB
   !!45;;;r   c                     |dz  }|j                  t        j                  i ddid             t        j	                  t        |            g k(  sJ y )Nr    r"   r   r#   )r   r   r   r9   r   r   rO   s      r   
test_emptyzTestDetectAnomaly.test_empty   sJ    x	TZZ":OQR9S TUV  Q(B...r   )rQ   rR   rS   r   r   r   r   r   r   r   r   r      s>    Yd Yt Y< < </4 /D /r   r   c                   l    e 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	y)
TestCLICommandsr.   r   Nc                     |dz  }t         j                  t        |t        t              gt        |             dt        j                  |j                               d   v sJ y )Nledger.json)
glob_pathsledger_pathrW   r   )	r9   scanr   r;   r<   r   r   loads	read_text)r>   r.   lps      r   test_scan_creates_ledgerz(TestCLICommands.test_scan_creates_ledger   sN    %
D1a01s2wGtzz",,.9'BBBBr   c                 j    ddddi}t         j                  dt        ||            }||d   dk(  sJ y )NrW   r5   皙?r   r   )r9   get_taskr+   )r>   r.   r   r?   s       r   test_get_task_foundz#TestCLICommands.test_get_task_found   sB    DsKLKKfXq&9:}>!2d!:::!:r   c                     |dz  }|j                  t        j                  i i d             t        j	                  dt        |            J y )Nr    r#   z
task-999.9)r   r   r   r9   r   r   rO   s      r   test_get_task_missingz%TestCLICommands.test_get_task_missing   sA    x	TZZ" <=>{{<Q0888r   c                     |dz  }|j                  t        j                  i ddid             t        j	                  t        |            d   dk(  sJ y )Nr    r   i  r#   )r   r   r   r9   get_summaryr   rO   s      r   test_get_summaryz TestCLICommands.test_get_summary   sK    x	TZZ".$9O PQR~~c!f%n5===r   c                     dddddddddd}t         j                  t        ||            }t        |      dk(  r|d   d	   d
k(  sJ y )Nr   r   r   r5   r   r   r7   r   rI   r   )r9   r   r+   r'   )r>   r.   r   r   s       r   test_anomaly_commandz$TestCLICommands.test_anomaly_command   s_     $'TB#&TB#'dC

 fXq121v{qtI$6666r   c                    t        j                  d      }t        j                  d      }|dz  }|j                  t        j                  |             |dz  }|j                  t        j                  |             t        j                  t        |      t        |             t        j                  |j                               d   d   }|d   d   d	k(  sJ |d
   dk(  r|d   dk(  sJ y )Nz\{"tasks":{"task-930.1":{"task_id":"task-930.1","team_id":"dev1-team","status":"completed"}}}z|{"tasks":{"task-930.1":{"input_tokens":1000,"output_tokens":300,"total_tokens":3800,"cost_estimate_usd":0.05}},"summary":{}}ztimers.jsonr   r   rW   token_usager   i  team_id	dev1-teamstatus	completed)r   r   r   r   r9   enrichr   r   )r>   r.   tdldtpr   tasks          r   test_enrichzTestCLICommands.test_enrich   s    ZZvwZZ  W  X%
djjn%%
djjn%
		#b'3r7#zz",,.)'2<@M">2d:::I+-$x.K2OOO2Or   )
rQ   rR   rS   r   r   r   r   r   r   r   r   r   r   r   r      sz    C C$ C
;D ;T ;
9d 9t 9
> >$ >
7T 7d 7PD PT Pr   r   c                       e Zd ZdZdede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)
TestTeamREu:   TEAM_RE 정규식 엣지 케이스 테스트 (task-1007.1)textr   c                 <    ddl }|j                  ddd|ddd      S )u<   팀 정보가 포함된 user 메시지 JSON 문자열 생성r   Nusertest)rolecontentz2026-03-25T10:00:00Z)type	sessionIdmessagerJ   r   )r>   r   _jsons      r   _make_user_msgzTestTeamRE._make_user_msg   s,    {{#$*t<3	
 	
r   r.   Nc                     |dz  }|j                  | j                  d      dz   t        z          t        j	                  t        |            }|d   dk(  sJ y)u&   dev1-team, dev2-team 등 정상 매칭r   u   task-1.1
팀: dev1-teamr	   r   r   Nr   r   _A_JSONr9   r:   r   r>   r.   r   r?   s       r   test_dev_team_patternz TestTeamRE.test_dev_team_pattern   sT    y 	T(()CDtKgUVSV$|{***r   c                     |dz  }|j                  | j                  d      dz   t        z          t        j	                  t        |            }|d   dk(  sJ y)u   marketing 명시 패턴 매칭r   ztask-1.1
team: marketingr	   r   	marketingNr   r   s       r   test_marketing_patternz!TestTeamRE.test_marketing_pattern   T    y 	T(()DELwVWSV$|{***r   c                     |dz  }|j                  | j                  d      dz   t        z          t        j	                  t        |            }|d   dk(  sJ y)u   anu-direct 명시 패턴 매칭r   ztask-1.1
team_id: anu-directr	   r   z
anu-directNr   r   s       r   test_anu_direct_patternz"TestTeamRE.test_anu_direct_pattern   sU    y 	T(()HIDPSZZ[SV$||+++r   c                     |dz  }|j                  | j                  d      dz   t        z          t        j	                  t        |            }|d   dk7  sJ |d   dk7  sJ y)uT   team_id: str, 같은 타입 어노테이션이 team_id로 캡처되지 않아야 함r   z task-4.4
team_id: str, team: strr	   r   zstr,r   Nr   r   s       r   test_str_comma_not_capturedz&TestTeamRE.test_str_comma_not_captured   sh    y 	T(()LMPTTW^^_SV$|v%%%|u$$$r   c                     |dz  }|j                  | j                  d      dz   t        z          t        j	                  t        |            }|d   dk7  sJ y)uG   team: int 같은 타입 이름이 team_id로 캡처되지 않아야 함r   ztask-1.1
team: intr	   r   r   Nr   r   s       r   %test_type_annotation_int_not_capturedz0TestTeamRE.test_type_annotation_int_not_captured   sT    y 	T(()>?$FPQSV$|u$$$r   c                     |dz  }|j                  | j                  d      dz   t        z          t        j	                  t        |            }|d   dk7  sJ y)u@   team_id: Optional[str] 이 team_id로 캡처되지 않아야 함r   ztask-1.1
team_id: Optional[str]r	   r   zOptional[str]Nr   r   s       r   test_optional_type_not_capturedz*TestTeamRE.test_optional_type_not_captured   sU    y 	T(()KLtSV]]^SV$|...r   c                     |dz  }|j                  | j                  d      dz   t        z          t        j	                  t        |            }|d   dk(  sJ y)u.   미래 팀 이름 (하이픈 포함)도 매칭r   u   task-1.1
팀: qa-teamr	   r   zqa-teamNr   r   s       r   test_future_team_with_hyphenz'TestTeamRE.test_future_team_with_hyphen   sT    y 	T(()ABTIGSTSV$|y(((r   c                     |dz  }|j                  | j                  d      dz   t        z          t        j	                  t        |            }|d   dk(  sJ y)u   한글 콜론(：) 매칭r   u   task-1.1
팀：dev2-teamr	   r   z	dev2-teamNr   r   s       r   test_korean_colonzTestTeamRE.test_korean_colon  r   r   c                     |dz  }|j                  | j                  d      dz   t        z          t        j	                  t        |            }|d   dk(  sJ y)uE   dev1-team, 뒤에 쉼표가 있어도 정확히 team 이름만 캡처r   z$task-1.1
team: dev1-team, other infor	   r   r   Nr   r   s       r    test_team_id_with_trailing_commaz+TestTeamRE.test_team_id_with_trailing_comma  sV    y 	T(()PQTXX[bbcSV$|{***r   )rQ   rR   rS   __doc__r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r      s    D
3 
3 
+d +t ++t + +, , ,%D %T %%d %t %/ / /)T )d )+$ +4 ++ +$ +r   r   )!r   	importlibr   syspathlibr   pathinsertr   __file__parentimport_moduler9   r   _U_JSON_UL_JSON_A2_JSON_UX_JSONr   r<   r;   r[   rB   r^   r   r   r+   r-   rU   rf   r   r   r   r   r   r   <module>r      s>   8   
  3tH~,,334 5Y_- C p w x DJJwDJJwTZZTZZTZZd D S  T c )> )>X] ]"Q Q./ /.+P +P\O+ O+r   