"""Tests for POST /videos/recompute-dedup endpoint (#1664 follow-up).""" from unittest.mock import MagicMock, patch import pytest from fastapi.testclient import TestClient @pytest.fixture def mock_video(): """Mock video with missing dedup data.""" v = MagicMock() v.id = "video-001" v.user_id = "user-abc" v.duplicate_rate = None v.video_fingerprint = None v.project_id = "proj-001" v.generation_task_id = "task-001" v.name = "test.mp4" v.file_url = "https://example.com/test.mp4" v.file_size = 1024 v.duration = 10.0 v.width = 1920 v.height = 1080 v.fps = 25.0 v.status = "completed" v.review_status = "pending_review" v.generation_params = {} v.thumbnail_url = None v.is_duplicate = False v.duplicate_of = None v.match_count = None v.visual_similarity = None v.generated_at = "2026-09-04T00:00:00" return v @pytest.fixture def mock_video_with_dedup(mock_video): """Mock video that already has dedup data.""" mock_video.duplicate_rate = 15.5 mock_video.video_fingerprint = {"md5": "abc123"} return mock_video class TestRecomputeDedupEndpoint: """POST /videos/recompute-dedup""" def test_enqueue_videos_without_dedup(self, mock_video): """Videos missing duplicate_rate should be enqueued.""" from app.api.routes.videos import RecomputeDedupRequest mock_repo = MagicMock() mock_repo.list_by_user.return_value = [mock_video] with (patch("app.api.routes.videos.celery_app") as mock_celery,): mock_celery.send_task.return_value = MagicMock(id="task-xyz") from app.api.routes.videos import recompute_dedup auth_user = MagicMock() auth_user.user.id = "user-abc" result = recompute_dedup( request=RecomputeDedupRequest(), repo=mock_repo, current_user=auth_user, ) assert result.enqueued == 1 assert result.total_scanned == 1 assert result.skipped == 0 mock_celery.send_task.assert_called_once_with("worker.check_duplicate", args=["video-001"]) def test_skip_videos_with_complete_dedup(self, mock_video_with_dedup): """Videos with both duplicate_rate and video_fingerprint should be skipped.""" from app.api.routes.videos import RecomputeDedupRequest, recompute_dedup mock_repo = MagicMock() mock_repo.list_by_user.return_value = [mock_video_with_dedup] with patch("app.api.routes.videos.celery_app") as mock_celery: auth_user = MagicMock() auth_user.user.id = "user-abc" result = recompute_dedup( request=RecomputeDedupRequest(), repo=mock_repo, current_user=auth_user, ) assert result.enqueued == 0 assert result.total_scanned == 1 assert result.skipped == 1 mock_celery.send_task.assert_not_called() def test_specific_video_ids(self, mock_video): """When video_ids are provided, only those videos are processed.""" from app.api.routes.videos import RecomputeDedupRequest, recompute_dedup mock_repo = MagicMock() mock_repo.get_by_ids.return_value = [mock_video] with patch("app.api.routes.videos.celery_app") as mock_celery: mock_celery.send_task.return_value = MagicMock(id="task-xyz") auth_user = MagicMock() auth_user.user.id = "user-abc" result = recompute_dedup( request=RecomputeDedupRequest(video_ids=["video-001"]), repo=mock_repo, current_user=auth_user, ) assert result.enqueued == 1 mock_repo.get_by_ids.assert_called_once_with(["video-001"]) def test_security_only_own_videos(self, mock_video): """Videos belonging to other users should be filtered out.""" from app.api.routes.videos import RecomputeDedupRequest, recompute_dedup mock_video.user_id = "user-OTHER" mock_repo = MagicMock() mock_repo.get_by_ids.return_value = [mock_video] with patch("app.api.routes.videos.celery_app") as mock_celery: auth_user = MagicMock() auth_user.user.id = "user-abc" result = recompute_dedup( request=RecomputeDedupRequest(video_ids=["video-001"]), repo=mock_repo, current_user=auth_user, ) assert result.enqueued == 0 mock_celery.send_task.assert_not_called() def test_mixed_complete_and_incomplete(self, mock_video, mock_video_with_dedup): """Mix of videos with and without dedup data.""" import copy from app.api.routes.videos import RecomputeDedupRequest, recompute_dedup # Create a second video object v2 = MagicMock() v2.id = "video-002" v2.user_id = "user-abc" v2.duplicate_rate = None v2.video_fingerprint = None mock_repo = MagicMock() mock_repo.list_by_user.return_value = [mock_video_with_dedup, v2] with patch("app.api.routes.videos.celery_app") as mock_celery: mock_celery.send_task.return_value = MagicMock(id="task-xyz") auth_user = MagicMock() auth_user.user.id = "user-abc" result = recompute_dedup( request=RecomputeDedupRequest(), repo=mock_repo, current_user=auth_user, ) assert result.enqueued == 1 assert result.total_scanned == 2 assert result.skipped == 1