Spaces:
Runtime error
Runtime error
| """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: | |
| # First call succeeds, second call fails | |
| mock_get.side_effect = [mock_response_success, mock_response_fail] | |
| blocklist = OnlineBlocklist(["http://example.com/list1", "http://example.com/list2"]) | |
| # First blocklist should be loaded | |
| assert blocklist.blocklist["http://example.com/list1"].users == ["user1", "user2"] | |
| # Second blocklist should remain empty (failed to load) | |
| assert blocklist.blocklist["http://example.com/list2"].users == [] | |
| assert blocklist.blocklist["http://example.com/list2"].etag is None | |