apple_kit

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

RadrootsDocumentPresentationTests.swift (7319B)


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