""" Test script to validate all requirements for bulk shot creation enhancement Validates Requirements 3.5: Bulk shot creation project consistency """ import requests import json BASE_URL = "http://localhost:8000" # Test credentials LOGIN_DATA = { "email": "admin@vfx.com", "password": "admin123" } def login(): """Login and get access token""" response = requests.post(f"{BASE_URL}/auth/login", json=LOGIN_DATA) if response.status_code == 200: return response.json()["access_token"] else: print(f"Login failed: {response.status_code}") return None def test_requirement_3_5(): """ Test Requirement 3.5: WHEN bulk creating shots, THE VFX_System SHALL validate that all shots belong to the same project as the episode """ print("=== Testing Requirement 3.5: Bulk Shot Project Consistency ===\n") token = login() if not token: return False headers = {"Authorization": f"Bearer {token}"} episode_id = 1 # Get episode information first to know which project it belongs to episode_response = requests.get(f"{BASE_URL}/episodes/{episode_id}", headers=headers) if episode_response.status_code != 200: print(f" ✗ Could not get episode info: {episode_response.json()}") return False episode_data = episode_response.json() expected_project_id = episode_data['project_id'] print(f" Episode {episode_id} belongs to project {expected_project_id}") # Test 1: Verify all shots in bulk creation belong to same project as episode print("\n1. Testing project consistency validation...") import time timestamp = int(time.time()) bulk_data = { "name_prefix": f"REQ35_{timestamp}_", "shot_count": 5, "start_number": 1, "number_padding": 3, "frame_start": 1001, "frame_end": 1100, "description_template": "Requirement 3.5 test shot {shot_name}", "create_default_tasks": True } response = requests.post( f"{BASE_URL}/shots/bulk?episode_id={episode_id}", headers=headers, json=bulk_data ) if response.status_code != 201: print(f" ✗ Bulk creation failed: {response.json()}") return False result = response.json() created_shots = result['created_shots'] # Verify all shots have the same project_id as the episode project_ids = [shot['project_id'] for shot in created_shots] all_same_project = len(set(project_ids)) == 1 correct_project = all(pid == expected_project_id for pid in project_ids) print(f" ✓ Created {len(created_shots)} shots") print(f" ✓ All shots have same project_id: {all_same_project}") print(f" ✓ All shots belong to episode's project ({expected_project_id}): {correct_project}") if not (all_same_project and correct_project): print(f" ✗ Project consistency validation failed!") print(f" Expected project_id: {expected_project_id}") print(f" Actual project_ids: {project_ids}") return False # Test 2: Verify project-scoped uniqueness validation print("\n2. Testing project-scoped uniqueness validation...") # Try to create shots with same names in same project (should fail) duplicate_data = { "name_prefix": f"REQ35_{timestamp}_", # Same prefix as above "shot_count": 3, "start_number": 1, # Same start number - will create duplicates "number_padding": 3, "frame_start": 1001, "frame_end": 1100, "create_default_tasks": False } response = requests.post( f"{BASE_URL}/shots/bulk?episode_id={episode_id}", headers=headers, json=duplicate_data ) if response.status_code == 400: error_detail = response.json()['detail'] if "already exist in project" in error_detail: print(f" ✓ Correctly rejected duplicate names in project: {error_detail}") else: print(f" ✗ Wrong error message: {error_detail}") return False else: print(f" ✗ Should have rejected duplicate names: {response.json()}") return False # Test 3: Verify bulk validation prevents partial creation print("\n3. Testing atomic bulk validation...") # Create a scenario where some names would be duplicates mixed_data = { "name_prefix": f"MIXED_{timestamp}_", "shot_count": 5, "start_number": 1, "number_padding": 3, "frame_start": 1001, "frame_end": 1100, "create_default_tasks": False } # First, create some shots response1 = requests.post( f"{BASE_URL}/shots/bulk?episode_id={episode_id}", headers=headers, json=mixed_data ) if response1.status_code != 201: print(f" ✗ First bulk creation failed: {response1.json()}") return False # Now try to create overlapping shots (should fail completely) overlapping_data = { "name_prefix": f"MIXED_{timestamp}_", "shot_count": 5, "start_number": 3, # Will overlap with shots 3, 4, 5 from above "number_padding": 3, "frame_start": 1001, "frame_end": 1100, "create_default_tasks": False } response2 = requests.post( f"{BASE_URL}/shots/bulk?episode_id={episode_id}", headers=headers, json=overlapping_data ) if response2.status_code == 400: print(f" ✓ Correctly rejected overlapping bulk creation") # Verify no partial shots were created by checking shot count list_response = requests.get( f"{BASE_URL}/shots/?episode_id={episode_id}", headers=headers ) if list_response.status_code == 200: shots = list_response.json() mixed_shots = [s for s in shots if s['name'].startswith(f"MIXED_{timestamp}_")] if len(mixed_shots) == 5: # Only the first 5 should exist print(f" ✓ No partial creation occurred - atomic validation working") else: print(f" ✗ Partial creation detected: {len(mixed_shots)} shots found") return False else: print(f" ✗ Could not verify shot count: {list_response.json()}") return False else: print(f" ✗ Should have rejected overlapping creation: {response2.json()}") return False print("\n✓ All Requirement 3.5 validations passed!") return True def main(): """Run all requirement validation tests""" print("=== Bulk Shot Creation Requirements Validation ===\n") success = test_requirement_3_5() if success: print("\n🎉 All requirements validated successfully!") print("✓ Requirement 3.5: Bulk shot project consistency - PASSED") else: print("\n❌ Requirements validation failed!") return 1 return 0 if __name__ == "__main__": exit(main())