Added a proper set of Unit Tests for each sub-project
All checks were successful
OpenClimb Docker Deploy / build-and-push (push) Successful in 2m28s

This commit is contained in:
2025-10-03 20:55:04 -06:00
parent 4e42985135
commit 4bbd422c09
33 changed files with 3158 additions and 833 deletions

View File

@@ -23,7 +23,7 @@ class ImageManager {
// Final integrity check
if !validateStorageIntegrity() {
print("🚨 CRITICAL: Storage integrity compromised - attempting emergency recovery")
print("CRITICAL: Storage integrity compromised - attempting emergency recovery")
emergencyImageRestore()
}
@@ -69,9 +69,9 @@ class ImageManager {
attributes: [
.protectionKey: FileProtectionType.completeUntilFirstUserAuthentication
])
print("Created directory: \(directory.path)")
print("Created directory: \(directory.path)")
} catch {
print(" Failed to create directory \(directory.path): \(error)")
print("ERROR: Failed to create directory \(directory.path): \(error)")
}
}
}
@@ -88,9 +88,9 @@ class ImageManager {
var backupURL = backupDirectory
try imagesURL.setResourceValues(resourceValues)
try backupURL.setResourceValues(resourceValues)
print("Excluded image directories from iCloud backup")
print("Excluded image directories from iCloud backup")
} catch {
print("⚠️ Failed to exclude from iCloud backup: \(error)")
print("WARNING: Failed to exclude from iCloud backup: \(error)")
}
}
@@ -114,11 +114,11 @@ class ImageManager {
}
private func performRobustMigration() {
print("🔄 Starting robust image migration system...")
print("Starting robust image migration system...")
// Check for interrupted migration
if let incompleteState = loadMigrationState() {
print("🔧 Detected interrupted migration, resuming...")
print("Detected interrupted migration, resuming...")
resumeMigration(from: incompleteState)
} else {
// Start fresh migration
@@ -135,7 +135,7 @@ class ImageManager {
private func startNewMigration() {
// First check for images in previous Application Support directories
if let previousAppSupportImages = findPreviousAppSupportImages() {
print("📁 Found images in previous Application Support directory")
print("Found images in previous Application Support directory")
migratePreviousAppSupportImages(from: previousAppSupportImages)
return
}
@@ -145,7 +145,7 @@ class ImageManager {
let hasLegacyImportImages = fileManager.fileExists(atPath: legacyImportImagesDirectory.path)
guard hasLegacyImages || hasLegacyImportImages else {
print("No legacy images to migrate")
print("No legacy images to migrate")
return
}
@@ -160,7 +160,7 @@ class ImageManager {
let legacyFiles = try fileManager.contentsOfDirectory(
atPath: legacyImagesDirectory.path)
allLegacyFiles.append(contentsOf: legacyFiles)
print("📦 Found \(legacyFiles.count) images in OpenClimbImages")
print("Found \(legacyFiles.count) images in OpenClimbImages")
}
// Collect files from Documents/images directory
@@ -168,10 +168,10 @@ class ImageManager {
let importFiles = try fileManager.contentsOfDirectory(
atPath: legacyImportImagesDirectory.path)
allLegacyFiles.append(contentsOf: importFiles)
print("📦 Found \(importFiles.count) images in Documents/images")
print("Found \(importFiles.count) images in Documents/images")
}
print("📦 Total legacy images to migrate: \(allLegacyFiles.count)")
print("Total legacy images to migrate: \(allLegacyFiles.count)")
let initialState = MigrationState(
version: MigrationState.currentVersion,
@@ -186,24 +186,24 @@ class ImageManager {
performMigrationWithCheckpoints(files: allLegacyFiles, currentState: initialState)
} catch {
print(" Failed to start migration: \(error)")
print("ERROR: Failed to start migration: \(error)")
}
}
private func resumeMigration(from state: MigrationState) {
print("🔄 Resuming migration from checkpoint...")
print("📊 Progress: \(state.completedFiles.count)/\(state.totalFiles)")
print("Resuming migration from checkpoint...")
print("Progress: \(state.completedFiles.count)/\(state.totalFiles)")
do {
let legacyFiles = try fileManager.contentsOfDirectory(
atPath: legacyImagesDirectory.path)
let remainingFiles = legacyFiles.filter { !state.completedFiles.contains($0) }
print("📦 Resuming with \(remainingFiles.count) remaining files")
print("Resuming with \(remainingFiles.count) remaining files")
performMigrationWithCheckpoints(files: remainingFiles, currentState: state)
} catch {
print(" Failed to resume migration: \(error)")
print("ERROR: Failed to resume migration: \(error)")
// Fallback: start fresh
removeMigrationState()
startNewMigration()
@@ -270,11 +270,11 @@ class ImageManager {
completedFiles.append(fileName)
migratedCount += 1
print("Migrated: \(fileName) (\(migratedCount)/\(currentState.totalFiles))")
print("Migrated: \(fileName) (\(migratedCount)/\(currentState.totalFiles))")
} catch {
failedCount += 1
print(" Failed to migrate \(fileName): \(error)")
print("ERROR: Failed to migrate \(fileName): \(error)")
}
// Save checkpoint every 5 files or if interrupted
@@ -288,7 +288,7 @@ class ImageManager {
lastCheckpoint: Date()
)
saveMigrationState(checkpointState)
print("💾 Checkpoint saved: \(completedFiles.count)/\(currentState.totalFiles)")
print("Checkpoint saved: \(completedFiles.count)/\(currentState.totalFiles)")
}
}
}
@@ -304,7 +304,7 @@ class ImageManager {
)
saveMigrationState(finalState)
print("🏁 Migration complete: \(migratedCount) migrated, \(failedCount) failed")
print("Migration complete: \(migratedCount) migrated, \(failedCount) failed")
// Clean up legacy directory if no failures
if failedCount == 0 {
@@ -313,7 +313,7 @@ class ImageManager {
}
private func verifyMigrationIntegrity() {
print("🔍 Verifying migration integrity...")
print("Verifying migration integrity...")
var allLegacyFiles = Set<String>()
@@ -331,12 +331,12 @@ class ImageManager {
allLegacyFiles.formUnion(importFiles)
}
} catch {
print(" Failed to read legacy directories: \(error)")
print("ERROR: Failed to read legacy directories: \(error)")
return
}
guard !allLegacyFiles.isEmpty else {
print("No legacy directories to verify against")
print("No legacy directories to verify against")
return
}
@@ -347,10 +347,10 @@ class ImageManager {
let missingFiles = allLegacyFiles.subtracting(migratedFiles)
if missingFiles.isEmpty {
print("Migration integrity verified - all files present")
print("Migration integrity verified - all files present")
cleanupLegacyDirectory()
} else {
print("⚠️ Missing \(missingFiles.count) files, re-triggering migration")
print("WARNING: Missing \(missingFiles.count) files, re-triggering migration")
// Re-trigger migration for missing files
performMigrationWithCheckpoints(
files: Array(missingFiles),
@@ -364,16 +364,16 @@ class ImageManager {
))
}
} catch {
print(" Failed to verify migration integrity: \(error)")
print("ERROR: Failed to verify migration integrity: \(error)")
}
}
private func cleanupLegacyDirectory() {
do {
try fileManager.removeItem(at: legacyImagesDirectory)
print("🗑️ Cleaned up legacy directory")
print("Cleaned up legacy directory")
} catch {
print("⚠️ Failed to clean up legacy directory: \(error)")
print("WARNING: Failed to clean up legacy directory: \(error)")
}
}
@@ -395,14 +395,14 @@ class ImageManager {
// Check if state is too old (more than 1 hour)
if Date().timeIntervalSince(state.lastCheckpoint) > 3600 {
print("⚠️ Migration state is stale, starting fresh")
print("WARNING: Migration state is stale, starting fresh")
removeMigrationState()
return nil
}
return state.isComplete ? nil : state
} catch {
print(" Failed to load migration state: \(error)")
print("ERROR: Failed to load migration state: \(error)")
removeMigrationState()
return nil
}
@@ -413,7 +413,7 @@ class ImageManager {
let data = try JSONEncoder().encode(state)
try data.write(to: migrationStateURL)
} catch {
print(" Failed to save migration state: \(error)")
print("ERROR: Failed to save migration state: \(error)")
}
}
@@ -429,7 +429,7 @@ class ImageManager {
private func cleanupMigrationState() {
try? fileManager.removeItem(at: migrationStateURL)
try? fileManager.removeItem(at: migrationLockURL)
print("🧹 Cleaned up migration state files")
print("Cleaned up migration state files")
}
func saveImageData(_ data: Data, withName name: String? = nil) -> String? {
@@ -444,10 +444,10 @@ class ImageManager {
// Create backup copy
try data.write(to: backupPath)
print("Saved image with backup: \(fileName)")
print("Saved image with backup: \(fileName)")
return fileName
} catch {
print(" Failed to save image \(fileName): \(error)")
print("ERROR: Failed to save image \(fileName): \(error)")
return nil
}
}
@@ -467,7 +467,7 @@ class ImageManager {
if fileManager.fileExists(atPath: backupPath.path),
let data = try? Data(contentsOf: backupPath)
{
print("📦 Restored image from backup: \(path)")
print("Restored image from backup: \(path)")
// Restore to primary location
try? data.write(to: URL(fileURLWithPath: primaryPath))
@@ -497,7 +497,7 @@ class ImageManager {
do {
try fileManager.removeItem(atPath: primaryPath)
} catch {
print(" Failed to delete primary image at \(primaryPath): \(error)")
print("ERROR: Failed to delete primary image at \(primaryPath): \(error)")
success = false
}
}
@@ -507,7 +507,7 @@ class ImageManager {
do {
try fileManager.removeItem(at: backupPath)
} catch {
print(" Failed to delete backup image at \(backupPath.path): \(error)")
print("ERROR: Failed to delete backup image at \(backupPath.path): \(error)")
success = false
}
}
@@ -544,7 +544,7 @@ class ImageManager {
}
func performMaintenance() {
print("🔧 Starting image maintenance...")
print("Starting image maintenance...")
syncBackups()
validateImageIntegrity()
@@ -562,11 +562,11 @@ class ImageManager {
let backupPath = backupDirectory.appendingPathComponent(fileName)
try? fileManager.copyItem(at: primaryPath, to: backupPath)
print("🔄 Created missing backup for: \(fileName)")
print("Created missing backup for: \(fileName)")
}
}
} catch {
print(" Failed to sync backups: \(error)")
print("ERROR: Failed to sync backups: \(error)")
}
}
@@ -585,15 +585,15 @@ class ImageManager {
}
}
print("Validated \(validFiles) of \(files.count) image files")
print("Validated \(validFiles) of \(files.count) image files")
} catch {
print(" Failed to validate images: \(error)")
print("ERROR: Failed to validate images: \(error)")
}
}
private func cleanupOrphanedFiles() {
// This would need access to the data manager to check which files are actually referenced
print("🧹 Cleanup would require coordination with data manager")
print("Cleanup would require coordination with data manager")
}
func getStorageInfo() -> (primaryCount: Int, backupCount: Int, totalSize: Int64) {
@@ -623,7 +623,7 @@ class ImageManager {
let previousDir = findPreviousAppSupportImages()
print(
"""
📁 OpenClimb Image Storage:
OpenClimb Image Storage:
- App Support: \(appSupportDirectory.path)
- Images: \(imagesDirectory.path) (\(info.primaryCount) files)
- Backups: \(backupDirectory.path) (\(info.backupCount) files)
@@ -635,7 +635,7 @@ class ImageManager {
}
func forceRecoveryMigration() {
print("🚨 FORCE RECOVERY: Starting manual migration recovery...")
print("FORCE RECOVERY: Starting manual migration recovery...")
// Remove any stale state
removeMigrationState()
@@ -644,7 +644,7 @@ class ImageManager {
// Force fresh migration
startNewMigration()
print("🚨 FORCE RECOVERY: Migration recovery completed")
print("FORCE RECOVERY: Migration recovery completed")
}
func saveImportedImage(_ imageData: Data, filename: String) throws -> String {
@@ -657,12 +657,12 @@ class ImageManager {
// Create backup
try? imageData.write(to: backupPath)
print("📥 Imported image: \(filename)")
print("Imported image: \(filename)")
return filename
}
func emergencyImageRestore() {
print("🆘 EMERGENCY: Attempting image restoration...")
print("EMERGENCY: Attempting image restoration...")
// Try to restore from backup directory
do {
@@ -680,14 +680,14 @@ class ImageManager {
}
}
print("🆘 EMERGENCY: Restored \(restoredCount) images from backup")
print("EMERGENCY: Restored \(restoredCount) images from backup")
} catch {
print("🆘 EMERGENCY: Failed to restore from backup: \(error)")
print("EMERGENCY: Failed to restore from backup: \(error)")
}
// Try previous Application Support directories first
if let previousAppSupportImages = findPreviousAppSupportImages() {
print("🆘 EMERGENCY: Found previous Application Support images, migrating...")
print("EMERGENCY: Found previous Application Support images, migrating...")
migratePreviousAppSupportImages(from: previousAppSupportImages)
return
}
@@ -696,21 +696,21 @@ class ImageManager {
if fileManager.fileExists(atPath: legacyImagesDirectory.path)
|| fileManager.fileExists(atPath: legacyImportImagesDirectory.path)
{
print("🆘 EMERGENCY: Attempting legacy migration as fallback...")
print("EMERGENCY: Attempting legacy migration as fallback...")
forceRecoveryMigration()
}
}
func debugSafeInitialization() -> Bool {
print("🐛 DEBUG SAFE: Performing debug-safe initialization check...")
print("DEBUG SAFE: Performing debug-safe initialization check...")
// Check if we're in a debug environment
#if DEBUG
print("🐛 DEBUG SAFE: Debug environment detected")
print("DEBUG SAFE: Debug environment detected")
// Check for interrupted migration more aggressively
if fileManager.fileExists(atPath: migrationLockURL.path) {
print("🐛 DEBUG SAFE: Found migration lock - likely debug interruption")
print("DEBUG SAFE: Found migration lock - likely debug interruption")
// Give extra time for file system to stabilize
Thread.sleep(forTimeInterval: 1.0)
@@ -732,14 +732,14 @@ class ImageManager {
((try? fileManager.contentsOfDirectory(atPath: backupDirectory.path)) ?? []).count > 0
if primaryEmpty && backupHasFiles {
print("🐛 DEBUG SAFE: Primary empty but backup exists - restoring")
print("DEBUG SAFE: Primary empty but backup exists - restoring")
emergencyImageRestore()
return true
}
// Check if primary storage is empty but previous Application Support images exist
if primaryEmpty, let previousAppSupportImages = findPreviousAppSupportImages() {
print("🐛 DEBUG SAFE: Primary empty but found previous Application Support images")
print("DEBUG SAFE: Primary empty but found previous Application Support images")
migratePreviousAppSupportImages(from: previousAppSupportImages)
return true
}
@@ -755,13 +755,15 @@ class ImageManager {
// Check if we have more backups than primary files (sign of corruption)
if backupFiles.count > primaryFiles.count + 5 {
print("⚠️ INTEGRITY: Backup count significantly exceeds primary - potential corruption")
print(
"WARNING INTEGRITY: Backup count significantly exceeds primary - potential corruption"
)
return false
}
// Check if primary is completely empty but we have data elsewhere
if primaryFiles.isEmpty && !backupFiles.isEmpty {
print("⚠️ INTEGRITY: Primary storage empty but backups exist")
print("WARNING INTEGRITY: Primary storage empty but backups exist")
return false
}
@@ -775,7 +777,7 @@ class ImageManager {
for: .applicationSupportDirectory, in: .userDomainMask
).first
else {
print(" Could not access Application Support directory")
print("ERROR: Could not access Application Support directory")
return nil
}
@@ -808,13 +810,13 @@ class ImageManager {
}
}
} catch {
print(" Error scanning for previous Application Support directories: \(error)")
print("ERROR: Error scanning for previous Application Support directories: \(error)")
}
return nil
}
private func migratePreviousAppSupportImages(from sourceDirectory: URL) {
print("🔄 Migrating images from previous Application Support directory")
print("Migrating images from previous Application Support directory")
do {
let imageFiles = try fileManager.contentsOfDirectory(atPath: sourceDirectory.path)
@@ -837,17 +839,17 @@ class ImageManager {
// Create backup
try? fileManager.copyItem(at: sourcePath, to: backupPath)
print("Migrated: \(fileName)")
print("Migrated: \(fileName)")
} catch {
print(" Failed to migrate \(fileName): \(error)")
print("ERROR: Failed to migrate \(fileName): \(error)")
}
}
}
print("Completed migration from previous Application Support directory")
print("Completed migration from previous Application Support directory")
} catch {
print(" Failed to migrate from previous Application Support: \(error)")
print("ERROR: Failed to migrate from previous Application Support: \(error)")
}
}
}