
    i9              	          d Z ddlZddlmZ ddlmZmZ ej                  j                  d e	 ee
      j                         j                  j                               ddlmZmZmZmZmZmZ d!de	dede	d	efd
Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d       Z y)"a  Unit tests for impact_scanner.py.

Covers:
  - Scenario 1: Python symbol reverse tracing
  - Scenario 2: TypeScript symbol reverse tracing
  - Scenario 3: COMMON_FILTER behavior
  - Scenario 4: Timeout behavior
  - Scenario 5: Zero references (PASS)
  - Additional edge-case tests
    N)Path)	MagicMockpatch)COMMON_FILTER_parse_diff_linesextract_symbols_pythonextract_symbols_typescriptgrep_referencesscanstdout
returncodestderrreturnc                 D    t               }||_        | |_        ||_        |S N)r   r   r   r   )r   r   r   mocks       8/home/jay/workspace/scripts/tests/test_impact_scanner.py
_make_procr   !   s#    ;D DODKDKK    c                       e Zd Zd Zd Zd Zy)TestPythonSymbolReverseTracingc                 f    |dz  }|j                  d       t        t        |      g       }d|v sJ y )N
premium.pyFdef calculate_premium(age, risk_factor):
    return age * risk_factor
calculate_premium
write_textr   strselftmp_pathsrcsymbolss       r   .test_extract_symbols_returns_calculate_premiumzMTestPythonSymbolReverseTracing.test_extract_symbols_returns_calculate_premium0   s7    %ab(S26"g---r   c                     |dz  }|j                  d       |dz  }|j                  d       t        dt        |      t        |      g      }|D cg c]  }|d   	 }}t        d |D              sJ y c c}w )Nr   r   
billing.pyzhfrom premium import calculate_premium

def process_billing(age):
    return calculate_premium(age, 1.5)
r   filec              3   $   K   | ]  }d |v  
 yw)r&   N .0fs     r   	<genexpr>z]TestPythonSymbolReverseTracing.test_grep_references_finds_referencing_file.<locals>.<genexpr>=   s     8<1$8   r   r
   r   anyr    r!   define_fileref_filerefsr	ref_filess          r   +test_grep_references_finds_referencing_filezJTestPythonSymbolReverseTracing.test_grep_references_finds_referencing_file6   s    -ijl*  K  	L2CMCDTCUV(,-1QvY-	-8i8888 .   A4c                     |dz  }|j                  d       |dz  }|j                  d       t        dt        |      t        |      g      }|D cg c]  }|d   	 }}t        d |D              rJ y c c}w )Nr   zdef calculate_premium(): pass
r&   z&from premium import calculate_premium
r   r'   c              3   $   K   | ]  }d |v  
 yw)r   Nr)   r*   s     r   r-   z[TestPythonSymbolReverseTracing.test_grep_references_excludes_source_file.<locals>.<genexpr>F   s     <Q|q(<r.   r/   r1   s          r   )test_grep_references_excludes_source_filezHTestPythonSymbolReverseTracing.test_grep_references_excludes_source_file?   s    -@Al*EF2CMCDTCUV(,-1QvY-	-<)<<<<< .r8   N)__name__
__module____qualname__r$   r7   r;   r)   r   r   r   r   .   s    .9=r   r   c                       e Zd Zd Zd Zy)"TestTypescriptSymbolReverseTracingc                 f    |dz  }|j                  d       t        t        |      g       }d|v sJ y )NFeatureGate.tsxzimport React from 'react';

export function FeatureGate({ flag, children }: Props) {
    if (!flag) return null;
    return <>{children}</>;
}
FeatureGater   r	   r   )r    r!   tsx_filer#   s       r   4test_extract_symbols_typescript_returns_feature_gatezWTestTypescriptSymbolReverseTracing.test_extract_symbols_typescript_returns_feature_gateP   s@    //  t  	u,S]B?'''r   c                     |dz  }|j                  d       |dz  }|j                  d       t        dt        |      t        |      g      }|D cg c]  }|d   	 }}t        d |D              sJ y c c}w )NrB   zLexport function FeatureGate({ flag, children }: Props) {
    return null;
}
use-feature-access.tszmimport { FeatureGate } from './FeatureGate';

export function useFeatureAccess() {
    return FeatureGate;
}
rC   r'   c              3   $   K   | ]  }d |v  
 yw)rH   Nr)   r*   s     r   r-   zcTestTypescriptSymbolReverseTracing.test_grep_references_finds_use_feature_access.<locals>.<genexpr>]   s     CA*a/Cr.   r/   )r    r!   rE   r3   r4   r5   r6   s          r   -test_grep_references_finds_use_feature_accesszPTestTypescriptSymbolReverseTracing.test_grep_references_finds_use_feature_accessV   s    //mn55  Q  	R}c(mc(m_M(,-1QvY-	-CCCCC .r8   N)r<   r=   r>   rF   rJ   r)   r   r   r@   r@   N   s    (Dr   r@   c                       e Zd Zd Zd Zy)TestCommonFilterc                     dt         v sJ |dz  }|j                  d       t        d      5 }t        dd      |_        t        t        |      t        |      gd	      }d d d        dd
   vsJ y # 1 sw Y   xY w)Ndatazutils.pyzdef data():
    return {}
subprocess.run r   r   r   ztest-filterproject_rootmodified_filestask_idsymbols_checked)r   r   r   r   return_valuer   r   r    r!   r"   mock_runresults        r    test_data_not_in_symbols_checkedz1TestCommonFilter.test_data_not_in_symbols_checkedg   s    &&&#56#$ 	h$.bQ$GH!s8}c#hZYfgF	h V$56666	h 	hs   4A22A;c                 :   dj                  d t        D              }|dz  }|j                  |dz          t        d      5 }t	        dd      |_        t        t        |      t        |      g      }d d d        t        D ]  }|d	   vrJ  y # 1 sw Y   xY w)
N
c              3   (   K   | ]
  }d | d  yw)def z(): passNr)   )r+   ns     r   r-   zITestCommonFilter.test_all_common_filter_names_excluded.<locals>.<genexpr>q   s     C4s(+Cs   zall_common.pyrO   rP   r   rQ   rS   rT   rV   )joinr   r   r   r   rW   r   r   )r    r!   bodyr"   rY   rZ   r`   s          r   %test_all_common_filter_names_excludedz6TestCommonFilter.test_all_common_filter_names_excludedp   s    yyC]CC(td{##$ 	Q$.bQ$GH!s8}c#hZPF	Q  	6AF#45555	6	Q 	Qs   3BBN)r<   r=   r>   r[   rd   r)   r   r   rL   rL   e   s    76r   rL   c                   $    e Zd Zd Zd Zd Zd Zy)TestTimeoutBehaviorc                 ,    t        d      }|g dk(  sJ y )Nz&@@ -1,3 +10,5 @@
 context
+added line
)
               r   r    liness     r    test_parse_diff_lines_basic_hunkz4TestTimeoutBehavior.test_parse_diff_lines_basic_hunk   s    !"MN,,,,r   c                 J    t        d      }d|v rd|v rd|v sJ d|v rd|v sJ y )Nz(@@ -1,2 +1,3 @@
 a
@@ -10,1 +20,2 @@
 b
               rm   rn   s     r   $test_parse_diff_lines_multiple_hunksz8TestTimeoutBehavior.test_parse_diff_lines_multiple_hunks   s;    !"PQEza5jQ%Z77U{rU{**{r   c                 &    t        d      dgk(  sJ y )Nz@@ -5 +7 @@
   rm   r    s    r   test_parse_diff_lines_no_countz2TestTimeoutBehavior.test_parse_diff_lines_no_count   s     1aS888r   c                    g }t        d      D ]:  }|d| dz  }|j                  d| d       |j                  t        |             < dgfd}t	        d|	      5  t	        d
      5 }t        dd      |_        t        t        |      |d      }d d d        d d d        d   dk(  sJ y # 1 sw Y   xY w# 1 sw Y    xY w)Nru   module_z.pyzdef unique_function_z	(): pass
r   c                  2     dxx   dz  cc<    d   dk(  ryy)Nr   rr   g    .Ag    H.Ar)   )
call_counts   r   fake_monotoniczMTestTimeoutBehavior.test_scan_returns_warn_on_timeout.<locals>.fake_monotonic   s#    qMQM!}!"r   zimpact_scanner.time.monotonicside_effectrO   rP   rQ   rr   )rS   rT   timeoutgate_resultWARN)ranger   appendr   r   r   rW   r   )	r    r!   filesir"   r   rY   rZ   r   s	           @r   !test_scan_returns_warn_on_timeoutz5TestTimeoutBehavior.test_scan_returns_warn_on_timeout   s    r 	#Awqc--CNN1!J?@LLS"	# S
	 2O 	['( [H(2"(K%3x=XYZ[	[ m$...[ [	[ 	[s$   !B>-*B2B>2B;	7B>>CN)r<   r=   r>   rp   rw   r{   r   r)   r   r   rf   rf      s    -+
9/r   rf   c                       e Zd Zd Zy)TestZeroReferencesPassc                    d}|dz  }|j                  d| d       t        d      5 }t        dd      |_        t	        t        |      t        |      gd	
      }d d d        d   dk(  sJ ||d   v sJ y # 1 sw Y   xY w)N xyzzy_totally_unique_func_a1b2c3zunique_module.pyr_   z():
    return 42
rO   rP   r   rQ   z	zero-refsrR   r   PASSrV   r   r   r   rW   r   r   )r    r!   unique_namer"   rY   rZ   s         r   /test_unique_function_no_references_returns_passzFTestZeroReferencesPass.test_unique_function_no_references_returns_pass   s    8++k]*>?@#$ 	f$.bQ$GH!s8}c#hZYdeF	f m$...f%67777		f 	fs   4A88BN)r<   r=   r>   r   r)   r   r   r   r      s    8r   r   c                   0    e Zd Zd Zd Zd Zd Zd Zd Zy)!TestExtractSymbolsPythonEdgeCasesc                 v    |dz  }|j                  d       t        t        |      g       }d|v rd|v rd|v sJ y )N	module.pyz5def alpha(): pass
class Beta: pass
def gamma(): pass
alphaBetagammar   r   s       r   )test_empty_diff_lines_returns_all_symbolszKTestExtractSymbolsPythonEdgeCases.test_empty_diff_lines_returns_all_symbols   sH    $QR(S26'!f&7Gw<NNN<Nr   c                 t    |dz  }|j                  d       t        t        |      dg      }d|v sJ d|vsJ y )Nr   z#def alpha(): pass
def beta(): pass
rr   r   betar   r   s       r   1test_specific_diff_lines_returns_only_overlappingzSTestExtractSymbolsPythonEdgeCases.test_specific_diff_lines_returns_only_overlapping   sG    $>?(SA37'!!!W$$$r   c                 d    |dz  }|j                  d       t        t        |      g       g k(  sJ y )Nz	broken.pyzdef (:
    pass
r   r    r!   r"   s      r   )test_syntax_error_file_returns_empty_listzKTestExtractSymbolsPythonEdgeCases.test_syntax_error_file_returns_empty_list   s2    $+,%c#h3r999r   c                 >    t        t        |dz        g       g k(  sJ y )Nzghost.py)r   r   r    r!   s     r   (test_nonexistent_file_returns_empty_listzJTestExtractSymbolsPythonEdgeCases.test_nonexistent_file_returns_empty_list   s!    %c(Z*?&@"EKKKr   c                 n    |dz  }|j                  d       t        t        |      g       }d|v rd|v sJ y )Nz	nested.pyz#def outer():
    def inner(): pass
outerinnerr   r   s       r   "test_nested_functions_are_includedzDTestExtractSymbolsPythonEdgeCases.test_nested_functions_are_included   sA    $>?(S26'!g&888&8r   c                 b    |dz  }|j                  d       dt        t        |      g       v sJ y )Nzasync_mod.pyzasync def fetch_data(): pass

fetch_datar   r   s      r    test_async_function_is_extractedzBTestExtractSymbolsPythonEdgeCases.test_async_function_is_extracted   s3    '785c#hCCCCr   N)	r<   r=   r>   r   r   r   r   r   r   r)   r   r   r   r      s#    O%:
L9Dr   r   c                   $    e Zd Zd Zd Zd Zd Zy)%TestExtractSymbolsTypescriptEdgeCasesc                 >    t        t        |dz        g       g k(  sJ y )Nzghost.ts)r	   r   r   s     r   r   zNTestExtractSymbolsTypescriptEdgeCases.test_nonexistent_file_returns_empty_list   s!    )#h.C*DbIROOOr   c                 t    |dz  }|j                  d       t        t        |      g       }dD ]  }||v rJ  y )Nz
exports.tszexport function MyFunction() {}
export class MyClass {}
export const MY_CONST = 42;
export enum MyEnum { A, B }
export interface MyInterface {}
export type MyType = string;
)
MyFunctionMyClassMY_CONSTMyEnumMyInterfaceMyTyperD   )r    r!   ts_filer#   ss        r   test_multiple_export_typesz@TestExtractSymbolsTypescriptEdgeCases.test_multiple_export_types   sL    \)  Q  	R,S\2>Y 	 A<<	 r   c                 p    |dz  }|j                  d       t        t        |      dg      }d|v rd|vsJ y )Nz	module.tsz5export function Alpha() {}
export function Beta() {}
rs   r   AlpharD   r    r!   r   r#   s       r   *test_specific_diff_lines_filters_correctlyzPTestExtractSymbolsTypescriptEdgeCases.test_specific_diff_lines_filters_correctly   sE    [(TU,S\A3? WG%;;;%;r   c                 n    |dz  }|j                  d       t        t        |      g       }d|v rd|vsJ y )Nzinternal.tsz;function privateHelper() {}
export function PublicApi() {}
	PublicApiprivateHelperrD   r   s       r   &test_non_exported_symbols_not_includedzLTestExtractSymbolsTypescriptEdgeCases.test_non_exported_symbols_not_included   sC    ]*Z[,S\2>g%/*HHH*Hr   N)r<   r=   r>   r   r   r   r   r)   r   r   r   r      s    P <Ir   r   c                   *    e Zd Zd Zd Zd Zd Zd Zy)TestParseDiffLinesc                 $    t        d      g k(  sJ y )NrP   rm   rz   s    r   test_empty_stringz$TestParseDiffLines.test_empty_string   s     $***r   c                 $    t        d      g k(  sJ y )Nz@@ -1,0 +5,0 @@
rm   rz   s    r   test_count_zeroz"TestParseDiffLines.test_count_zero   s     !45;;;r   c                 &    t        d      dgk(  sJ y )Nz@@ -3,1 +7,1 @@
ry   rm   rz   s    r   test_single_linez#TestParseDiffLines.test_single_line   s     !45!<<<r   c                 :    t        d      }|t        |      k(  sJ y )Nz"@@ -1,3 +20,3 @@
@@ -10,2 +1,2 @@
)r   sorted)r    rZ   s     r   test_sorted_outputz%TestParseDiffLines.test_sorted_output  s    "#IJ'''r   c                 $    t        d      g k(  sJ y )Nz/diff --git a/f.py b/f.py
index abc..def 100644
rm   rz   s    r   test_no_hunk_headersz'TestParseDiffLines.test_no_hunk_headers  s     !TUY[[[[r   N)r<   r=   r>   r   r   r   r   r   r)   r   r   r   r      s    +<=(\r   r   c                   :    e Zd ZdedefdZd Zd Zd Zd Z	d Z
y	)
TestGateThresholdsn_refsr   c                 
   dz  }|j                  d       dj                  fdt        |      D              fd}t        d|      5  t	        t              t        |      gd	      cd d d        S # 1 sw Y   y xY w)
Nz	source.pyzdef threshold_func(): pass
r]   c              3   .   K   | ]  } d | d  yw)z/other_z.py:1:threshold_func()Nr)   )r+   r   r!   s     r   r-   z;TestGateThresholds._run_scan_with_n_refs.<locals>.<genexpr>  s      dQR(71#5KLds   c                 X    | d   dk(  rt        r      S d      S t        dd      S )Nr   greprP   )r   rQ   )r   )cmd_kw
grep_liness     r   fake_runz:TestGateThresholds._run_scan_with_n_refs.<locals>.fake_run  s1    1v!zJJrJJRA66r   rO   r   zthreshold-testrR   )r   rb   r   r   r   r   )r    r!   r   r"   r   r   s    `   @r   _run_scan_with_n_refsz(TestGateThresholds._run_scan_with_n_refs  sw    $56YYdV[\bVcdd
	7
 #: 	iS]CH:Wgh	i 	i 	is   !A99Bc                 8    | j                  |d      d   dk(  sJ y )Nr   r   r   r   r   s     r   test_zero_refs_is_passz)TestGateThresholds.test_zero_refs_is_pass  "    ))(A6}EOOOr   c                 8    | j                  |d      d   dk(  sJ y )Nrr   r   r   r   r   s     r   test_one_ref_is_warnz'TestGateThresholds.test_one_ref_is_warn  r   r   c                 8    | j                  |d      d   dk(  sJ y )N   r   r   r   r   s     r   test_five_refs_is_warnz)TestGateThresholds.test_five_refs_is_warn  r   r   c                 8    | j                  |d      d   dk(  sJ y )N   r   BLOCKr   r   s     r   test_six_refs_is_blockz)TestGateThresholds.test_six_refs_is_block!  s"    ))(A6}EPPPr   c                 8    | j                  |d      d   dk(  sJ y )Nru   r   r   r   r   s     r   test_many_refs_is_blockz*TestGateThresholds.test_many_refs_is_block$  s"    ))(B7F'QQQr   N)r<   r=   r>   intdictr   r   r   r   r   r   r)   r   r   r   r   	  s6    ic id iPPPQRr   r   c                       e Zd Zd Zd Zy)TestGrepReferencesExcludeFilesc                 B   |dz  }|j                  d       |dz  }|j                  d       t        dt        |      t        |      t        |      g      }|D cg c]  }|d   	 }}t        |      t        |      fD ]  t        fd|D              sJ  y c c}w )N	define.pyzdef my_func(): pass
zother.pyz
my_func()
my_funcr'   c              3      K   | ]7  }t        |      j                         t              j                         k(   9 y wr   )r   resolve)r+   r,   excludeds     r   r-   zPTestGrepReferencesExcludeFiles.test_exclude_files_are_omitted.<locals>.<genexpr>2  s.     \Q47??,X0F0F0HH\s   =A r/   )r    r!   r2   
other_filer4   r5   r6   r   s          @r   test_exclude_files_are_omittedz=TestGrepReferencesExcludeFiles.test_exclude_files_are_omitted*  s    ,67
*
m,y#h-#k:JCPZO9\](,-1QvY-	-[)3z?; 	]H\R[\\\\	] .s   Bc                     |dz  }|j                  d       |dz  }|j                  d       t        dt        |      t        |      g      }t        d |D              sJ y )Nr   zdef visible_func(): pass
ref.pyzvisible_func()
visible_funcc              3   *   K   | ]  }d |d   v   yw)r   r'   Nr)   )r+   r5   s     r   r-   zTTestGrepReferencesExcludeFiles.test_non_excluded_file_is_included.<locals>.<genexpr>:  s     7Q8qy(7s   r/   )r    r!   r2   r3   r4   s        r   "test_non_excluded_file_is_includedzATestGrepReferencesExcludeFiles.test_non_excluded_file_is_included4  sb    ,;<h&./~s8}s;?O>PQ7$7777r   N)r<   r=   r>   r   r   r)   r   r   r   r   (  s    ]8r   r   c                       e Zd Zd Zd Zd Zy)TestScanOutputStructurec                    |dz  }|j                  d       t        d      5 }t        dd      |_        t	        t        |      t        |      gd      }d d d        d	D ]  }|v rJ  d
   dk(  sJ y # 1 sw Y   !xY w)Nzcheck.pyzdef check_something(): pass
rO   rP   r   rQ   zstructure-testrR   )rU   r   unmodified_referencesrV   rU   r   )r    r!   r"   rY   rZ   ks         r   test_result_has_required_keysz5TestScanOutputStructure.test_result_has_required_keys?  s    #67#$ 	k$.bQ$GH!s8}c#hZYijF	k X 	A;;	i $4444	k 	ks   4A66A?c                     |dz  }|j                  d       t        d      5 }t        dd      |_        t	        t        |      t        |      g      }d d d        d   d	k(  sJ |d
   g k(  sJ y # 1 sw Y   xY w)Nzdata.csvzcol1,col2
1,2
rO   rP   r   rQ   ra   r   r   rV   r   rX   s        r   "test_unsupported_extension_skippedz:TestScanOutputStructure.test_unsupported_extension_skippedI  s    #)*#$ 	Q$.bQ$GH!s8}c#hZPF	Q m$...'(B...		Q 	Qs   3A22A;c                     |dz  }|j                  d       t        d      5 }t        dd      |_        t	        t        |      t        |      gd      }d d d        d	   dk(  sJ y # 1 sw Y   xY w)
Nzmod.pyzdef func(): pass
rO   rP   r   rQ   z
my-task-99rR   rU   r   rX   s        r   test_task_id_propagatedz/TestScanOutputStructure.test_task_id_propagatedR  sz    !+,#$ 	g$.bQ$GH!s8}c#hZYefF	g i L000	g 	gs   4A))A2N)r<   r=   r>   r   r   r  r)   r   r   r   r   =  s    5/1r   r   )rP   r   rP   )!__doc__syspathlibr   unittest.mockr   r   pathinsertr   __file__r   parentimpact_scannerr   r   r   r	   r
   r   r   r   r   r@   rL   rf   r   r   r   r   r   r   r   r)   r   r   <module>r     s   	   * 3tH~--/66==> ? s S c 9 = =@D D.6 66 /  /P
8 
8$ D  DFI I2\ \&R R>8 8*1 1r   