| """Tests for the blocklist module."""
|
| from unittest.mock import Mock, patch
|
|
|
| import pytest
|
| from urllib3.exceptions import HTTPError
|
|
|
| from lib.blocklist import BlocklistData, _parse_block_list_from_url, OnlineBlocklist
|
|
|
|
|
| def test_parse_block_list_from_url_success() -> None:
|
| """Test parsing blocklist from URL with successful response."""
|
| mock_response = Mock()
|
| mock_response.status_code = 200
|
| mock_response.text = "user1\nuser2\nuser3"
|
| mock_response.headers = {"ETag": "test-etag-123"}
|
| mock_response.raise_for_status = Mock()
|
|
|
| old_data = BlocklistData([], None)
|
|
|
| with patch("lib.blocklist.requests.get", return_value=mock_response):
|
| result = _parse_block_list_from_url("http://example.com/blocklist", old_data)
|
|
|
| assert result.users == ["user1", "user2", "user3"]
|
| assert result.etag == "test-etag-123"
|
|
|
|
|
| def test_parse_block_list_from_url__several_line_breaks() -> None:
|
| """Test parsing blocklist from URL with several line-breaks."""
|
| mock_response = Mock()
|
| mock_response.status_code = 200
|
| mock_response.headers = {"ETag": "test-etag-123"}
|
| mock_response.raise_for_status = Mock()
|
|
|
| old_data = BlocklistData([], None)
|
|
|
| texts = [
|
| "user1\nuser2\nuser3",
|
| "user1\r\nuser2\r\n\r\nuser3",
|
| "user1\ruser2\r\ruser3",
|
| "user1\nuser2\nuser3",
|
| ]
|
| for text in texts:
|
| mock_response.text = text
|
|
|
| with patch("lib.blocklist.requests.get", return_value=mock_response):
|
| result = _parse_block_list_from_url("http://example.com/blocklist", old_data)
|
|
|
| assert result.users == ["user1", "user2", "user3"]
|
| assert result.etag == "test-etag-123"
|
|
|
|
|
| def test_parse_block_list_from_url_not_modified() -> None:
|
| """Test parsing blocklist returns cached data when not modified (304)."""
|
| mock_response = Mock()
|
| mock_response.status_code = 304
|
| mock_response.raise_for_status = Mock()
|
|
|
| old_data = BlocklistData(["cached_user1", "cached_user2"], "old-etag")
|
|
|
| with patch("lib.blocklist.requests.get", return_value=mock_response):
|
| result = _parse_block_list_from_url("http://example.com/blocklist", old_data)
|
|
|
| assert result.users == ["cached_user1", "cached_user2"]
|
| assert result.etag == "old-etag"
|
|
|
|
|
| def test_parse_block_list_from_url_with_etag_header() -> None:
|
| """Test that ETag is sent in request headers when available."""
|
| mock_response = Mock()
|
| mock_response.status_code = 200
|
| mock_response.text = "user1"
|
| mock_response.headers = {}
|
| mock_response.raise_for_status = Mock()
|
|
|
| old_data = BlocklistData([], "existing-etag")
|
|
|
| with patch("lib.blocklist.requests.get", return_value=mock_response) as mock_get:
|
| _parse_block_list_from_url("http://example.com/blocklist", old_data)
|
| mock_get.assert_called_once_with(
|
| "http://example.com/blocklist",
|
| headers={"If-None-Match": "existing-etag"},
|
| timeout=15,
|
| )
|
|
|
|
|
| def test_parse_block_list_from_url_without_etag_header() -> None:
|
| """Test that no ETag header is sent when old_data has no ETag."""
|
| mock_response = Mock()
|
| mock_response.status_code = 200
|
| mock_response.text = "user1"
|
| mock_response.headers = {}
|
| mock_response.raise_for_status = Mock()
|
|
|
| old_data = BlocklistData([], None)
|
|
|
| with patch("lib.blocklist.requests.get", return_value=mock_response) as mock_get:
|
| _parse_block_list_from_url("http://example.com/blocklist", old_data)
|
| mock_get.assert_called_once_with(
|
| "http://example.com/blocklist",
|
| headers={},
|
| timeout=15
|
| )
|
|
|
|
|
| def test_parse_block_list_from_url_strips_whitespace() -> None:
|
| """Test that usernames are stripped of leading/trailing whitespace."""
|
| mock_response = Mock()
|
| mock_response.status_code = 200
|
| mock_response.text = " user1 \n user2\t\nuser3 "
|
| mock_response.headers = {}
|
| mock_response.raise_for_status = Mock()
|
|
|
| old_data = BlocklistData([], None)
|
|
|
| with patch("lib.blocklist.requests.get", return_value=mock_response):
|
| result = _parse_block_list_from_url("http://example.com/blocklist", old_data)
|
|
|
| assert result.users == ["user1", "user2", "user3"]
|
|
|
|
|
| def test_parse_block_list_from_url_empty_response() -> None:
|
| """Test parsing blocklist with empty response."""
|
| mock_response = Mock()
|
| mock_response.status_code = 200
|
| mock_response.text = ""
|
| mock_response.headers = {}
|
| mock_response.raise_for_status = Mock()
|
|
|
| old_data = BlocklistData([], None)
|
|
|
| with patch("lib.blocklist.requests.get", return_value=mock_response):
|
| result = _parse_block_list_from_url("http://example.com/blocklist", old_data)
|
|
|
| assert result.users == []
|
|
|
|
|
| def test_parse_block_list_from_url_no_etag_in_response() -> None:
|
| """Test parsing blocklist when response has no ETag header."""
|
| mock_response = Mock()
|
| mock_response.status_code = 200
|
| mock_response.text = "user1\nuser2"
|
| mock_response.headers = {}
|
| mock_response.raise_for_status = Mock()
|
|
|
| old_data = BlocklistData([], None)
|
|
|
| with patch("lib.blocklist.requests.get", return_value=mock_response):
|
| result = _parse_block_list_from_url("http://example.com/blocklist", old_data)
|
|
|
| assert result.users == ["user1", "user2"]
|
| assert result.etag is None
|
|
|
|
|
| def test_parse_block_list_from_url_multiline_with_blanks() -> None:
|
| """Test parsing blocklist that contains blank lines."""
|
| mock_response = Mock()
|
| mock_response.status_code = 200
|
| mock_response.text = "user1\n\nuser2\n\n\nuser3"
|
| mock_response.headers = {}
|
| mock_response.raise_for_status = Mock()
|
|
|
| old_data = BlocklistData([], None)
|
|
|
| with patch("lib.blocklist.requests.get", return_value=mock_response):
|
| result = _parse_block_list_from_url("http://example.com/blocklist", old_data)
|
|
|
| assert result.users == ["user1", "user2", "user3"]
|
|
|
|
|
| def test_parse_block_list_from_url__exception() -> None:
|
| """Test parsing blocklist from URL with exception."""
|
| mock_response = Mock()
|
| mock_response.status_code = 500
|
| mock_response.raise_for_status.side_effect = HTTPError("500 Internal Server Error")
|
|
|
| old_data = BlocklistData([], None)
|
|
|
| with patch("lib.blocklist.requests.get", return_value=mock_response), pytest.raises(HTTPError):
|
| _parse_block_list_from_url("http://example.com/blocklist", old_data)
|
|
|
|
|
| def test_online_blocklist_refresh_success() -> None:
|
| """Test successful refresh of all blocklists."""
|
| mock_response1 = Mock()
|
| mock_response1.status_code = 200
|
| mock_response1.text = "user1\nuser2"
|
| mock_response1.headers = {"ETag": "etag1"}
|
| mock_response1.raise_for_status = Mock()
|
|
|
| mock_response2 = Mock()
|
| mock_response2.status_code = 200
|
| mock_response2.text = "user3\nuser4\nuser5"
|
| mock_response2.headers = {"ETag": "etag2"}
|
| mock_response2.raise_for_status = Mock()
|
|
|
| with patch("lib.blocklist.requests.get") as mock_get:
|
| mock_get.side_effect = [mock_response1, mock_response2]
|
| blocklist = OnlineBlocklist(["http://example.com/list1", "http://example.com/list2"])
|
|
|
| assert blocklist.blocklist["http://example.com/list1"].users == ["user1", "user2"]
|
| assert blocklist.blocklist["http://example.com/list1"].etag == "etag1"
|
| assert blocklist.blocklist["http://example.com/list2"].users == ["user3", "user4", "user5"]
|
| assert blocklist.blocklist["http://example.com/list2"].etag == "etag2"
|
|
|
|
|
| def test_online_blocklist_refresh_partial_failure() -> None:
|
| """Test refresh when some blocklists fail to load."""
|
| mock_response_success = Mock()
|
| mock_response_success.status_code = 200
|
| mock_response_success.text = "user1\nuser2"
|
| mock_response_success.headers = {"ETag": "etag1"}
|
| mock_response_success.raise_for_status = Mock()
|
|
|
| mock_response_fail = Mock()
|
| mock_response_fail.raise_for_status.side_effect = HTTPError("500 Server Error")
|
|
|
| with patch("lib.blocklist.requests.get") as mock_get:
|
|
|
| mock_get.side_effect = [mock_response_success, mock_response_fail]
|
| blocklist = OnlineBlocklist(["http://example.com/list1", "http://example.com/list2"])
|
|
|
|
|
| assert blocklist.blocklist["http://example.com/list1"].users == ["user1", "user2"]
|
|
|
|
|
| assert blocklist.blocklist["http://example.com/list2"].users == []
|
| assert blocklist.blocklist["http://example.com/list2"].etag is None
|
|
|