"""Coverage for animetix.api.mlops view module. Exercises the MLOps admin endpoints: - AIREvaluationViewSet (stats / failures) [DI: eval_adapter] - LatentSpaceAPIView (found / not found) [DI: repository] - AIFeedbackAPIView (get history / post feedback) [DI: feedback_adapter] - GoldDatasetViewSet (sync / validate ok+404) [DI: gold_dataset_adapter] - DPOCurationViewSet (list / create ok+invalid) [get_container] - DSPyOptimizerView (success / missing / error) [DI: dspy_prompt_optimizer] - SOTABenchmarkListView [get_container] - DPOFeedbackLoopView (get / export / optimize) [get_container] - AdaptersView (get / set_primary / set_model) [get_container] DI-injected views are overridden through the global ``container`` providers; ``get_container``-based views are patched at ``animetix.api.mlops.get_container``. All collaborators are mocked; ORM uses the sqlite test DB. """ from unittest.mock import MagicMock, patch import pytest from animetix.api import mlops as mlops_mod from animetix.containers import container from animetix.models import AIFeedback, AIREvalResult, GoldDatasetEntry from dependency_injector import providers from django.contrib.auth.models import User from django.urls import reverse from rest_framework.test import APIClient @pytest.fixture def api_client(): return APIClient() @pytest.fixture def admin_user(db): return User.objects.create_superuser( username="admin", password="password", email="a@b.c" ) @pytest.fixture def admin_client(api_client, admin_user): api_client.force_authenticate(user=admin_user) return api_client @pytest.fixture def normal_user(db): return User.objects.create_user(username="plain", password="password") # --------------------------------------------------------------------------- # # AIREvaluationViewSet # --------------------------------------------------------------------------- # @pytest.mark.django_db def test_evaluation_stats(admin_client): eval_mock = MagicMock() eval_mock.get_evaluation_stats.return_value = {"avg_faithfulness": 0.9, "count": 3} with container.persistence.eval_adapter.override(providers.Object(eval_mock)): response = admin_client.get(reverse("admin_ai_eval_data")) assert response.status_code == 200 assert response.json()["avg_faithfulness"] == 0.9 @pytest.mark.django_db def test_evaluation_failures(admin_client): AIREvalResult.objects.create( input_context="q", output_text="a", hallucination_detected=True ) AIREvalResult.objects.create(input_context="q2", output_text="a2", faithfulness=0.2) AIREvalResult.objects.create( input_context="ok", output_text="good", faithfulness=0.9, relevancy=0.9 ) eval_mock = MagicMock() with container.persistence.eval_adapter.override(providers.Object(eval_mock)): response = admin_client.get(reverse("mlops_eval_failures")) assert response.status_code == 200 # Only the two failing rows are returned. assert len(response.json()) == 2 # --------------------------------------------------------------------------- # # LatentSpaceAPIView # # NOTE: the ``api_latent_space`` URL is served by labs.LatentSpaceDataView, # which shadows this mlops view, so the mlops view is unreachable via reverse(). # We therefore drive it directly with an APIRequestFactory request, injecting the # mocked repository explicitly (the @inject default would otherwise resolve the # real singleton). # --------------------------------------------------------------------------- # @pytest.mark.django_db def test_latent_space_found(): from rest_framework.test import APIRequestFactory repo = MagicMock() repo.load_latent_space.return_value = {"points": [[0.1, 0.2]]} view = mlops_mod.LatentSpaceAPIView(repository=repo) request = APIRequestFactory().get("/", {"media": "Anime", "type": "Thematic"}) response = view.get(request) assert response.status_code == 200 assert response.data["points"] == [[0.1, 0.2]] # query params are lowercased before hitting the repo repo.load_latent_space.assert_called_once_with("anime", "thematic") @pytest.mark.django_db def test_latent_space_not_found(): from rest_framework.test import APIRequestFactory repo = MagicMock() repo.load_latent_space.return_value = None view = mlops_mod.LatentSpaceAPIView(repository=repo) request = APIRequestFactory().get("/") response = view.get(request) assert response.status_code == 404 assert response.data["error"] == "Data not found" # --------------------------------------------------------------------------- # # AIFeedbackAPIView # --------------------------------------------------------------------------- # @pytest.mark.django_db def test_feedback_get_history(api_client, normal_user): api_client.force_authenticate(user=normal_user) AIFeedback.objects.create( user=normal_user, input_context="ctx", output_text="out", is_positive=True ) feedback_mock = MagicMock() with container.persistence.feedback_adapter.override( providers.Object(feedback_mock) ): response = api_client.get(reverse("submit_ai_feedback")) assert response.status_code == 200 assert len(response.json()) == 1 @pytest.mark.django_db def test_feedback_post_valid(api_client, normal_user): api_client.force_authenticate(user=normal_user) feedback_mock = MagicMock() with container.persistence.feedback_adapter.override( providers.Object(feedback_mock) ): response = api_client.post( reverse("submit_ai_feedback"), { "is_positive": True, "type": "rag", "input_context": "Who is X?", "output_text": "X is Y.", }, format="json", ) assert response.status_code == 200 assert response.json()["status"] == "success" feedback_mock.save_feedback.assert_called_once() @pytest.mark.django_db def test_feedback_post_invalid(api_client, normal_user): api_client.force_authenticate(user=normal_user) feedback_mock = MagicMock() with container.persistence.feedback_adapter.override( providers.Object(feedback_mock) ): # is_positive missing -> serializer invalid -> 400 response = api_client.post( reverse("submit_ai_feedback"), {"type": "rag"}, format="json" ) assert response.status_code == 400 # --------------------------------------------------------------------------- # # GoldDatasetViewSet # --------------------------------------------------------------------------- # @pytest.mark.django_db def test_gold_sync_positive_feedback(admin_client): gold_mock = MagicMock() gold_mock.sync_positive_feedback.return_value = 7 with container.persistence.gold_dataset_adapter.override( providers.Object(gold_mock) ): response = admin_client.post(reverse("mlops_gold_dataset_sync")) assert response.status_code == 200 assert response.json()["synced_count"] == 7 @pytest.mark.django_db def test_gold_validate_success(admin_client): entry = GoldDatasetEntry.objects.create(instruction="i", context="c", response="r") gold_mock = MagicMock() gold_mock.validate_entry.return_value = True with container.persistence.gold_dataset_adapter.override( providers.Object(gold_mock) ): response = admin_client.post( reverse("mlops_gold_dataset_validate", args=[entry.id]) ) assert response.status_code == 200 assert response.json()["status"] == "validated" @pytest.mark.django_db def test_gold_validate_not_found(admin_client): entry = GoldDatasetEntry.objects.create(instruction="i", context="c", response="r") gold_mock = MagicMock() gold_mock.validate_entry.return_value = False with container.persistence.gold_dataset_adapter.override( providers.Object(gold_mock) ): response = admin_client.post( reverse("mlops_gold_dataset_validate", args=[entry.id]) ) assert response.status_code == 404 # --------------------------------------------------------------------------- # # DPOCurationViewSet # --------------------------------------------------------------------------- # @pytest.mark.django_db def test_dpo_curation_list(admin_client): dpo_loop = MagicMock() dpo_loop.get_rejected_for_curation.return_value = [{"id": 1, "text": "x"}] fake = MagicMock() fake.core.dpo_feedback_loop.return_value = dpo_loop with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.get(reverse("api_dpo_curation"), {"limit": "10"}) assert response.status_code == 200 assert response.json()[0]["id"] == 1 dpo_loop.get_rejected_for_curation.assert_called_once_with(limit=10) @pytest.mark.django_db def test_dpo_curation_create_success(admin_client): dpo_loop = MagicMock() dpo_loop.curate_feedback.return_value = True fake = MagicMock() fake.core.dpo_feedback_loop.return_value = dpo_loop with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.post( reverse("api_dpo_curation"), {"feedback_id": 5, "chosen_text": "better answer"}, format="json", ) assert response.status_code == 200 assert response.json()["status"] == "success" @pytest.mark.django_db def test_dpo_curation_create_invalid(admin_client): fake = MagicMock() with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.post( reverse("api_dpo_curation"), {"chosen_text": "x"}, format="json" ) assert response.status_code == 400 @pytest.mark.django_db def test_dpo_curation_create_failure(admin_client): dpo_loop = MagicMock() dpo_loop.curate_feedback.return_value = False fake = MagicMock() fake.core.dpo_feedback_loop.return_value = dpo_loop with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.post( reverse("api_dpo_curation"), {"feedback_id": 5, "chosen_text": "x"}, format="json", ) assert response.status_code == 500 # --------------------------------------------------------------------------- # # DSPyOptimizerView # --------------------------------------------------------------------------- # @pytest.mark.django_db def test_dspy_optimizer_missing_template(admin_client): optimizer = MagicMock() with container.core.dspy_prompt_optimizer.override(providers.Object(optimizer)): response = admin_client.post(reverse("api_dspy_optimizer"), {}, format="json") assert response.status_code == 400 assert response.json()["error"] == "template is required" @pytest.mark.django_db def test_dspy_optimizer_success_with_default_dataset(admin_client): optimizer = MagicMock() optimizer.evaluate_and_select_best.return_value = ("BEST", 0.95) optimizer.mutate_template.return_value = ["m1", "m2", "m3"] with container.core.dspy_prompt_optimizer.override(providers.Object(optimizer)): # no dataset -> the view supplies a fallback dataset response = admin_client.post( reverse("api_dspy_optimizer"), {"template": "T"}, format="json" ) assert response.status_code == 200 body = response.json() assert body["best_template"] == "BEST" assert body["best_score"] == 0.95 assert body["all_mutations"] == ["m1", "m2", "m3"] @pytest.mark.django_db def test_dspy_optimizer_exception(admin_client): optimizer = MagicMock() optimizer.evaluate_and_select_best.side_effect = ValueError("bad template") with container.core.dspy_prompt_optimizer.override(providers.Object(optimizer)): response = admin_client.post( reverse("api_dspy_optimizer"), {"template": "T", "dataset": [{"query": "q", "expected": "e"}]}, format="json", ) assert response.status_code == 500 assert response.json()["error"] == "Internal server error" # --------------------------------------------------------------------------- # # SOTABenchmarkListView # --------------------------------------------------------------------------- # @pytest.mark.django_db def test_sota_benchmarks(api_client): service = MagicMock() service.get_all_benchmarks.return_value = [{"model": "X"}] service.get_best_model.return_value = {"model": "X", "elo": 1500} service.get_open_source_best.return_value = [{"model": "OS"}] fake = MagicMock() fake.core.sota_benchmark_service.return_value = service with patch.object(mlops_mod, "get_container", return_value=fake): # AllowAny -> no auth required response = api_client.get(reverse("api_sota_benchmarks")) assert response.status_code == 200 body = response.json() assert body["status"] == "success" assert body["top_model"]["model"] == "X" assert body["best_open_source"]["model"] == "OS" @pytest.mark.django_db def test_sota_benchmarks_no_open_source(api_client): service = MagicMock() service.get_all_benchmarks.return_value = [] service.get_best_model.return_value = None service.get_open_source_best.return_value = [] fake = MagicMock() fake.core.sota_benchmark_service.return_value = service with patch.object(mlops_mod, "get_container", return_value=fake): response = api_client.get(reverse("api_sota_benchmarks")) assert response.status_code == 200 assert response.json()["best_open_source"] is None # --------------------------------------------------------------------------- # # DPOFeedbackLoopView # --------------------------------------------------------------------------- # @pytest.mark.django_db def test_dpo_feedback_loop_get(admin_client): dpo_loop = MagicMock() dpo_loop.analyze_feedback_trends.return_value = {"trend": "up"} fake = MagicMock() fake.core.dpo_feedback_loop.return_value = dpo_loop with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.get(reverse("api_dpo_feedback_loop")) assert response.status_code == 200 body = response.json() assert body["status"] == "active" assert body["metrics"]["trend"] == "up" @pytest.mark.django_db def test_dpo_feedback_loop_export(admin_client): dpo_loop = MagicMock() fake = MagicMock() fake.core.dpo_feedback_loop.return_value = dpo_loop with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.post( reverse("api_dpo_feedback_loop"), {"action": "export"}, format="json" ) assert response.status_code == 200 assert response.json()["status"] == "success" dpo_loop.export_preference_dataset.assert_called_once() @pytest.mark.django_db def test_dpo_feedback_loop_optimize_success(admin_client): dpo_loop = MagicMock() dpo_loop.optimize_prompt_from_feedback.return_value = "NEW PROMPT" fake = MagicMock() fake.core.dpo_feedback_loop.return_value = dpo_loop with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.post( reverse("api_dpo_feedback_loop"), {"action": "optimize", "prompt_key": "rag_response"}, format="json", ) assert response.status_code == 200 assert response.json()["new_system_prompt"] == "NEW PROMPT" @pytest.mark.django_db def test_dpo_feedback_loop_optimize_failure(admin_client): dpo_loop = MagicMock() dpo_loop.optimize_prompt_from_feedback.return_value = None fake = MagicMock() fake.core.dpo_feedback_loop.return_value = dpo_loop with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.post( reverse("api_dpo_feedback_loop"), {"action": "optimize"}, format="json" ) assert response.status_code == 400 @pytest.mark.django_db def test_dpo_feedback_loop_invalid_action(admin_client): dpo_loop = MagicMock() fake = MagicMock() fake.core.dpo_feedback_loop.return_value = dpo_loop with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.post( reverse("api_dpo_feedback_loop"), {"action": "bogus"}, format="json" ) assert response.status_code == 400 assert response.json()["error"] == "Invalid action" # --------------------------------------------------------------------------- # # AdaptersView # --------------------------------------------------------------------------- # @pytest.mark.django_db def test_adapters_get_health(admin_client): engine = MagicMock() engine.health_check.return_value = {"ok": True} fake = MagicMock() fake.inference.inference_engine.return_value = engine with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.get(reverse("api_mlops_adapters")) assert response.status_code == 200 body = response.json() assert body["engine"] == "FallbackInferenceAdapter" assert body["health"] == {"ok": True} @pytest.mark.django_db def test_adapters_set_primary_success(admin_client): engine = MagicMock() engine.set_primary_adapter.return_value = True fake = MagicMock() fake.inference.inference_engine.return_value = engine with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.post( reverse("api_mlops_adapters"), {"action": "set_primary", "index": 1}, format="json", ) assert response.status_code == 200 assert "primary" in response.json()["message"] @pytest.mark.django_db def test_adapters_set_primary_invalid_index(admin_client): engine = MagicMock() engine.set_primary_adapter.return_value = False fake = MagicMock() fake.inference.inference_engine.return_value = engine with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.post( reverse("api_mlops_adapters"), {"action": "set_primary", "index": 99}, format="json", ) assert response.status_code == 400 assert response.json()["error"] == "Invalid index" @pytest.mark.django_db def test_adapters_set_model_success(admin_client): engine = MagicMock() unified = MagicMock() fake = MagicMock() fake.inference.inference_engine.return_value = engine fake.inference.unified_inference_adapter.return_value = unified with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.post( reverse("api_mlops_adapters"), {"action": "set_model", "model_name": "llama3"}, format="json", ) assert response.status_code == 200 unified.set_model_name.assert_called_once_with("llama3") @pytest.mark.django_db def test_adapters_set_model_missing_name(admin_client): engine = MagicMock() fake = MagicMock() fake.inference.inference_engine.return_value = engine with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.post( reverse("api_mlops_adapters"), {"action": "set_model"}, format="json", ) assert response.status_code == 400 assert response.json()["error"] == "model_name required" @pytest.mark.django_db def test_adapters_invalid_action(admin_client): engine = MagicMock() fake = MagicMock() fake.inference.inference_engine.return_value = engine with patch.object(mlops_mod, "get_container", return_value=fake): response = admin_client.post( reverse("api_mlops_adapters"), {"action": "nope"}, format="json" ) assert response.status_code == 400