unityCatalog-ChatBot / test_chatbot.py
Jackie Makhija
Add HF deployment docs and fix tests
96cc367
"""
Test Suite for Unity Catalog Chatbot
"""
import pytest
from unittest.mock import Mock, patch
from unity_catalog_service import UnityCatalogService
from app import parse_with_claude, execute_intent
class TestUnityCatalogService:
"""Tests for Unity Catalog Service"""
def test_parse_object_path_catalog(self, uc_service):
"""Test parsing catalog path"""
result = uc_service.parse_object_path("my_catalog")
assert result == {'catalog': 'my_catalog'}
def test_parse_object_path_schema(self, uc_service):
"""Test parsing schema path"""
result = uc_service.parse_object_path("my_catalog.my_schema")
assert result == {
'catalog': 'my_catalog',
'schema': 'my_schema'
}
def test_parse_object_path_table(self, uc_service):
"""Test parsing table path"""
result = uc_service.parse_object_path("my_catalog.my_schema.my_table")
assert result == {
'catalog': 'my_catalog',
'schema': 'my_schema',
'table': 'my_table'
}
def test_create_catalog_success(self, uc_service):
"""Test successful catalog creation"""
mock_catalog = Mock()
mock_catalog.name = "test_catalog"
mock_catalog.owner = "test_user"
mock_catalog.created_at = "2025-01-01T00:00:00Z"
uc_service.client.catalogs.create = Mock(return_value=mock_catalog)
result = uc_service.create_catalog("test_catalog")
assert result['success'] is True
assert result['catalog']['name'] == "test_catalog"
assert "CREATE CATALOG" in result['sql']
def test_create_catalog_error(self, uc_service):
"""Test catalog creation error handling"""
uc_service.client.catalogs.create = Mock(
side_effect=Exception("Permission denied")
)
result = uc_service.create_catalog("test_catalog")
assert result['success'] is False
assert "Permission denied" in result['message']
def test_create_schema_success(self, uc_service):
"""Test successful schema creation"""
mock_schema = Mock()
mock_schema.name = "test_schema"
mock_schema.catalog_name = "test_catalog"
mock_schema.owner = "test_user"
uc_service.client.schemas.create = Mock(return_value=mock_schema)
result = uc_service.create_schema("test_catalog", "test_schema")
assert result['success'] is True
assert "CREATE SCHEMA" in result['sql']
def test_validate_name_valid(self, uc_service):
"""Test name validation with valid names"""
assert uc_service.validate_name("valid_name") is True
assert uc_service.validate_name("ValidName123") is True
def test_validate_name_invalid(self, uc_service):
"""Test name validation with invalid names"""
assert uc_service.validate_name("invalid-name") is False
assert uc_service.validate_name("invalid name") is False
assert uc_service.validate_name("invalid.name") is False
class TestIntentParsing:
"""Tests for intent parsing with Claude"""
def test_parse_create_catalog(self, claude_client_mock):
"""Test parsing create catalog intent"""
mock_response = Mock()
mock_response.content = [
Mock(
text='{"intent": "createCatalog", "params": {"catalog": "sales"}, "explanation": "Creating catalog"}',
type='text'
)
]
claude_client_mock.messages.create.return_value = mock_response
result = parse_with_claude("Create a catalog named sales")
assert result['intent'] == 'createCatalog'
assert result['params']['catalog'] == 'sales'
def test_parse_grant_permission(self, claude_client_mock):
"""Test parsing grant permission intent"""
mock_response = Mock()
mock_response.content = [
Mock(
text='{"intent": "grantPermission", "params": {"privilege": "SELECT", "object": "sales.customers", "principal": "analysts"}}',
type='text'
)
]
claude_client_mock.messages.create.return_value = mock_response
result = parse_with_claude("Grant SELECT on sales.customers to analysts")
assert result['intent'] == 'grantPermission'
assert result['params']['privilege'] == 'SELECT'
def test_parse_error_handling(self, claude_client_mock):
"""Test error handling in parsing"""
claude_client_mock.messages.create.side_effect = Exception("API Error")
result = parse_with_claude("Invalid request")
assert result['intent'] == 'help'
class TestExecuteIntent:
"""Tests for intent execution"""
def test_execute_create_catalog(self):
"""Test executing create catalog intent"""
mock_uc = Mock()
mock_uc.create_catalog.return_value = {
'success': True,
'message': 'Catalog created',
'sql': 'CREATE CATALOG test'
}
with patch('app._init_services', return_value=(mock_uc, Mock())):
intent_data = {
'intent': 'createCatalog',
'params': {'catalog': 'test'}
}
result = execute_intent(intent_data)
assert result['success'] is True
mock_uc.create_catalog.assert_called_once()
def test_execute_grant_permission(self):
"""Test executing grant permission intent"""
mock_uc = Mock()
mock_uc.grant_permission.return_value = {
'success': True,
'message': 'Permission granted'
}
with patch('app._init_services', return_value=(mock_uc, Mock())):
intent_data = {
'intent': 'grantPermission',
'params': {
'privilege': 'SELECT',
'object': 'catalog.schema.table',
'principal': 'user'
}
}
result = execute_intent(intent_data)
assert result['success'] is True
mock_uc.grant_permission.assert_called_once()
def test_execute_help(self, uc_service):
"""Test executing help intent"""
intent_data = {'intent': 'help', 'params': {}}
result = execute_intent(intent_data)
assert result['success'] is True
assert 'help' in result['message'].lower()
class TestAPIEndpoints:
"""Integration tests for API endpoints"""
@pytest.fixture
def client(self):
"""Flask test client"""
from app import app
app.config['TESTING'] = True
with app.test_client() as client:
yield client
def test_health_endpoint(self, client):
"""Test health check endpoint"""
response = client.get('/api/health')
assert response.status_code == 200
assert response.json['status'] == 'healthy'
@patch('app.parse_with_claude')
@patch('app.execute_intent')
def test_chat_endpoint_success(self, mock_execute, mock_parse, client):
"""Test chat endpoint with successful response"""
mock_parse.return_value = {
'intent': 'createCatalog',
'params': {'catalog': 'test'},
'explanation': 'Creating catalog'
}
mock_execute.return_value = {
'success': True,
'message': 'Catalog created',
'sql': 'CREATE CATALOG test'
}
response = client.post('/api/chat', json={
'message': 'Create catalog test'
})
assert response.status_code == 200
assert response.json['success'] is True
def test_chat_endpoint_no_message(self, client):
"""Test chat endpoint without message"""
response = client.post('/api/chat', json={})
assert response.status_code == 400
def test_get_catalogs(self, client):
"""Test get catalogs endpoint"""
mock_uc = Mock()
mock_uc.list_catalogs.return_value = {
'success': True,
'catalogs': [{'name': 'test'}]
}
with patch('app._init_services', return_value=(mock_uc, Mock())):
response = client.get('/api/catalogs')
assert response.status_code == 200
assert 'catalogs' in response.json
class TestComplexScenarios:
"""Integration tests for complex scenarios"""
@pytest.fixture
def service(self, uc_service):
"""Service with mocked client"""
return uc_service
def test_medallion_architecture_setup(self, service):
"""Test setting up medallion architecture"""
# This would be a comprehensive integration test
# that tests creating multiple catalogs, schemas, and tables
pass
def test_permission_workflow(self, service):
"""Test complete permission management workflow"""
# Test grant, show, and revoke permissions
pass
def test_error_recovery(self, service):
"""Test error recovery scenarios"""
# Test handling of API errors, network issues, etc.
pass
# Performance tests
class TestPerformance:
"""Performance tests"""
def test_concurrent_requests(self):
"""Test handling concurrent requests"""
# Test with multiple simultaneous API calls
pass
def test_large_catalog_listing(self):
"""Test listing large number of catalogs"""
# Test performance with hundreds of catalogs
pass