mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 10:29:58 +02:00
test(apple): CoreGateway seam, XCTest suite, and CI
- Introduce a CoreGateway protocol so feature models depend on a seam (CoreRepository conforms); enables faking the core in tests - Add a VniDropTests target with 42 tests mirroring the KMP suites: approval coordinator, send/receive/settings/app models, preferences, file previews, invitation decode, message queue, error mapping - Add a fake gateway/file-system/device-info and fixtures - Add .github/workflows/apple.yml: build the Rust core, generate the project, and run the tests on an iOS Simulator
This commit is contained in:
69
apple/Tests/ApprovalCoordinatorTests.swift
Normal file
69
apple/Tests/ApprovalCoordinatorTests.swift
Normal file
@@ -0,0 +1,69 @@
|
||||
import XCTest
|
||||
import Combine
|
||||
@testable import VniDrop
|
||||
|
||||
/// Ports `feature/approvals/ApprovalCoordinatorTest.kt` (the gateway-observable
|
||||
/// parts; notification assertions require a notification-service seam we don't
|
||||
/// have on Apple yet).
|
||||
@MainActor
|
||||
final class ApprovalCoordinatorTests: XCTestCase {
|
||||
|
||||
private func makeCoordinator(_ core: FakeCoreGateway) -> ApprovalCoordinator {
|
||||
ApprovalCoordinator(
|
||||
repository: core,
|
||||
preferences: Fixtures.preferences(),
|
||||
notifications: LocalNotificationService(),
|
||||
visibility: AppVisibility(),
|
||||
messages: UiMessageController()
|
||||
)
|
||||
}
|
||||
|
||||
func testOrdersPendingRequestsByRequestedAt() async {
|
||||
let core = FakeCoreGateway()
|
||||
core.requests[1] = [Fixtures.request(id: "new", requestedAt: 20),
|
||||
Fixtures.request(id: "old", requestedAt: 10)]
|
||||
let coordinator = makeCoordinator(core)
|
||||
|
||||
core.setState(CoreState(isInitialized: true, transfers: [Fixtures.transfer(id: 1, direction: .send, status: .sharing)]))
|
||||
core.emit(.approvalChanged(transferId: 1))
|
||||
|
||||
await waitUntil { !coordinator.state.pending.isEmpty }
|
||||
XCTAssertEqual(coordinator.state.pending.map(\.id), ["old", "new"])
|
||||
XCTAssertEqual(coordinator.state.current?.id, "old")
|
||||
}
|
||||
|
||||
func testFailedResponseKeepsRequestVisibleAndClearsResponding() async {
|
||||
let core = FakeCoreGateway()
|
||||
core.requests[1] = [Fixtures.request(id: "request", requestedAt: 10)]
|
||||
core.responseResult = .failure(TestError.unimplemented)
|
||||
let coordinator = makeCoordinator(core)
|
||||
|
||||
core.setState(CoreState(isInitialized: true, transfers: [Fixtures.transfer(id: 1, direction: .send, status: .sharing)]))
|
||||
core.emit(.approvalChanged(transferId: 1))
|
||||
await waitUntil { coordinator.state.pending.contains { $0.id == "request" } }
|
||||
|
||||
coordinator.accept("request")
|
||||
await waitUntil { coordinator.state.respondingIds.isEmpty && core.responses.count == 1 }
|
||||
|
||||
XCTAssertTrue(coordinator.state.pending.contains { $0.id == "request" })
|
||||
XCTAssertTrue(coordinator.state.respondingIds.isEmpty)
|
||||
XCTAssertEqual(core.responses.first?.accepted, true)
|
||||
}
|
||||
|
||||
func testAcceptRespondsPositivelyAndSingleFlights() async {
|
||||
let core = FakeCoreGateway()
|
||||
core.requests[1] = [Fixtures.request(id: "request", requestedAt: 10)]
|
||||
let coordinator = makeCoordinator(core)
|
||||
core.setState(CoreState(isInitialized: true, transfers: [Fixtures.transfer(id: 1, direction: .send, status: .sharing)]))
|
||||
core.emit(.approvalChanged(transferId: 1))
|
||||
await waitUntil { coordinator.state.current != nil }
|
||||
|
||||
coordinator.accept("request")
|
||||
coordinator.accept("request") // second call must be ignored (single-flight)
|
||||
await waitUntil { core.responses.count >= 1 }
|
||||
try? await Task.sleep(nanoseconds: 50_000_000)
|
||||
|
||||
XCTAssertEqual(core.responses.count, 1)
|
||||
XCTAssertEqual(core.responses.first?.id, "request")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user