import uuid import pytest from fastapi.testclient import TestClient @pytest.mark.integration class TestConfigFoldersAPI: """Integration tests for config folders API.""" def test_list_config_folders_requires_authentication(self, test_client: TestClient) -> None: """Test that listing config folders requires authentication.""" response = test_client.get("/config-folders") assert response.status_code == 401 def test_list_config_folders_returns_user_folders(self, authenticated_client: TestClient) -> None: """Test that authenticated users can list their folders.""" response = authenticated_client.get("/config-folders") assert response.status_code == 200 data = response.json() assert isinstance(data, dict) assert "folders" in data assert isinstance(data["folders"], list) def test_create_config_folder_successfully(self, authenticated_client: TestClient) -> None: """Test creating a config folder.""" response = authenticated_client.post( "/config-folders", json={ "name": "test-folder", "description": "Test folder", "mount_path": "/home/user", "files": {"test.txt": "hello world"}, }, ) assert response.status_code == 201 data = response.json() assert data["name"] == "test-folder" assert data["mount_path"] == "/home/user" assert data["files"] == {"test.txt": "hello world"} def test_create_config_folder_duplicate_name(self, authenticated_client: TestClient) -> None: """Test that duplicate folder names are rejected.""" # Create first folder response = authenticated_client.post( "/config-folders", json={ "name": "duplicate-folder", "mount_path": "/home/user", "files": {}, }, ) assert response.status_code == 201 # Try to create second with same name response = authenticated_client.post( "/config-folders", json={ "name": "duplicate-folder", "mount_path": "/home/user", "files": {}, }, ) assert response.status_code == 409 def test_create_config_folder_exceeds_size_limit(self, authenticated_client: TestClient) -> None: """Test that folders exceeding 10MB are rejected.""" large_content = "x" * (11 * 1024 * 1024) # 11MB response = authenticated_client.post( "/config-folders", json={ "name": "large-folder", "mount_path": "/home/user", "files": {"large.txt": large_content}, }, ) assert response.status_code == 422 def test_create_config_folder_path_traversal_attack(self, authenticated_client: TestClient) -> None: """Test that path traversal in file paths is prevented.""" response = authenticated_client.post( "/config-folders", json={ "name": "bad-folder", "mount_path": "/home/user", "files": {"../../../etc/passwd": "malicious"}, }, ) assert response.status_code == 422 def test_get_config_folder_by_id(self, authenticated_client: TestClient) -> None: """Test getting a config folder by ID.""" # Create folder first create_response = authenticated_client.post( "/config-folders", json={ "name": "get-test", "mount_path": "/home/user", "files": {}, }, ) folder_id = create_response.json()["id"] # Get it back response = authenticated_client.get(f"/config-folders/{folder_id}") assert response.status_code == 200 data = response.json() assert data["name"] == "get-test" def test_get_config_folder_not_found(self, authenticated_client: TestClient) -> None: """Test getting a non-existent folder.""" response = authenticated_client.get(f"/config-folders/{uuid.uuid4()}") assert response.status_code == 404 def test_update_config_folder_successfully(self, authenticated_client: TestClient) -> None: """Test updating a config folder.""" # Create folder first create_response = authenticated_client.post( "/config-folders", json={ "name": "update-test", "mount_path": "/home/user", "files": {}, }, ) folder_id = create_response.json()["id"] # Update it response = authenticated_client.put( f"/config-folders/{folder_id}", json={ "name": "updated-name", "mount_path": "/workspace", "files": {"new.txt": "content"}, }, ) assert response.status_code == 200 data = response.json() assert data["name"] == "updated-name" assert data["mount_path"] == "/workspace" def test_delete_config_folder_successfully(self, authenticated_client: TestClient) -> None: """Test deleting a config folder.""" # Create folder first create_response = authenticated_client.post( "/config-folders", json={ "name": "delete-test", "mount_path": "/home/user", "files": {}, }, ) folder_id = create_response.json()["id"] # Delete it response = authenticated_client.delete(f"/config-folders/{folder_id}") assert response.status_code == 204 # Verify it's gone get_response = authenticated_client.get(f"/config-folders/{folder_id}") assert get_response.status_code == 404 def test_add_project_override_successfully(self, authenticated_client: TestClient) -> None: """Test adding a project override.""" # Create folder first create_response = authenticated_client.post( "/config-folders", json={ "name": "override-test", "mount_path": "/home/user", "files": {"global.txt": "global"}, }, ) folder_id = create_response.json()["id"] project_id = str(uuid.uuid4()) # Add override response = authenticated_client.post( f"/config-folders/{folder_id}/overrides", json={ "project_id": project_id, "mount_path": "/workspace", "files": {"project.txt": "project"}, }, ) assert response.status_code == 200 data = response.json() assert project_id in data["project_overrides"] def test_update_project_override_successfully(self, authenticated_client: TestClient) -> None: """Test updating a project override.""" # Create folder with override create_response = authenticated_client.post( "/config-folders", json={ "name": "update-override-test", "mount_path": "/home/user", "files": {}, }, ) folder_id = create_response.json()["id"] project_id = str(uuid.uuid4()) # Add override authenticated_client.post( f"/config-folders/{folder_id}/overrides", json={ "project_id": project_id, "mount_path": "/workspace", "files": {"old.txt": "old"}, }, ) # Update override response = authenticated_client.put( f"/config-folders/{folder_id}/overrides/{project_id}", json={ "mount_path": "/app", "files": {"new.txt": "new"}, }, ) assert response.status_code == 200 data = response.json() assert data["project_overrides"][project_id]["mount_path"] == "/app" def test_delete_project_override_successfully(self, authenticated_client: TestClient) -> None: """Test deleting a project override.""" # Create folder with override create_response = authenticated_client.post( "/config-folders", json={ "name": "delete-override-test", "mount_path": "/home/user", "files": {}, }, ) folder_id = create_response.json()["id"] project_id = str(uuid.uuid4()) # Add override authenticated_client.post( f"/config-folders/{folder_id}/overrides", json={ "project_id": project_id, "mount_path": "/workspace", "files": {}, }, ) # Delete override response = authenticated_client.delete( f"/config-folders/{folder_id}/overrides/{project_id}" ) assert response.status_code == 200 data = response.json() assert project_id not in data["project_overrides"]