RadrootsCaptureIntakeTests.swift (5933B)
1 import Foundation 2 import Testing 3 @testable import RadrootsKit 4 5 @Test func mediaImportRequestNormalizesKindsAndSelectionLimit() throws { 6 let request = try RadrootsMediaImportRequest( 7 allowedMediaKinds: [.image, .image], 8 selectionLimit: 2, 9 destinationScope: .data 10 ) 11 12 #expect(request.allowedMediaKinds == [.image]) 13 #expect(request.selectionLimit == 2) 14 #expect(request.destinationScope == .data) 15 } 16 17 @Test func mediaImportRequestRejectsInvalidSelectionLimits() { 18 #expect(throws: RadrootsCaptureIntakeError.invalidRequest("media import selection limit must be positive")) { 19 _ = try RadrootsMediaImportRequest(selectionLimit: 0) 20 } 21 #expect(throws: RadrootsCaptureIntakeError.invalidRequest("media import selection limit cannot exceed 100")) { 22 _ = try RadrootsMediaImportRequest(selectionLimit: 101) 23 } 24 } 25 26 @Test func mediaPickerSupportOmitsKindsWhenUnavailable() throws { 27 let support = try RadrootsMediaPickerSupport( 28 importAvailable: false, 29 cameraCaptureAvailable: false, 30 supportedImportKinds: [.image], 31 supportedCaptureKinds: [.image], 32 multipleSelectionSupported: true 33 ) 34 35 #expect(!support.importAvailable) 36 #expect(!support.cameraCaptureAvailable) 37 #expect(support.supportedImportKinds.isEmpty) 38 #expect(support.supportedCaptureKinds.isEmpty) 39 #expect(support.multipleSelectionSupported) 40 } 41 42 @Test func mediaAssetNormalizesMetadata() throws { 43 let asset = try RadrootsMediaAsset( 44 source: .libraryImport, 45 kind: .image, 46 file: RadrootsFileReference(scope: .temporary, relativePath: "capture/photo.jpg"), 47 mediaType: " Image/JPEG ", 48 suggestedFilename: " photo.jpg ", 49 sizeBytes: 12, 50 pixelWidth: 640, 51 pixelHeight: 480, 52 capturedAt: Date(timeIntervalSince1970: 10) 53 ) 54 55 #expect(asset.mediaType == "image/jpeg") 56 #expect(asset.suggestedFilename == "photo.jpg") 57 #expect(asset.pixelWidth == 640) 58 #expect(asset.pixelHeight == 480) 59 #expect(asset.capturedAt == Date(timeIntervalSince1970: 10)) 60 } 61 62 @Test func mediaAssetRejectsUnsafeMetadata() { 63 #expect(throws: RadrootsCaptureIntakeError.invalidRequest("capture filename cannot contain path separators")) { 64 _ = try RadrootsMediaAsset( 65 source: .libraryImport, 66 kind: .image, 67 file: RadrootsFileReference(scope: .temporary, relativePath: "capture/photo.jpg"), 68 mediaType: "image/jpeg", 69 suggestedFilename: "../photo.jpg", 70 sizeBytes: 12, 71 capturedAt: Date(timeIntervalSince1970: 10) 72 ) 73 } 74 #expect(throws: RadrootsCaptureIntakeError.invalidRequest("capture media type must be type/subtype")) { 75 _ = try RadrootsMediaAsset( 76 source: .libraryImport, 77 kind: .image, 78 file: RadrootsFileReference(scope: .temporary, relativePath: "capture/photo.jpg"), 79 mediaType: "image", 80 suggestedFilename: "photo.jpg", 81 sizeBytes: 12, 82 capturedAt: Date(timeIntervalSince1970: 10) 83 ) 84 } 85 #expect(throws: RadrootsCaptureIntakeError.invalidRequest("image dimensions must include width and height together")) { 86 _ = try RadrootsMediaAsset( 87 source: .libraryImport, 88 kind: .image, 89 file: RadrootsFileReference(scope: .temporary, relativePath: "capture/photo.jpg"), 90 mediaType: "image/jpeg", 91 suggestedFilename: "photo.jpg", 92 sizeBytes: 12, 93 pixelWidth: 640, 94 capturedAt: Date(timeIntervalSince1970: 10) 95 ) 96 } 97 } 98 99 @Test func mediaImportResultRequiresAtLeastOneItem() throws { 100 let asset = try testMediaAsset() 101 let result = try RadrootsMediaImportResult(items: [asset]) 102 103 #expect(result.items == [asset]) 104 #expect(throws: RadrootsCaptureIntakeError.invalidRequest("media import result cannot be empty")) { 105 _ = try RadrootsMediaImportResult(items: []) 106 } 107 } 108 109 @Test func scannerSupportOmitsOutputKindsWhenUnavailable() throws { 110 let support = try RadrootsDocumentScannerSupport( 111 interactiveScanAvailable: false, 112 multiPageSupported: true, 113 supportedOutputKinds: [.pdf] 114 ) 115 116 #expect(!support.interactiveScanAvailable) 117 #expect(!support.multiPageSupported) 118 #expect(support.supportedOutputKinds.isEmpty) 119 } 120 121 @Test func scannedDocumentNormalizesPdfMetadata() throws { 122 let document = try RadrootsScannedDocument( 123 file: RadrootsFileReference(scope: .temporary, relativePath: "capture/scan.pdf"), 124 outputKind: .pdf, 125 suggestedFilename: " scan.pdf ", 126 mediaType: " Application/PDF ", 127 pageCount: 2, 128 sizeBytes: 2048, 129 capturedAt: Date(timeIntervalSince1970: 11) 130 ) 131 132 #expect(document.suggestedFilename == "scan.pdf") 133 #expect(document.mediaType == "application/pdf") 134 #expect(document.pageCount == 2) 135 #expect(document.sizeBytes == 2048) 136 } 137 138 @Test func scannedDocumentRejectsEmptyPageCount() { 139 #expect(throws: RadrootsCaptureIntakeError.invalidRequest("scanned document page count must be positive")) { 140 _ = try RadrootsScannedDocument( 141 file: RadrootsFileReference(scope: .temporary, relativePath: "capture/scan.pdf"), 142 outputKind: .pdf, 143 suggestedFilename: "scan.pdf", 144 mediaType: "application/pdf", 145 pageCount: 0, 146 sizeBytes: 2048, 147 capturedAt: Date(timeIntervalSince1970: 11) 148 ) 149 } 150 } 151 152 private func testMediaAsset() throws -> RadrootsMediaAsset { 153 try RadrootsMediaAsset( 154 source: .libraryImport, 155 kind: .image, 156 file: RadrootsFileReference(scope: .temporary, relativePath: "capture/photo.jpg"), 157 mediaType: "image/jpeg", 158 suggestedFilename: "photo.jpg", 159 sizeBytes: 12, 160 capturedAt: Date(timeIntervalSince1970: 10) 161 ) 162 }