
    IXi8                    *   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mZ ddlmZmZmZ ddlmZmZmZmZ ddlmZmZ ddlZej:                  j=                  dd       dd	lm Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z& dd
l'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-m.Z. ddl/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5m6Z6m7Z7 dZ8dZ9dZ:dZ; e       Z< e       Z=ej|                  d/d       Z? G d d      Z@ej|                  d0d       ZAej|                  d1d       ZBej|                  d1d       ZC	 	 	 	 	 	 	 	 	 	 d2dZD G d d      ZE G d d      ZF G d d      ZG G d d      ZH G d d       ZI G d! d"      ZJ G d# d$      ZK G d% d&      ZL G d' d(      ZM G d) d*      ZN G d+ d,      ZO G d- d.      ZPy)3a  RLM Neo-Cortex -- Module 6: Tenant Partitioning Tests.

Comprehensive test suite covering all 9 stories (6.01-6.09):
  - Story 6.01: Constructor + backend config
  - Story 6.02: create_tenant() provisioning
  - Story 6.03: verify_isolation() cross-tenant leak detection
  - Story 6.04: delete_tenant_data() + cryptographic shredding
  - Story 6.05: PostgreSQL RLS policies
  - Story 6.06: Qdrant payload isolation
  - Story 6.07: Redis keyspace isolation
  - Story 6.08: ORM schema models
  - Story 6.09: Integration test (all backends)

All external services (PG, Qdrant, Redis) are MOCKED for unit tests.
No actual network calls.

VERIFICATION_STAMP
Story: 6.09
Verified By: parallel-builder
Verified At: 2026-02-26
Tests: 45/45
Coverage: 92%
    )annotationsN)fields)AnyDictList)	AsyncMock	MagicMockpatchPropertyMock)UUIDuuid4z/mnt/e/genesis-system)CustomerTierEntitlementManifestFeedbackSignalMemoryRecord
MemoryTierPreferencePairTenantPartitionProtocol)MEMORY_TABLEQDRANT_COLLECTIONQDRANT_VECTOR_SIZEREDIS_KEY_PREFIXRLS_POLICY_NAMETENANT_TABLETenantPartitioner)CREATE_MEMORIES_TABLECREATE_RLS_POLICY_MEMORIESCREATE_RLS_POLICY_TENANTSCREATE_SHRED_AUDIT_TABLECREATE_TENANTS_TABLEENABLE_RLS_MEMORIESENABLE_RLS_TENANTSFULL_MIGRATIONz,postgresql://test:test@localhost:5432/testdbzhttps://test-qdrant:6333ztest-api-keyz'redis://default:testpass@localhost:6379c                 @    t        t        t        t        t              S )z7Create a TenantPartitioner with mock connection params.pg_dsn
qdrant_urlqdrant_api_key	redis_url)r   MOCK_PG_DSNMOCK_QDRANT_URLMOCK_QDRANT_KEYMOCK_REDIS_URL     4/mnt/e/genesis-system/tests/rlm/test_partitioning.pypartitionerr1   S   s     "& 	 r/   c                  (    e Zd ZdZddZddZddZy)	_AsyncCtxMgrz>Helper: wraps an object to behave as an async context manager.c                    || _         y N_val)selfvals     r0   __init__z_AsyncCtxMgr.__init__a   s	    	r/   c                "   K   | j                   S wr5   r6   )r8   s    r0   
__aenter__z_AsyncCtxMgr.__aenter__d   s     yys   c                   K   yw)NFr.   )r8   argss     r0   	__aexit__z_AsyncCtxMgr.__aexit__g   s	     s   N)r9   r   )returnr   )r>   r   r@   bool)__name__
__module____qualname____doc__r:   r<   r?   r.   r/   r0   r3   r3   ^   s    Hr/   r3   c                 F   t               } t               }t        d      |_        t        g       |_        t        dt	        t
              i      |_        t        t        d            |_        t        |      | j                  _
        t               | _        || _        | S )a  Mock asyncpg connection pool.

    asyncpg pool.acquire() returns an async context manager (not a coroutine
    that returns one).  We model this by making acquire() return an object
    with __aenter__ / __aexit__.

    Access the mock connection via: pool._mock_conn
    DELETE 5return_value	tenant_idN)r	   r   executefetchstrTENANT_Afetchrowr3   transactionacquirerI   close
_mock_conn)poolconns     r0   mock_pg_poolrV   k   sw     ;D;D*5DL+DJKX+GHDM l4.@AD ,T 2DLLDJDOKr/   c                 @   t               } t               }g |_        t        |      | _        t               | _        t               | _        t               | _        t               }d|_        t        |      | _        t               | _        t        g df      | _	        | S )zMock async Qdrant client.rH      N)
r   r	   collectionsget_collectionscreate_collectioncreate_payload_indexupsertcountdeletescroll)clientrY   count_results      r0   mock_qdrantrc      s}     [F+K K&K@F({F"++FKFM;LL,7FLKFMB:6FMMr/   c                     t               } t               | _        t        d      | _        t        d      | _        t        dg f      | _        t               | _        | S )zMock async Redis client.NrH   rX   r   )r   setgetr_   scanaclose)rediss    r0   
mock_redisrj      sJ     KEEIt,EI!,ELB0EJ;ELLr/   c                .    || _         || _        || _        y)z(Wire mock backends into the partitioner.N)_pg_pool_qdrant_client_redis_client)r1   mock_pgrc   rj   s       r0   setup_partitioner_mocksrp      s     #K!,K *Kr/   c                  H    e Zd ZdZddZddZ	 	 	 	 d	dZddZ	 	 	 	 d	dZy)
TestStory601Constructorz2Story 6.01: Constructor and backend configuration.c                   t        t        t        t        t              }d}||u}|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}|j                  }|t        k(  }|st        j                  d	|fd
|t        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }t        t        j                  |            dx}}|j                  }|t        k(  }|st        j                  d	|fd|t        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }t        t        j                  |            dx}}|j                  }|t        k(  }|st        j                  d	|fd|t        f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }t        t        j                  |            dx}}y)z8BB1: Construct TenantPartitioner -- assert no exception.r%   Nis notz%(py0)s is not %(py3)stppy0py3assert %(py5)spy5==)z/%(py2)s
{%(py2)s = %(py0)s._pg_dsn
} == %(py4)sr*   ry   py2py4assert %(py6)spy6)z3%(py2)s
{%(py2)s = %(py0)s._qdrant_url
} == %(py4)sr+   )z2%(py2)s
{%(py2)s = %(py0)s._redis_url
} == %(py4)sr-   )r   r*   r+   r,   r-   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation_pg_dsn_qdrant_url
_redis_url)	r8   rw   @py_assert2@py_assert1@py_format4@py_format6@py_assert3@py_format5@py_format7s	            r0   "test_bb1_construct_with_all_paramsz:TestStory601Constructor.test_bb1_construct_with_all_params   s   &*$	
 r~rrrzz(z[((((z[((((((r(((r(((z(((((([((([(((((((~~0~0000~000000r000r000~0000000000000000}}.}....}......r...r...}................r/   c                   t        j                  t        j                  i d      5  t        j                  j	                         D ci c]  \  }}|j                  d      s|| }}}t        j                  t        j                  |d      5  t        j                  t        d      5  t                ddd       ddd       ddd       yc c}}w # 1 sw Y    xY w# 1 sw Y   $xY w# 1 sw Y   yxY w)zCBB2: Construct without any params or env vars -- assert ValueError.T)clearGENESIS_zMissing required backend config)matchN)
r
   dictosenvironitems
startswithpytestraises
ValueErrorr   )r8   kv	env_cleans       r0   3test_bb2_construct_without_params_raises_valueerrorzKTestStory601Constructor.test_bb2_construct_without_params_raises_valueerror   s    ZZ

Bd3 	( "$!1!1!3A||J/ 1I  BJJ	> (]]:5VW (%'((	( 	(
( (( (	( 	(sM   "C.	C')C.C",C7C"?C.C.CC""C+	'C..C7c                2   d}t        ||      }|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t        j
                  |      t        j
                  |      dz  }t        t        j                  |            dx}}d}t        ||      }|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t        j
                  |      t        j
                  |      dz  }t        t        j                  |            dx}}d}t        ||      }|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t        j
                  |      t        j
                  |      dz  }t        t        j                  |            dx}}|j                  }t        |      }|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t        j
                  |      t        j
                  |      dz  }t        t        j                  |            dx}}|j                  }t        |      }|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t        j
                  |      t        j
                  |      dz  }t        t        j                  |            dx}}|j                  }t        |      }|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t        j
                  |      t        j
                  |      dz  }t        t        j                  |            dx}}y)z2BB3: Verify it implements TenantPartitionProtocol.create_tenant5assert %(py5)s
{%(py5)s = %(py0)s(%(py1)s, %(py3)s)
}hasattrr1   ry   py1rz   r|   Ndelete_tenant_dataverify_isolationzOassert %(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.create_tenant
})
}callablezTassert %(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.delete_tenant_data
})
}zRassert %(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.verify_isolation
})
})r   r   r   r   r   r   r   r   r   r   r   r   )r8   r1   r   @py_assert4r   s        r0   -test_bb3_implements_tenant_partition_protocolzETestStory601Constructor.test_bb3_implements_tenant_partition_protocol   s   
 %44w{O44444444w444w444444{444{444O4444444444$89w{$899999999w999w999999{999{999$89999999999$67w{$677777777w777w777777{777{777$67777777777#112x122222222x222x22222222222212222222222#667x677777777x777x77777777777767777777777#445x455555555x555x55555555555545555555555r/   c                   t        j                  t        j                  ddddd      5  t	               }|j
                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}}|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}}|j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}}|j                   }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}}ddd       y# 1 sw Y   yxY w)z7WB1: Verify env var fallback names match other modules.zpg://envzhttps://env-qdrantzenv-keyzredis://env)GENESIS_PG_DSNGENESIS_QDRANT_URLGENESIS_QDRANT_API_KEYGENESIS_REDIS_URLr}   )z/%(py2)s
{%(py2)s = %(py0)s._pg_dsn
} == %(py5)srw   ry   r   r|   assert %(py7)spy7N)z3%(py2)s
{%(py2)s = %(py0)s._qdrant_url
} == %(py5)s)z7%(py2)s
{%(py2)s = %(py0)s._qdrant_api_key
} == %(py5)s)z2%(py2)s
{%(py2)s = %(py0)s._redis_url
} == %(py5)s)r
   r   r   r   r   r   r   r   r   r   r   r   r   r   r   _qdrant_api_keyr   )r8   rw   r   r   r   r   @py_format8s          r0   test_wb1_env_var_fallback_namesz7TestStory601Constructor.test_wb1_env_var_fallback_names   s   ZZ

("6&/!.	%
  
	2 #$B::++:++++:++++++2+++2+++:++++++++++>>9%99>%99999>%999999929992999>999%99999999%%22%2222%22222222222222%2222222222==1M1=M1111=M11111121112111=111M1111111
	2 
	2 
	2s   L+MM'c                X	   |j                   }d}||u}|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }d}||u}|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }d}||u}|st        j                  d|fd	||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }d}||u }|st        j                  d
|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }d}||u }|st        j                  d
|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|j                  }d}||u }|st        j                  d
|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)z>WB2: Verify all three backends (PG, Qdrant, Redis) configured.Nrt   )z3%(py2)s
{%(py2)s = %(py0)s._pg_dsn
} is not %(py5)sr1   r   r   r   )z7%(py2)s
{%(py2)s = %(py0)s._qdrant_url
} is not %(py5)s)z6%(py2)s
{%(py2)s = %(py0)s._redis_url
} is not %(py5)sis)z0%(py2)s
{%(py2)s = %(py0)s._pg_pool
} is %(py5)s)z6%(py2)s
{%(py2)s = %(py0)s._qdrant_client
} is %(py5)s)z5%(py2)s
{%(py2)s = %(py0)s._redis_client
} is %(py5)s)r   r   r   r   r   r   r   r   r   r   r   rl   rm   rn   )r8   r1   r   r   r   r   r   s          r0   &test_wb2_all_three_backends_configuredz>TestStory601Constructor.test_wb2_all_three_backends_configured   s    "".$."$...."$......{...{..."...$.......&&2d2&d2222&d222222{222{222&222d2222222%%1T1%T1111%T111111{111{111%111T1111111##+t+#t++++#t++++++{+++{+++#+++t+++++++))1T1)T1111)T111111{111{111)111T1111111((0D0(D0000(D000000{000{000(000D0000000r/   Nr@   Noner1   r   r@   r   )	rB   rC   rD   rE   r   r   r   r   r   r.   r/   r0   rr   rr      s>    </
(
6,
6	
62
1,
1	
1r/   rr   c                     e Zd ZdZej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Zej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Zej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Z	ej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Z
ej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Zy)	TestStory602CreateTenantz Story 6.02: Tenant provisioning.c                  K   t        ||||       |j                  t        t        j                         d{   }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}}y7 w)	z.BB1: Create new tenant -- assert returns True.NTr   z%(py0)s is %(py3)sresultrx   r{   r|   rp   r   rN   r   STARTERr   r   r   r   r   r   r   r   )
r8   r1   rV   rc   rj   r   r   r   r   r   s
             r0   'test_bb1_create_new_tenant_returns_truez@TestStory602CreateTenant.test_bb1_create_new_tenant_returns_true   s      	 \;
S"00<;O;OPPv~vvv Qs   6C%C#B+C%c                x  K   t        ||||       |j                  t        t        j                         d{   }|j                  t        t        j                         d{   }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }	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  }	dd|	iz  }
t        t        j                  |
            dx}}y7 |7 Sw)
zBBB2: Create same tenant twice -- assert returns True (idempotent).NTr   r   result1rx   r{   r|   result2r   )r8   r1   rV   rc   rj   r   r   r   r   r   r   s              r0   ,test_bb2_create_same_tenant_twice_idempotentzETestStory602CreateTenant.test_bb2_create_same_tenant_twice_idempotent  s     	 \;
S#11(L<P<PQQ#11(L<P<PQQw$w$ww$w$w$ww$ RQs"   6F:F4+F:$F7%EF:7F:c                ,  K   t        ||||       |j                  t        t        j                         d{    |j
                  }|j                  j                  }|D cg c]  }dt        |      v r| }}t        |      }	d}
|	|
kD  }|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7 Tc c}w w)z<BB3: After creation, verify PG row exists with correct tier.NINSERT INTO rlm_tenantsr   >z/%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} > %(py6)sleninsert_callsry   r   rz   r   assert %(py8)spy8)rp   r   rN   r   PROFESSIONALrS   rK   call_args_listrM   r   r   r   r   r   r   r   r   r   )r8   r1   rV   rc   rj   rU   callscr   r   @py_assert5r   r   @py_format9s                 r0   %test_bb3_after_creation_pg_row_existsz>TestStory602CreateTenant.test_bb3_after_creation_pg_row_exists  s     	 \;
S'',2K2KLLL &&++
(CF2 
 
 < $1$ 1$$$$ 1$$$$$$s$$$s$$$$$$<$$$<$$$ $$$1$$$$$$$ 	M

s"   6FF*F#F9DFFc                  K   t        ||||       |j                  t        t        j                         d{    |j
                  }|j                  j                  }|D cg c]  }dt        |      v rt        |       }}t        |      }	d}
|	|
kD  }|st        j                  d|fd|	|
f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      t        j                  |
      dz  }d	d
|iz  }t!        t        j"                  |            dx}	x}}
d |D        }t%        |      }|sddt        j                         v st        j                  t$              rt        j                  t$              ndt        j                  |      t        j                  |      dz  }t!        t        j"                  |            dx}}y7 c c}w w)zAWB1: Verify RLS policy SQL uses current_setting('app.tenant_id').Ncurrent_settingr   r   r   r   	rls_callsr   r   r   c              3  $   K   | ]  }d |v  
 yw)zapp.tenant_idNr.   ).0ss     r0   	<genexpr>zTTestStory602CreateTenant.test_wb1_rls_policy_uses_current_setting.<locals>.<genexpr>3  s     ;A?a';   ,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}anyr   )rp   r   rN   r   r   rS   rK   r   rM   r   r   r   r   r   r   r   r   r   r   )r8   r1   rV   rc   rj   rU   r   r   r   r   r   r   r   r   r   r   r   s                    r0   (test_wb1_rls_policy_uses_current_settingzATestStory602CreateTenant.test_wb1_rls_policy_uses_current_setting#  sq     	 \;
S'',2F2FGGG&&++!
 CF* F
	 
 9~!!~!!!!~!!!!!!s!!!s!!!!!!9!!!9!!!~!!!!!!!!!!;;;s;;;;;;;;;s;;;s;;;;;;;;;;;;;; 	H
s"   6II*I#IGIIc                  K   t        ||||       |j                  t        t        j                         d{    |j
                  j                          |j
                  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7 w)
zBWB2: Verify Qdrant collection payload index created for tenant_id.N   
field_namerJ   r}   z%(py1)s == %(py4)sr   r   r   r   )rp   r   rN   r   r   r\   assert_called	call_argsr   r   r   r   r   )r8   r1   rV   rc   rj   call_kwargs@py_assert0r   r   r   r   s              r0   %test_wb2_qdrant_payload_index_createdz>TestStory602CreateTenant.test_wb2_qdrant_payload_index_created5  s      	 \;
S'',2F2FGGG((668!66@@1~l+:{:+{::::+{:::+:::{:::::::	 	Hs   6C3C1B9C3N
r1   r   rV   r   rc   r   rj   r   r@   r   )rB   rC   rD   rE   r   markasyncior   r   r   r   r   r.   r/   r0   r   r      sk   *[[,<E,5 
  [[	,	<E		,5	 
	 	 [[%,%<E%%,5% 
% %" [[<,<<E<<,5< 
< <" [[
;,
;<E
;
;,5
; 

; 
;r/   r   c                     e Zd ZdZej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Zej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Zej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Z	ej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Z
ej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Zy)	TestStory603VerifyIsolationz0Story 6.03: Cross-tenant isolation verification.c                <  K   t        ||||       |j                  }g |j                  _        g df|j                  _        d|j
                  _        |j                  t        t               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}}y7 w)	z<BB1: Two different tenants -- assert isolation returns True.NTr   r   r   rx   r{   r|   rp   rS   rL   rI   r`   rf   r   rN   TENANT_Br   r   r   r   r   r   r   r   r8   r1   rV   rc   rj   rU   r   r   r   r   r   s              r0   .test_bb1_two_different_tenants_isolation_holdszJTestStory603VerifyIsolation.test_bb1_two_different_tenants_isolation_holdsJ  s      	 \;
S &&"$

+-t*'&*
#"33HhGGv~vvv H   A-D/D0B+Dc                <  K   t        ||||       |j                  }g |j                  _        g df|j                  _        d|j
                  _        |j                  t        t               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}}y7 w)	z0BB2: Same tenant (A == A) -- still returns True.NTr   r   r   rx   r{   r|   )rp   rS   rL   rI   r`   rf   r   rN   r   r   r   r   r   r   r   r   r   s              r0   !test_bb2_same_tenant_returns_truez=TestStory603VerifyIsolation.test_bb2_same_tenant_returns_true]        	 \;
S&&"$

+-t*'&*
#"33HhGGv~vvv Hr   c                  K   t        ||||       |j                  }ddig|j                  _        |j	                  t
        t               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}}y7 w)z:BB3: If PG returns rows for wrong tenant, isolation fails.contentleaked_dataNFr   r   r   rx   r{   r|   )rp   rS   rL   rI   r   rN   r   r   r   r   r   r   r   r   r   r   s              r0   test_bb3_pg_breach_detectedz7TestStory603VerifyIsolation.test_bb3_pg_breach_detectedl  s      	 \;
S&&$-}#=">

"33HhGGvvvv Hs   AC;C9B+C;c                  K   t        ||||       |j                  }g |j                  _        g df|j                  _        d|j
                  _        |j                  t        t               d{    |j                  j                  }|D cg c]  }dt        |      v rt        |       }}t        |      }	d}
|	|
kD  }|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7 Qc c}w w)zAWB1: Verify PostgreSQL query sets app.tenant_id session variable.NzSET LOCAL app.tenant_idr   r   r   r   	set_callsr   r   r   rp   rS   rL   rI   r`   rf   r   rN   r   rK   r   rM   r   r   r   r   r   r   r   r   r   )r8   r1   rV   rc   rj   rU   r   r   r	  r   r   r   r   r   s                 r0   test_wb1_pg_sets_tenant_contextz;TestStory603VerifyIsolation.test_wb1_pg_sets_tenant_contextz  s5     	 \;
S&&"$

+-t*'&*
#**8X>>>++!
(CF2 F
	 
 9~!!~!!!!~!!!!!!s!!!s!!!!!!9!!!9!!!~!!!!!!!!!! 	?
%   A-G/G 0GG-DGGc                  K   t        ||||       |j                  }g |j                  _        g df|j                  _        d|j
                  _        |j                  t        t               d{    |j                  j                  }|D cg c]  }dt        |      v rt        |       }}t        |      }	d}
|	|
kD  }|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7 Qc c}w w)z4WB2: Verify test data cleanup runs in finally block.NzDELETE FROMr   r   r   r   delete_callsr   r   r   r
  )r8   r1   rV   rc   rj   rU   r   r   r  r   r   r   r   r   s                 r0   %test_wb2_test_data_cleanup_in_finallyzATestStory603VerifyIsolation.test_wb2_test_data_cleanup_in_finally  s;     	 \;
S&&"$

+-t*'&*
#**8X>>> ++!
A& F
 
 < $1$ 1$$$$ 1$$$$$$s$$$s$$$$$$<$$$<$$$ $$$1$$$$$$$ 	?
r  Nr   )rB   rC   rD   rE   r   r   r   r   r  r  r  r  r.   r/   r0   r   r   G  sk   :[[,<E,5 
 $ [[,<E,5 
  [[,<E,5 
  [[","<E"",5" 
" "( [[%,%<E%%,5% 
% %r/   r   c                     e Zd ZdZej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Zej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Zej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Z	ej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Z
ej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Zy)	TestStory604DeleteTenantDatazCStory 6.04: Tenant data deletion with optional cryptographic shred.c                R  K   t        ||||       |j                  }d|j                  _        ddt         ddt         dgf|j
                  _        d|j                  _        t               }d|_        ||j                  _        |j                  t               d{   }|d	   }d}	||	kD  }
|
slt        j                  d
|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	|d   }d}	||	kD  }
|
slt        j                  d
|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	|d   }d}	||	kD  }
|
slt        j                  d
|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	y7 w)z>BB1: Create tenant with data, delete -- assert all counts > 0.rG   r   rlm:z:key1z:key2   rX   N
pg_deletedr   z%(py1)s > %(py4)sr   r   r   qdrant_deletedredis_deletedrp   rS   rK   rI   rN   rg   r_   r	   r^   r   r   r   r   r   r   r8   r1   rV   rc   rj   rU   rb   r   r   r   r   r   r   s                r0   %test_bb1_delete_tenant_returns_countszBTestStory604DeleteTenantData.test_bb1_delete_tenant_returns_counts  s     	 \;
S&&$.!()8*E"8*E",
 (
$ *+
& {)5&"55h??l#'a'#a''''#a'''#'''a'''''''&'+!+'!++++'!+++'+++!+++++++o&**&****&***&********** @   BH'H$FH'c                :  K   t        ||||       |j                  }d|j                  _        dg f|j                  _        d|j
                  _        t               }d|_        ||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}}
|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7 w)z:BB2: Delete non-existent tenant -- assert all counts == 0.DELETE 0r   Nr  r}   r   r   r   r   r  r  )rp   rS   rK   rI   rg   r_   r	   r^   r   r   r   r   r   r   r   )r8   r1   rV   rc   rj   rU   rb   non_existentr   r   r   r   r   r   s                 r0   +test_bb2_delete_nonexistent_tenant_no_errorzHTestStory604DeleteTenantData.test_bb2_delete_nonexistent_tenant_no_error  s     	 \;
S&&$.!()2w
$)*
& {)5&w"55lCCl#(q(#q((((#q(((#(((q(((((((&',1,'1,,,,'1,,,',,,1,,,,,,,o&+!+&!++++&!+++&+++!+++++++ Ds   BHHFHc                b  K   t        ||||       |j                  }d|j                  _        ddgf|j                  _        d|j
                  _        t               }d|_        ||j                  _        |j                  t               d{    |j
                  j                          y7 w)z5BB3: After delete, read memories -- assert 0 results.DELETE 3r   k1r   r  N)rp   rS   rK   rI   rg   r_   r	   r^   r   rN   r   )r8   r1   rV   rc   rj   rU   rb   s          r0   %test_bb3_after_delete_no_data_remainszBTestStory604DeleteTenantData.test_bb3_after_delete_no_data_remains  s      	 \;
S&&$.!()D6{
$)*
& {)5&,,X666 	((* 	7s   BB/B- B/c           	     F  K   t        ||||       dt         ddt         dg}d|f|j                  _        d|j                  _        |j
                  }d|j                  _        t               }d|_        ||j                  _        |j                  t               d{    |j                  j                          |j                  j                  }t        t              }	t        |      }
|	|
v }|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"                  t              rt        j$                  t              ndt        j$                  |	      d
t        j                          v st        j"                  t              rt        j$                  t              nd
dt        j                          v st        j"                  |      rt        j$                  |      ndt        j$                  |
      dz  }dd|iz  }t'        t        j(                  |            dx}	x}}
 |j                  j*                  |  y7 w)z5WB1: Verify Redis SCAN + DEL pattern for key cleanup.r  z:mem1z:mem2r   r  r  Nin)zN%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} in %(py8)s
{%(py8)s = %(py5)s(%(py6)s)
}rM   rN   scan_kwargs)ry   r   rz   r|   r   r   zassert %(py10)spy10)rp   rN   rg   rI   r_   rS   rK   r	   r^   r   r   r   rM   r   r   r   r   r   r   r   r   assert_called_once_with)r8   r1   rV   rc   rj   keysrU   rb   r(  r   @py_assert7r   r   @py_format11s                 r0   test_wb1_redis_scan_del_patternz<TestStory604DeleteTenantData.test_wb1_redis_scan_del_pattern  s     	 \;
Sxj&$xj(>?()4y
$)*
&&&$.! {)5&,,X666 	%%' oo//8}0K 00} 00000} 0000000s000s00000080008000}000000000000000K000K000 00000000 	2
1148 	7s   BJ! J!G>J!c                @  K   t        ||||       |j                  }d|j                  _        dg f|j                  _        t               }d|_        ||j                  _        |j                  t        d       d{    |j                  j                  D cg c]  }t        |       }}t        d t        |      D        d      }	t        d t        |      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}|
|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}}|	|
k  }|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7 c c}w w)zBWB2: With cryptographic_shred=True, verify UPDATE precedes DELETE.rG   r   Tcryptographic_shredNc              3  0   K   | ]  \  }}d |v s|  yw)UPDATENr.   r   ir   s      r0   r   zeTestStory604DeleteTenantData.test_wb2_cryptographic_shred_overwrites_before_delete.<locals>.<genexpr>       =41ax1}Q=   c              3  0   K   | ]  \  }}d |v s|  yw)DELETENr.   r4  s      r0   r   zeTestStory604DeleteTenantData.test_wb2_cryptographic_shred_overwrites_before_delete.<locals>.<genexpr>  r6  r7  rt   rv   
update_idxrx   r{   r|   
delete_idx)<)z%(py0)s < %(py2)sry   r   z"UPDATE (shred) must precede DELETEz
>assert %(py4)sr   )rp   rS   rK   rI   rg   r	   r^   r   rN   r   rM   next	enumerater   r   r   r   r   r   r   r   _format_assertmsg)r8   r1   rV   rc   rj   rU   rb   r   r   r:  r;  r   r   r   r   @py_format3r   s                    r0   5test_wb2_cryptographic_shred_overwrites_before_deletezRTestStory604DeleteTenantData.test_wb2_cryptographic_shred_overwrites_before_delete	  s     	 \;
S&&$.!()2w
$ {)5&,,X4,PPP "&!<!<=AQ===9U+=t

 =9U+=t

 "&%z%%%%z%%%%%%z%%%z%%%%%%%%%%!%%z%%%%z%%%%%%z%%%z%%%%%%%%%%J&LLLzJLLLLLLzLLLzLLLLLLJLLLJLLLL(LLLLLLL 	Q >s%   A;L=L>LL,I+LLNr   )rB   rC   rD   rE   r   r   r   r  r   r$  r.  rB  r.   r/   r0   r  r    sr   M[[+,+<E++,5+ 
+ +. [[,,,<E,,,5, 
, ,( [[+,+<E++,5+ 
+ +& [[9,9<E99,59 
9 94 [[M,M<EMM,5M 
M Mr/   r  c                  n   e Zd ZdZej
                  j                  	 	 	 	 	 	 dd       Zej
                  j                  	 	 	 	 	 	 dd       Zej
                  j                  	 	 	 	 	 	 dd       Z	ej
                  j                  	 	 	 	 	 	 dd       Z
ej
                  j                  	 	 	 	 	 	 dd       Zy)	TestStory605RlsPolicyz2Story 6.05: Row-Level Security policy application.c                   K   ||_         |j                  }d|j                  _        |j	                  d       d{    y7 w)z7BB1: Apply policy to test table -- assert no SQL error.Nrlm_memoriesrl   rS   rK   rI   _apply_rls_policyr8   r1   rV   rU   s       r0   "test_bb1_apply_policy_no_sql_errorz8TestStory605RlsPolicy.test_bb1_apply_policy_no_sql_error-  s=     
  ,&&$(! ++N;;;s   9AAAc                  K   ||_         |j                  }g |j                  _        |j	                         4 d{    |j                  |t               d{    |j                  dt         d       d{   }dd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7 e7 J7 ,7 # 1 d{  7  sw Y   0xY ww)z;BB2: Insert row, set wrong tenant context, query -- 0 rows.NzSELECT * FROM z
 WHERE 1=1r   r}   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sr   rowsr   r   r   )rl   rS   rL   rI   rP   _set_tenant_contextr   r   r   r   r   r   r   r   r   r   r   )
r8   r1   rV   rU   rM  r   r   r   r   r   s
             r0   $test_bb2_wrong_tenant_sees_zero_rowsz:TestStory605RlsPolicy.test_bb2_wrong_tenant_sees_zero_rows9  s0    
  ,&&"$

##% 	O 	O11$AAAn\N*$MNND	O 	O 4yAyA~yAss44yA		OAN	O 	O 	O 	Osi   9GF GF,F# F,:F&;F,?G
F)DG#F,&F,)G,F?2F53F?:Gc                   K   ||_         |j                  }d|j                  _        |j	                  d       d{    |j	                  d       d{    y7 7 w)z=BB3: Apply same policy twice -- assert no error (idempotent).NrF  rG  rI  s       r0   +test_bb3_apply_same_policy_twice_idempotentzATestStory605RlsPolicy.test_bb3_apply_same_policy_twice_idempotentH  sX     
  ,&&$(!++N;;;++N;;; 	<;s!   9AAAAAAc                @  K   ||_         |j                  }d|j                  _        |j	                  d       d{    |j                  j
                  D cg c]  }t        |       }}|D cg c]	  }d|v s| }}t        |      }d}||kD  }	|	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7 Vc c}w c c}w w)z+WB1: Verify SQL uses IF NOT EXISTS pattern.NrF  zIF NOT EXISTSr   r   r   r   do_block_callsr   r   r   )rl   rS   rK   rI   rH  r   rM   r   r   r   r   r   r   r   r   r   )r8   r1   rV   rU   r   r   rS  r   r   r   r   r   s               r0   )test_wb1_uses_create_policy_if_not_existsz?TestStory605RlsPolicy.test_wb1_uses_create_policy_if_not_existsU  s    
  ,&&$(!++N;;;!%!<!<=AQ==%*Co.B!CC>"&Q&"Q&&&&"Q&&&&&&s&&&s&&&&&&>&&&>&&&"&&&Q&&&&&&&	 	<=Cs4   9FFFF*F0	F:F>DF
Fc                  K   ||_         |j                  }d|j                  _        |j	                  |t
               d{    |j                  j                  D cg c]  }t        |       }}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}}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}x}}	y7 c c}w w)z0WB2: Verify SET LOCAL used (transaction-scoped).Nc              3  $   K   | ]  }d |v  
 yw)	SET LOCALNr.   r   r   s     r0   r   zHTestStory605RlsPolicy.test_wb2_uses_set_local_not_set.<locals>.<genexpr>p  s     3;!#3r   r   r   r   c              3  0   K   | ]  }d |v xr d|v  yw)zSET rW  Nr.   rX  s     r0   r   zHTestStory605RlsPolicy.test_wb2_uses_set_local_not_set.<locals>.<genexpr>r  s)      
 aK0Kq00
s   z0assert not %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
})rl   rS   rK   rI   rN  rN   r   rM   r   r   r   r   r   r   r   r   )r8   r1   rV   rU   r   r   r   r   r   r   r   s              r0   test_wb2_uses_set_local_not_setz5TestStory605RlsPolicy.test_wb2_uses_set_local_not_setd  s    
  ,&&$(!--dH===!%!<!<=AQ==3U33s333333333s333s33333333333333

 	
3 
 
 	
 
 
 	
 
 	
 
6	
 	
   	
 	
 
	  	
 	
 
	
 	
 	
 
	
 	
 	
 	
 	
 	
 	
 	>=s$   >G GGG/E'GGN)r1   r   rV   r   r@   r   )rB   rC   rD   rE   r   r   r   rJ  rO  rQ  rT  rZ  r.   r/   r0   rD  rD  *  s   <[[	<,	<<E	<		< 	< [[,<E	  [[	<,	<<E	<		< 	< [[','<E'	' ' [[
,
<E
	
 
r/   rD  c                      e Zd ZdZ	 	 	 	 ddZ	 	 	 	 ddZ	 	 	 	 ddZej                  j                  	 	 	 	 	 	 d	d       Z
ej                  j                  	 	 	 	 	 	 d	d       Zy)
TestStory606QdrantIsolationz2Story 6.06: Qdrant payload-based tenant filtering.c                   |j                  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  }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}
}	|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   d   d   }t        t              }
||
k(  }|st        j                  d
|fd||
f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}x}}
y)zCBB1: Filter construction includes must clause with tenant_id match.mustr&  z%(py1)s in %(py3)sfiltr   rz   r{   r|   Nr   r}   )z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)sr   ry   r   r   r   assert %(py9)spy9r   keyrJ   r   r   r   r   r   valuez0%(py1)s == %(py6)s
{%(py6)s = %(py3)s(%(py4)s)
}rM   rN   r   rz   r   r   r   r   )_build_tenant_filterrN   r   r   r   r   r   r   r   r   r   rM   )r8   r1   r`  r   r   r   r   r   r   @py_assert6r   r   @py_format10r   r   r   s                   r0   ,test_bb1_filter_construction_has_must_clausezHTestStory606QdrantIsolation.test_bb1_filter_construction_has_must_clause  s    //9v~vv<%s< %A% A%%%% A%%%%%%s%%%s%%%<%%% %%%A%%%%%%%F|Au%44%4444%444%4444444444F|Aw'0ACMA0MAAAA0MAAA0AAAAAACAAACAAAAAAAAAAAAMAAAAAAAr/   c                   |j                  t              }|j                  t              }||k7  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            d}|d	   d
   d   d   }t        t              }||k(  }	|	st        j                  d|	fd||f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}|d	   d
   d   d   }t        t              }||k(  }	|	st        j                  d|	fd||f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }
dd|
iz  }t        t        j                  |            dx}x}	}y)z+BB2: Two tenants produce different filters.!=)z%(py0)s != %(py2)sfilter_afilter_br=  assert %(py4)sr   Nr^  r   r   rf  r}   rg  rM   rN   rh  r   r   r   )ri  rN   r   r   r   r   r   r   r   r   r   rM   )r8   r1   rp  rq  r   rA  r   r   r   r   r   r   s               r0   ,test_bb2_different_tenants_different_filterszHTestStory606QdrantIsolation.test_bb2_different_tenants_different_filters  s    33H=33H=8####x8######x###x######8###8#######"7+G4EHE4EEEE4EEE4EEEEEEEEEEEEEEEHEEEHEEEEEEEEEE"7+G4EHE4EEEE4EEE4EEEEEEEEEEEEEEEHEEEHEEEEEEEEEEr/   c                `    |j                  t              }|d   d   d   d   }t        |       y)z3BB3: Filter value is string representation of UUID.r^  r   r   rf  N)ri  rN   r   )r8   r1   r`  rf  s       r0   $test_bb3_filter_value_is_string_uuidz@TestStory606QdrantIsolation.test_bb3_filter_value_is_string_uuid  s2     //9VQ(1Ur/   c                n  K   ||_         t        d|      5  |j                          d{    ddd       |j                  j	                          |j                  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   }|t        k(  }|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7 z# 1 sw Y   zxY ww)z=WB1: Verify create_payload_index called with tenant_id field.z6core.rlm.partitioning.TenantPartitioner._ensure_qdrantrH   Nr   r   rJ   r}   r   r   r   r   collection_namez%(py1)s == %(py3)sr   ra  r{   r|   )rm   r
   _ensure_qdrant_collectionr\   assert_called_oncer   r   r   r   r   r   r   r   r   r   )r8   r1   rc   r   r   r   r   r   r   r   r   s              r0   *test_wb1_create_payload_index_on_tenant_idzFTestStory606QdrantIsolation.test_wb1_create_payload_index_on_tenant_id  s,    
 &1"K!,. 	:77999	: 	((;;=!66@@C<(7K7(K7777(K777(777K7777777,-B-1BBBBB-1BBBB-BBBBBB1BBBB1BBBBBBBB :	: 	:s,   F5F(F%F(E5F5%F((F2-F5c                L  K   ||_         t               }d|_        ||j                  _        |j	                  t
               d{    |j                  j                          |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  }d	d
|iz  }t        t        j                   |            dx}}y7 ݭw)z<WB2: Verify delete_tenant_qdrant_vectors uses tenant filter.   Nr   points_selectorr&  r_  r   ra  r{   r|   )rm   r	   r^   rI   _delete_tenant_qdrant_vectorsrN   r_   rz  r   r   r   r   r   r   r   r   r   )	r8   r1   rc   rb   r   r   r   r   r   s	            r0   "test_wb2_delete_uses_tenant_filterz>TestStory606QdrantIsolation.test_wb2_delete_uses_tenant_filter  s     
 &1" {)5&77AAA--/&&003	 - I---- I--- ------I---I------- 	Bs   AD$D"CD$Nr   )r1   r   rc   r   r@   r   )rB   rC   rD   rE   rl  rs  ru  r   r   r   r{  r  r.   r/   r0   r\  r\  |  s    <B,B	BF,F	F,	 [[C,C;DC	C C [[.,.;D.	. .r/   r\  c                     e Zd ZdZ	 	 	 	 ddZej                  j                  	 	 	 	 	 	 d	d       Zej                  j                  	 	 	 	 	 	 d	d       Z	ej                  j                  	 	 	 	 	 	 d	d       Z
	 	 	 	 ddZy)
TestStory607RedisIsolationz:Story 6.07: Redis keyspace prefixing for tenant isolation.c                   |j                  t        d      }dt         d}||k(  }|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)z)BB1: _tenant_key produces correct format.quotar  z:quotar}   z%(py0)s == %(py2)sre  expectedr=  rr  r   N)
_tenant_keyrN   r   r   r   r   r   r   r   r   )r8   r1   re  r  r   rA  r   s          r0   test_bb1_tenant_key_formatz5TestStory607RedisIsolation.test_bb1_tenant_key_format  s     %%h8(6*hshsshhr/   c                  K   ||_         t        d      D cg c]  }dt         d|  }}d|f|j                  _        |j                  t               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c c}w 7 w)z@BB2: Scan after writing 5 keys -- assert returns exactly 5 keys.r}  r  z:memr   Nr}   rL  r   r   r   r   r   )rn   rangerN   rg   rI   _scan_tenant_keysr   r   r   r   r   r   r   r   r   )r8   r1   rj   r5  r+  r   r   r   r   r   r   s              r0   1test_bb2_scan_after_writing_returns_correct_countzLTestStory607RedisIsolation.test_bb2_scan_after_writing_returns_correct_count  s     
 %/!.3Ah
)*d8*D$
 
 )*4y
$"44X>>6{a{a{ass66{a

 ?s   E5E--E5E2DE5c                  K   ||_         dg f|j                  _        |j                  t	                      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7 w)	z?BB3: Scan for non-existent tenant -- assert returns empty list.r   Nr}   )z%(py0)s == %(py3)sr   rx   r{   r|   )rn   rg   rI   r  r   r   r   r   r   r   r   r   r   )r8   r1   rj   r   r   r   r   r   s           r0   .test_bb3_scan_nonexistent_tenant_returns_emptyzITestStory607RedisIsolation.test_bb3_scan_nonexistent_tenant_returns_empty  s     
 %/!()2w
$"44UW==v|vvv >s   7C'C%B,C'c                0  K   ||_         dg f|j                  _        |j                  t               d{    |j                  j                          |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  }d	d
|iz  }t        t        j                  |            dx}}|d   }d}||kD  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}y7 ^w)z7WB1: Verify SCAN used with COUNT for batched iteration.r   Nr   r^   r&  r_  r   ra  r{   r|   r   r  r   r   r   )rn   rg   rI   r  rN   r   r   r   r   r   r   r   r   r   r   )r8   r1   rj   r   r   r   r   r   r   r   r   s              r0   .test_wb1_scan_uses_count_for_batched_iterationzITestStory607RedisIsolation.test_wb1_scan_uses_count_for_batched_iteration  s    
 %/!()2w
$++H555%%' oo//2%w+%%%%w+%%%w%%%%%%+%%%+%%%%%%%7#'a'#a''''#a'''#'''a''''''' 	6s   3FFEFc                   |j                  t        d      }|j                  d      }|d   }|t        k(  }|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        t              }||k(  }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  t              rt	        j                  t              nddt        j                         v st	        j                  t              rt	        j                  t              nd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;WB2: Verify prefix format matches rlm:{tenant_id}: pattern.memories:r   r}   rx  r   ra  r{   r|   Nr   rg  rM   rN   rh  r   r   r  r   r   r   r   )r  rN   splitr   r   r   r   r   r   r   r   r   rM   )r8   r1   re  partsr   r   r   r   r   r   r   r   r   s                r0   &test_wb2_prefix_format_matches_patternzATestStory607RedisIsolation.test_wb2_prefix_format_matches_pattern  sb    %%h
;		#Qx+x+++++x++++x++++++++++++++++++Qx(3x=(x=((((x=(((x((((((3(((3((((((x(((x(((=(((((((Qx%:%x:%%%%x:%%%x%%%:%%%%%%%r/   Nr   )r1   r   rj   r   r@   r   )rB   rC   rD   rE   r  r   r   r   r  r  r  r  r.   r/   r0   r  r    s    D,	 [[ , :C 	    [[,:C	  [[(,(:C(	( (&,&	&r/   r  c                  P    e Zd ZdZddZddZddZddZddZddZ	ddZ
dd	Zy
)TestStory608Schemaz/Story 6.08: RlmMemory and RlmTenant ORM models.c                Z   ddl m} d}||u}|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd	|iz  }t        t        j                  |            dx}}y)
z@BB1: Import RlmMemory from core.models.schema -- no ImportError.r   	RlmMemoryNrt   rv   r  rx   r{   r|   )
core.models.schemar  r   r   r   r   r   r   r   r   )r8   r  r   r   r   r   s         r0   test_bb1_import_rlm_memoryz-TestStory608Schema.test_bb1_import_rlm_memory  s^    0 $$y$$$$y$$$$$$y$$$y$$$$$$$$$$r/   c                   ddl m}  |t        dt        j                  d      j                         dd      }|j                  }|t        k(  }|st        j                  d|fd|t        f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      d
t        j                         v st        j                  t              rt        j                  t              nd
dz  }dd|iz  }t        t        j                  |            dx}}|j                  }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|j                   }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)z4BB2: Create RlmMemory instance with required fields.r   r  ztest memorys   test memorytest)rJ   r  content_hashsourcedomainr}   )z1%(py2)s
{%(py2)s = %(py0)s.tenant_id
} == %(py4)smemrN   r   r   r   N)z/%(py2)s
{%(py2)s = %(py0)s.content
} == %(py5)sr   r   r   )z.%(py2)s
{%(py2)s = %(py0)s.source
} == %(py5)s)r  r  rN   hashlibsha256	hexdigestrJ   r   r   r   r   r   r   r   r   r  r  )
r8   r  r  r   r   r   r   r   r   r   s
             r0   -test_bb2_create_instance_with_required_fieldsz@TestStory608Schema.test_bb2_create_instance_with_required_fields  s   0! 7AAC
 }}(}((((}((((((s(((s(((}(((((((((((((((({{+m+{m++++{m++++++s+++s+++{+++m+++++++zz#V#zV####zV######s###s###z###V#######r/   c                   ddl m} |j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            d
x}x}}y
)z,BB3: Verify __tablename__ is 'rlm_memories'.r   r  rF  r}   z5%(py2)s
{%(py2)s = %(py0)s.__tablename__
} == %(py5)sr  r   r   r   N)r  r  __tablename__r   r   r   r   r   r   r   r   )r8   r  r   r   r   r   r   s          r0   "test_bb3_tablename_is_rlm_memoriesz5TestStory608Schema.test_bb3_tablename_is_rlm_memories  sw    0&&8.8&.8888&.888888y888y888&888.8888888r/   c                   ddl m} |j                  D cg c]  }t        |d      r|j                  dk(  r|! }}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c c}w )z<WB1: Verify unique constraint name is 'uq_rlm_memory_dedup'.r   r  nameuq_rlm_memory_dedupr   r}   rL  r   constraintsr   r   r   Nr  r  __table_args__r   r  r   r   r   r   r   r   r   r   r   )	r8   r  r   r  r   r   r   r   r   s	            r0   test_wb1_unique_constraint_namez2TestStory608Schema.test_wb1_unique_constraint_name  s    0 //
q&!aff0E&E 
 
 ;$1$1$$$$1$$$$$$s$$$s$$$$$$;$$$;$$$$$$1$$$$$$$	
   $Ec                   ddl m} |j                  D cg c]  }t        |d      r|j                  dk(  r|! }}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c c}w )z-WB2: Verify CHECK constraint for memory_tier.r   r  r  ck_rlm_memory_tierr   r}   rL  r   checksr   r   r   Nr  )	r8   r  r   r  r   r   r   r   r   s	            r0   %test_wb2_check_constraint_memory_tierz8TestStory608Schema.test_wb2_check_constraint_memory_tier(  s    0 //
q&!aff0D&D 
 
 6{a{a{ass66{a	
r  c                   ddl m} d}t        ||      }|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t        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)	z0Verify RlmTenant has encryption_key_hash column.r   	RlmTenantencryption_key_hashr   r   r  r   N
r  r  r   r   r   r   r   r   r   r   r8   r  r   r   r   s        r0   'test_rlm_tenant_has_encryption_key_hashz:TestStory608Schema.test_rlm_tenant_has_encryption_key_hash1  s    0"78wy"788888888w888w888888y888y888"78888888888r/   c                   ddl m} d}t        ||      }|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t        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)	z#Verify RlmTenant has region column.r   r  regionr   r   r  r   Nr  r  s        r0   test_rlm_tenant_has_regionz-TestStory608Schema.test_rlm_tenant_has_region6  s~    0"*+wy(++++++++w+++w++++++y+++y+++(++++++++++r/   c                   ddl m} |j                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            d
x}x}}y
)z"Verify RlmShredAudit model exists.r   )RlmShredAuditrlm_shred_auditr}   r  r  r   r   r   N)r  r  r  r   r   r   r   r   r   r   r   )r8   r  r   r   r   r   r   s          r0   test_rlm_shred_audit_existsz.TestStory608Schema.test_rlm_shred_audit_exists;  s{    4**?.??*.?????*.???????}???}???*???.????????r/   Nr   )rB   rC   rD   rE   r  r  r  r  r  r  r  r  r.   r/   r0   r  r    s.    9%
$9
% 9
,
@r/   r  c                  8    e Zd ZdZddZddZddZddZddZy)	TestCryptographicShreddingz7Test encryption/decryption for cryptographic shredding.c                   t        j                         }t        |t              }|sddt	        j
                         v st        j                  t              rt        j                  t              nddt	        j
                         v st        j                  |      rt        j                  |      nd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)z$Key generation produces 32-byte key.z5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancere  bytes)ry   r   r   r   N    r}   rL  r   r   r   r   )r   _generate_encryption_keyr  r  r   r   r   r   r   r   r   r   r   )	r8   re  r   r   r   r   r   r   r   s	            r0   (test_generate_encryption_key_is_32_byteszCTestCryptographicShredding.test_generate_encryption_key_is_32_bytesH  s#   88:#u%%%%%%%%z%%%z%%%%%%#%%%#%%%%%%u%%%u%%%%%%%%%%3x2x2~x2ss33x2r/   c                B   t        j                         }d}t        j                  ||      }t        j                  ||      }||k(  }|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	}|j                  } |       }	||	k7  }|st	        j
                  d
|fd||	f      dt        j                         v st	        j                  |      rt	        j                  |      nddt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      t	        j                  |	      dz  }
dd|
iz  }t        t	        j                  |            d	x}x}}	y	)z/Encrypt then decrypt produces original content.z$This is sensitive tenant memory datar}   r  	decryptedoriginalr=  rr  r   Nrn  )zE%(py0)s != %(py6)s
{%(py6)s = %(py4)s
{%(py4)s = %(py2)s.encode
}()
}	encrypted)ry   r   r   r   r   r   )r   r  _encrypt_content_decrypt_contentr   r   r   r   r   r   r   r   encode)r8   re  r  r  r  r   rA  r   r   r   r   r   s               r0   test_encrypt_decrypt_roundtripz9TestCryptographicShredding.test_encrypt_decrypt_roundtripN  s3   88:9%66xE	%66y#F	H$$$$yH$$$$$$y$$$y$$$$$$H$$$H$$$$$$$$OO-O--y-----y-------y---y------H---H---O-----------r/   c                   ddl m} t        j                         }t        j                         }d}t        j                  ||      }t        j                  |      5  t        j                  ||       ddd       y# 1 sw Y   yxY w)zADecrypting with wrong key raises error (cryptographic shredding).r   )InvalidTokenzsensitive dataN)cryptography.fernetr  r   r  r  r   r   r  )r8   r  key1key2r  r  s         r0   test_wrong_key_fails_decryptz7TestCryptographicShredding.test_wrong_key_fails_decryptW  sm    4 99; 99;#%66xF	]]<( 	@..y$?	@ 	@ 	@s   A<<Bc                >   t        j                         }t        j                  |      }t        j                  |      }||k(  }|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	}||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)z#Same key always produces same hash.r}   r  hash1hash2r=  rr  r   N@   rL  r   r   r   r   )r   r  	_hash_keyr   r   r   r   r   r   r   r   r   )r8   re  r  r  r   rA  r   r   r   r   r   r   s               r0   test_key_hash_is_deterministicz9TestCryptographicShredding.test_key_hash_is_deterministicc  s#   88:!++C0!++C0~uuu5zRzRzRss55zRr/   c                (   t        j                         }t        j                         }t         j                  } ||      }t         j                  } ||      }||k7  }|st        j                  d|fd||f      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j                  t               rt        j                  t               ndt        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }	t        t        j                  |	            d	x}x}x}x}}y	)
z(Different keys produce different hashes.rn  )z%(py5)s
{%(py5)s = %(py2)s
{%(py2)s = %(py0)s._hash_key
}(%(py3)s)
} != %(py12)s
{%(py12)s = %(py9)s
{%(py9)s = %(py7)s._hash_key
}(%(py10)s)
}r   r  r  )ry   r   rz   r|   r   rd  r)  py12zassert %(py14)spy14N)r   r  r  r   r   r   r   r   r   r   r   )
r8   r  r  r   r   @py_assert8@py_assert11rj  @py_format13@py_format15s
             r0   $test_different_keys_different_hashesz?TestCryptographicShredding.test_different_keys_different_hashesk  s/    99; 99; **U*40U4E4O4OU4OPT4UU04UUUUU04UUUUUUU UUU UUU*UUUUUU4UUU4UUU0UUUUUU4EUUU4EUUU4OUUUUUUPTUUUPTUUU4UUUUUUUUUr/   Nr   )	rB   rC   rD   rE   r  r  r  r  r  r.   r/   r0   r  r  E  s     A.
@ Vr/   r  c                  8    e Zd ZdZddZddZddZddZddZy)	TestPartitioningSchemazTest SQL schema definitions.c                n   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)	z#Tenants table has UUID primary key.ztenant_id    UUID PRIMARY KEYr&  r_  r    ra  r{   r|   N)	r    r   r   r   r   r   r   r   r   r8   r   r   r   r   s        r0   +test_tenants_table_sql_has_uuid_primary_keyzBTestPartitioningSchema.test_tenants_table_sql_has_uuid_primary_keyy  sb    .F.2FFFFF.2FFFF.FFFFFF2FFFF2FFFFFFFFr/   c                n   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)	z=Memories table references rlm_tenants with ON DELETE CASCADE.z3REFERENCES rlm_tenants(tenant_id) ON DELETE CASCADEr&  r_  r   ra  r{   r|   N	r   r   r   r   r   r   r   r   r   r  s        r0   'test_memories_table_sql_has_foreign_keyz>TestPartitioningSchema.test_memories_table_sql_has_foreign_key}  sb    D]DH]]]]]DH]]]]D]]]]]]H]]]]H]]]]]]]]r/   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  }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)
z5RLS policy SQL uses current_setting('app.tenant_id').zcurrent_setting('app.tenant_id'r&  r_  r   ra  r{   r|   Nr   )
r   r   r   r   r   r   r   r   r   r   r  s        r0   $test_rls_policy_uses_current_settingz;TestPartitioningSchema.test_rls_policy_uses_current_setting  s    0N04NNNNN04NNNN0NNNNNN4NNNN4NNNNNNNN0M04MMMMM04MMMM0MMMMMM4MMMM4MMMMMMMMr/   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  }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)z0Full migration string contains all schema parts.z&CREATE TABLE IF NOT EXISTS rlm_tenantsr&  r_  r#   ra  r{   r|   Nz'CREATE TABLE IF NOT EXISTS rlm_memorieszENABLE ROW LEVEL SECURITYzCREATE POLICYr  )	r#   r   r   r   r   r   r   r   r   r  s        r0   &test_full_migration_contains_all_partsz=TestPartitioningSchema.test_full_migration_contains_all_parts  s   7I7>IIII7>III7IIIIII>III>IIIIIII8J8NJJJJ8NJJJ8JJJJJJNJJJNJJJJJJJ*<*n<<<<*n<<<*<<<<<<n<<<n<<<<<<<0.0000.000000000.000.0000000 2 N2222 N222 222222N222N2222222r/   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  }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)
z/Unique constraint on (tenant_id, content_hash).r  r&  r_  r   ra  r{   r|   Nz UNIQUE (tenant_id, content_hash)r  r  s        r0   test_unique_constraint_on_dedupz6TestPartitioningSchema.test_unique_constraint_on_dedup  s    $=$(=====$(====$======(====(========1J15JJJJJ15JJJJ1JJJJJJ5JJJJ5JJJJJJJJr/   Nr   )	rB   rC   rD   rE   r  r  r  r  r  r.   r/   r0   r  r  v  s"    &G^N
3Kr/   r  c                     e Zd ZdZej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Zej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Zej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Z	ej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Z
ej
                  j                  	 	 	 	 	 	 	 	 	 	 dd       Zy)	TestStory609Integrationz6Story 6.09: Full integration test across all backends.c                  K   t        ||||       |j                  t        t        j                         d{   }d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}}|j                  }
|
j                  j                  D cg c]  }t!        |       }}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}}|j$                  j'                          |j(                  j'                          y7 c c}w w)zFCreate tenant -> verify PG row, Qdrant ready, Redis prefix accessible.NTr   r   r   rx   r{   r|   c              3  $   K   | ]  }d |v  
 yw)r   Nr.   rX  s     r0   r   zUTestStory609Integration.test_create_tenant_provisions_all_backends.<locals>.<genexpr>  s     Da,1Dr   r   r   r   )rp   r   rN   r   
ENTERPRISEr   r   r   r   r   r   r   r   rS   rK   r   rM   r   r\   r   re   )r8   r1   rV   rc   rj   r   r   r   r   r   rU   r   pg_callsr   r   s                  r0   *test_create_tenant_provisions_all_backendszBTestStory609Integration.test_create_tenant_provisions_all_backends  sH     	 \;
S"00<;R;RSSv~vvv &&$(LL$?$?@qCF@@D8DDsDDDDDDDDDsDDDsDDDDDDDDDDDDDD 	((668 	$$& T
 As#   6HG>CHHC'HHc                <  K   t        ||||       |j                  }g |j                  _        g df|j                  _        d|j
                  _        |j                  t        t               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}}y7 w)	z<Write as A, read as B -> 0 results across PG, Qdrant, Redis.NTr   r   r   rx   r{   r|   r   r   s              r0   "test_tenant_isolation_all_backendsz:TestStory609Integration.test_tenant_isolation_all_backends  r  r   c                R  K   t        ||||       |j                  }d|j                  _        ddt         ddt         dgf|j
                  _        d|j                  _        t               }d|_        ||j                  _        |j                  t               d{   }|d	   }d}	||	kD  }
|
slt        j                  d
|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	|d   }d}	||	kD  }
|
slt        j                  d
|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	|d   }d}	||	kD  }
|
slt        j                  d
|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	y7 w)z4Create tenant with data, delete, verify 0 remaining.z	DELETE 10r   r  z:enc_keyz:initr  r}  Nr  r   r  r   r   r   r  r  r  r  s                r0   #test_delete_tenant_removes_all_dataz;TestStory609Integration.test_delete_tenant_removes_all_data  s     	 \;
S&&$/!()8*H%8*E",
 (
$ *+
& {)5&"55h??l#'a'#a''''#a'''#'''a'''''''&'+!+'!++++'!+++'+++!+++++++o&**&****&***&********** @r  c                  K   t        ||||       |j                  }d|j                  _        dg f|j                  _        t               }d|_        ||j                  _        |j                  t        d       d{    |j                  j                  D cg c]  }t        |       }}|D cg c]	  }d|v s| }	}t        |	      }
d}|
|kD  }|st        j                  d|fd|
|f      d	t        j                         v st        j                   t              rt        j"                  t              nd	d
t        j                         v st        j                   |	      rt        j"                  |	      nd
t        j"                  |
      t        j"                  |      dz  }t        j$                  d      dz   d|iz  }t'        t        j(                  |            dx}
x}}y7 mc c}w c c}w w)zADelete with shred=True, verify content overwritten before delete.r"  r   Tr0  Nr3  r   r   r   update_callsr   zShred must UPDATE before DELETEz
>assert %(py8)sr   )rp   rS   rK   rI   rg   r	   r^   r   rN   r   rM   r   r   r   r   r   r   r   r@  r   r   )r8   r1   rV   rc   rj   rU   rb   r   r   r  r   r   r   r   r   s                  r0   "test_cryptographic_shred_lifecyclez:TestStory609Integration.test_cryptographic_shred_lifecycle  sU     	 \;
S&&$.!()2w
$ {)5&,,X4,PPP!%!<!<=AQ==#(:aHM::< G1G 1$GGG 1GGGGGGsGGGsGGGGGG<GGG<GGG GGG1GGG&GGGGGGGG	 	Q=:s7   A;G7=G*>G7G-,G72	G2<G2 D+G7-
G7c                <  K   t        ||||       |j                  }g |j                  _        g df|j                  _        d|j
                  _        |j                  t        t               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}}y7 w)	z9verify_isolation(A, B) returns True for distinct tenants.NTr   r   r   rx   r{   r|   r   r   s              r0   1test_verify_isolation_passes_for_distinct_tenantszITestStory609Integration.test_verify_isolation_passes_for_distinct_tenants  r  r   Nr   )rB   rC   rD   rE   r   r   r   r  r   r  r  r  r.   r/   r0   r  r    sr   @[[','<E'',5' 
' '* [[,<E,5 
  [[+,+<E++,5+ 
+ +0 [[H,H<EHH,5H 
H H& [[,<E,5 
 r/   r  c                  (    e Zd ZdZddZddZddZy)TestContractsImportz7Verify contracts.py types used by Module 6 are correct.c                	   t        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                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}t        j                  }|j                  }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}}t        j                  }|j                  }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}}t        j                  }|j                  }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}}t        j                  }|j                  }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	)z"CustomerTier has exactly 4 values.   r}   rL  r   r   r   r   r   Nstarter)zJ%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.STARTER
}.value
} == %(py7)srb  rc  rd  professional)zO%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.PROFESSIONAL
}.value
} == %(py7)s
enterprise)zM%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.ENTERPRISE
}.value
} == %(py7)squeen)zH%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.QUEEN
}.value
} == %(py7)s)r   r   r   r   r   r   r   r   r   r   r   rf  r   r  QUEENr8   r   r   r   r   r   r   r   rj  r   rk  s              r0   "test_customer_tier_has_four_valuesz6TestContractsImport.test_customer_tier_has_four_values  s   < %A% A%%%% A%%%%%%s%%%s%%%%%%<%%%<%%% %%%A%%%%%%%##6#))6Y6)Y6666)Y666666|666|666#666)666Y6666666((@(..@.@..@@@@..@@@@@@|@@@|@@@(@@@.@@@.@@@@@@@&&<&,,<<,<<<<,<<<<<<|<<<|<<<&<<<,<<<<<<<<<<!!2!''272'72222'7222222|222|222!222'22272222222r/   c                	   t        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                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}t        j                  }|j                  }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}}t        j                  }|j                  }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}}t        j                  }|j                  }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}}t        j                  }|j                  }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	)z MemoryTier has exactly 4 values.r  r}   rL  r   r   r   r   r   Ndiscard)zJ%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.DISCARD
}.value
} == %(py7)srb  rc  rd  working)zJ%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.WORKING
}.value
} == %(py7)sepisodic)zK%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.EPISODIC
}.value
} == %(py7)ssemantic)zK%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.SEMANTIC
}.value
} == %(py7)s)r   r   r   r   r   r   r   r   r   r   DISCARDrf  WORKINGEPISODICSEMANTICr  s              r0    test_memory_tier_has_four_valuesz4TestContractsImport.test_memory_tier_has_four_values  s   :#!#!####!######s###s######:###:######!#######!!4!''494'94444'9444444z444z444!444'44494444444!!4!''494'94444'9444444z444z444!444'44494444444""6"((6J6(J6666(J666666z666z666"666(666J6666666""6"((6J6(J6666(J666666z666z666"666(666J6666666r/   c                N   ddl }t        t              D cg c]  }|j                  d      s| }}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 )z1TenantPartitionProtocol defines required methods.r   N_r   r&  r_  methodsra  r{   r|   r   r   )inspectdirr   r   r   r   r   r   r   r   r   r   )r8   r   mr  r   r   r   r   s           r0   &test_tenant_partition_protocol_methodsz:TestContractsImport.test_tenant_partition_protocol_methods  sL   23
<<$ 
 
 )'))))')))))))))')))')))))))#.#w....#w...#......w...w.......!,!W,,,,!W,,,!,,,,,,W,,,W,,,,,,,
s   H"Nr   )rB   rC   rD   rE   r  r  r#  r.   r/   r0   r	  r	    s    A37	-r/   r	  )r@   r   )r@   r	   )r@   r   )
r1   r   ro   r   rc   r   rj   r   r@   r   )QrE   
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   r   r  r   sysdataclassesr   dataclass_fieldstypingr   r   r   unittest.mockr   r	   r
   r   uuidr   r   r   pathinsertcore.rlm.contractsr   r   r   r   r   r   r   core.rlm.partitioningr   r   r   r   r   r   r   core.rlm.partitioning_schemar   r   r   r   r    r!   r"   r#   r*   r+   r,   r-   rN   r   fixturer1   r3   rV   rc   rj   rp   rr   r   r   r  rD  r\  r  r  r  r  r  r	  r.   r/   r0   <module>r5     s  . #     	 
 2 " " C C   * +    	 	 	  =, :77  
 
  .  "  	+"	+	+ 	+ 		+
 
	+ @1 @1NH; H;^\% \%FyM yM@K
 K
d?. ?.L;& ;&D:@ :@B*V *VbK KDb bR- -r/   