Files
hiffi-artist-intelligence/test_multiplatform_ranking.py
T

150 lines
6.3 KiB
Python

"""
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