field_ios

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

TeraVisibilityTests.swift (5644B)


      1 import Foundation
      2 @testable import TeraApp
      3 import XCTest
      4 
      5 @MainActor
      6 final class TeraVisibilityTests: XCTestCase {
      7   func testChangeClearsEveryVisibleStoreAndRejectsLateSearchAndMedia() async throws {
      8     let backend = try TeraScopeBackend()
      9     let client = try await TeraScopeFixtures.client(backend)
     10     let stores = TeraProductStores(runtimeClient: client)
     11     stores.configure(snapshot: TeraScopeFixtures.snapshot())
     12     let context = try XCTUnwrap(stores.today.selectedContext)
     13     await stores.today.reload(refreshProjection: false)
     14     let card = try XCTUnwrap(stores.today.cards.first)
     15     stores.search.updateQuery("old")
     16     await stores.search.search()
     17     await stores.me.reload()
     18     XCTAssertFalse(stores.search.results.isEmpty)
     19     XCTAssertNotNil(stores.me.snapshot)
     20     let search = await backend.pause(.search)
     21     let searching = Task { await stores.search.search() }
     22     await search.entered.wait()
     23     let media = await backend.pause(.media)
     24     let reference = TeraScopeFixtures.reference()
     25     stores.media.load(media: reference, context: context)
     26     await media.entered.wait()
     27     let change = ResourceTestPause()
     28     await backend.visibilityStorage.hold(change)
     29     let oldScope = stores.today.scopeGeneration
     30     let changing = Task { await stores.visibility.change(author: card.authorPublicKey, to: .blocked) }
     31     await change.entered.wait()
     32     XCTAssertTrue(stores.today.cards.isEmpty)
     33     XCTAssertNil(stores.today.currentCard(id: card.id))
     34     XCTAssertNotEqual(stores.today.scopeGeneration, oldScope)
     35     XCTAssertTrue(stores.search.results.isEmpty)
     36     XCTAssertNil(stores.me.snapshot)
     37     await backend.setPage(TeraTodayPage(asOfUnixSeconds: 1, items: [], nextCursor: nil, calendar: TeraScopeFixtures.viewerCalendar(asOf: 1)))
     38     await change.resume.open()
     39     await changing.value
     40     await search.resume.open()
     41     await searching.value
     42     await media.resume.open()
     43     XCTAssertTrue(stores.search.results.isEmpty)
     44     XCTAssertTrue(stores.today.cards.isEmpty)
     45     XCTAssertNotEqual(stores.media.state(for: reference, context: context), try .ready(TeraScopeFixtures.artifact("a")))
     46     XCTAssertEqual(stores.visibility.policy?.entries.first?.visibility, .blocked)
     47     let counts = await backend.counts
     48     XCTAssertNil(counts[.refresh], "Visibility changes only re-read local data")
     49     XCTAssertEqual(counts[.media], 1)
     50     stores.stop()
     51     _ = try await client.stop()
     52   }
     53 
     54   func testUnconfirmedChangeRetainsClearedPresentation() async throws {
     55     let backend = try TeraScopeBackend()
     56     let client = try await TeraScopeFixtures.client(backend)
     57     let stores = TeraProductStores(runtimeClient: client)
     58     stores.configure(snapshot: TeraScopeFixtures.snapshot())
     59     await stores.today.reload(refreshProjection: false)
     60     XCTAssertFalse(stores.today.cards.isEmpty)
     61     await backend.visibilityStorage.fail()
     62     await stores.visibility.change(author: String(repeating: "a", count: 64), to: .muted)
     63     XCTAssertTrue(stores.today.cards.isEmpty)
     64     XCTAssertNil(stores.visibility.policy)
     65     XCTAssertNotNil(stores.visibility.message)
     66     XCTAssertFalse(stores.visibility.isWorking)
     67     stores.stop()
     68     _ = try await client.stop()
     69   }
     70 
     71   func testRealGeneratedPolicyPersistsAndExplicitRestoreSurvivesRestart() async throws {
     72     let fixture = try OfflineMediaFixture()
     73     defer { fixture.remove() }
     74     let client = TeraRuntimeClient.production()
     75     let signer = ComposerForbiddenSigner()
     76     let configuration = fixture.configuration(signer)
     77     let snapshot = try await client.start(configuration: configuration)
     78     let author = snapshot.identity.publicKeyHex
     79     let blocked = try await client.setAuthorVisibility(author: author, visibility: .blocked)
     80     XCTAssertEqual(blocked.entries, [TeraAuthorVisibilityEntry(author: author, visibility: .blocked)])
     81     XCTAssertFalse(String(reflecting: blocked).contains(author))
     82     XCTAssertFalse(String(reflecting: blocked.entries).contains(author))
     83     _ = try await client.stop()
     84     _ = try await client.start(configuration: configuration)
     85     let loaded = try await client.authorVisibility()
     86     XCTAssertEqual(loaded, blocked)
     87     let muted = try await client.setAuthorVisibility(author: author, visibility: .muted)
     88     XCTAssertEqual(muted.revision, blocked.revision + 1)
     89     let restored = try await client.setAuthorVisibility(author: author, visibility: .visible)
     90     XCTAssertTrue(restored.entries.isEmpty)
     91     XCTAssertEqual(restored.revision, muted.revision + 1)
     92     let signs = await signer.requests
     93     XCTAssertEqual(signs, 0)
     94     _ = try await client.stop()
     95   }
     96 }
     97 
     98 actor VisibilityTestStorage {
     99   private var policy = TeraAuthorVisibilityPolicy(revision: 0, entries: [])
    100   private var pause: ResourceTestPause?
    101   private var failed = false
    102   func hold(_ value: ResourceTestPause) {
    103     pause = value
    104   }
    105 
    106   func fail() {
    107     failed = true
    108   }
    109 
    110   func read() -> TeraAuthorVisibilityPolicy {
    111     policy
    112   }
    113 
    114   func change(author: String, to visibility: TeraAuthorVisibility) async throws -> TeraAuthorVisibilityPolicy {
    115     await pause?.wait()
    116     if failed {
    117       throw TeraComposerAcknowledgment.unconfirmed
    118     }
    119     policy = TeraAuthorVisibilityPolicy(revision: policy.revision + 1, entries: visibility == .visible ? [] : [.init(author: author, visibility: visibility)])
    120     return policy
    121   }
    122 }
    123 
    124 extension TeraScopeBackend {
    125   func authorVisibility() async -> TeraAuthorVisibilityPolicy {
    126     await visibilityStorage.read()
    127   }
    128 
    129   func setAuthorVisibility(author: String, visibility: TeraAuthorVisibility) async throws -> TeraAuthorVisibilityPolicy {
    130     try await visibilityStorage.change(author: author, to: visibility)
    131   }
    132 }