pyPhotoAlbum/tests/test_migration.py
Duncan Tourolle f6ed11b0bc
All checks were successful
Python CI / test (push) Successful in 1m20s
Lint / lint (push) Successful in 1m4s
Tests / test (3.11) (push) Successful in 1m27s
Tests / test (3.12) (push) Successful in 2m25s
Tests / test (3.13) (push) Successful in 2m52s
Tests / test (3.14) (push) Successful in 1m9s
black formatting
2025-11-27 23:07:16 +01:00

162 lines
6.2 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Test script for v2.0 to v3.0 migration
This script creates a v2.0 project, saves it, then loads it back
to verify that the migration to v3.0 works correctly.
"""
import os
import sys
import tempfile
import json
import zipfile
from datetime import datetime
# Add pyPhotoAlbum to path
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
from pyPhotoAlbum.project import Project, Page
from pyPhotoAlbum.models import ImageData, TextBoxData
from pyPhotoAlbum.project_serializer import save_to_zip, load_from_zip
from pyPhotoAlbum.version_manager import CURRENT_DATA_VERSION
def create_v2_project_json():
"""Create a v2.0 project JSON (without UUIDs, timestamps, project_id)"""
return {
"name": "Test Project v2.0",
"folder_path": "./test_project",
"page_size_mm": [140, 140],
"working_dpi": 300,
"export_dpi": 300,
"has_cover": False,
"data_version": "2.0",
"pages": [
{
"page_number": 1,
"is_cover": False,
"is_double_spread": False,
"manually_sized": False,
"layout": {
"size": [140, 140],
"background_color": [1.0, 1.0, 1.0],
"elements": [
{
"type": "textbox",
"position": [10, 10],
"size": [100, 50],
"rotation": 0,
"z_index": 0,
"text_content": "Hello v2.0",
"font_settings": {"family": "Arial", "size": 12, "color": [0, 0, 0]},
"alignment": "left",
}
],
"snapping_system": {"snap_threshold_mm": 5.0, "grid_size_mm": 10.0},
},
}
],
"history": {"undo_stack": [], "redo_stack": [], "max_history": 100},
"asset_manager": {"reference_counts": {}},
}
def test_migration():
"""Test v2.0 to v3.0 migration"""
print("=" * 60)
print("Testing v2.0 to v3.0 Migration")
print("=" * 60)
with tempfile.TemporaryDirectory() as temp_dir:
# Create a fake v2.0 .ppz file
v2_file = os.path.join(temp_dir, "test_v2.ppz")
print(f"\n1. Creating v2.0 project file: {v2_file}")
v2_data = create_v2_project_json()
with zipfile.ZipFile(v2_file, "w", zipfile.ZIP_DEFLATED) as zipf:
project_json = json.dumps(v2_data, indent=2)
zipf.writestr("project.json", project_json)
print(f" ✓ Created v2.0 project with {len(v2_data['pages'])} page(s)")
print(f" ✓ Version: {v2_data['data_version']}")
# Load the v2.0 file (should trigger migration)
print(f"\n2. Loading v2.0 project (migration should occur)...")
try:
project = load_from_zip(v2_file)
print(f" ✓ Project loaded successfully")
print(f" ✓ Project name: {project.name}")
# Verify migration
print(f"\n3. Verifying migration to v3.0...")
# Check project-level fields
assert hasattr(project, "project_id"), "Missing project_id"
assert hasattr(project, "created"), "Missing created timestamp"
assert hasattr(project, "last_modified"), "Missing last_modified timestamp"
print(f" ✓ Project has project_id: {project.project_id}")
print(f" ✓ Project has created: {project.created}")
print(f" ✓ Project has last_modified: {project.last_modified}")
# Check page-level fields
assert len(project.pages) > 0, "No pages in project"
page = project.pages[0]
assert hasattr(page, "uuid"), "Page missing uuid"
assert hasattr(page, "created"), "Page missing created"
assert hasattr(page, "last_modified"), "Page missing last_modified"
assert hasattr(page, "deleted"), "Page missing deleted flag"
print(f" ✓ Page 1 has uuid: {page.uuid}")
print(f" ✓ Page 1 has timestamps and deletion tracking")
# Check element-level fields
assert len(page.layout.elements) > 0, "No elements in page"
element = page.layout.elements[0]
assert hasattr(element, "uuid"), "Element missing uuid"
assert hasattr(element, "created"), "Element missing created"
assert hasattr(element, "last_modified"), "Element missing last_modified"
assert hasattr(element, "deleted"), "Element missing deleted flag"
print(f" ✓ Element has uuid: {element.uuid}")
print(f" ✓ Element has timestamps and deletion tracking")
# Save as v3.0 and verify
print(f"\n4. Saving migrated project as v3.0...")
v3_file = os.path.join(temp_dir, "test_v3.ppz")
success, error = save_to_zip(project, v3_file)
assert success, f"Save failed: {error}"
print(f" ✓ Saved to: {v3_file}")
# Verify v3.0 file structure
with zipfile.ZipFile(v3_file, "r") as zipf:
project_json = zipf.read("project.json").decode("utf-8")
v3_data = json.loads(project_json)
assert v3_data.get("data_version") == "3.0", "Wrong version"
assert "project_id" in v3_data, "Missing project_id in saved file"
assert "created" in v3_data, "Missing created in saved file"
assert "uuid" in v3_data["pages"][0], "Missing page uuid in saved file"
print(f" ✓ Saved file version: {v3_data.get('data_version')}")
print(f" ✓ All v3.0 fields present in saved file")
print(f"\n{'=' * 60}")
print("✅ Migration test PASSED")
print(f"{'=' * 60}\n")
return True
except Exception as e:
print(f"\n{'=' * 60}")
print(f"❌ Migration test FAILED: {e}")
print(f"{'=' * 60}\n")
import traceback
traceback.print_exc()
return False
if __name__ == "__main__":
success = test_migration()
sys.exit(0 if success else 1)