field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

TeraPublicDisclosureTests.swift (4429B)


      1 import ImageIO
      2 @testable import TeraApp
      3 import UIKit
      4 import UniformTypeIdentifiers
      5 import XCTest
      6 
      7 @MainActor
      8 final class TeraPublicDisclosureTests: XCTestCase {
      9   func testCameraAndLibraryStripSensitiveMetadataBeforeCreatingPublicMedia() async throws {
     10     let source = try taggedImage()
     11     let sourceProperties = try properties(source)
     12     XCTAssertNotNil(sourceProperties[kCGImagePropertyGPSDictionary], "The fixture must actually contain GPS")
     13     XCTAssertNotNil(sourceProperties[kCGImagePropertyTIFFDictionary])
     14     for camera in [false, true] {
     15       let fixture = try OfflineMediaFixture(image: source, filename: "tagged.jpg", mediaType: "image/jpeg")
     16       defer { fixture.remove() }
     17       let coordinator = fixture.coordinator()
     18       let photos = try await camera ? [coordinator.captureImage()] : coordinator.importImages(limit: 1)
     19       let photo = try XCTUnwrap(photos.first)
     20       let bytes = try Data(contentsOf: fixture.roots.stagedBlobsRoot.appendingPathComponent(photo.sha256))
     21       let output = try properties(bytes)
     22       XCTAssertNil(output[kCGImagePropertyGPSDictionary])
     23       XCTAssertNil(output[kCGImagePropertyTIFFDictionary])
     24       XCTAssertNil(output[kCGImagePropertyIPTCDictionary])
     25       let exif = output[kCGImagePropertyExifDictionary] as? [CFString: Any] ?? [:]
     26       XCTAssertNil(exif[kCGImagePropertyExifUserComment])
     27       XCTAssertNil(bytes.range(of: Data("C117_PRIVATE_CAMERA".utf8)))
     28       XCTAssertNil(photo.remoteURL, "Local intake does not upload or publish")
     29       XCTAssertEqual(photo.alt, "")
     30       XCTAssertEqual(try Data(contentsOf: fixture.roots.cacheRoot.appendingPathComponent("tagged.jpg")), source)
     31       let transfers = await fixture.transfer.enqueueCount
     32       XCTAssertEqual(transfers, 0)
     33     }
     34   }
     35 
     36   func testLocationIsAbsentByDefaultAndOnlyManualTextSurvivesSaveAndRestart() async throws {
     37     let fixture = try OfflineMediaFixture()
     38     defer { fixture.remove() }
     39     let client = TeraRuntimeClient.production()
     40     let signer = ComposerForbiddenSigner()
     41     let configuration = fixture.configuration(signer)
     42     let snapshot = try await client.start(configuration: configuration)
     43     var saved: [TeraComposerDraft] = []
     44     for type in TeraAddCommandType.allCases {
     45       let store = TeraAddStore(runtimeClient: client, initialType: type)
     46       store.configure(snapshot: snapshot)
     47       await store.start()
     48       XCTAssertNil(store.form.location)
     49       store.updateForm(\.content, "local editing")
     50       if type == .createEvent || type == .createFoodAvailability {
     51         store.updateForm(\.location, "North side of the public market")
     52       }
     53       await store.save()
     54       let draft = try XCTUnwrap(store.savedComposer)
     55       XCTAssertEqual(draft.form.location, store.form.location)
     56       saved.append(draft)
     57       store.stop()
     58     }
     59     _ = try await client.stop()
     60     _ = try await client.start(configuration: configuration)
     61     for draft in saved {
     62       let restored = try await client.loadComposer(scope: draft.scope, id: draft.id)
     63       XCTAssertEqual(restored, draft)
     64     }
     65     let signs = await signer.requests
     66     XCTAssertEqual(signs, 0)
     67     _ = try await client.stop()
     68   }
     69 
     70   private func properties(_ data: Data) throws -> [CFString: Any] {
     71     let source = try XCTUnwrap(CGImageSourceCreateWithData(data as CFData, nil))
     72     return try XCTUnwrap(CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [CFString: Any])
     73   }
     74 
     75   private func taggedImage() throws -> Data {
     76     let image = UIGraphicsImageRenderer(size: CGSize(width: 2, height: 2)).image { context in
     77       UIColor.green.setFill()
     78       context.fill(CGRect(x: 0, y: 0, width: 2, height: 2))
     79     }
     80     let output = NSMutableData()
     81     let destination = try XCTUnwrap(CGImageDestinationCreateWithData(output, UTType.jpeg.identifier as CFString, 1, nil))
     82     let properties: [CFString: Any] = [
     83       kCGImagePropertyGPSDictionary: [kCGImagePropertyGPSLatitude: 12.3456, kCGImagePropertyGPSLatitudeRef: "N"],
     84       kCGImagePropertyTIFFDictionary: [kCGImagePropertyTIFFMake: "C117_PRIVATE_CAMERA"],
     85       kCGImagePropertyExifDictionary: [kCGImagePropertyExifUserComment: "C117_PRIVATE_CAMERA"],
     86       kCGImagePropertyIPTCDictionary: [kCGImagePropertyIPTCCaptionAbstract: "C117_PRIVATE_CAMERA"],
     87     ]
     88     try CGImageDestinationAddImage(destination, XCTUnwrap(image.cgImage), properties as CFDictionary)
     89     XCTAssertTrue(CGImageDestinationFinalize(destination))
     90     return output as Data
     91   }
     92 }