from __future__ import annotations import binascii import json import os from collections.abc import Callable from datetime import UTC, datetime from random import randint from uuid import uuid4 import pytest from redis import Redis from generalresearch.models.gr.authentication import Claims, GRToken, GRUser from generalresearch.models.gr.business import Business from generalresearch.models.gr.team import Membership, Team from generalresearch.models.thl.product import Product from generalresearch.pg_helper import PostgresConfig from generalresearch.redis_helper import RedisConfig SSO_ISSUER = "" class TestGRUser: def test_init(self, gr_user: GRUser): assert isinstance(gr_user, GRUser) assert not gr_user.is_superuser assert gr_user.teams is None assert gr_user.businesses is None assert gr_user.products is None @pytest.mark.skip(reason="TODO") def test_businesses(self): pass def test_teams( self, gr_user: GRUser, membership: Membership, gr_db: PostgresConfig, gr_redis_config: RedisConfig, ): assert gr_user.teams is None gr_user.prefetch_teams(pg_config=gr_db, redis_config=gr_redis_config) assert isinstance(gr_user.teams, list) assert len(gr_user.teams) == 1 assert isinstance(gr_user.teams[0], Team) def test_prefetch_team_duplicates( self, gr_user_token: GRToken, gr_user: GRUser, membership: Membership, product_factory: Callable[..., Product], membership_factory: Callable[..., Membership], team: Team, thl_web_rr: PostgresConfig, gr_redis_config: RedisConfig, gr_db: PostgresConfig, ): product_factory(team=team) membership_factory(team=team, gr_user=gr_user) gr_user.prefetch_teams( pg_config=gr_db, redis_config=gr_redis_config, ) assert len(gr_user.teams) == 1 def test_products( self, gr_user: GRUser, product_factory: Callable[..., Product], team: Team, membership: Membership, gr_db: PostgresConfig, thl_web_rr: PostgresConfig, gr_redis_config: RedisConfig, ): from generalresearch.models.thl.product import Product assert gr_user.products is None # Create a new Team membership, and then create a Product that # is part of that team membership.prefetch_team(pg_config=gr_db, redis_config=gr_redis_config) assert isinstance(membership.team, Team) p: Product = product_factory(team=team) assert p.id_int assert team.uuid == membership.team.uuid assert p.team_id == team.uuid assert p.team_uuid == membership.team.uuid assert gr_user.id == membership.user_id gr_user.prefetch_products( pg_config=gr_db, thl_pg_config=thl_web_rr, redis_config=gr_redis_config, ) assert isinstance(gr_user.products, list) assert len(gr_user.products) == 1 assert isinstance(gr_user.products[0], Product) class TestGRUserMethods: def test_cache_key(self, gr_user: GRUser, gr_redis: RedisConfig): assert isinstance(gr_user.cache_key, str) assert ":" in gr_user.cache_key assert str(gr_user.id) in gr_user.cache_key def test_to_redis( self, gr_user: GRUser, gr_redis: Redis, team: Team, business: Business, product_factory: Callable[..., Product], membership_factory: Callable[..., Membership], ): product_factory(team=team, business=business) membership_factory(team=team, gr_user=gr_user) res = gr_user.to_redis() assert isinstance(res, str) from generalresearch.models.gr.authentication import GRUser instance = GRUser.from_redis(res) assert isinstance(instance, GRUser) def test_set_cache( self, gr_user: GRUser, gr_user_token: GRToken, gr_redis: Redis, gr_db: PostgresConfig, thl_web_rr: PostgresConfig, gr_redis_config: RedisConfig, ): assert gr_redis.get(name=gr_user.cache_key) is None assert gr_redis.get(name=f"{gr_user.cache_key}:team_uuids") is None assert gr_redis.get(name=f"{gr_user.cache_key}:business_uuids") is None assert gr_redis.get(name=f"{gr_user.cache_key}:product_uuids") is None gr_user.set_cache( pg_config=gr_db, thl_web_rr=thl_web_rr, redis_config=gr_redis_config ) assert gr_redis.get(name=gr_user.cache_key) is not None assert gr_redis.get(name=f"{gr_user.cache_key}:team_uuids") is not None assert gr_redis.get(name=f"{gr_user.cache_key}:business_uuids") is not None assert gr_redis.get(name=f"{gr_user.cache_key}:product_uuids") is not None def test_set_cache_gr_user( self, gr_user: GRUser, gr_user_token: GRToken, gr_redis: RedisConfig, gr_redis_config: RedisConfig, gr_db: PostgresConfig, thl_web_rr: PostgresConfig, product_factory: Callable[..., Product], team: Team, membership_factory: Callable[..., Membership], thl_redis_config: RedisConfig, ): from generalresearch.models.gr.authentication import GRUser p1 = product_factory(team=team) membership_factory(team=team, gr_user=gr_user) gr_user.set_cache( pg_config=gr_db, thl_web_rr=thl_web_rr, redis_config=gr_redis_config ) res: str = gr_redis.get(name=gr_user.cache_key) gru2 = GRUser.from_redis(res) assert gr_user.model_dump_json( exclude={"businesses", "teams", "products"} ) == gru2.model_dump_json(exclude={"businesses", "teams", "products"}) gru2.prefetch_products( pg_config=gr_db, thl_pg_config=thl_web_rr, redis_config=thl_redis_config, ) assert gru2.product_uuids == [p1.uuid] def test_set_cache_team_uuids( self, gr_user: GRUser, membership: Membership, gr_user_token: GRToken, gr_redis: Redis, gr_db: PostgresConfig, thl_web_rr: PostgresConfig, product_factory: Callable[..., Product], team: Team, gr_redis_config: RedisConfig, ): product_factory(team=team) gr_user.set_cache( pg_config=gr_db, thl_web_rr=thl_web_rr, redis_config=gr_redis_config ) res = json.loads(gr_redis.get(name=f"{gr_user.cache_key}:team_uuids")) assert len(res) == 1 assert gr_user.team_uuids == res @pytest.mark.skip def test_set_cache_business_uuids( self, gr_user: GRUser, gr_redis: Redis, gr_db: PostgresConfig, thl_web_rr: PostgresConfig, product_factory: Callable[..., Product], business: Business, team: Team, gr_redis_config: RedisConfig, ): product_factory(team=team, business=business) gr_user.set_cache( pg_config=gr_db, thl_web_rr=thl_web_rr, redis_config=gr_redis_config ) res = json.loads(gr_redis.get(name=f"{gr_user.cache_key}:business_uuids")) assert len(res) == 1 assert gr_user.business_uuids == res def test_set_cache_product_uuids( self, gr_user: GRUser, membership: Membership, gr_user_token: GRToken, gr_redis: Redis, gr_db: PostgresConfig, thl_web_rr: PostgresConfig, product_factory: Callable[..., Product], team: Team, gr_redis_config: RedisConfig, ): product_factory(team=team) gr_user.set_cache( pg_config=gr_db, thl_web_rr=thl_web_rr, redis_config=gr_redis_config ) res = json.loads(gr_redis.get(name=f"{gr_user.cache_key}:product_uuids")) assert len(res) == 1 assert gr_user.product_uuids == res class TestGRToken: @pytest.fixture def gr_token(self, gr_user: GRUser): now = datetime.now(tz=UTC) token = binascii.hexlify(os.urandom(20)).decode() gr_token = GRToken(key=token, created=now, user_id=gr_user.id) return gr_token def test_init(self, gr_token: GRToken): assert isinstance(gr_token, GRToken) assert gr_token.created def test_user( self, gr_token: GRToken, gr_db: PostgresConfig, gr_redis_config: RedisConfig ): assert gr_token.user is None gr_token.prefetch_user(pg_config=gr_db, redis_config=gr_redis_config) assert isinstance(gr_token.user, GRUser) def test_auth_header(self, gr_token: GRToken): assert isinstance(gr_token.auth_header, dict) class TestClaims: def test_init(self): d = { "iss": SSO_ISSUER, "sub": f"{uuid4().hex}{uuid4().hex}", "aud": uuid4().hex, "exp": randint(a=1_500_000_000, b=2_000_000_000), "iat": randint(a=1_500_000_000, b=2_000_000_000), "auth_time": randint(a=1_500_000_000, b=2_000_000_000), "acr": "goauthentik.io/providers/oauth2/default", "amr": ["pwd", "mfa"], "sid": f"{uuid4().hex}{uuid4().hex}", "email": "max@g-r-l.com", "email_verified": True, "name": "Max Nanis", "given_name": "Max Nanis", "preferred_username": "nanis", "nickname": "nanis", "groups": [ "authentik Admins", "Developers", "Systems Admin", "Customer Support", "admin", ], "azp": uuid4().hex, "uid": uuid4().hex, } instance = Claims.model_validate(d) assert isinstance(instance, Claims)