This commit is contained in:
Justin Mazzocchi 2020-09-09 21:51:31 -07:00
parent b0d0792014
commit 83bce93225
No known key found for this signature in database
GPG Key ID: E223E6937AAFB01C
6 changed files with 32 additions and 27 deletions

View File

@ -5,7 +5,7 @@ import Combine
import Foundation import Foundation
import HTTP import HTTP
public struct InstanceFilterService { public struct InstanceURLService {
private let httpClient: HTTPClient private let httpClient: HTTPClient
private var userDefaultsClient: UserDefaultsClient private var userDefaultsClient: UserDefaultsClient
@ -15,7 +15,7 @@ public struct InstanceFilterService {
} }
} }
public extension InstanceFilterService { public extension InstanceURLService {
func isFiltered(url: URL) -> Bool { func isFiltered(url: URL) -> Bool {
guard let host = url.host else { return true } guard let host = url.host else { return true }
@ -54,7 +54,7 @@ private struct UpdatedFilterTarget: DecodableTarget {
let headers: HTTPHeaders? = nil let headers: HTTPHeaders? = nil
} }
private extension InstanceFilterService { private extension InstanceURLService {
var filter: BloomFilter<String> { var filter: BloomFilter<String> {
userDefaultsClient.updatedInstanceFilter ?? Self.defaultFilter userDefaultsClient.updatedInstanceFilter ?? Self.defaultFilter
} }

View File

@ -8,9 +8,9 @@ import CombineExpectations
import Stubbing import Stubbing
import XCTest import XCTest
class InstanceFilterServiceTests: XCTestCase { class InstanceURLServiceTests: XCTestCase {
func testFiltering() throws { func testFiltering() throws {
let sut = InstanceFilterService(environment: .mock()) let sut = InstanceURLService(environment: .mock())
let unfilteredInstanceURL = URL(string: "https://unfiltered.instance")! let unfilteredInstanceURL = URL(string: "https://unfiltered.instance")!
let filteredInstanceURL = URL(string: "https://filtered.instance")! let filteredInstanceURL = URL(string: "https://filtered.instance")!
let subdomainFilteredInstanceURL = URL(string: "https://subdomain.filtered.instance")! let subdomainFilteredInstanceURL = URL(string: "https://subdomain.filtered.instance")!
@ -22,7 +22,7 @@ class InstanceFilterServiceTests: XCTestCase {
func testUpdating() throws { func testUpdating() throws {
let environment = AppEnvironment.mock() let environment = AppEnvironment.mock()
var sut = InstanceFilterService(environment: environment) var sut = InstanceURLService(environment: environment)
let previouslyFilteredInstanceURL = URL(string: "https://filtered.instance")! let previouslyFilteredInstanceURL = URL(string: "https://filtered.instance")!
let newlyFilteredInstanceURL = URL(string: "https://instance.filtered")! let newlyFilteredInstanceURL = URL(string: "https://instance.filtered")!
@ -45,7 +45,7 @@ class InstanceFilterServiceTests: XCTestCase {
XCTAssertFalse(sut.isFiltered(url: previouslyFilteredInstanceURL)) XCTAssertFalse(sut.isFiltered(url: previouslyFilteredInstanceURL))
XCTAssertTrue(sut.isFiltered(url: newlyFilteredInstanceURL)) XCTAssertTrue(sut.isFiltered(url: newlyFilteredInstanceURL))
sut = InstanceFilterService(environment: environment) sut = InstanceURLService(environment: environment)
XCTAssertFalse(sut.isFiltered(url: previouslyFilteredInstanceURL)) XCTAssertFalse(sut.isFiltered(url: previouslyFilteredInstanceURL))
XCTAssertTrue(sut.isFiltered(url: newlyFilteredInstanceURL)) XCTAssertTrue(sut.isFiltered(url: newlyFilteredInstanceURL))

View File

@ -15,13 +15,13 @@ public final class AddIdentityViewModel: ObservableObject {
public let addedIdentityID: AnyPublisher<UUID, Never> public let addedIdentityID: AnyPublisher<UUID, Never>
private let allIdentitiesService: AllIdentitiesService private let allIdentitiesService: AllIdentitiesService
private let instanceFilterService: InstanceFilterService private let instanceURLService: InstanceURLService
private let addedIdentityIDSubject = PassthroughSubject<UUID, Never>() private let addedIdentityIDSubject = PassthroughSubject<UUID, Never>()
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
init(allIdentitiesService: AllIdentitiesService, instanceFilterService: InstanceFilterService) { init(allIdentitiesService: AllIdentitiesService, instanceURLService: InstanceURLService) {
self.allIdentitiesService = allIdentitiesService self.allIdentitiesService = allIdentitiesService
self.instanceFilterService = instanceFilterService self.instanceURLService = instanceURLService
addedIdentityID = addedIdentityIDSubject.eraseToAnyPublisher() addedIdentityID = addedIdentityIDSubject.eraseToAnyPublisher()
} }
} }
@ -36,7 +36,7 @@ public extension AddIdentityViewModel {
} }
func refreshFilter() { func refreshFilter() {
instanceFilterService.updateFilter() instanceURLService.updateFilter()
.sink { _ in } .sink { _ in }
.store(in: &cancellables) .store(in: &cancellables)
} }
@ -46,21 +46,26 @@ private extension AddIdentityViewModel {
private static let filteredURL = URL(string: "https://filtered")! private static let filteredURL = URL(string: "https://filtered")!
private static let HTTPSPrefix = "https://" private static let HTTPSPrefix = "https://"
static func url(fieldText: String) -> URL? {
if fieldText.hasPrefix(HTTPSPrefix), let prefixedURL = URL(string: fieldText) {
return prefixedURL
} else if let unprefixedURL = URL(string: HTTPSPrefix + fieldText) {
return unprefixedURL
}
return nil
}
func addIdentity(authenticated: Bool) { func addIdentity(authenticated: Bool) {
let identityID = UUID() let identityID = UUID()
let url: URL
if urlFieldText.hasPrefix(Self.HTTPSPrefix), let prefixedURL = URL(string: urlFieldText) { guard let url = Self.url(fieldText: urlFieldText) else {
url = prefixedURL
} else if let unprefixedURL = URL(string: Self.HTTPSPrefix + urlFieldText) {
url = unprefixedURL
} else {
alertItem = AlertItem(error: AddIdentityError.unableToConnectToInstance) alertItem = AlertItem(error: AddIdentityError.unableToConnectToInstance)
return return
} }
if instanceFilterService.isFiltered(url: url) { if instanceURLService.isFiltered(url: url) {
loading = true loading = true
DispatchQueue.main.asyncAfter(deadline: .now() + .random(in: 0.01...0.1)) { DispatchQueue.main.asyncAfter(deadline: .now() + .random(in: 0.01...0.1)) {

View File

@ -51,7 +51,7 @@ public extension RootViewModel {
func addIdentityViewModel() -> AddIdentityViewModel { func addIdentityViewModel() -> AddIdentityViewModel {
AddIdentityViewModel( AddIdentityViewModel(
allIdentitiesService: allIdentitiesService, allIdentitiesService: allIdentitiesService,
instanceFilterService: InstanceFilterService(environment: environment)) instanceURLService: InstanceURLService(environment: environment))
} }
} }

View File

@ -15,7 +15,7 @@ class AddIdentityViewModelTests: XCTestCase {
let environment = AppEnvironment.mock() let environment = AppEnvironment.mock()
let sut = AddIdentityViewModel( let sut = AddIdentityViewModel(
allIdentitiesService: try AllIdentitiesService(environment: environment), allIdentitiesService: try AllIdentitiesService(environment: environment),
instanceFilterService: InstanceFilterService(environment: environment)) instanceURLService: InstanceURLService(environment: environment))
let addedIDRecorder = sut.addedIdentityID.record() let addedIDRecorder = sut.addedIdentityID.record()
sut.urlFieldText = "https://mastodon.social" sut.urlFieldText = "https://mastodon.social"
@ -28,7 +28,7 @@ class AddIdentityViewModelTests: XCTestCase {
let environment = AppEnvironment.mock() let environment = AppEnvironment.mock()
let sut = AddIdentityViewModel( let sut = AddIdentityViewModel(
allIdentitiesService: try AllIdentitiesService(environment: environment), allIdentitiesService: try AllIdentitiesService(environment: environment),
instanceFilterService: InstanceFilterService(environment: environment)) instanceURLService: InstanceURLService(environment: environment))
let addedIDRecorder = sut.addedIdentityID.record() let addedIDRecorder = sut.addedIdentityID.record()
sut.urlFieldText = "mastodon.social" sut.urlFieldText = "mastodon.social"
@ -41,7 +41,7 @@ class AddIdentityViewModelTests: XCTestCase {
let environment = AppEnvironment.mock() let environment = AppEnvironment.mock()
let sut = AddIdentityViewModel( let sut = AddIdentityViewModel(
allIdentitiesService: try AllIdentitiesService(environment: environment), allIdentitiesService: try AllIdentitiesService(environment: environment),
instanceFilterService: InstanceFilterService(environment: environment)) instanceURLService: InstanceURLService(environment: environment))
let recorder = sut.$alertItem.record() let recorder = sut.$alertItem.record()
XCTAssertNil(try wait(for: recorder.next(), timeout: 1)) XCTAssertNil(try wait(for: recorder.next(), timeout: 1))
@ -51,14 +51,14 @@ class AddIdentityViewModelTests: XCTestCase {
let alertItem = try wait(for: recorder.next(), timeout: 1) let alertItem = try wait(for: recorder.next(), timeout: 1)
XCTAssertEqual((alertItem?.error as? URLError)?.code, URLError.badURL) XCTAssertEqual((alertItem?.error as? AddIdentityError), AddIdentityError.unableToConnectToInstance)
} }
func testDoesNotAlertCanceledLogin() throws { func testDoesNotAlertCanceledLogin() throws {
let environment = AppEnvironment.mock(webAuthSessionType: CanceledLoginMockWebAuthSession.self) let environment = AppEnvironment.mock(webAuthSessionType: CanceledLoginMockWebAuthSession.self)
let sut = AddIdentityViewModel( let sut = AddIdentityViewModel(
allIdentitiesService: try AllIdentitiesService(environment: environment), allIdentitiesService: try AllIdentitiesService(environment: environment),
instanceFilterService: InstanceFilterService(environment: environment)) instanceURLService: InstanceURLService(environment: environment))
let recorder = sut.$alertItem.record() let recorder = sut.$alertItem.record()
XCTAssertNil(try wait(for: recorder.next(), timeout: 1)) XCTAssertNil(try wait(for: recorder.next(), timeout: 1))

View File

@ -14,7 +14,7 @@ class RootViewModelTests: XCTestCase {
let sut = try RootViewModel( let sut = try RootViewModel(
environment: .mock(), environment: .mock(),
registerForRemoteNotifications: { Empty().setFailureType(to: Error.self).eraseToAnyPublisher() }) registerForRemoteNotifications: { Empty().setFailureType(to: Error.self).eraseToAnyPublisher() })
let recorder = sut.$identification.record() let recorder = sut.$navigationViewModel.record()
XCTAssertNil(try wait(for: recorder.next(), timeout: 1)) XCTAssertNil(try wait(for: recorder.next(), timeout: 1))
@ -27,8 +27,8 @@ class RootViewModelTests: XCTestCase {
addIdentityViewModel.urlFieldText = "https://mastodon.social" addIdentityViewModel.urlFieldText = "https://mastodon.social"
addIdentityViewModel.logInTapped() addIdentityViewModel.logInTapped()
let mainNavigationViewModel = try wait(for: recorder.next(), timeout: 1)! let navigationViewModel = try wait(for: recorder.next(), timeout: 1)!
XCTAssertNotNil(mainNavigationViewModel) XCTAssertNotNil(navigationViewModel)
} }
} }