import Foundation import XCTest @testable import QueueCubeShareCore final class SettingsTests: XCTestCase { func testExistingSettingsArePublishedOnReadWithoutChangingLegacyStorage() throws { try withDefaults { legacy, shared, legacyName, _ in let first = Server(baseURL: URL(string: "http://living-room.invalid:8080/api")!) let selected = Server(baseURL: URL(string: "https://bedroom.invalid/api")!) let original = Settings(selectedServer: selected, configuredServers: [first, selected]) original.save(defaults: legacy, sharedDefaults: nil) let legacyBefore = legacy.persistentDomain(forName: legacyName)! as NSDictionary XCTAssertFalse(Settings.fromSharedDefaults(defaults: shared).isConfigured) let loaded = Settings.fromDefaults(defaults: legacy, sharedDefaults: shared) let snapshot = Settings.fromSharedDefaults(defaults: shared) XCTAssertEqual(loaded.configuredServers, original.configuredServers) XCTAssertEqual(loaded.selectedServer, selected) XCTAssertEqual(snapshot.configuredServers, original.configuredServers) XCTAssertEqual(snapshot.selectedServer, selected) XCTAssertEqual(legacy.persistentDomain(forName: legacyName)! as NSDictionary, legacyBefore) } } func testSavePublishesOneSnapshotAndExtensionReadsLeaveItUnchanged() throws { try withDefaults { legacy, shared, _, sharedName in let first = Server(baseURL: URL(string: "http://living-room.invalid:8080/api")!) let second = Server(baseURL: URL(string: "http://bedroom.invalid:8080/api")!) let settings = Settings(selectedServer: first, configuredServers: [first, second]) settings.save(defaults: legacy, sharedDefaults: shared) settings.selectedServer(second).save(defaults: legacy, sharedDefaults: shared) let before = shared.persistentDomain(forName: sharedName)! XCTAssertEqual(before.count, 1, "Player list and selection must be published atomically") let snapshot = Settings.fromSharedDefaults(defaults: shared) XCTAssertEqual(snapshot.selectedServer, second) XCTAssertEqual(snapshot.configuredServers, [first, second]) XCTAssertEqual(shared.persistentDomain(forName: sharedName)! as NSDictionary, before as NSDictionary) } } func testMissingOrInvalidSharedSnapshotIsAnUnconfiguredState() throws { XCTAssertFalse(Settings.fromSharedDefaults(defaults: nil).isConfigured) try withDefaults { _, shared, _, _ in XCTAssertNil(Settings.fromSharedDefaults(defaults: shared).selectedServer) shared.set(Data("not a property list".utf8), forKey: "shareSettings.v1") let invalid = Settings.fromSharedDefaults(defaults: shared) XCTAssertTrue(invalid.configuredServers.isEmpty) XCTAssertNil(invalid.selectedServer) } } func testSelectionTracksFirstAddedAndRemovedConfiguredPlayers() { let first = Server(baseURL: URL(string: "http://living-room.invalid/api")!) let second = Server(baseURL: URL(string: "http://bedroom.invalid/api")!) var settings = Settings(selectedServer: nil, configuredServers: []) settings.configuredServers = [first, second] XCTAssertEqual(settings.selectedServer, first) settings.configuredServers = [second] XCTAssertEqual(settings.selectedServer, second) settings.configuredServers = [] XCTAssertNil(settings.selectedServer) } private func withDefaults(_ body: (UserDefaults, UserDefaults, String, String) throws -> Void) throws { let prefix = "QueueCubeShareTests.\(UUID().uuidString)" let legacyName = "\(prefix).legacy" let sharedName = "\(prefix).shared" let legacy = try XCTUnwrap(UserDefaults(suiteName: legacyName)) let shared = try XCTUnwrap(UserDefaults(suiteName: sharedName)) defer { legacy.removePersistentDomain(forName: legacyName) shared.removePersistentDomain(forName: sharedName) } try body(legacy, shared, legacyName, sharedName) } }