| """ |
| Test suite for token optimization changes. |
| Ensures backward compatibility and validates token reduction. |
| """ |
|
|
| import json |
| from unittest.mock import Mock, patch |
|
|
| import pytest |
|
|
| from src.server.services.projects import ProjectService |
| from src.server.services.projects.document_service import DocumentService |
| from src.server.services.projects.task_service import TaskService |
|
|
|
|
| class TestProjectServiceOptimization: |
| """Test ProjectService with include_content parameter.""" |
|
|
| @patch("src.server.utils.get_supabase_client") |
| async def test_list_projects_with_full_content(self, mock_supabase): |
| """Test backward compatibility - default returns full content.""" |
| |
| mock_client = Mock() |
| mock_supabase.return_value = mock_client |
|
|
| |
| mock_response = Mock() |
| mock_response.data = [ |
| { |
| "id": "test-id", |
| "title": "Test Project", |
| "description": "Test Description", |
| "github_repo": "https://github.com/test/repo", |
| "docs": [{"id": "doc1", "content": {"large": "content" * 100}}], |
| "features": [{"feature1": "data"}], |
| "data": [{"key": "value"}], |
| "pinned": False, |
| "created_at": "2024-01-01", |
| "updated_at": "2024-01-01", |
| } |
| ] |
|
|
| mock_table = Mock() |
| mock_select = Mock() |
| mock_order = Mock() |
| mock_order.execute = Mock(return_value=mock_response) |
| mock_select.order.return_value = mock_order |
| mock_table.select.return_value = mock_select |
| mock_client.table.return_value = mock_table |
|
|
| |
| service = ProjectService(mock_client) |
| success, result = await service.list_projects() |
|
|
| |
| assert success |
| assert len(result["projects"]) == 1 |
| assert "docs" in result["projects"][0] |
| assert "features" in result["projects"][0] |
| assert "data" in result["projects"][0] |
|
|
| |
| assert len(result["projects"][0]["docs"]) == 1 |
| assert result["projects"][0]["docs"][0]["content"]["large"] is not None |
|
|
| |
| mock_table.select.assert_called_with("*") |
|
|
| @patch("src.server.utils.get_supabase_client") |
| async def test_list_projects_lightweight(self, mock_supabase): |
| """Test lightweight response excludes large fields.""" |
| |
| mock_client = Mock() |
| mock_supabase.return_value = mock_client |
|
|
| |
| mock_response = Mock() |
| mock_response.data = [ |
| { |
| "id": "test-id", |
| "title": "Test Project", |
| "description": "Test Description", |
| "github_repo": "https://github.com/test/repo", |
| "created_at": "2024-01-01", |
| "updated_at": "2024-01-01", |
| "pinned": False, |
| "docs": [{"id": "doc1"}, {"id": "doc2"}, {"id": "doc3"}], |
| "features": [{"feature1": "data"}, {"feature2": "data"}], |
| "data": [{"key": "value"}], |
| } |
| ] |
|
|
| |
| mock_table = Mock() |
| mock_select = Mock() |
| mock_order = Mock() |
|
|
| mock_order.execute = Mock(return_value=mock_response) |
| mock_select.order.return_value = mock_order |
| mock_table.select.return_value = mock_select |
| mock_client.table.return_value = mock_table |
|
|
| |
| service = ProjectService(mock_client) |
| success, result = await service.list_projects(include_content=False) |
|
|
| |
| assert success |
| assert len(result["projects"]) == 1 |
| project = result["projects"][0] |
|
|
| |
| assert "docs" not in project |
| assert "features" not in project |
| assert "data" not in project |
|
|
| |
| assert "stats" in project |
| assert project["stats"]["docs_count"] == 3 |
| assert project["stats"]["features_count"] == 2 |
| assert project["stats"]["has_data"] is True |
|
|
| |
| mock_table.select.assert_called_with("*") |
| assert mock_client.table.call_count == 1 |
|
|
| def test_token_reduction(self): |
| """Verify token count reduction.""" |
| |
| full_content = { |
| "projects": [ |
| { |
| "id": "test", |
| "title": "Test", |
| "description": "Test Description", |
| "docs": [{"content": {"large": "x" * 10000}} for _ in range(5)], |
| "features": [{"data": "y" * 5000} for _ in range(3)], |
| "data": [{"values": "z" * 8000}], |
| } |
| ] |
| } |
|
|
| |
| lightweight = { |
| "projects": [ |
| { |
| "id": "test", |
| "title": "Test", |
| "description": "Test Description", |
| "stats": {"docs_count": 5, "features_count": 3, "has_data": True}, |
| } |
| ] |
| } |
|
|
| |
| full_tokens = len(json.dumps(full_content)) / 4 |
| light_tokens = len(json.dumps(lightweight)) / 4 |
|
|
| reduction_percentage = (1 - light_tokens / full_tokens) * 100 |
|
|
| |
| assert reduction_percentage > 95, f"Token reduction is only {reduction_percentage:.1f}%" |
|
|
|
|
| class TestTaskServiceOptimization: |
| """Test TaskService with exclude_large_fields parameter.""" |
|
|
| @patch("src.server.utils.get_supabase_client") |
| async def test_list_tasks_with_large_fields(self, mock_supabase): |
| """Test backward compatibility - default includes large fields.""" |
| mock_client = Mock() |
| mock_supabase.return_value = mock_client |
|
|
| mock_response = Mock() |
| mock_response.data = [ |
| { |
| "id": "task-1", |
| "project_id": "proj-1", |
| "title": "Test Task", |
| "description": "Test Description", |
| "sources": [{"url": "http://example.com", "content": "large"}], |
| "code_examples": [{"code": "function() { /* large */ }"}], |
| "status": "todo", |
| "assignee": "User", |
| "task_order": 0, |
| "feature": None, |
| "created_at": "2024-01-01", |
| "updated_at": "2024-01-01", |
| } |
| ] |
|
|
| |
| mock_table = Mock() |
| mock_select = Mock() |
| mock_or = Mock() |
| mock_order1 = Mock() |
| mock_order2 = Mock() |
|
|
| mock_order2.execute = Mock(return_value=mock_response) |
| mock_order1.order.return_value = mock_order2 |
| mock_or.order.return_value = mock_order1 |
| mock_select.neq().or_.return_value = mock_or |
| mock_table.select.return_value = mock_select |
| mock_client.table.return_value = mock_table |
|
|
| service = TaskService(mock_client) |
| success, result = await service.list_tasks() |
|
|
| assert success |
| assert "sources" in result["tasks"][0] |
| assert "code_examples" in result["tasks"][0] |
|
|
| @patch("src.server.utils.get_supabase_client") |
| async def test_list_tasks_exclude_large_fields(self, mock_supabase): |
| """Test excluding large fields returns counts instead.""" |
| mock_client = Mock() |
| mock_supabase.return_value = mock_client |
|
|
| mock_response = Mock() |
| mock_response.data = [ |
| { |
| "id": "task-1", |
| "project_id": "proj-1", |
| "title": "Test Task", |
| "description": "Test Description", |
| "status": "todo", |
| "assignee": "User", |
| "task_order": 0, |
| "feature": None, |
| "sources": [1, 2, 3], |
| "code_examples": [1, 2], |
| "created_at": "2024-01-01", |
| "updated_at": "2024-01-01", |
| } |
| ] |
|
|
| |
| mock_table = Mock() |
| mock_select = Mock() |
| mock_or = Mock() |
| mock_order1 = Mock() |
| mock_order2 = Mock() |
|
|
| mock_order2.execute = Mock(return_value=mock_response) |
| mock_order1.order.return_value = mock_order2 |
| mock_or.order.return_value = mock_order1 |
| mock_select.neq().or_.return_value = mock_or |
| mock_table.select.return_value = mock_select |
| mock_client.table.return_value = mock_table |
|
|
| service = TaskService(mock_client) |
| success, result = await service.list_tasks(exclude_large_fields=True) |
|
|
| assert success |
| task = result["tasks"][0] |
| assert "sources" not in task |
| assert "code_examples" not in task |
| assert "stats" in task |
| assert task["stats"]["sources_count"] == 3 |
| assert task["stats"]["code_examples_count"] == 2 |
|
|
|
|
| class TestDocumentServiceOptimization: |
| """Test DocumentService with include_content parameter.""" |
|
|
| @patch("src.server.utils.get_supabase_client") |
| def test_list_documents_metadata_only(self, mock_supabase): |
| """Test default returns metadata only.""" |
| mock_client = Mock() |
| mock_supabase.return_value = mock_client |
|
|
| mock_response = Mock() |
| mock_response.data = [ |
| { |
| "docs": [ |
| { |
| "id": "doc-1", |
| "title": "Test Doc", |
| "content": {"huge": "content" * 1000}, |
| "document_type": "spec", |
| "status": "draft", |
| "version": "1.0", |
| "tags": ["test"], |
| "author": "Test Author", |
| } |
| ] |
| } |
| ] |
|
|
| |
| mock_table = Mock() |
| mock_select = Mock() |
| mock_eq = Mock() |
|
|
| mock_eq.execute.return_value = mock_response |
| mock_select.eq.return_value = mock_eq |
| mock_table.select.return_value = mock_select |
| mock_client.table.return_value = mock_table |
|
|
| service = DocumentService(mock_client) |
| success, result = service.list_documents("project-1") |
|
|
| assert success |
| doc = result["documents"][0] |
| assert "content" not in doc |
| assert "stats" in doc |
| assert doc["stats"]["content_size"] > 0 |
| assert doc["title"] == "Test Doc" |
|
|
| @patch("src.server.utils.get_supabase_client") |
| def test_list_documents_with_content(self, mock_supabase): |
| """Test include_content=True returns full documents.""" |
| mock_client = Mock() |
| mock_supabase.return_value = mock_client |
|
|
| mock_response = Mock() |
| mock_response.data = [ |
| {"docs": [{"id": "doc-1", "title": "Test Doc", "content": {"huge": "content"}, "document_type": "spec"}]} |
| ] |
|
|
| |
| mock_table = Mock() |
| mock_select = Mock() |
| mock_eq = Mock() |
|
|
| mock_eq.execute.return_value = mock_response |
| mock_select.eq.return_value = mock_eq |
| mock_table.select.return_value = mock_select |
| mock_client.table.return_value = mock_table |
|
|
| service = DocumentService(mock_client) |
| success, result = service.list_documents("project-1", include_content=True) |
|
|
| assert success |
| doc = result["documents"][0] |
| assert "content" in doc |
| assert doc["content"]["huge"] == "content" |
|
|
|
|
| class TestBackwardCompatibility: |
| """Ensure all changes are backward compatible.""" |
|
|
| def test_api_defaults_preserve_behavior(self): |
| """Test that API defaults maintain current behavior.""" |
| |
| service = ProjectService(Mock()) |
| |
| import inspect |
|
|
| sig = inspect.signature(service.list_projects) |
| assert sig.parameters["include_content"].default is True |
|
|
| |
| doc_service = DocumentService(Mock()) |
| sig = inspect.signature(doc_service.list_documents) |
| assert sig.parameters["include_content"].default is False |
|
|
| |
| task_service = TaskService(Mock()) |
| sig = inspect.signature(task_service.list_tasks) |
| assert sig.parameters["exclude_large_fields"].default is False |
|
|
|
|
| if __name__ == "__main__": |
| pytest.main([__file__, "-v"]) |
|
|