Files
learningcircuit--local-deep…/tests/news/test_storage_interaction_behavior.py
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

260 lines
8.8 KiB
Python

"""
Deep behavioral tests for InteractionType enum and StorageManager interaction logic.
Tests enum values, interaction recording patterns, and user stats calculation.
"""
from unittest.mock import Mock
import pytest
from local_deep_research.news.core.storage_manager import InteractionType
# --- InteractionType enum ---
class TestInteractionType:
"""Tests for InteractionType enum values."""
def test_view_value(self):
assert InteractionType.VIEW.value == "view"
def test_vote_up_value(self):
assert InteractionType.VOTE_UP.value == "vote_up"
def test_vote_down_value(self):
assert InteractionType.VOTE_DOWN.value == "vote_down"
def test_research_value(self):
assert InteractionType.RESEARCH.value == "research"
def test_share_value(self):
assert InteractionType.SHARE.value == "share"
def test_total_count(self):
assert len(InteractionType) == 5
def test_from_string_view(self):
assert InteractionType("view") == InteractionType.VIEW
def test_from_string_vote_up(self):
assert InteractionType("vote_up") == InteractionType.VOTE_UP
def test_invalid_value_raises(self):
with pytest.raises(ValueError):
InteractionType("invalid")
def test_enum_is_iterable(self):
values = [t.value for t in InteractionType]
assert "view" in values
assert "vote_up" in values
assert "vote_down" in values
assert "research" in values
assert "share" in values
def test_enum_name_matches(self):
assert InteractionType.VIEW.name == "VIEW"
assert InteractionType.VOTE_UP.name == "VOTE_UP"
def test_enum_comparison(self):
assert InteractionType.VIEW == InteractionType.VIEW
assert InteractionType.VIEW != InteractionType.VOTE_UP
def test_enum_identity(self):
assert InteractionType.VIEW is InteractionType.VIEW
def test_enum_hashing(self):
"""Enums should be hashable for use as dict keys."""
d = {InteractionType.VIEW: "viewed", InteractionType.VOTE_UP: "upvoted"}
assert d[InteractionType.VIEW] == "viewed"
# --- StorageManager interaction patterns ---
class TestStorageManagerInteractionPatterns:
"""Tests for how StorageManager handles different interaction types.
These test the interaction recording logic by verifying the card
mutation patterns for each interaction type.
"""
def _make_card_with_interaction(self):
"""Create a mock card with an interaction dict."""
card = Mock()
card.interaction = {
"views": 0,
"votes_up": 0,
"votes_down": 0,
}
return card
def test_view_increments_view_count(self):
card = self._make_card_with_interaction()
# Simulate VIEW interaction logic from StorageManager.record_interaction
card.interaction["viewed"] = True
card.interaction["views"] = card.interaction.get("views", 0) + 1
assert card.interaction["views"] == 1
assert card.interaction["viewed"] is True
def test_view_multiple_times(self):
card = self._make_card_with_interaction()
for _ in range(5):
card.interaction["views"] = card.interaction.get("views", 0) + 1
assert card.interaction["views"] == 5
def test_vote_up_sets_voted_up(self):
card = self._make_card_with_interaction()
card.interaction["voted"] = "up"
card.interaction["votes_up"] = card.interaction.get("votes_up", 0) + 1
assert card.interaction["voted"] == "up"
assert card.interaction["votes_up"] == 1
def test_vote_down_sets_voted_down(self):
card = self._make_card_with_interaction()
card.interaction["voted"] = "down"
card.interaction["votes_down"] = (
card.interaction.get("votes_down", 0) + 1
)
assert card.interaction["voted"] == "down"
assert card.interaction["votes_down"] == 1
def test_research_sets_researched_flag(self):
card = self._make_card_with_interaction()
card.interaction["researched"] = True
card.interaction["research_count"] = (
card.interaction.get("research_count", 0) + 1
)
assert card.interaction["researched"] is True
assert card.interaction["research_count"] == 1
def test_research_multiple_times(self):
card = self._make_card_with_interaction()
for _ in range(3):
card.interaction["researched"] = True
card.interaction["research_count"] = (
card.interaction.get("research_count", 0) + 1
)
assert card.interaction["research_count"] == 3
def test_metadata_stored_with_interaction(self):
card = self._make_card_with_interaction()
metadata = {"source": "button", "position": 3}
interaction_type = InteractionType.VIEW
card.interaction[f"{interaction_type}_metadata"] = metadata
assert card.interaction[f"{interaction_type}_metadata"] == metadata
# --- User stats calculation patterns ---
class TestUserStatsCalculation:
"""Tests for the stats calculation logic used in get_user_stats."""
def test_count_votes_up(self):
ratings = [
{"value": 1},
{"value": 1},
{"value": -1},
{"value": 1},
]
votes_up = sum(1 for r in ratings if r.get("value", 0) > 0)
assert votes_up == 3
def test_count_votes_down(self):
ratings = [
{"value": 1},
{"value": -1},
{"value": -1},
]
votes_down = sum(1 for r in ratings if r.get("value", 0) < 0)
assert votes_down == 2
def test_count_total_views(self):
user_cards = [
{"interaction": {"views": 10}},
{"interaction": {"views": 5}},
{"interaction": {}},
]
total_views = sum(
c.get("interaction", {}).get("views", 0) for c in user_cards
)
assert total_views == 15
def test_empty_ratings(self):
ratings = []
votes_up = sum(1 for r in ratings if r.get("value", 0) > 0)
votes_down = sum(1 for r in ratings if r.get("value", 0) < 0)
assert votes_up == 0
assert votes_down == 0
def test_missing_value_in_rating(self):
ratings = [{"other": "data"}]
votes_up = sum(1 for r in ratings if r.get("value", 0) > 0)
assert votes_up == 0
def test_zero_value_counts_neither(self):
ratings = [{"value": 0}]
votes_up = sum(1 for r in ratings if r.get("value", 0) > 0)
votes_down = sum(1 for r in ratings if r.get("value", 0) < 0)
assert votes_up == 0
assert votes_down == 0
# --- Card interaction conversion patterns ---
class TestCardInteractionConversion:
"""Tests for converting ratings to interaction format (get_card_interactions logic)."""
def test_positive_rating_is_upvote(self):
rating = {"user_id": "u1", "value": 1, "created_at": "2025-01-01"}
interaction = {
"user_id": rating.get("user_id"),
"interaction_type": "vote",
"interaction_data": {
"vote": "up" if rating.get("value", 0) > 0 else "down"
},
"timestamp": rating.get("created_at"),
}
assert interaction["interaction_data"]["vote"] == "up"
def test_negative_rating_is_downvote(self):
rating = {"user_id": "u1", "value": -1, "created_at": "2025-01-01"}
interaction = {
"user_id": rating.get("user_id"),
"interaction_type": "vote",
"interaction_data": {
"vote": "up" if rating.get("value", 0) > 0 else "down"
},
"timestamp": rating.get("created_at"),
}
assert interaction["interaction_data"]["vote"] == "down"
def test_zero_rating_is_downvote(self):
"""Zero value is not > 0, so it goes to 'down' branch."""
rating = {"value": 0}
vote = "up" if rating.get("value", 0) > 0 else "down"
assert vote == "down"
def test_multiple_ratings_to_interactions(self):
ratings = [
{"user_id": "u1", "value": 1, "created_at": "2025-01-01"},
{"user_id": "u2", "value": -1, "created_at": "2025-01-02"},
]
interactions = []
for rating in ratings:
interactions.append(
{
"user_id": rating.get("user_id"),
"interaction_type": "vote",
"interaction_data": {
"vote": "up" if rating.get("value", 0) > 0 else "down"
},
"timestamp": rating.get("created_at"),
}
)
assert len(interactions) == 2
assert interactions[0]["interaction_data"]["vote"] == "up"
assert interactions[1]["interaction_data"]["vote"] == "down"