"""Test search and filter functionality for Milestone 6.""" from src.database.db_manager import DatabaseManager from src.database.models import Recipe, Output from src.services.output_service import OutputService from pathlib import Path def test_search_and_filter(): """Test all search and filter operations.""" print("=" * 60) print("Testing Search & Filter Functionality (Milestone 6)") print("=" * 60) # Initialize database db = DatabaseManager("database/chef.db") db.init_database() output_service = OutputService(db) # Test 1: Recipe search by name print("\n1. Testing Recipe.search() by name...") results = Recipe.search(db, query="Python") print(f" Search 'Python': Found {len(results)} recipe(s)") for recipe in results: print(f" - {recipe.name}") assert len(results) >= 1, "Should find at least one recipe with 'Python' in name" print(" ✓ Name search works") # Test 2: Recipe search by description print("\n2. Testing Recipe.search() by description...") results = Recipe.search(db, query="documentation") print(f" Search 'documentation': Found {len(results)} recipe(s)") for recipe in results: desc_preview = recipe.description[:50] if recipe.description else "" print(f" - {recipe.name} (description: {desc_preview}...)") assert len(results) >= 1, "Should find at least one recipe with 'documentation' in description" print(" ✓ Description search works") # Test 3: Recipe search by notes print("\n3. Testing Recipe.search() by notes...") # First, update a recipe to add notes all_recipes = Recipe.get_all(db) if all_recipes: Recipe.update(db, all_recipes[0].id, notes="This is a test note with keyword SEARCHME") results = Recipe.search(db, query="SEARCHME") print(f" Search 'SEARCHME': Found {len(results)} recipe(s)") for recipe in results: print(f" - {recipe.name}") assert len(results) >= 1, "Should find recipe with 'SEARCHME' in notes" print(" ✓ Notes search works") # Test 4: Recipe search is case-insensitive print("\n4. Testing case-insensitive search...") results_lower = Recipe.search(db, query="python") results_upper = Recipe.search(db, query="PYTHON") results_mixed = Recipe.search(db, query="PyThOn") print(f" Search 'python': {len(results_lower)} recipe(s)") print(f" Search 'PYTHON': {len(results_upper)} recipe(s)") print(f" Search 'PyThOn': {len(results_mixed)} recipe(s)") assert len(results_lower) == len(results_upper) == len(results_mixed), "Case-insensitive search failed" print(" ✓ Case-insensitive search works") # Test 5: Tag filter - single tag print("\n5. Testing Recipe.search() by single tag...") results = Recipe.search(db, tags=["python"]) print(f" Filter by tag 'python': Found {len(results)} recipe(s)") for recipe in results: print(f" - {recipe.name} (tags: {recipe.tags})") assert len(results) >= 1, "Should find at least one recipe with 'python' tag" print(" ✓ Single tag filter works") # Test 6: Tag filter - multiple tags (ANY match) print("\n6. Testing Recipe.search() by multiple tags (ANY match)...") results = Recipe.search(db, tags=["python", "sql"]) print(f" Filter by tags ['python', 'sql']: Found {len(results)} recipe(s)") for recipe in results: print(f" - {recipe.name} (tags: {recipe.tags})") assert len(results) >= 2, "Should find at least two recipes with 'python' or 'sql' tags" print(" ✓ Multiple tag filter (ANY match) works") # Test 7: Combined search query + tag filter print("\n7. Testing combined search query + tag filter...") results = Recipe.search(db, query="Code", tags=["python"]) print(f" Search 'Code' + tag 'python': Found {len(results)} recipe(s)") for recipe in results: print(f" - {recipe.name} (tags: {recipe.tags})") assert len(results) >= 1, "Should find at least one recipe with 'Code' and 'python' tag" print(" ✓ Combined search + tag filter works") # Test 8: Empty query returns all recipes print("\n8. Testing empty query (should return all)...") results_empty = Recipe.search(db, query="") results_all = Recipe.get_all(db) print(f" Empty search: {len(results_empty)} recipe(s)") print(f" Get all: {len(results_all)} recipe(s)") assert len(results_empty) == len(results_all), "Empty query should return all recipes" print(" ✓ Empty query returns all recipes") # Test 9: Empty tags list ignores tag filter print("\n9. Testing empty tags list (should ignore tag filter)...") results_no_tags = Recipe.search(db, query="Python", tags=None) results_empty_tags = Recipe.search(db, query="Python", tags=[]) print(f" Search 'Python' with tags=None: {len(results_no_tags)} recipe(s)") print(f" Search 'Python' with tags=[]: {len(results_empty_tags)} recipe(s)") assert len(results_no_tags) == len(results_empty_tags), "Empty tags should be ignored" print(" ✓ Empty tags list ignored correctly") # Test 10: Output search in OutputManager (simulated) print("\n10. Testing output search by filename...") # Get a recipe with outputs recipes = Recipe.get_all(db) if recipes: recipe = recipes[0] all_outputs = output_service.get_recipe_outputs(recipe.id) # Test filename search (case-insensitive) if all_outputs: search_query = "test" filtered = [ output for output in all_outputs if search_query.lower() in output.filename.lower() or (output.execution_notes and search_query.lower() in output.execution_notes.lower()) ] print(f" Recipe '{recipe.name}' has {len(all_outputs)} output(s)") print(f" Search 'test': Found {len(filtered)} output(s)") for output in filtered: print(f" - {output.filename}") print(" ✓ Output filename search works") else: print(" ⚠ No outputs found to test output search") # Test 11: Output search by notes print("\n11. Testing output search by execution notes...") if all_outputs: # Find outputs with notes search_query = "test" filtered = [ output for output in all_outputs if output.execution_notes and search_query.lower() in output.execution_notes.lower() ] print(f" Search 'test' in notes: Found {len(filtered)} output(s)") for output in filtered: print(f" - {output.filename} (notes: {output.execution_notes[:50]}...)") print(" ✓ Output notes search works") else: print(" ⚠ No outputs found to test notes search") # Close database db.close() print("\n" + "=" * 60) print("All Search & Filter Tests Passed!") print("=" * 60) print("\nSummary:") print("✓ Recipe search by name") print("✓ Recipe search by description") print("✓ Recipe search by notes") print("✓ Case-insensitive search") print("✓ Tag filter (single tag)") print("✓ Tag filter (multiple tags - ANY match)") print("✓ Combined search query + tag filter") print("✓ Empty query returns all recipes") print("✓ Empty tags list ignored") print("✓ Output search by filename") print("✓ Output search by execution notes") if __name__ == "__main__": test_search_and_filter()