77//
88
99import MullvadMockData
10+ import MullvadREST
1011import MullvadTypes
1112import XCTest
1213
@@ -30,6 +31,15 @@ class AllLocationsDataSourceTests: XCTestCase {
3031 XCTAssertNotNil ( rootNode. descendantNodeFor ( codes: [ " se2-wireguard " ] ) )
3132 }
3233
34+ func testAddAutomaticLocation( ) throws {
35+ let automaticNode = dataSource. nodes. compactMap { $0. asAutomaticLocationNode } . first!
36+
37+ XCTAssertTrue ( automaticNode. name == " Automatic " )
38+ XCTAssertTrue ( automaticNode. code == " automatic " )
39+ XCTAssertTrue ( automaticNode. locations. isEmpty)
40+ XCTAssertTrue ( automaticNode. locationInfo == nil )
41+ }
42+
3343 func testSearchCity( ) throws {
3444 dataSource. search ( by: " got " )
3545 let rootNode = RootLocationNode ( children: dataSource. nodes)
@@ -85,34 +95,113 @@ class AllLocationsDataSourceTests: XCTestCase {
8595 func testNodeByLocation( ) throws {
8696 let rootNode = RootLocationNode ( children: dataSource. nodes)
8797
88- var nodeByLocation = dataSource. node ( by: . init( locations: [ . country( " es " ) ] ) )
98+ var nodeByLocation = dataSource. node ( by: . only ( . init( locations: [ . country( " es " ) ] ) ) )
8999 var nodeByCode = rootNode. descendantNodeFor ( codes: [ " es " ] )
90100 XCTAssertEqual ( nodeByLocation, nodeByCode)
91101
92- nodeByLocation = dataSource. node ( by: . init( locations: [ . city( " es " , " mad " ) ] ) )
102+ nodeByLocation = dataSource. node ( by: . only ( . init( locations: [ . city( " es " , " mad " ) ] ) ) )
93103 nodeByCode = rootNode. descendantNodeFor ( codes: [ " es " , " mad " ] )
94104 XCTAssertEqual ( nodeByLocation, nodeByCode)
95105
96- nodeByLocation = dataSource. node ( by: . init( locations: [ . hostname( " es " , " mad " , " es1-wireguard " ) ] ) )
106+ nodeByLocation = dataSource. node ( by: . only ( . init( locations: [ . hostname( " es " , " mad " , " es1-wireguard " ) ] ) ) )
97107 nodeByCode = rootNode. descendantNodeFor ( codes: [ " es1-wireguard " ] )
98108 XCTAssertEqual ( nodeByLocation, nodeByCode)
99109 }
100110
101- func testConnectedNode ( ) throws {
111+ func testConnectedNodeWithValidHostname ( ) throws {
102112 let hostname = " es1-wireguard "
103- dataSource. setConnectedRelay ( hostname: hostname)
113+ let constraint = RelayConstraint< UserSelectedRelays> . only( . init( locations: [ . hostname( " es " , " mad " , hostname) ] ) )
114+ let selectedRelay = SelectedRelay (
115+ endpoint: . init(
116+ socketAddress: . ipv4( . init( ip: . loopback, port: 0 ) ) ,
117+ ipv4Gateway: . loopback,
118+ ipv6Gateway: . loopback,
119+ publicKey: Data ( ) ,
120+ obfuscation: . off
121+ ) ,
122+ hostname: hostname,
123+ location: . init(
124+ country: " " ,
125+ countryCode: " " ,
126+ city: " " ,
127+ cityCode: " " ,
128+ latitude: 0 ,
129+ longitude: 0
130+ ) ,
131+ features: nil
132+ )
133+
134+ dataSource. setConnectedRelay ( relayConstraint: constraint, selectedRelay: selectedRelay)
104135 dataSource. nodes. forEachNode { node in
105136 XCTAssertEqual ( node. isConnected, node. name == hostname)
106137 }
138+ }
107139
108- dataSource. setConnectedRelay ( hostname: " invalid-hostname " )
140+ func testConnectedNodeWithInvalidHostname( ) throws {
141+ let constraint = RelayConstraint< UserSelectedRelays> . only(
142+ . init( locations: [ . hostname( " es " , " mad " , " es1-wireguard " ) ] ) )
143+ let selectedRelay = SelectedRelay (
144+ endpoint: . init(
145+ socketAddress: . ipv4( . init( ip: . loopback, port: 0 ) ) ,
146+ ipv4Gateway: . loopback,
147+ ipv6Gateway: . loopback,
148+ publicKey: Data ( ) ,
149+ obfuscation: . off
150+ ) ,
151+ hostname: " invalid-hostname " ,
152+ location: . init(
153+ country: " " ,
154+ countryCode: " " ,
155+ city: " " ,
156+ cityCode: " " ,
157+ latitude: 0 ,
158+ longitude: 0
159+ ) ,
160+ features: nil
161+ )
162+
163+ dataSource. setConnectedRelay ( relayConstraint: constraint, selectedRelay: selectedRelay)
109164 dataSource. nodes. forEachNode { node in
110165 XCTAssertFalse ( node. isConnected)
111166 }
112167 }
113168
169+ func testConnectedNodeWithAutomaticLocation( ) throws {
170+ let constraint = RelayConstraint< UserSelectedRelays> . any
171+ let selectedRelay = SelectedRelay (
172+ endpoint: . init(
173+ socketAddress: . ipv4( . init( ip: . loopback, port: 0 ) ) ,
174+ ipv4Gateway: . loopback,
175+ ipv6Gateway: . loopback,
176+ publicKey: Data ( ) ,
177+ obfuscation: . off
178+ ) ,
179+ hostname: " " ,
180+ location: . init(
181+ country: " Sweden " ,
182+ countryCode: " " ,
183+ city: " Gothenburg " ,
184+ cityCode: " " ,
185+ latitude: 0 ,
186+ longitude: 0
187+ ) ,
188+ features: nil
189+ )
190+
191+ dataSource. setConnectedRelay ( relayConstraint: constraint, selectedRelay: selectedRelay)
192+
193+ let connectedNodes = dataSource. nodes. filter { node in
194+ node. isConnected
195+ }
196+ XCTAssert ( connectedNodes. count == 1 )
197+
198+ let connectedNode = try XCTUnwrap ( connectedNodes. first? . asAutomaticLocationNode)
199+ XCTAssertTrue ( connectedNode. isConnected)
200+ XCTAssertEqual ( connectedNode. locationInfo, [ " Sweden " , " Gothenburg " ] )
201+ }
202+
114203 func testSetSelectedLocation( ) throws {
115- dataSource. setSelectedNode ( selectedRelays : . init( locations: [ . country( " es " ) ] ) )
204+ dataSource. setSelectedNode ( constraint : . only ( . init( locations: [ . country( " es " ) ] ) ) )
116205
117206 dataSource. nodes. forEachNode { node in
118207 if node. locations == [ . country( " es " ) ] {
@@ -124,7 +213,7 @@ class AllLocationsDataSourceTests: XCTestCase {
124213
125214 dataSource
126215 . setSelectedNode (
127- selectedRelays : . init( locations: [ . country( " invalid " ) ] )
216+ constraint : . only ( . init( locations: [ . country( " invalid " ) ] ) )
128217 )
129218 dataSource. nodes. forEachNode { node in
130219 XCTAssertFalse ( node. isSelected)
@@ -133,16 +222,16 @@ class AllLocationsDataSourceTests: XCTestCase {
133222
134223 func testExcludeLocation( ) throws {
135224 let excludedRelays = UserSelectedRelays ( locations: [ . hostname( " se " , " sto " , " se2-wireguard " ) ] )
136- dataSource. setExcludedNode ( excludedSelection : excludedRelays)
137- let excludedNode = dataSource. node ( by: excludedRelays) !
225+ dataSource. setExcludedNode ( constraint : . only ( excludedRelays) )
226+ let excludedNode = dataSource. node ( by: . only ( excludedRelays) ) !
138227
139228 XCTAssertTrue ( excludedNode. isExcluded)
140229
141230 excludedNode. forEachAncestor { ancestor in
142231 XCTAssertFalse ( ancestor. isExcluded)
143232 }
144233
145- let includedNode = dataSource. node ( by: . init( locations: [ . country( " es " ) ] ) ) !
234+ let includedNode = dataSource. node ( by: . only ( . init( locations: [ . country( " es " ) ] ) ) ) !
146235 XCTAssertFalse ( includedNode. isExcluded)
147236 includedNode. forEachDescendant { child in
148237 XCTAssertFalse ( child. isExcluded)
@@ -156,13 +245,13 @@ class AllLocationsDataSourceTests: XCTestCase {
156245 let entryRelays = UserSelectedRelays ( locations: [ . hostname( " jp " , " tyo " , " jp1-wireguard " ) ] )
157246
158247 // Simulate multihop: exclusion is applied, Japan is excluded.
159- dataSource. setExcludedNode ( excludedSelection : entryRelays)
160- let jpNode = dataSource. node ( by: entryRelays) !
248+ dataSource. setExcludedNode ( constraint : . only ( entryRelays) )
249+ let jpNode = dataSource. node ( by: . only ( entryRelays) ) !
161250 XCTAssertTrue ( jpNode. isExcluded)
162251
163252 // Simulate switching to singlehop: the view model should pass nil
164253 // to clear exclusions rather than passing the entry relay.
165- dataSource. setExcludedNode ( excludedSelection : nil )
254+ dataSource. setExcludedNode ( constraint : nil )
166255
167256 // In singlehop, Japan should NOT be excluded
168257 XCTAssertFalse ( jpNode. isExcluded)
@@ -173,8 +262,8 @@ class AllLocationsDataSourceTests: XCTestCase {
173262
174263 func testExcludeLocationIncludesAncestors( ) throws {
175264 let excludedRelays = UserSelectedRelays ( locations: [ . hostname( " jp " , " tyo " , " jp1-wireguard " ) ] )
176- dataSource. setExcludedNode ( excludedSelection : excludedRelays)
177- let excludedNode = dataSource. node ( by: excludedRelays) !
265+ dataSource. setExcludedNode ( constraint : . only ( excludedRelays) )
266+ let excludedNode = dataSource. node ( by: . only ( excludedRelays) ) !
178267
179268 XCTAssertTrue ( excludedNode. isExcluded)
180269
@@ -183,7 +272,7 @@ class AllLocationsDataSourceTests: XCTestCase {
183272 XCTAssertTrue ( ancestor. isExcluded)
184273 }
185274
186- let includedNode = dataSource. node ( by: . init( locations: [ . country( " se " ) ] ) ) !
275+ let includedNode = dataSource. node ( by: . only ( . init( locations: [ . country( " se " ) ] ) ) ) !
187276 XCTAssertFalse ( includedNode. isExcluded)
188277 includedNode. forEachDescendant { child in
189278 XCTAssertFalse ( child. isExcluded)
@@ -198,5 +287,6 @@ extension AllLocationsDataSourceTests {
198287
199288 dataSource = AllLocationDataSource ( )
200289 dataSource. reload ( relays)
290+ dataSource. addAutomaticLocationNode ( )
201291 }
202292}
0 commit comments