
    mjk                       d dl mZ d dlmZ d dlmZmZ d dlZd dlmZ d dl	Z	d dl
mZ d dlmZ d dlmZ d d	lmZ d d
lmZ d dlmZ d dlmZ d dlmZmZmZmZmZ d dlmZ d dl m!Z!m"Z" d dl#m$Z$ d dl%m&Z&m'Z' dZ(dNdOdZ)dPdZ*e(dddQd$Z+dRd%Z,ddddddd&dSd/Z-dRd0Z.dTdUd4Z/dVd6Z0dWd;Z1dXd<Z2dXd=Z3dXd>Z4dXd?Z5dXd@Z6dXdAZ7dYdBZ8dXdCZ9dPdDZ:dXdEZ;dXdFZ<dXdGZ=dXdHZ>dXdIZ?dXdJZ@dZdKZAdXdLZBdXdMZCdS )[    )annotations)ThreadPoolExecutor)datetime	timedeltaN)Path)
TestClient)run_postfinance_source_contract)+run_portfolio_market_analytics_v1_scenarios)#run_sprint9_fx_fmp_history_contract)get_db)
create_app)portfolio_data)build_portfolio_reconciliationconfirm_ingestioningestion_historylist_data_sourcespreview_ingestion)build_portfolio_performance)!set_performance_cashflow_coverage$set_performance_scope_classification)connect)MIGRATION_VERSIONapply_migrationsz2026-07-23T12:00:00+00:00pathPath | Nonereturnsqlite3.Connectionc                d    t          | pd          }t          |           t          |           |S )Nz:memory:)r   r   seed_registry)r   conns     :tests/unit/test_portfolio_data_ingestion_reconciliation.pydatabaser"   $   s5    4%:&&DT$K    r    Nonec                   |                      dt          f           dD ]^\  }}}}|                      d|d||t          f           t          | |d|ddt                     t          | |d	d
dddt                     _|                      dt          f           |                      dt          f           |                                  d S )NzcINSERT INTO platforms(platform_id,name,platform_type,created_at) VALUES('p','Synthetic','broker',?)))azAKB Haushaltskonto	brokeragepostfinance_etrading_depot)bzRaiffeisen Transferkontor'   postfinance_etrading_cash)totalzSynthetic Totalwertrobo_advisorcrypto_portfoliozINSERT INTO accounts(account_id,platform_id,account_name,account_type,currency,performance_included,is_active,created_at)
               VALUES(?,?,?,?, 'CHF',0,1,?)pTsynthetic_test_fixturezNExplicit synthetic investment role; account names are intentionally irrelevant)
account_idincludedclassification_rolesourcenoteclassified_atz
1900-01-01z
2100-12-31completez#Complete synthetic cashflow history)r0   coverage_fromcoverage_tostatusr3   r4   recorded_atzINSERT INTO instruments(instrument_id,asset_class,name,ticker,isin,exchange,currency,is_active,created_at)
           VALUES('i','equity','Synthetic Asset','SYN','CH0000000001','SIX','CHF',1,?)zINSERT INTO instruments(instrument_id,asset_class,name,ticker,isin,exchange,currency,is_active,created_at)
           VALUES('eur','equity','Synthetic EUR Asset','EURX','CH0000000002','SIX','EUR',1,?))executeNOWr   r   commit)r    r0   nameaccount_typeroles        r!   r   r   +   s    LLvy|x~1 
 
,
D,
 	/1;S$VY0Z	
 	
 	
 	-! $+a	
 	
 	
 	
 	*!&$+6		
 		
 		
 		
 		
 	LL	Z\_[a   	LL	acfbh   	KKMMMMMr#   )createdupdatedsnapshot_idstraccountatvaluerA   rB   
str | Nonec          	     f    |                      d||||||f           |                                  d S )NzINSERT INTO account_value_snapshots(snapshot_id,account_id,valuation_date,total_value_chf,currency,source_type,quality_status,created_at,updated_at)
           VALUES(?,?,?,?,'CHF','manual_total_value','ok',?,?))r;   r=   )r    rC   rE   rF   rG   rA   rB   s          r!   
add_legacyrJ   T   sF    LL	BDOQXZ\^celnuCv   	KKMMMMMr#   c           	     p    |                      d|||||t          f           |                                  d S )NzINSERT INTO cash_account_snapshots(snapshot_id,account_id,snapshot_type,balance_date,amount_original,currency,amount_chf,source,created_at)
           VALUES(?,?, 'reconciliation', ?,?,'CHF',?,'manual',?)r;   r<   r=   r    rC   rE   rF   rG   s        r!   add_cashrN   \   sF    LL	DFQSZ\^`eglnqEr   	KKMMMMMr#   )
instrumentquantitygrossnetrow_hashtransfer_grouptransaction_idkindrO   rP   rQ   rR   rS   rT   c                   |                      d|||||||||||	pd| |
t          f           |                                  d S )Na  INSERT INTO transactions(transaction_id,transaction_type,activity_kind,account_id,instrument_id,trade_date,
             event_timestamp,quantity,gross_amount_original,net_amount_original,currency_original,fx_rate_to_chf,
             fx_status,source_type,source_id,row_hash,is_confirmed,quality_status,internal_transfer_group_id,created_at)
           VALUES(?,?,?,?,?,?,?,?,?,?,'CHF','1','ok','fixture','fixture',?,1,'ok',?,?)zhash-rL   )r    rU   rV   rE   rF   rO   rP   rQ   rR   rS   rT   s              r!   add_transactionrX   d   sq    
 	LL	Z 
tWj"b(ESVX`X|d|lzd|d|  M  OR  	S   	KKMMMMMr#   c           	     p    |                      d|||||t          f           |                                  d S )Nae  INSERT INTO portfolio_valuation_snapshots(snapshot_id,scope_kind,scope_id,account_id,value_original,currency,
             base_currency,fx_rate_to_base,fx_direction,valuation_at,source,captured_at,snapshot_version,quality_status,reason_codes_json)
           VALUES(?, 'account', ?, ?, ?, 'CHF','CHF','1','original_to_base',?,'fixture',?,1,'complete','[]')rL   rM   s        r!   add_canonical_valuationrZ   s   sC    LL	p 
gwr37	   	KKMMMMMr#   legacy_account_valuesr3   dict[str, object]c                    | dd ddt           dS )N	portfolio
2025-01-01
2025-12-31
source_key
scope_kindr0   period_from	period_todata_cutoff)r<   )r3   s    r!   requestrg   }   s,     4`l  |H  Y\  ]  ]  ]r#   previewc                H     i  fddD              fddD             ddiS )Nc                "    i | ]}||         S  rk   .0keyrh   s     r!   
<dictcomp>z#confirm_payload.<locals>.<dictcomp>   s    
~
~
~3
~
~
~r#   ra   c                "    i | ]}||         S rk   rk   rl   s     r!   ro   z#confirm_payload.<locals>.<dictcomp>   s1      Y  Y  Y3  Y  Y  Yr#   )
preview_idconfirmation_idpreview_created_atsource_revisioninput_fingerprintpayload_hashconfirmTrk   )rh   s   `r!   confirm_payloadrx      sp    
~
~
~
~(}
~
~
~ Y  Y  Y  Y  )X  Y  Y  Y 	4  r#   tmp_pathr   monkeypatchpytest.MonkeyPatchc           
        t                      }t          dk    sJ |                    d                                          d         t          k    sJ d |                    d          D             h dk    sJ |                    d                                          sJ t	          |dd	d
d           t          |t          t          |t                                                    }t          j
        t          j        d          5  |                    d|d         f           d d d            n# 1 swxY w Y   t          j
        t          j        d          5  |                    d|d         f           d d d            n# 1 swxY w Y   t          j
        t          j        d          5  |                    d|d         f           d d d            n# 1 swxY w Y   t          j
        t          j        d          5  |                    d|d         f           d d d            n# 1 swxY w Y   t          | dz             t          | dz  |           d S )N1   z*SELECT MAX(version) FROM schema_migrationsr   c                    h | ]
}|d          S )   rk   rm   rows     r!   	<setcomp>z[test_migrations_41_to_43_are_additive_and_ingestion_history_is_immutable.<locals>.<setcomp>   s    [[[sCF[[[r#   z,PRAGMA table_info(instrument_price_mappings)>   source_venuesource_symbolsource_currencyzXSELECT name FROM sqlite_master WHERE type='table' AND name='portfolio_ingestion_batches'legacyr&   r`   1000	immutablematchzJUPDATE portfolio_ingestion_batches SET status='confirmed' WHERE batch_id=?batch_idzcannot be deletedz6DELETE FROM portfolio_ingestion_items WHERE batch_id=?zaudit is immutablez7UPDATE audit_log SET action='changed' WHERE entity_id=?zaudit cannot be deletedz'DELETE FROM audit_log WHERE entity_id=?zmarket-analyticszsprint9-history)r"   r   r;   fetchonerJ   r   rx   r   rg   pytestraisessqlite3DatabaseErrorr
   r   )ry   rz   r    results       r!   Htest_migrations_41_to_43_are_additive_and_ingestion_history_is_immutabler      s    ::D""""<<DEENNPPQRSWhhhhh[[dll+YZZ[[[ ` ` `     <<rss||~~~~~tXsL&999t_5FtWYY5W5W%X%XYYF	w,K	@	@	@ z zadjkudvcxyyyz z z z z z z z z z z z z z z	w,4G	H	H	H f fMPVWaPbOdeeef f f f f f f f f f f f f f f	w,4H	I	I	I g gNQWXbQcPefffg g g g g g g g g g g g g g g	w,4M	N	N	N W W>
AS@UVVVW W W W W W W W W W W W W W W/;M0MNNN'3D(DkRRRRRsH   D88D<?D<#FFF8G""G&)G&H77H;>H;c            	        t                      } t          | dddd           t          | dddd           t          | ddddt          	           | j        }t	          | t                                }| j        |k    sJ |d
         ddddddddk    sJ |d         d         dk    sJ t          |d                   dk    sJ t          d |d         D                       sJ t          | t          |                    }|d         dk    sJ | 
                    d|d         f                                          d         dk    sJ d S )Noner&   r`   r   	duplicateblockedr)   900)rB   countsr   r      )new	unchangedr   	ambiguousr   	versioned
discoveredquality_impactr9   partialrv   @   c              3  *   K   | ]}d |d         vV  dS )/source_record_refNrk   rm   items     r!   	<genexpr>zbtest_preview_is_storage_free_and_classifies_new_duplicate_blocked_and_unchanged.<locals>.<genexpr>   s-      QQs$233QQQQQQr#   itemswritten_recordsz?SELECT COUNT(*) FROM portfolio_ingestion_items WHERE batch_id=?r   )r"   rJ   r<   total_changesr   rg   lenallr   rx   r;   r   )r    beforerh   	confirmeds       r!   Otest_preview_is_storage_free_and_classifies_new_duplicate_blocked_and_unchangedr      s   ::DtUCv666t[#|V<<<tY\5#FFFFFgii00G''''8XYfgvw  HI  !J  !J  J  J  J  J#$X.);;;;w~&''2----QQ@PQQQQQQQQ!$(@(@AAI&'1,,,,<<Y\efp\q[stt}}  AB  C  GH  H  H  H  H  H  Hr#   c                    t                      } t          | dddd           t          | t                                }t	          |          }t          | |          }t          | |          }|d         du r|d         dk    sJ ||d	         |d
         ddk    sJ |                     d                                          d         dk    sJ |                     d|d	         f                                          d         dk    sJ t          | t                                }|d         d         dk    r|d         d         dk    sJ t                       d S )Nr   r&   r`   r   
idempotentFr   r   r   audit_idTr   r   r   zaSELECT COUNT(*) FROM portfolio_valuation_snapshots WHERE source='ingestion:legacy_account_values'r   z0SELECT COUNT(*) FROM audit_log WHERE entity_id=?r   r   r   )	r"   rJ   r   rg   rx   r   r;   r   r	   )r    rh   payloadfirstsecondfreshs         r!   Otest_confirm_is_atomic_audited_idempotent_and_second_fresh_preview_is_unchangedr      s   ::DtXsL&999gii00Gg&&GdG,,EtW--F%''E2C,D,I,I,II%
"3zARbfgggggg<<{||  F  F  H  H  IJ  K  OP  P  P  P  P<<JUS]M^L`aajjllmnostttttdGII..E?;'1,,x1G11L1L1LL#%%%%%r#   c                 p   t                      } t          | dddd           t          | t                                }t	          |          }t          | |           t          |          }d|d<   t          j        t          d          5  t          | |           d d d            n# 1 swxY w Y   t          | t                                }t          | d	d
ddd           t          j        t          d          5  t          | t	          |                     d d d            d S # 1 swxY w Y   d S )Nr   r&   r`   r   z
2025-02-01rd      verändertem Inhaltr   laterr)   5002026-01-01T00:00:00+00:00rA   zveraltet|Ausgangsrevision)
r"   rJ   r   rg   rx   r   dictr   r   
ValueError)r    rh   r   changedpreview2s        r!   Ltest_confirmation_identity_rejects_changed_payload_and_stale_source_revisionr      s   ::DtXsL&999gii00Gg&&GdG$$$7mmG)GM	z)>	?	?	? ) )$((() ) ) ) ) ) ) ) ) ) ) ) ) ) ) wyy11HtWc<@[\\\\	z)D	E	E	E ; ;$ 9 9:::; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;s$   B))B-0B- D++D/2D/c                 &   t                      } t          | dddd           t          | t                      d          }t	          j        t          d          5  t          | t          |                     d d d            d S # 1 swxY w Y   d S )	Nr   r&   r`   r   z2020-01-01T00:00:00+00:00)rs   
abgelaufenr   )	r"   rJ   r   rg   r   r   r   r   rx   r    rh   s     r!    test_expired_preview_is_rejectedr      s    ::DtXsL&999giiD_```G	z	6	6	6 : :$ 8 8999: : : : : : : : : : : : : : : : : :s   BB
B
c            
        t                      } t          | ddddd           t          | t          t	          | t                                                     t          | ddddd	           t	          | t                                }|d
         d         dk    sJ t          | t          |                     |                     d                                          }d |D             ddgk    sJ |d         d         J d S )Nv1r&   r`   r   r   r   v210102026-02-01T00:00:00+00:00r   r   r   zSELECT snapshot_version,supersedes_snapshot_id,value_original FROM portfolio_valuation_snapshots WHERE source='ingestion:legacy_account_values' ORDER BY snapshot_versionc                .    g | ]}|d          |d         fS snapshot_versionvalue_originalrk   r   s     r!   
<listcomp>zJtest_snapshot_correction_creates_new_immutable_version.<locals>.<listcomp>   )    MMMS#$c*:&;<MMMr#   r   r      r   supersedes_snapshot_id)r"   rJ   r   rx   r   rg   r;   fetchall)r    rh   rowss      r!   6test_snapshot_correction_creates_new_immutable_versionr      s%   ::DtT3f>YZZZZdO,=dGII,N,NOOPPPtT3f>YZZZZgii00G8[)Q....dOG44555<<  D  E  E  N  N  P  PDMMMMMR]_jQkkkkk7+,88888r#   c                 @   t                      } t          | dddddddd		  	         t          j        t          j        d
          5  t          | dddddddd		  	         d d d            n# 1 swxY w Y   t          | dddddd           t          | dddddd           t          | t          d                    }|d         d         dk    sJ |d         d         dk    sJ |d         d         dk    sJ |d         d         dk    sJ |d         g k    sJ d S )Nbuy1buyr&   r_   i1100same)rO   rP   rQ   rS   ztransactions.row_hashr   buy2r   z
2025-01-02)rP   rQ   r   option_exercisez
2025-01-03)rO   rP   canonical_transactionsr   r   r   r   r   planned_activities)r"   rX   r   r   r   IntegrityErrorr   rg   r   s     r!   Ttest_canonical_activity_preview_handles_duplicates_ambiguity_and_unsupported_recordsr      s   ::DD&%lsUX`epvwwww	w-5L	M	M	M | |feS,3Y\ditz{{{{| | | | | | | | | | | | | | |D+uc<#UZ[[[[D)%6\VYdghhhhg.F&G&GHHG8[)Q....8[)Q....8[)Q....8Y'1,,,,'(B......s   A,,A03A0c           	        | dz  t                    }t          |dddd           t          |t                                }t	          |          |                                 dfdt          d	
          5 }t          |                    fdt          d	                              }d d d            n# 1 swxY w Y   t                    }t          d |D                       dk    sJ |                    d                                          d         dk    sJ |                    d                                          d         dk    sJ d S )Nzparallel.sqlite3r   r&   r`   r   r   r\   c                     t                    } 	 t          |           |                                  S # |                                  w xY wN)r   r   close)localr   r   s    r!   r;   zOtest_parallel_confirmation_produces_one_batch_and_one_snapshot.<locals>.execute   sA    	$UG44KKMMMMEKKMMMMs	   6 Ar   )max_workersc                                 S r   rk   )_r;   s    r!   <lambda>zPtest_parallel_confirmation_produces_one_batch_and_one_snapshot.<locals>.<lambda>  s    '')) r#   c              3  B   K   | ]}t          |d                     V  dS )r   N)bool)rm   r   s     r!   r   zQtest_parallel_confirmation_produces_one_batch_and_one_snapshot.<locals>.<genexpr>  s2      DD&4|,---DDDDDDr#   r   z0SELECT COUNT(*) FROM portfolio_ingestion_batchesr   z2SELECT COUNT(*) FROM portfolio_valuation_snapshots)r   r\   )r"   rJ   r   rg   rx   r   r   listmapranger   sumr;   r   )	ry   r    rh   poolresultsverifyr;   r   r   s	         @@@r!   >test_parallel_confirmation_produces_one_batch_and_one_snapshotr      s   ((DD>>DtXsL&999gii00Gg&&GJJLLL       
	*	*	* @dtxx 3 3 3 3U1XX>>??@ @ @ @ @ @ @ @ @ @ @ @ @ @ @T]]FDDGDDDDDIIII>>LMMVVXXYZ[_`````>>NOOXXZZ[\]abbbbbbbs   4CCCc            
        t                      } t          | dddd           t          | t          t	          | t                                                     t          |           }d |d         D             h dk    sJ t          | dd	
          }|d         dk    sJ |d         d	         d                             d          sJ dt          |          vsJ d S )Nr   r&   r`   r   c                    h | ]
}|d          S )rb   rk   r   s     r!   r   zLtest_source_status_and_history_are_redacted_and_paginated.<locals>.<setcomp>  s    >>>4D>>>r#   sources>   fx_ratesmarket_pricesreported_positionsr[   r   
   r   )limitoffsetr+   r   r   r   zbatch-source_filename)
r"   rJ   r   rx   r   rg   r   r   
startswithrD   )r    r   historys      r!   9test_source_status_and_history_are_redacted_and_paginatedr    s   ::DtXsL&999dO,=dGII,N,NOOPPP%%G>>79+=>>>  Ci  Ci  Ci  i  i  i  iBq999G7q    7Az*55h?????CLL000000r#   c           
     d   t          | ddddddd           |                     dt          f           |                     d	t          f           t          | d
ddd           t	          | dddd           t	          | dddd           t          | dddd           |                                  d S )Nr   r&   r_   r   10800rO   rP   rQ   INSERT INTO positions_snapshot(position_snapshot_id,snapshot_date,account_id,platform_id,instrument_id,quantity,market_value_chf,data_quality_status,created_at)
           VALUES('pos','2025-12-31','a','p','i','10','1000','ok',?)INSERT INTO market_prices(market_price_id,instrument_id,price_date,close,currency,provider,quality_status,created_at)
           VALUES('price','i','2025-12-31','100','CHF','fixture','ok',?)cashr`   r   ztotal-a1100z
total-onlyr+   5000zcanonical-total)rX   r;   r<   rN   rJ   rZ   r=   r    s    r!   seed_matched_reconciliationr    s    D%\cTX`effffLL	HJM   	LL	LNQV   T63e444tY\6:::t\7L&AAAD"3WlFSSSKKMMMMMr#   c                 (   t                      } t          |            t          | dt                    }t	          d |d         D                       }|d         ddddd	k    sJ |d
         d         dk    sJ t	          d |d         D                       }|d         dk    sJ |d         J |d         d         d         dk    sJ t          |d                   dk    sJ t          | dt                    }|d         |d         k    sJ d S )Nr`   as_ofrf   c              3  2   K   | ]}|d          dk    |V  dS instrument_labelzSynthetic AssetNrk   r   s     r!   r   zktest_reconciliation_matches_quantity_value_cash_and_total_value_account_without_holdings.<locals>.<genexpr>.  s3      nntEW@X\m@m@md@m@m@m@mnnr#   differencesrP   r  0matched)ledgerreported
differencer9   	valuationr9   c              3  *   K   | ]}|d          
|V  dS )total_value_onlyNrk   r   s     r!   r   zktest_reconciliation_matches_quantity_value_cash_and_total_value_account_without_holdings.<locals>.<genexpr>1  s-      \\t4HZC[\d\\\\\\r#   account_totalspositions_valuecoverage
activitiesratior   ru   r   )r"   r  r   r<   nextr   )r    r   rO   
total_onlyrepeateds        r!   Xtest_reconciliation_matches_quantity_value_cash_and_total_value_account_without_holdingsr)  *  sO   ::D%%%+DRUVVVFnnvm'<nnnnnJj!$VYen%o%oooook"8,	9999\\v.>'?\\\\\Jh9,,,,'(000*l+G4;;;;v)*++r1111-d,TWXXXH'(F3F,GGGGGGGr#   c                 T   t                      } t          |            |                     d           t          | dt                    }|d         d         d         d         dk    sJ |                     d	           t          | dt                    }d
|d         d         d         v sJ d|d         d         d         v sJ |                     d           |                     dt          f           |                     dt          f           t          | dt                    }dt          d |d         D                       d         v sJ |                     d           t          | dt                    }dt          d |d         D                       d         v sJ d S )NzZUPDATE positions_snapshot SET market_value_chf='1000.005' WHERE position_snapshot_id='pos'r`   r  r  r   r  r9   within_tolerancezbUPDATE positions_snapshot SET quantity='9',market_value_chf='900' WHERE position_snapshot_id='pos'quantity_mismatchreason_codesvalue_mismatchzvUPDATE positions_snapshot SET instrument_id='eur',quantity='1',market_value_chf='100' WHERE position_snapshot_id='pos'a?  INSERT INTO transactions(transaction_id,transaction_type,activity_kind,account_id,instrument_id,trade_date,quantity,gross_amount_original,currency_original,source_type,source_id,row_hash,quality_status,created_at) VALUES('eur-buy','buy','buy','a','eur','2025-01-01','1','90','EUR','fixture','fixture','eur-hash','ok',?)INSERT INTO market_prices(market_price_id,instrument_id,price_date,close,currency,provider,quality_status,created_at) VALUES('eur-price','eur','2025-12-31','100','EUR','fixture','ok',?)
missing_fxc              3  2   K   | ]}|d          dk    |V  dS r  zSynthetic EUR AssetNrk   r   s     r!   r   z]test_reconciliation_distinguishes_tolerance_mismatch_missing_fx_and_cutoff.<locals>.<genexpr>H  s?         A   AdSeNfjNNNNNN   A   Ar#   YUPDATE positions_snapshot SET snapshot_date='2025-12-30' WHERE position_snapshot_id='pos'cutoff_mismatchc              3  2   K   | ]}|d          dk    |V  dS r2  rk   r   s     r!   r   z]test_reconciliation_distinguishes_tolerance_mismatch_missing_fx_and_cutoff.<locals>.<genexpr>K  s[        %B  %BdtTfOg  lA  PA  PAT  PA  PA  PA  PA  %B  %Br#   )r"   r  r;   r   r<   r&  )r    insidemismatchr0  cutoffs        r!   Jtest_reconciliation_distinguishes_tolerance_mismatch_missing_fx_and_cutoffr9  :  sG   ::D%%%LLmnnn+DRUVVVF- #K0:>PPPPPLLuvvv-d,TWXXXH(="9!"<^"LLLLLx6q9.IIIIILL  J  K  K  KLL  S  VY  U[  \  \  \LL  M  PS  OU  V  V  V/LVYZZZJ4   A   AM1J   A   A   A  A  A  BP  Q  Q  Q  Q  QLLlmmm+DRUVVVF  %B  %Bf]6K  %B  %B  %B  !B  !B  CQ  !R  R  R  R  R  R  Rr#   c            	        t                      } t          | dddddd           t          | ddd	dd
d           ddd                                D ]5\  }}t          dd|          D ]\  }}}t	          | | d| |||           6t          | ddt                    }t          | dddt                    }|d         g k    sJ |d         ddddgk    sJ d S )Nztransfer-outinternal_transferr&   2025-06-01T00:00:00+00:00z-100zraiffeisen-akb)rR   rT   ztransfer-inr)   r   )r   r   r   )r   r   r  )r&   r)   )openflowr   )r_   r<  r`   -r_   r`   )	from_dateto_daterf   )r@  rA  r0   rf   external_cashflowsexternal_withdrawal)rF   rV   amount)r"   rX   r   ziprZ   r   r<   )r    rE   valuessuffixrF   rG   r^   s          r!   [test_scope_aware_raiffeisen_akb_transfer_is_internal_for_portfolio_and_external_for_accountrH  N  s   ::DD.*=sD_ek  }M  N  N  N  ND-)<cC^di  {K  L  L  L  L!8?WXX^^`` U U!$%>@y  |B  "C  "C 	U 	UFB#DW*?*?v*?*?"eTTTT	U+DLR^lopppI)$,P\ilz}~~~G)*b0000'(4OYn  {A  .B  .B  -C  C  C  C  C  C  Cr#   c                 ,   t                      t                     t          d          } fd| j        t          <   t          |           }|                    d          j        dk    sJ |                    d          j        dk    sJ |                    dt                                }|j        dk    sJ |                    d	t          |                                                    }|j        dk    sJ |                    d
          }|j        dk    sJ |                                d         dk    sJ |                                 d         }t          |d                   dhk    sJ t          |d                   dhk    sJ t          |d	                   dhk    sJ t          |d                   dhk    sJ t          |d                   dhk    sJ |d	         d         d         d         d         dk    sJ t          d          }fd|j        t          <   t          |d          }|                    dt                                j        dk    sJ |                    d	i           j        dk    sJ d S )Ntest)
write_modec                      S r   rk   r  s   r!   r   zAtest_api_openapi_write_security_and_read_routes.<locals>.<lambda>_  s    t r#   z/api/portfolio/data-sources   z\/api/portfolio/reconciliation?as_of=2025-12-31&data_cutoff=2026-07-23T12%3A00%3A00%2B00%3A00z /api/portfolio/ingestion/preview)jsonz /api/portfolio/ingestion/confirmz2/api/portfolio/ingestion/history?limit=20&offset=0r+   r   pathsgetpostz /api/portfolio/ingestion/historyz/api/portfolio/reconciliation	responses409descriptionu$   Abgelaufene oder veränderte Previewdisabledc                      S r   rk   r  s   r!   r   zAtest_api_openapi_write_security_and_read_routes.<locals>.<lambda>r  s    t r#   )z100.64.0.10iP  )clienti  )r"   r  r   dependency_overridesr   r   rP  status_coderQ  rg   rx   rN  openapiset)	apprW  preview_responseconfirm_responsehistory_responserO  blocked_appr   r    s	           @r!   /test_api_openapi_write_security_and_read_routesra  [  s   ::D%%%

'
'
'C'3|||CV$__F::344@CGGGG::tuu  B  FI  I  I  I  I{{#EGII{VV'3....{{#EO\l\q\q\s\sLtLt{uu'3....zz"VWW'3....  ""7+q0000KKMM'"Eu2344????u7899fXEEEEu7899fXEEEEu7899eWDDDDu45665'AAAA34V<[I%PQ^_  dJ  J  J  J  J
333K/;|||K$V,-CDDDG<<:<KKW[^^^^^<<:<DDPTWWWWWWWr#   c                 6   t                      } t          j        t          d          5  t	          | i t                      ddi           d d d            n# 1 swxY w Y   t          j        t          d          5  t	          | i t                      ddi           d d d            n# 1 swxY w Y   t          j        t          d          5  t	          | i t                      d	d
i           d d d            n# 1 swxY w Y   t          j        t          d          5  t          | dd           d d d            n# 1 swxY w Y   t          j        t          d          5  t          | dd           d d d            d S # 1 swxY w Y   d S )NDatenqueller   rb   bank_scraperZeitraumrd   z
2026-01-01zeindeutige Zeitzonerf   z2026-07-23T12:00:00zausschliesslich CHFr`   EUR)r  base_currencyzDecimal-Textg?)r  absolute_tolerance)r"   r   r   r   r   rg   r   r  s    r!   #test_invalid_parameters_fail_closedri  x  s   ::D	z	7	7	7 M M$ K799 KlN K KLLLM M M M M M M M M M M M M M M	z	4	4	4 L L$ J799 Jm\ J JKKKL L L L L L L L L L L L L L L	z)>	?	?	? U U$ S799 Sm=R S STTTU U U U U U U U U U U U U U U	z)>	?	?	? V V&t<uUUUUV V V V V V V V V V V V V V V	z	8	8	8 Y Y&t<TWXXXXY Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y YsY   #AA A?#B..B25B2#DD
D)EEE.FFFc                 
   t                      } t          | ddddd           t          | ddddd	           t          | t                                }|d
         d         dk    sJ |d
         d         dk    sJ t	          | t          |                    }|d         dk    sJ |                     d                                          }d |D             ddgk    sJ |d         d         |d         d         k    sJ d S )Nr   r&   r`   r   r   r   r   r   r   r   r   r   r   r   r   zSELECT snapshot_id,snapshot_version,supersedes_snapshot_id,value_original
           FROM portfolio_valuation_snapshots ORDER BY snapshot_versionc                .    g | ]}|d          |d         fS r   rk   r   s     r!   r   zZtest_multiple_same_day_values_receive_sequential_versions_in_one_batch.<locals>.<listcomp>  r   r#   r   r   r   r   rC   )r"   rJ   r   rg   r   rx   r;   r   )r    rh   r   r   s       r!   Ftest_multiple_same_day_values_receive_sequential_versions_in_one_batchrl    s4   ::DtT3f>YZZZZtT3f>YZZZZgii00G8U#q((((8[)Q....t_W%=%=>>F#$))))<<	K  hjj 	 NMMMMR]_jQkkkkk7+,Q0FFFFFFFr#   c                   t                      }t          |dddd           t          |t                                }t	          |          }t          ||          }t          j        t          |d                             | 	                    t          dfd           t          ||          }||d         |d	         d
dk    sJ t          |          }t          d          z                                   |d<   t          j        t           d          5  t          ||           d d d            d S # 1 swxY w Y   d S )Nr   r&   r`   r   rs   _nowc                 *     t          d          z   S )Nr   )hours)r   r   s   r!   r   zbtest_preview_timestamp_is_hash_bound_and_expired_idempotent_retry_still_succeeds.<locals>.<lambda>  s    )RSBTBTBT8T r#   r   r   Tr   r   )daysr   r   )r"   rJ   r   rg   rx   r   r   fromisoformatrD   setattrr   r   r   	isoformatr   r   r   )rz   r    rh   r   r   retrytamperedrA   s          @r!   Ptest_preview_timestamp_is_hash_bound_and_expired_idempotent_retry_still_succeedsrw    s   ::DtXsL&999gii00Gg&&GdG,,E$S1E)F%G%GHHG0T0T0T0TUUUdG,,Ez!2j@QaeffffffG}}H&-	q0A0A0A&A%L%L%N%NH!"	z)>	?	?	? * *$)))* * * * * * * * * * * * * * * * * *s   D<<E E c            
        t                      } t          | ddddddd           |                     d	t          f           |                     d
t          f           |                     dt          f           |                                  t          | t          d                    }|d         d         dk    sJ t          | dt                    }|d         d         }|d         d         dk    sJ |d         d         dk    sJ d S )Nr   r   r&   r_   r   r  r	  r
  ar  INSERT INTO transactions(transaction_id,transaction_type,activity_kind,account_id,instrument_id,trade_date,
             quantity,gross_amount_original,currency_original,source_type,source_id,row_hash,is_confirmed,quality_status,created_at)
           VALUES('unconfirmed','buy','buy','a','i','2025-01-02','5','400','CHF','fixture','fixture','unconfirmed-hash',0,'ok',?)r  r  r   r   r   r   r`   r  r  r   rP   r  r9   r  )r"   rX   r;   r<   r=   r   rg   r   )r    rh   reconciliationr   s       r!   Ltest_unconfirmed_transactions_are_excluded_from_ingestion_and_reconciliationrz    s7   ::DD+uc<CZ^fkllllLL	E 
	   	LL	H	  
 	LL	L	  
 	KKMMMg.F&G&GHHG8\*a////3DZ]^^^N-(+D
H%----
H%222222r#   c            
        t                      } t          |            |                     d           |                     d           t          | dt                    }t          d |d         D                       }|d         d         d	k    sJ |d
         d         d	k    sJ ddht          |d                   k    sJ |d         d	k    sJ |d         d         d         dk    sJ |                     d           |                     d           t          | ddddddd           |                     d           |                     dt          f           |                     dt          f           |                                  t          | dt                    }t          d |d         D                       }|d
         d         d	k    sJ d |d         v sJ |d         d!         d         dk    sJ d S )"Nr3  zNUPDATE market_prices SET price_date='2025-12-30' WHERE market_price_id='price'r`   r  c              3  2   K   | ]}|d          dk    |V  dS r  rk   r   s     r!   r   zatest_stale_position_price_and_fx_inputs_are_not_comparable_and_reduce_coverage.<locals>.<genexpr>  s3      gg>P9QUf9f9f9f9f9f9fggr#   r  rP   r9   not_comparabler  stale_snapshotstale_pricer-  r#  pricescoveredr   zUPDATE positions_snapshot SET instrument_id='eur',snapshot_date='2025-12-31',quantity='1',market_value_chf='100' WHERE position_snapshot_id='pos'z3DELETE FROM transactions WHERE transaction_id='buy'zeur-buyr   r&   r_   eurr   90r
  zDELETE FROM market_pricesr/  zINSERT INTO fx_rates(fx_rate_id,base_currency,quote_currency,rate_date,rate,provider,rate_type,quality_status,created_at) VALUES('eur-fx','EUR','CHF','2025-12-30','1','fixture','close','ok',?)c              3  2   K   | ]}|d          dk    |V  dS r2  rk   r   s     r!   r   zatest_stale_position_price_and_fx_inputs_are_not_comparable_and_reduce_coverage.<locals>.<genexpr>  s3      mm4@R;SWl;l;lt;l;l;l;lmmr#   stale_fxfx)	r"   r  r;   r   r<   r&  r[  rX   r=   )r    staler   r  r  s        r!   Ntest_stale_position_price_and_fx_inputs_are_not_comparable_and_reduce_coverager    sa   ::D%%%LLlmmmLLabbb*4|QTUUUEgg}!5gggggD
H%)99999X&*:::::m,D4H0I0IIIII?.....X&y1Q6666LL  e  f  f  fLLFGGGD)UC%Z]eijjjjLL,---LL  M  PS  OU  V  V  VLL  T  WZ  V\  ]  ]  ]KKMMM-d,TWXXXH
mm 7mmm
m
mC{H%)99999^,,,,,J%i0A555555r#   r   )r   r   r   r   )r    r   r   r$   )r    r   rC   rD   rE   rD   rF   rD   rG   rD   rA   rD   rB   rH   r   r$   )r    r   rC   rD   rE   rD   rF   rD   rG   rD   r   r$   )r    r   rU   rD   rV   rD   rE   rD   rF   rD   rO   rH   rP   rH   rQ   rH   rR   rH   rS   rH   rT   rH   r   r$   )r[   )r3   rD   r   r\   )rh   r\   r   r\   )ry   r   rz   r{   r   r$   )r   r$   )ry   r   r   r$   )rz   r{   r   r$   )D
__future__r   concurrent.futuresr   r   r   r   pathlibr   r   fastapi.testclientr   !test_postfinance_portfolio_sourcer	   "test_portfolio_market_analytics_v1r
   test_sprint9_fx_fmp_historyr   jarvis_finance.api.dependenciesr   jarvis_finance.api.mainr   jarvis_finance.servicesr   &jarvis_finance.services.portfolio_datar   r   r   r   r   -jarvis_finance.services.portfolio_performancer   )jarvis_finance.services.performance_scoper   r   jarvis_finance.storage.databaser   !jarvis_finance.storage.migrationsr   r   r<   r"   r   rJ   rN   rX   rZ   rg   rx   r   r   r   r   r   r   r   r   r  r  r)  r9  rH  ra  ri  rl  rw  rz  r  rk   r#   r!   <module>r     s%   " " " " " " 1 1 1 1 1 1 ( ( ( ( ( ( ( (         ) ) ) ) ) ) M M M M M M Z Z Z Z Z Z K K K K K K 2 2 2 2 2 2 . . . . . . 2 2 2 2 2 2              V U U U U U        4 3 3 3 3 3 Q Q Q Q Q Q Q Q!    & & & &R qt  LP          !%TW[4VZ        ] ] ] ] ]   S S S S0H H H H"& & & & ; ; ; ; : : : :
9 
9 
9 
9/ / / /c c c c.	1 	1 	1 	1   "H H H H R R R R(
C 
C 
C 
CX X X X:Y Y Y YG G G G"* * * * 3 3 3 386 6 6 6 6 6r#   