169 lines
6.7 KiB
Swift
169 lines
6.7 KiB
Swift
//
|
|
// ServerAddressTests.swift
|
|
//
|
|
// Copyright 2026 Brendan Szymanski <hello@bscubed.dev>
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
|
|
import Foundation
|
|
import Testing
|
|
|
|
@testable import LuminateCore
|
|
|
|
/// Covers normalization, validation, and candidate selection for onboarding server addresses.
|
|
@Suite struct ServerAddressTests {
|
|
@Test("Normalizes a bare host and permits HTTPS fallback")
|
|
func bareHost() throws {
|
|
let address = try ServerAddress.normalize(address: " jellyfin.example.com ", port: "", policy: .automatic)
|
|
#expect(address.baseURL.absoluteString == "https://jellyfin.example.com/")
|
|
#expect(
|
|
address.probeCandidates.map(\.absoluteString) == [
|
|
"https://jellyfin.example.com/",
|
|
"http://jellyfin.example.com/",
|
|
])
|
|
}
|
|
|
|
@Test("Preserves a custom port on a bare host")
|
|
func customPort() throws {
|
|
let address = try ServerAddress.normalize(address: "jellyfin.example.com", port: "8096", policy: .automatic)
|
|
#expect(address.baseURL.absoluteString == "https://jellyfin.example.com:8096/")
|
|
}
|
|
|
|
@Test("Honors an explicit HTTP scheme without fallback")
|
|
func explicitHTTP() throws {
|
|
let address = try ServerAddress.normalize(
|
|
address: "http://192.168.1.20:8096",
|
|
port: "",
|
|
policy: .automatic
|
|
)
|
|
#expect(address.baseURL.absoluteString == "http://192.168.1.20:8096/")
|
|
#expect(address.probeCandidates.map(\.absoluteString) == ["http://192.168.1.20:8096/"])
|
|
}
|
|
|
|
@Test("Force HTTPS preserves an explicit nonstandard port")
|
|
func forceHTTPSPreservesPort() throws {
|
|
let address = try ServerAddress.normalize(
|
|
address: "http://192.168.1.20:8096",
|
|
port: "",
|
|
policy: .httpsOnly
|
|
)
|
|
#expect(address.baseURL.absoluteString == "https://192.168.1.20:8096/")
|
|
#expect(address.probeCandidates.map(\.absoluteString) == ["https://192.168.1.20:8096/"])
|
|
}
|
|
|
|
@Test("Lowercases an explicit HTTPS scheme without changing its host")
|
|
func lowercasesScheme() throws {
|
|
let address = try ServerAddress.normalize(address: "HTTPS://Host.Example", port: "", policy: .automatic)
|
|
#expect(address.baseURL.absoluteString == "https://Host.Example/")
|
|
}
|
|
|
|
@Test("Preserves a reverse proxy path prefix")
|
|
func preservesPathPrefix() throws {
|
|
let address = try ServerAddress.normalize(
|
|
address: "https://example.com/jellyfin",
|
|
port: "",
|
|
policy: .automatic
|
|
)
|
|
#expect(address.baseURL.absoluteString == "https://example.com/jellyfin/")
|
|
}
|
|
|
|
@Test("Strips a redundant HTTPS default port")
|
|
func stripsDefaultPort() throws {
|
|
let address = try ServerAddress.normalize(address: "https://example.com:443", port: "", policy: .automatic)
|
|
#expect(address.baseURL.absoluteString == "https://example.com/")
|
|
}
|
|
|
|
@Test("Preserves IPv6 literal brackets")
|
|
func preservesIPv6() throws {
|
|
let address = try ServerAddress.normalize(
|
|
address: "http://[2001:db8::10]:8096",
|
|
port: "",
|
|
policy: .automatic
|
|
)
|
|
#expect(address.baseURL.absoluteString == "http://[2001:db8::10]:8096/")
|
|
}
|
|
|
|
@Test("Force HTTPS disables fallback for a bare host")
|
|
func forceHTTPSDisablesFallback() throws {
|
|
let address = try ServerAddress.normalize(address: "jellyfin.example.com", port: "", policy: .httpsOnly)
|
|
#expect(address.probeCandidates.map(\.absoluteString) == ["https://jellyfin.example.com/"])
|
|
}
|
|
|
|
@Test("Rejects an empty address")
|
|
func emptyAddress() {
|
|
#expect(throws: ServerAddressError.emptyAddress) {
|
|
_ = try ServerAddress.normalize(address: "", port: "", policy: .automatic)
|
|
}
|
|
}
|
|
|
|
@Test("Rejects an unsupported scheme")
|
|
func unsupportedScheme() {
|
|
#expect(throws: ServerAddressError.unsupportedScheme("ftp")) {
|
|
_ = try ServerAddress.normalize(address: "ftp://host", port: "", policy: .automatic)
|
|
}
|
|
}
|
|
|
|
@Test("Rejects conflicting port fields")
|
|
func conflictingPorts() {
|
|
#expect(throws: ServerAddressError.conflictingPorts) {
|
|
_ = try ServerAddress.normalize(address: "https://host:8920", port: "8096", policy: .automatic)
|
|
}
|
|
}
|
|
|
|
@Test("Rejects ports outside the TCP range")
|
|
func invalidPortRange() {
|
|
#expect(throws: ServerAddressError.invalidPort("70000")) {
|
|
_ = try ServerAddress.normalize(address: "host", port: "70000", policy: .automatic)
|
|
}
|
|
#expect(throws: ServerAddressError.invalidPort("0")) {
|
|
_ = try ServerAddress.normalize(address: "host", port: "0", policy: .automatic)
|
|
}
|
|
}
|
|
|
|
@Test("Rejects nonnumeric port text")
|
|
func invalidPortText() {
|
|
#expect(throws: ServerAddressError.invalidPort("80abc")) {
|
|
_ = try ServerAddress.normalize(address: "host", port: "80abc", policy: .automatic)
|
|
}
|
|
}
|
|
|
|
@Test("Rejects credentials in the server address")
|
|
func rejectsCredentials() {
|
|
#expect(throws: ServerAddressError.credentialsNotAllowed) {
|
|
_ = try ServerAddress.normalize(address: "https://user:pw@host", port: "", policy: .automatic)
|
|
}
|
|
}
|
|
|
|
@Test("Rejects query strings and fragments")
|
|
func rejectsQueryAndFragment() {
|
|
#expect(throws: ServerAddressError.queryOrFragmentNotAllowed) {
|
|
_ = try ServerAddress.normalize(address: "https://host/?token=x", port: "", policy: .automatic)
|
|
}
|
|
#expect(throws: ServerAddressError.queryOrFragmentNotAllowed) {
|
|
_ = try ServerAddress.normalize(address: "https://host/#login", port: "", policy: .automatic)
|
|
}
|
|
}
|
|
|
|
@Test("Explains invalid address input")
|
|
func errorMessages() {
|
|
#expect(ServerAddressError.emptyAddress.message == "Enter a server address.")
|
|
#expect(ServerAddressError.unsupportedScheme("ftp").message == "ftp is not supported. Use http or https.")
|
|
#expect(
|
|
ServerAddressError.invalidPort("70000").message
|
|
== "70000 is not a valid port. Enter a number from 1 to 65535.")
|
|
}
|
|
}
|