213 lines
7.0 KiB
Python
213 lines
7.0 KiB
Python
"""
|
|
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()) |