soulsync/tests/quality/test_quality_meets_profile.py
dev d2abec4a92 feat(quality): unify quality scanner onto the real ranked-target core (strict)
The library quality scanner judged quality by FILE EXTENSION only
(get_quality_tier_from_extension) and read the legacy v2 `qualities` dict —
so every FLAC was "lossless tier 1" regardless of bit depth / sample rate. It
could never flag a 16-bit FLAC as upgradeable under a 24-bit profile, and it
ignored the v3 ranked_targets entirely. Completely inconsistent with the
download guard.

Now both share one core:
- selection.targets_from_profile(profile) — single profile→targets conversion
  (v2→v3 migration), reused by load_profile_targets.
- selection.quality_meets_profile(aq, targets) — strict: meets iff the real
  measured quality satisfies a ranked target (fallback ignored — it's a
  download concession, not a definition of "good enough").
- guards.check_quality_target refactored to use both.
- quality_scanner probes real quality (probe_audio_quality) and checks against
  the v3 targets via quality_meets_profile. Extension tier kept only as a
  fallback label when a file can't be probed.

Result: the scan flags exactly what the download gate would reject — 16-bit
when you want 24-bit, wrong sample rate, MP3 when you want FLAC.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 22:50:01 +02:00

60 lines
2.1 KiB
Python

"""quality_meets_profile / targets_from_profile — the shared strict
quality-decision core used by BOTH the import quality guard and the library
quality scanner. A file "meets" the profile iff its real measured quality
satisfies at least one ranked target (bit depth + sample rate are minimums;
fallback is NOT consulted — that's a download-time concession, not a definition
of quality).
"""
from core.quality.model import AudioQuality, QualityTarget
from core.quality.selection import quality_meets_profile, targets_from_profile
FLAC_HI = AudioQuality('flac', sample_rate=96000, bit_depth=24)
FLAC_CD = AudioQuality('flac', sample_rate=44100, bit_depth=16)
MP3 = AudioQuality('mp3', bitrate=320)
WANT_24BIT = [
QualityTarget(label='FLAC 24/96', format='flac', bit_depth=24, min_sample_rate=96000),
QualityTarget(label='FLAC 24/44.1', format='flac', bit_depth=24, min_sample_rate=44100),
]
def test_24bit_meets_24bit_target():
assert quality_meets_profile(FLAC_HI, WANT_24BIT) is True
def test_16bit_does_not_meet_24bit_target():
assert quality_meets_profile(FLAC_CD, WANT_24BIT) is False
def test_mp3_does_not_meet_flac_target():
assert quality_meets_profile(MP3, WANT_24BIT) is False
def test_no_targets_means_no_constraint():
assert quality_meets_profile(FLAC_CD, []) is True
def test_unprobeable_file_is_not_flagged():
# aq=None (probe failed) → don't act (avoid false re-downloads).
assert quality_meets_profile(None, WANT_24BIT) is True
def test_targets_from_profile_reads_v3_ranked_targets():
profile = {
'version': 3,
'fallback_enabled': False,
'ranked_targets': [
{'label': 'FLAC 24/96', 'format': 'flac', 'bit_depth': 24, 'min_sample_rate': 96000},
],
}
targets, fallback = targets_from_profile(profile)
assert [t.label for t in targets] == ['FLAC 24/96']
assert fallback is False
def test_targets_from_profile_migrates_v2_qualities():
profile = {'qualities': {'flac': {'enabled': True, 'priority': 1}}}
targets, _ = targets_from_profile(profile)
assert any(t.format == 'flac' for t in targets)