GitLab is used only for code review, issue tracking and project management. Canonical locations for source code are still https://gitweb.torproject.org/ https://git.torproject.org/ and git-rw.torproject.org.

test_relaylist.py 5.23 KB
Newer Older
1 2 3 4 5 6 7 8 9
"""relaylist.py unit tests."""
from datetime import datetime, timedelta

# 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.
# freezegun is able to mock any datetime object, it also allows comparations.
from freezegun import freeze_time

juga  's avatar
juga committed
10
from sbws.lib.relaylist import RelayList, remove_old_consensus_timestamps
11
from sbws.util.state import State
12 13 14 15 16 17 18 19 20 21 22 23 24


def test_remove_old_consensus_timestamps():
    days_ago = datetime(2020, 3, 1)
    timestamps = [days_ago] + [
        days_ago + timedelta(days=x) for x in range(1, 5)
    ]
    with freeze_time(days_ago + timedelta(days=5, seconds=1)):
        new_timestamps = remove_old_consensus_timestamps(
            timestamps, 5 * 24 * 60 * 60
        )
    assert len(new_timestamps) == len(timestamps) - 1
    assert days_ago not in new_timestamps
juga  's avatar
juga committed
25 26 27 28 29 30 31 32 33 34


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 timesamps and relays is correct.
    """
35
    state = State(conf['paths']['state_fpath'])
juga  's avatar
juga committed
36 37 38 39 40
    # 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 freeze_time("2020-02-29 10:00:00"):
41 42
        relay_list = RelayList(args, conf, controller, state=state)
    assert relay_list.recent_consensus_count == 1
43
    assert len(relay_list._relays[0]._consensus_timestamps) == 1
juga  's avatar
juga committed
44 45 46 47 48 49 50 51 52
    # The actual number of relays in the consensus
    assert len(relay_list._relays) == 6433
    fps = {r.fingerprint for r in relay_list._relays}

    # One hour later there is a new consensus
    relay_list._controller = controller_1h_later
    with freeze_time("2020-02-29 11:00:00"):
        # Call relays update the list of relays.
        relay_list.relays
53
    assert relay_list.recent_consensus_count == 2
54
    assert len(relay_list._relays[0]._consensus_timestamps) == 2
juga  's avatar
juga committed
55 56 57 58 59 60 61 62 63 64 65
    # 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)

    # Five days later plus 1 second.
    # The first consensus timestamp will get removed.
    relay_list._controller = controller_5days_later
    with freeze_time("2020-03-05 10:00:01"):
        relay_list.relays
66
    assert relay_list.recent_consensus_count == 2
67
    assert len(relay_list._relays[0]._consensus_timestamps) == 2
juga  's avatar
juga committed
68 69 70 71 72 73 74 75 76
    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.
77
    assert 6925 == 6505 + len(added_fps) - len(removed_fps)
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


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'])
    # For this test it does not matter that the consensus timestamps or relays
    # are not correct.
    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 freeze_time("2020-02-29 10:00:00"):
        relay_list.increment_recent_measurement_attempt()
    assert 1 == relay_list.recent_measurement_attempt_count
    assert [datetime(2020, 2, 29, 10, 0)] == state[
        "recent_measurement_attempt"
    ]

    # And a second measurement attempt is made 4 days later.
    with freeze_time("2020-03-04 10:00:00"):
        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 freeze_time("2020-03-05 10:00:00"):
        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 freeze_time("2020-03-06 10:00:00"):
        relay_list.increment_recent_measurement_attempt()
    assert 3 == relay_list.recent_measurement_attempt_count
    assert 3 == len(state["recent_measurement_attempt"])