summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadVPNTests/MullvadREST/ApiHandlers/AddressCacheTests.swift
blob: 778482f7b9b161e825ed2090e78ada583c5249bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//
//  AddressCacheTests.swift
//  MullvadRESTTests
//
//  Created by Marco Nikic on 2023-05-05.
//  Copyright © 2025 Mullvad VPN AB. All rights reserved.
//

import MullvadTypes
import Network
import XCTest

@testable import MullvadREST

final class AddressCacheTests: XCTestCase {
    let apiEndpoint: AnyIPEndpoint = .ipv4(IPv4Endpoint(ip: IPv4Address.loopback, port: 80))

    // MARK: - Tests

    func testAddressCacheHasDefaultEndpoint() {
        let addressCache = REST.AddressCache(
            canWriteToCache: false,
            fileCache: MockFileCache(initialState: .fileNotFound)
        )
        XCTAssertEqual(addressCache.getCurrentEndpoint(), REST.defaultAPIEndpoint)
    }

    func testSetEndpoints() throws {
        let addressCache = REST.AddressCache(
            canWriteToCache: false,
            fileCache: MockFileCache(initialState: .fileNotFound)
        )

        addressCache.setEndpoints([apiEndpoint])
        XCTAssertEqual(addressCache.getCurrentEndpoint(), apiEndpoint)
    }

    func testSetEndpointsUpdatesDateWhenSettingSameAddress() throws {
        let addressCache = REST.AddressCache(
            canWriteToCache: false,
            fileCache: MockFileCache(initialState: .fileNotFound)
        )
        addressCache.setEndpoints([apiEndpoint])

        let dateBeforeUpdate = addressCache.getLastUpdateDate()
        // Calling `Date()` several times in a row can result in the same Date object being returned.
        // Force a sleep before setting the next endpoint to avoid getting the same Date object twice in a row.
        Thread.sleep(forTimeInterval: Duration.milliseconds(10).timeInterval)
        addressCache.setEndpoints([apiEndpoint])
        let dateAfterUpdate = addressCache.getLastUpdateDate()

        let timeDifference = dateAfterUpdate.timeIntervalSince(dateBeforeUpdate)
        XCTAssertNotEqual(0.0, timeDifference)
    }

    func testSetEndpointsDoesNotDoAnythingIfSettingEmptyEndpoints() throws {
        let addressCache = REST.AddressCache(
            canWriteToCache: false,
            fileCache: MockFileCache(initialState: .fileNotFound)
        )
        addressCache.loadFromFile()

        let currentEndpoint = addressCache.getCurrentEndpoint()
        addressCache.setEndpoints([])

        XCTAssertEqual(addressCache.getCurrentEndpoint(), currentEndpoint)
    }

    func testSetEndpointsOnlyAcceptsTheFirstEndpoint() throws {
        let ipAddresses = (1...10)
            .map { "\($0).\($0).\($0).\($0):80" }
            .compactMap { AnyIPEndpoint(string: $0) }

        let firstIPEndpoint = try XCTUnwrap(ipAddresses.first)

        let fileCache = MockFileCache<REST.StoredAddressCache>()
        let addressCache = REST.AddressCache(canWriteToCache: true, fileCache: fileCache)
        addressCache.setEndpoints(ipAddresses)

        let fileState = fileCache.getState()
        guard case let .exists(storedAddressCache) = fileState else {
            XCTFail("State is expected to contain cached addresses.")
            return
        }

        XCTAssertEqual(storedAddressCache.endpoint, firstIPEndpoint)
        XCTAssertEqual(addressCache.getCurrentEndpoint(), firstIPEndpoint)
    }

    func testCacheReadsFromFile() throws {
        let fixedDate = Date()
        let addressCache = REST.AddressCache(
            canWriteToCache: true,
            fileCache: MockFileCache(
                initialState: .exists(
                    REST.StoredAddressCache(updatedAt: fixedDate, endpoint: apiEndpoint)
                ))
        )
        addressCache.loadFromFile()

        XCTAssertEqual(addressCache.getCurrentEndpoint(), apiEndpoint)
        XCTAssertEqual(addressCache.getLastUpdateDate(), fixedDate)
    }

    func testCacheWritesToDiskWhenSettingNewEndpoints() throws {
        let fileCache = MockFileCache<REST.StoredAddressCache>()
        let addressCache = REST.AddressCache(canWriteToCache: true, fileCache: fileCache)

        XCTAssertEqual(fileCache.getState(), .fileNotFound)
        addressCache.setEndpoints([apiEndpoint])

        let fileState = fileCache.getState()
        XCTAssertTrue(fileState.isExists)

        guard case let .exists(storedAddressCache) = fileState else {
            XCTFail("State is expected to contain cached addresses.")
            return
        }

        XCTAssertEqual(storedAddressCache.endpoint, addressCache.getCurrentEndpoint())
        XCTAssertEqual(storedAddressCache.updatedAt, addressCache.getLastUpdateDate())
    }

    func testGetCurrentEndpointReadsFromCacheWhenReadOnly() throws {
        let addressCache = REST.AddressCache(
            canWriteToCache: false,
            fileCache: MockFileCache(
                initialState: .exists(
                    REST.StoredAddressCache(updatedAt: Date(), endpoint: apiEndpoint)
                ))
        )
        XCTAssertEqual(addressCache.getCurrentEndpoint(), apiEndpoint)
    }
}