"""Lambda a360_profile_creator unit tests.""" import json import uuid from unittest.mock import MagicMock, patch import pytest from src.app import ( generate_manifest_json_to_s3, generate_pdp_csv_to_s3, insert_a360_profile, parse_s3_event, to_snake_case, ) from src.constants import PARENT_COMPANIES class TestParseS3Event: def test_success(self): event = { "detail": { "bucket": {"name": "test-bucket"}, "object": {"key": "test/key.csv"}, } } bucket, key = parse_s3_event(event) assert bucket == "test-bucket" assert key == "test/key.csv" def test_missing_detail(self): with pytest.raises(ValueError, match="Invalid EventBridge S3 event structure"): parse_s3_event({}) def test_missing_bucket(self): event = {"detail": {"object": {"key": "test/key.csv"}}} with pytest.raises(ValueError, match="Invalid EventBridge S3 event structure"): parse_s3_event(event) class TestToSnakeCase: def test_spaces(self): assert to_snake_case("First Name") == "first_name" def test_hyphens(self): assert to_snake_case("OA User-Id") == "oa_user_id" def test_real_csv_header(self): # Matches the actual "Existing ABACUS Access" header from the CSV assert to_snake_case("Existing ABACUS Access") == "existing_abacus_access" def test_already_snake(self): assert to_snake_case("email") == "email" class TestInsertA360Profile: def test_returns_uuid_on_success(self): identity_id = str(uuid.uuid4()) session = MagicMock() session.run.return_value.single.return_value = {"identityId": identity_id} result = insert_a360_profile({"email": "user@example.com"}, session) assert result == uuid.UUID(identity_id) def test_returns_none_when_no_record(self): session = MagicMock() session.run.return_value.single.return_value = None result = insert_a360_profile({"email": "user@example.com"}, session) assert result is None def test_returns_none_when_identity_id_missing(self): session = MagicMock() session.run.return_value.single.return_value = {"identityId": None} result = insert_a360_profile({"email": "user@example.com"}, session) assert result is None class TestGeneratePdpCsvToS3: @patch("src.app.boto3.client") @patch("src.app.config") def test_generates_two_rows_per_uuid(self, mock_config, mock_boto3_client): mock_config.A360_OUTPUT_FOLDER = "a360_output/" mock_s3 = MagicMock() mock_boto3_client.return_value = mock_s3 identity = uuid.uuid4() timestamp = "2024-01-15T10-30-00" url, ts = generate_pdp_csv_to_s3([identity], "test-bucket", timestamp) assert url == f"s3://test-bucket/a360_output/{timestamp}/{timestamp}.csv" assert ts == timestamp csv_content = mock_s3.put_object.call_args.kwargs["Body"] lines = csv_content.strip().splitlines() assert len(lines) == 3 # header + 2 parent companies assert lines[0] == "identity_uuid,tenant_uuid,tenant_type,role,operation" assert ( f"{identity},{PARENT_COMPANIES[0]['uuid']},parent_company,contract_viewer,attach" in lines[1] ) assert ( f"{identity},{PARENT_COMPANIES[1]['uuid']},parent_company,contract_viewer,attach" in lines[2] ) @patch("src.app.boto3.client") @patch("src.app.config") def test_empty_uuids_writes_header_only(self, mock_config, mock_boto3_client): mock_config.A360_OUTPUT_FOLDER = "a360_output/" mock_s3 = MagicMock() mock_boto3_client.return_value = mock_s3 generate_pdp_csv_to_s3([], "test-bucket", "2024-01-15T10-30-00") csv_content = mock_s3.put_object.call_args.kwargs["Body"] lines = csv_content.strip().splitlines() assert len(lines) == 1 assert lines[0] == "identity_uuid,tenant_uuid,tenant_type,role,operation" @patch("src.app.boto3.client") @patch("src.app.config") def test_multiple_uuids(self, mock_config, mock_boto3_client): mock_config.A360_OUTPUT_FOLDER = "a360_output/" mock_s3 = MagicMock() mock_boto3_client.return_value = mock_s3 uuids = [uuid.uuid4(), uuid.uuid4(), uuid.uuid4()] generate_pdp_csv_to_s3(uuids, "test-bucket", "2024-01-15T10-30-00") csv_content = mock_s3.put_object.call_args.kwargs["Body"] lines = csv_content.strip().splitlines() assert len(lines) == 1 + len(uuids) * len(PARENT_COMPANIES) class TestGenerateManifestJsonToS3: @patch("src.app.boto3.client") @patch("src.app.config") def test_manifest_content(self, mock_config, mock_boto3_client): mock_config.ENVIRONMENT = "qa" mock_config.A360_OUTPUT_FOLDER = "a360_output/" mock_s3 = MagicMock() mock_boto3_client.return_value = mock_s3 timestamp = "2024-01-15T10-30-00" result = generate_manifest_json_to_s3(timestamp, "test-bucket") assert result == f"s3://test-bucket/a360_output/{timestamp}/manifest.json" call_kwargs = mock_s3.put_object.call_args.kwargs assert call_kwargs["Key"] == f"a360_output/{timestamp}/manifest.json" assert call_kwargs["ContentType"] == "application/json" manifest = json.loads(call_kwargs["Body"]) assert manifest == { "bucket": "qa-pdp-backfill", "jobs": [ { "job_type": "attach_and_detach", "keys": [f"a360-backfill/{timestamp}.csv"], } ], } @patch("src.app.boto3.client") @patch("src.app.config") def test_manifest_prod_environment(self, mock_config, mock_boto3_client): mock_config.ENVIRONMENT = "prod" mock_config.A360_OUTPUT_FOLDER = "a360_output/" mock_s3 = MagicMock() mock_boto3_client.return_value = mock_s3 generate_manifest_json_to_s3("2024-01-15T10-30-00", "prod-bucket") manifest = json.loads(mock_s3.put_object.call_args.kwargs["Body"]) assert manifest["bucket"] == "prod-pdp-backfill"