"""Integration test for the gRPC FraudService over an in-process channel.""" from __future__ import annotations from uuid import uuid4 import grpc import pytest from app.grpc_server import FraudServicer, serve_grpc, stop_grpc from app.scoring import HeuristicScorer from fraud.v1 import fraud_pb2, fraud_pb2_grpc @pytest.mark.asyncio async def test_score_low_risk_first_access(): scorer = HeuristicScorer() server = await serve_grpc(scorer, "127.0.0.1:0") # add_insecure_port returns 0 so we need to fish out the actual bound port via _server's state. # Easier: rebind on a known free port for the test. await stop_grpc(server) addr = "127.0.0.1:50951" server = await serve_grpc(scorer, addr) try: async with grpc.aio.insecure_channel(addr) as channel: stub = fraud_pb2_grpc.FraudServiceStub(channel) resp = await stub.Score( fraud_pb2.ScoreRequest( event_id=str(uuid4()), guest_id=str(uuid4()), token_id=str(uuid4()), access_log_id=str(uuid4()), fingerprint={"ua": "Chrome", "platform": "macOS"}, ip_address="203.0.113.7", user_agent="Mozilla/5.0", ), timeout=2.0, ) assert resp.score <= 30 assert resp.risk == fraud_pb2.RISK_LOW finally: await stop_grpc(server) @pytest.mark.asyncio async def test_score_high_risk_after_baseline_change(): scorer = HeuristicScorer() addr = "127.0.0.1:50952" server = await serve_grpc(scorer, addr) try: guest_id = str(uuid4()) async with grpc.aio.insecure_channel(addr) as channel: stub = fraud_pb2_grpc.FraudServiceStub(channel) await stub.Score( fraud_pb2.ScoreRequest( event_id=str(uuid4()), guest_id=guest_id, token_id=str(uuid4()), access_log_id=str(uuid4()), fingerprint={"ua": "Chrome"}, ip_address="203.0.113.7", user_agent="Mozilla/5.0", ), timeout=2.0, ) resp = await stub.Score( fraud_pb2.ScoreRequest( event_id=str(uuid4()), guest_id=guest_id, token_id=str(uuid4()), access_log_id=str(uuid4()), fingerprint={"ua": "curl/8"}, ip_address="198.51.100.42", user_agent="", ), timeout=2.0, ) assert resp.score >= 60 assert resp.risk in {fraud_pb2.RISK_HIGH, fraud_pb2.RISK_BLOCK} finally: await stop_grpc(server) @pytest.mark.asyncio async def test_invalid_uuid_returns_invalid_argument(): scorer = HeuristicScorer() addr = "127.0.0.1:50953" server = await serve_grpc(scorer, addr) try: async with grpc.aio.insecure_channel(addr) as channel: stub = fraud_pb2_grpc.FraudServiceStub(channel) with pytest.raises(grpc.RpcError) as excinfo: await stub.Score( fraud_pb2.ScoreRequest( event_id="not-a-uuid", guest_id=str(uuid4()), token_id=str(uuid4()), ), timeout=2.0, ) assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT finally: await stop_grpc(server) def test_servicer_constructs(): # Ensures the servicer wires up against the generated stub. FraudServicer(HeuristicScorer(), None)