
    i                       d Z ddlmZ ddlZddlmc mZ ddl	Z	ddl
Z
ddlZddlZddlZddlZej                  j!                  dd       ddlZddlmZmZ ddlmZmZmZ ddlmZmZ ddlmZ dd	lmZ dd
lm Z  ddl!m"Z" djdZ#dkdjdZ$dkdldZ%	 	 	 	 dm	 	 	 	 	 	 	 	 	 dndZ&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#      Z0 G d$ d%      Z1 G d& d'      Z2e3d(k(  rddl4Z5g d) e(       jl                  fd* e(       jn                  fd+ e(       jp                  fd, e(       jr                  fd- e(       jt                  fd. e(       jv                  fd/ e(       jx                  fd0 e)       jz                  fd1 e)       j|                  fd2 e)       j~                  fd3 e)       j                  fd4 e*       j                  fd5 e*       j                  fd6 e*       j                  fd7 e*       j                  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dI e.       j                  fdJ e.       j                  fdK e.       j                  fdL e.       j                  fdM e.       j                  fdN e/       j                  fdO e/       j                  fdP e/       j                  fdQ e/       j                  fdR e/       j                  fdS e/       j                  fdT e0       j                  fdU e0       j                  fdV e0       j                  fdW e0       j                  fdX e0       j                  fdY e1       j                  fdZ e1       j                  fd[ e1       j                  fd\ e1       j                  fd] e1       j                  fd^ e1       j                  fd_ e2       j                  fd` e2       j                  fda e2       j                  fdb e2       j                  fZpdZqdZrepD ]  \  ZsZt	  et         eudces        eqdz  Zq  eudfeq dgeqerz    dh       erdk(  r	 eudi       y ej                  d       yy# ev$ r.Zw euddes deew         e5j                          erdz  ZrY dZw[wdZw[www xY w)ou'  
Tests for Story 5.09 (Track B): Module 5 WB Integration Test Suite
                                  Event Sourcing Internal Verification

White Box tests: verify the internal implementation contracts of all
Module 5 storage components — SQL parameterisation, connection pool patterns,
dual-write behaviour, state-folding logic, hash determinism, and
no-sqlite guarantee.

ALL external calls (psycopg2, Redis) are mocked. Zero real I/O.

Story: 5.09
Files under test:
  core/storage/event_sourcing.py    (primary — stream + replay + state)
  core/storage/cold_ledger.py       (SQL parameterisation + pool pattern)
  core/storage/session_store.py     (SQL parameterisation)
  core/storage/shadow_router.py     (SHA256 determinism)
  core/storage/cold_ledger_interceptor.py  (_fire_and_forget non-fatal)
    )annotationsNz/mnt/e/genesis-system)datetimetimezone)	MagicMockpatchcall)EventSourcingStreamGenesisEvent)
ColdLedger)SessionStore)ShadowRouter)ColdLedgerInterceptorc                 "   t               } t               }g |j                  _        d|j                  _        d|_        t        |      | j
                  j                  _        t        d      | j
                  j                  _        || _        | S )zFReturn a mock psycopg2 connection with context-manager cursor support.Nr   return_valueF)	r   fetchallr   fetchonerowcountcursor	__enter____exit___cursor)connr   s     :/mnt/e/genesis-system/tests/storage/test_event_sourcing.py_make_mock_connr   5   si    ;D[F#%FOO #'FOO FO)2)GDKK&(1u(EDKK%DLK    c                r    | 
t               } t               }| |j                  _        t               |_        |S )z%Return a mock ThreadedConnectionPool.)r   r   getconnr   putconn)r   pools     r   _make_mock_poolr!   B   s0    | ;D $DLL;DLKr   c           	         | 
t               } t        |       }t        d|      5  t        dddddd      }ddd       || fS # 1 sw Y   xY w)	z0Create a ColdLedger with a mocked pool and conn.Nz$psycopg2.pool.ThreadedConnectionPoolr   	localhosti8  test)hostportuserpassworddbname)connection_params)r   r!   r   r   )r   r    ledgers      r   _make_cold_ledgerr,   L   sc    | 4 D	5D	I &/
 
 4 s   AA   c           
         t        t        t        j                               |||xs ddi| t	        j
                  t        j                              S )z'Return a GenesisEvent for use in tests.agentforgetz)id
session_id
event_typepayloadversion
created_at)r
   struuiduuid4r   nowr   utc)r7   r4   r5   r6   s       r   _make_eventr>   Y   sE     tzz|-GW-<<8<<0 r   c                H    t        j                         j                  |       S )z)Execute an async coroutine synchronously.)asyncioget_event_looprun_until_complete)coros    r   _runrD   j   s    !!#66t<<r   c                  :    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
y	)
TestWB1AppendDualWritezSWB1: append() writes to ColdLedger (mandatory) AND to Redis via XADD (best-effort).c                    t               }t               }t        ||      }t        d      }|j                  |       |j                  j                          |j                  j                  }|d   r|d   }y|d   }y)u<   Postgres write is mandatory — must be called exactly once.cold_ledgerredis_clientr-   r7   r   N)r   r	   r>   appendwrite_eventassert_called_once	call_args)selfmock_ledgerredisstreameventcall_kwargsargss          r   $test_append_calls_ledger_write_eventz;TestWB1AppendDualWrite.test_append_calls_ledger_write_eventv   sl    k$5QA&e224!--77!,Q{1~[^r   c                   t               }t        |      }t        dd      }|j                  |       |j                  j
                  }g }|d   }|j                  }d} ||      }	d}
|	|
k(  }|}|s0g }|d   }t        |      }d}||k\  }|}|r|d   d   }d}||k(  }|}|}|s*t        j                  d|fd|	|
f      t        j                  |      t        j                  |      t        j                  |      t        j                  |	      t        j                  |
      d	z  }d
d|iz  }|j                  |       |sNt        j                  dfdf      dt        j                         v st        j                  t              rt        j                  t              ndt        j                        t        j                  |      t        j                  |      dz  }dd|iz  }j                  |       |r_t        j                  dfdf      t        j                  |      t        j                  |      dz  }dd|iz  }|j                  |       t        j                  |d      i z  }dd|iz  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                   |            d x}x}x}x}x}x}	x}x}
x}x}x}x}x}x}x}x}}y )NrI   
my-sessionr-   )r4   r7   r4   r   ==)zJ%(py9)s
{%(py9)s = %(py5)s
{%(py5)s = %(py3)s.get
}(%(py7)s)
} == %(py12)s)py3py5py7py9py12z%(py14)spy14)>=)z5%(py22)s
{%(py22)s = %(py18)s(%(py20)s)
} >= %(py25)slen)py18py20py22py25z%(py27)spy27)z%(py30)s == %(py33)s)py30py33z%(py35)spy35z%(py38)spy38zassert %(py41)spy41)r   r	   r>   rL   rM   rO   getrd   
@pytest_ar_call_reprcompare	_saferepr@py_builtinslocals_should_repr_global_name_format_boolopAssertionError_format_explanation) rP   rQ   rS   rT   rU   @py_assert1@py_assert2@py_assert4@py_assert6@py_assert8@py_assert11@py_assert10@py_assert0@py_assert17@py_assert19@py_assert21@py_assert24@py_assert23@py_assert16@py_assert29@py_assert32@py_assert31@py_format13@py_format15@py_format26@py_format28@py_format34@py_format36@py_format37@py_format39@py_format40@py_format42s                                    r   'test_append_passes_session_id_to_ledgerz>TestWB1AppendDualWrite.test_append_passes_session_id_to_ledger   s   k$=|Q?e!--77	P{1~ 	P~!! 	P, 	P!,/ 	P< 	P/<? 	PN	PN#	P'(	P#q(	P-8^A->	PBN	P->,-N	P 	P>O>O	P/< 	P 	PFOi  	P 	PFOi " 	P 	PFOi #/ 	P 	PFOi 0 	P 	PFOi 4@ 	P 	P 	PIO	PIO	P>O>O	P#q	P 	PIO	P 	P7O7O	P 	PFOi	P 	PFOiN	P 	PFOi#	P 	PFOiq	P 	P 	PIO	PIO	P>O>O	P->,	P 	PFOi->	P 	PFOi,	P 	P 	PIO	PIO	PAO	P 	P 	PIO	PIO	PAO	P 	P 	P 	P<O<O	P 	P 	P 	P 	P 	Pr   c                    t               }t               }t        ||      }t        d      }|j                  |       |j                  j                          y)z?Redis XADD must be called once when a redis client is provided.rH   r-   rK   N)r   r	   r>   rL   xaddrN   rP   rQ   rR   rS   rT   s        r   0test_append_calls_redis_xadd_when_redis_providedzGTestWB1AppendDualWrite.test_append_calls_redis_xadd_when_redis_provided   sB    k$5QA&e

%%'r   c                   t               }t               }t        ||      }t        d      }|j                  |       |j                  j
                  d   d   }d}||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)z1XADD must target the 'genesis:events' stream key.rH      rK   r   zgenesis:eventsr[   z%(py0)s == %(py3)sxadd_keypy0r]   assert %(py5)sr^   N)r   r	   r>   rL   r   rO   rp   rq   rs   rt   ru   rr   rw   rx   )
rP   rQ   rR   rS   rT   r   rz   ry   @py_format4@py_format6s
             r   (test_append_xadd_uses_genesis_events_keyz?TestWB1AppendDualWrite.test_append_xadd_uses_genesis_events_key   s    k$5QA&e::''*1-++x+++++x+++++++x+++x+++++++++++r   c                   t               }t               }t        ||      }t        d      }|j                  |       |j                  j
                  d   d   }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}}d}
t        |
      }|d   }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |
      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}x}}y)z6The Redis XADD message must include the event version.rH      rK   r   r-   r7   inz%(py1)s in %(py3)s	xadd_datapy1r]   r   r^   Nr[   )z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)sr9   )r   py2py4r_   zassert %(py9)sr`   )r   r	   r>   rL   r   rO   rp   rq   rr   rs   rt   ru   rw   rx   r9   )rP   rQ   rR   rS   rT   r   r   rz   r   r   ry   @py_assert3r|   @py_assert5@py_format8@py_format10s                   r   'test_append_xadd_includes_version_fieldz>TestWB1AppendDualWrite.test_append_xadd_includes_version_field   s)   k$5QA&eJJ((+A.	%yI%%%%yI%%%y%%%%%%I%%%I%%%%%%%-s1v-9--v-----v-------s---s---1---v-----------r   c                    t               }t        |d      }t        d      }|j                  |       |j                  j                          y)z/append() with redis_client=None must not raise.NrH   r-   rK   )r   r	   r>   rL   rM   rN   )rP   rQ   rS   rT   s       r   (test_append_without_redis_does_not_raisez?TestWB1AppendDualWrite.test_append_without_redis_does_not_raise   s=    k$4PA&e224r   c                    t               }t               }t        d      |j                  _        t	        ||      }t        d      }|j                  |       |j                  j                          y)uG   Redis XADD failure must be swallowed — ColdLedger write must succeed.z
Redis downrH   r-   rK   N)	r   ConnectionErrorr   side_effectr	   r>   rL   rM   rN   r   s        r   (test_append_redis_failure_does_not_raisez?TestWB1AppendDualWrite.test_append_redis_failure_does_not_raise   sU    k!0!>

$5QA&e224r   N)__name__
__module____qualname____doc__rW   r   r   r   r   r   r    r   r   rF   rF   s   s*    ]DP(,	.55r   rF   c                  :    e Zd ZdZd	dZd
ddZd Zd Zd Zd Z	y)TestWB2ReplayVersionOrderz9WB2: replay() returns events sorted by version ascending.c                <    t               }||j                  _        |S )z>Return a mock ledger whose get_events returns *rows* as dicts.)r   
get_eventsr   )rP   rowsr+   s      r   _build_ledger_with_rowsz1TestWB2ReplayVersionOrder._build_ledger_with_rows   s    )-&r   c                    t        j                  t        j                        }t	        t        j                               |dt	        t        j                               ||j                         d| d|dS )z&Build a synthetic ColdLedger row dict.r1   
test_eventv)event_idr7   r8   datar3   r4   r5   r6   r8   r   r<   r   r=   r9   r:   r;   	isoformat)rP   r7   r4   tss       r   _rowzTestWB2ReplayVersionOrder._row   s_    \\X\\*djjl#$&

-" llnG9	 
 	
r   c                   | j                  g       }t        |      }|j                  d      }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 )NrY   s15assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstanceresultlistr   r   r   r   )r   r	   replayr   r   rs   rt   rp   ru   rr   rw   rx   )rP   r+   rS   r   r   @py_format5s         r   test_replay_returns_listz2TestWB2ReplayVersionOrder.test_replay_returns_list   s    --b1$8t$&$''''''''z'''z''''''&'''&''''''$'''$''''''''''r   c                   | j                  | j                  d      g      }t        |      }|j                  d      }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 )Nr-   rY   r   c              3  <   K   | ]  }t        |t                y wN)r   r
   ).0es     r   	<genexpr>zVTestWB2ReplayVersionOrder.test_replay_returns_genesis_event_objects.<locals>.<genexpr>   s     ?1:a.?s   ,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}allr   r   r   )r   r   r	   r   r   rs   rt   rp   ru   rr   rw   rx   )rP   r+   rS   r   ry   r   r   s          r   )test_replay_returns_genesis_event_objectszCTestWB2ReplayVersionOrder.test_replay_returns_genesis_event_objects   s    --tyy|n=$8t$???s?????????s???s??????????????r   c                   | j                  d      | j                  d      | j                  d      g}| j                  |      }t        |      }|j                  d      }|D cg c]  }|j                   }}g 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c c}w )uH   Events returned in order: version 1, 2, 3 — regardless of input order.   r-   r   rY   r   )r-   r   r   r[   z%(py1)s == %(py4)sr   r   assert %(py6)spy6N)
r   r   r	   r   r7   rp   rq   rr   rw   rx   )rP   r   r+   rS   r   r   r   r   rz   r   @py_format7s              r   'test_replay_sorted_by_version_ascendingzATestWB2ReplayVersionOrder.test_replay_sorted_by_version_ascending   s    		!diilDIIaL9--d3$8t$#)*a		*7*i7*i7777*i777*777i7777777*s   %C8c                    t               }g |j                  _        t        |      }|j	                  d       |j                  j                  d       y )NrY   rZ   )r   r   r   r	   r   assert_called_once_with)rP   r+   rS   s      r   3test_replay_calls_ledger_get_events_with_session_idzMTestWB2ReplayVersionOrder.test_replay_calls_ledger_get_events_with_session_id   sA    )+&$8l#11,?r   N)r   r   returnr   )r   r7   intr   dict)
r   r   r   r   r   r   r   r   r   r   r   r   r   r   r      s%    C
 (@8@r   r   c                  0    e Zd ZdZddZd Zd Zd Zd Zy)	TestWB3ReplayFromVersionz>WB3: replay_from_version(N) excludes events where version < N.c                    t        j                  t        j                        }t	        t        j                               ddt	        t        j                               ||j                         d|dS )Nr1   r   evtr   r7   r8   r   r   )rP   r7   r   s      r   r   zTestWB3ReplayFromVersion._row   sV    \\X\\*djjl#

-" lln
 

 
	
r   c                   t               }| j                  d      | j                  d      | j                  d      | j                  d      | j                  d      g|j                  _        t	        |      }|j                  dd      }|D cg c]  }|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  }d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  }d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  }d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  }d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  }dd|iz  }	t        t        j                  |	            d x}}y c c}w )Nr-   r      r   	   rY   r   from_versionnot inz%(py1)s not in %(py3)sversionsr   r   r^   r   r   r   r   r   r   r	   replay_from_versionr7   rp   rq   rr   rs   rt   ru   rw   rx   
rP   r+   rS   r   r   r   r   rz   r   r   s
             r   )test_from_version_5_skips_versions_1_to_4zBTestWB3ReplayFromVersion.test_from_version_5_skips_versions_1_to_4  sC   IIaL$))A,		!diilDIIaL*
& %8++Dq+A'-.!AII.. q    q   q                 q    q   q                qH}qHqHHqH}qHqHHqH}qHqHH /s   Oc                   t               }| j                  d      | j                  d      | j                  d      g|j                  _        t	        |      }|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}}y )Nr-   r   r   rY   r   r   r[   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)srd   r   r   r   r]   r   assert %(py8)spy8)r   r   r   r   r	   r   rd   rp   rq   rs   rt   ru   rr   rw   rx   )	rP   r+   rS   r   rz   r   r{   r   @py_format9s	            r   test_from_version_1_returns_allz8TestWB3ReplayFromVersion.test_from_version_1_returns_all  s    IIaL$))A,		!*
& %8++Dq+A6{a{a{ass66{ar   c                   t               }| j                  d      | j                  d      g|j                  _        t	        |      }|j                  dd      }g }||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
z  }dd|iz  }t        t        j                  |            d x}}y )Nr-   r   rY   r   d   r   r[   r   r   r   r   r^   )r   r   r   r   r	   r   rp   rq   rs   rt   ru   rr   rw   rx   )rP   r+   rS   r   rz   ry   r   r   s           r   *test_from_version_beyond_all_returns_emptyzCTestWB3ReplayFromVersion.test_from_version_beyond_all_returns_empty$  s    IIaL$))A,*
& %8++Ds+Cv|vvvr   c                   t               }| j                  d      | j                  d      g|j                  _        t	        |      }|j                  dd      }|D cg c]  }|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  }d
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  }d
d|iz  }	t        t        j                  |	            dx}}yc c}w )z>from_version=5 must INCLUDE version 5 (inclusive lower bound).   r   rY   r   r   r   r   r   r   r   r^   Nr   r   r   r   s
             r   test_boundary_inclusivez0TestWB3ReplayFromVersion.test_boundary_inclusive-  s   *.))A,		!)E&$8++Dq+A'-.!AII.. q    q   q                qH}qHqHH /s   F?Nr   )	r   r   r   r   r   r  r  r  r  r   r   r   r   r      s    H
 r   r   c                  6    e Zd ZdZd	dZd Zd Zd Zd Zd Z	y)
TestWB4GetCurrentStateFoldingzWB4: get_current_state folds payloads sequentially in version order,
    with later versions overriding earlier ones on key collision.c                    t        j                  t        j                        }t	        t        j                               ||j                         d|}t	        t        j                               dd||dS )Nr1   r   r   state_updater   r   )rP   r7   r   r   r6   s        r   r   z"TestWB4GetCurrentStateFolding._row@  se    \\X\\*DJJL),,.
 	
 djjl#(
 	
r   c                   t               }g |j                  _        t        |      }|j	                  d      }i }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}}y )	NrY   r   r[   r   stater   r   r^   )r   r   r   r	   get_current_staterp   rq   rs   rt   ru   rr   rw   rx   )rP   r+   rS   r  rz   ry   r   r   s           r   %test_empty_session_returns_empty_dictzCTestWB4GetCurrentStateFolding.test_empty_session_returns_empty_dictP  s    )+&$8((.u{uuur   c                   t               }| j                  dddi      g|j                  _        t	        |      }|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}}y )Nr-   keyvalue1rY   r   r[   r   r   r   r   r   r   r   r   r	   r  rp   rq   rr   rw   rx   	rP   r+   rS   r  r   r   rz   r   r   s	            r   %test_single_event_returns_its_payloadzCTestWB4GetCurrentStateFolding.test_single_event_returns_its_payloadW  s    *.))Ax7H*I)J&$8((.U|'x'|x''''|x'''|'''x'''''''r   c                   t               }| j                  dddi      | j                  dddi      g|j                  _        t	        |      }|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}}y)z*Version 2 'key' overrides version 1 'key'.r   r  value2r-   r  rY   r   r[   r   r   r   r   Nr  r  s	            r   1test_later_version_overrides_earlier_on_collisionzOTestWB4GetCurrentStateFolding.test_later_version_overrides_earlier_on_collision^  s    IIa%*+IIa%*+*
& %8((.U|'x'|x''''|x'''|'''x'''''''r   c                   t               }| j                  dddi      | j                  dddi      g|j                  _        t	        |      }|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)z?Keys from different events are merged into a single state dict.r-   r/   r0   r   taskbuildrY   r   r[   r   r   r   r   Nr  r  s	            r    test_non_conflicting_keys_mergedz>TestWB4GetCurrentStateFolding.test_non_conflicting_keys_mergedi  s    IIa'7+,IIa&'*+*
& %8((.W~((~((((~(((~((((((((((V}''}''''}'''}''''''''''r   c                   t               }| j                  dddi      g|j                  _        t	        |      }|j                  d      }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}}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}}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}}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)zFInternal keys event_id, version, created_at are NOT in the state dict.r-   user_keyuser_valrY   r   r   r   r   r  r   r   r^   Nr7   r8   r   r   )r   r   r   r   r	   r  rp   rq   rr   rs   rt   ru   rw   rx   )rP   r+   rS   r  r   rz   r   r   s           r   test_bookkeeping_keys_strippedz<TestWB4GetCurrentStateFolding.test_bookkeeping_keys_strippedu  s   *.))A
J7O*P)Q&$8((.&z&&&&z&&&z&&&&&&&&&&&&&&&&%y%%%%y%%%y%%%%%%%%%%%%%%%%(|5((((|5(((|((((((5(((5((((((("zU""""zU"""z""""""U"""U"""""""r   N)r7   r   r   r   r   r   )
r   r   r   r   r   r  r  r  r#  r'  r   r   r   r  r  <  s&    E
 (	(
(	#r   r  c                  R    e Zd ZdZ ej
                  d      Zd Zd Zd Z	d Z
d Zy)	TestWB5ParameterisedSQLuH   WB5: cold_ledger.py uses %s placeholders throughout — never f-strings.1/mnt/e/genesis-system/core/storage/cold_ledger.pyc                   | j                   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	)
zBNo SQL query in cold_ledger.py must be constructed with f-strings.%sr   r   sourcer   z7cold_ledger.py must use %s placeholder parameterisation
>assert %(py5)sr^   NSOURCE_PATH	read_textrp   rq   rr   rs   rt   ru   _format_assertmsgrw   rx   rP   r-  r   rz   r   r   s         r   "test_no_fstring_sql_in_cold_ledgerz:TestWB5ParameterisedSQL.test_no_fstring_sql_in_cold_ledger  s|    !!++- Xtv~XXXtvXXXtXXXXXXvXXXvXXXXXXXXXXXr   c                   | j                   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  }d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  }dd|iz  }t        t        j                  |            dx}}y)
z8write_event SQL must use %s (not %() or format strings).zINSERT INTO eventsr   r   r-  r   r   r^   N
VALUES (%s
r0  r1  rp   rq   rr   rs   rt   ru   rw   rx   r3  s         r   +test_write_event_uses_percent_s_placeholderzCTestWB5ParameterisedSQL.test_write_event_uses_percent_s_placeholder  s    !!++-#-#v----#v---#------v---v-------%|v%%%%|v%%%|%%%%%%v%%%v%%%%%%%r   c                   | j                   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  }dd|iz  }t        t        j                  |            dx}}y)	z@get_events SQL must use %s for session_id and event_type params.zWHERE session_id = %sr   r   r-  r   r   r^   Nr7  r3  s         r   *test_get_events_uses_percent_s_placeholderzBTestWB5ParameterisedSQL.test_get_events_uses_percent_s_placeholder  sr    !!++-&0&&0000&&000&000000&000&0000000r   c                   | j                   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  }d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  }dd|iz  }t        t        j                  |            dx}}y)
z4write_saga SQL must use %s for all inserted columns.zINSERT INTO swarm_sagasr   r   r-  r   r   r^   Nr6  r7  r3  s         r   *test_write_saga_uses_percent_s_placeholderzBTestWB5ParameterisedSQL.test_write_saga_uses_percent_s_placeholder  s    !!++-(2(F2222(F222(222222F222F2222222%|v%%%%|v%%%|%%%%%%v%%%v%%%%%%%r   c                   | j                   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  }dd|iz  }t        t        j                  |            dx}}y)	z.get_saga WHERE clause must use %s for saga_id.zWHERE saga_id = %sr   r   r-  r   r   r^   Nr7  r3  s         r   (test_get_saga_uses_percent_s_placeholderz@TestWB5ParameterisedSQL.test_get_saga_uses_percent_s_placeholder  sr    !!++-#-#v----#v---#------v---v-------r   N)r   r   r   r   pathlibPathr0  r4  r8  r:  r<  r>  r   r   r   r)  r)    s2    R',,;KY&1
&.r   r)  c                  R    e Zd ZdZ ej
                  d      Zd Zd Zd Z	d Z
d Zy)	TestWB6ConnectionPoolPatternu   WB6: ColdLedger acquires connections with getconn and releases with putconn
    in a try/finally block — connection leaks are structurally impossible.r*  c                $   | j                   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 )
Ntry:r   r   r-  r   z8cold_ledger.py must use try/finally for pool connectionsr.  r^   finally:r/  r3  s         r   test_source_uses_try_finallyz9TestWB6ConnectionPoolPattern.test_source_uses_try_finally  s    !!++-[v[[[v[[[v[[[[[[[[[[[[[![[[[[[[_zV#___zV___z______V___V____%_______r   c                   | j                   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   r   r   r-  r   z9ColdLedger must use pool.getconn() to acquire connectionsr.  r^   r/  r3  s         r   test_source_calls_getconnz6TestWB6ConnectionPoolPattern.test_source_calls_getconn  {    !!++-_yF"___yF___y______F___F____$_______r   c                   | j                   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   r   r   r-  r   z9ColdLedger must use pool.putconn() to release connectionsr.  r^   r/  r3  s         r   test_source_calls_putconnz6TestWB6ConnectionPoolPattern.test_source_calls_putconn  rI  r   c                    t               }t        d      |j                  j                  _        t        |      \  }}}	 |j                  ddi        |j                  j                  |       y# t        $ r Y 'w xY w)uF   putconn must be called in finally — even when cursor.execute raises.DB errorr   r   N)	r   	Exceptionr   executer   r,   rM   r   r   )rP   r   r+   r    _s        r   ,test_putconn_called_even_when_execute_raiseszITestWB6ConnectionPoolPattern.test_putconn_called_even_when_execute_raises  sp     +4Z+@(+D1a	tUB/
 	,,T2	  		s   A. .	A:9A:c                   ddl m} t               }t        d      |j                  j
                  _        t        |      \  }}} |t        t        j                               t        t        j                               i g ddt        j                  t        j                              }	 |j                  |       |j                   j#                  |       y# t        $ r Y 'w xY w)z6write_saga must release connection even when it fails.r   )	SwarmSagarM  NRUNNINGr1   )saga_idr4   orchestrator_dagproposed_deltasresolved_statestatusr8   )core.storage.cold_ledgerrS  r   rN  r   rO  r   r,   r9   r:   r;   r   r<   r   r=   
write_sagar   r   )rP   rS  r   r+   r    rP  sagas          r   )test_putconn_called_in_write_saga_finallyzFTestWB6ConnectionPoolPattern.test_putconn_called_in_write_saga_finally  s    6 +4Z+@(+D1a

%4::<(||x||4
	d# 	,,T2  		s   ,C 	C%$C%N)r   r   r   r   r?  r@  r0  rF  rH  rK  rQ  r]  r   r   r   rB  rB    s9    P ',,;K`
``33r   rB  c                  f    e Zd ZdZ ej
                  d      ZddZd Zd Z	d Z
d Zd Zd	 Zd
 Zy)TestWB7NoSqliteuP   WB7: sqlite3 must not be imported in any core.storage module — Genesis Rule 7.z"/mnt/e/genesis-system/core/storagec                <    | j                   |z  j                         S r   )STORAGE_DIRr1  )rP   filenames     r   _sourcezTestWB7NoSqlite._source  s      8+6688r   c                   d}| j                   }d} ||      }||v}|st        j                  d|fd||f      t        j                  |      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}x}}y )	Nimport sqlite3zcold_ledger.pyr   zQ%(py1)s not in %(py9)s
{%(py9)s = %(py5)s
{%(py5)s = %(py3)s._source
}(%(py7)s)
}rP   r   r]   r^   r_   r`   assert %(py11)spy11	rc  rp   rq   rr   rs   rt   ru   rw   rx   rP   r   r{   r|   r}   rz   r   @py_format12s           r   test_no_sqlite3_in_cold_ledgerz.TestWB7NoSqlite.test_no_sqlite3_in_cold_ledger      Et||E4DE|4D'EE'EEEEE'EEEEEEEEEEtEEEtEEE|EEE4DEEE'EEEEEEEEEr   c                   d}| j                   }d} ||      }||v}|st        j                  d|fd||f      t        j                  |      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}x}}y )	Nre  zevent_sourcing.pyr   rf  rP   rg  rh  ri  rj  rk  s           r   !test_no_sqlite3_in_event_sourcingz1TestWB7NoSqlite.test_no_sqlite3_in_event_sourcing  s    Ht||H4GH|4G'HH'HHHHH'HHHHHHHHHHtHHHtHHH|HHH4GHHH'HHHHHHHHHr   c                   d}| j                   }d} ||      }||v}|st        j                  d|fd||f      t        j                  |      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}x}}y )	Nre  zsaga_writer.pyr   rf  rP   rg  rh  ri  rj  rk  s           r   test_no_sqlite3_in_saga_writerz.TestWB7NoSqlite.test_no_sqlite3_in_saga_writer  rn  r   c                   d}| j                   }d} ||      }||v}|st        j                  d|fd||f      t        j                  |      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}x}}y )	Nre  zsession_store.pyr   rf  rP   rg  rh  ri  rj  rk  s           r    test_no_sqlite3_in_session_storez0TestWB7NoSqlite.test_no_sqlite3_in_session_store      Gt||G4FG|4F'GG'GGGGG'GGGGGGGGGGtGGGtGGG|GGG4FGGG'GGGGGGGGGr   c                   d}| j                   }d} ||      }||v}|st        j                  d|fd||f      t        j                  |      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}x}}y )	Nre  zcold_ledger_interceptor.pyr   rf  rP   rg  rh  ri  rj  rk  s           r   *test_no_sqlite3_in_cold_ledger_interceptorz:TestWB7NoSqlite.test_no_sqlite3_in_cold_ledger_interceptor  s    Qt||Q4PQ|4P'QQ'QQQQQ'QQQQQQQQQQtQQQtQQQ|QQQ4PQQQ'QQQQQQQQQr   c                   d}| j                   }d} ||      }||v}|st        j                  d|fd||f      t        j                  |      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}x}}y )	Nre  zshadow_router.pyr   rf  rP   rg  rh  ri  rj  rk  s           r    test_no_sqlite3_in_shadow_routerz0TestWB7NoSqlite.test_no_sqlite3_in_shadow_router  ru  r   c                   d}| j                   }d} ||      }||v}|st        j                  d|fd||f      t        j                  |      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}x}}y )	Nre  zpostgres_schema.pyr   rf  rP   rg  rh  ri  rj  rk  s           r   "test_no_sqlite3_in_postgres_schemaz2TestWB7NoSqlite.test_no_sqlite3_in_postgres_schema
  s    It||I4HI|4H'II'IIIII'IIIIIIIIIItIIItIII|III4HIII'IIIIIIIIIr   N)rb  r9   r   r9   )r   r   r   r   r?  r@  ra  rc  rm  rp  rr  rt  rw  ry  r{  r   r   r   r_  r_    sE    Z',,CDK9FIFHRHJr   r_  c                  X    e Zd ZdZ ej
                  d      Zd Zd Zd Z	d Z
d Zd Zy	)
#TestWB8SessionStoreParameterisedSQLuF   WB8: session_store.py uses %s placeholders — never f-strings in SQL.z3/mnt/e/genesis-system/core/storage/session_store.pyc                   | j                   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,  r   r   r-  r   z-session_store.py must use %s parameterisationr.  r^   r/  r3  s         r   test_source_contains_percent_szBTestWB8SessionStoreParameterisedSQL.test_source_contains_percent_s  sz    !!++-Ntv~NNNtvNNNtNNNNNNvNNNvNNNNNNNNNNNr   c                   | j                   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  }d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  }dd|iz  }t        t        j                  |            d x}}y )	NzINSERT INTO sessionsr   r   r-  r   r   r^   r6  r7  r3  s         r    test_open_session_uses_percent_szDTestWB8SessionStoreParameterisedSQL.test_open_session_uses_percent_s  s    !!++-%/%////%///%////////////////%|v%%%%|v%%%|%%%%%%v%%%v%%%%%%%r   c                   | j                   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  }d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  }dd|iz  }t        t        j                  |            d x}}y )	Nz$UPDATE sessions SET ended_at = NOW()r   r   r-  r   r   r^   WHERE id = %sr7  r3  s         r   !test_close_session_uses_percent_szETestWB8SessionStoreParameterisedSQL.test_close_session_uses_percent_s"  s    !!++-5?5????5???5????????????????(&((((&(((((((((&(((&(((((((r   c                   | j                   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  }dd|iz  }t        t        j                  |            d x}}y )Nr  r   r   r-  r   r   r^   r7  r3  s         r   test_get_session_uses_percent_szCTestWB8SessionStoreParameterisedSQL.test_get_session_uses_percent_s'  so    !!++-(&((((&(((((((((&(((&(((((((r   c                   | j                   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  }d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  }dd|iz  }t        t        j                  |            d x}}y )	NrD  r   r   r-  r   r   r^   rE  r7  r3  s         r   #test_session_store_uses_try_finallyzGTestWB8SessionStoreParameterisedSQL.test_session_store_uses_try_finally+  s    !!++-vvv#zV####zV###z######V###V#######r   c                   | j                   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  }d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  }dd|iz  }t        t        j                  |            d x}}y )	Nr   r   r   r-  r   r   r^   r   r7  r3  s         r   ,test_session_store_calls_getconn_and_putconnzPTestWB8SessionStoreParameterisedSQL.test_session_store_calls_getconn_and_putconn0  s    !!++-"yF""""yF"""y""""""F"""F""""""""yF""""yF"""y""""""F"""F"""""""r   N)r   r   r   r   r?  r@  r0  r  r  r  r  r  r  r   r   r   r}  r}    s7    P',,=KO&
)
)$
#r   r}  c                  .    e Zd ZdZd Zd Zd Zd Zd Zy)$TestWB9ShadowRouterDeterministicHashzIWB9: ShadowRouter generates a deterministic SHA256 hash from the payload.c                   t               }d|_        dddd}t        j                  |d      5  |j	                  d|      }|j	                  d|      }d d d        j
                  d   }j
                  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)NSHADOWzuser@example.comHello*   )tosubjectamount_write_shadow_logemailpayload_hashr[   r   r   r   r   r   default_moder   objectroute_side_effect	log_entryrp   rq   rr   rw   rx   )
rP   routerr6   result1result2r   r   rz   r   r   s
             r   $test_same_payload_produces_same_hashzITestWB9ShadowRouterDeterministicHash.test_same_payload_produces_same_hash=  s    &+2N\\&"56 	A..w@G..w@G	A   0UG4E4En4UU04UUUUU04UUUU0UUU4UUUUUUUU		A 	As   %C33C<c                   t               }d|_        t        j                  |d      5  |j	                  dddi      }|j	                  dddi      }d d d        j
                  d   }j
                  d   }||k7  }|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  r  r  r  za@b.comzc@d.comr  )!=)z%(py1)s != %(py4)sr   r   r   r  )	rP   r  r1r2r   r   rz   r   r   s	            r   0test_different_payloads_produce_different_hasheszUTestWB9ShadowRouterDeterministicHash.test_different_payloads_produce_different_hashesH  s    &\\&"56 	F))'D)3DEB))'D)3DEB	F ||N+Kr||N/KK+/KKKKK+/KKKK+KKK/KKKKKKKK		F 	Fs   )C11C:c                $   t               }d|_        t        j                  |d      5  |j	                  dddi      }ddd       j
                  d   }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}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# 1 sw Y   JxY w)z6The hash must be a 64-char hex string (SHA256 output).r  r  smsbodyr$   Nr  r   r   hr9   r   @   r[   r  rd   r  r  r  )r   r  r   r  r  r  r   r9   rs   rt   rp   ru   rr   rw   rx   rd   rq   )rP   r  r   r  r   r   rz   r   r{   r   r  s              r   test_hash_algorithm_is_sha256zBTestWB9ShadowRouterDeterministicHash.test_hash_algorithm_is_sha256R  su   &\\&"56 	G--eff5EFF	G ^,!S!!!!!!!!z!!!z!!!!!!!!!!!!!!!!!S!!!S!!!!!!!!!!1vv|vss11v	G 	Gs   JJc                   ddd}t        j                  t        j                  |d      j	                  d            j                         }t               }d|_        t        j                  |d      5  |j                  d	|      }d
d
d
       j                  d   }||k(  }|st        j                  d|fd||f      t        j                  |      dt        j                          v st        j"                  |      rt        j                  |      nddz  }dd|iz  }t%        t        j&                  |            d
x}}y
# 1 sw Y   xY w)zIManual SHA256 of json.dumps(payload, sort_keys=True) must equal log hash.zhttps://api.example.comPOST)urlmethodT	sort_keysutf-8r  r  external_apiNr  r[   z%(py1)s == %(py3)sexpected_hashr   r   r^   hashlibsha256jsondumpsencode	hexdigestr   r  r   r  r  r  rp   rq   rr   rs   rt   ru   rw   rx   )	rP   r6   r  r  r   r   rz   r   r   s	            r   $test_hash_matches_manual_computationzITestWB9ShadowRouterDeterministicHash.test_hash_matches_manual_computation^  s    3vFJJw$/66w?

)+ 	 &\\&"56 	G--ngFF	G /@/=@@@@/=@@@/@@@@@@=@@@=@@@@@@@	G 	Gs   4EEc                4   ddd}ddd}t        j                  t        j                  |d      j	                  d            j                         }t               }d|_        t        j                  |d	      5  |j                  d
|      }|j                  d
|      }ddd       j                  d   }||k(  }|st        j                  d|fd||f      t        j                  |      dt        j                          v st        j"                  |      rt        j                  |      nddz  }	dd|	iz  }
t%        t        j&                  |
            dx}}j                  d   }||k(  }|st        j                  d|fd||f      t        j                  |      dt        j                          v st        j"                  |      rt        j                  |      nddz  }	dd|	iz  }
t%        t        j&                  |
            dx}}y# 1 sw Y   qxY w)z;Order of keys in the payload dict must NOT affect the hash.r   r-   )ba)r  r  Tr  r  r  r  r  Nr  r[   r  expectedr   r   r^   r  )rP   payload_orderedpayload_reversedr  r  r  r  r   rz   r   r   s              r   (test_hash_uses_sorted_keys_for_stabilityzMTestWB9ShadowRouterDeterministicHash.test_hash_uses_sorted_keys_for_stabilitym  sr    !*!"+>>JJ$7>>wG

)+ 	 &\\&"56 	C))%AB))%1ABB	C ||N+7+x7777+x777+777777x777x7777777||N+7+x7777+x777+777777x777x7777777	C 	Cs   9%HHN)	r   r   r   r   r  r  r  r  r  r   r   r   r  r  :  s!    S	VL
A8r   r  c                  f    e Zd ZdZ ej
                  d      ZddZd Zd Z	d Z
d Zd Zd	 Zd
 Zy)TestWB10FireAndForgetNonFatalu   WB10: _fire_and_forget catches all exceptions and logs a WARNING —
    interceptor chain is never interrupted by a storage failure.z=/mnt/e/genesis-system/core/storage/cold_ledger_interceptor.pyc                P    t               }||j                  _        t        |      S )N)r+   )r   rM   r   r   )rP   excrQ   s      r   %_make_interceptor_with_failing_ledgerzCTestWB10FireAndForgetNonFatal._make_interceptor_with_failing_ledger  s"    k.1+$K88r   c                   | j                   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  }dd|iz  }t        t        j                  |            d x}}y )N_fire_and_forgetr   r   r-  r   r   r^   r7  r3  s         r   %test_fire_and_forget_exists_in_sourcezCTestWB10FireAndForgetNonFatal.test_fire_and_forget_exists_in_source  sr    !!++-!+!V++++!V+++!++++++V+++V+++++++r   c                   | j                   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  }d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  }dd|iz  }t        t        j                  |            d x}}y )	NrD  r   r   r-  r   r   r^   exceptr7  r3  s         r   $test_fire_and_forget_uses_try_exceptzBTestWB10FireAndForgetNonFatal.test_fire_and_forget_uses_try_except  s    !!++-vvv!x6!!!!x6!!!x!!!!!!6!!!6!!!!!!!r   c                   | j                  t        d            }t        |j                  ddi            }ddi}||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd	|iz  }t        t	        j                  |            d x}}y )
Ncrashr4   r   r[   r   r   r   r   r^   )r  RuntimeErrorrD   pre_executerp   rq   rs   rt   ru   rr   rw   rx   rP   interceptorr   rz   ry   r   r   s          r   9test_runtime_error_does_not_propagate_through_pre_executezWTestWB10FireAndForgetNonFatal.test_runtime_error_does_not_propagate_through_pre_execute  s    @@gAVWk--|T.BCD&--v-----v-------v---v-----------r   c                v    | j                  t        d            }t        |j                  ddiddi             y )Nzpool exhaustedsuccessTr4   r   )r  r   rD   post_execute)rP   r  s     r   =test_connection_error_does_not_propagate_through_post_executez[TestWB10FireAndForgetNonFatal.test_connection_error_does_not_propagate_through_post_execute  s<    @@,-
 	[%%y$&7,9MNOr   c                   | j                  t        d            }t        |j                  t	        d      ddi            }ddi}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }d	d
|iz  }t        t        j                  |            d x}}y )Nzledger timeoutzoriginal errorr4   r   r[   r   r   r   r   r^   )r  TimeoutErrorrD   on_error
ValueErrorrp   rq   rs   rt   ru   rr   rw   rx   r  s          r   6test_timeout_error_does_not_propagate_through_on_errorzTTestWB10FireAndForgetNonFatal.test_timeout_error_does_not_propagate_through_on_error  s    @@)*
 k**:6F+G,X\I]^_&--v-----v-------v---v-----------r   c                   | j                  t        d            }ddd}t        |j                  |            }||u }|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 )Nzany exceptionr   r   )r4   attempt)is)z%(py0)s is %(py2)sr   
correction)r   r   zassert %(py4)sr   )r  rN  rD   on_correctionrp   rq   rs   rt   ru   rr   rw   rx   )rP   r  r  r   ry   @py_format3r   s          r   ;test_any_exception_does_not_propagate_through_on_correctionzYTestWB10FireAndForgetNonFatal.test_any_exception_does_not_propagate_through_on_correction  s    @@?A[\$(Q7
k//
;<####v######v###v################r   c                N   dd l }| j                  t        d            }|j                  |j                  d      5  t        |j                  ddi             d d d        d |j                  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 # 1 sw Y   xY w)Nr   boomz$core.storage.cold_ledger_interceptor)loggerr4   r   c              3  8   K   | ]  }d |j                   v   yw)zwrite_event failedN)message)r   rs     r   r   z]TestWB10FireAndForgetNonFatal.test_fire_and_forget_logs_warning_on_failure.<locals>.<genexpr>  s     M'1994Ms   r   anyr   )loggingr  r  at_levelWARNINGrD   r  recordsr  rs   rt   rp   ru   rr   rw   rx   )rP   caplogr  r  ry   r   r   s          r   ,test_fire_and_forget_logs_warning_on_failurezJTestWB10FireAndForgetNonFatal.test_fire_and_forget_logs_warning_on_failure  s    @@fAUV__W__5[_\ 	@((,)=>?	@MfnnMMsMMMMMMMMMsMMMsMMMMMMMMMMMMMM	@ 	@s   DD$N)r  rN  r   r   )r   r   r   r   r?  r@  r0  r  r  r  r  r  r  r  r  r   r   r   r  r    sG    D ',,GK9
,"
.
P.$Nr   r  c                  (    e Zd ZdZd Zd Zd Zd Zy)TestWBEventEnrichedPayloadzHVerify the enriched_payload structure written to ColdLedger by append().c                   t               }t        |      }t        d      }|j                  |       |j                  j
                  d   d   }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 )NrY   r   rK   r-   r6   r   r   r   written_payloadr   r   r^   r   r	   r>   rL   rM   rO   rp   rq   rr   rs   rt   ru   rw   rx   	rP   rQ   rS   rT   r  r   rz   r   r   s	            r   'test_enriched_payload_includes_event_idzBTestWBEventEnrichedPayload.test_enriched_payload_includes_event_id  s    k$=A&e%11;;A>yI,z_,,,,z_,,,z,,,,,,_,,,_,,,,,,,r   c                   t               }t        |      }t        d      }|j                  |       |j                  j
                  d   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 )NrY   r   rK   r-   r6   r7   r[   r   r   r   r   r   r	   r>   rL   rM   rO   rp   rq   rr   rw   rx   
rP   rQ   rS   rT   r  r   r   rz   r   r   s
             r   &test_enriched_payload_includes_versionzATestWBEventEnrichedPayload.test_enriched_payload_includes_version  s    k$=A&e%11;;A>yIy).Q.)Q....)Q...)...Q.......r   c                   t               }t        |      }t        d      }|j                  |       |j                  j
                  d   d   }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 )NrY   r-   rK   r6   r8   r   r   r  r   r   r^   r  r  s	            r   )test_enriched_payload_includes_created_atzDTestWBEventEnrichedPayload.test_enriched_payload_includes_created_at  s    k$=A&e%11;;A>yI.|....|...|................r   c                   t               }t        |      }t        dddd      }|j                  |       |j                  j
                  d   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 )NrY   r-   r0   gold)r/   tier)r7   r6   r6   r/   r[   r   r   r   r   r  r  r  s
             r   +test_enriched_payload_merges_caller_payloadzFTestWBEventEnrichedPayload.test_enriched_payload_merges_caller_payload  s    k$=A&/QRe%11;;A>yIw'272'72222'7222'22272222222v&0&0&&0000&&000&000&0000000r   N)r   r   r   r   r  r  r  r  r   r   r   r  r    s    R-//1r   r  __main__z%WB1a: append calls ledger.write_eventz(WB1b: append passes session_id to ledgerz1WB1c: append calls Redis XADD when redis providedz"WB1d: XADD uses genesis:events keyz!WB1e: XADD includes version fieldz)WB1f: append without redis does not raisez"WB1g: Redis failure does not raisezWB2a: replay returns listz)WB2b: replay returns GenesisEvent objectsz(WB2c: replay sorted by version ascendingz-WB2d: replay calls get_events with session_idz,WB3a: replay_from_version skips versions < Nz WB3b: from_version=1 returns allz+WB3c: from_version beyond all returns emptyz%WB3d: from_version boundary inclusivez&WB4a: empty session returns empty dictz&WB4b: single event returns its payloadz2WB4c: later version overrides earlier on collisionz!WB4d: non-conflicting keys mergedzWB4e: bookkeeping keys strippedz$WB5a: no f-string SQL in cold_ledgerzWB5b: write_event uses %szWB5c: get_events uses %szWB5d: write_saga uses %szWB5e: get_saga uses %szWB6a: source uses try/finallyzWB6b: source calls getconnzWB6c: source calls putconnz-WB6d: putconn called even when execute raisesz*WB6e: putconn called in write_saga finallyzWB7a: no sqlite3 in cold_ledgerz"WB7b: no sqlite3 in event_sourcingzWB7c: no sqlite3 in saga_writerz!WB7d: no sqlite3 in session_storez+WB7e: no sqlite3 in cold_ledger_interceptorz!WB7f: no sqlite3 in shadow_routerz#WB7g: no sqlite3 in postgres_schemazWB8a: session_store contains %szWB8b: open_session uses %szWB8c: close_session uses %szWB8d: get_session uses %sz$WB8e: session_store uses try/finallyz(WB8f: session_store uses getconn/putconnu    WB9a: same payload → same hashu-   WB9b: different payloads → different hasheszWB9c: hash is 64-char SHA256z,WB9d: hash matches manual SHA256 computationz'WB9e: hash uses sort_keys for stabilityz(WB10a: _fire_and_forget exists in sourcez'WB10b: _fire_and_forget uses try/exceptz9WB10c: RuntimeError doesn't propagate through pre_executez=WB10d: ConnectionError doesn't propagate through post_executez6WB10e: TimeoutError doesn't propagate through on_errorz<WB10f: any exception doesn't propagate through on_correctionz+WB-EP-a: enriched payload includes event_idz*WB-EP-b: enriched payload includes versionz-WB-EP-c: enriched payload includes created_atz/WB-EP-d: enriched payload merges caller payloadz	  [PASS] z	  [FAIL] z: 
/z tests passedzLALL TESTS PASSED -- Story 5.09 (Track B): Module 5 WB Integration Test Suite)r   r   r   )r   z'tuple[ColdLedger, MagicMock, MagicMock])r-   zsession-001dispatch_startN)
r7   r   r4   r9   r5   r9   r6   r   r   r
   )zr   
__future__r   builtinsrs   _pytest.assertion.rewrite	assertionrewriterp   r@   r  r  r?  sysr:   pathinsertpytestr   r   unittest.mockr   r   r   core.storage.event_sourcingr	   r
   rZ  r   core.storage.session_storer   core.storage.shadow_routerr   $core.storage.cold_ledger_interceptorr   r   r!   r,   r>   rD   rF   r   r   r  r)  rB  r_  r}  r  r  r  r   	tracebacktbrW   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r  r  r#  r'  r4  r8  r:  r<  r>  rF  rH  rK  rQ  r]  rm  rp  rr  rt  rw  ry  r{  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  testspassedfailednamefnprintrN  r  	print_excexitr   r   r   <module>r      s  ( #       
  * +  ' 0 0 J / 3 3 F

 #&	  	
 "=I5 I5`2@ 2@r9 9@B# B#R$. $.V93 93@J JD!# !#PD8 D8V4N 4Nv"1 "1R z@	0		!	F	F	H@ 
4		!	I	I	K	@ 
=		!	R	R	T@ 
.		!	J	J	L@ 
-		!	I	I	K@ 
5		!	J	J	L@ 
.		!	J	J	L@" 
%	"	$	=	=	?#@& 
5	"	$	N	N	P'@* 
4	"	$	L	L	N+@. 
9	"	$	X	X	Z/@4 
8	!	#	M	M	O5@8 
,	!	#	C	C	E9@< 
7	!	#	N	N	P=@@ 
1	!	#	;	;	=A@F 
2	&	(	N	N	PG@J 
2	&	(	N	N	PK@N 
>	&	(	Z	Z	\O@R 
-	&	(	I	I	KS@V 
+	&	(	G	G	IW@\ 
0	 	"	E	E	G]@` 
%	 	"	N	N	Pa@d 
$	 	"	M	M	Oe@h 
$	 	"	M	M	Oi@l 
"	 	"	K	K	Mm@r 
)	%	'	D	D	Fs@v 
&	%	'	A	A	Cw@z 
&	%	'	A	A	C{@~ 
9	%	'	T	T	V@B 
6	%	'	Q	Q	SC@H 
+			9	9	;I@L 
.			<	<	>M@P 
+			9	9	;Q@T 
-			;	;	=U@X 
7			E	E	GY@\ 
-			;	;	=]@` 
/			=	=	?a@f 
+	,	.	M	M	Og@j 
&	,	.	O	O	Qk@n 
'	,	.	P	P	Ro@r 
%	,	.	N	N	Ps@v 
0	,	.	R	R	Tw@z 
4	,	.	[	[	]{@@ 
,	-	/	T	T	VA@D 
9	-	/	`	`	bE@H 
(	-	/	M	M	OI@L 
8	-	/	T	T	VM@P 
3	-	/	X	X	ZQ@V 
4	&	(	N	N	PW@Z 
3	&	(	M	M	O[@^ 
E	&	(	b	b	d_@b 
I	&	(	f	f	hc@f 
B	&	(	_	_	ag@j 
H	&	(	d	d	fk@p 
7	#	%	M	M	Oq@t 
6	#	%	L	L	Nu@x 
9	#	%	O	O	Qy@| 
;	#	%	Q	Q	S}@ED FF b	DIdV$%aKF	 
Bvha(
67{\]k X  	IdV2cU+,BLLNaKF	s   8WW8
$W33W8