from fastapi.testclient import TestClient from app.main import create_app def test_places_search_endpoint() -> None: client = TestClient(create_app()) response = client.post( "/places/search", json={ "query": "lugares tranquilos para cenar", "city": "Tuxtla Gutierrez", "filters": {"is_active": True}, "limit": 3, }, ) assert response.status_code == 200 payload = response.json() assert payload["query"] == "lugares tranquilos para cenar" assert payload["places"] assert payload["metrics"]["engine"] == "fasttext_mean_embeddings" assert payload["metrics"]["candidate_retrieval"] == "mock_embeddings" assert payload["metrics"]["score_metric"] == "cosine_similarity" assert payload["metrics"]["ranking_parameters"] == {"dimension": 16.0} assert payload["metrics"]["field_weights"] == { "tags": 6, "category": 4, "description": 3, "name": 1, } assert payload["metrics"]["returned_count"] == len(payload["places"]) assert payload["metrics"]["max_score"] >= payload["metrics"]["mean_score"] def test_places_search_metrics_endpoint() -> None: client = TestClient(create_app()) response = client.get("/places/search/metrics?k=3") assert response.status_code == 200 payload = response.json() assert payload["engine"] == "bm25" assert payload["benchmark"] == "built_in_places_v3_bm25" assert payload["qrels_source"] == "predefined_graded_qrels" assert payload["query_count"] == 10 assert payload["metric_definitions"]["precision_at_k"]["label"] == "Precision@3" assert payload["metric_definitions"]["recall_at_k"]["label"] == "Recall@3" assert payload["metric_definitions"]["mrr"]["label"] == "MRR" assert payload["metric_definitions"]["map"]["label"] == "MAP" assert payload["metric_definitions"]["ndcg_at_k"]["label"] == "nDCG@3" assert payload["recommended_metric"]["key"] == "ndcg_at_k" assert payload["recommended_metric"]["label"] == "nDCG@3" assert payload["recommended_metric"]["value"] == payload["aggregate"]["ndcg_at_k"] assert all( 0.0 <= payload["aggregate"][metric] <= 1.0 for metric in ["precision_at_k", "recall_at_k", "mrr", "map", "ndcg_at_k"] ) def test_places_search_metrics_post_requires_no_body() -> None: client = TestClient(create_app()) response = client.post("/places/search/metrics?k=2") assert response.status_code == 200 payload = response.json() assert payload["k"] == 2 assert payload["query_count"] == 10 def test_places_chat_endpoint_returns_trace_and_structured_places() -> None: client = TestClient(create_app()) response = client.post( "/places/chat", json={ "message": "quiero una cena tranquila con mi pareja", "city": "Tuxtla Gutierrez", "filters": {"occasion": "pareja", "is_active": True}, "limit": 3, }, ) assert response.status_code == 200 payload = response.json() assert payload["response_id"].startswith("resp_") assert payload["nlp_trace_id"].startswith("trace_") assert payload["message"] assert payload["places"] assert payload["metadata"]["places_used_as_context"] def test_places_recommendations_returns_llm_message_and_semantic_metadata() -> None: client = TestClient(create_app()) response = client.post( "/places/recommendations", json={ "query": "quiero ver el atardecer y tomar fotos", "city": "Tuxtla Gutierrez", "filters": {"is_active": True}, "limit": 3, }, ) assert response.status_code == 200 payload = response.json() assert payload["message"] assert payload["places"] assert payload["metrics"]["engine"] == "fasttext_mean_embeddings" assert payload["metrics"]["score_metric"] == "cosine_similarity" assert payload["metrics"]["returned_count"] == len(payload["places"]) assert payload["metrics"]["candidate_retrieval"] == "mock_embeddings" assert payload["metrics"]["query_token_count"] > 0 assert payload["metrics"]["matched_query_token_count"] > 0 assert payload["metrics"]["scope"] == "current_query" assert payload["metrics"]["ground_truth_available"] is False assert "evaluation_metrics" not in payload assert payload["metadata"]["ranking"] == "fasttext_mean_embeddings" assert payload["metadata"]["response_mode"] == "confident" assert payload["metadata"]["used_llm"] is True def test_places_recommendations_returns_no_places_without_candidates() -> None: client = TestClient(create_app()) response = client.post( "/places/recommendations", json={ "query": "xqzv blorf 998zz", "city": "Ciudad inexistente", "filters": {"is_active": True}, "limit": 3, }, ) assert response.status_code == 200 payload = response.json() assert payload["places"] == [] assert payload["metrics"]["max_score"] == 0.0 assert payload["metrics"]["returned_count"] == 0 assert payload["metrics"]["match_quality"] == "no_match" assert payload["metadata"]["response_mode"] == "no_match" assert payload["message"] def test_places_search_rejects_incomplete_coordinates() -> None: client = TestClient(create_app()) response = client.post( "/places/search", json={ "query": "parque cercano", "lat": 16.7531, "limit": 5, }, ) assert response.status_code == 422 def test_posts_recommendations_endpoint() -> None: client = TestClient(create_app()) response = client.post( "/posts/recommendations", json={ "query": "ideas para fotos con amigos", "city": "Tuxtla Gutierrez", "limit": 3, }, ) assert response.status_code == 200 payload = response.json() assert payload["posts"] assert payload["metadata"]["computed_clusters_during_request"] is False def test_posts_clusters_endpoint() -> None: client = TestClient(create_app()) response = client.get("/posts/clusters") assert response.status_code == 200 payload = response.json() assert payload["clusters"] assert payload["metadata"]["computed_during_request"] is False