
    +j(                        d dl mZ d dlZd dlmZ d dlZd dlmZmZm	Z	m
Z
mZmZmZmZmZ d dlmZmZ d dlmZ d dlmZ d Zdd	Zdd
ZddZddZddZddZddZddZddZ ddZ!ddZ"ddZ#ddZ$ddZ%y)    )annotationsN)Path)	CandidateInputconfirm_candidatedeterministic_parse_docx#import_confirmed_candidate_snapshotqwen_extract_candidatesqwen_resolve_staged_candidatesresolve_candidatestage_docx_candidatesupsert_candidate)CatalogEntryInputupsert_catalog_entry)connect_memory)apply_migrationsc                 t    t               } t        |        | j                  d       | j                  d       | S )NzINSERT INTO platforms(platform_id,name,platform_type,default_currency,created_at) VALUES('p1','PostFinance','broker','CHF','now')zINSERT INTO accounts(account_id,platform_id,account_name,account_type,currency,created_at) VALUES('a1','p1','Depot','brokerage','CHF','now'))r   r   execute)conns    /tests/unit/test_instrument_import_candidates.py
setup_connr      s=    DTLL  U  VLL  `  aK    c                    dj                  d |D              }d| d}t        j                  | d      5 }|j                  d|       d d d        | S # 1 sw Y   | S xY w)N c              3  (   K   | ]
  }d | d  yw)z<w:p><w:r><w:t>z</w:t></w:r></w:p>N ).0lines     r   	<genexpr>zmake_docx.<locals>.<genexpr>!   s     P$_TF*<=Ps   z<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body>z</w:body></w:document>wzword/document.xml)joinzipfileZipFilewritestr)pathlinesbodyxmlzfs        r   	make_docxr)       sf    77P%PPD\\`[aaw{C	s	# .r
'-.K.Ks   AAc                 l    t        ddddddddd	dddd	d
      }|j                  |        t        di |S )Nbroker.docxpostfinance1
Sample ETFSAMPr   CHF1.2345etfneeds_manual_review)source_fileplatformsource_row_numberraw_name
raw_tickerraw_isinraw_currencyraw_quantityraw_asset_classproposed_nameproposed_tickerproposed_currencyproposed_asset_classmapping_statusr   )dictupdater   )	overridesdatas     r   	candidaterF   )   sR    !"",D  	KK	!D!!r   c                    t        | dz  dg      }t        |d      }t        |      dk(  sJ |d   j                  dk(  sJ |d   j                  dk(  sJ y )	NzPostfinance.docx"Sample ETF CH0000000001 CHF 12.345r,   r5      r   CH0000000001exact_isin_match)r)   r   lenr9   rA   )tmp_pathdocxrowss      r   ,test_deterministic_parser_creates_candidatesrQ   >   se    X 225Y4Z[D#D=ADt9>>7~---7!!%7777r   c                   i  G d d      fd}| j                  d|       t               }t        dgdd      \  }}|d	u sJ t        |      d
k(  sJ dd   vsJ |d   j                  dk(  sJ t        ||d          |j                  d      j                         d   d
k(  sJ |j                  d      j                         d   dk(  sJ y )Nc                      e Zd Zd Zd Zd Zy)Ntest_qwen_extraction_creates_only_candidates_no_bookings.<locals>.FakeResponsec                    | S Nr   selfs    r   	__enter__zXtest_qwen_extraction_creates_only_candidates_no_bookings.<locals>.FakeResponse.__enter__I       D[r   c                     yNFr   rX   argss     r   __exit__zWtest_qwen_extraction_creates_only_candidates_no_bookings.<locals>.FakeResponse.__exit__J       %r   c                     y)Ns   {"choices":[{"message":{"content":"{\"positions\":[{\"name\":\"Q ETF\",\"ticker\":\"QETF\",\"currency\":\"CHF\",\"quantity\":\"1.23\",\"asset_class\":\"etf\",\"row_number\":2,\"confidence\":\"medium\"}]}"}}]}r   rW   s    r   readzStest_qwen_extraction_creates_only_candidates_no_bookings.<locals>.FakeResponse.readK   s     _r   N__name__
__module____qualname__rY   r_   rb   r   r   r   FakeResponserT   H   s    (/	_r   rg   c                N    | j                   j                  d      d<           S Nzutf-8r&   rE   decodereqkwargsrg   captureds     r   fake_urlopenzNtest_qwen_extraction_creates_only_candidates_no_bookings.<locals>.fake_urlopenM   "    88??73~r   <jarvis_finance.imports.instrument_candidates.request.urlopenzQ ETF CHF 1.23z	True.docxtrue_wealth)r4   r5   TrJ   z1.23r&   r   r   6SELECT COUNT(*) AS c FROM instrument_import_candidatesc&SELECT COUNT(*) AS c FROM transactions)setattrr   r	   rM   r;   r   r   fetchone)monkeypatchrp   r   rP   	availablerg   ro   s        @@r   8test_qwen_extraction_creates_only_candidates_no_bookingsr{   F   s    H_ _
 VXde<D-/?.@kdqrOD)t9>>&))))72%%%T47#<<PQZZ\]`aeffff<<@AJJLSQUVVVVr   c                 t    t               } t        | t        ddd            }t        | |      }|d   dk(  sJ y )Nr   r9   proposed_isinproposed_exchangerA   r3   )r   r   rF   r   r   cidresults      r   5test_candidate_without_isin_stays_needs_manual_reviewr   \   s@    <D
4Bb\^!_
`CtS)F"#'<<<<r   c                     t               } t        | t        ddddddd      d	       t        | t	        dd
            }t        | |      }|d   dk(  sJ y )Nr2   r.   rK   r/   SWXr0   )asset_classnameisintickerexchangetrading_currencyinstrument_currencytest)noter9   r~   rA   rL   )r   r   r   r   rF   r   r   s      r   0test_candidate_with_isin_can_be_exact_isin_matchr   c   sx    <D0U\jsy  EJ  ]b  x}   ~  EK  L
4NR`!a
bCtS)F"#'9999r   c            	     B   t               } t        | t        dddd            }t        j                  t
              5  t        | |dd       d d d        t        j                  t
              5  t        | |dddd	
       d d d        y # 1 sw Y   =xY w# 1 sw Y   y xY w)Nr   r/   )r9   r~   r   r>   manual review confirms)r   r>   a1
2025-12-31noT
account_idsnapshot_dater   confirm)r   r   rF   pytestraises
ValueErrorr   r   r   r   s     r   ,test_ticker_alone_does_not_confirm_or_importr   k   s    <D
4Bb\^pv!w
xC	z	" \$*BTZ[\	z	" }+D#$Vbimw{|} }\ \} }s   B	.B	BBc                    t               } t        | t                     }t        | |dddd       | j	                  d|f      j                         }|d   dk(  sJ | j	                  d	      j                         d
   dk(  sJ y )Nr   r/   r   r0   )r   r>   r   r?   zLSELECT mapping_status FROM instrument_import_candidates WHERE candidate_id=?rA   	confirmedzDSELECT COUNT(*) AS c FROM audit_log WHERE action='confirm_candidate'ru   rJ   )r   r   rF   r   r   rx   )r   r   rows      r   'test_manual_confirmation_sets_confirmedr   t   s    <D
4
-CdC&>PVjo  DI  J
,,ehkgm
n
w
w
yC K///<<^_hhjknosttttr   c            	        t               } t        | t        dd            }t        | |ddddd       t	        | |dd	d
d      }| j                  d|f      j                         }|d   dk(  sJ |d   dk(  sJ |d   dk(  sJ | j                  d      j                         d   dk(  sJ | j                  d      j                         d   dk(  sJ y )NrK   r   r   r/   r   r0   )r   r~   r>   r   r?   r   r   zapproved importTr   zWSELECT transaction_type, source_type, quantity FROM transactions WHERE transaction_id=?transaction_typeinitial_position_snapshotsource_typebroker_import_reviewed_snapshotquantityr1   zESELECT COUNT(*) AS c FROM alerts WHERE rule_id='cost_basis_uncertain'ru   rJ   zVSELECT COUNT(*) AS c FROM audit_log WHERE action='import_confirmed_candidate_snapshot')r   r   rF   r   r   r   rx   )r   r   tx_idr   s       r   Ntest_confirmed_candidate_can_create_initial_snapshot_with_uncertain_cost_basisr   }   s$   <D
4NR`!a
bCdC&>nnt  IN  bg  h/cdZfm~  IM  NE
,,psxrz
{  E  E  GC!"&AAAA}!BBBBz?h&&&<<_`iikloptuuuu<<pqzz|  ~A  B  FG  G  G  Gr   c            	        t               } t        | t                     }t        j                  t
              5  t        | |dddd       d d d        | j                  d      j                         d   dk(  sJ y # 1 sw Y   1xY w)	Nr   r   blockedTr   rv   ru   r   )	r   r   rF   r   r   r   r   r   rx   r   s     r   5test_unconfirmed_candidate_cannot_productively_importr      s    <D
4
-C	z	" B+D#$Vbir  }A  	BB<<@AJJLSQUVVVVB Bs   A::Bc                    t               }t        | dz  dg      }t        ||gd      }|j                  dk(  sJ |j	                  d      j                         d   dk(  sJ y )	NzTrue_Wealth.docxrH   T)dry_runrJ   rt   ru   r   )r   r)   r   candidates_totalr   rx   )rN   r   rO   r   s       r   1test_stage_docx_candidates_dry_run_writes_no_rowsr      sk    <DX 225Y4Z[D"4$>F""a'''<<PQZZ\]`aeffffr   c                    | dz  dz  }|j                   j                          t        |dg       t        |d      sJ y )Nrepor+   z Synthetic ETF CH0000000001 CHF 1r   rI   )parentmkdirr)   r   )rN   	repo_docxs     r   0test_no_real_data_in_repo_git_safety_placeholderr      sA    6!M1Ii<=>#I???r   c                   t               }t        |t        dd            i  G fdd      fd}| j                  d|       t	        |d	      }|j
                  d
u sJ |j                  d
u sJ |j                  dk(  sJ dd   vsJ dd   vsJ |j                  df      j                         }|d   dk(  sJ d|d   v sJ y )Nz999.123r   )r;   r   c                  $    e Zd Zd Zd Z fdZy)Htest_qwen_resolver_sends_sanitized_candidates_only.<locals>.FakeResponsec                    | S rV   r   rW   s    r   rY   zRtest_qwen_resolver_sends_sanitized_candidates_only.<locals>.FakeResponse.__enter__   rZ   r   c                     yr\   r   r]   s     r   r_   zQtest_qwen_resolver_sends_sanitized_candidates_only.<locals>.FakeResponse.__exit__   r`   r   c                b    dz  }dd l }|j                  ddd|iigi      j                         S )Na  {"updates":[{"candidate_id":"%s","proposed_name":"Sample ETF","proposed_ticker":"SAMP","proposed_isin":"","proposed_exchange":"","proposed_currency":"CHF","proposed_asset_class":"etf","mapping_status":"needs_manual_review","review_note":"Ticker-only candidate remains manual review."}]}r   choicesmessagecontentjsondumpsencode)rX   r&   r   r   s      r   rb   zMtest_qwen_resolver_sends_sanitized_candidates_only.<locals>.FakeResponse.read   sE     t  wz  zD::yI	47H+I*JKLSSUUr   Nrc   r   s   r   rg   r          (/	Vr   rg   c                N    | j                   j                  d      d<           S ri   rj   rl   s     r   rp   zHtest_qwen_resolver_sends_sanitized_candidates_only.<locals>.fake_urlopen   rq   r   rr   zqwen-3.6-agent)modelTr   r&   r;   zYSELECT mapping_status, review_note FROM instrument_import_candidates WHERE candidate_id=?rA   r3   zmanual reviewreview_note)
r   r   rF   rw   r
   qwen_available	qwen_usedconfirmed_afterr   rx   )ry   r   rp   r   r   rg   ro   r   s        @@@r   2test_qwen_resolver_sends_sanitized_candidates_onlyr      s   <D
4	UW!X
YCHV V VXde+D8HIF  D(((t###!!Q&&&HV,,,,&!1111
,,ruxtz
{  E  E  GC $9999c-0000r   c                0   t               }t        |t        ddd             G fdd      | j                  dfd       t	        |      }|j
                  dk(  sJ |j                  df      j                         }|d	   dk(  sJ |d
   dk(  sJ y )Nr   r}   c                  $    e Zd Zd Zd Z fdZy)Ftest_qwen_resolver_does_not_accept_invented_isin.<locals>.FakeResponsec                    | S rV   r   rW   s    r   rY   zPtest_qwen_resolver_does_not_accept_invented_isin.<locals>.FakeResponse.__enter__   rZ   r   c                     yr\   r   r]   s     r   r_   zOtest_qwen_resolver_does_not_accept_invented_isin.<locals>.FakeResponse.__exit__   r`   r   c                b    dd l }dz  }|j                  ddd|iigi      j                         S )Nr   a  {"updates":[{"candidate_id":"%s","proposed_name":"Sample ETF","proposed_ticker":"SAMP","proposed_isin":"CH9999999999","proposed_exchange":"SWX","proposed_currency":"CHF","proposed_asset_class":"etf","mapping_status":"probable","review_note":"Qwen guessed an ISIN."}]}r   r   r   r   )rX   r   r&   r   s      r   rb   zKtest_qwen_resolver_does_not_accept_invented_isin.<locals>.FakeResponse.read   sE     a  dg  gD::yI	47H+I*JKLSSUUr   Nrc   r   s   r   rg   r      r   r   rg   rr   c                             S rV   r   )akrg   s     r   <lambda>zBtest_qwen_resolver_does_not_accept_invented_isin.<locals>.<lambda>   s    hthv r   rJ   z[SELECT proposed_isin, mapping_status FROM instrument_import_candidates WHERE candidate_id=?r~   rA   probable)r   r   rF   rw   r
   warningsr   rx   )ry   r   r   r   rg   r   s       @@r   0test_qwen_resolver_does_not_accept_invented_isinr      s    <D
4Bb\^!_
`CV V VXvw+D1F??a
,,twzv|
}  G  G  IC2%%% J...r   )r$   r   r%   z	list[str]returnr   )r   r   )rN   r   r   None)r   r   )&
__future__r   r!   pathlibr   r   ,jarvis_finance.imports.instrument_candidatesr   r   r   r   r	   r
   r   r   r   "jarvis_finance.market_data.catalogr   r   jarvis_finance.storage.databaser   !jarvis_finance.storage.migrationsr   r   r)   rF   rQ   r{   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>r      s~    "   
 
 
 W : >"*8W,=:}u
GWg@1:/r   