Init Repo
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script to verify the shot project_id migration.
|
||||
Tests all requirements: 2.1, 2.2, 2.3, 2.4, 2.5
|
||||
"""
|
||||
|
||||
import sqlite3
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def test_migration():
|
||||
"""Test that the migration meets all requirements."""
|
||||
|
||||
db_path = "vfx_project_management.db"
|
||||
if not Path(db_path).exists():
|
||||
print("❌ Database not found")
|
||||
return False
|
||||
|
||||
try:
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
print("Testing Shot Project ID Migration Requirements")
|
||||
print("=" * 50)
|
||||
|
||||
# Requirement 2.1: Non-nullable project_id column
|
||||
print("Testing Requirement 2.1: Non-nullable project_id column...")
|
||||
cursor.execute("PRAGMA table_info(shots)")
|
||||
columns = {col[1]: col for col in cursor.fetchall()}
|
||||
|
||||
if 'project_id' not in columns:
|
||||
print("❌ project_id column not found")
|
||||
return False
|
||||
|
||||
project_id_col = columns['project_id']
|
||||
if project_id_col[3] != 1: # NOT NULL constraint
|
||||
print("❌ project_id column is nullable")
|
||||
return False
|
||||
|
||||
print("✅ project_id column exists and is NOT NULL")
|
||||
|
||||
# Requirement 2.2: Foreign key constraint to projects table
|
||||
print("\nTesting Requirement 2.2: Foreign key constraint...")
|
||||
cursor.execute("PRAGMA foreign_key_list(shots)")
|
||||
foreign_keys = cursor.fetchall()
|
||||
|
||||
project_fk_found = False
|
||||
for fk in foreign_keys:
|
||||
if fk[2] == 'projects' and fk[3] == 'project_id':
|
||||
project_fk_found = True
|
||||
break
|
||||
|
||||
if not project_fk_found:
|
||||
print("❌ Foreign key constraint to projects table not found")
|
||||
return False
|
||||
|
||||
print("✅ Foreign key constraint to projects table exists")
|
||||
|
||||
# Requirement 2.3: Index on project_id column
|
||||
print("\nTesting Requirement 2.3: Performance indexes...")
|
||||
cursor.execute("PRAGMA index_list(shots)")
|
||||
indexes = [index[1] for index in cursor.fetchall()]
|
||||
|
||||
required_indexes = ['idx_shots_project_id']
|
||||
for index_name in required_indexes:
|
||||
if index_name not in indexes:
|
||||
print(f"❌ Index {index_name} not found")
|
||||
return False
|
||||
|
||||
print("✅ Performance indexes exist")
|
||||
|
||||
# Requirement 2.4: Data preservation - all existing shots have project_id
|
||||
print("\nTesting Requirement 2.4: Data preservation...")
|
||||
cursor.execute("SELECT COUNT(*) FROM shots WHERE project_id IS NULL")
|
||||
null_count = cursor.fetchone()[0]
|
||||
|
||||
if null_count > 0:
|
||||
print(f"❌ {null_count} shots have NULL project_id")
|
||||
return False
|
||||
|
||||
cursor.execute("SELECT COUNT(*) FROM shots")
|
||||
total_shots = cursor.fetchone()[0]
|
||||
print(f"✅ All {total_shots} shots have project_id populated")
|
||||
|
||||
# Requirement 2.5: Data integrity - project_id matches episode's project
|
||||
print("\nTesting Requirement 2.5: Data integrity...")
|
||||
cursor.execute("""
|
||||
SELECT COUNT(*) FROM shots s
|
||||
JOIN episodes e ON s.episode_id = e.id
|
||||
WHERE s.project_id != e.project_id
|
||||
""")
|
||||
|
||||
inconsistent_count = cursor.fetchone()[0]
|
||||
if inconsistent_count > 0:
|
||||
print(f"❌ {inconsistent_count} shots have inconsistent project_id")
|
||||
return False
|
||||
|
||||
print("✅ All shots have consistent project_id with their episodes")
|
||||
|
||||
# Additional test: Unique constraint for project-scoped names
|
||||
print("\nTesting project-scoped name uniqueness...")
|
||||
|
||||
# Check that unique index exists
|
||||
if 'idx_shots_project_name_unique' not in indexes:
|
||||
print("❌ Unique constraint index not found")
|
||||
return False
|
||||
|
||||
# Test the constraint by trying to insert a duplicate
|
||||
try:
|
||||
# Get a sample shot to duplicate
|
||||
cursor.execute("SELECT project_id, episode_id, name FROM shots LIMIT 1")
|
||||
sample = cursor.fetchone()
|
||||
if sample:
|
||||
cursor.execute("""
|
||||
INSERT INTO shots (project_id, episode_id, name, frame_start, frame_end)
|
||||
VALUES (?, ?, ?, 1001, 1100)
|
||||
""", sample)
|
||||
print("❌ Duplicate shot name was allowed in same project")
|
||||
return False
|
||||
except sqlite3.IntegrityError:
|
||||
print("✅ Unique constraint prevents duplicate names in same project")
|
||||
|
||||
# Performance test: Check query performance with indexes
|
||||
print("\nTesting query performance with indexes...")
|
||||
|
||||
# Test project filtering query
|
||||
cursor.execute("EXPLAIN QUERY PLAN SELECT * FROM shots WHERE project_id = 1")
|
||||
query_plan = cursor.fetchall()
|
||||
|
||||
# Check if index is being used (should contain "idx_shots_project_id")
|
||||
uses_index = any("idx_shots_project_id" in str(step) for step in query_plan)
|
||||
if uses_index:
|
||||
print("✅ Project filtering queries use index")
|
||||
else:
|
||||
print("⚠️ Project filtering queries may not be optimized")
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("✅ ALL REQUIREMENTS PASSED")
|
||||
print("✅ Migration completed successfully")
|
||||
|
||||
# Summary
|
||||
cursor.execute("""
|
||||
SELECT p.name, COUNT(s.id) as shot_count
|
||||
FROM projects p
|
||||
LEFT JOIN shots s ON p.id = s.project_id AND s.deleted_at IS NULL
|
||||
GROUP BY p.id, p.name
|
||||
ORDER BY shot_count DESC
|
||||
""")
|
||||
|
||||
print("\nShots per project:")
|
||||
for project_name, shot_count in cursor.fetchall():
|
||||
print(f" {project_name}: {shot_count} shots")
|
||||
|
||||
return True
|
||||
|
||||
except sqlite3.Error as e:
|
||||
print(f"❌ Database error: {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ Unexpected error: {e}")
|
||||
return False
|
||||
finally:
|
||||
if conn:
|
||||
conn.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = test_migration()
|
||||
sys.exit(0 if success else 1)
|
||||
Reference in New Issue
Block a user