"""End-to-End Testing Script for Milestone 7. Tests complete workflow as specified in Milestone 7: 1. Create 3 recipes (different categories) 2. Add outputs for each 3. Test search by name 4. Test filter by tag 5. Modify a recipe 6. Delete a recipe with outputs (verify cascade) 7. Verify all functionality works end-to-end """ from src.database.db_manager import DatabaseManager from src.database.models import Recipe, Output from src.services.recipe_service import RecipeService from src.services.output_service import OutputService from pathlib import Path import tempfile def test_end_to_end(): """Complete end-to-end workflow test.""" print("=" * 70) print("ChefSystem - End-to-End Testing (Milestone 7)") print("=" * 70) print("") # Initialize services db = DatabaseManager("database/chef.db") db.init_database() recipe_service = RecipeService(db) output_service = OutputService(db) created_recipe_ids = [] # Test 1: Create 3 recipes in different categories print("Test 1: Creating 3 recipes in different categories...") recipes_to_create = [ { "name": "Financial Analysis Report", "prompt_text": "Analyze the following financial data and provide insights on trends, risks, and opportunities:\n\n[PASTE DATA HERE]", "tags": "finance,analysis,reporting", "description": "Generate comprehensive financial analysis reports", "notes": "Use with quarterly or monthly financial data" }, { "name": "Software Architecture Design", "prompt_text": "Design a software architecture for the following requirements:\n\n[PASTE REQUIREMENTS HERE]\n\nProvide class diagrams, sequence diagrams, and technology stack recommendations.", "tags": "development,architecture,design", "description": "Create software architecture designs", "notes": "Best for greenfield projects" }, { "name": "Data Science EDA Template", "prompt_text": "Perform exploratory data analysis on the following dataset:\n\n[PASTE DATASET DESCRIPTION]\n\nInclude statistical summaries, visualizations, and insights.", "tags": "data-science,analysis,python", "description": "Exploratory data analysis template", "notes": "Works well with Pandas/NumPy datasets" } ] for recipe_data in recipes_to_create: recipe = recipe_service.create_recipe(**recipe_data) if recipe: created_recipe_ids.append(recipe.id) print(f" ✓ Created: {recipe.name} (ID: {recipe.id})") else: print(f" ✗ Failed to create: {recipe_data['name']}") assert len(created_recipe_ids) == 3, "Should create 3 recipes" print(f" ✓ All 3 recipes created successfully\n") # Test 2: Add 2 outputs for each recipe print("Test 2: Adding outputs for each recipe...") total_outputs = 0 for recipe_id in created_recipe_ids: # Create temporary test files for i in range(2): with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as temp_file: temp_file.write(f"Test output {i+1} for recipe {recipe_id}") temp_path = temp_file.name output = output_service.save_output( recipe_id, temp_path, f"Test execution #{i+1} notes for recipe {recipe_id}" ) if output: total_outputs += 1 print(f" ✓ Added output for recipe {recipe_id}: {output.filename}") # Clean up temp file Path(temp_path).unlink() assert total_outputs == 6, "Should create 6 outputs (2 per recipe)" print(f" ✓ All {total_outputs} outputs added successfully\n") # Test 3: Search by name print("Test 3: Testing search by name...") search_results = recipe_service.search_recipes("Financial") assert len(search_results) == 1, "Should find 1 recipe with 'Financial'" assert search_results[0].name == "Financial Analysis Report" print(f" ✓ Search 'Financial': Found {len(search_results)} recipe") search_results = recipe_service.search_recipes("Analysis") assert len(search_results) >= 2, "Should find at least 2 recipes with 'Analysis'" print(f" ✓ Search 'Analysis': Found {len(search_results)} recipes\n") # Test 4: Filter by tag print("Test 4: Testing filter by tags...") finance_recipes = recipe_service.filter_by_tags(["finance"]) assert len(finance_recipes) == 1, "Should find 1 recipe with 'finance' tag" print(f" ✓ Filter by 'finance': Found {len(finance_recipes)} recipe") analysis_recipes = recipe_service.filter_by_tags(["analysis"]) assert len(analysis_recipes) >= 2, "Should find at least 2 recipes with 'analysis' tag" print(f" ✓ Filter by 'analysis': Found {len(analysis_recipes)} recipes") dev_recipes = recipe_service.filter_by_tags(["development"]) assert len(dev_recipes) == 1, "Should find 1 recipe with 'development' tag" print(f" ✓ Filter by 'development': Found {len(dev_recipes)} recipe\n") # Test 5: Modify a recipe print("Test 5: Modifying a recipe...") recipe_to_modify = created_recipe_ids[0] success = recipe_service.update_recipe( recipe_to_modify, description="Updated: Comprehensive financial analysis with AI-generated insights", notes="Updated notes: Now includes trend forecasting" ) assert success, "Should successfully update recipe" updated_recipe = recipe_service.get_recipe(recipe_to_modify) assert "Updated:" in updated_recipe.description assert "Updated notes:" in updated_recipe.notes print(f" ✓ Modified recipe ID {recipe_to_modify}") print(f" - New description: {updated_recipe.description[:60]}...") print(f" - New notes: {updated_recipe.notes[:60]}...\n") # Test 6: Delete a recipe with outputs (verify CASCADE) print("Test 6: Deleting recipe with outputs (testing CASCADE)...") recipe_to_delete = created_recipe_ids[1] # Count outputs before deletion outputs_before = output_service.get_recipe_outputs(recipe_to_delete) assert len(outputs_before) == 2, "Should have 2 outputs before deletion" print(f" - Recipe ID {recipe_to_delete} has {len(outputs_before)} outputs") # Delete recipe success = recipe_service.delete_recipe(recipe_to_delete) assert success, "Should successfully delete recipe" print(f" ✓ Deleted recipe ID {recipe_to_delete}") # Verify recipe is gone deleted_recipe = recipe_service.get_recipe(recipe_to_delete) assert deleted_recipe is None, "Recipe should be deleted" print(f" ✓ Recipe confirmed deleted from database") # Verify outputs are also gone (CASCADE) outputs_after = output_service.get_recipe_outputs(recipe_to_delete) assert len(outputs_after) == 0, "Outputs should be CASCADE deleted" print(f" ✓ Outputs CASCADE deleted (0 outputs remain)\n") # Test 7: Verify remaining data integrity print("Test 7: Verifying remaining data integrity...") all_recipes = recipe_service.get_all_recipes() expected_count = len(created_recipe_ids) - 1 # Created 3, deleted 1 actual_count = sum(1 for r in all_recipes if r.id in created_recipe_ids) assert actual_count == expected_count, f"Should have {expected_count} recipes remaining" print(f" ✓ {actual_count} recipes remain (correct)") all_outputs = output_service.get_all_outputs() remaining_outputs = [o for o in all_outputs if o.recipe_id in created_recipe_ids] assert len(remaining_outputs) == 4, "Should have 4 outputs remaining (6 - 2)" print(f" ✓ {len(remaining_outputs)} outputs remain (correct)\n") # Test 8: Test name uniqueness validation print("Test 8: Testing name uniqueness validation...") duplicate_recipe = recipe_service.create_recipe( name="Financial Analysis Report", # Duplicate name prompt_text="This should fail", tags="test" ) assert duplicate_recipe is None, "Should reject duplicate recipe name" print(f" ✓ Duplicate name rejected correctly\n") # Test 9: Test combined search + tag filter print("Test 9: Testing combined search + tag filter...") combined_results = recipe_service.search_and_filter("Analysis", ["data-science"]) assert len(combined_results) == 1, "Should find 1 recipe matching both criteria" assert combined_results[0].name == "Data Science EDA Template" print(f" ✓ Combined search+filter: Found {len(combined_results)} recipe") print(f" - {combined_results[0].name}\n") # Test 10: Test empty search/filter (should return all) print("Test 10: Testing empty search/filter...") empty_search = recipe_service.search_recipes("") assert len(empty_search) >= 2, "Empty search should return remaining recipes" print(f" ✓ Empty search returns {len(empty_search)} recipes") empty_filter = recipe_service.filter_by_tags([]) assert len(empty_filter) >= 2, "Empty tag filter should return all recipes" print(f" ✓ Empty tag filter returns {len(empty_filter)} recipes\n") # Cleanup: Delete test recipes print("Cleanup: Removing test recipes...") for recipe_id in created_recipe_ids: if recipe_service.get_recipe(recipe_id): # Only delete if still exists recipe_service.delete_recipe(recipe_id) print(f" ✓ Cleaned up recipe ID {recipe_id}") db.close() # Final summary print("") print("=" * 70) print("End-to-End Testing Complete!") print("=" * 70) print("") print("Summary:") print("✅ Recipe creation (3 recipes in different categories)") print("✅ Output management (2 outputs per recipe)") print("✅ Search by name (partial matching)") print("✅ Filter by tags (single and multiple)") print("✅ Recipe modification (update description and notes)") print("✅ Recipe deletion with CASCADE (outputs deleted automatically)") print("✅ Data integrity verification") print("✅ Name uniqueness validation") print("✅ Combined search + tag filter") print("✅ Empty search/filter handling") print("") print("All 10 end-to-end test scenarios passed! 🎉") print("") if __name__ == "__main__": test_end_to_end()