
    מie              	         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
mZmZ ddlmZmZmZmZ ddlZddlZ	 ddlmZmZ ddlmZ ddlZdZ	 	 	 d/	 	 	 	 	 	 	 d0d
Z	 	 d1	 	 	 	 	 d2dZ	 	 	 	 d3	 	 	 	 	 	 	 	 	 d4dZej>                  d        Z ej>                  d        Z! G d d      Z" G d d      Z# G d d      Z$ G d d      Z%ejL                  jO                  e d       G d d             Z(ejL                  jO                  e d       G d d             Z)ejL                  jO                  e d       G d d             Z* G d d       Z+ejL                  jO                  e d       G d! d"             Z,ejL                  jO                  e d       G d# d$             Z- G d% d&      Z.e/d'k(  rFddl0Z0 e0jb                  e	jd                  d(d)e3d*d+d,gd-.      Z4 e	jj                  e4jl                         yy# e$ r d	ZY w xY w)5u  
Genesis Customer Auth — Test Suite
====================================
Module 5: Supabase Customer Auth

Black-box and white-box tests for:
  - core/auth/supabase_client.py (SupabaseAuth)
  - core/auth/middleware.py (get_current_user, require_auth)
  - api/auth/routes.py (FastAPI auth router)

Test coverage:
  BB1: sign_up returns user dict with id and email (3 tests)
  BB2: sign_in returns session with access_token (3 tests)
  BB3: get_user returns user profile from valid JWT (2 tests)
  BB4: sign_out invalidates session (2 tests)
  BB5: Invalid JWT returns 401 (2 tests)
  BB6: Missing auth header returns 401 (1 test)
  BB7: Auth routes return proper HTTP status codes (4 tests)

  WB1: Supabase SDK client initialized with correct URL/key (2 tests)
  WB2: Middleware extracts Bearer token correctly (2 tests)
  WB3: require_auth checks subscription tier (3 tests)
  WB4: ImportError raised when supabase SDK missing (1 test)

All tests mock the Supabase SDK. ZERO live API calls.
FastAPI route tests use httpx.AsyncClient with ASGITransport.

VERIFICATION_STAMP
Story: 5.05
Verified By: parallel-builder
Verified At: 2026-02-25
Tests: 26/26
Coverage: 100%
    )annotationsN)AnyDict)	AsyncMock	MagicMockpatchPropertyMockFastAPIDepends)
TestClientTFc                f    t               }| |_        ||_        |dd|_        d|_        d|_        |S )z#Return a mock Supabase User object.z	Test User)subscription_tiernamez2026-01-01T00:00:00)r   idemailuser_metadata
created_at
updated_at)uidr   tierusers       ./mnt/e/genesis-system/tests/infra/test_auth.py
_mock_userr   =   s9     ;DDGDJ/3[ID+DO+DOK    c                R    t               }| |_        ||_        d|_        d|_        |S )z&Return a mock Supabase Session object.  bearer)r   access_tokenrefresh_token
expires_in
token_type)r   r    sessions      r   _mock_sessionr$   L   s0    
 kG'G)GG!GNr   c                r    t               }t        | ||      |_        |rt               |_        |S d|_        |S )z$Return a mock Supabase AuthResponse.N)r   r   r   r$   r#   )r   r   r   include_sessionresponses        r   _mock_auth_responser(   Y   s=     {HsE40HM*9}HO @DHOr   c               #    K   t               } t               }t        |       |_        t         |_        t        j                  t
        j                  d|i      5  t        dd      5  t        d|j                        5  |  ddd       ddd       ddd       y# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   yxY ww)z8Patch supabase.create_client so no live SDK is required.return_valuesupabase-core.auth.supabase_client._SUPABASE_AVAILABLETz'core.auth.supabase_client.create_clientN)r   create_clientClientr   dictsysmodules)mock_clientmock_modules     r   mock_supabase_moduler5   j   s      +K+K ){ CK"K	CKK*k!:	; "BDI 	"@+B[B[\ "!!"	"" "" "	" 	"" "sN   AB?B3&B'=BB'
B3	B?B$ B''B0	,B33B<8B?c                    ddl m} ddl mc m} |j                  }d|_         |ddd      }| |_        | |_        ||_        |S )	z8Return a SupabaseAuth instance backed by the mocked SDK.r   SupabaseAuthNTzhttps://test.supabase.coztest-anon-keyztest-service-key)urlanon_keyservice_key)core.auth.supabase_clientr8   authsupabase_client_SUPABASE_AVAILABLE_client_admin_client)r5   r8   sc_modorig_availableclients        r   auth_clientrE   y   sQ     7..//N!%F& &F *FN/F!/FMr   c                      e Zd ZdZej
                  j                  d        Zej
                  j                  d        Zej
                  j                  d        Z	y)
TestSignUpu   BB1 — sign_up behaviour.c                  K   t               |j                  j                  _        |j                  dd       d{   }d}||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }d	d
|iz  }t        t	        j                  |            dx}}|d   d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }	dd|	iz  }
t        t	        j                  |
            dx}x}}y7 .w)u7   BB1.1 — Result dict has 'user' key with non-empty id.new@example.compassword123Nr   inz%(py1)s in %(py3)sresultpy1py3assert %(py5)spy5r   user-abc-123==z%(py1)s == %(py4)srP   py4assert %(py6)spy6)r(   r=   sign_upr+   
@pytest_ar_call_reprcompare	_saferepr@py_builtinslocals_should_repr_global_nameAssertionError_format_explanationselfrE   r5   rN   @py_assert0@py_assert2@py_format4@py_format6@py_assert3@py_format5@py_format7s              r   !test_sign_up_returns_user_with_idz,TestSignUp.test_sign_up_returns_user_with_id   s      :M9N!!))6"**+<mLLvvvf~d#5~5#~5555#~555#555~5555555 Ms   9E,E)D.E,c                  K   t        d      |j                  j                  _        |j                  dd       d{   }|d   d   }d}||k(  }|slt	        j
                  d|fd||f      t	        j                  |      t	        j                  |      d	z  }d
d|iz  }t        t	        j                  |            dx}x}}y7 w)u8   BB1.2 — Result dict has 'user' key with correct email.rI   )r   rJ   Nr   r   rU   rW   rX   rZ   r[   )	r(   r=   r\   r+   r]   r^   r_   rc   rd   	rf   rE   r5   rN   rg   rk   rh   rl   rm   s	            r   $test_sign_up_returns_user_with_emailz/TestSignUp.test_sign_up_returns_user_with_email   s      :M#:
!!))6 #**+<mLLf~g&;*;;&*;;;;;&*;;;;&;;;*;;;;;;;; Ms   ;CCB	Cc                  K   t               |j                  j                  _        |j                  ddddd       d{    |j                  j                  j                  }|d   d   }|d	   d
   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y7 w)u2   BB1.3 — Metadata dict is passed to Supabase SDK.zmeta@example.comrJ   zJane Doestarter)r   r   )metadataNr   optionsdatar   rU   rW   rX   rZ   r[   )
r(   r=   r\   r+   	call_argsr]   r^   r_   rc   rd   )
rf   rE   r5   rw   payloadrg   rk   rh   rl   rm   s
             r   (test_sign_up_with_metadata_passes_to_sdkz3TestSignUp.test_sign_up_with_metadata_passes_to_sdk   s      :M9N!!))6!!(yI " 
 	
 	

 )--55??	A,q/y!&)&1?Z?1Z????1Z???1???Z???????	
s   >C6 C4B4C6N)
__name__
__module____qualname____doc__pytestmarkasynciorn   rq   ry    r   r   rG   rG      s_    $[[6 6 [[< < [[
@ 
@r   rG   c                      e Zd ZdZej
                  j                  d        Zej
                  j                  d        Zej
                  j                  d        Z	y)
TestSignInu   BB2 — sign_in behaviour.c                  K   t               |j                  j                  _        |j	                  dd       d{   }|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}x}}y7 w)u,   BB2.1 — Result has top-level access_token.user@example.comrJ   Nr   fake-jwt-tokenrU   rW   rX   rZ   r[   )
r(   r=   sign_in_with_passwordr+   sign_inr]   r^   r_   rc   rd   rp   s	            r   !test_sign_in_returns_access_tokenz,TestSignIn.test_sign_in_returns_access_token   s      H[G\!!77D"**+=}MMn%9)99%)99999%)9999%999)99999999 Ns   9CCBCc                  K   t               |j                  j                  _        |j	                  dd       d{   }d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }d	d
|iz  }t        t        j                  |            dx}}|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}|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)u6   BB2.2 — Result has 'session' dict with token fields.r   rJ   Nr#   rK   rM   rN   rO   rR   rS   r   r   rU   rW   rX   rZ   r[   r    fake-refresh-token)r(   r=   r   r+   r   r]   r^   r_   r`   ra   rb   rc   rd   re   s              r   #test_sign_in_returns_session_objectz.TestSignIn.test_sign_in_returns_session_object   sM     H[G\!!77D"**+=}MM"yF""""yF"""y""""""F"""F"""""""i 0D4DD04DDDDD04DDDD0DDD4DDDDDDDDi 1I5II15IIIII15IIII1III5IIIIIIII Ns   9G/G,F1G/c                   K   t        d      |j                  j                  _        t	        j
                  t         d      5  |j                  dd       d{    ddd       y7 # 1 sw Y   yxY ww)u5   BB2.3 — Exception raised on authentication failure.zInvalid credentialsmatchzbad@example.com	wrongpassN)	Exceptionr=   r   side_effectr~   raisesr   rf   rE   r5   s      r   4test_sign_in_propagates_exception_on_bad_credentialsz?TestSignIn.test_sign_in_propagates_exception_on_bad_credentials   sr     
 GP!G
!!77C ]]9,AB 	F%%&7EEE	F 	FE	F 	Fs0   A A4A(A&A(	A4&A((A1-A4N)
rz   r{   r|   r}   r~   r   r   r   r   r   r   r   r   r   r      sa    $[[: : [[J J [[F Fr   r   c                      e Zd ZdZej
                  j                  d        Zej
                  j                  d        Zy)TestGetUseru   BB3 — get_user behaviour.c                  K   t               }t               |_        ||j                  j                  _        |j	                  d       d{   }|d   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd	|iz  }	t        t        j                  |	            dx}x}}|d
   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd	|iz  }	t        t        j                  |	            dx}x}}|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)u;   BB3.1 — Valid JWT returns user dict with expected fields.zvalid-jwt-tokenNr   rT   rU   rW   rX   rZ   r[   r   test@example.comr   rs   )r   r   r   r=   get_userr+   r]   r^   r_   rc   rd   )
rf   rE   r5   mock_responserN   rg   rk   rh   rl   rm   s
             r   +test_get_user_returns_profile_for_valid_jwtz7TestGetUser.test_get_user_returns_profile_for_valid_jwt   s:    
 "'\:G!!**7"++,=>>d|-~-|~----|~---|---~-------g4"44"44444"4444444"44444444)*7i7*i7777*i777*777i7777777 ?s   A	GGFGc                   K   t        d      |j                  j                  _        t	        j
                  t        d      5  |j                  d       d{    ddd       y7 # 1 sw Y   yxY ww)u0   BB3.2 — Invalid/expired JWT raises ValueError.zJWT expiredzInvalid or expired JWTr   zexpired-jwtN)r   r=   r   r   r~   r   
ValueErrorr   s      r   0test_get_user_raises_value_error_for_invalid_jwtz<TestGetUser.test_get_user_raises_value_error_for_invalid_jwt   sc     
 :C=9Q!!**6]]:-EF 	6&&}555	6 	65	6 	6s0   A A3A'A%A'	A3%A''A0,A3N)	rz   r{   r|   r}   r~   r   r   r   r   r   r   r   r   r      s?    %[[8 8 [[6 6r   r   c                      e Zd ZdZej
                  j                  d        Zej
                  j                  d        Zy)TestSignOutu   BB4 — sign_out behaviour.c                  K   d|j                   j                  _        |j                  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}}y7 w)
u+   BB4.1 — Successful sign-out returns True.Nvalid-tokenT)is)z%(py0)s is %(py3)srN   )py0rQ   rR   rS   )r=   sign_outr+   r]   r^   r`   ra   rb   r_   rc   rd   )rf   rE   r5   rN   rh   @py_assert1ri   rj   s           r   test_sign_out_returns_truez&TestSignOut.test_sign_out_returns_true   s      ;?!!**7"++M::v~vvv ;s   0CCB+Cc                   K   d|j                   j                  _        |j                  d       d{    |j                   j                  j                          y7 )w)u,   BB4.2 — SDK sign_out() is actually called.Nr   )r=   r   r+   assert_called_oncer   s      r    test_sign_out_calls_sdk_sign_outz,TestSignOut.test_sign_out_calls_sdk_sign_out  sN      ;?!!**7""=111!!**==? 	2s   0AA*AN)	rz   r{   r|   r}   r~   r   r   r   r   r   r   r   r   r      sA    %[[  [[@ @r   r   zFastAPI not installed)reasonc                  "    e Zd ZdZd Zd Zd Zy)TestInvalidJWTu+   BB5 — Invalid JWT handling in middleware.c                    ddl m} ddlmc m}  |       }fd}||j
                  |j                  <   |j                  d      t        |j                        fd       }|S )z:Build a minimal FastAPI app wired to the mock auth client.r   r   Nc                    K    S wNr   	mock_auths   r   override_authz0TestInvalidJWT._build_app.<locals>.override_auth  s        
/protectedc                   K   d| d   iS wNuser_idr   r   r   s    r   	protectedz,TestInvalidJWT._build_app.<locals>.protected!       tDz**   
)
fastapir   core.auth.middlewarer=   
middlewaredependency_overrides_get_auth_clientgetr   get_current_user)rf   r   r   mwappr   r   s    `     r   
_build_appzTestInvalidJWT._build_app  sa    #))i	 9F  !4!45		!()<)<!= 	+ 
	+ 
r   c                B   t               }t        t        d            |_        | j                  |      }t	        |d      }|j                  dddi      }|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)uB   BB5.1 — Sending an invalid JWT to a protected route returns 401.zInvalid tokenr   Fraise_server_exceptionsr   AuthorizationzBearer invalid-tokenheaders  rU   z3%(py2)s
{%(py2)s = %(py0)s.status_code
} == %(py5)srespr   py2rS   assert %(py7)spy7N)r   r   r   r   r   r   status_coder]   r^   r`   ra   rb   r_   rc   rd   )
rf   r   r   rD   r   r   @py_assert4rk   rj   @py_format8s
             r   test_invalid_jwt_returns_401z+TestInvalidJWT.test_invalid_jwt_returns_401'  s    K	&:o3NO	ooi(C?zz$&<=  
 &3&3&&&&3&&&&&&t&&&t&&&&&&3&&&&&&&r   c                   t               }t        t        d            |_        | j                  |      }t	        |d      }|j                  dddi      }|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}}g }d}
|j                         d   }|j                   } |       }|
|v }|}|s.d}|j                         d   }|j                   } |       }||v }|}|sNt        j                  d|fd|
|f      t        j                  |
      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }|j#                  |       |st        j                  dfdf      t        j                  |      t        j                        t        j                        t        j                  |      dz  }dd|iz  }|j#                  |       t        j$                  |d      i z  }dd|iz  }t        t        j                  |            dx}x}x}
x}x}x}x}x}x}x}x}}y) u9   BB5.2 — An expired JWT returns 401 with detail message.z#Invalid or expired JWT: JWT expiredr   Fr   r   r   zBearer expired-tokenr   r   rU   r   r   r   r   r   NexpireddetailinvalidrK   )zF%(py3)s in %(py10)s
{%(py10)s = %(py8)s
{%(py8)s = %(py6)s.lower
}()
})rQ   r[   py8py10z%(py12)spy12)zJ%(py15)s in %(py22)s
{%(py22)s = %(py20)s
{%(py20)s = %(py18)s.lower
}()
})py15py18py20py22z%(py24)spy24   zassert %(py27)spy27)r   r   r   r   r   r   r   r]   r^   r`   ra   rb   r_   rc   rd   jsonlowerappend_format_boolop)rf   r   r   rD   r   r   r   rk   rj   r   rh   @py_assert5@py_assert7@py_assert9rg   @py_assert14@py_assert17@py_assert19@py_assert21@py_assert16@py_format11@py_format13@py_format23@py_format25@py_format26@py_format28s                             r   test_expired_jwt_returns_401z+TestInvalidJWT.test_expired_jwt_returns_4015  s   K	&"#HI
	 ooi(C?zz$&<=  
 &3&3&&&&3&&&&&&t&&&t&&&&&&3&&&&&&&	:y 	:DIIK1 	:177 	:79 	:y99 	:	: IIK1	:177	:79	:99	: 	:(9(9	:y9 	: 	:09	  	: 	:09	 2 	: 	:09	 8 	: 	:09	 : 	: 	: 	:396	:39	:(9(9	:9	: 	:09	y	: 	:09	1	: 	:09	7	: 	:09	9	: 	: 	:396	:39	:+9>	: 	: 	: 	:&9&9	: 	: 	: 	: 	:r   N)rz   r{   r|   r}   r   r   r   r   r   r   r   r     s    5&':r   r   c                      e Zd ZdZd Zy)TestMissingAuthHeaderu%   BB6 — Missing Authorization header.c                P   ddl m}m} ddlmc m}  |       }|j                  d       ||j                        fd       }t        |d      }|j                  d      }|j                  }d}	||	v }
|
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)u;   BB6.1 — Request without Authorization header returns 401.r   r
   Nr   c                   K   d| d   iS wr   r   r   s    r   r   zHTestMissingAuthHeader.test_missing_header_returns_401.<locals>.protectedW  r   r   Fr   )r     rK   z3%(py2)s
{%(py2)s = %(py0)s.status_code
} in %(py5)sr   r   r   r   r   r   r   r   r=   r   r   r   r   r   r]   r^   r`   ra   rb   r_   rc   rd   )rf   r   r   r   r   r   rD   r   r   r   rk   rj   r   s                r   test_missing_header_returns_401z5TestMissingAuthHeader.test_missing_header_returns_401P  s    ,))i		!()<)<!= 	+ 
	+ C?zz,' -:-:----:------t---t------:-------r   N)rz   r{   r|   r}   r  r   r   r   r   r   L  s
    /.r   r   c                  L    e Zd ZdZej
                  d        Zd Zd Zd Z	d Z
y)TestAuthRouteStatusCodesu5   BB7 — HTTP status codes from auth router endpoints.c           	     \   ddl m} ddlm}m}  |       }|j                  |       t               t        dddi dddd	d
dddd      _        t        dddi dddd	d
dddd	d      _	        t        ddd      _
        t        d      _        fd|j                  |<   |fS )z5FastAPI app with auth router and mocked SupabaseAuth.r   r   )auth_router	_get_authu1t@e.comrs    r   r   r   rt   r   r   tokrefr   r   )r   r    r!   r"   )r   r#   r*   )r   r#   r   z"Magic link sent. Check your email.)messager   Tc                      S r   r   r   s   r   <lambda>z=TestAuthRouteStatusCodes.app_with_mock_auth.<locals>.<lambda>  s    i r   )r   r   api.auth.routesr  r  include_routerr   r   r\   r   sign_in_magic_linkreset_passwordr   )rf   r   r  r  r   r   s        @r   app_with_mock_authz+TestAuthRouteStatusCodes.app_with_mock_authj  s     	$:i;'
 K	%)*3!#2RI ).&*(D	4
 	 &)*3!#2RI ).&*(D!4
 	 (1;?
 (	$ $-$#?	  /@  +I~r   c                   |\  }}t        |      }|j                  ddddd      }|j                  }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |      rt        j                  |      nd
t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}y)u0   BB7.1 — POST /auth/signup returns 201 Created.z/auth/signuprI   z
Secure123!Jane)r   passwordr   r      rU   r   r   r   r   r   Nr   postr   r]   r^   r`   ra   rb   r_   rc   rd   rf   r  r   _rD   r   r   r   rk   rj   r   s              r   test_signup_returns_201z0TestAuthRouteStatusCodes.test_signup_returns_201  s    #QC{{,,PVW  
 &3&3&&&&3&&&&&&t&&&t&&&&&&3&&&&&&&r   c                   |\  }}t        |      }|j                  dddd      }|j                  }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      d
z  }	dd|	iz  }
t        t        j                  |
            dx}x}}y)u*   BB7.2 — POST /auth/login returns 200 OK.z/auth/loginr   rJ   )r   r  r     rU   r   r   r   r   r   Nr  r  s              r   test_login_returns_200z/TestAuthRouteStatusCodes.test_login_returns_200  s    #QC{{-=I  
 &3&3&&&&3&&&&&&t&&&t&&&&&&3&&&&&&&r   c                   |\  }}t        |      }|j                  dddi      }|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)u/   BB7.3 — POST /auth/magic-link returns 200 OK.z/auth/magic-linkr   r   r  r"  rU   r   r   r   r   r   Nr  r  s              r   test_magic_link_returns_200z4TestAuthRouteStatusCodes.test_magic_link_returns_200  s    #QC{{-.  
 &3&3&&&&3&&&&&&t&&&t&&&&&&3&&&&&&&r   c                   |\  }}t        |      }|j                  dddi      }|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)u3   BB7.4 — POST /auth/reset-password returns 200 OK.z/auth/reset-passwordr   r   r  r"  rU   r   r   r   r   r   Nr  r  s              r   test_reset_password_returns_200z8TestAuthRouteStatusCodes.test_reset_password_returns_200  s    #QC{{"-.  
 &3&3&&&&3&&&&&&t&&&t&&&&&&3&&&&&&&r   N)rz   r{   r|   r}   r~   fixturer  r   r#  r%  r'  r   r   r   r  r  f  s0    ?^^# #J''''r   r  c                      e Zd ZdZd Zd Zy)TestSDKInitialisationu2   WB1 — SDK instantiation with correct parameters.c                V   ddl mc m} t        t                     }|j                  }t        |dd      }d|_        ||_        	 ddl m}  |dd	      }|j                  d   }|d   d   }d}	||	k(  }
|
slt        j                  d
|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	|d   d   }d}	||	k(  }
|
slt        j                  d
|
fd||	f      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                  |            dx}x}
}	||_        |t        |d       y||_        y# ||_        |t        |d       w ||_        w xY w)u6   WB1.1 — create_client receives the URL and anon key.r   Nr*   r.   Tr7   zhttps://myproject.supabase.cozanon-key-abcr9   r:   rU   rW   rX   rZ   r[   r   )r<   r=   r>   r   r?   getattrr.   r8   call_args_listr]   r^   r_   rc   rd   delattr)rf   rB   mock_createoriginal_availableoriginal_creater8   r  
first_callrg   rk   rh   rl   rm   s                r   2test_create_client_called_with_correct_url_and_keyzHTestSDKInitialisation.test_create_client_called_with_correct_url_and_key  s^   22Y[9 $77!&/4@%)"*	7>3'A %33A6Ja=#F'FF#'FFFFF#'FFFF#FFF'FFFFFFFFa=#5~5#~5555#~555#555~5555555);F&&0'6$	 *<F&&0'6$s   D%F	 	F(c                   ddl mc m} t        t                     }|j                  }t        |dd      }d|_        ||_        	 ddl m} t        j                  t        d      5   |d	d
       ddd       ||_        |t        |d       y||_        y# 1 sw Y   'xY w# ||_        |t        |d       w ||_        w xY w)u,   WB1.2 — ValueError raised if URL is empty.r   Nr*   r.   Tr7   URLr   r  zsome-keyr,  )r<   r=   r>   r   r?   r-  r.   r8   r~   r   r   r/  )rf   rB   r0  r1  r2  r8   s         r   #test_missing_url_raises_value_errorz9TestSDKInitialisation.test_missing_url_raises_value_error  s    22Y[9#77!&/4@%)"*		7>z7 :j9: *<F&&0'6$: : *<F&&0'6$s$   !B$ 'B2B$ B!B$ $CN)rz   r{   r|   r}   r4  r7  r   r   r   r*  r*    s    <767r   r*  c                      e Zd ZdZd Zd Zy)TestMiddlewareBearerExtractionu0   WB2 — Internal token extraction in middleware.c                   ddl m}m} ddlmc m}  |       }i fd}||j                  |j                  <   |j                  d       ||j                        fd       }t        |      }|j                  dddi	       j                  }d
}	 ||	      }
d}|
|k(  }|st        j                  d|fd|
|f      dt        j                         v st        j                        rt        j                         ndt        j                   |      t        j                   |	      t        j                   |
      t        j                   |      dz  }dd|iz  }t#        t        j$                  |            dx}x}	x}
x}}y)u:   WB2.1 — The raw token string is passed to auth.get_user.r   r
   Nc                 <   K   t               } fd}|| _        | S w)Nc                (   K   | d<   dddi dddS w)Ntokenr	  r
  rs   r  r  r   )r=  captureds    r   	_get_userzsTestMiddlewareBearerExtraction.test_bearer_token_forwarded_to_get_user.<locals>.mock_auth_client.<locals>._get_user  s*     $)!)2 ""B s   )r   r   )r=   r?  r>  s     r   mock_auth_clientz`TestMiddlewareBearerExtraction.test_bearer_token_forwarded_to_get_user.<locals>.mock_auth_client   s!     ;D &DMKs   /testc                   K   | S wr   r   r   s    r   endpointzXTestMiddlewareBearerExtraction.test_bearer_token_forwarded_to_get_user.<locals>.endpoint       K   r   zBearer my-special-tokenr   r=  zmy-special-tokenrU   )zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} == %(py9)sr>  )r   r   rY   r[   py9zassert %(py11)spy11)r   r   r   r   r=   r   r   r   r   r   r   r]   r^   r`   ra   rb   r_   rc   rd   )rf   r   r   r   r   r@  rC  rD   r   rk   r   @py_assert8r   @py_format10@py_format12r>  s                  @r   'test_bearer_token_forwarded_to_get_userzFTestMiddlewareBearerExtraction.test_bearer_token_forwarded_to_get_user  s   ,))i
	 9I  !4!45		 '(;(; < 	 
	 C

7_6O$P
Q||:G:|G$:(::$(:::::$(:::::::x:::x:::|:::G:::$:::(:::::::::r   c                X   ddl m}m} ddlmc m}  |       }|j                  d       ||j                        fd       }t        |d      }|j                  ddd	i
      }|j                  }d}	||	v }
|
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)uH   WB2.2 — Request with non-Bearer auth scheme is rejected by HTTPBearer.r   r
   NrA  c                   K   | S wr   r   r   s    r   rC  z]TestMiddlewareBearerExtraction.test_malformed_bearer_header_returns_non_200.<locals>.endpoint  rD  rE  Fr   r   zBasic dXNlcjpwYXNzr   )r   r   i  rK   r  r   r   r   r   r  )rf   r   r   r   r   rC  rD   r   r   r   rk   rj   r   s                r   ,test_malformed_bearer_header_returns_non_200zKTestMiddlewareBearerExtraction.test_malformed_bearer_header_returns_non_200  s    ,))i		 '(;(; < 	 
	 C? zz'O=Q+RzS2?2?2222?222222t222t222222?2222222r   N)rz   r{   r|   r}   rK  rN  r   r   r   r9  r9    s    :;<3r   r9  c                  *    e Zd ZdZddZd Zd Zd Zy)TestRequireAuthTierGatingu2   WB3 — Tier-based access control in require_auth.c                   	 ddl m}m} ddlmc m}  |       }dd|i ddd		fd}||j                  |j                  <   |j                  d	 ||j                  |            g
      d        }|S )z/Build a minimal app with a tier-gated endpoint.r   r
   Nr	  r
  r  r  c                    K    S wr   r   )user_profiles   r   mock_get_userz>TestRequireAuthTierGating._app_for_tier.<locals>.mock_get_user<  s     r   /gated)dependenciesc                    K   ddiS w)Naccessgrantedr   r   r   r   gatedz6TestRequireAuthTierGating._app_for_tier.<locals>.gatedA  s     
 i((s   )
r   r   r   r   r=   r   r   r   r   require_auth)
rf   required_tier	user_tierr   r   r   r   rT  rZ  rS  s
            @r   _app_for_tierz'TestRequireAuthTierGating._app_for_tier0  s    ,))i!*"B
	  9F  !4!45	!"//-"@AB 
 

	)	

	) 
r   c                   | j                  dd      }t        |      }|j                  dddi      }|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)u/   WB3.1 — User with matching tier receives 200.professionalrU  r   
Bearer tokr   r"  rU   r   r   r   r   r   Nr^  r   r   r   r]   r^   r`   ra   rb   r_   rc   rd   	rf   r   rD   r   r   r   rk   rj   r   s	            r   "test_sufficient_tier_grants_accessz<TestRequireAuthTierGating.test_sufficient_tier_grants_accessJ  s      @Czz$l3  
 &3&3&&&&3&&&&&&t&&&t&&&&&&3&&&&&&&r   c                   | j                  dd      }t        |      }|j                  dddi      }|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)u<   WB3.2 — User with higher tier than required also gets 200.rs   
enterpriserU  r   ra  r   r"  rU   r   r   r   r   r   Nrb  rc  s	            r   test_higher_tier_grants_accessz8TestRequireAuthTierGating.test_higher_tier_grants_accessT  s      L9Czz$l3  
 &3&3&&&&3&&&&&&t&&&t&&&&&&3&&&&&&&r   c                   | j                  dd      }t        |      }|j                  dddi      }|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}	|j                         d   }|	|v }
|
slt	        j
                  d|
fd|	|f      t	        j                  |	      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}	x}
}y)u:   WB3.3 — User with lower tier than required receives 403.rf  rs   rU  r   ra  r   r   rU   r   r   r   r   r   Nr   rK   )z%(py1)s in %(py4)srX   rZ   r[   )r^  r   r   r   r]   r^   r`   ra   rb   r_   rc   rd   r   )rf   r   rD   r   r   r   rk   rj   r   rg   rh   rl   rm   s                r   "test_insufficient_tier_returns_403z<TestRequireAuthTierGating.test_insufficient_tier_returns_403^  s     y9Czz$l3  
 &3&3&&&&3&&&&&&t&&&t&&&&&&3&&&&&&&4tyy{844|44444|4444|44444444444r   N)r\  strr]  rj  )rz   r{   r|   r}   r^  rd  rg  ri  r   r   r   rP  rP  ,  s    <4''	5r   rP  c                      e Zd ZdZd Zy)TestSDKMissingErroruA   WB4 — Graceful degradation when supabase package not installed.c                    t        dd      5  ddlm} t        j                  t
        d      5   |dd	       d
d
d
       d
d
d
       y
# 1 sw Y   xY w# 1 sw Y   y
xY w)u?   WB4.1 — SupabaseAuth raises ImportError if SDK flag is False.r-   Fr   r7   zsupabase SDK is requiredr   zhttps://x.supabase.cokeyr,  N)r   r<   r8   r~   r   ImportError)rf   r8   s     r   -test_import_error_raised_when_sdk_unavailablezATestSDKMissingError.test_import_error_raised_when_sdk_unavailableq  s\    BEJ 	>{2LM /"	 	 	 	s!   "AAAA	AA N)rz   r{   r|   r}   rp  r   r   r   rl  rl  n  s
    Kr   rl  __main__z-mr~   z-vz
--tb=shortz--no-headerz/mnt/e/genesis-system)cwd)rT   r   rs   )r   rj  r   rj  r   rj  returnr   )r   r   )r   rj  r    rj  rs  r   )rT   r   rs   T)
r   rj  r   rj  r   rj  r&   boolrs  r   )7r}   
__future__r   builtinsr`   _pytest.assertion.rewrite	assertionrewriter]   r1   typingr   r   unittest.mockr   r   r   r	   r~   pytest_asyncior   r   r   fastapi.testclientr   httpx_FASTAPI_AVAILABLEro  r   r$   r(   r(  r5   rE   rG   r   r   r   r   skipifr   r   r  r*  r9  rP  rl  rz   
subprocessrun
executable__file__rN   exit
returncoder   r   r   <module>r     s  !F #   
  C C  
(- #	  	  )-


 
 # 	
	

 
 	

 
" " "  .@ @LF FF6 6@@ @, **3JK4: 4: L4:v **3JK. . L.2 **3JKO' O' LO'l07 07n **3JK03 03 L03n **3JK:5 :5 L:5B $ zZ^^NND(	
 $	F CHHV U  s   G; ;HH