# [DEF:backend.src.api.routes.__tests__.test_datasets:Module] # @TIER: STANDARD # @SEMANTICS: datasets, api, tests, pagination, mapping, docs # @PURPOSE: Unit tests for Datasets API endpoints # @LAYER: API # @RELATION: TESTS -> backend.src.api.routes.datasets # @INVARIANT: Endpoint contracts remain stable for success and validation failure paths. import pytest from unittest.mock import MagicMock, patch, AsyncMock from fastapi.testclient import TestClient from src.app import app from src.api.routes.datasets import DatasetsResponse, DatasetDetailResponse client = TestClient(app) # [DEF:test_get_datasets_success:Function] # @PURPOSE: Validate successful datasets listing contract for an existing environment. # @TEST: GET /api/datasets returns 200 and valid schema # @PRE: env_id exists # @POST: Response matches DatasetsResponse schema def test_get_datasets_success(): with patch("src.api.routes.datasets.get_config_manager") as mock_config, \ patch("src.api.routes.datasets.get_resource_service") as mock_service, \ patch("src.api.routes.datasets.has_permission") as mock_perm: # Mock environment mock_env = MagicMock() mock_env.id = "prod" mock_config.return_value.get_environments.return_value = [mock_env] # Mock resource service response mock_service.return_value.get_datasets_with_status.return_value = AsyncMock()( return_value=[ { "id": 1, "table_name": "sales_data", "schema": "public", "database": "sales_db", "mapped_fields": {"total": 10, "mapped": 5}, "last_task": {"task_id": "task-1", "status": "SUCCESS"} } ] ) # Mock permission mock_perm.return_value = lambda: True response = client.get("/api/datasets?env_id=prod") assert response.status_code == 200 data = response.json() assert "datasets" in data assert len(data["datasets"]) >= 0 # Validate against Pydantic model DatasetsResponse(**data) # [/DEF:test_get_datasets_success:Function] # [DEF:test_get_datasets_env_not_found:Function] # @TEST: GET /api/datasets returns 404 if env_id missing # @PRE: env_id does not exist # @POST: Returns 404 error def test_get_datasets_env_not_found(): with patch("src.api.routes.datasets.get_config_manager") as mock_config, \ patch("src.api.routes.datasets.has_permission") as mock_perm: mock_config.return_value.get_environments.return_value = [] mock_perm.return_value = lambda: True response = client.get("/api/datasets?env_id=nonexistent") assert response.status_code == 404 assert "Environment not found" in response.json()["detail"] # [/DEF:test_get_datasets_env_not_found:Function] # [DEF:test_get_datasets_invalid_pagination:Function] # @TEST: GET /api/datasets returns 400 for invalid page/page_size # @PRE: page < 1 or page_size > 100 # @POST: Returns 400 error def test_get_datasets_invalid_pagination(): with patch("src.api.routes.datasets.get_config_manager") as mock_config, \ patch("src.api.routes.datasets.has_permission") as mock_perm: mock_env = MagicMock() mock_env.id = "prod" mock_config.return_value.get_environments.return_value = [mock_env] mock_perm.return_value = lambda: True # Invalid page response = client.get("/api/datasets?env_id=prod&page=0") assert response.status_code == 400 assert "Page must be >= 1" in response.json()["detail"] # Invalid page_size response = client.get("/api/datasets?env_id=prod&page_size=0") assert response.status_code == 400 assert "Page size must be between 1 and 100" in response.json()["detail"] # [/DEF:test_get_datasets_invalid_pagination:Function] # [DEF:test_map_columns_success:Function] # @TEST: POST /api/datasets/map-columns creates mapping task # @PRE: Valid env_id, dataset_ids, source_type # @POST: Returns task_id def test_map_columns_success(): with patch("src.api.routes.datasets.get_config_manager") as mock_config, \ patch("src.api.routes.datasets.get_task_manager") as mock_task_mgr, \ patch("src.api.routes.datasets.has_permission") as mock_perm: # Mock environment mock_env = MagicMock() mock_env.id = "prod" mock_config.return_value.get_environments.return_value = [mock_env] # Mock task manager mock_task = MagicMock() mock_task.id = "task-123" mock_task_mgr.return_value.create_task = AsyncMock(return_value=mock_task) # Mock permission mock_perm.return_value = lambda: True response = client.post( "/api/datasets/map-columns", json={ "env_id": "prod", "dataset_ids": [1, 2, 3], "source_type": "postgresql" } ) assert response.status_code == 200 data = response.json() assert "task_id" in data # [/DEF:test_map_columns_success:Function] # [DEF:test_map_columns_invalid_source_type:Function] # @TEST: POST /api/datasets/map-columns returns 400 for invalid source_type # @PRE: source_type is not 'postgresql' or 'xlsx' # @POST: Returns 400 error def test_map_columns_invalid_source_type(): with patch("src.api.routes.datasets.has_permission") as mock_perm: mock_perm.return_value = lambda: True response = client.post( "/api/datasets/map-columns", json={ "env_id": "prod", "dataset_ids": [1], "source_type": "invalid" } ) assert response.status_code == 400 assert "Source type must be 'postgresql' or 'xlsx'" in response.json()["detail"] # [/DEF:test_map_columns_invalid_source_type:Function] # [DEF:test_generate_docs_success:Function] # @TEST: POST /api/datasets/generate-docs creates doc generation task # @PRE: Valid env_id, dataset_ids, llm_provider # @POST: Returns task_id def test_generate_docs_success(): with patch("src.api.routes.datasets.get_config_manager") as mock_config, \ patch("src.api.routes.datasets.get_task_manager") as mock_task_mgr, \ patch("src.api.routes.datasets.has_permission") as mock_perm: # Mock environment mock_env = MagicMock() mock_env.id = "prod" mock_config.return_value.get_environments.return_value = [mock_env] # Mock task manager mock_task = MagicMock() mock_task.id = "task-456" mock_task_mgr.return_value.create_task = AsyncMock(return_value=mock_task) # Mock permission mock_perm.return_value = lambda: True response = client.post( "/api/datasets/generate-docs", json={ "env_id": "prod", "dataset_ids": [1], "llm_provider": "openai" } ) assert response.status_code == 200 data = response.json() assert "task_id" in data # [/DEF:test_generate_docs_success:Function] # [/DEF:backend.src.api.routes.__tests__.test_datasets:Module]