mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
417 lines
11 KiB
Swift
417 lines
11 KiB
Swift
//
|
|
// NodeFilterParametersTests.swift
|
|
// Meshtastic
|
|
//
|
|
// Created on 3/16/26.
|
|
//
|
|
|
|
import Foundation
|
|
import XCTest
|
|
|
|
@testable import Meshtastic
|
|
|
|
@MainActor
|
|
class NodeFilterParametersTests: XCTestCase {
|
|
|
|
// MARK: - Initialization Tests
|
|
|
|
func testDefaultInitialization() async throws {
|
|
// Clean up UserDefaults before test
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
|
|
XCTAssertEqual(filters.searchText, "")
|
|
XCTAssertEqual(filters.isOnline, false)
|
|
XCTAssertEqual(filters.isPkiEncrypted, false)
|
|
XCTAssertEqual(filters.isFavorite, false)
|
|
XCTAssertEqual(filters.isIgnored, false)
|
|
XCTAssertEqual(filters.isEnvironment, false)
|
|
XCTAssertEqual(filters.distanceFilter, false)
|
|
XCTAssertEqual(filters.maxDistance, 800_000)
|
|
XCTAssertEqual(filters.hopsAway, -1.0)
|
|
XCTAssertEqual(filters.roleFilter, false)
|
|
XCTAssertTrue(filters.deviceRoles.isEmpty)
|
|
XCTAssertEqual(filters.viaLora, true)
|
|
XCTAssertEqual(filters.viaMqtt, true)
|
|
}
|
|
|
|
func testInitializationWithPersistedDeviceRoles() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
// Store device roles in UserDefaults
|
|
let expectedRoles = [1, 2, 3, 5, 8]
|
|
UserDefaults.standard.set(expectedRoles, forKey: "nodeFilter.deviceRoles")
|
|
|
|
let filters = NodeFilterParameters()
|
|
|
|
XCTAssertEqual(filters.deviceRoles, Set(expectedRoles))
|
|
}
|
|
|
|
// MARK: - @AppStorage Persistence Tests
|
|
|
|
func testSearchTextPersistence() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters1 = NodeFilterParameters()
|
|
filters1.searchText = "Test Node"
|
|
|
|
let filters2 = NodeFilterParameters()
|
|
XCTAssertEqual(filters2.searchText, "Test Node")
|
|
}
|
|
|
|
func testBooleanFiltersPersistence() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters1 = NodeFilterParameters()
|
|
filters1.isOnline = true
|
|
filters1.isPkiEncrypted = true
|
|
filters1.isFavorite = true
|
|
filters1.isIgnored = true
|
|
filters1.isEnvironment = true
|
|
filters1.distanceFilter = true
|
|
filters1.roleFilter = true
|
|
|
|
let filters2 = NodeFilterParameters()
|
|
XCTAssertEqual(filters2.isOnline, true)
|
|
XCTAssertEqual(filters2.isPkiEncrypted, true)
|
|
XCTAssertEqual(filters2.isFavorite, true)
|
|
XCTAssertEqual(filters2.isIgnored, true)
|
|
XCTAssertEqual(filters2.isEnvironment, true)
|
|
XCTAssertEqual(filters2.distanceFilter, true)
|
|
XCTAssertEqual(filters2.roleFilter, true)
|
|
}
|
|
|
|
func testNumericFiltersPersistence() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters1 = NodeFilterParameters()
|
|
filters1.maxDistance = 500_000
|
|
filters1.hopsAway = 3.0
|
|
|
|
let filters2 = NodeFilterParameters()
|
|
XCTAssertEqual(filters2.maxDistance, 500_000)
|
|
XCTAssertEqual(filters2.hopsAway, 3.0)
|
|
}
|
|
|
|
// MARK: - Device Roles Tests
|
|
|
|
func testDeviceRolesPersistence() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters1 = NodeFilterParameters()
|
|
filters1.deviceRoles = [1, 3, 5, 7]
|
|
|
|
// Verify it's stored in UserDefaults
|
|
let storedRoles = UserDefaults.standard.array(forKey: "nodeFilter.deviceRoles") as? [Int]
|
|
XCTAssertNotNil(storedRoles)
|
|
XCTAssertEqual(Set(storedRoles!), Set([1, 3, 5, 7]))
|
|
|
|
// Verify it persists to new instance
|
|
let filters2 = NodeFilterParameters()
|
|
XCTAssertEqual(filters2.deviceRoles, Set([1, 3, 5, 7]))
|
|
}
|
|
|
|
func testAddingDeviceRoles() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
filters.deviceRoles.insert(2)
|
|
filters.deviceRoles.insert(4)
|
|
filters.deviceRoles.insert(6)
|
|
|
|
let newFilters = NodeFilterParameters()
|
|
XCTAssertTrue(newFilters.deviceRoles.contains(2))
|
|
XCTAssertTrue(newFilters.deviceRoles.contains(4))
|
|
XCTAssertTrue(newFilters.deviceRoles.contains(6))
|
|
XCTAssertEqual(newFilters.deviceRoles.count, 3)
|
|
}
|
|
|
|
func testRemovingDeviceRoles() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters1 = NodeFilterParameters()
|
|
filters1.deviceRoles = [1, 2, 3, 4, 5]
|
|
|
|
filters1.deviceRoles.remove(2)
|
|
filters1.deviceRoles.remove(4)
|
|
|
|
let filters2 = NodeFilterParameters()
|
|
XCTAssertEqual(filters2.deviceRoles, Set([1, 3, 5]))
|
|
XCTAssertFalse(filters2.deviceRoles.contains(2))
|
|
XCTAssertFalse(filters2.deviceRoles.contains(4))
|
|
}
|
|
|
|
func testEmptyDeviceRolesPersistence() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters1 = NodeFilterParameters()
|
|
filters1.deviceRoles = [1, 2, 3]
|
|
|
|
// Clear the set
|
|
filters1.deviceRoles = []
|
|
|
|
let filters2 = NodeFilterParameters()
|
|
XCTAssertTrue(filters2.deviceRoles.isEmpty)
|
|
}
|
|
|
|
// MARK: - Via Lora/MQTT Enforcement Tests
|
|
|
|
func testViaLoraEnforcesViaMqtt() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
|
|
// Start with both true
|
|
XCTAssertEqual(filters.viaLora, true)
|
|
XCTAssertEqual(filters.viaMqtt, true)
|
|
|
|
// Set viaLora to false
|
|
filters.viaLora = false
|
|
|
|
// viaMqtt should remain true
|
|
XCTAssertEqual(filters.viaLora, false)
|
|
XCTAssertEqual(filters.viaMqtt, true)
|
|
|
|
// Try to set viaMqtt to false - it should enforce viaLora to true
|
|
filters.viaMqtt = false
|
|
|
|
XCTAssertEqual(filters.viaLora, true)
|
|
XCTAssertEqual(filters.viaMqtt, false)
|
|
}
|
|
|
|
func testViaMqttEnforcesViaLora() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
|
|
// Start with both true
|
|
XCTAssertEqual(filters.viaLora, true)
|
|
XCTAssertEqual(filters.viaMqtt, true)
|
|
|
|
// Set viaMqtt to false
|
|
filters.viaMqtt = false
|
|
|
|
// viaLora should remain true
|
|
XCTAssertEqual(filters.viaLora, true)
|
|
XCTAssertEqual(filters.viaMqtt, false)
|
|
|
|
// Try to set viaLora to false - it should enforce viaMqtt to true
|
|
filters.viaLora = false
|
|
|
|
XCTAssertEqual(filters.viaLora, false)
|
|
XCTAssertEqual(filters.viaMqtt, true)
|
|
}
|
|
|
|
func testBothViaTrue() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
filters.viaLora = true
|
|
filters.viaMqtt = true
|
|
|
|
XCTAssertEqual(filters.viaLora, true)
|
|
XCTAssertEqual(filters.viaMqtt, true)
|
|
}
|
|
|
|
func testViaSettingsPersistence() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters1 = NodeFilterParameters()
|
|
filters1.viaLora = false
|
|
// viaMqtt should be enforced to true
|
|
|
|
let filters2 = NodeFilterParameters()
|
|
XCTAssertEqual(filters2.viaLora, false)
|
|
XCTAssertEqual(filters2.viaMqtt, true)
|
|
}
|
|
|
|
func testCannotHaveBothViaFalse() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
|
|
// Set viaLora to false first
|
|
filters.viaLora = false
|
|
XCTAssertEqual(filters.viaLora, false)
|
|
XCTAssertEqual(filters.viaMqtt, true)
|
|
|
|
// Try to set viaMqtt to false
|
|
filters.viaMqtt = false
|
|
|
|
// viaLora should be enforced back to true
|
|
XCTAssertEqual(filters.viaLora, true)
|
|
XCTAssertEqual(filters.viaMqtt, false)
|
|
}
|
|
|
|
// MARK: - ObservableObject Tests
|
|
|
|
func testObjectWillChange() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
var changeCount = 0
|
|
|
|
let cancellable = filters.objectWillChange.sink {
|
|
changeCount += 1
|
|
}
|
|
|
|
// Modify various properties
|
|
filters.searchText = "Test"
|
|
filters.isOnline = true
|
|
filters.deviceRoles.insert(1)
|
|
filters.viaLora = false
|
|
|
|
// Should have triggered changes
|
|
XCTAssertGreaterThan(changeCount, 0)
|
|
|
|
cancellable.cancel()
|
|
}
|
|
|
|
// MARK: - Edge Cases
|
|
|
|
func testLargeMaxDistance() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
filters.maxDistance = 10_000_000
|
|
|
|
let newFilters = NodeFilterParameters()
|
|
XCTAssertEqual(newFilters.maxDistance, 10_000_000)
|
|
}
|
|
|
|
func testNegativeHopsAway() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
filters.hopsAway = -1.0
|
|
|
|
let newFilters = NodeFilterParameters()
|
|
XCTAssertEqual(newFilters.hopsAway, -1.0)
|
|
}
|
|
|
|
func testZeroHopsAway() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
filters.hopsAway = 0.0
|
|
|
|
let newFilters = NodeFilterParameters()
|
|
XCTAssertEqual(newFilters.hopsAway, 0.0)
|
|
}
|
|
|
|
func testSpecialCharactersInSearchText() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
filters.searchText = "Test!@#$%^&*()_+-=[]{}|;':\",./<>?"
|
|
|
|
let newFilters = NodeFilterParameters()
|
|
XCTAssertEqual(newFilters.searchText, "Test!@#$%^&*()_+-=[]{}|;':\",./<>?")
|
|
}
|
|
|
|
func testUnicodeInSearchText() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
filters.searchText = "测试 Тест 🎉🚀"
|
|
|
|
let newFilters = NodeFilterParameters()
|
|
XCTAssertEqual(newFilters.searchText, "测试 Тест 🎉🚀")
|
|
}
|
|
|
|
func testLongSearchText() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
let longText = String(repeating: "A", count: 1000)
|
|
filters.searchText = longText
|
|
|
|
let newFilters = NodeFilterParameters()
|
|
XCTAssertEqual(newFilters.searchText, longText)
|
|
}
|
|
|
|
func testLargeDeviceRolesSet() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
filters.deviceRoles = Set(0...100)
|
|
|
|
let newFilters = NodeFilterParameters()
|
|
XCTAssertEqual(newFilters.deviceRoles, Set(0...100))
|
|
XCTAssertEqual(newFilters.deviceRoles.count, 101)
|
|
}
|
|
|
|
// MARK: - Reset/Clear Tests
|
|
|
|
func testResetAllFilters() async throws {
|
|
clearAllFilterDefaults()
|
|
|
|
let filters = NodeFilterParameters()
|
|
|
|
// Set all values to non-default
|
|
filters.searchText = "Test"
|
|
filters.isOnline = true
|
|
filters.isPkiEncrypted = true
|
|
filters.isFavorite = true
|
|
filters.isIgnored = true
|
|
filters.isEnvironment = true
|
|
filters.distanceFilter = true
|
|
filters.maxDistance = 500_000
|
|
filters.hopsAway = 5.0
|
|
filters.roleFilter = true
|
|
filters.deviceRoles = [1, 2, 3]
|
|
filters.viaLora = false
|
|
|
|
// Reset to defaults
|
|
filters.searchText = ""
|
|
filters.isOnline = false
|
|
filters.isPkiEncrypted = false
|
|
filters.isFavorite = false
|
|
filters.isIgnored = false
|
|
filters.isEnvironment = false
|
|
filters.distanceFilter = false
|
|
filters.maxDistance = 800_000
|
|
filters.hopsAway = -1.0
|
|
filters.roleFilter = false
|
|
filters.deviceRoles = []
|
|
filters.viaLora = true
|
|
filters.viaMqtt = true
|
|
|
|
// Verify all are back to defaults
|
|
let newFilters = NodeFilterParameters()
|
|
XCTAssertEqual(newFilters.searchText, "")
|
|
XCTAssertEqual(newFilters.isOnline, false)
|
|
XCTAssertEqual(newFilters.isPkiEncrypted, false)
|
|
XCTAssertEqual(newFilters.isFavorite, false)
|
|
XCTAssertEqual(newFilters.isIgnored, false)
|
|
XCTAssertEqual(newFilters.isEnvironment, false)
|
|
XCTAssertEqual(newFilters.distanceFilter, false)
|
|
XCTAssertEqual(newFilters.maxDistance, 800_000)
|
|
XCTAssertEqual(newFilters.hopsAway, -1.0)
|
|
XCTAssertEqual(newFilters.roleFilter, false)
|
|
XCTAssertTrue(newFilters.deviceRoles.isEmpty)
|
|
XCTAssertEqual(newFilters.viaLora, true)
|
|
XCTAssertEqual(newFilters.viaMqtt, true)
|
|
}
|
|
|
|
// MARK: - Helper Functions
|
|
|
|
/// Clears all NodeFilter-related UserDefaults to ensure clean test state
|
|
private func clearAllFilterDefaults() {
|
|
let defaults = UserDefaults.standard
|
|
defaults.removeObject(forKey: "nodeFilter.searchText")
|
|
defaults.removeObject(forKey: "nodeFilter.isOnline")
|
|
defaults.removeObject(forKey: "nodeFilter.isPkiEncrypted")
|
|
defaults.removeObject(forKey: "nodeFilter.isFavorite")
|
|
defaults.removeObject(forKey: "nodeFilter.isIgnored")
|
|
defaults.removeObject(forKey: "nodeFilter.isEnvironment")
|
|
defaults.removeObject(forKey: "nodeFilter.distanceFilter")
|
|
defaults.removeObject(forKey: "nodeFilter.maxDistance")
|
|
defaults.removeObject(forKey: "nodeFilter.hopsAway")
|
|
defaults.removeObject(forKey: "nodeFilter.roleFilter")
|
|
defaults.removeObject(forKey: "nodeFilter.deviceRoles")
|
|
defaults.removeObject(forKey: "nodeFilter.viaLora")
|
|
defaults.removeObject(forKey: "nodeFilter.viaMqtt")
|
|
}
|
|
}
|