// // StackDexTests.swift // StackDexTests // // Created by Matthias Meister on 18.04.26. // import Testing @testable import StackDex import UIKit struct StackDexTests { @Test func cameraSessionStateBlocksStartUntilConfigurationCommits() { var state = CameraSessionState() #expect(state.canStartSession) state.beginConfiguration() #expect(!state.canStartSession) state.commitConfiguration() #expect(state.canStartSession) } @Test func cameraSessionStateTracksNestedConfigurationDepth() { var state = CameraSessionState() state.beginConfiguration() state.beginConfiguration() #expect(!state.canStartSession) state.commitConfiguration() #expect(!state.canStartSession) state.commitConfiguration() #expect(state.canStartSession) } @Test func heuristicExtractorFindsStructuredFields() { let extractor = CardTextHeuristicExtractor() let payload = OCRTextPayload( rawText: "Charizard\n120 HP\n4/102 Base Set\nHolo Rare", lines: [ RecognizedTextLine(text: "Charizard", confidence: 0.95, normalizedBounds: CGRect(x: 0.1, y: 0.82, width: 0.4, height: 0.08)), RecognizedTextLine(text: "120 HP", confidence: 0.81, normalizedBounds: CGRect(x: 0.75, y: 0.83, width: 0.12, height: 0.05)), RecognizedTextLine(text: "4/102 Base Set", confidence: 0.92, normalizedBounds: CGRect(x: 0.2, y: 0.12, width: 0.4, height: 0.05)), RecognizedTextLine(text: "Holo Rare", confidence: 0.88, normalizedBounds: CGRect(x: 0.7, y: 0.1, width: 0.2, height: 0.05)), ], averageConfidence: 0.89 ) let draft = extractor.extract(payload: payload, source: .onDeviceOffline) #expect(draft.cardName == "Charizard") #expect(draft.cardNumber == "4/102") #expect(draft.setIdentifier == "Base Set") #expect(draft.rarity == CardRarity.holoRare.rawValue) #expect(draft.confidence == .high) } @Test func pipelineFallsBackToVisionWhenCloudIsStubbed() async throws { let monitor = TestNetworkStatusProvider(isOnline: true) let pipeline = CardRecognitionPipeline( networkStatusProvider: monitor, cloudOCRClient: StubCloudOCRClient(), fallbackOCR: TestOCRService(payload: OCRTextPayload( rawText: "Pikachu\n25/165\nScarlet & Violet\nRare", lines: [ RecognizedTextLine(text: "Pikachu", confidence: 0.95, normalizedBounds: CGRect(x: 0.1, y: 0.8, width: 0.3, height: 0.08)), RecognizedTextLine(text: "25/165", confidence: 0.91, normalizedBounds: CGRect(x: 0.3, y: 0.15, width: 0.18, height: 0.05)), RecognizedTextLine(text: "Scarlet & Violet", confidence: 0.87, normalizedBounds: CGRect(x: 0.25, y: 0.11, width: 0.4, height: 0.05)), RecognizedTextLine(text: "Rare", confidence: 0.75, normalizedBounds: CGRect(x: 0.76, y: 0.1, width: 0.12, height: 0.04)), ], averageConfidence: 0.87 )) ) let session = try await pipeline.recognizeCard(in: TestImageFactory.makeImage()) #expect(session.draft.source == .onDeviceFallback) #expect(session.draft.cardName == "Pikachu") #expect(session.draft.cardNumber == "25/165") #expect(session.draft.notes.contains(where: { $0.contains("Cloud OCR") })) } } private final class TestNetworkStatusProvider: NetworkStatusProviding { var isOnline: Bool init(isOnline: Bool) { self.isOnline = isOnline } func startMonitoring() {} func stopMonitoring() {} } private struct TestOCRService: CardTextRecognizing { let payload: OCRTextPayload func recognizeText(in image: PreparedImage) async throws -> OCRTextPayload { payload } } private enum TestImageFactory { static func makeImage() -> UIImage { let renderer = UIGraphicsImageRenderer(size: CGSize(width: 300, height: 420)) return renderer.image { context in UIColor.white.setFill() context.fill(CGRect(x: 0, y: 0, width: 300, height: 420)) UIColor.black.setStroke() context.cgContext.setLineWidth(6) context.cgContext.stroke(CGRect(x: 16, y: 16, width: 268, height: 388)) } } }