2026-10-23 15:50:33 +05:30
|
|
|
"""
|
|
|
|
|
test_multiplatform_ranking.py
|
2026-10-24 09:48:37 +05:30
|
|
|
Automated Quality Assurance & Governance Test Suite for HIFFI Multi-Platform Pipeline.
|
2026-10-23 15:50:33 +05:30
|
|
|
"""
|
|
|
|
|
|
2026-10-24 09:48:37 +05:30
|
|
|
from pathlib import Path
|
2026-10-23 15:50:33 +05:30
|
|
|
import numpy as np
|
2026-10-24 09:48:37 +05:30
|
|
|
import pandas as pd
|
|
|
|
|
import pytest
|
2026-10-23 15:50:33 +05:30
|
|
|
|
2026-10-24 09:48:37 +05:30
|
|
|
DATA_FILE = Path("data/hiffi_multiplatform_500.csv")
|
2026-10-23 15:50:33 +05:30
|
|
|
|
|
|
|
|
|
2026-10-24 09:48:37 +05:30
|
|
|
@pytest.fixture(scope="module")
|
|
|
|
|
def df():
|
|
|
|
|
assert DATA_FILE.exists(), f"Target dataset {DATA_FILE} does not exist."
|
|
|
|
|
return pd.read_csv(DATA_FILE)
|
2026-10-23 15:50:33 +05:30
|
|
|
|
|
|
|
|
|
2026-10-24 09:48:37 +05:30
|
|
|
def test_cohort_size(df):
|
|
|
|
|
"""Dataset must contain exactly 500 artist records."""
|
|
|
|
|
assert len(df) == 500, f"Expected 500 artists, found {len(df)}"
|
2026-10-23 15:50:33 +05:30
|
|
|
|
|
|
|
|
|
2026-10-24 09:48:37 +05:30
|
|
|
def test_spotify_streaming_governance(df):
|
|
|
|
|
"""
|
|
|
|
|
Validates audited public streaming ingestion:
|
|
|
|
|
1. Populated listener counts must be positive integers (> 0).
|
|
|
|
|
2. Status must be explicit (either VERIFIED_PUBLIC_AGGREGATOR or UNAVAILABLE_IN_TOP_5000).
|
|
|
|
|
3. No zero-imputed values allowed.
|
|
|
|
|
"""
|
|
|
|
|
valid_statuses = {"VERIFIED_PUBLIC_AGGREGATOR", "UNAVAILABLE_IN_TOP_5000"}
|
|
|
|
|
assert set(df["spotify_status"].unique()).issubset(valid_statuses), (
|
|
|
|
|
f"Found invalid spotify_status values: {df['spotify_status'].unique()}"
|
2026-10-23 15:50:33 +05:30
|
|
|
)
|
|
|
|
|
|
2026-10-24 09:48:37 +05:30
|
|
|
valid_listeners = df["spotify_monthly_listeners"].dropna()
|
|
|
|
|
assert len(valid_listeners) > 0, "Expected verified streaming records to be populated."
|
|
|
|
|
assert (valid_listeners > 0).all(), "Found zero or negative listener values (must be positive integer or NaN)."
|
2026-10-23 15:50:33 +05:30
|
|
|
|
2026-10-24 09:48:37 +05:30
|
|
|
|
|
|
|
|
def test_entity_resolution_completeness(df):
|
|
|
|
|
"""Top 50 established artists must have candidate Spotify IDs resolved (>= 90%)."""
|
|
|
|
|
top_50 = df[df["production_hiffi_rank"] <= 50]
|
|
|
|
|
top_50_resolved = (top_50["spotify_id"].notna()) & (top_50["spotify_id"] != "N/A")
|
|
|
|
|
assert top_50_resolved.mean() >= 0.90, (
|
|
|
|
|
f"Expected >= 90% of top 50 artists to have resolved candidate Spotify IDs, got {top_50_resolved.mean():.1%}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
valid_mask = (
|
|
|
|
|
df["spotify_id"].isna()
|
|
|
|
|
| (df["spotify_id"] == "N/A")
|
|
|
|
|
| (df["spotify_id"].astype(str).str.len() == 22)
|
|
|
|
|
)
|
|
|
|
|
assert valid_mask.all(), "Found malformed Spotify IDs."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_instagram_follower_validity(df):
|
|
|
|
|
"""Follower counts must be non-negative numbers or NaN."""
|
|
|
|
|
valid_followers = df["instagram_followers"].dropna()
|
|
|
|
|
assert (valid_followers > 0).all(), "Found zero or negative follower values."
|
|
|
|
|
assert np.isfinite(valid_followers).all(), "Found infinite values."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_score_normalization_bounds(df):
|
|
|
|
|
"""Production and experimental scores must strictly fall within [0.0, 100.0]."""
|
|
|
|
|
for col in ["production_hiffi_score", "hiffi_xp_score"]:
|
|
|
|
|
valid_scores = df[col].dropna()
|
|
|
|
|
assert (valid_scores >= 0.0).all(), f"Found scores < 0 in {col}"
|
|
|
|
|
assert (valid_scores <= 100.0).all(), f"Found scores > 100 in {col}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_rank_monotonicity(df):
|
|
|
|
|
"""Higher or equal scores must produce better (lower numeric) or equal ranks."""
|
|
|
|
|
sorted_df = df.sort_values("hiffi_xp_rank", ascending=True).reset_index(drop=True)
|
|
|
|
|
scores = sorted_df["hiffi_xp_score"].tolist()
|
|
|
|
|
for i in range(len(scores) - 1):
|
|
|
|
|
assert scores[i] >= scores[i + 1], f"Rank inversion detected at row {i}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_schema_provenance_columns(df):
|
|
|
|
|
"""Dataset must contain all mandatory provenance and audit timestamps."""
|
|
|
|
|
mandatory_cols = [
|
|
|
|
|
"name", "production_hiffi_rank", "production_hiffi_score",
|
|
|
|
|
"hiffi_xp_rank", "hiffi_xp_score", "wikidata_qid", "spotify_id",
|
|
|
|
|
"spotify_status", "spotify_monthly_listeners", "spotify_data_source",
|
|
|
|
|
"instagram_handle", "instagram_followers", "instagram_collected_at",
|
|
|
|
|
"youtube_data_source", "has_complete_multiplatform_signals"
|
2026-10-23 15:50:33 +05:30
|
|
|
]
|
2026-10-24 09:48:37 +05:30
|
|
|
for col in mandatory_cols:
|
|
|
|
|
assert col in df.columns, f"Mandatory column missing: {col}"
|