tests: centralize script path setup in conftest.py
Add a pytest-discovered tests/conftest.py for the last30days scripts path and remove duplicate per-file sys.path.insert boilerplate from tests. Normalize affected imports to rely on the shared scripts path and remove the now-unneeded E402 suppressions.
This commit is contained in:
+17
-17
@@ -1,20 +1,16 @@
|
||||
"""Tests for hackernews.py - HN search via Algolia API."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "skills" / "last30days" / "scripts"))
|
||||
|
||||
from lib import hackernews
|
||||
|
||||
|
||||
# === Helper Functions ===
|
||||
|
||||
|
||||
def create_mock_hit(
|
||||
object_id="12345",
|
||||
title="Test HN Story",
|
||||
@@ -40,9 +36,9 @@ def create_mock_hit(
|
||||
"url": url,
|
||||
}
|
||||
|
||||
|
||||
# === Tests for _date_to_unix() ===
|
||||
|
||||
|
||||
def test_date_to_unix_basic():
|
||||
"""Test converting YYYY-MM-DD to Unix timestamp."""
|
||||
result = hackernews._date_to_unix("2026-01-01")
|
||||
@@ -59,9 +55,9 @@ def test_date_to_unix_leap_day():
|
||||
expected = datetime(2024, 2, 29, tzinfo=timezone.utc).timestamp()
|
||||
assert result == int(expected)
|
||||
|
||||
|
||||
# === Tests for _unix_to_date() ===
|
||||
|
||||
|
||||
def test_unix_to_date_basic():
|
||||
"""Test converting Unix timestamp to YYYY-MM-DD."""
|
||||
ts = int(datetime(2026, 1, 15, tzinfo=timezone.utc).timestamp())
|
||||
@@ -77,9 +73,9 @@ def test_unix_to_date_with_time():
|
||||
|
||||
assert result == "2026-01-15"
|
||||
|
||||
|
||||
# === Tests for _strip_html() ===
|
||||
|
||||
|
||||
def test_strip_html_basic():
|
||||
"""Test HTML stripping and entity decoding."""
|
||||
html_text = "<p>Hello & goodbye</p>"
|
||||
@@ -113,9 +109,9 @@ def test_strip_html_entities():
|
||||
# Entities are decoded
|
||||
assert "&" in result or "test" in result
|
||||
|
||||
|
||||
# === Tests for _title_matches_query() ===
|
||||
|
||||
|
||||
def test_title_matches_query_basic():
|
||||
"""Test basic query matching."""
|
||||
title = "New AI framework for developers"
|
||||
@@ -199,10 +195,11 @@ def test_title_matches_query_flattens_hyphens_and_commas():
|
||||
# query 'rust, go, zig' flattens; title contains 'go'
|
||||
assert hackernews._title_matches_query("Go 1.24 generics update", "rust, go, zig") is True
|
||||
|
||||
|
||||
# === Tests for search_hackernews() ===
|
||||
|
||||
@patch('lib.hackernews.http.request')
|
||||
|
||||
|
||||
def test_search_hackernews_basic(mock_request):
|
||||
"""Test basic HN search."""
|
||||
mock_request.return_value = {
|
||||
@@ -221,8 +218,9 @@ def test_search_hackernews_basic(mock_request):
|
||||
assert len(result["hits"]) == 1
|
||||
assert mock_request.called
|
||||
|
||||
|
||||
@patch('lib.hackernews.http.request')
|
||||
|
||||
|
||||
def test_search_hackernews_depth_config(mock_request):
|
||||
"""Test that depth parameter controls hit count."""
|
||||
mock_request.return_value = {"hits": [], "nbHits": 0}
|
||||
@@ -235,8 +233,9 @@ def test_search_hackernews_depth_config(mock_request):
|
||||
|
||||
assert "hitsPerPage=15" in url
|
||||
|
||||
|
||||
@patch('lib.hackernews.http.request')
|
||||
|
||||
|
||||
def test_search_hackernews_date_filtering(mock_request):
|
||||
"""Test that date range is applied correctly."""
|
||||
mock_request.return_value = {"hits": [], "nbHits": 0}
|
||||
@@ -250,8 +249,9 @@ def test_search_hackernews_date_filtering(mock_request):
|
||||
assert "numericFilters" in url
|
||||
assert "created_at_i" in url
|
||||
|
||||
|
||||
@patch('lib.hackernews.http.request')
|
||||
|
||||
|
||||
def test_search_hackernews_http_error_handling(mock_request):
|
||||
"""Test graceful handling of HTTP errors."""
|
||||
from lib.http import HTTPError
|
||||
@@ -263,8 +263,9 @@ def test_search_hackernews_http_error_handling(mock_request):
|
||||
assert result["hits"] == []
|
||||
assert "error" in result
|
||||
|
||||
|
||||
@patch('lib.hackernews.http.request')
|
||||
|
||||
|
||||
def test_search_hackernews_engagement_filter(mock_request):
|
||||
"""Test that low-engagement stories are filtered."""
|
||||
mock_request.return_value = {"hits": [], "nbHits": 0}
|
||||
@@ -277,9 +278,9 @@ def test_search_hackernews_engagement_filter(mock_request):
|
||||
# Should filter for points > 2 (URL-encoded)
|
||||
assert "points" in url and "%3E2" in url
|
||||
|
||||
|
||||
# === Tests for parse_hackernews_response() ===
|
||||
|
||||
|
||||
def test_parse_hackernews_response_basic():
|
||||
"""Test parsing basic Algolia response."""
|
||||
response = {
|
||||
@@ -402,9 +403,9 @@ def test_parse_hackernews_response_empty_response():
|
||||
|
||||
assert items == []
|
||||
|
||||
|
||||
# === Tests for engagement scoring ===
|
||||
|
||||
|
||||
def test_engagement_score_calculation():
|
||||
"""Test that engagement dict contains points and comments."""
|
||||
response = {
|
||||
@@ -435,6 +436,5 @@ def test_engagement_score_zero_values():
|
||||
assert engagement["points"] == 0
|
||||
assert engagement["comments"] == 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
|
||||
Reference in New Issue
Block a user