import importlib.util import pathlib import types import unittest spec = importlib.util.spec_from_file_location('auto_lyrics', pathlib.Path(__file__).with_name('auto_lyrics.py')) worker = importlib.util.module_from_spec(spec) spec.loader.exec_module(worker) class FakeApi: def __init__(self): self.calls = [] def call(self, method, path, body=None, raw=False): self.calls.append((method, path, body)) if raw: return 200, b'fixture audio' if path.startswith('/api/notes/'): return 200, {'lyrics': {'rev': 5, 'data': {'lines': [{'t': 0, 'text': 'Keep the original'}]}}} return 200, {'ok': True} class FakeModel: def transcribe(self, path, **options): assert options['vad_filter'] is False words = [types.SimpleNamespace(word=word, start=i, end=i + .8) for i, word in enumerate('Because You are God You can do anything'.split())] return [types.SimpleNamespace(words=words)], types.SimpleNamespace(language='en', duration=10) class WhisperRequests(unittest.TestCase): def setUp(self): self.args = types.SimpleNamespace(overwrite=False, no_web=False, language=None, min_words=1, dry_run=False) self.api = FakeApi() self.job = {'id': 'fixture-job', 'videoId': '0gfX0dFLaBc', 'lease': 'fixture-lease', 'audioPath': '/api/media/0gfX0dFLaBc?a=1'} def test_manual_request_uses_whisper_despite_existing_lyrics_and_never_publishes(self): result = worker.transcribe_one(self.args, self.api, FakeModel(), self.job['videoId'], draft_job=self.job) self.assertIsInstance(result, dict) self.assertTrue(result['lines']) self.assertFalse(any(method == 'PUT' or path.endswith('/lyrics/web') for method, path, body in self.api.calls)) def test_requested_job_reports_stages_and_completes_a_draft(self): worker.process_requested(self.args, self.api, self.job, lambda: FakeModel()) posts = [body for method, path, body in self.api.calls if path.startswith('/api/lyrics-worker/jobs/')] self.assertEqual(posts[0]['stage'], 'downloading-audio') self.assertEqual(posts[1]['stage'], 'transcribing') self.assertEqual(posts[-1]['status'], 'complete') self.assertEqual(posts[-1]['lease'], 'fixture-lease') def test_rejected_draft_is_reported_failed_instead_of_retrying_forever(self): original = self.api.call def reject(method, path, body=None, raw=False): if body and body.get('status') == 'complete': return 400, {'error': 'too many lines'} return original(method, path, body, raw) self.api.call = reject worker.process_requested(self.args, self.api, self.job, lambda: FakeModel()) self.assertEqual(self.api.calls[-1][2]['status'], 'failed') self.assertEqual(self.api.calls[-1][2]['error'], 'too many lines') def test_no_vocals_fails_without_touching_saved_lyrics(self): self.args.min_words = 25 worker.process_requested(self.args, self.api, self.job, lambda: FakeModel()) last = self.api.calls[-1][2] self.assertEqual(last['status'], 'failed') self.assertFalse(any(method == 'PUT' for method, path, body in self.api.calls)) if __name__ == '__main__': unittest.main()