227 lines
6.6 KiB
Swift
227 lines
6.6 KiB
Swift
import Testing
|
||
@testable import bookstax
|
||
|
||
// MARK: - URL Validation
|
||
|
||
@Suite("OnboardingViewModel – URL Validation")
|
||
struct OnboardingViewModelURLTests {
|
||
|
||
// MARK: Empty input
|
||
|
||
@Test("Empty URL sets error and returns false")
|
||
func emptyURL() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = ""
|
||
#expect(vm.validateServerURL() == false)
|
||
#expect(vm.serverURLError != nil)
|
||
}
|
||
|
||
@Test("Whitespace-only URL sets error and returns false")
|
||
func whitespaceURL() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = " "
|
||
#expect(vm.validateServerURL() == false)
|
||
#expect(vm.serverURLError != nil)
|
||
}
|
||
|
||
// MARK: Auto-prefix https://
|
||
|
||
@Test("URL without scheme gets https:// prepended")
|
||
func autoprefixHTTPS() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "wiki.example.com"
|
||
let result = vm.validateServerURL()
|
||
#expect(result == true)
|
||
#expect(vm.serverURLInput.hasPrefix("https://"))
|
||
}
|
||
|
||
@Test("URL already starting with https:// is left unchanged")
|
||
func httpsAlreadyPresent() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "https://wiki.example.com"
|
||
let result = vm.validateServerURL()
|
||
#expect(result == true)
|
||
#expect(vm.serverURLInput == "https://wiki.example.com")
|
||
}
|
||
|
||
@Test("URL starting with http:// is left as-is (not double-prefixed)")
|
||
func httpNotDoublePrefixed() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "http://wiki.example.com"
|
||
let result = vm.validateServerURL()
|
||
#expect(result == true)
|
||
#expect(vm.serverURLInput == "http://wiki.example.com")
|
||
}
|
||
|
||
// MARK: Trailing slash removal
|
||
|
||
@Test("Trailing slash is stripped from URL")
|
||
func trailingSlash() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "https://wiki.example.com/"
|
||
_ = vm.validateServerURL()
|
||
#expect(vm.serverURLInput == "https://wiki.example.com")
|
||
}
|
||
|
||
@Test("Multiple trailing slashes – only last slash stripped then accepted")
|
||
func multipleSlashesInPath() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "https://wiki.example.com/path/"
|
||
_ = vm.validateServerURL()
|
||
#expect(vm.serverURLInput == "https://wiki.example.com/path")
|
||
}
|
||
|
||
// MARK: Successful validation
|
||
|
||
@Test("Valid URL clears any previous error")
|
||
func validURLClearsError() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLError = "Previous error"
|
||
vm.serverURLInput = "https://books.mycompany.com"
|
||
#expect(vm.validateServerURL() == true)
|
||
#expect(vm.serverURLError == nil)
|
||
}
|
||
}
|
||
|
||
// MARK: - isHTTP Flag
|
||
|
||
@Suite("OnboardingViewModel – isHTTP")
|
||
struct OnboardingViewModelHTTPTests {
|
||
|
||
@Test("http:// URL sets isHTTP = true")
|
||
func httpURL() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "http://wiki.local"
|
||
#expect(vm.isHTTP == true)
|
||
}
|
||
|
||
@Test("https:// URL sets isHTTP = false")
|
||
func httpsURL() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "https://wiki.local"
|
||
#expect(vm.isHTTP == false)
|
||
}
|
||
|
||
@Test("Empty URL is not HTTP")
|
||
func emptyNotHTTP() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = ""
|
||
#expect(vm.isHTTP == false)
|
||
}
|
||
}
|
||
|
||
// MARK: - isRemoteServer Detection
|
||
|
||
@Suite("OnboardingViewModel – isRemoteServer")
|
||
struct OnboardingViewModelRemoteTests {
|
||
|
||
// MARK: Local / private addresses
|
||
|
||
@Test("localhost is not remote")
|
||
func localhost() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "http://localhost"
|
||
#expect(vm.isRemoteServer == false)
|
||
}
|
||
|
||
@Test("127.0.0.1 is not remote")
|
||
func loopback() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "http://127.0.0.1"
|
||
#expect(vm.isRemoteServer == false)
|
||
}
|
||
|
||
@Test("IPv6 loopback ::1 is not remote")
|
||
func ipv6Loopback() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "http://[::1]"
|
||
#expect(vm.isRemoteServer == false)
|
||
}
|
||
|
||
@Test(".local mDNS host is not remote")
|
||
func mdnsLocal() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "http://bookstack.local"
|
||
#expect(vm.isRemoteServer == false)
|
||
}
|
||
|
||
@Test("Plain hostname without dots is not remote")
|
||
func plainHostname() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "http://mywiki"
|
||
#expect(vm.isRemoteServer == false)
|
||
}
|
||
|
||
@Test("10.x.x.x private range is not remote")
|
||
func privateClass_A() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "http://10.0.1.50"
|
||
#expect(vm.isRemoteServer == false)
|
||
}
|
||
|
||
@Test("192.168.x.x private range is not remote")
|
||
func privateClass_C() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "http://192.168.1.100"
|
||
#expect(vm.isRemoteServer == false)
|
||
}
|
||
|
||
@Test("172.16.x.x private range is not remote")
|
||
func privateClass_B_low() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "http://172.16.0.1"
|
||
#expect(vm.isRemoteServer == false)
|
||
}
|
||
|
||
@Test("172.31.x.x private range is not remote")
|
||
func privateClass_B_high() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "http://172.31.255.255"
|
||
#expect(vm.isRemoteServer == false)
|
||
}
|
||
|
||
@Test("172.15.x.x is outside private range and is remote")
|
||
func justBelowPrivateClass_B() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "http://172.15.0.1"
|
||
#expect(vm.isRemoteServer == true)
|
||
}
|
||
|
||
@Test("172.32.x.x is outside private range and is remote")
|
||
func justAbovePrivateClass_B() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "http://172.32.0.1"
|
||
#expect(vm.isRemoteServer == true)
|
||
}
|
||
|
||
// MARK: Remote addresses
|
||
|
||
@Test("Public IP 8.8.8.8 is remote")
|
||
func publicIP() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "http://8.8.8.8"
|
||
#expect(vm.isRemoteServer == true)
|
||
}
|
||
|
||
@Test("Public domain with subdomain is remote")
|
||
func publicDomain() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "https://wiki.mycompany.com"
|
||
#expect(vm.isRemoteServer == true)
|
||
}
|
||
|
||
@Test("Top-level domain name is remote")
|
||
func topLevelDomain() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = "https://bookstack.io"
|
||
#expect(vm.isRemoteServer == true)
|
||
}
|
||
|
||
@Test("Empty URL is not remote")
|
||
func emptyIsNotRemote() {
|
||
let vm = OnboardingViewModel()
|
||
vm.serverURLInput = ""
|
||
#expect(vm.isRemoteServer == false)
|
||
}
|
||
}
|