""" test_multiplatform_ranking.py Comprehensive QA Governance & Regression Test Suite. Validates candidate verification, fallback null governance, non-interference with production baseline, zero-imputation policy, score boundaries, and schema provenance. """ from pathlib import Path from unittest.mock import MagicMock, patch import numpy as np import pandas as pd import pytest from collectors.instagram_verifier import InstagramCandidateVerifier from collectors.spotify_fallback_collector import SpotifyFallbackCollector PROJECT_ROOT = Path(__file__).resolve().parent DATASET_PATH = PROJECT_ROOT / "data" / "hiffi_multiplatform_500.csv" @pytest.fixture(scope="module") def dataset(): if not DATASET_PATH.exists(): pytest.fail(f"Dataset missing at {DATASET_PATH}. Run pipeline/batch_500_runner.py first.") return pd.read_csv(DATASET_PATH) def test_cohort_size(dataset): """Ensures exact cohort preservation without dropped or added records.""" assert len(dataset) == 500, f"Expected 500 cohort records, found {len(dataset)}" def test_spotify_streaming_governance(dataset): """Strictly enforces zero-imputation policy: all populated listeners must be > 0 and no synthetic zeroes.""" valid_streams = dataset["spotify_monthly_listeners"].dropna() assert (valid_streams > 0).all(), "Found invalid zero or negative Spotify monthly listeners" assert (dataset["spotify_monthly_listeners"] == 0).sum() == 0, "Synthetic zeroes detected in Spotify metrics" def test_missing_metrics_remain_nan(dataset): """Verifies that artists unlisted in streaming tables retain clean NaN rather than arbitrary numbers.""" unlisted = dataset[dataset["spotify_status"] == "UNAVAILABLE_IN_TOP_5000"] assert unlisted["spotify_monthly_listeners"].isna().all(), "Unlisted Spotify artists must strictly remain NaN" def test_production_ranking_preserved(dataset): """Guarantees that experimental changes do NOT alter the official YouTube production ranking.""" assert "production_hiffi_rank" in dataset.columns assert "production_hiffi_score" in dataset.columns # Check monotonicity of production ranks sorted_df = dataset.sort_values("production_hiffi_rank") diffs = np.diff(sorted_df["production_hiffi_score"].values) assert (diffs <= 1e-5).all(), "Production score must be monotonically non-increasing with production rank" def test_score_normalization_bounds(dataset): """Validates that all composite HIFFI-XP scores are strictly within [0.0, 100.0].""" assert (dataset["hiffi_xp_score"] >= 0.0).all() assert (dataset["hiffi_xp_score"] <= 100.0).all() def test_rank_monotonicity(dataset): """Confirms no ranking inversions in experimental rankings.""" sorted_xp = dataset.sort_values("hiffi_xp_rank") score_diffs = np.diff(sorted_xp["hiffi_xp_score"].values) assert (score_diffs <= 1e-5).all(), "HIFFI-XP score must strictly decrease or tie as rank index increases" def test_schema_provenance_columns(dataset): """Ensures complete data provenance and audit trails exist on every record.""" required = [ "name", "production_hiffi_score", "production_hiffi_rank", "hiffi_xp_score", "hiffi_xp_rank", "spotify_monthly_listeners", "spotify_daily_change", "spotify_data_source", "spotify_status", "instagram_handle", "instagram_followers", "instagram_verification_status", "instagram_confidence_score", "instagram_audit_evidence" ] for col in required: assert col in dataset.columns, f"Missing required audit column: {col}" def test_instagram_verifier_candidate_matching(): """Validates automated candidate scoring and rejection of fan pages.""" verifier = InstagramCandidateVerifier() # Exact official candidate with link in bio match = verifier.evaluate_candidate( artist_name="Metro Boomin", candidate_handle="metroboomin", profile_name="Metro Boomin", bio_text="Official accounts & tour dates on linktr.ee/metroboomin", bio_links=["https://linktr.ee/metroboomin"], follower_count=5000000 ) assert match["verification_status"] == "AUTOMATED_VERIFIED" assert match["confidence_score"] >= 0.70 # Fan page rejection test fan_match = verifier.evaluate_candidate( artist_name="Metro Boomin", candidate_handle="metroboomin_fanpage", profile_name="Metro Daily Updates", bio_text="Fan page celebrating Metro Boomin beats", follower_count=12000 ) assert fan_match["verification_status"] == "REJECTED_FAN_PAGE" assert fan_match["confidence_score"] == 0.0 def test_instagram_verifier_suspicious_follower_count(): """Checks that candidate accounts with < 10k followers are capped in confidence.""" verifier = InstagramCandidateVerifier() res = verifier.evaluate_candidate( artist_name="Emerging MC", candidate_handle="emergingmc", profile_name="Emerging MC", follower_count=1200 ) assert res["confidence_score"] <= 0.40 assert "below 10k threshold" in res["evidence"] def test_spotify_fallback_null_safety(): """Ensures fallback returns safe NaN when artist has no ID or is missing.""" collector = SpotifyFallbackCollector() res = collector.fetch_artist_fallback("Unknown Underground Artist", spotify_id_or_uri=None) assert res["spotify_monthly_listeners"] is None assert res["spotify_status"] == "UNAVAILABLE_NO_ID" def test_spotify_fallback_mocked_network_error(): """Verifies that network or HTTP errors do not crash execution or impute fake data.""" collector = SpotifyFallbackCollector() with patch.object(collector.session, "get") as mock_get: mock_get.return_value = MagicMock(status_code=500) res = collector.fetch_artist_fallback("Test Artist", "4gzpq5DPGxSnKTe4SA8HAU") assert res["spotify_monthly_listeners"] is None assert res["spotify_status"] == "HTTP_ERROR_500" def test_duplicate_records_handled_safely(): """Verifies deduplication safety on cohort inputs.""" sample_df = pd.DataFrame([ {"name": "Artist A", "production_hiffi_score": 80.0}, {"name": "Artist A", "production_hiffi_score": 80.0}, {"name": "Artist B", "production_hiffi_score": 60.0} ]) deduped = sample_df.drop_duplicates(subset=["name"]) assert len(deduped) == 2