Skip to main content
Glama
test_basic.py6.12 kB
"""Basic unit tests for CSV Editor.""" from __future__ import annotations from pathlib import Path from typing import TYPE_CHECKING, Any import pytest from databeak.core.session import get_session_manager from databeak.utils.validators import sanitize_filename, validate_column_name, validate_url if TYPE_CHECKING: from fastmcp import Context class TestValidators: """Test validation utilities.""" def test_validate_column_name(self) -> None: """Test column name validation.""" # Valid names assert validate_column_name("age")[0] assert validate_column_name("first_name")[0] assert validate_column_name("_id")[0] # Invalid names assert not validate_column_name("123name")[0] assert not validate_column_name("name-with-dash")[0] assert not validate_column_name("")[0] def test_sanitize_filename(self) -> None: """Test filename sanitization.""" assert sanitize_filename("test.csv") == "test.csv" assert sanitize_filename("test<>file.csv") == "test__file.csv" assert sanitize_filename("../../../etc/passwd") == "passwd" def test_validate_url(self) -> None: """Test URL validation with enhanced security.""" # Valid URLs (public addresses) assert validate_url("https://example.com/data.csv")[0] assert validate_url("https://raw.githubusercontent.com/user/repo/data.csv")[0] # Invalid URLs (now includes localhost due to security enhancement) assert not validate_url("http://localhost:8000/file.csv")[0] # Now blocked assert not validate_url("ftp://example.com/data.csv")[0] assert not validate_url("not-a-url")[0] # Additional security tests for private networks assert not validate_url("http://192.168.1.1/data.csv")[0] # Private network assert not validate_url("http://10.0.0.1/data.csv")[0] # Private network @pytest.mark.asyncio class TestSessionManager: """Test session management.""" async def test_get_or_create_session(self) -> None: """Test session creation.""" manager = get_session_manager() test_session_id = "test_session_123" session = manager.get_or_create_session(test_session_id) assert session is not None assert session.session_id == test_session_id assert manager.get_or_create_session(test_session_id) is not None # Cleanup await manager.remove_session(test_session_id) async def test_session_cleanup(self) -> None: """Test session removal.""" manager = get_session_manager() test_session_id = "test_cleanup_456" session = manager.get_or_create_session(test_session_id) session_id = session.session_id # Session should exist assert manager.get_or_create_session(session_id) is not None # Remove session await manager.remove_session(session_id) # Session should not exist (check sessions dict directly since get_session auto-creates) assert session_id not in manager.sessions @pytest.mark.asyncio class TestDataOperations: """Test basic data operations.""" @pytest.mark.asyncio async def test_load_csv_from_content( self, isolated_context: Context, temp_work_dir: Path ) -> None: """Test loading CSV from string content with isolated session.""" from databeak.servers.io_server import load_csv_from_content csv_content = """a,b,c 1,2,3 4,5,6""" result = await load_csv_from_content(isolated_context, content=csv_content, delimiter=",") assert result.rows_affected == 2 assert len(result.columns_affected) == 3 assert result.columns_affected == ["a", "b", "c"] # Verify session isolation - each test gets its own unique session assert isolated_context.session_id is not None assert len(isolated_context.session_id) > 0 @pytest.mark.asyncio async def test_filter_rows(self, csv_session_with_data: tuple[Context, Any]) -> None: """Test filtering rows with isolated session.""" from databeak.servers.transformation_server import filter_rows # Get the isolated context and load result from fixture isolated_context, _load_result = csv_session_with_data # Test the filter operation on price > 100 (should filter to Laptop, Desk, Chair) filter_result = filter_rows( isolated_context, conditions=[{"column": "price", "operator": ">", "value": 100}], # type: ignore[list-item] mode="and", ) assert filter_result.success assert filter_result.rows_after < filter_result.rows_before # Original data has 5 rows, filter should leave 3 rows (Laptop: 999.99, Desk: 299.99, Chair: 199.99) assert filter_result.rows_before == 5 assert filter_result.rows_after == 3 @pytest.mark.asyncio async def test_filter_rows_category( self, isolated_context: Context, csv_session_with_data: tuple[Context, Any] ) -> None: # Test a different filter condition - Electronics category on fresh data # Need to reload data first since previous filter modified the session from databeak.servers.io_server import load_csv_from_content from databeak.servers.transformation_server import filter_rows csv_content = """product,price,quantity,category Laptop,999.99,10,Electronics Mouse,29.99,50,Electronics Keyboard,79.99,25,Electronics Desk,299.99,5,Furniture Chair,199.99,8,Furniture""" await load_csv_from_content(isolated_context, content=csv_content, delimiter=",") filter_result2 = filter_rows( isolated_context, conditions=[{"column": "category", "operator": "==", "value": "Electronics"}], # type: ignore[list-item] mode="and", ) assert filter_result2.success # Should have 3 Electronics items from fresh data (5 total, 3 Electronics) assert filter_result2.rows_before == 5 assert filter_result2.rows_after == 3

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jonpspri/databeak'

If you have feedback or need assistance with the MCP directory API, please join our Discord server