import pytest from pydantic import ValidationError from app.catalogue import Catalogue, build_record from app.events import COLUMNS, ClientEvent, EventBatch, clean_notes, to_row SID = "0123456789abcdef" class FakeCatalogue: def __init__(self, recs): self.by_id = {r.id: r for r in recs} CAT = FakeCatalogue([ build_record({"id": "mlx-community/Qwen3-8B-4bit", "downloads": 42, "likes": 3, "safetensors": {"total": 8.19e9}, "tags": ["4-bit"], "pipeline_tag": "text-generation"}), build_record({"id": "mlx-community/Llama-3.3-70B-Instruct-4bit", "safetensors": {"total": 70e9}, "tags": ["4-bit"], "pipeline_tag": "text-generation"}), ]) def ev(**kw): return ClientEvent(**{"event_type": "search", "session_id": SID, **kw}) def test_valid_search_row_has_all_columns_and_no_pii(): row = to_row(ev(model_family="Qwen", parameter_bucket="8-15B", quantization="4-bit", target_context=32768, priority="balanced", hardware_source="confirmed", hardware_memory_class=36), CAT) assert set(row) == set(COLUMNS) assert row["hardware_confirmed"] is True and row["suspicious_flags"] == [] assert row["timestamp"].endswith("Z") and "." not in row["timestamp"] for forbidden in ("ip", "user_agent", "email", "name", "cookie"): assert forbidden not in row def test_server_fills_catalogue_facts(): row = to_row(ev(event_type="model_select", selected_model="mlx-community/Qwen3-8B-4bit", selected_model_rank=0), CAT) assert row["hf_downloads_at_selection"] == 42 and row["quant_bits"] == 4 assert row["model_family"] == "Qwen" and row["parameter_bucket"] == "8-15B" @pytest.mark.parametrize("bad", [ {"event_type": "delete_everything"}, {"event_type": "search", "session_id": "NOT-HEX"}, {"event_type": "search", "target_context": 12345}, {"event_type": "search", "hardware_memory_class": 17}, {"event_type": "search", "quantization": "7-bit"}, {"event_type": "feedback", "reported_tokens_per_second": -1}, {"event_type": "feedback", "reported_tokens_per_second": 99999}, {"event_type": "search", "selected_model": "../../etc/passwd"}, {"event_type": "search", "selected_model": "") assert e.notes == "" # stored as inert text; UI renders textContent only def test_flags_unknown_model_and_implausible_tps(): row = to_row(ev(event_type="mlx_benchmark_submission", selected_model="mlx-community/Nope-1B", generation_tps=10, benchmark_version="1", benchmark_type="mlx_lm"), CAT) assert "unknown_model" in row["suspicious_flags"] row = to_row(ev(event_type="mlx_benchmark_submission", selected_model="mlx-community/Llama-3.3-70B-Instruct-4bit", generation_tps=1500, benchmark_version="1", benchmark_type="mlx_lm"), CAT) assert "implausible_tps" in row["suspicious_flags"] row = to_row(ev(event_type="mlx_benchmark_submission", selected_model="mlx-community/Qwen3-8B-4bit", generation_tps=45, benchmark_version="1", benchmark_type="mlx_lm", peak_memory_gb=5.1, reported_ram_gb=36), CAT) assert row["suspicious_flags"] == [] def test_flags_other_inconsistencies(): row = to_row(ev(event_type="mlx_benchmark_submission", peak_memory_gb=40, reported_ram_gb=16), CAT) assert {"peak_memory_exceeds_ram", "incomplete_benchmark"} <= set(row["suspicious_flags"]) row = to_row(ev(event_type="feedback", tried="yes", quality_rating="good", failure_reason="too_slow"), CAT) assert "conflicting_feedback" in row["suspicious_flags"] row = to_row(ev(event_type="browser_benchmark", benchmark_type="mlx_lm"), CAT) assert "benchmark_type_mismatch" in row["suspicious_flags"]