
    i9                    R   d Z ddlmZ ddlZddlmc mZ ddl	Z	ddl
Z
ddlZddlZddlmZ ddlZej"                  j%                  d e ee      j+                         j,                  j,                               ddlmZmZmZ  G d d      Z ed       ed	       ed
      dZ G d d      Zd(d)dZ	 d*ddddd	 	 	 	 	 	 	 	 	 	 	 d+dZd,dZ d Z!d Z"d Z#d Z$d-dZ%d Z&d Z'd Z(d Z)d Z*d Z+d Z,d  Z-d! Z.d" Z/d# Z0d$ Z1d% Z2d.d&Z3e4d'k(  r ejj                   e3              yy)/u   
APEX V18 — TVDataFeedProvider tests.

The tvdatafeed-enhanced package is dependency-injected through
`client_factory` and `interval_map_builder` so tests run offline:
no real TvDatafeed instantiation, no network, no auth.
    )annotationsN)Path)TVDataFeedProviderTV_SYMBOL_MAP_build_interval_mapc                  0    e Zd ZdZddZddZd	dZd
dZy)FakeIntervalu@   Stand-in for tvDatafeed.Interval enum members — opaque tokens.c                    || _         y Nlabel)selfr   s     2/home/work/apex_v16/tests/test_tv_data_provider.py__init__zFakeInterval.__init__    s	    
    c                "    d| j                   dS )NzFakeInterval()r   r   s    r   __repr__zFakeInterval.__repr__"   s    tzznA..r   c                X    t        |t              xr | j                  |j                  k(  S r   )
isinstancer	   r   )r   others     r   __eq__zFakeInterval.__eq__$   s!    %.L4::3LLr   c                ,    t        | j                        S r   )hashr   r   s    r   __hash__zFakeInterval.__hash__&   s    DJJr   N)r   strreturnNone)r   r   )r   boolr   int)__name__
__module____qualname____doc__r   r   r   r    r   r   r	   r	      s    J/M r   r	   5min1hour4hourr(   r)   r*   c                  "    e Zd ZdZ	 	 ddZd Zy)FakeTvClientzRecords get_hist calls. Captures init kwargs (username, password,
    token, token_cache_file) so tests can assert on auth wiring.Nc                f    || _         || _        || _        || _        g | _        g | _        d | _        y r   )usernamepasswordtokentoken_cache_filecalls	responsesraise_on_call)r   r/   r0   r1   r2   s        r   r   zFakeTvClient.__init__4   s5      
 0!#
46/3r   c                   | j                   j                  |||||d       | j                  | j                  | j                  sy | j                  j	                  d      S )N)symbolexchangeintervaln_barsfut_contractr   )r3   appendr5   r4   pop)r   r7   r8   r9   r:   r;   s         r   get_histzFakeTvClient.get_hist>   s_    

( F(
 	
 )$$$~~~~!!!$$r   )NNNN)r#   r$   r%   r&   r   r>   r'   r   r   r-   r-   1   s    D;?"&4
%r   r-   c                   t        j                  d| d      }t        j                  dg| z  t        |       D cg c]  }d|z   	 c}t        |       D cg c]  }d|z   	 c}t        |       D cg c]  }d|z   	 c}t        |       D cg c]  }d|z   	 c}t        |       D cg c]
  }d	|d
z  z    c}d|      }d|j                  _        |S c c}w c c}w c c}w c c}w c c}w )zCBuild a TV-shaped DataFrame: DatetimeIndex 'datetime' + symbol col.z2026-05-01 13:00r(   )periodsfreqzCME_MINI:MES1!g     @g     @g     @g     @d   
   )r7   openhighlowclosevolume)indexdatetime)pd
date_range	DataFramerangerI   name)rowsidxidfs       r   _make_tv_dfrT   K   s    
--*Dv
FC	#$t+',T{3!6A:3',T{3!6A:3',T{3!6A:3',T{3!6A:3).t5A3R<5 
B BHHMI 43335s   C
C0CC"&C'userpassr1   r2   r/   r0   c               V      xs
 t                 fd}t        |||||d       }| fS )Nc                 R    | j                         D ]  \  }}t        ||        S r   )itemssetattr)kwargskvclients      r   factoryz_make_provider.<locals>.factoryc   s-    LLN 	"DAqFAq!	"r   c                      t        t              S r   dictFAKE_INTERVALSr'   r   r   <lambda>z _make_provider.<locals>.<lambda>l       T.%9 r   )r/   r0   r1   r2   client_factoryinterval_map_builder)r-   r   )r_   r1   r2   r/   r0   r`   providers   `      r   _make_providerrj   Z   s@     %|~F
 "H&69	H Vr   c                     t        d|         y )Nz  ok  )print)msgs    r   _okrn   q   s    	F3%.r   c                    ddl m}  | D cg c]  }|t        vs| }}g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d|       dz   d	|iz  }t        t        j                  |            d
x}}t        d       y
c c}w )zDEvery symbol in TRADING_HOURS (V16 universe) must have a TV mapping.r   )TRADING_HOURS==z%(py0)s == %(py3)smissingpy0py3z#TV_SYMBOL_MAP missing entries for: z
>assert %(py5)spy5Nz-TV_SYMBOL_MAP covers all V16 universe symbols)core.config_futuresrp   r   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgAssertionError_format_explanationrn   )rp   srt   @py_assert2@py_assert1@py_format4@py_format6s          r   'test_symbol_map_covers_all_v16_universer   y   s    1'BQ1M+AqBGBI7b=III7bIIIIII7III7IIIbIII?yIIIIIII78 Cs
   C*C*c                 .   t               \  } }t        d      g|_        t        j                  | j                  ddd            }|j                  }t        |      }g d}||k(  }|s
t        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                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}}d}	|j                  }|	|v}|st        j                  d|fd|	|f      t        j                  |	      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}	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}}|d   }	|	j"                  }|sLdt        j                  |	      t        j                  |      dz  }t        t        j                  |            dx}	}|d   j$                  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}}|j&                  }t!        |      }d$}||k(  }|s
t        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                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}}|j&                  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)}|	|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(  }|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,   }	d$}|	|k(  }|slt        j                  d|fd |	|f      t        j                  |	      t        j                  |      d!z  }d"d#|iz  }t        t        j                  |            dx}	x}}t+        d-       y).z
    TV returns a DatetimeIndex DataFrame with 'symbol' column;
    provider strips 'symbol', renames index to 'time', sorts ascending.
       rP   MESr(      timerD   rE   rF   rG   rH   rq   zM%(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.columns
})
} == %(py8)slistrS   rv   py1rw   rx   py8assert %(py10)spy10Nr7   not inz3%(py1)s not in %(py5)s
{%(py5)s = %(py3)s.columns
}r   rw   rx   assert %(py7)spy7z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenrv   r   rw   py6assert %(py8)sr   r   ;assert %(py3)s
{%(py3)s = %(py1)s.is_monotonic_increasing
}r   rw   rG   g     @z%(py1)s == %(py4)sr   py4assert %(py6)sr      zK%(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.calls
})
} == %(py8)sr_   r   r8   CME_MINIr9   r:   r;   z:happy path: normalized columns + ascending sort + fwd args)rj   rT   r4   asynciorunget_barscolumnsr   rz   r{   r|   r}   r~   r   r   r   r   is_monotonic_increasingilocr3   rd   rn   )ri   r_   rS   r   @py_assert4@py_assert7@py_assert6@py_format9@py_format11@py_assert0r   @py_format8@py_assert5@py_format7r   @py_assert3@py_format5calls                     r   .test_get_bars_happy_path_returns_normalized_dfr      s   
 &'Hf#+,F	X&&ufc:	;B

Q4
QQQQQQQQQQQQQQQ4QQQ4QQQQQQQQQQQQ
QQQQQQQQQQQQQQ%2::%8:%%%%8:%%%8%%%%%%2%%%2%%%:%%%%%%%r7a7a<7a33rr7af:-:------:----------g;B)6)6))))6))))))6))))))) ||!3|!!!!!!!!!!!!3!!!3!!!!!!v!!!v!!!|!!!!!!!!!!!!!<<?D>"U">U"""">U""">"""U"""""""
)z)z))))z))))))z)))))))
5~f5555555555555555555555> S >S    >S   >   S       $1$1$$$$1$$$$$$1$$$$$$$DEr   c                    g d} | D ]L  \  }}}t               \  }}t        d      g|_        t        j                  |j                  |dd             |j                  d   d   }||k(  }|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}}|j                  d   d   }||k(  }|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}}|j                  d   d   }t        d   }
||
k(  }|slt        j                  d|fd||
f      t        j                  |      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}x}}
O t!        d       y)z86E -> CME, MGC -> COMEX, MCL -> NYMEX, MYM -> CBOT_MINI.))6Er   CME)MGCr   COMEX)MCLr   NYMEX)MYMr   	CBOT_MINI)MNQr   r      r   r)   2   r   r7   rq   )z%(py1)s == %(py3)stv_symr   assert %(py5)srx   Nr8   tv_exr9   r   r   r   r   z?symbol-mapping forwards correct (TV ticker, exchange) per asset)rj   rT   r4   r   r   r   r3   rz   r{   r   r|   r}   r~   r   r   rd   rn   )casesv16r   r   ri   r_   r   r   r   r   r   r   r   s                r   2test_get_bars_forwards_correct_exchange_per_symbolr      s   E $ FVU)+&'Q/0H%%c7B78||Ax(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||Az*3*e3333*e333*333333e333e3333333||Az*EnW.EE*.EEEEE*.EEEE*EEE.EEEEEEEEF IJr   c                    dD ]  } t               \  }}t        d      g|_        t        j                  |j                  d| d             |j                  d   d   }t        |    }||k(  }|slt        j                  d|fd	||f      t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}x}} t        d       y )Nr+   r   r   r   rB   r   r9   rq   r   r   r   r   z0timeframe-mapping forwards correct Interval enum)rj   rT   r4   r   r   r   r3   rd   rz   r{   r   r   r   rn   )tfri   r_   r   r   r   r   r   s           r   5test_get_bars_forwards_correct_interval_per_timeframer      s    ( A)+&'Q/0H%%eR56||Az*@nR.@@*.@@@@@*.@@@@*@@@.@@@@@@@@	A
 :;r   c                   t         j                  }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                  |      t        j                  |      dz  }t        t        j                  |            d x}}| j                  }|syddt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      dz  }t        t        j                  |            d }| j                  }t        |      }g d}||k(  }	|	s
t        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                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            d x}x}x}	}y )NzTassert %(py6)s
{%(py6)s = %(py0)s(%(py1)s, %(py4)s
{%(py4)s = %(py2)s.DataFrame
})
}r   rS   rK   )rv   r   py2r   r   z)assert %(py2)s
{%(py2)s = %(py0)s.empty
})rv   r   r   rq   r   r   r   r   r   )rK   rM   r   r|   r}   rz   r~   r   r   r   emptyr   r   r{   )rS   r   r   r   r   @py_format3r   r   r   r   r   r   s               r   _assert_empty_v16_dfr      sy   ,,':b,'''''''':''':''''''b'''b''''''"'''"''',''''''''''88O8OOOOO2OOO2OOO8OOOOOO

Q4
QQQQQQQQQQQQQQQ4QQQ4QQQQQQQQQQQQ
QQQQQQQQQQQQQQr   c                 R   t               \  } }t        j                  | j                  ddd            }t	        |       |j
                  }g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                  d      d	z   d
|iz  }t        t        j                  |            d x}x}}t        d       y )NUNKNOWNr(   rB   rq   z-%(py2)s
{%(py2)s = %(py0)s.calls
} == %(py5)sr_   rv   r   rx   z-no TV call should be made for unmapped symbol
>assert %(py7)sr   z'unmapped symbol -> empty df, no TV callrj   r   r   r   r   r3   rz   r{   r|   r}   r~   r   r   r   r   rn   ri   r_   rS   r   r   r   r   r   s           r   %test_unmapped_symbol_returns_empty_dfr      s    %'Hf	X&&y&#>	?B<<N2N<2NNN<2NNNNNN6NNN6NNN<NNN2NNNNNNNNNNN12r   c                 R   t               \  } }t        j                  | j                  ddd            }t	        |       |j
                  }g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                  d      d	z   d
|iz  }t        t        j                  |            d x}x}}t        d       y )Nr   1minrB   rq   r   r_   r   z,no TV call should be made for unsupported tfr   r   z-unsupported timeframe -> empty df, no TV callr   r   s           r   +test_unsupported_timeframe_returns_empty_dfr      s    %'Hf	X&&ufc:	;B<<M2M<2MMM<2MMMMMM6MMM6MMM<MMM2MMMMMMMMMMM78r   c                    t               \  } }d g|_        t        j                  | j	                  ddd            }t        |       |j                  }t        |      }d}||k(  }|s
t        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                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            d x}x}x}}t!        d       y )Nr   r(   rB   r   rq   r   r   r_   r   r   r   z*TV returns None -> empty df (failure mode))rj   r4   r   r   r   r   r3   r   rz   r{   r|   r}   r~   r   r   r   rn   	ri   r_   rS   r   r   r   r   r   r   s	            r   $test_tv_returns_none_yields_empty_dfr      s    %'HfvF	X&&ufc:	;B||!3|!!!!!!!!!!!!3!!!3!!!!!!v!!!v!!!|!!!!!!!!!!!!!45r   c                     t               \  } }t        j                         g|_        t	        j
                  | j                  ddd            }t        |       t        d       y )Nr   r(   rB   zTV returns empty df -> empty df)	rj   rK   rM   r4   r   r   r   r   rn   )ri   r_   rS   s      r   (test_tv_returns_empty_df_yields_empty_dfr      sN    %'Hf'F	X&&ufc:	;B)*r   c                    t               \  } }t        d      |_        t        j                  | j                  ddd            }t        |       |j                  }t        |      }d}||k(  }|s
t        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                  |      t        j                  |      d
z  }dd|iz  }t        t        j                   |            d x}x}x}}t#        d       y )Nzwebsocket droppedr   r(   rB   r   rq   r   r   r_   r   r   r   z@TV raises (network down / auth fail) -> empty df, no propagation)rj   ConnectionErrorr5   r   r   r   r   r3   r   rz   r{   r|   r}   r~   r   r   r   rn   r   s	            r   test_tv_raises_yields_empty_dfr      s    %'Hf*+>?F	X&&ufc:	;B||!3|!!!!!!!!!!!!3!!!3!!!!!!v!!!v!!!|!!!!!!!!!!!!!JKr   c                    g t               fd} t        dd| d       }g }|k(  }|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      t        d      g_        t        j                  |j                  ddd             t        j                  |j                  dd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}}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}|
|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 }|
|u }|slt        j                  d#|fd$|
|f      t        j                  |
      t        j                  |      dz  }dd |iz  }t        t        j                  |            d x}
x}}t!        d%       y )&Nc                 *    j                  |        S r   )r<   )r\   r_   factory_callss    r   r`   z;test_client_instantiated_lazily_and_reused.<locals>.factory   s    V$r   alicesecretc                      t        t              S r   rb   r'   r   r   re   z<test_client_instantiated_lazily_and_reused.<locals>.<lambda>  rf   r   )r/   r0   rg   rh   rq   rs   r   ru   r   rx   r   r      r   r(   rB   r   r)   r   r   r   r   r   r   r   r   r/   r   r   r   r   r0   r1   is)z%(py1)s is %(py4)sz1client built lazily, reused across get_bars calls)r-   r   rz   r{   r|   r}   r~   r   r   r   rT   r4   r   r   r   r   rn   )r`   ri   r   r   r   r   r   r   r   r   r   r   r   r_   r   s                @@r   *test_client_instantiated_lazily_and_reusedr      ss    "M^F "89H =B=B==B#+[a-@AFKK!!%56KK!!%"56 }""""""""""""3"""3""""""}"""}"""""""""""""J'272'72222'7222'22272222222J'383'83333'8333'33383333333G$,,$,,,,$,,,$,,,,,,,,,,;<r   c                    t        d      } t        j                  |       }d}|j                  }||v}|st	        j
                  d|fd||f      t	        j                  |      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}x}}t        d
       y )Nr   r   r7   r   r   outr   r   r   z"normalize: 'symbol' column dropped)rT   r   
_normalizer   rz   r{   r   r|   r}   r~   r   r   rn   )df_inr   r   r   r   r   r   s          r   "test_normalize_drops_symbol_columnr     s    QE

'
'
.C&3;;&8;&&&&8;&&&8&&&&&&3&&&3&&&;&&&&&&&,-r   c                 F   t        d      } | j                  d d d   } t        j                  |       }|d   }|j                  }|sLdt        j                  |      t        j                  |      dz  }t        t        j                  |            d x}}t        d       y )N   r   r   r   r   r   z/normalize: descending input -> ascending output)
rT   r   r   r   r   rz   r   r   r   rn   )r   r   r   r   r   s        r   test_normalize_sorts_ascendingr   !  su    QEJJttE

'
'
.Cv;.;......;..........9:r   c                 v   t        d      } d| j                  _        t        j                  |       }d}|j
                  }||v }|st        j                  d|fd||f      t        j                  |      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}x}}|j
                  }t        |      }g d}||k(  }|s
t        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                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}x}}t        d       y)zIf TV ever returns an unnamed index (older lib versions), the
    fallback rename via 'index' must still produce a 'time' column.r   r   Nr   in)z/%(py1)s in %(py5)s
{%(py5)s = %(py3)s.columns
}r   r   r   r   r   rq   r   r   r   r   r   z2normalize: unnamed index -> 'time' column produced)rT   rI   rO   r   r   r   rz   r{   r   r|   r}   r~   r   r   r   rn   )r   r   r   r   r   r   r   r   r   r   r   s              r   $test_normalize_handles_unnamed_indexr  )  sH    QEEKK

'
'
.C S[[ 6[    6[   6      S   S   [       R4R RR RRRRR RRRRRRR4RRR4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<=r   c            	        	 t               } | j                  } |       }t	        |      }h d}||k(  }|st        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                  |      t        j                  |      t        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}x}x}x}}t        d       y# t        $ r t        d       Y yw xY w)z{Smoke: when the package IS installed, the real builder produces
    exactly the V16 timeframes. Skipped if package missing.z?  skip  tvdatafeed-enhanced not installed; skipped real builderN>   r(   r)   r*   rq   )zb%(py7)s
{%(py7)s = %(py0)s(%(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py1)s.keys
}()
})
} == %(py10)ssetm)rv   r   rw   rx   r   r   zassert %(py12)spy12z;real interval map: 3 V16 timeframes mapped to Interval enum)r   ImportErrorrl   keysr  rz   r{   r|   r}   r~   r   r   r   rn   )r  r   r   r   @py_assert9@py_assert8r   @py_format13s           r   (test_real_interval_map_has_three_entriesr  8  s    ! vv6vx63x=666=66666=666666636663666666q666q666v666x666=66666666666EF	  OPs   
E* *F Fc            	     F   t        j                         5 } t        |       dz  }t        d|dd      \  }}t	        d      g|_        t        j                  |j                  ddd	             |j                  } |       }|sd
dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        t        j                   |            dx}}t#        j$                  |j'                               }ddi}||k(  }|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}}|j*                  }d}||k(  }|st        j(                  d|fd||f      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}}|j,                  }t/        |      }||k(  }|s7t        j(                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      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                  |      dz  }dd|iz  }t        t        j                   |            dx}x}}ddd       t1        d       y# 1 sw Y   xY w)zWhen TV_TOKEN provided, provider pre-writes it to the cache file
    so the lib's _load_token() picks it up (skips CAPTCHA login).tv_token.jsonJWT_TOKEN_FROM_BROWSERNrW   r   r   r   r(   rB   zAassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}cacherv   r   r   r1   rq   rs   dataru   r   rx   )z-%(py2)s
{%(py2)s = %(py0)s.token
} == %(py5)sr_   r   r   r   )zV%(py2)s
{%(py2)s = %(py0)s.token_cache_file
} == %(py7)s
{%(py7)s = %(py4)s(%(py5)s)
}r   )rv   r   r   rx   r   zassert %(py9)spy9z6TV_TOKEN: pre-written to cache file + forwarded to lib)tempfileTemporaryDirectoryr   rj   rT   r4   r   r   r   existsr|   r}   rz   r~   r   r   r   jsonloads	read_textr{   r1   r2   r   rn   )tmpr  ri   r_   r   r   r   r  r   r   r   r   r   r   @py_format10s                  r   'test_tv_token_pre_written_to_cache_filer  H  sz    
	$	$	& 5#S	O+)*"D
&
 (Q/0H%%eVS9:|||~~uu|~zz%//+,!9::t:::::t:::::::t:::t:::::::::::||777|77777|7777777v777v777|77777777777&&4#e*4&*4444&*444444v444v444&444444#444#444444e444e444*44444445  @A!5 5s   O.PP c                 ,   t        j                         5 } t        |       dz  }t        d|dd      \  }}t	        d      g|_        t        j                  |j                  ddd             |j                  } |       }| }|st        j                  d	      d
z   dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t!        t        j"                  |            dx}x}}|j$                  }d}||u }|st        j&                  d|fd||f      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d       t)        d       y# 1 sw Y   xY w)z7No token, no creds -> anonymous; no cache file written.r  NrW   r   r   r   r(   rB   zno token -> no cache writezG
>assert not %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}r  r  r   )z-%(py2)s
{%(py2)s = %(py0)s.token
} is %(py5)sr_   r   r   r   zDanonymous mode: no token, no creds -> no cache, factory called clean)r  r  r   rj   rT   r4   r   r   r   r  rz   r   r|   r}   r~   r   r   r   r1   r{   rn   )
r  r  ri   r_   r   r   r   r   r   r   s
             r   *test_anonymous_mode_when_no_creds_no_tokenr  ^  s]   		$	$	& 
$#S	O+)D
& (Q/0H%%eVS9:<<?<>?>!?!??#???????5???5???<???>??????||#t#|t####|t######v###v###|###t#######
$ NO
$ 
$s   G!H

Hc                    ddl m}  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}}|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}}t        d       y
)uu   Default cache path lives under ~/.apex_v18/, not the lib's
    ~/.tv_token.json — avoids clobbering other TV tools.r   )DEFAULT_TOKEN_CACHEz	.apex_v18r   )z%(py1)s in %(py3)sr   r   r   rx   Nr  zJassert %(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.endswith
}(%(py4)s)
})rv   r   r   r   z,default cache path scoped under ~/.apex_v18/)analysis.tv_data_providerr  r   rz   r{   r   r|   r}   r~   r   r   endswithrn   )
r  r   r   r   r   r   r   r   r   r   s
             r   ,test_default_token_cache_path_is_apex_scopedr"  n  s     > A;!;!;!!::&o&:o&&&&&&&&1&&&1&&&:&&&o&&&&&&&&&&67r   c                    t        d       t                t                t                t	                t                t                t                t                t                t                t                t                t                t                t                t!                t#                t        d       y)Nztest_tv_data_provider.pyzALL TESTS PASSEDr   )rl   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r"  r'   r   r   mainr$  |  s}    	
$%+-24689;)+/1(*,."$.0&("$(*,.+-.002	
r   __main__)r   )rP   r"   r   pd.DataFramer   )r_   zFakeTvClient | Noner1   
str | Noner2   zstr | Path | Noner/   r'  r0   r'  r   z'tuple[TVDataFeedProvider, FakeTvClient])rm   r   r   r   )rS   r&  r   r   r!   )6r&   
__future__r   builtinsr|   _pytest.assertion.rewrite	assertionrewriterz   r   r  sysr  pathlibr   pandasrK   pathinsertr   __file__resolveparentr   r   r   r   r	   rd   r-   rT   rj   rn   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r"  r$  r#   exitr'   r   r   <module>r6     s|   #     
    3tH~--/66==> ? 	  	  &!'"'"% %4  #' *.!!  (	
   -.9FBK&<R396+L=@.;>	G B,P 8. zCHHTV r   