
    }it+                        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mZmZmZ ddlmZmZmZmZmZ d3dZd Zd	 Zd
 Zd Zd Zd Zd Zd Zd Z d Z!d Z"d Z#d Z$d Z%d Z&d Z'd Z(d Z)e*dk(  rddl+Z+da,da-d4dZ. e.de        e.de        e.de        e.de        e.d e        e.d!e        e.d"e        e.d#e        e.d$e         e.d%e!        e.d&e"        e.d'e#        e.d(e$        e.d)e%        e.d*e&        e.d+e'        e.d,e(        e.d-e)        e/d.tZ         d/tX         d0       tZ        tX        k(  r	 e/d1       y e	j`                  d2       yy)5u  
Tests for Story 5.01 (Track B): Postgres Schema — sessions, events, swarm_sagas

Black Box tests (BB): verify the public contract from the outside —
    correct DDL structure, idempotency, and correct drop order.
White Box tests (WB): verify internal DDL content — field names, FK constraints,
    CHECK constraints, indexes, and CASCADE behaviour.

Story: 5.01
File under test: core/storage/postgres_schema.py
    )annotationsNz/mnt/e/genesis-system)	MagicMockcallpatch)create_all_tablesdrop_all_tablesSESSIONS_DDL
EVENTS_DDLSWARM_SAGAS_DDLc                     t               } t               }t        |      | j                  j                  _        t        d      | j                  j                  _        || _        | S )zHReturn a mock psycopg2 connection with a working context-manager cursor.)return_valueF)r   cursorr   	__enter____exit___cursor)connr   s     6/mnt/e/genesis-system/tests/track_b/test_story_5_01.py_make_mock_connr   (   sJ    ;D[F)2)GDKK&(1u(EDKK%DLK    c                 .    t               } t        |        y)z<BB1: create_all_tables() runs without raising any exception.Nr   r   r   s    r   -test_bb1_create_all_tables_runs_without_errorr   8   s    Ddr   c                 D    t               } t        |        t        |        y)zDBB2: Calling create_all_tables() twice must not raise any exception.Nr   r   s    r   (test_bb2_create_all_tables_is_idempotentr   >   s    Dddr   c                    t               } | j                  }t        |        |j                  j                  D cg c]  }|d   d   j                          }}t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                  dt        |             d	z   d
|iz  }t        t        j                  |            dx}x}}d}	|d   }
|	|
v }|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   }
|	|
v }|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   }
|	|
v }|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c c}w )zXBB3: drop_all_tables() drops sagas first, then events, then sessions (reverse FK order).r      ==z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenexecute_callspy0py1py3py6z Expected 3 DROP statements, got z
>assert %(py8)spy8Nswarm_sagasin)z%(py1)s in %(py4)s)r%   py4z)First drop must target swarm_sagas, got: z
>assert %(py6)sr'   events   z%Second drop must target events, got: sessions   z&Third drop must target sessions, got: )r   r   r   executecall_args_liststripr!   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgAssertionError_format_explanation)r   curcr"   @py_assert2@py_assert5@py_assert4@py_format7@py_format9@py_assert0@py_assert3@py_format5s               r   2test_bb3_drop_all_tables_drops_in_reverse_fk_orderrG   E   s>   D
,,CD.1kk.H.HIQqT!W]]_IMI}[["[[[[[[[[[3[[[3[[[[[[}[[[}[[[[[[[[[&Fs=GYFZ$[[[[[[[[ lM!,l=,,lll=,lll=lll,lll0YZghiZjYk.llllllllc}Q'c8''ccc8'ccc8ccc'ccc+PQ^_`QaPb)ccccccccfq)f:))fff:)fff:fff)fff-STabcTdSe+ffffffff Js   Mc                    d} | t         v }|st        j                  d|fd| t         f      t        j                  |       dt	        j
                         v st        j                  t               rt        j                  t               nddz  }t        j                  d      dz   d|iz  }t        t        j                  |            d	x} }y	)
z=BB4: SESSIONS_DDL uses 'IF NOT EXISTS' to ensure idempotency.IF NOT EXISTSr*   z%(py1)s in %(py3)sr	   r%   r&   z)SESSIONS_DDL must contain 'IF NOT EXISTS'
>assert %(py5)spy5N)
r	   r4   r5   r9   r6   r7   r8   r:   r;   r<   rD   r?   @py_format4@py_format6s       r   ,test_bb4_sessions_ddl_contains_if_not_existsrQ   U   sg    W?l*WWW?lWWW?WWWWWWlWWWlWWWW,WWWWWWWr   c                    d} | t         v }|st        j                  d|fd| t         f      t        j                  |       dt	        j
                         v st        j                  t               rt        j                  t               nddz  }t        j                  d      dz   d|iz  }t        t        j                  |            d	x} }y	)
z?BB5: EVENTS_DDL uses 'IF NOT EXISTS' on every CREATE statement.rI   r*   rJ   r
   rK   z'EVENTS_DDL must contain 'IF NOT EXISTS'rL   rM   N
r
   r4   r5   r9   r6   r7   r8   r:   r;   r<   rN   s       r   *test_bb5_events_ddl_contains_if_not_existsrT   Z   sg    S?j(SSS?jSSS?SSSSSSjSSSjSSSS*SSSSSSSr   c                    d} | t         v }|st        j                  d|fd| t         f      t        j                  |       dt	        j
                         v st        j                  t               rt        j                  t               nddz  }t        j                  d      dz   d|iz  }t        t        j                  |            d	x} }y	)
z@BB6: SWARM_SAGAS_DDL uses 'IF NOT EXISTS' to ensure idempotency.rI   r*   rJ   r   rK   z,SWARM_SAGAS_DDL must contain 'IF NOT EXISTS'rL   rM   N
r   r4   r5   r9   r6   r7   r8   r:   r;   r<   rN   s       r   /test_bb6_swarm_sagas_ddl_contains_if_not_existsrW   _   sg    ]?o-]]]?o]]]?]]]]]]o]]]o]]]]/]]]]]]]r   c                 b    t               } t        |        | j                  j                          y)z>BB7: create_all_tables() must call conn.commit() exactly once.N)r   r   commitassert_called_oncer   s    r   2test_bb7_create_all_tables_commits_the_transactionr[   d   s"    DdKK""$r   c                 b    t               } t        |        | j                  j                          y)z<BB8: drop_all_tables() must call conn.commit() exactly once.N)r   r   rY   rZ   r   s    r   0test_bb8_drop_all_tables_commits_the_transactionr]   k   s"    DDKK""$r   c                 
   t         } g }d}|| v }|}|rd}|| v }|}|snt        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  }|j                  |       |rt        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  }
|j                  |
       t        j                  |d      i z  }t        j                  d      dz   d|iz  }t        t        j                  |            dx}x}x}x}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}}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}}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) zUWB1: SESSIONS_DDL contains UUID PK, started_at, ended_at, agent_id, metadata columns.idzUUID PRIMARY KEYr*   )z%(py3)s in %(py5)sddl)r&   rM   z%(py7)spy7)z%(py10)s in %(py12)s)py10py12z%(py14)spy14r   z/sessions must have UUID PRIMARY KEY column 'id'z
>assert %(py17)spy17N
started_atrJ   rK   z&sessions must have 'started_at' columnrL   rM   ended_atz$sessions must have 'ended_at' columnagent_idz$sessions must have 'agent_id' columnmetadataz$sessions must have 'metadata' columnJSONBz"metadata column must be JSONB type)r	   r4   r5   r9   r6   r7   r8   append_format_boolopr:   r;   r<   )r`   @py_assert1r?   rA   rD   @py_assert9@py_assert11rP   @py_format8@py_format13@py_format15@py_format16@py_format18rO   s                 r   ,test_wb1_sessions_table_has_required_columnsru   w   s   
Cg4g43;g-g-4gggg43ggg4gggggg3ggg3ggggggg-ggg-ggggggggggggggggggg6ggggggggH<3HHH<3HHH<HHHHHH3HHH3HHHH HHHHHHHD:DDD:DDD:DDDDDDDDDDDDDDDDDDDDD:DDD:DDD:DDDDDDDDDDDDDDDDDDDDD:DDD:DDD:DDDDDDDDDDDDDDDDDDDD?7c>???7c???7??????c???c???????????r   c                    t         } d}|| v }|st        j                  d|fd|| f      t        j                  |      dt	        j
                         v st        j                  |       rt        j                  |       nddz  }t        j                  d      dz   d|iz  }t        t        j                  |            d	x}}y	)
z?WB2: EVENTS_DDL defines a foreign key referencing sessions(id).zREFERENCES sessions(id)r*   rJ   r`   rK   zDevents.session_id must have a FK constraint: REFERENCES sessions(id)rL   rM   NrS   )r`   rD   r?   rO   rP   s        r   (test_wb2_events_table_has_fk_to_sessionsrw      s    
C$ $+  $    %      ),    ),    	O    r   c                 d   t         } d}|| v }|st        j                  d|fd|| f      t        j                  |      dt	        j
                         v st        j                  |       rt        j                  |       nddz  }t        j                  d      dz   d|iz  }t        t        j                  |            d	x}}d
D ]  }|| v }|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  }t        j                  d| d      dz   d|iz  }t        t        j                  |            d	} y	)zaWB3: SWARM_SAGAS_DDL includes a CHECK constraint on the status column with all four valid values.CHECKr*   rJ   r`   rK   z2swarm_sagas must have a CHECK constraint on statusrL   rM   N)RUNNING	COMPLETEDPARTIAL_FAILFAILED)z%(py0)s in %(py2)sexpected_statusr$   py2z<SWARM_SAGAS_DDL CHECK constraint must include status value ''z
>assert %(py4)sr,   rV   )	r`   rD   r?   rO   rP   r~   rm   @py_format3rF   s	            r   0test_wb3_swarm_sagas_has_status_check_constraintr      s<   
CO7c>OOO7cOOO7OOOOOOcOOOcOOOOOOOOOOOM 
#% 	
 	
# 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
  #& 	
 	
 		 #& 	
 	
  K?J[[\]	
 	
 	
 	
 	

r   c                    t               } | j                  }t        |        dj                  d |j                  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  }t        j                  d      d	z   d
|iz  }t        t        j                  |            dx}}y)zPWB4: drop_all_tables() uses CASCADE so FK constraints are automatically dropped. c              3  ,   K   | ]  }|d    d      yw)r   N ).0r>   s     r   	<genexpr>z8test_wb4_drop_all_tables_uses_cascade.<locals>.<genexpr>   s     DA!QDs   CASCADEr*   rJ   executedrK   z9drop_all_tables() must use CASCADE in all DROP statementsrL   rM   N)r   r   r   joinr1   r2   r4   r5   r9   r6   r7   r8   r:   r;   r<   )r   r=   r   rD   r?   rO   rP   s          r   %test_wb4_drop_all_tables_uses_cascader      s    D
,,CDxxD)C)CDDH]9 ]]]9]]]9]]]]]]]]]]]]]"]]]]]]]r   c                 B
   d} | t         v }|st        j                  d|fd| t         f      t        j                  |       dt	        j
                         v st        j                  t               rt        j                  t               nddz  }t        j                  d      dz   d|iz  }t        t        j                  |            d	x} }d} | t        v }|st        j                  d|fd| t        f      t        j                  |       d
t	        j
                         v st        j                  t              rt        j                  t              nd
dz  }t        j                  d      dz   d|iz  }t        t        j                  |            d	x} }d} | t         v }|st        j                  d|fd| t         f      t        j                  |       dt	        j
                         v st        j                  t               rt        j                  t               nddz  }dd|iz  }t        t        j                  |            d	x} }d} | t         v }|st        j                  d|fd| t         f      t        j                  |       dt	        j
                         v st        j                  t               rt        j                  t               nddz  }dd|iz  }t        t        j                  |            d	x} }d} | t         v }|st        j                  d|fd| t         f      t        j                  |       dt	        j
                         v st        j                  t               rt        j                  t               nddz  }dd|iz  }t        t        j                  |            d	x} }d} | t        v }|st        j                  d|fd| t        f      t        j                  |       d
t	        j
                         v st        j                  t              rt        j                  t              nd
dz  }dd|iz  }t        t        j                  |            d	x} }d} | t        v }|st        j                  d|fd| t        f      t        j                  |       d
t	        j
                         v st        j                  t              rt        j                  t              nd
dz  }dd|iz  }t        t        j                  |            d	x} }y	)zTWB5: EVENTS_DDL and SWARM_SAGAS_DDL both define at least one CREATE INDEX statement.zCREATE INDEXr*   rJ   r
   rK   zEVENTS_DDL must define indexesrL   rM   Nr   z#SWARM_SAGAS_DDL must define indexesidx_events_session_idzassert %(py5)sidx_events_typeidx_events_createdidx_sagas_session_ididx_sagas_status)r
   r4   r5   r9   r6   r7   r8   r:   r;   r<   r   rN   s       r   &test_wb5_events_and_sagas_have_indexesr      s   I>Z'III>ZIII>IIIIIIZIIIZIIII)IIIIIIIS>_,SSS>_SSS>SSSSSS_SSS_SSSS.SSSSSSS #0"j0000"j000"000000j000j0000000*
****
*********
***
*******-:----:---------:---:-------!4!_4444!_444!444444_444_4444444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                 t   t               } | j                  }t        |        |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  }t        j                  d|j                  j                         dz   d|iz  }t        t        j                  |            d	x}x}x}}y	)
zTWB6: create_all_tables() calls cur.execute() exactly 3 times (once per table/block).r   r   )zO%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.execute
}.call_count
} == %(py7)sr=   )r$   r   r,   ra   z8Expected 3 cur.execute() calls (one per DDL block), got z
>assert %(py9)spy9N)r   r   r   r1   
call_countr4   r5   r6   r7   r8   r9   r:   r;   r<   )r   r=   rm   rE   @py_assert6r@   rp   @py_format10s           r   8test_wb6_create_all_tables_executes_three_ddl_statementsr      s   D
,,Cd;; ;!! Q !Q&  !Q                  "    &'    C3;;CYCYBZ[     r   c                 Z   t               } | j                  }t        |        |j                  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
   d
   }|d   d
   d
   }	|d   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  }t        j                  d|d	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	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	d        dz   d|iz  }t        t        j                  |            d	x}}y	)z\WB7: create_all_tables() executes sessions DDL first, events second, sagas third (FK order).r   r   r    r!   callsr#   zassert %(py8)sr(   Nr   r.   r0   r/   r*   rJ   
first_stmtrK   z%First DDL must create sessions, got: P   rL   rM   r-   second_stmtz$Second DDL must create events, got: r)   
third_stmtz(Third DDL must create swarm_sagas, got: )r   r   r   r1   r2   r!   r4   r5   r6   r7   r8   r9   r;   r<   r:   )r   r=   r   r?   r@   rA   rB   rC   r   r   r   rD   rO   rP   s                 r   *test_wb7_create_all_tables_execution_orderr      sJ   D
,,CdKK&&Eu::?:33uu:q!QJ(1+a.Kq!QJ^:#^^^:^^^:^^^^^^^^^^^^^'LZX[Y[_L]%^^^^^^^]8{"]]]8{]]]8]]]]]]{]]]{]]]]&J;WZXZK[J\$]]]]]]]d=J&ddd=Jddd=ddddddJdddJdddd*RS]^a_aSbRc(dddddddr   c                    ddl m}  | t        u }|st        j                  d|fd| t        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z  }dd	|iz  }t        t        j                  |            d
}y
)z>Package level: create_all_tables importable from core.storage.r   )r   isz%(py0)s is %(py2)s_fnr   r   assert %(py4)sr,   N)
core.storager   r4   r5   r6   r7   r8   r9   r;   r<   r   rm   r   rF   s       r   &test_package_exports_create_all_tablesr      sq    5#####3#######3###3##################r   c                    ddl m}  | t        u }|st        j                  d|fd| t        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z  }dd	|iz  }t        t        j                  |            d
}y
)z<Package level: drop_all_tables importable from core.storage.r   )r   r   r   r   r   r   r   r,   N)
r   r   r4   r5   r6   r7   r8   r9   r;   r<   r   s       r   $test_package_exports_drop_all_tablesr      sm    3/!!!!3/!!!!!!3!!!3!!!!!!/!!!/!!!!!!!r   c                 L   ddl m} m}m} | t        u }|st	        j
                  d|fd| t        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z  }dd	|iz  }t        t	        j                  |            d
}|t        u }|st	        j
                  d|fd|t        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z  }dd	|iz  }t        t	        j                  |            d
}|t        u }|st	        j
                  d|fd|t        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z  }dd	|iz  }t        t	        j                  |            d
}y
)zDPackage level: all three DDL constants importable from core.storage.r   )r	   r
   r   r   r   sr	   r   r   r,   Ner
   gr   )r   r	   r
   r   r4   r5   r6   r7   r8   r9   r;   r<   )r   r   r   rm   r   rF   s         r   &test_package_exports_all_ddl_constantsr      s?   UU111
?1
11

111r   __main__c                    t         dz  a 	  |        t        d|         t        dz  ay # t        $ r/}t        d|  d|        t	        j
                          Y d }~y d }~ww xY w)Nr.   z	  [PASS] z	  [FAIL] z: )	tests_runprinttests_passed	Exception	traceback	print_exc)namefnexcs      r   _runr      sb    Q		"DIdV$%AL 	"IdV2cU+,!!	"s   * 	A"%AA"zBB1: create_all_tables no errorz!BB2: create_all_tables idempotentzBB3: drop order reversedzBB4: SESSIONS_DDL IF NOT EXISTSzBB5: EVENTS_DDL IF NOT EXISTSz"BB6: SWARM_SAGAS_DDL IF NOT EXISTSzBB7: create commitszBB8: drop commitszWB1: sessions columnszWB2: events FK to sessionszWB3: sagas CHECK constraintzWB4: drop uses CASCADEzWB5: indexes definedzWB6: 3 execute callszWB7: create DDL orderzPKG: create_all_tables exportzPKG: drop_all_tables exportzPKG: DDL constants export
/z tests passedz(ALL TESTS PASSED -- Story 5.01 (Track B)r.   )returnr   )r   str)1__doc__
__future__r   builtinsr6   _pytest.assertion.rewrite	assertionrewriter4   syspathinsertpytestunittest.mockr   r   r   core.storage.postgres_schemar   r   r	   r
   r   r   r   r   rG   rQ   rT   rW   r[   r]   ru   rw   r   r   r   r   r   r   r   r   __name__r   r   r   r   r   exitr   r   r   <module>r      s  
 #   
 * +  0 0  g X
T
^
%%@
^
1	e0$"  zIL	" 		*,YZ	,.VW	#%WX	*,XY	(*TU	-/^_	 RS	NO	 "NO	%'OP	&(XY	!#HI	!GH	!YZ	 "LM	(*PQ	&(LM	$&LM	B|nAi[
67y 89Q r   