""" Tests for Help Command models Validates model creation, validation, and business logic. """ import pytest from datetime import UTC, datetime, timedelta from pydantic import ValidationError from models.help_command import ( HelpCommand, HelpCommandSearchFilters, HelpCommandSearchResult, HelpCommandStats, ) class TestHelpCommandModel: """Test HelpCommand model functionality.""" def test_help_command_creation_minimal(self): """Test help command creation with minimal required fields.""" help_cmd = HelpCommand( id=1, name="test-topic", title="Test Topic", content="This is test content", created_by_discord_id="123456789", created_at=datetime.now(UTC), ) assert help_cmd.id == 1 assert help_cmd.name == "test-topic" assert help_cmd.title == "Test Topic" assert help_cmd.content == "This is test content" assert help_cmd.created_by_discord_id == "123456789" assert help_cmd.is_active is True assert help_cmd.view_count == 0 def test_help_command_creation_with_optional_fields(self): """Test help command creation with all optional fields.""" now = datetime.now(UTC) help_cmd = HelpCommand( id=2, name="trading-rules", title="Trading Rules & Guidelines", content="Complete trading rules...", category="rules", created_by_discord_id="123456789", created_at=now, updated_at=now, last_modified_by="987654321", is_active=True, view_count=100, display_order=10, ) assert help_cmd.category == "rules" assert help_cmd.updated_at == now assert help_cmd.last_modified_by == "987654321" assert help_cmd.view_count == 100 assert help_cmd.display_order == 10 def test_help_command_name_validation(self): """Test help command name validation.""" base_data = { "id": 3, "title": "Test", "content": "Content", "created_by_discord_id": "123", "created_at": datetime.now(UTC), } # Valid names valid_names = ["test", "test-topic", "test_topic", "test123", "abc"] for name in valid_names: help_cmd = HelpCommand(name=name, **base_data) assert help_cmd.name == name.lower() # Invalid names - too short with pytest.raises(ValidationError): HelpCommand(name="a", **base_data) # Invalid names - too long with pytest.raises(ValidationError): HelpCommand(name="a" * 33, **base_data) # Invalid names - special characters with pytest.raises(ValidationError): HelpCommand(name="test@topic", **base_data) with pytest.raises(ValidationError): HelpCommand(name="test topic", **base_data) def test_help_command_title_validation(self): """Test help command title validation.""" base_data = { "id": 4, "name": "test", "content": "Content", "created_by_discord_id": "123", "created_at": datetime.now(UTC), } # Valid title help_cmd = HelpCommand(title="Test Topic", **base_data) assert help_cmd.title == "Test Topic" # Empty title with pytest.raises(ValidationError): HelpCommand(title="", **base_data) # Title too long with pytest.raises(ValidationError): HelpCommand(title="a" * 201, **base_data) def test_help_command_content_validation(self): """Test help command content validation.""" base_data = { "id": 5, "name": "test", "title": "Test", "created_by_discord_id": "123", "created_at": datetime.now(UTC), } # Valid content help_cmd = HelpCommand(content="Test content", **base_data) assert help_cmd.content == "Test content" # Empty content with pytest.raises(ValidationError): HelpCommand(content="", **base_data) # Content too long with pytest.raises(ValidationError): HelpCommand(content="a" * 4001, **base_data) def test_help_command_category_validation(self): """Test help command category validation.""" base_data = { "id": 6, "name": "test", "title": "Test", "content": "Content", "created_by_discord_id": "123", "created_at": datetime.now(UTC), } # Valid categories valid_categories = ["rules", "guides", "resources", "info", "faq"] for category in valid_categories: help_cmd = HelpCommand(category=category, **base_data) assert help_cmd.category == category.lower() # None category help_cmd = HelpCommand(category=None, **base_data) assert help_cmd.category is None # Invalid category - special characters with pytest.raises(ValidationError): HelpCommand(category="test@category", **base_data) def test_help_command_is_deleted_property(self): """Test is_deleted property.""" active = HelpCommand( id=7, name="active", title="Active Topic", content="Content", created_by_discord_id="123", created_at=datetime.now(UTC), is_active=True, ) deleted = HelpCommand( id=8, name="deleted", title="Deleted Topic", content="Content", created_by_discord_id="123", created_at=datetime.now(UTC), is_active=False, ) assert active.is_deleted is False assert deleted.is_deleted is True def test_help_command_days_since_update(self): """Test days_since_update property.""" # No updates no_update = HelpCommand( id=9, name="test", title="Test", content="Content", created_by_discord_id="123", created_at=datetime.now(UTC), updated_at=None, ) assert no_update.days_since_update is None # Recent update recent = HelpCommand( id=10, name="test", title="Test", content="Content", created_by_discord_id="123", created_at=datetime.now(UTC), updated_at=datetime.now(UTC) - timedelta(days=5), ) assert recent.days_since_update == 5 def test_help_command_days_since_creation(self): """Test days_since_creation property.""" old = HelpCommand( id=11, name="test", title="Test", content="Content", created_by_discord_id="123", created_at=datetime.now(UTC) - timedelta(days=30), ) assert old.days_since_creation == 30 def test_help_command_popularity_score(self): """Test popularity_score property.""" # No views no_views = HelpCommand( id=12, name="test", title="Test", content="Content", created_by_discord_id="123", created_at=datetime.now(UTC), view_count=0, ) assert no_views.popularity_score == 0.0 # New topic with views new_popular = HelpCommand( id=13, name="test", title="Test", content="Content", created_by_discord_id="123", created_at=datetime.now(UTC) - timedelta(days=5), view_count=50, ) score = new_popular.popularity_score assert score > 5.0 # Base score (5.0) with new topic bonus (1.5x) # Old topic with views old_popular = HelpCommand( id=14, name="test", title="Test", content="Content", created_by_discord_id="123", created_at=datetime.now(UTC) - timedelta(days=100), view_count=50, ) old_score = old_popular.popularity_score assert old_score < new_popular.popularity_score # Older topics get penalty class TestHelpCommandSearchFilters: """Test HelpCommandSearchFilters model.""" def test_search_filters_defaults(self): """Test search filters with default values.""" filters = HelpCommandSearchFilters() assert filters.name_contains is None assert filters.category is None assert filters.is_active is True assert filters.sort_by == "name" assert filters.sort_desc is False assert filters.page == 1 assert filters.page_size == 25 def test_search_filters_custom_values(self): """Test search filters with custom values.""" filters = HelpCommandSearchFilters( name_contains="trading", category="rules", is_active=False, sort_by="view_count", sort_desc=True, page=2, page_size=50, ) assert filters.name_contains == "trading" assert filters.category == "rules" assert filters.is_active is False assert filters.sort_by == "view_count" assert filters.sort_desc is True assert filters.page == 2 assert filters.page_size == 50 def test_search_filters_sort_by_validation(self): """Test sort_by field validation.""" # Valid sort fields valid_sorts = [ "name", "title", "category", "created_at", "updated_at", "view_count", "display_order", ] for sort_field in valid_sorts: filters = HelpCommandSearchFilters(sort_by=sort_field) assert filters.sort_by == sort_field # Invalid sort field with pytest.raises(ValidationError): HelpCommandSearchFilters(sort_by="invalid_field") def test_search_filters_page_validation(self): """Test page number validation.""" # Valid page numbers filters = HelpCommandSearchFilters(page=1) assert filters.page == 1 filters = HelpCommandSearchFilters(page=100) assert filters.page == 100 # Invalid page numbers with pytest.raises(ValidationError): HelpCommandSearchFilters(page=0) with pytest.raises(ValidationError): HelpCommandSearchFilters(page=-1) def test_search_filters_page_size_validation(self): """Test page size validation.""" # Valid page sizes filters = HelpCommandSearchFilters(page_size=1) assert filters.page_size == 1 filters = HelpCommandSearchFilters(page_size=100) assert filters.page_size == 100 # Invalid page sizes with pytest.raises(ValidationError): HelpCommandSearchFilters(page_size=0) with pytest.raises(ValidationError): HelpCommandSearchFilters(page_size=101) class TestHelpCommandSearchResult: """Test HelpCommandSearchResult model.""" def test_search_result_creation(self): """Test search result creation.""" help_commands = [ HelpCommand( id=i, name=f"topic-{i}", title=f"Topic {i}", content=f"Content {i}", created_by_discord_id="123", created_at=datetime.now(UTC), ) for i in range(1, 11) ] result = HelpCommandSearchResult( help_commands=help_commands, total_count=50, page=1, page_size=10, total_pages=5, has_more=True, ) assert len(result.help_commands) == 10 assert result.total_count == 50 assert result.page == 1 assert result.page_size == 10 assert result.total_pages == 5 assert result.has_more is True def test_search_result_start_index(self): """Test start_index property.""" result = HelpCommandSearchResult( help_commands=[], total_count=100, page=3, page_size=25, total_pages=4, has_more=True, ) assert result.start_index == 51 # (3-1) * 25 + 1 def test_search_result_end_index(self): """Test end_index property.""" # Last page with remaining items result = HelpCommandSearchResult( help_commands=[], total_count=55, page=3, page_size=25, total_pages=3, has_more=False, ) assert result.end_index == 55 # min(3 * 25, 55) # Full page result = HelpCommandSearchResult( help_commands=[], total_count=100, page=2, page_size=25, total_pages=4, has_more=True, ) assert result.end_index == 50 # min(2 * 25, 100) class TestHelpCommandStats: """Test HelpCommandStats model.""" def test_stats_creation(self): """Test stats creation.""" stats = HelpCommandStats( total_commands=50, active_commands=45, total_views=1000, most_viewed_command=None, recent_commands_count=5, ) assert stats.total_commands == 50 assert stats.active_commands == 45 assert stats.total_views == 1000 assert stats.most_viewed_command is None assert stats.recent_commands_count == 5 def test_stats_with_most_viewed(self): """Test stats with most viewed command.""" most_viewed = HelpCommand( id=1, name="popular-topic", title="Popular Topic", content="Content", created_by_discord_id="123", created_at=datetime.now(UTC), view_count=500, ) stats = HelpCommandStats( total_commands=50, active_commands=45, total_views=1000, most_viewed_command=most_viewed, recent_commands_count=5, ) assert stats.most_viewed_command is not None assert stats.most_viewed_command.name == "popular-topic" assert stats.most_viewed_command.view_count == 500 def test_stats_average_views_per_command(self): """Test average_views_per_command property.""" # Normal case stats = HelpCommandStats( total_commands=50, active_commands=40, total_views=800, most_viewed_command=None, recent_commands_count=5, ) assert stats.average_views_per_command == 20.0 # 800 / 40 # No active commands stats = HelpCommandStats( total_commands=10, active_commands=0, total_views=0, most_viewed_command=None, recent_commands_count=0, ) assert stats.average_views_per_command == 0.0 class TestHelpCommandFromAPIData: """Test creating HelpCommand from API data.""" def test_from_api_data_complete(self): """Test from_api_data with complete data.""" api_data = { "id": 1, "name": "trading-rules", "title": "Trading Rules & Guidelines", "content": "Complete trading rules...", "category": "rules", "created_by_discord_id": "123456789", "created_at": "2025-01-01T12:00:00", "updated_at": "2025-01-10T15:30:00", "last_modified_by": "987654321", "is_active": True, "view_count": 100, "display_order": 10, } help_cmd = HelpCommand.from_api_data(api_data) assert help_cmd.id == 1 assert help_cmd.name == "trading-rules" assert help_cmd.title == "Trading Rules & Guidelines" assert help_cmd.content == "Complete trading rules..." assert help_cmd.category == "rules" assert help_cmd.view_count == 100 def test_from_api_data_minimal(self): """Test from_api_data with minimal required data.""" api_data = { "id": 2, "name": "simple-topic", "title": "Simple Topic", "content": "Simple content", "created_by_discord_id": "123456789", "created_at": "2025-01-01T12:00:00", } help_cmd = HelpCommand.from_api_data(api_data) assert help_cmd.id == 2 assert help_cmd.name == "simple-topic" assert help_cmd.category is None assert help_cmd.updated_at is None assert help_cmd.view_count == 0