Files
MusicWorkshop/backend/tests/test_metadata_normalization.py
2026-04-30 14:34:28 +08:00

96 lines
4.0 KiB
Python

import os
import tempfile
import unittest
from pathlib import Path
os.environ['MUSIC_WORKSHOP_DB_PATH'] = str(
Path(tempfile.gettempdir()) / f'music_workshop_metadata_normalization_{next(tempfile._get_candidate_names())}.db'
)
from backend.app.metadata_normalization import MetadataNormalizationService, can_ingest_metadata, parse_artist_string
from backend.app.task_store import TaskStore
class MetadataNormalizationTests(unittest.TestCase):
def setUp(self):
self.db_path = Path(os.environ['MUSIC_WORKSHOP_DB_PATH'])
if self.db_path.exists():
self.db_path.unlink()
self.task_store = TaskStore(self.db_path)
self.task = self.task_store.create_task_if_idle(
{
'input': '/tmp/input',
'output': '/tmp/output',
'trash': '/tmp/trash'
}
)
self.service = MetadataNormalizationService(self.task_store)
def test_parse_artist_string_supports_common_delimiters(self):
self.assertEqual(parse_artist_string('A / B')['tokens'], ['A', 'B'])
self.assertEqual(parse_artist_string('A; B')['tokens'], ['A', 'B'])
self.assertEqual(parse_artist_string('A & B')['tokens'], ['A', 'B'])
self.assertEqual(parse_artist_string('A feat. B')['tokens'], ['A', 'B'])
self.assertEqual(parse_artist_string('A、B')['tokens'], ['A', 'B'])
def test_single_artist_album_derives_album_artist(self):
item = self._insert_item('track-01.flac', {'title': 'Song 1', 'artist': 'Artist A', 'album': 'Album X'})
self._insert_item('track-02.flac', {'title': 'Song 2', 'artist': 'Artist A', 'album': 'Album X'})
normalized = self.service.normalize_item(item)
self.assertEqual(normalized['album_artist'], 'Artist A')
self.assertEqual(normalized['normalization_strategy'], 'single_artist')
self.assertTrue(can_ingest_metadata({**normalized, 'title': 'Song 1'}))
def test_feat_album_uses_dominant_primary_artist(self):
item = self._insert_item('track-01.flac', {'title': 'Song 1', 'artist': 'Artist A feat. Guest', 'album': 'Album X'})
self._insert_item('track-02.flac', {'title': 'Song 2', 'artist': 'Artist A', 'album': 'Album X'})
self._insert_item('track-03.flac', {'title': 'Song 3', 'artist': 'Artist A & Another', 'album': 'Album X'})
normalized = self.service.normalize_item(item)
self.assertEqual(normalized['album_artist'], 'Artist A')
self.assertEqual(normalized['normalization_strategy'], 'main_artist_feat')
def test_compilation_album_sets_various_artists(self):
item = self._insert_item('track-01.flac', {'title': 'Song 1', 'artist': 'Artist A', 'album': 'Top Hits 2025'})
self._insert_item('track-02.flac', {'title': 'Song 2', 'artist': 'Artist B', 'album': 'Top Hits 2025'})
self._insert_item('track-03.flac', {'title': 'Song 3', 'artist': 'Artist C', 'album': 'Top Hits 2025'})
normalized = self.service.normalize_item(item)
self.assertEqual(normalized['album_artist'], 'Various Artists')
self.assertEqual(normalized['compilation'], 1)
self.assertEqual(normalized['normalization_strategy'], 'compilation')
def test_existing_album_artist_is_preserved(self):
item = self._insert_item(
'track-01.flac',
{'title': 'Song 1', 'artist': '阿信', 'album': 'Solo Album', 'album_artist': '五月天'}
)
normalized = self.service.normalize_item(item)
self.assertEqual(normalized['album_artist'], '五月天')
self.assertEqual(normalized['normalization_strategy'], 'source_preserved')
def _insert_item(self, filename: str, matched_metadata_json: dict):
return self.task_store.insert_task_item(
self.task['task_id'],
original_path=f'/tmp/input/{filename}',
current_file_path=f'/tmp/input/{filename}',
relative_path=f'Artist/Album/{filename}',
filename=filename,
extension='.flac',
size_bytes=123456,
modified_at='2024-01-01T00:00:00Z',
local_cover=None,
local_lyric=None,
scan_status='queued',
scan_reason=None,
scan_message=None,
match_status='matched_fallback',
matched_metadata_json=matched_metadata_json
)
if __name__ == '__main__':
unittest.main()