
    iY                       d Z ddlmZ ddlZddlmc mZ ddl	Z	e	j                  j                  dd       ddlZddlZddlZddlZddlmZ ddlmZmZmZmZ ddlZddlmZmZ ddlmZ dd	lmZ dQd
ZdQdZ	 	 	 	 dR	 	 	 	 	 	 	 	 	 dSdZ G d d      Z  G d d      Z! G d d      Z" G d d      Z# G d d      Z$ G d d      Z% G d d      Z& G d d      Z' G d d      Z( G d d       Z) G d! d"      Z* G d# d$      Z+ G d% d&      Z, G d' d(      Z-e.d)k(  rddl/Z/g d* e        j`                  fd+ e        jb                  fd, e        jd                  fd- e!       jf                  fd. e!       jh                  fd/ e"       jj                  fd0 e#       jl                  fd1 e#       jn                  fd2 e$       jp                  fd3 e$       jr                  fd4 e%       jt                  fd5 e%       jv                  fd6 e%       jx                  fd7 e%       jz                  fd8 e%       j|                  fd9 e%       j~                  fd: e&       j                  fd; e&       j                  fd< e'       j                  fd= e'       j                  fd> e(       j                  fd? e(       j                  fd@ e)       j                  fdA e)       j                  fdB e*       j                  fdC e*       j                  fdD e*       j                  fdE e*       j                  fdF e-       j                  fdG e-       j                  fdH e,       j                  fZOdZPdZQeOD ]  \  ZRZS	  eS         eTdIeR        ePdJz  ZP  eTdMeP dNePeQz    dO       eQdk(  r	 eTdP       y e	j                  dJ       yy# eU$ r.ZV eTdKeR dLeV         e/j                          eQdJz  ZQY dZV[VdZV[Vww xY w)Tu  
Tests for Story 5.02 (Track B): ColdLedger — L4 Postgres Read/Write Client

Black Box tests (BB): verify the public contract from the caller's perspective —
    correct UUIDs returned, filtering, ordering, None on missing records.
White Box tests (WB): verify internals — connection pool getconn/putconn usage,
    no SQLite import, parameterised queries only, UUID4 generation, close().

ALL tests use mocks — NO real Postgres connection is required.

Story: 5.02
File under test: core/storage/cold_ledger.py
    )annotationsNz/mnt/e/genesis-system)datetime)	MagicMockcallpatchPropertyMock)
ColdLedger	SwarmSaga)r	   )r
   c                J   t               }t               }t        |      |j                  j                  _        t        d      |j                  j                  _        | |j
                  _        ||ng |j                  _        t               }||j                  _        |||fS )u  
    Returns (mock_pool, mock_conn) where:
      - mock_pool.getconn() → mock_conn
      - mock_pool.putconn(conn) is tracked
      - mock_conn.cursor() supports context manager
      - cursor.fetchone() → fetchone_return
      - cursor.fetchall() → fetchall_return or []
    return_valueF)r   cursorr   	__enter____exit__fetchonefetchallgetconn)fetchone_returnfetchall_return	mock_connmock_cursor	mock_pools        6/mnt/e/genesis-system/tests/track_b/test_story_5_02.py_make_pool_and_connr   +   s     I+K /8[.QI!!+-6E-JI!!* )8K%;J;V\^K%I%.I"i,,    c           	         t        | |      \  }}}t        d|      5  t        dddddd      }d	d	d	       |_        ||_        ||_        |S # 1 sw Y    xY w)
z:Return a ColdLedger instance wired to a fully-mocked pool.$psycopg2.pool.ThreadedConnectionPoolr   	localhost8  upgenesishostportuserpassworddbnameN)r   r   r	   
_mock_pool
_mock_conn_mock_cursor)r   r   r   r   r   ledgers         r   _make_ledgerr-   E   st    (;Hh(O%Iy+	5I	N Q[$%(cYP QQ "F!F%FMQ Qs   AAc                    t        | xs t        t        j                               |xs t        t        j                               ddiddig||t	        ddddd	d	      
      S )Nstep   deltaa        
   r   saga_id
session_idorchestrator_dagproposed_deltasresolved_statestatus
created_at)r
   struuiduuid4r   )r8   r9   r=   resolveds       r   
_make_sagarC   Q   s`     ,3tzz|,2TZZ\!2 !!3(D!RQ2 r   c                  "    e Zd ZdZd Zd Zd Zy)TestBB1WriteEventReturnsUUIDz-BB1: write_event returns a valid UUID string.c                   t               }|j                  ddddi      }t        |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                  |      d	z  }t        t        j                  |            d }y )
N	session-1dispatch_startkv5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstanceresultr?   py0py1py2py4)r-   write_eventrL   r?   @py_builtinslocals
@pytest_ar_should_repr_global_name	_safereprAssertionError_format_explanation)selfr,   rM   @py_assert3@py_format5s        r   test_returns_stringz0TestBB1WriteEventReturnsUUID.test_returns_stringj   s    ##K1AC:N&#&&&&&&&&z&&&z&&&&&&&&&&&&&&&&&#&&&#&&&&&&&&&&r   c                   t               }|j                  ddi       }t        j                  |      }t	        |      }||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                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }d	d
|iz  }t        t        j                  |            d x}}y )NrG   rH   ==)z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py5)sr?   parsedrM   rO   rP   py3py5assert %(py7)spy7)r-   rS   r@   UUIDr?   rV   _call_reprcomparerT   rU   rW   rX   rY   rZ   )r[   r,   rM   rb   @py_assert2@py_assert4@py_format6@py_format8s           r   !test_returned_value_is_valid_uuidz>TestBB1WriteEventReturnsUUID.test_returned_value_is_valid_uuido   s    ##K1A2F6"6{${f$$$${f$$$$$$s$$$s$$$$$$6$$$6$$${$$$$$$f$$$f$$$$$$$r   c                   t               }|j                  ddi       }|j                  ddi       }||k7  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      nddt	        j
                         v st        j                  |      rt        j                  |      nddz  }d	d
|iz  }t        t        j                  |            d }y )Ns1type_atype_b)!=)z%(py0)s != %(py2)sid1id2rO   rQ   assert %(py4)srR   )
r-   rS   rV   ri   rT   rU   rW   rX   rY   rZ   )r[   r,   rt   ru   @py_assert1@py_format3r]   s          r   %test_two_calls_return_different_uuidszBTestBB1WriteEventReturnsUUID.test_two_calls_return_different_uuidsv   s      x4  x4czscssccr   N)__name__
__module____qualname____doc__r^   rn   rz    r   r   rE   rE   g   s    7'
%r   rE   c                      e Zd ZdZd Zd Zy)TestBB2GetEventsFilterByTypez@BB2: get_events with event_type filter returns matching records.c           	        t        t        j                               ddddit        ddd      d}t	        |g	      }|j                  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}}
y )Nsess-1rH   r2   r0   r3   r4   r5   idr9   
event_typepayloadr>   r   )r   r`   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenresultsrO   rP   rd   py6assert %(py8)spy8r   r   z%(py1)s == %(py4)srP   rR   assert %(py6)sr   r?   r@   rA   r   r-   
get_eventsr   rV   ri   rT   rU   rW   rX   rY   rZ   )r[   rowr,   r   rj   @py_assert5rk   @py_format7@py_format9@py_assert0r\   r]   s               r   test_filter_appliedz0TestBB2GetEventsFilterByType.test_filter_applied   s:   djjl#"*Qx"4B/
 u-##H9I#J7| q |q    |q      s   s      7   7   |   q       qz,';+;;'+;;;;;'+;;;;';;;+;;;;;;;;r   c                   t        g       }|j                  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}}y	)
z@When event_type is None, a different (shorter) SQL path is used.r   r   r`   z%(py0)s == %(py3)sr   rO   rd   assert %(py5)sre   N)
r-   r   rV   ri   rT   rU   rW   rX   rY   rZ   r[   r,   r   rj   rx   @py_format4rl   s          r   (test_no_filter_uses_different_sql_branchzETestBB2GetEventsFilterByType.test_no_filter_uses_different_sql_branch   st    r*##H-w"}w"ww"r   N)r{   r|   r}   r~   r   r   r   r   r   r   r   }   s    J<r   r   c                      e Zd ZdZd Zy)TestBB3TwoWritesSameSessionz:BB3: two writes with same session_id are both retrievable.c           	     T   t        t        j                               }t        t        j                               |di t        ddd      d}t        t        j                               |di t        ddd      d}t	        ||g      }|j                  |      }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}}y )Nstartr3   r4   r5   r   endr   r`   r   r   r   r   r   r   r   )r[   r9   row1row2r,   r   rj   r   rk   r   r   s              r   test_get_events_returns_bothz8TestBB3TwoWritesSameSession.test_get_events_returns_both   s   &
djjl#:!bqRT@U

 djjl#:BhtQPR>S
 d|4##J/7| q |q    |q      s   s      7   7   |   q       r   N)r{   r|   r}   r~   r   r   r   r   r   r      s
    D!r   r   c                      e Zd ZdZd Zd Zy)TestBB4WriteSagaThenGetSagaz9BB4: write_saga then get_saga returns matching SwarmSaga.c                t   t        d      }|j                  |j                  |j                  |j                  d d|j
                  d}t        |      }t        j                  |d|j                        5  |j                  |      }d d d        |j                  |j                        }d }||u}|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                  }|j                  }
||
k(  }|st        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                  |      rt        j                   |      ndt        j                   |
      dz  }dd|iz  }t#        t        j$                  |            d x}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}}y # 1 sw Y   xY w)N	COMPLETEDr=   r7   r   
write_sagar   )is not)z%(py0)s is not %(py3)sfetchedr   r   re   r`   )zL%(py2)s
{%(py2)s = %(py0)s.saga_id
} == %(py6)s
{%(py6)s = %(py4)s.saga_id
}sagarO   rQ   rR   r   r   r   z.%(py2)s
{%(py2)s = %(py0)s.status
} == %(py5)srO   rQ   re   rf   rg   )rC   r8   r9   r:   r;   r>   r-   r   objectr   get_sagarV   ri   rT   rU   rW   rX   rY   rZ   r=   )r[   r   db_rowr,   r8   r   rj   rx   r   rl   r   r\   r   r   rk   rm   s                   r   %test_returned_saga_has_correct_fieldszATestBB4WriteSagaThenGetSaga.test_returned_saga_has_correct_fields   s   - ||// $ 5 5#33"!//
 v.\\&,T\\J 	.''-G	.//$,,/""wd""""wd""""""w"""w"""d""""""".$,,.,....,......w...w.........$...$...,.......~~,,~,,,,~,,,,,,w,,,w,,,~,,,,,,,,,,	. 	.s   7L--L7c                   t        d      }|j                  |j                  ddiddigd d|j                  d}t	        |      }|j                  |j                        }|j                  }|j                  }||k(  }|st        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                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }	t        t        j                  |	            d x}x}}|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                  |      t        j                  |
      dz  }dd|iz  }t        t        j                  |            d x}x}}
|j                  }ddi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  }dd|i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}}
y )NRUNNINGr   r/   r0   r1   r2   r7   r   r`   )zR%(py2)s
{%(py2)s = %(py0)s.session_id
} == %(py6)s
{%(py6)s = %(py4)s.session_id
}r   r   r   r   r   )z8%(py2)s
{%(py2)s = %(py0)s.orchestrator_dag
} == %(py5)sr   rf   rg   )z7%(py2)s
{%(py2)s = %(py0)s.proposed_deltas
} == %(py5)sisz6%(py2)s
{%(py2)s = %(py0)s.resolved_state
} is %(py5)s)rC   r8   r9   r>   r-   r   rV   ri   rT   rU   rW   rX   rY   rZ   r:   r;   r<   )r[   r   r   r,   r   rx   r   r\   r   r   rk   rl   rm   s                r   test_saga_fields_match_originalz;TestBB4WriteSagaThenGetSaga.test_saga_fields_match_original   sV   +||//!'!(#/"//
 v.//$,,/!!4T__4!_4444!_444444w444w444!444444T444T444_4444444''6FA;6';6666';666666w666w666'666;6666666&&:GS>*::&*:::::&*:::::::w:::w:::&:::*::::::::%%--%----%------w---w---%----------r   N)r{   r|   r}   r~   r   r   r   r   r   r   r      s    C-*.r   r   c                      e Zd ZdZd Zd Zy)TestBB5GetSagasBySessionz=BB5: get_sagas_by_session returns all sagas for that session.c           	        t        t        j                               }t        ddd      }t        t        j                               |i g d d|dt        t        j                               |i g d d|dg}t	        |      }|j                  |      }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        }t        |      }|sd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}}y )Nr3   r4   r5   r   r7   r   r   r`   r   r   r   r   r   r   c              3  <   K   | ]  }t        |t                y wN)rL   r
   ).0ss     r   	<genexpr>zGTestBB5GetSagasBySession.test_returns_multiple_sagas.<locals>.<genexpr>   s     =:a+=s   z,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}all)rO   rQ   rR   )r?   r@   rA   r   r-   get_sagas_by_sessionr   rV   ri   rT   rU   rW   rX   rY   rZ   r   )r[   r9   nowrowsr,   r   rj   r   rk   r   r   rx   r\   r]   s                 r   test_returns_multiple_sagasz4TestBB5GetSagasBySession.test_returns_multiple_sagas   st   &
tQ# tzz|,J$&2"&)3 tzz|,J$&2"&+S
 t,--j97| q |q    |q      s   s      7   7   |   q       =W==s=========s===s==============r   c                   t        g       }|j                  t        t        j                                     }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}}y )Nr   r`   r   r   r   r   re   )r-   r   r?   r@   rA   rV   ri   rT   rU   rW   rX   rY   rZ   r   s          r   !test_returns_empty_list_when_nonez:TestBB5GetSagasBySession.test_returns_empty_list_when_none   s    r*--c$**,.?@w"}w"ww"r   N)r{   r|   r}   r~   r   r   r   r   r   r   r      s    G>(r   r   c                  4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	TestWB1ConnectionPoolPatternuG   WB1: getconn/putconn pattern — connection always returned in finally.c                    t               }|j                  }|j                  ddi        |j                  j	                          |j
                  j                  |j                         y )Nsesstype)r-   r)   rS   r   assert_called_onceputconnassert_called_once_withr*   r[   r,   pools      r   +test_write_event_calls_getconn_then_putconnzHTestWB1ConnectionPoolPattern.test_write_event_calls_getconn_then_putconn   sN      662.''),,V->->?r   c                    t        g       }|j                  }|j                  d       |j                  j	                          |j
                  j                  |j                         y )Nr   r   )r-   r)   r   r   r   r   r   r*   r   s      r   *test_get_events_calls_getconn_then_putconnzGTestWB1ConnectionPoolPattern.test_get_events_calls_getconn_then_putconn   sM    r*  &!''),,V->->?r   c                    t               }|j                  }|j                  t                      |j                  j                          |j                  j                  |j                         y r   )	r-   r)   r   rC   r   r   r   r   r*   r   s      r   *test_write_saga_calls_getconn_then_putconnzGTestWB1ConnectionPoolPattern.test_write_saga_calls_getconn_then_putconn  sL      *,'''),,V->->?r   c                
   t        d       }|j                  }|j                  t        t	        j
                                      |j                  j                          |j                  j                  |j                         y )Nr   )r-   r)   r   r?   r@   rA   r   r   r   r   r*   r   s      r   (test_get_saga_calls_getconn_then_putconnzETestWB1ConnectionPoolPattern.test_get_saga_calls_getconn_then_putconn  sV    t,  DJJL)*''),,V->->?r   c                
   t        g       }|j                  }|j                  t        t	        j
                                      |j                  j                          |j                  j                  |j                         y )Nr   )r-   r)   r   r?   r@   rA   r   r   r   r   r*   r   s      r   4test_get_sagas_by_session_calls_getconn_then_putconnzQTestWB1ConnectionPoolPattern.test_get_sagas_by_session_calls_getconn_then_putconn  sX    r*  ##C

$56''),,V->->?r   c                D   t               }t        d      |j                  j                  _        |j
                  }t        j                  t              5  |j                  ddi        ddd       |j                  j                  |j                         y# 1 sw Y   /xY w)z<putconn must be called in finally even if the cursor raises.zDB errorr   r   N)r-   RuntimeErrorr+   executeside_effectr)   pytestraisesrS   r   r   r*   r   s      r   ,test_putconn_called_even_when_execute_raiseszITestWB1ConnectionPoolPattern.test_putconn_called_even_when_execute_raises  sz    2>z2J##/  ]]<( 	3vvr2	3 	,,V->->?	3 	3s   BBN)
r{   r|   r}   r~   r   r   r   r   r   r   r   r   r   r   r      s)    Q@@@@@	@r   r   c                      e Zd ZdZd Zd Zy)TestWB2NoSQLiteImportz5WB2: cold_ledger.py must not import sqlite3 anywhere.c                   dd l }|j                  d      j                         }d}||v}|st        j                  d|fd||f      t        j
                  |      dt        j                         v st        j                  |      rt        j
                  |      nddz  }t        j                  d      d	z   d
|iz  }t        t        j                  |            d x}}y )Nr   1/mnt/e/genesis-system/core/storage/cold_ledger.pyzimport sqlite3not inz%(py1)s not in %(py3)ssourcerP   rd   uE   cold_ledger.py must NOT import sqlite3 — Genesis Rule 7 (no SQLite)
>assert %(py5)sre   )pathlibPath	read_textrV   ri   rX   rT   rU   rW   _format_assertmsgrY   rZ   )r[   r   r   r   rj   r   rl   s          r    test_no_sqlite3_import_in_sourcez6TestWB2NoSQLiteImport.test_no_sqlite3_import_in_source)  s    QR\\^ 	
v- 	
 	
v 	
 	
 		   	
 	
	6	
 	
  (. 	
 	
 		 (. 	
 	
  T	
 	
 	
 	
 	
r   c                    dd l mc m} d}t        ||      }| }|st	        j
                  d      dz   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  }t        t	        j                  |            d x}x}}y )Nr   sqlite3z;sqlite3 must not be present in cold_ledger module namespacez;
>assert not %(py5)s
{%(py5)s = %(py0)s(%(py1)s, %(py3)s)
}hasattrmodrc   )core.storage.cold_ledgerstoragecold_ledgerr   rV   r   rT   rU   rW   rX   rY   rZ   )r[   r   rj   rk   @py_assert6r   s         r   $test_sqlite3_not_in_module_namespacez:TestWB2NoSQLiteImport.test_sqlite3_not_in_module_namespace0  s    .. ) 	
73	* 	
** 	
* 	
  J	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
   	
 	
 		  	
 	
 		 !* 	
 	
 		 + 	
 	
 	
 	
 	
 	
r   N)r{   r|   r}   r~   r   r  r   r   r   r   r   &  s    ?

r   r   c                      e Zd ZdZd Zd Zy)TestWB3ParameterisedQueriesuH   WB3: all SQL in cold_ledger.py uses %s placeholders — no f-string SQL.c                   dd l }|j                  d      j                         }t        j                  d|      }| }|s~t        j                  d|       dz   ddt        j                         v st        j                  |      rt        j                  |      ndiz  }t        t        j                  |            d }y )Nr   r   z4f["\'].*?(SELECT|INSERT|UPDATE|DELETE|WHERE).*?["\']z%Found f-string SQL (injection risk): z
>assert not %(py0)srO   fstring_sql)r   r   r   refindallrV   r   rT   rU   rW   rX   rY   rZ   )r[   r   r   r  rx   @py_format2s         r   test_no_fstring_sql_in_sourcez9TestWB3ParameterisedQueries.test_no_fstring_sql_in_source:  s    QR\\^ jj!XZ`a 	
 	
  4K=A	
 	
	6	
 	
   	
 	
 		  	
 	
 	
 	
 	
r   c                   t               }|j                  ddddi       |j                  }|j                  }|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                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}x}}|j                  j                  D ]/  }	|	d   }
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                  |      dz  }t        j                  d      dz   d|iz  }t        t        j                  |            dx}x}}2 y)uM   write_event must call cur.execute(sql, params) — not bare cur.execute(sql).zsess-123rq   rI   rJ   r0   )>=)zO%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.execute
}.call_count
} >= %(py7)scur)rO   rQ   rR   rg   zassert %(py9)spy9Nr   r4   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} >= %(py6)sr   argsr   uC   cur.execute() must be called with (sql, params) — not just (sql,)z
>assert %(py8)sr   )r-   rS   r+   r   
call_countrV   ri   rT   rU   rW   rX   rY   rZ   call_args_listr   r   )r[   r,   r  rx   r\   r   r   rm   @py_format10	call_argsr  rj   rk   r   r   s                  r   /test_write_event_sql_executed_with_params_tuplezKTestWB3ParameterisedQueries.test_write_event_sql_executed_with_params_tupleE  s   :x#s<!!{{*{%%**%****%******s***s***{***%**********33 	IQ<Dt9  9>   9  v     I   v     I   I   I !"    V     	r   N)r{   r|   r}   r~   r	  r  r   r   r   r  r  7  s    R	
r   r  c                      e Zd ZdZd Zd Zy)TestWB4WriteEventGeneratesUUID4z0WB4: write_event generates a UUID4 for event_id.c                L   t               }t        d      5 }t        j                  d      }||_        |j                  ddi       }d d d        t              }|k(  }|s#t        j                  d|fd||f      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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                          y # 1 sw Y   UxY w)Nz#core.storage.cold_ledger.uuid.uuid4z$12345678-1234-4234-b234-123456789abcr   tr`   )z0%(py0)s == %(py5)s
{%(py5)s = %(py2)s(%(py3)s)
}returned_idr?   fixed_id)rO   rQ   rd   re   rf   rg   )r-   r   r@   rh   r   rS   r?   rV   ri   rT   rU   rW   rX   rY   rZ   assert_called)	r[   r,   
mock_uuid4r  r  rk   rx   rl   rm   s	            r   (test_event_id_passed_to_execute_is_uuid4zHTestWB4WriteEventGeneratesUUID4.test_event_id_passed_to_execute_is_uuid4V  s    89 	>Zyy!GHH&.J# ,,VS"=K	>
 "(m+{m++++{m++++++{+++{++++++c+++c++++++(+++(+++m+++++++  "	> 	>s   0FF#c                H   dd l mc m} dd l}|j	                  d      j                         }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                  d      d	z   d
|iz  }t        t        j                  |            d x}}d}||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                  d      d	z   d
|iz  }t        t        j                  |            d x}}y )Nr   r   zuuid.uuid4()inz%(py1)s in %(py3)sr   r   z/write_event must use uuid.uuid4() for event IDsr   re   zuuid.uuid1()r   r   u    Must not use uuid1 — use uuid4)r   r   r   r   r   r   rV   ri   rX   rT   rU   rW   r   rY   rZ   )r[   r   r   r   r   rj   r   rl   s           r   test_uuid4_is_used_not_uuid1z<TestWB4WriteEventGeneratesUUID4.test_uuid4_is_used_not_uuid1a  s    ..QR\\^Z~'ZZZ~ZZZ~ZZZZZZZZZZZZZ)ZZZZZZZO~V+OOO~VOOO~OOOOOOVOOOVOOOO-OOOOOOOr   N)r{   r|   r}   r~   r  r!  r   r   r   r  r  S  s    :	#Pr   r  c                      e Zd ZdZd Zd Zy)"TestWB5GetSagaUnknownIdReturnsNonezAWB5: get_saga with an unknown ID returns None (not an exception).c                   t        d       }|j                  d      }d }||u }|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}}y )
Nr   z$00000000-0000-0000-0000-000000000000r   z%(py0)s is %(py3)srM   r   z-get_saga must return None for unknown saga_idr   re   )r-   r   rV   ri   rT   rU   rW   rX   r   rY   rZ   )r[   r,   rM   rj   rx   r   rl   s          r   !test_unknown_saga_id_returns_nonezDTestWB5GetSagaUnknownIdReturnsNone.test_unknown_saga_id_returns_nonem  s|    t,!GHNv~NNNvNNNNNNvNNNvNNNNNNNNNNNNNr   c                   t        d       }	 |j                  t        t        j                                     }d }||u }|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}}y # t        $ r"}t        j                  d|        Y d }~y d }~ww xY w)	Nr   r   r%  rM   r   r   re   zget_saga raised unexpectedly: )r-   r   r?   r@   rA   rV   ri   rT   rU   rW   rX   rY   rZ   	Exceptionr   fail)r[   r,   rM   rj   rx   r   rl   excs           r   "test_does_not_raise_on_missing_rowzETestWB5GetSagaUnknownIdReturnsNone.test_does_not_raise_on_missing_rowr  s    t,	@__S%67F!!6T>!!!6T!!!!!!6!!!6!!!T!!!!!!! 	@KK8>??	@s   CC   	D)DDN)r{   r|   r}   r~   r&  r+  r   r   r   r#  r#  j  s    KO
@r   r#  c                  (    e Zd ZdZd Zd Zd Zd Zy)TestPackageExportsz>ColdLedger and SwarmSaga must be importable from core.storage.c                   t         t        u }|st        j                  d|fdt         t        f      dt	        j
                         v st        j                  t               rt        j                  t               nddt	        j
                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }t        t        j                  |            d }y )Nr   z%(py0)s is %(py2)sColdLedgerFromPackager	   rv   rw   rR   )
r0  r	   rV   ri   rT   rU   rW   rX   rY   rZ   r[   rx   ry   r]   s       r   (test_cold_ledger_importable_from_packagez;TestPackageExports.test_cold_ledger_importable_from_package  sm    $
2222$
222222$222$222222
222
2222222r   c                   t         t        u }|st        j                  d|fdt         t        f      dt	        j
                         v st        j                  t               rt        j                  t               nddt	        j
                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }t        t        j                  |            d }y )Nr   r/  SwarmSagaFromPackager
   rv   rw   rR   )
r4  r
   rV   ri   rT   rU   rW   rX   rY   rZ   r1  s       r   'test_swarm_saga_importable_from_packagez:TestPackageExports.test_swarm_saga_importable_from_package  sm    #y0000#y000000#000#000000y000y0000000r   c                Z   ddl m} 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}}y )
Nr   __all__r	   r  r   r8  r   r   re   
core.storager8  rV   ri   rX   rT   rU   rW   rY   rZ   r[   r8  r   rj   r   rl   s         r   test_all_includes_cold_ledgerz0TestPackageExports.test_all_includes_cold_ledger  s^    (&|w&&&&|w&&&|&&&&&&w&&&w&&&&&&&r   c                Z   ddl m} 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}}y )
Nr   r7  r
   r  r   r8  r   r   re   r9  r;  s         r   test_all_includes_swarm_sagaz/TestPackageExports.test_all_includes_swarm_saga  s^    (%{g%%%%{g%%%{%%%%%%g%%%g%%%%%%%r   N)r{   r|   r}   r~   r2  r5  r<  r>  r   r   r   r-  r-    s    H31'&r   r-  c                  "    e Zd ZdZd Zd Zd Zy)TestSwarmSagaDataclassz+SwarmSaga must be a proper typed dataclass.c                X   t               }t        |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                  |      dz  }t        t        j                  |            d }y )NrK   rL   r   r
   rN   )
rC   rL   r
   rT   rU   rV   rW   rX   rY   rZ   )r[   r   r\   r]   s       r   test_instantiationz)TestSwarmSagaDataclass.test_instantiation  s    |$	********z***z******$***$******	***	**********r   c                B   t        d      }|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        |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t        j                  |      dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }	t        t        j                  |	            d x}}|j                  }t        |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t        j                  |      dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }	t        t        j                  |	            d 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}}y )NFAILEDr   r`   r   r   r   rf   rg   z[assert %(py6)s
{%(py6)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.orchestrator_dag
}, %(py4)s)
}rL   dict)rO   rP   rd   rR   r   zZassert %(py6)s
{%(py6)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.proposed_deltas
}, %(py4)s)
}listr   r   )rC   r=   rV   ri   rT   rU   rW   rX   rY   rZ   r:   rL   rE  r;   rF  r<   )
r[   r   rx   rk   r\   rl   rm   rj   r   r   s
             r   test_all_fields_accessiblez1TestSwarmSagaDataclass.test_all_fields_accessible  s+   *{{&h&{h&&&&{h&&&&&&t&&&t&&&{&&&h&&&&&&&//6z/66666666z666z666666$666$666/6666666666666666666..5z.55555555z555z555555$555$555.5555555555555555555""*d*"d****"d******t***t***"***d*******r   c                   t        ddi      }|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                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            d x}x}}y )
NfinalT)rB   r`   )z6%(py2)s
{%(py2)s = %(py0)s.resolved_state
} == %(py5)sr   r   rf   rg   )
rC   r<   rV   ri   rT   rU   rW   rX   rY   rZ   )r[   r   rx   rk   r\   rl   rm   s          r   test_resolved_state_can_be_dictz6TestSwarmSagaDataclass.test_resolved_state_can_be_dict  s    GT?3""5wo5"o5555"o555555t555t555"555o5555555r   N)r{   r|   r}   r~   rB  rG  rJ  r   r   r   r@  r@    s    5++6r   r@  c                      e Zd ZdZd Zd Zy)	TestClosez"close() must call pool.closeall().c                    t               }|j                          |j                  j                  j	                          y r   )r-   closer)   closeallr   )r[   r,   s     r   test_close_calls_closeallz#TestClose.test_close_calls_closeall  s*    ""557r   c                p   t               }|j                          |j                          |j                  }|j                  }|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                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d x}x}x}x}}y )Nr4   r`   )zp%(py6)s
{%(py6)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s._mock_pool
}.closeall
}.call_count
} == %(py9)sr,   )rO   rQ   rR   r   r  zassert %(py11)spy11)r-   rN  r)   rO  r  rV   ri   rT   rU   rW   rX   rY   rZ   )	r[   r,   rx   r\   r   @py_assert8@py_assert7r  @py_format12s	            r   *test_close_is_idempotent_when_pool_is_mockz4TestClose.test_close_is_idempotent_when_pool_is_mock  s      9 ))9)4499499994999999v999v999 999)999499999999999r   N)r{   r|   r}   r~   rP  rV  r   r   r   rL  rL    s    ,8
:r   rL  c                      e Zd ZdZd Zd Zy)TestConnectionPoolInitzEColdLedger must create ThreadedConnectionPool(minconn=2, maxconn=10).c                   t        d      5 }t               |_        dddddd}t        |       d d d        j                  }|d   }|d   }d	}||k(  }|st        j                  d
|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|d          dz   d|iz  }	t        t        j                  |	            d x}x}}|d   }d}||k(  }|st        j                  d
|fd||f      t        j                  |      t        j                  |      dz  }t        j                  d|d          dz   d|iz  }	t        t        j                  |	            d x}x}}y # 1 sw Y   TxY w)Nr   hr   r    r!   dbr#   r   r4   r`   r   r   zminconn must be 2, got z
>assert %(py6)sr   r0   r6   zmaxconn must be 10, got )r   r   r   r	   r  rV   ri   rX   r   rY   rZ   )
r[   mock_pool_clsparamsr  
positionalr   r\   rj   r]   r   s
             r   &test_pool_created_with_correct_min_maxz=TestConnectionPoolInit.test_pool_created_with_correct_min_max  s'   9: 	m)2M&!4"%7Fv		 "++	q\
!}LL}!LLL}LLL}LLLLLL%<Z]O#LLLLLLLL!}NN}"NNN}NNN}NNNNNN&>z!}o$NNNNNNNN	 	s   #FFc                   t        d      5 }t               |_        dddddd}t        |       d d d        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}}y # 1 sw Y   xY w)Nr   myhosti9  adminsecretgenesis_testr#   r0   r$   r`   r   r   r   r   r(   )
r   r   r   r	   r  rV   ri   rX   rY   rZ   )	r[   r\  r]  kwargsr   r\   rj   r]   r   s	            r   $test_pool_receives_connection_paramsz;TestConnectionPoolInit.test_pool_receives_connection_params  s    9: 	m)2M&&g"*nFFv		 ((+f~))~))))~)))~))))))))))h1>1>1111>111111>1111111	 	s   #EEN)r{   r|   r}   r~   r_  rf  r   r   r   rX  rX    s    O
O	2r   rX  __main__z BB1a: write_event returns stringz"BB1b: returned value is valid UUIDu#   BB1c: two calls → different UUIDszBB2a: filter appliedz%BB2b: no filter uses different branchzBB3: two writes same sessionzBB4a: write saga then get sagaz BB4b: saga fields match originalz#BB5a: get sagas by session multiplezBB5b: get sagas emptyz!WB1a: write_event getconn/putconnz WB1b: get_events getconn/putconnz WB1c: write_saga getconn/putconnzWB1d: get_saga getconn/putconnz*WB1e: get_sagas_by_session getconn/putconnz"WB1f: putconn called even on errorz!WB2a: no sqlite3 import in sourcezWB2b: sqlite3 not in namespacezWB3a: no f-string SQLz"WB3b: write_event uses param tuplezWB4a: event_id is UUID4zWB4b: uuid4 not uuid1u   WB5a: unknown saga → Nonez!WB5b: no exception on missing rowzPKG: ColdLedger importablezPKG: SwarmSaga importablezPKG: __all__ has ColdLedgerzPKG: __all__ has SwarmSagazPOOL: minconn=2 maxconn=10zPOOL: params forwardedzCLOSE: closeall calledz	  [PASS] r0   z	  [FAIL] z: 
/z tests passedz4ALL TESTS PASSED -- Story 5.02 (Track B): ColdLedger)NN)NNr   N)
r8   
str | Noner9   rj  r=   r?   rB   zdict | Nonereturnr
   )Yr~   
__future__r   builtinsrT   _pytest.assertion.rewrite	assertionrewriterV   syspathinsert	importlibjsonr  r@   r   unittest.mockr   r   r   r   r   r   r	   r
   r:  r0  r4  r   r-   rC   rE   r   r   r   r   r   r   r  r  r#  r-  r@  rL  rX  r{   	tracebackr^   rn   rz   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r	  r  r  r!  r&  r+  r2  r5  r<  r>  r_  rf  rP  testspassedfailednamefnprintr(  r*  	print_excexitr   r   r   <module>r     s   #   
 * +   	   > >  ; < :-4	 ! 	  	
 , , 0! !$(. (.V D/@ /@d
 
" 8P P.@ @.& &.6 60: :(2 2> z%	+-I-K-_-_`% 
./K/M/o/op% 
/0L0N0t0tu	%
 
 !=!?!S!ST% 
12N2P2y2yz% 
()D)F)c)cd% 
*+F+H+n+no% 
,-H-J-j-jk% 
/0H0J0f0fg% 
!":"<"^"^_% 
-.J.L.x.xy% 
,-I-K-v-vw% 
,-I-K-v-vw%  
*+G+I+r+rs!%" 
67S7U  8K  8K  	L#%$ 
./K/M/z/z{%%& 
-.C.E.f.fg'%( 
*+@+B+g+gh)%* 
!"="?"]"]^+%, 
./J/L/|/|}-%. 
#$C$E$n$no/%0 
!"A"C"`"`a1%2 
'(J(L(n(no3%4 
-.P.R.u.uv5%8 
&'9';'d'de9%: 
%&8&:&b&bc;%< 
'(:(<(Z(Z[=%> 
&'9';'X'XY?%B 
&'='?'f'fgC%D 
"#9#;#`#`aE%H 
"9;#H#HII%EN FF b	DIdV$%aKF	 
Bvha(
67{DEu b  	IdV2cU+,I!aKF	s   <O		O<$O77O<