#!/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)