
    (iD                    *   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 e
j                         j                  dd  ZdZ ej"                  d      d	        Z G d
 d      Z G d d      Z G d d      Z G d d      Z G d d      Zy)uQ  
Story 6.05 — PG Store Integration Tests
=========================================
All tests use the REAL Elestio PostgreSQL server.
Test data is isolated via a unique platform name that is cleaned up
in the module-scoped fixture teardown.

Run:
    cd /mnt/e/genesis-system
    python3 -m pytest tests/kb/test_m6_pg_integration.py -v
    )annotationsNtest_   cust_abcmodule)scopec               #  (  K   ddl m} m}  |        } ||       | |j                         5 }|j	                  dt
        f       |j	                  dt
        f       |j                          ddd       |j                          y# 1 sw Y   xY ww)zBModule-scoped real PG connection. Cleans up test rows on teardown.r   )get_connectionensure_schema1DELETE FROM platform_kb_pages WHERE platform = %s9DELETE FROM platform_kb_ingestion_log WHERE platform = %sN)core.kb.pg_storer
   r   cursorexecuteTEST_PLATFORMcommitclose)r
   r   conncurs       8/mnt/e/genesis-system/tests/kb/test_m6_pg_integration.pypg_connr      s      ?D$
J 
 	#?	
 	G	
 		 	JJL	 	s   ,B?B-BBBc                      e Zd Zd Zd Zy)TestSchemaCreationc                   |j                         5 }|j                  d       |j                         D ch c]  }|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
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c c}w # 1 sw Y   xY w)zDensure_schema creates both tables (verified via information_schema).a'  
                SELECT table_name
                FROM information_schema.tables
                WHERE table_schema = 'public'
                  AND table_name IN (
                      'platform_kb_pages',
                      'platform_kb_ingestion_log'
                  )
                r   Nplatform_kb_pagesin)z%(py1)s in %(py3)stables)py1py3zplatform_kb_pages table missing
>assert %(py5)spy5platform_kb_ingestion_logz'platform_kb_ingestion_log table missing)r   r   fetchall
@pytest_ar_call_reprcompare	_saferepr@py_builtinslocals_should_repr_global_name_format_assertmsgAssertionError_format_explanation)	selfr   r   rowr   @py_assert0@py_assert2@py_format4@py_format6s	            r   test_schema_creationz'TestSchemaCreation.test_schema_creation;   s*   ^^ 	8KK
 ),7c!f7F7	8 #O"f,OOO"fOOO"OOOOOOfOOOfOOOO.OOOOOOO*_*f4___*f___*______f___f____6_______ 8	8 	8s   $GGGGGc                0    ddl m}  ||        ||       y)zCCalling ensure_schema twice raises no error (CREATE IF NOT EXISTS).r   )r   N)r   r   )r.   r   r   s      r   test_schema_idempotentz)TestSchemaCreation.test_schema_idempotentM   s    2gg    N)__name__
__module____qualname__r4   r6    r7   r   r   r   :   s    `$r7   r   c                  $    e Zd Zd Zd Zd Zd Zy)TestPageUpsertc                   ddl m}  ||t        ddd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}d}||kD  }|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)z9Inserting a brand-new page returns a positive integer ID.r   )upsert_pagezhttps://example.com/page1zPage One	aabbcc001   platformurltitlecontent_hashchunk_countz5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancepage_idint)py0r   py2py4N>z%(py0)s > %(py3)srK   r    assert %(py5)sr"   )r   r?   r   rH   rJ   r(   r)   r%   r*   r'   r,   r-   r&   )
r.   r   r?   rI   @py_assert3@py_format5r1   @py_assert1r2   r3   s
             r   test_insert_new_pagez#TestPageUpsert.test_insert_new_pageZ   s   0"+$
 '3''''''''z'''z'''''''''''''''''3'''3''''''''''w{wwwr7   c                   ddl m}m} d} ||t        |ddd        ||t        |dd	d
        ||t              }||   }d	}||k(  }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }	t	        j                  d|j                  |       d      dz   d|	iz  }
t        t	        j                  |
            dx}x}}y)z?Upserting the same URL with a new hash updates the stored hash.r   r?   get_content_hasheszhttps://example.com/page-updatezUpdate Testoriginal_hash   rB   zUpdate Test v2new_hash_xyz   ==z%(py1)s == %(py4)sr   rM   zExpected 'new_hash_xyz', got ''
>assert %(py6)spy6N)r   r?   rY   r   r%   r&   r'   r+   getr,   r-   )r.   r   r?   rY   rD   hashesr0   rS   r1   rT   @py_format7s              r   test_update_existing_pagez(TestPageUpsert.test_update_existing_pagei   s    D/"(	
 	""'	
 $G];c{ 	
n 	
{n, 	
 	
{n 	
 	
 		  	
 	
 		 - 	
 	
  -VZZ_,=Q?	
 	
 	
 	
 	
 	
r7   c           	     p   ddl m}m} d} ||t        |dddd      }d}||kD  }|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}} ||t        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z   d|iz  }t        t	        j                  |            d}y)zAcustomer_id=None (global KB) is inserted and retrieved correctly.r   rX   zhttps://example.com/global-pagezGlobal Pageglobalhash001   NrC   rD   rE   rF   rG   customer_idrN   rP   rI   rQ   rR   r"   rm   r   z%(py0)s in %(py2)srD   rf   rK   rL   z$Global page not found in hash lookup
>assert %(py4)srM   )r   r?   rY   r   r%   r&   r(   r)   r*   r'   r,   r-   r+   )r.   r   r?   rY   rD   rI   r1   rU   r2   r3   rf   @py_format3rT   s                r   test_global_kb_pagez"TestPageUpsert.test_global_kb_page   s   D/"(
 w{www $G]Mf}DDDsfDDDDDDsDDDsDDDDDDfDDDfDDDDDDDDDDDr7   c           	        ddl m}m} d} ||t        |dddt                ||t        t              }||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  }dd|iz  }t        t        j                  |            d} ||t        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z   d|iz  }t        t        j                  |            d}y)zBPages with explicit customer_id are stored separately from global.r   rX   z!https://example.com/customer-pagezCustomer Scopedcusthash001r[   rl   rn   r   ro   rD   rf   rp   assert %(py4)srM   Nnot in)z%(py0)s not in %(py2)sglobal_hashesz3Customer-scoped page leaked into global hash lookuprq   )r   r?   rY   r   OTHER_CUSTOMERr%   r&   r(   r)   r*   r'   r,   r-   r+   )
r.   r   r?   rY   rD   rf   rU   rr   rT   ry   s
             r   test_customer_scoped_pagez(TestPageUpsert.test_customer_scoped_page   se   D1"#&&	
 $G]Wf}sfssff +7MtT-' 	
 	
s- 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
  ( 	
 	
 		 ( 	
 	
  B	
 	
 	
 	
 	
r7   N)r8   r9   r:   rV   rh   rs   r{   r;   r7   r   r=   r=   Y   s    
6E(
r7   r=   c                      e Zd Zd Zd Zd Zy)TestContentHashLookupc           
        ddl m}m} t         d}t	        d      D cg c]  }d| 	 }}t        |      D ]  \  }} ||||d| d|dd	
         |||      }t        |      }	d}
|	|
k(  }|st        j                  d|fd|	|
f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      t        j                  |
      dz  }t        j                  dt        |             dz   d|iz  }t        t        j                  |            dx}	x}}
t        |      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}	} |j!                         5 }|j#                  d|f       |j%                          ddd       yc c}w # 1 sw Y   yxY w)z@Inserting 5 pages produces 5 hash entries in get_content_hashes.r   rX   _5pages   zhttps://example.com/hash-test/z
Hash Test hash_03drk   rB   r^   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenrf   rK   r   r    rd   zExpected 5 hashes, got 
>assert %(py8)spy8Nr`   ra   assert %(py6)srd   r   )r   r?   rY   r   range	enumerater   r%   r&   r(   r)   r*   r'   r+   r,   r-   r   r   r   )r.   r   r?   rY   subiurlsrD   rf   r1   @py_assert5@py_assert4rg   @py_format9r0   rS   rT   r   s                     r   test_get_hashesz%TestContentHashLookup.test_get_hashes   s   D w'>CAhG04GGo 	FAs"1#&$QsG_	 $GS16{HaH{aHHH{aHHHHHHsHHHsHHHHHH6HHH6HHH{HHHaHHH#:3v;-!HHHHHHHHo 	2FAs#;1E!C/1;/1111;/111;111/1111111	2 ^^ 	KKCcV NN		 	# H"	 	s   I%8$I**I3c           	     8   ddl m}m} d}d}d}d} ||t        |ddd	|
        ||t        |ddd	|
        ||t        |      } ||t        |      }	g }
||v }|}|r||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  }dd|iz  }|
j                  |       |rt	        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  }|
j                  |       t	        j                  |
d      i z  }t	        j                  d      dz   d|iz  }t        t	        j                  |            dx}x}
x}}g }
||	v }|}|r||	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  }dd|iz  }|
j                  |       |rt	        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  }|
j                  |       t	        j                  |
d      i z  }t	        j                  d       dz   d|iz  }t        t	        j                  |            dx}x}
x}}y)!z>Customer-specific scoping returns only that customer's hashes.r   rX   scoped_cust_ascoped_cust_bzhttps://example.com/scoped/azhttps://example.com/scoped/bAhash_ark   rl   Bhash_brn   r   )z%(py2)s in %(py4)surl_ahashes_a)rL   rM   z%(py6)srd   rw   )z%(py8)s not in %(py10)surl_b)r   py10z%(py12)spy12zcust_a sees cust_b's pagesz
>assert %(py15)spy15Nhashes_bzcust_b sees cust_a's pages)r   r?   rY   r   r%   r&   r(   r)   r*   r'   append_format_boolopr+   r,   r-   )r.   r   r?   rY   cust_acust_br   r   r   r   rU   rS   r0   @py_assert9rT   rg   @py_format11@py_format13@py_format14@py_format16s                       r   test_get_hashes_scopedz,TestContentHashLookup.test_get_hashes_scoped   s   D  ..mH!	
 	mH!	

 &g}&Q%g}&Q	
u  	
U(%: 	
 	
 	
u 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
  ! 	
 	
 		 ! 	
 	
 	
	6	
		
 	
U( 	
 	
	6	
 	
  &+ 	
 	
 		 &+ 	
 	
	6	
 	
  3; 	
 	
 		 3; 	
 	
 	
	6	
		
 	
 	
  )	
 	
 	
 	
 	
 	
	
u  	
U(%: 	
 	
 	
u 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
  ! 	
 	
 		 ! 	
 	
 	
	6	
		
 	
U( 	
 	
	6	
 	
  &+ 	
 	
 		 &+ 	
 	
	6	
 	
  3; 	
 	
 		 3; 	
 	
 	
	6	
		
 	
 	
  )	
 	
 	
 	
 	
 	
r7   c                   ddl m}  ||dt        j                         j                  dd        }i }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }t        j                  d
|       dz   d|iz  }t        t        j                  |            dx}}y)zFget_content_hashes returns an empty dict for a platform with no pages.r   )rY   nonexistent_N   r^   z%(py0)s == %(py3)srf   rQ   zExpected empty dict, got r!   r"   )r   rY   uuiduuid4hexr%   r&   r(   r)   r*   r'   r+   r,   r-   )r.   r   rY   rf   r1   rU   r2   r3   s           r   test_get_hashes_emptyz+TestContentHashLookup.test_get_hashes_empty   s    7#G|DJJL<L<LRa<P;Q-RSAv|AAAvAAAAAAvAAAvAAAAAA8AAAAAAAr7   N)r8   r9   r:   r   r   r   r;   r7   r   r}   r}      s    :
8Br7   r}   c                  $    e Zd Zd Zd Zd Zd Zy)TestIngestionLogc                   ddl m}  ||t              }g }t        |t              }|}|r	d}||kD  }|}|sd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  }	|j                  |	       |rt        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  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}x}}|j                         5 }|j!                  d|f       |j#                         }ddd       d}|u}|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y# 1 sw Y   0xY w)z?log_ingestion_start returns a positive ID; status is 'running'.r   )log_ingestion_startz.%(py6)s
{%(py6)s = %(py2)s(%(py3)s, %(py4)s)
}rH   log_idrJ   )rL   r    rM   rd   rN   )z%(py8)s > %(py11)s)r   py11z%(py13)spy13zassert %(py16)spy16Nz:SELECT status FROM platform_kb_ingestion_log WHERE id = %sis notz%(py0)s is not %(py3)sr/   rQ   rR   r"   runningr^   r`   ra   r   rd   )r   r   r   rH   rJ   r(   r)   r%   r*   r'   r   r&   r   r,   r-   r   r   fetchone)r.   r   r   r   rU   r   r0   @py_assert10r   rg   @py_format12r   @py_format15@py_format17r   r/   r1   r2   r3   rS   rT   s                        r   test_log_startzTestIngestionLog.test_log_start   s   8$Wm<5z&#&5&5&A56A:555555z555z555555&555&555555#555#555&5555&5556A55555565556555A55555555555555^^ 	!KKL	 ,,.C	! s$s$ss$1v""v""""v"""v""""""""""	! 	!s   $M,,M6c                   ddl m}m}  ||t              }ddddddd} ||||       |j	                         5 }|j                  d|f       |j                         }d	d	d	       d	}|u}	|	st        j                  d
|	fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }
dd|
iz  }t        t        j                  |            d	x}	}|\  }}}}}}}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}	}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}	}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}	}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}	}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}	}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}	}d	}||u}	|	st        j                  d
|	fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }
t        j                  d      dz   d|
iz  }t        t        j                  |            d	x}	}y	# 1 sw Y   YxY w)z9log_ingestion_complete sets stats and status='completed'.r   r   log_ingestion_completed   
   i,  	completedpages_fetchedpages_changedchunks_createdvectors_upsertederrorsstatusz
                SELECT pages_fetched, pages_changed, chunks_created,
                       vectors_upserted, errors, status, completed_at
                FROM platform_kb_ingestion_log WHERE id = %s
                Nr   r   r/   rQ   rR   r"   r^   r   pfpcccvuerrr   completed_atzcompleted_at should be setr!   )r   r   r   r   r   r   r   r%   r&   r(   r)   r*   r'   r,   r-   r+   )r.   r   r   r   r   statsr   r/   r1   rU   r2   r3   r   r   r   r   r   r   r   s                      r   test_log_completez"TestIngestionLog.test_log_complete  s1   P$Wm< ! #!
 	w6^^ 		!KK
 	 ,,.C		! s$s$ss$471BBV\rSyrSrrSrRxrRrrRrSyrSrrSrSyrSrrSsaxsassa$$v$$$$v$$$$$$v$$$v$$$$$$$$$$#'E|4'EEE|4EEEEEE|EEE|EEE4EEE)EEEEEEE'		! 		!s   $V44V>c                   ddl m}m}  ||t              } |||ddddddddid       |j	                         5 }|j                  d	|f       |j                         }d
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
# 1 sw Y   xY w)zDlog_ingestion_complete with status='failed' stores failure metadata.r   r   r   rA   failederror_detailzConnection timeout)r   r   r   r   r   r   metadatazBSELECT status, errors FROM platform_kb_ingestion_log WHERE id = %sNr^   r`   ra   r   rd   rk   )r   r   r   r   r   r   r   r%   r&   r'   r,   r-   )r.   r   r   r   r   r   r/   r0   rS   r1   rT   rg   s               r   test_log_failedz TestIngestionLog.test_log_failed1  s    P$Wm<!"!""#$%"+-AB	
 ^^ 	!KKT	 ,,.C	! 1v!!v!!!!v!!!v!!!!!!!!!!1vv{vv	! 	!s   $E''E1c                   ddl m}m}m} t         d}g }t        d      D ],  } |||      } ||||dz   dd       |j                  |       .  |||d	      }	t        |	      }
d}|
|k(  }|st        j                  d
|fd|
|f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |	      rt        j                  |	      ndt        j                  |
      t        j                  |      dz  }t        j                  dt        |	             dz   d|iz  }t        t        j                   |            dx}
x}}|	D cg c]  }|d   	 }}d}t#        ||      }||k(  }|sMt        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  t"              rt        j                  t"              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                  d      dz   d|iz  }t        t        j                   |            dx}x}}|j%                         5 }|j'                  d|f       |j)                          ddd       yc c}w # 1 sw Y   yxY w)z:get_ingestion_history returns N runs ordered newest-first.r   )r   r   get_ingestion_history_histrA   rk   r   )r   r   r   limitr^   r   r   historyr   zExpected 3 history rows, got r   r   NidT)reverse)zA%(py0)s == %(py7)s
{%(py7)s = %(py2)s(%(py3)s, reverse=%(py5)s)
}returned_idssorted)rK   rL   r    r"   py7z History not ordered newest-firstz
>assert %(py9)spy9r   )r   r   r   r   r   r   r   r   r%   r&   r(   r)   r*   r'   r+   r,   r-   r   r   r   r   )r.   r   r   r   r   hist_platformidsr   lidr   r1   r   r   rg   r   rr   @py_assert6rU   @py_format8@py_format10r   s                         r   test_ingestion_historyz'TestIngestionLog.test_ingestion_historyN  s   	
 	
 )//q 	A%g}=C""#a%;?
 JJsO	 (bI7|PqP|q PPP|qPPPPPPsPPPsPPPPPP7PPP7PPP|PPPqPPP$A#g,"PPPPPPPP *11A$11<@ 	
vlDA 	
|AA 	
 	
 	
|A 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
   & 	
 	
 		  & 	
 	
	6	
 	
  '3 	
 	
 		 '3 	
 	
 		 =A 	
 	
 		  B 	
 	
  /	
 	
 	
 	
 	

 ^^ 	KKK  NN	 	 2	 	s   M$MMN)r8   r9   r:   r   r   r   r   r;   r7   r   r   r      s    # "FH:"r7   r   c                      e Zd Zd Zy)TestFullLifecyclec                   ddl m}m}m}m}m}m}  ||        ||t        d      d}|kD  }	|	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}	}d} ||t        |dddd      }d}||kD  }	|	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}	} ||t        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  }dd|iz  }t        t        j                  |            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}} ||t        |ddd d        ||t        d      }||   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }t        j                   d!|j#                  |       d"      d#z   d|iz  }t        t        j                  |            dx}x}} ||d$d$d d dd%d&        ||t        d'(      }|D cg c]  }|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,z   d|iz  }t        t        j                  |            d}	t%        f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c c}w )0u   
        Full pipeline smoke-test:
          schema → log start → insert page → lookup hash → update page
          → verify hash changed → log complete → verify history
        r   )r   r   r   r?   rY   r   lifecycle_custrn   rN   rP   r   rQ   rR   r"   Nzhttps://example.com/lifecyclezLifecycle Pagehash_v1r   rl   rI   r   ro   rD   rf   rp   rv   rM   r^   r`   ra   r   rd   zLifecycle Page v2hash_v2r   zHash should be 'hash_v2', got 'rb   rc   rk   r   r   2   r   r   log_idsz&Completed log run not found in historyrq   c              3  4   K   | ]  }|d    k(  s|  yw)r   Nr;   ).0r   r   s     r   	<genexpr>z8TestFullLifecycle.test_full_lifecycle.<locals>.<genexpr>  s     ?QQtW->q?s   r   r   )r   r   r   r   r?   rY   r   r   r%   r&   r(   r)   r*   r'   r,   r-   r+   re   next)r.   r   r   r   r   r?   rY   r   r1   rU   r2   r3   rD   rI   rf   rr   rT   r0   rS   rg   hashes_afterr   r   r   our_runr   s                            @r   test_full_lifecyclez%TestFullLifecycle.test_full_lifecyclex  s   	
 	
 	g %WmIYZvzvvv ."""(
 w{www $G]HXYf}sfssffc{'i'{i''''{i'''{'''i''''''' 	"%"(	
 *]0@
 C  	
I 	
 I- 	
 	
 I 	
 	
 		 ! 	
 	
 		 %. 	
 	
  .l.>.>s.C-DAF	
 	
 	
 	
 	

 	!"!""#$%%	
 (bI$+,q1T7,, JJJvJJJJJJvJJJvJJJJJJJJJJJJJ"JJJJJJJ ?'??x /K/ K//// K/// ///K///////'(-A-(A----(A---(---A------- -s   WN)r8   r9   r:   r   r;   r7   r   r   r   w  s    Q.r7   r   )__doc__
__future__r   builtinsr(   _pytest.assertion.rewrite	assertionrewriter%   timer   psycopg2pytestr   r   r   rz   fixturer   r   r=   r}   r   r   r;   r7   r   <module>r     s   
 #       


((!,-. h  4 >V
 V
z?B ?BLt tvR. R.r7   