fix: address codeql notes and warnings (#740)

This commit is contained in:
Jens
2025-12-20 18:17:51 +01:00
committed by GitHub
parent f0ebb26e5d
commit ba9b14b71b
9 changed files with 128 additions and 103 deletions

View File

@@ -1076,29 +1076,31 @@ class TestAdExtractorDownload:
page_mock.url = "https://www.kleinanzeigen.de/s-anzeige/test/12345"
extractor.page = page_mock
with patch.object(extractor, "web_text", new_callable = AsyncMock, side_effect = [
with (
patch.object(extractor, "web_text", new_callable = AsyncMock, side_effect = [
"Test Title", # Title extraction
"Test Title", # Second title call for full extraction
"Description text", # Description
"03.02.2025" # Creation date
]), \
patch.object(extractor, "web_execute", new_callable = AsyncMock, return_value = {
"universalAnalyticsOpts": {
"dimensions": {
"dimension92": "",
"dimension108": ""
}
]),
patch.object(extractor, "web_execute", new_callable = AsyncMock, return_value = {
"universalAnalyticsOpts": {
"dimensions": {
"dimension92": "",
"dimension108": ""
}
}), \
patch.object(extractor, "_extract_category_from_ad_page", new_callable = AsyncMock, return_value = "160"), \
patch.object(extractor, "_extract_special_attributes_from_ad_page", new_callable = AsyncMock, return_value = {}), \
patch.object(extractor, "_extract_pricing_info_from_ad_page", new_callable = AsyncMock, return_value = (None, "NOT_APPLICABLE")), \
patch.object(extractor, "_extract_shipping_info_from_ad_page", new_callable = AsyncMock, return_value = ("NOT_APPLICABLE", None, None)), \
patch.object(extractor, "_extract_sell_directly_from_ad_page", new_callable = AsyncMock, return_value = False), \
patch.object(extractor, "_download_images_from_ad_page", new_callable = AsyncMock, return_value = []), \
patch.object(extractor, "_extract_contact_from_ad_page", new_callable = AsyncMock, return_value = ContactPartial(
name = "Test", zipcode = "12345", location = "Berlin"
)):
}
}),
patch.object(extractor, "_extract_category_from_ad_page", new_callable = AsyncMock, return_value = "160"),
patch.object(extractor, "_extract_special_attributes_from_ad_page", new_callable = AsyncMock, return_value = {}),
patch.object(extractor, "_extract_pricing_info_from_ad_page", new_callable = AsyncMock, return_value = (None, "NOT_APPLICABLE")),
patch.object(extractor, "_extract_shipping_info_from_ad_page", new_callable = AsyncMock, return_value = ("NOT_APPLICABLE", None, None)),
patch.object(extractor, "_extract_sell_directly_from_ad_page", new_callable = AsyncMock, return_value = False),
patch.object(extractor, "_download_images_from_ad_page", new_callable = AsyncMock, return_value = []),
patch.object(extractor, "_extract_contact_from_ad_page", new_callable = AsyncMock, return_value = ContactPartial(
name = "Test", zipcode = "12345", location = "Berlin"
)),
):
ad_cfg, result_dir = await extractor._extract_ad_page_info_with_directory_handling(
base_dir, 12345
@@ -1133,29 +1135,31 @@ class TestAdExtractorDownload:
page_mock.url = "https://www.kleinanzeigen.de/s-anzeige/test/12345"
extractor.page = page_mock
with patch.object(extractor, "web_text", new_callable = AsyncMock, side_effect = [
with (
patch.object(extractor, "web_text", new_callable = AsyncMock, side_effect = [
"Test Title", # Title extraction
"Test Title", # Second title call for full extraction
"Description text", # Description
"03.02.2025" # Creation date
]), \
patch.object(extractor, "web_execute", new_callable = AsyncMock, return_value = {
"universalAnalyticsOpts": {
"dimensions": {
"dimension92": "",
"dimension108": ""
}
]),
patch.object(extractor, "web_execute", new_callable = AsyncMock, return_value = {
"universalAnalyticsOpts": {
"dimensions": {
"dimension92": "",
"dimension108": ""
}
}), \
patch.object(extractor, "_extract_category_from_ad_page", new_callable = AsyncMock, return_value = "160"), \
patch.object(extractor, "_extract_special_attributes_from_ad_page", new_callable = AsyncMock, return_value = {}), \
patch.object(extractor, "_extract_pricing_info_from_ad_page", new_callable = AsyncMock, return_value = (None, "NOT_APPLICABLE")), \
patch.object(extractor, "_extract_shipping_info_from_ad_page", new_callable = AsyncMock, return_value = ("NOT_APPLICABLE", None, None)), \
patch.object(extractor, "_extract_sell_directly_from_ad_page", new_callable = AsyncMock, return_value = False), \
patch.object(extractor, "_download_images_from_ad_page", new_callable = AsyncMock, return_value = []), \
patch.object(extractor, "_extract_contact_from_ad_page", new_callable = AsyncMock, return_value = ContactPartial(
name = "Test", zipcode = "12345", location = "Berlin"
)):
}
}),
patch.object(extractor, "_extract_category_from_ad_page", new_callable = AsyncMock, return_value = "160"),
patch.object(extractor, "_extract_special_attributes_from_ad_page", new_callable = AsyncMock, return_value = {}),
patch.object(extractor, "_extract_pricing_info_from_ad_page", new_callable = AsyncMock, return_value = (None, "NOT_APPLICABLE")),
patch.object(extractor, "_extract_shipping_info_from_ad_page", new_callable = AsyncMock, return_value = ("NOT_APPLICABLE", None, None)),
patch.object(extractor, "_extract_sell_directly_from_ad_page", new_callable = AsyncMock, return_value = False),
patch.object(extractor, "_download_images_from_ad_page", new_callable = AsyncMock, return_value = []),
patch.object(extractor, "_extract_contact_from_ad_page", new_callable = AsyncMock, return_value = ContactPartial(
name = "Test", zipcode = "12345", location = "Berlin"
)),
):
ad_cfg, result_dir = await extractor._extract_ad_page_info_with_directory_handling(
base_dir, 12345
@@ -1192,29 +1196,31 @@ class TestAdExtractorDownload:
page_mock.url = "https://www.kleinanzeigen.de/s-anzeige/test/12345"
extractor.page = page_mock
with patch.object(extractor, "web_text", new_callable = AsyncMock, side_effect = [
with (
patch.object(extractor, "web_text", new_callable = AsyncMock, side_effect = [
"Test Title", # Title extraction
"Test Title", # Second title call for full extraction
"Description text", # Description
"03.02.2025" # Creation date
]), \
patch.object(extractor, "web_execute", new_callable = AsyncMock, return_value = {
"universalAnalyticsOpts": {
"dimensions": {
"dimension92": "",
"dimension108": ""
}
]),
patch.object(extractor, "web_execute", new_callable = AsyncMock, return_value = {
"universalAnalyticsOpts": {
"dimensions": {
"dimension92": "",
"dimension108": ""
}
}), \
patch.object(extractor, "_extract_category_from_ad_page", new_callable = AsyncMock, return_value = "160"), \
patch.object(extractor, "_extract_special_attributes_from_ad_page", new_callable = AsyncMock, return_value = {}), \
patch.object(extractor, "_extract_pricing_info_from_ad_page", new_callable = AsyncMock, return_value = (None, "NOT_APPLICABLE")), \
patch.object(extractor, "_extract_shipping_info_from_ad_page", new_callable = AsyncMock, return_value = ("NOT_APPLICABLE", None, None)), \
patch.object(extractor, "_extract_sell_directly_from_ad_page", new_callable = AsyncMock, return_value = False), \
patch.object(extractor, "_download_images_from_ad_page", new_callable = AsyncMock, return_value = []), \
patch.object(extractor, "_extract_contact_from_ad_page", new_callable = AsyncMock, return_value = ContactPartial(
name = "Test", zipcode = "12345", location = "Berlin"
)):
}
}),
patch.object(extractor, "_extract_category_from_ad_page", new_callable = AsyncMock, return_value = "160"),
patch.object(extractor, "_extract_special_attributes_from_ad_page", new_callable = AsyncMock, return_value = {}),
patch.object(extractor, "_extract_pricing_info_from_ad_page", new_callable = AsyncMock, return_value = (None, "NOT_APPLICABLE")),
patch.object(extractor, "_extract_shipping_info_from_ad_page", new_callable = AsyncMock, return_value = ("NOT_APPLICABLE", None, None)),
patch.object(extractor, "_extract_sell_directly_from_ad_page", new_callable = AsyncMock, return_value = False),
patch.object(extractor, "_download_images_from_ad_page", new_callable = AsyncMock, return_value = []),
patch.object(extractor, "_extract_contact_from_ad_page", new_callable = AsyncMock, return_value = ContactPartial(
name = "Test", zipcode = "12345", location = "Berlin"
)),
):
ad_cfg, result_dir = await extractor._extract_ad_page_info_with_directory_handling(
base_dir, 12345
@@ -1246,29 +1252,31 @@ class TestAdExtractorDownload:
base_dir = tmp_path / "downloaded-ads"
base_dir.mkdir()
with patch.object(extractor, "web_text", new_callable = AsyncMock, side_effect = [
with (
patch.object(extractor, "web_text", new_callable = AsyncMock, side_effect = [
title_with_umlauts, # Title extraction
title_with_umlauts, # Second title call for full extraction
"Description text", # Description
"03.02.2025" # Creation date
]), \
patch.object(extractor, "web_execute", new_callable = AsyncMock, return_value = {
"universalAnalyticsOpts": {
"dimensions": {
"dimension92": "",
"dimension108": ""
}
]),
patch.object(extractor, "web_execute", new_callable = AsyncMock, return_value = {
"universalAnalyticsOpts": {
"dimensions": {
"dimension92": "",
"dimension108": ""
}
}), \
patch.object(extractor, "_extract_category_from_ad_page", new_callable = AsyncMock, return_value = "160"), \
patch.object(extractor, "_extract_special_attributes_from_ad_page", new_callable = AsyncMock, return_value = {}), \
patch.object(extractor, "_extract_pricing_info_from_ad_page", new_callable = AsyncMock, return_value = (None, "NOT_APPLICABLE")), \
patch.object(extractor, "_extract_shipping_info_from_ad_page", new_callable = AsyncMock, return_value = ("NOT_APPLICABLE", None, None)), \
patch.object(extractor, "_extract_sell_directly_from_ad_page", new_callable = AsyncMock, return_value = False), \
patch.object(extractor, "_download_images_from_ad_page", new_callable = AsyncMock, return_value = []), \
patch.object(extractor, "_extract_contact_from_ad_page", new_callable = AsyncMock, return_value = ContactPartial(
name = "Test", zipcode = "12345", location = "Berlin"
)):
}
}),
patch.object(extractor, "_extract_category_from_ad_page", new_callable = AsyncMock, return_value = "160"),
patch.object(extractor, "_extract_special_attributes_from_ad_page", new_callable = AsyncMock, return_value = {}),
patch.object(extractor, "_extract_pricing_info_from_ad_page", new_callable = AsyncMock, return_value = (None, "NOT_APPLICABLE")),
patch.object(extractor, "_extract_shipping_info_from_ad_page", new_callable = AsyncMock, return_value = ("NOT_APPLICABLE", None, None)),
patch.object(extractor, "_extract_sell_directly_from_ad_page", new_callable = AsyncMock, return_value = False),
patch.object(extractor, "_download_images_from_ad_page", new_callable = AsyncMock, return_value = []),
patch.object(extractor, "_extract_contact_from_ad_page", new_callable = AsyncMock, return_value = ContactPartial(
name = "Test", zipcode = "12345", location = "Berlin"
)),
):
ad_cfg, result_dir = await extractor._extract_ad_page_info_with_directory_handling(
base_dir, 12345
@@ -1285,7 +1293,10 @@ class TestAdExtractorDownload:
from kleinanzeigen_bot.utils import dicts # noqa: PLC0415
header_string = "# yaml-language-server: $schema=https://raw.githubusercontent.com/Second-Hand-Friends/kleinanzeigen-bot/refs/heads/main/schemas/ad.schema.json"
header_string = (
"# yaml-language-server: $schema="
"https://raw.githubusercontent.com/Second-Hand-Friends/kleinanzeigen-bot/refs/heads/main/schemas/ad.schema.json"
)
# save_dict normalizes path to NFC, matching the NFC directory name
dicts.save_dict(str(ad_file_path), ad_cfg.model_dump(), header = header_string)

View File

@@ -1582,6 +1582,14 @@ def test_file_logger_writes_message(tmp_path:Path, caplog:pytest.LogCaptureFixtu
assert "Logger test log message" in contents
def _apply_price_reduction_persistence(count:int | None) -> dict[str, Any]:
"""Return a dict with price_reduction_count only when count is positive (count -> dict[str, Any])."""
ad_cfg_orig:dict[str, Any] = {}
if count is not None and count > 0:
ad_cfg_orig["price_reduction_count"] = count
return ad_cfg_orig
class TestPriceReductionPersistence:
"""Tests for price_reduction_count persistence logic."""
@@ -1589,12 +1597,8 @@ class TestPriceReductionPersistence:
def test_persistence_logic_saves_when_count_positive(self) -> None:
"""Test the conditional logic that decides whether to persist price_reduction_count."""
# Simulate the logic from publish_ad lines 1076-1079
ad_cfg_orig:dict[str, Any] = {}
# Test case 1: price_reduction_count = 3 (should persist)
price_reduction_count = 3
if price_reduction_count is not None and price_reduction_count > 0:
ad_cfg_orig["price_reduction_count"] = price_reduction_count
ad_cfg_orig = _apply_price_reduction_persistence(3)
assert "price_reduction_count" in ad_cfg_orig
assert ad_cfg_orig["price_reduction_count"] == 3
@@ -1602,23 +1606,15 @@ class TestPriceReductionPersistence:
@pytest.mark.unit
def test_persistence_logic_skips_when_count_zero(self) -> None:
"""Test that price_reduction_count == 0 does not get persisted."""
ad_cfg_orig:dict[str, Any] = {}
# Test case 2: price_reduction_count = 0 (should NOT persist)
price_reduction_count = 0
if price_reduction_count is not None and price_reduction_count > 0:
ad_cfg_orig["price_reduction_count"] = price_reduction_count
ad_cfg_orig = _apply_price_reduction_persistence(0)
assert "price_reduction_count" not in ad_cfg_orig
@pytest.mark.unit
def test_persistence_logic_skips_when_count_none(self) -> None:
"""Test that price_reduction_count == None does not get persisted."""
ad_cfg_orig:dict[str, Any] = {}
# Test case 3: price_reduction_count = None (should NOT persist)
price_reduction_count = None
if price_reduction_count is not None and price_reduction_count > 0:
ad_cfg_orig["price_reduction_count"] = price_reduction_count
ad_cfg_orig = _apply_price_reduction_persistence(None)
assert "price_reduction_count" not in ad_cfg_orig

View File

@@ -18,7 +18,7 @@ from kleinanzeigen_bot.utils.pydantics import ContextualValidationError
@runtime_checkable
class _ApplyAutoPriceReduction(Protocol):
def __call__(self, ad_cfg:SimpleNamespace, ad_cfg_orig:dict[str, Any], ad_file_relative:str) -> None:
...
pass
@pytest.fixture

View File

@@ -36,7 +36,7 @@ class ConfigProtocol(Protocol):
user_data_dir:str | None
def add_extension(self, ext:str) -> None:
...
pass
def _nodriver_start_mock() -> Mock: