File: test_relaylist.py

package info (click to toggle)
sbws 2.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 16,084 kB
  • sloc: python: 10,432; sh: 146; makefile: 38
file content (195 lines) | stat: -rw-r--r-- 8,714 bytes parent folder | download | duplicates (2)
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
"""relaylist.py unit tests."""

from datetime import datetime

try:
    from datetime import UTC
except ImportError:
    from datetime import timezone

    UTC = timezone.utc

# When datetime is imported as a class (`from datetime import datetime`) it can
# not be mocked because it is a built-in type. It can only be mocked when
# imported as module.
# time_machine is able to mock any datetime object, it also allows comparisons.
import time_machine

from sbws.lib.relaylist import Relay, RelayList
from sbws.util.state import State


def test_init_relays(
    args, conf, controller, controller_1h_later, controller_5days_later
):
    """
    Test `init_relays` when creating the RelayList the first time and when a
    new consensus is received.
    Test that the number of consesus timestamps and relays is correct.
    Additionally, make sure the calculated min bw for the second hop for
    exit/non-exit relays is correct, too.
    """
    state = State(conf["paths"]["state_fpath"])
    # There is no need to mock datetime to update the consensus, since the
    # actual date will be always later.
    # But it's needed to have the correct list of timestamps both for RelayList
    # and Relay.
    with time_machine.travel("2020-02-29 10:00:00 +0000"):
        relay_list = RelayList(args, conf, controller, state=state)
    assert relay_list.recent_consensus_count == 1
    assert relay_list._relays[0].relay_in_recent_consensus_count == 1
    # The actual number of relays in the consensus
    assert len(relay_list._relays) == 6433
    fps = {r.fingerprint for r in relay_list._relays}
    # The calculated min bw for the second hop
    assert 2100000 == relay_list._exit_min_bw
    assert 220000 == relay_list._non_exit_min_bw

    # One hour later there is a new consensus
    relay_list._controller = controller_1h_later
    with time_machine.travel("2020-02-29 11:00:00 +0000"):
        # Call relays update the list of relays.
        relay_list.relays
    assert relay_list.recent_consensus_count == 2
    assert relay_list._relays[0].relay_in_recent_consensus_count == 2
    # Check that the number of relays is now the previous one plus the relays
    # that are in the new consensus that there were not in the previous one.
    fps_1h_later = {r.fingerprint for r in relay_list._relays}
    added_fps = fps_1h_later.difference(fps)
    assert 6505 == 6433 + len(added_fps)
    # The calculated min bw for the second hop
    assert 2120000 == relay_list._exit_min_bw
    assert 210000 == relay_list._non_exit_min_bw

    # Five days later plus 1 second.
    # The first consensus timestamp will get removed.
    relay_list._controller = controller_5days_later
    with time_machine.travel("2020-03-05 10:00:01 +0000"):
        relay_list.relays
    assert relay_list.recent_consensus_count == 2
    assert relay_list._relays[0].relay_in_recent_consensus_count == 2
    fps_5days_later = {r.fingerprint for r in relay_list._relays}
    # The number of added relays will be the number of relays in this
    # consensus that were not in the other 2 conensuses
    added_fps = fps_5days_later.difference(fps_1h_later)
    # The number of removed relays that are in this consensus, plus the added
    # ones that were not in the first consensus (because it has been removed).
    removed_fps = fps.difference(fps_5days_later)
    # The number of relays will be the number of relays in the cosensus plus
    # the added ones minus the removed ones.
    assert 6596 == 6505 + len(added_fps) - len(removed_fps)
    # The calculated min bw for the second hop
    assert 2800000 == relay_list._exit_min_bw
    assert 150000 == relay_list._non_exit_min_bw


def test_increment_recent_measurement_attempt(args, conf, controller):
    """Test that incrementing the measurement attempts does not go on forever

    And instead it only counts the number of attempts in the last days.
    It also tests that the state file is updated correctly.
    """
    state = State(conf["paths"]["state_fpath"])
    with time_machine.travel("2020-02-29 10:00:00 +0000"):
        relay_list = RelayList(args, conf, controller=controller, state=state)
    # The initial count is 0 and the state does not have that key.
    assert 0 == relay_list.recent_measurement_attempt_count
    assert not state.get("recent_measurement_attempt", None)

    # Pretend that a measurement attempt is made.
    with time_machine.travel("2020-02-29 10:00:00 +0000"):
        relay_list.increment_recent_measurement_attempt()
    assert 1 == relay_list.recent_measurement_attempt_count
    assert ["2020-02-29T10:00:00+00:00"] == state["recent_measurement_attempt"]

    # And a second measurement attempt is made 4 days later.
    with time_machine.travel("2020-03-04 10:00:00 +0000"):
        relay_list.increment_recent_measurement_attempt()
    assert 2 == relay_list.recent_measurement_attempt_count
    assert 2 == len(state["recent_measurement_attempt"])

    # And a third measurement attempt is made 5 days later.
    with time_machine.travel("2020-03-05 10:00:00 +0000"):
        relay_list.increment_recent_measurement_attempt()
    assert 3 == relay_list.recent_measurement_attempt_count
    assert 3 == len(state["recent_measurement_attempt"])

    # And a fourth measurement attempt is made 6 days later. The first one is
    # now removed and not counted.
    with time_machine.travel("2020-03-06 10:00:00 +0000"):
        relay_list.increment_recent_measurement_attempt()
    assert 3 == relay_list.recent_measurement_attempt_count
    assert 3 == len(state["recent_measurement_attempt"])


def test_increment_relay_recent_measurement_attempt(
    controller, router_status, server_descriptor
):
    """Test that incrementing the measurement attempts do not go on forever

    And instead it only counts the number of attempts in the last days.
    """
    # For this test it does not matter that the consensus timestamps
    # are not correct.
    now = datetime.now(UTC)
    relay = Relay("A" * 40, controller, timestamp=now)
    # The initial count is 0 and the state does not have that key.
    assert 0 == relay.relay_recent_measurement_attempt_count

    # Pretend that a measurement attempt is made.
    with time_machine.travel("2020-02-29 10:00:00 +0000"):
        relay.increment_relay_recent_measurement_attempt()
    assert 1 == relay.relay_recent_measurement_attempt_count

    # And a second measurement attempt is made 4 days later.
    with time_machine.travel("2020-03-04 10:00:00 +0000"):
        relay.increment_relay_recent_measurement_attempt()
    assert 2 == relay.relay_recent_measurement_attempt_count

    # And a third measurement attempt is made before 5 days later.
    with time_machine.travel("2020-03-05 09:00:00 +0000"):
        relay.increment_relay_recent_measurement_attempt()
    assert 3 == relay.relay_recent_measurement_attempt_count

    # And a fourth measurement attempt is made 6 days later. The first one is
    # now removed and not counted.
    with time_machine.travel("2020-03-06 10:00:00 +0000"):
        relay.increment_relay_recent_measurement_attempt()
    assert 3 == relay.relay_recent_measurement_attempt_count
    # XXX: Write to a Result and load it back


def test_increment_relay_recent_priority_list(
    controller, router_status, server_descriptor
):
    """Test that incrementing the priority lists do not go on forever

    And instead it only counts the number of priority lists in the last days.
    """
    # For this test it does not matter that the consensus timestamps
    # are not correct.
    now = datetime.now(UTC)
    relay = Relay("A" * 40, controller, timestamp=now)
    # The initial count is 0 and the state does not have that key.
    assert 0 == relay.relay_recent_priority_list_count

    # Pretend that a measurement attempt is made.
    with time_machine.travel("2020-02-29 10:00:00 +0000"):
        relay.increment_relay_recent_priority_list()
    assert 1 == relay.relay_recent_priority_list_count

    # And a second measurement attempt is made 4 days later.
    with time_machine.travel("2020-03-04 10:00:00 +0000"):
        relay.increment_relay_recent_priority_list()
    assert 2 == relay.relay_recent_priority_list_count

    # And a third measurement attempt is made 5 days later.
    with time_machine.travel("2020-03-05 10:00:00 +0000"):
        relay.increment_relay_recent_priority_list()
    assert 3 == relay.relay_recent_priority_list_count

    # And a fourth measurement attempt is made 6 days later. The first one is
    # now removed and not counted.
    with time_machine.travel("2020-03-06 10:00:00 +0000"):
        relay.increment_relay_recent_priority_list()
    assert 3 == relay.relay_recent_priority_list_count