apple_kit

Apple-native services for Radroots iOS and macOS apps
git clone https://radroots.dev/git/apple_kit.git
Log | Files | Refs | README | LICENSE

RadrootsDocumentPresentationTests.swift (7676B)


      1 import Darwin
      2 import Foundation
      3 @testable import RadrootsKit
      4 import Testing
      5 import UniformTypeIdentifiers
      6 
      7 @Test func documentPresentationMapsImportContentTypes() throws {
      8     let request = try RadrootsDocumentImportRequest(
      9         allowedContentKinds: [.json, .plainText, .url, .file, .stagedBlob]
     10     )
     11 
     12     let types = RadrootsDocumentPresentationAdapter.contentTypes(for: request)
     13 
     14     #expect(types == [.json, .plainText, .url, .item, .data])
     15     #expect(RadrootsDocumentPresentationAdapter.contentType(forMediaType: "application/json") == .json)
     16     #expect(RadrootsDocumentPresentationAdapter.contentType(forMediaType: "text/plain") == .plainText)
     17     #expect(RadrootsDocumentPresentationAdapter.contentType(forMediaType: nil) == .data)
     18 }
     19 
     20 @Test func documentPresentationBuildsImportDestinations() throws {
     21     let destination = try RadrootsDocumentPresentationAdapter.importDestination(
     22         sourceURL: URL(fileURLWithPath: "/tmp/relays.json"),
     23         scope: .data,
     24         importID: "import_1"
     25     )
     26 
     27     #expect(destination.scope == .data)
     28     #expect(destination.relativePath == "document_import/import_1/relays.json")
     29 
     30     #expect(throws: RadrootsDocumentInterchangeError.self) {
     31         _ = try RadrootsDocumentPresentationAdapter.importDestination(
     32             sourceURL: URL(fileURLWithPath: "/tmp/../bad.json"),
     33             scope: .data,
     34             importID: "../escape"
     35         )
     36     }
     37 }
     38 
     39 @Test func documentPresentationAdaptsPublicShareItems() throws {
     40     let textRequest = try RadrootsShareRequest(items: [.text(" public post ")], subject: " Radroots ")
     41     let textItem = try RadrootsDocumentPresentationAdapter.transferItem(for: textRequest)
     42 
     43     #expect(textItem.payload == .text("public post"))
     44     #expect(textItem.text == "public post")
     45     #expect(textItem.subject == "Radroots")
     46 
     47     let urlRequest = try RadrootsShareRequest(items: [.url(#require(URL(string: "https://radroots.org/posts/1")))])
     48     let urlItem = try RadrootsDocumentPresentationAdapter.transferItem(for: urlRequest)
     49 
     50     #expect(try urlItem.payload == .url(#require(URL(string: "https://radroots.org/posts/1"))))
     51     #expect(urlItem.text == "https://radroots.org/posts/1")
     52 
     53     let file = RadrootsFileReference(scope: .data, relativePath: "exports/diagnostics.json")
     54     let fileRequest = try RadrootsShareRequest(
     55         items: [.file(file, suggestedFilename: "diagnostics.json", mediaType: "application/json", sizeBytes: nil)]
     56     )
     57 
     58     #expect(throws: RadrootsDocumentInterchangeError.self) {
     59         _ = try RadrootsDocumentPresentationAdapter.transferItem(for: fileRequest)
     60     }
     61 }
     62 
     63 @Test func documentPresentationPreparesScopedFileShareItems() throws {
     64     let access = try testDocumentPresentationFileAccess()
     65     let file = RadrootsFileReference(scope: .data, relativePath: "exports/diagnostics.json")
     66     let data = Data(#"{"status":"ok"}"#.utf8)
     67     try access.write(.inline(data), to: file)
     68     let request = try RadrootsShareRequest(
     69         items: [
     70             .file(
     71                 file,
     72                 suggestedFilename: " diagnostics.json ",
     73                 mediaType: " Application/JSON ",
     74                 sizeBytes: UInt64(data.count)
     75             ),
     76         ],
     77         subject: " Radroots "
     78     )
     79 
     80     let item = try RadrootsDocumentPresentationAdapter.transferItem(for: request, fileAccess: access)
     81     guard let prepared = item.preparedExport else {
     82         Issue.record("expected prepared file share item")
     83         return
     84     }
     85 
     86     #expect(item.subject == "Radroots")
     87     #expect(prepared.suggestedFilename == "diagnostics.json")
     88     #expect(prepared.mediaType == "application/json")
     89     #expect(prepared.sizeBytes == UInt64(data.count))
     90     #expect(try Data(contentsOf: prepared.fileURL) == data)
     91     #expect(try access.preparedExportExists(prepared))
     92 }
     93 
     94 @Test func documentPresentationPreparesStagedBlobShareItems() throws {
     95     let access = try testDocumentPresentationFileAccess()
     96     let data = Data("staged export".utf8)
     97     let staged = try access.stageBlob(
     98         data,
     99         mediaType: "text/plain",
    100         filenameHint: "staged-note.txt"
    101     )
    102     let request = try RadrootsShareRequest(
    103         items: [.stagedBlob(staged, suggestedFilename: nil)],
    104         subject: nil
    105     )
    106 
    107     let item = try RadrootsDocumentPresentationAdapter.transferItem(for: request, fileAccess: access)
    108     guard let prepared = item.preparedExport else {
    109         Issue.record("expected prepared staged blob share item")
    110         return
    111     }
    112 
    113     #expect(prepared.suggestedFilename == "staged-note.txt")
    114     #expect(prepared.mediaType == "text/plain")
    115     #expect(prepared.sizeBytes == UInt64(data.count))
    116     #expect(try Data(contentsOf: prepared.fileURL) == data)
    117 }
    118 
    119 @Test func documentPresentationRejectsUnsafeShareFilesAndSecretMaterial() throws {
    120     #expect(throws: RadrootsDocumentInterchangeError.self) {
    121         _ = try RadrootsShareRequest(
    122             items: [
    123                 .file(
    124                     RadrootsFileReference(scope: .data, relativePath: "/tmp/private.txt"),
    125                     suggestedFilename: "private.txt",
    126                     mediaType: "text/plain",
    127                     sizeBytes: nil
    128                 ),
    129             ]
    130         )
    131     }
    132     #expect(throws: RadrootsDocumentInterchangeError.self) {
    133         _ = try RadrootsShareRequest(items: [.text("nostr:nsec1qqqqqq")])
    134     }
    135     #expect(throws: RadrootsDocumentInterchangeError.self) {
    136         _ = try RadrootsShareRequest(
    137             items: [
    138                 .file(
    139                     RadrootsFileReference(scope: .data, relativePath: "identity/public.json"),
    140                     suggestedFilename: "selected_secret_hex.json",
    141                     mediaType: "application/json",
    142                     sizeBytes: nil
    143                 ),
    144             ]
    145         )
    146     }
    147 }
    148 
    149 @Test func preparedExportFileDocumentWrapsPreparedExportURL() throws {
    150     let directory = FileManager.default.temporaryDirectory
    151         .appendingPathComponent("radroots-prepared-export-\(UUID().uuidString)", isDirectory: true)
    152     try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
    153     let fileURL = directory.appendingPathComponent("diagnostics.json")
    154     let data = Data(#"{"status":"ok"}"#.utf8)
    155     try data.write(to: fileURL)
    156     let prepared = try RadrootsPreparedExportDocument(
    157         preparedID: "prepared_1",
    158         fileURL: fileURL,
    159         suggestedFilename: "diagnostics.json",
    160         mediaType: "application/json",
    161         sizeBytes: UInt64(data.count)
    162     )
    163 
    164     let document = RadrootsPreparedExportFileDocument(preparedExport: prepared)
    165 
    166     #expect(document.fileURL == fileURL.standardizedFileURL)
    167     #expect(RadrootsPreparedExportFileDocument.readableContentTypes == [.data])
    168 }
    169 
    170 private func testDocumentPresentationFileAccess() throws -> RadrootsAppleFileAccess {
    171     let unresolvedRoot = FileManager.default.temporaryDirectory
    172         .appendingPathComponent("radroots-document-presentation-\(UUID().uuidString)", isDirectory: true)
    173     try FileManager.default.createDirectory(at: unresolvedRoot, withIntermediateDirectories: true)
    174     let pointer = try #require(unresolvedRoot.path.withCString { Darwin.realpath($0, nil) })
    175     defer { Darwin.free(pointer) }
    176     let root = URL(fileURLWithPath: String(cString: pointer), isDirectory: true)
    177     let roots = try RadrootsAppleFileRoots(
    178         appIdentifier: "org.radroots.document-presentation.tests",
    179         dataRoot: root.appendingPathComponent("data", isDirectory: true),
    180         cacheRoot: root.appendingPathComponent("cache", isDirectory: true),
    181         temporaryRoot: root.appendingPathComponent("tmp", isDirectory: true)
    182     )
    183     return RadrootsAppleFileAccess(roots: roots)
    184 }