Spaces:
Running
Running
""" | |
Unit tests for FoodWiseNotionClient. | |
Tests cover all CRUD operations with mocked Notion API responses. | |
""" | |
import pytest | |
from unittest.mock import Mock, patch | |
from datetime import date | |
from pydantic import ValidationError | |
from src.foodwise.notion.notion_client import FoodWiseNotionClient | |
def mock_notion_client(): | |
"""Create a mock Notion client for testing.""" | |
return Mock() | |
def foodwise_client(mock_notion_client, monkeypatch): | |
"""Create FoodWiseNotionClient with mocked Notion client.""" | |
# Mock environment variables | |
monkeypatch.setenv("NOTION_SECRET", "test-secret") | |
monkeypatch.setenv("NOTION_INVENTORY_DB_ID", "test-db-id") | |
with patch('src.foodwise.notion.notion_client.Client', return_value=mock_notion_client): | |
client = FoodWiseNotionClient() | |
client.client = mock_notion_client | |
return client | |
def sample_item_data(): | |
"""Sample item data for testing.""" | |
return { | |
"name": "Test Apple", | |
"category": "Fruits", | |
"storage_type": "Pantry", | |
"quantity": 5.0, | |
"unit": "pieces", | |
"best_by_date": "2024-12-31", | |
"purchase_date": "2024-12-01" | |
} | |
def mock_notion_page(): | |
"""Mock Notion page response.""" | |
return { | |
'id': 'test-page-id-123', | |
'url': 'https://notion.so/test-page', | |
'properties': { | |
'Name': { | |
'title': [{'text': {'content': 'Test Apple'}}] | |
}, | |
'Category': { | |
'select': {'name': 'Fruits'} | |
}, | |
'Storage Type': { | |
'select': {'name': 'Pantry'} | |
}, | |
'Quantity': { | |
'number': 5.0 | |
}, | |
'Unit': { | |
'select': {'name': 'pieces'} | |
} | |
} | |
} | |
class TestFoodWiseNotionClient: | |
"""Test cases for FoodWiseNotionClient.""" | |
def test_add_inventory_item_success(self, foodwise_client, sample_item_data, mock_notion_page): | |
"""Test successful item addition.""" | |
foodwise_client.client.pages.create.return_value = mock_notion_page | |
result = foodwise_client.add_inventory_item(sample_item_data) | |
assert result['id'] == 'test-page-id-123' | |
assert result['name'] == 'Test Apple' | |
assert result['category'] == 'Fruits' | |
foodwise_client.client.pages.create.assert_called_once() | |
def test_add_inventory_item_failure(self, foodwise_client, sample_item_data): | |
"""Test item addition failure handling.""" | |
foodwise_client.client.pages.create.side_effect = Exception("Notion API Error") | |
with pytest.raises(Exception, match="Failed to add item"): | |
foodwise_client.add_inventory_item(sample_item_data) | |
def test_query_inventory_success(self, foodwise_client, mock_notion_page): | |
"""Test successful inventory query.""" | |
foodwise_client.client.databases.query.return_value = { | |
'results': [mock_notion_page] | |
} | |
result = foodwise_client.query_inventory() | |
assert len(result) == 1 | |
assert result[0]['name'] == 'Test Apple' | |
foodwise_client.client.databases.query.assert_called_once() | |
def test_update_inventory_item_success(self, foodwise_client, mock_notion_page): | |
"""Test successful item update.""" | |
foodwise_client.client.pages.update.return_value = mock_notion_page | |
update_data = { | |
"quantity": 3.0, | |
"best_by_date": date(2024, 12, 20) | |
} | |
result = foodwise_client.update_inventory_item("test-page-id", update_data) | |
assert result['id'] == 'test-page-id-123' | |
foodwise_client.client.pages.update.assert_called_once() | |
def test_update_inventory_item_validation_error(self, foodwise_client): | |
"""Test update with invalid data.""" | |
invalid_data = { | |
"quantity": "not-a-number" # Invalid type | |
} | |
with pytest.raises(ValueError, match="Invalid update data"): | |
foodwise_client.update_inventory_item("test-page-id", invalid_data) | |
def test_update_inventory_item_empty_data(self, foodwise_client): | |
"""Test update with no valid fields.""" | |
with pytest.raises(Exception, match="No valid fields provided"): | |
foodwise_client.update_inventory_item("test-page-id", {}) | |
def test_remove_inventory_item_success(self, foodwise_client): | |
"""Test successful item removal.""" | |
foodwise_client.client.pages.update.return_value = {"archived": True} | |
result = foodwise_client.remove_inventory_item("test-page-id") | |
assert result['success'] is True | |
assert "archived successfully" in result['message'] | |
foodwise_client.client.pages.update.assert_called_once_with( | |
page_id="test-page-id", | |
archived=True | |
) | |
def test_remove_inventory_item_failure(self, foodwise_client): | |
"""Test item removal failure.""" | |
foodwise_client.client.pages.update.side_effect = Exception("Notion API Error") | |
with pytest.raises(Exception, match="Failed to remove item"): | |
foodwise_client.remove_inventory_item("test-page-id") | |
def test_search_items_success(self, foodwise_client, mock_notion_page): | |
"""Test successful item search.""" | |
foodwise_client.client.databases.query.return_value = { | |
'results': [mock_notion_page] | |
} | |
result = foodwise_client.search_items("apple") | |
assert len(result) == 1 | |
assert result[0]['name'] == 'Test Apple' | |
def test_get_expiring_items_success(self, foodwise_client, mock_notion_page): | |
"""Test successful expiring items query.""" | |
foodwise_client.client.databases.query.return_value = { | |
'results': [mock_notion_page] | |
} | |
result = foodwise_client.get_expiring_items(7) | |
assert len(result) == 1 | |
foodwise_client.client.databases.query.assert_called_once() | |
class TestPartialFoodInventoryItem: | |
"""Test the PartialFoodInventoryItem validation.""" | |
def test_valid_partial_data(self): | |
"""Test validation with valid partial data.""" | |
from src.foodwise.database.db_models import PartialFoodInventoryItem | |
data = { | |
"name": "Test Item", | |
"quantity": 2.5 | |
} | |
validated = PartialFoodInventoryItem.model_validate(data) | |
assert validated.name == "Test Item" | |
assert validated.quantity == 2.5 | |
assert validated.category is None | |
def test_invalid_partial_data(self): | |
"""Test validation with invalid data types.""" | |
from src.foodwise.database.db_models import PartialFoodInventoryItem | |
data = { | |
"quantity": "not-a-number" | |
} | |
with pytest.raises(ValidationError): | |
PartialFoodInventoryItem.model_validate(data) | |
def test_empty_partial_data(self): | |
"""Test validation with empty data.""" | |
from src.foodwise.database.db_models import PartialFoodInventoryItem | |
validated = PartialFoodInventoryItem.model_validate({}) | |
assert all(getattr(validated, field) is None for field in PartialFoodInventoryItem.model_fields) |