Files
learningcircuit--local-deep…/tests/news/test_storage_interfaces_deep_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

320 lines
9.4 KiB
Python

"""
Deep behavioral tests for storage interfaces and base storage patterns.
Tests BaseStorage, CardStorage, RatingStorage,
and PreferenceStorage interfaces.
"""
import uuid
import pytest
from local_deep_research.news.core.storage import (
BaseStorage,
CardStorage,
RatingStorage,
PreferenceStorage,
)
# --- Concrete implementations for testing ---
class ConcreteBaseStorage(BaseStorage):
"""Concrete implementation of BaseStorage for testing."""
def __init__(self):
self._data = {}
def create(self, data):
id = self.generate_id()
self._data[id] = data
return id
def get(self, id):
return self._data.get(id)
def update(self, id, data):
if id in self._data:
self._data[id].update(data)
return True
return False
def delete(self, id):
if id in self._data:
del self._data[id]
return True
return False
def list(self, filters=None, limit=100, offset=0):
items = list(self._data.values())
return items[offset : offset + limit]
# --- BaseStorage.generate_id ---
class TestBaseStorageGenerateId:
"""Tests for BaseStorage.generate_id."""
def test_returns_string(self):
storage = ConcreteBaseStorage()
result = storage.generate_id()
assert isinstance(result, str)
def test_is_valid_uuid(self):
storage = ConcreteBaseStorage()
result = storage.generate_id()
# Should not raise
uuid.UUID(result)
def test_unique_ids(self):
storage = ConcreteBaseStorage()
ids = {storage.generate_id() for _ in range(100)}
assert len(ids) == 100
def test_uuid4_format(self):
storage = ConcreteBaseStorage()
result = storage.generate_id()
parsed = uuid.UUID(result)
assert parsed.version == 4
def test_non_empty(self):
storage = ConcreteBaseStorage()
result = storage.generate_id()
assert len(result) > 0
def test_contains_hyphens(self):
storage = ConcreteBaseStorage()
result = storage.generate_id()
assert "-" in result
# --- Concrete BaseStorage CRUD ---
class TestConcreteStorageCrud:
"""Tests for the concrete storage implementation CRUD."""
def test_create_returns_id(self):
storage = ConcreteBaseStorage()
id = storage.create({"name": "test"})
assert isinstance(id, str)
def test_get_after_create(self):
storage = ConcreteBaseStorage()
id = storage.create({"name": "test"})
result = storage.get(id)
assert result["name"] == "test"
def test_get_nonexistent_returns_none(self):
storage = ConcreteBaseStorage()
assert storage.get("nonexistent") is None
def test_update_returns_true(self):
storage = ConcreteBaseStorage()
id = storage.create({"name": "test"})
assert storage.update(id, {"name": "updated"}) is True
def test_update_nonexistent_returns_false(self):
storage = ConcreteBaseStorage()
assert storage.update("nonexistent", {"name": "test"}) is False
def test_update_modifies_data(self):
storage = ConcreteBaseStorage()
id = storage.create({"name": "test"})
storage.update(id, {"name": "updated"})
assert storage.get(id)["name"] == "updated"
def test_delete_returns_true(self):
storage = ConcreteBaseStorage()
id = storage.create({"name": "test"})
assert storage.delete(id) is True
def test_delete_nonexistent_returns_false(self):
storage = ConcreteBaseStorage()
assert storage.delete("nonexistent") is False
def test_delete_removes_data(self):
storage = ConcreteBaseStorage()
id = storage.create({"name": "test"})
storage.delete(id)
assert storage.get(id) is None
def test_list_empty(self):
storage = ConcreteBaseStorage()
assert storage.list() == []
def test_list_after_create(self):
storage = ConcreteBaseStorage()
storage.create({"name": "a"})
storage.create({"name": "b"})
result = storage.list()
assert len(result) == 2
def test_list_with_limit(self):
storage = ConcreteBaseStorage()
for i in range(10):
storage.create({"name": f"item{i}"})
result = storage.list(limit=5)
assert len(result) == 5
def test_list_with_offset(self):
storage = ConcreteBaseStorage()
for i in range(10):
storage.create({"name": f"item{i}"})
offset_items = storage.list(offset=5)
assert len(offset_items) == 5
# --- Abstract interface enforcement ---
class TestBaseStorageAbstract:
"""Tests that BaseStorage cannot be instantiated directly."""
def test_cannot_instantiate(self):
with pytest.raises(TypeError):
BaseStorage()
class TestCardStorageAbstract:
"""Tests that CardStorage enforces abstract methods."""
def test_cannot_instantiate(self):
with pytest.raises(TypeError):
CardStorage()
def test_required_methods(self):
"""CardStorage requires get_by_user, get_latest_version, etc."""
# These should be in the abstract methods
abstract_methods = CardStorage.__abstractmethods__
assert "get_by_user" in abstract_methods
assert "get_latest_version" in abstract_methods
assert "add_version" in abstract_methods
assert "update_latest_info" in abstract_methods
assert "archive_card" in abstract_methods
assert "pin_card" in abstract_methods
class TestRatingStorageAbstract:
"""Tests that RatingStorage enforces abstract methods."""
def test_cannot_instantiate(self):
with pytest.raises(TypeError):
RatingStorage()
def test_required_methods(self):
abstract_methods = RatingStorage.__abstractmethods__
assert "get_user_rating" in abstract_methods
assert "upsert_rating" in abstract_methods
assert "get_ratings_summary" in abstract_methods
assert "get_user_ratings" in abstract_methods
class TestPreferenceStorageAbstract:
"""Tests that PreferenceStorage enforces abstract methods."""
def test_cannot_instantiate(self):
with pytest.raises(TypeError):
PreferenceStorage()
def test_required_methods(self):
abstract_methods = PreferenceStorage.__abstractmethods__
assert "get_user_preferences" in abstract_methods
assert "upsert_preferences" in abstract_methods
assert "add_liked_item" in abstract_methods
assert "add_disliked_item" in abstract_methods
assert "update_preference_embedding" in abstract_methods
# --- Interface inheritance chain ---
class TestStorageInheritance:
"""Tests for the storage interface inheritance chain."""
def test_card_storage_extends_base(self):
assert issubclass(CardStorage, BaseStorage)
def test_rating_storage_extends_base(self):
assert issubclass(RatingStorage, BaseStorage)
def test_preference_storage_extends_base(self):
assert issubclass(PreferenceStorage, BaseStorage)
def test_all_share_generate_id(self):
"""All storage interfaces should inherit generate_id from BaseStorage."""
assert hasattr(CardStorage, "generate_id")
assert hasattr(RatingStorage, "generate_id")
assert hasattr(PreferenceStorage, "generate_id")
def test_base_storage_is_abc(self):
"""BaseStorage should be an ABC."""
from abc import ABC
assert issubclass(BaseStorage, ABC)
# --- Default parameter values ---
class TestStorageDefaultParameters:
"""Tests for default parameter values in storage interfaces."""
def test_list_default_limit_100(self):
"""list() should default to limit=100."""
import inspect
sig = inspect.signature(BaseStorage.list)
assert sig.parameters["limit"].default == 100
def test_list_default_offset_0(self):
"""list() should default to offset=0."""
import inspect
sig = inspect.signature(BaseStorage.list)
assert sig.parameters["offset"].default == 0
def test_list_default_filters_none(self):
"""list() should default to filters=None."""
import inspect
sig = inspect.signature(BaseStorage.list)
assert sig.parameters["filters"].default is None
def test_get_by_user_default_limit_50(self):
import inspect
sig = inspect.signature(CardStorage.get_by_user)
assert sig.parameters["limit"].default == 50
def test_get_by_user_default_offset_0(self):
import inspect
sig = inspect.signature(CardStorage.get_by_user)
assert sig.parameters["offset"].default == 0
def test_pin_card_default_pinned_true(self):
import inspect
sig = inspect.signature(CardStorage.pin_card)
assert sig.parameters["pinned"].default is True
def test_upsert_rating_default_item_type(self):
import inspect
sig = inspect.signature(RatingStorage.upsert_rating)
assert sig.parameters["item_type"].default == "card"
def test_get_ratings_summary_default_item_type(self):
import inspect
sig = inspect.signature(RatingStorage.get_ratings_summary)
assert sig.parameters["item_type"].default == "card"
def test_get_user_ratings_default_limit(self):
import inspect
sig = inspect.signature(RatingStorage.get_user_ratings)
assert sig.parameters["limit"].default == 100