refactor(adapters): accept SharedTableProtocol; replace thread-local Store pattern with clone()

This commit is contained in:
pyr0ball 2026-05-18 09:12:00 -07:00
parent 9d8b627fe1
commit 80ac13e69f
3 changed files with 28 additions and 7 deletions

View file

@ -22,7 +22,7 @@ _SHOPPING_API_INTER_REQUEST_DELAY = 0.5 # seconds between successive calls
_SELLER_ENRICH_TTL_HOURS = 24 # skip re-enrichment within this window
from app.db.models import Listing, MarketComp, Seller
from app.db.store import Store
from app.db.protocol import SharedTableProtocol
from app.platforms import PlatformAdapter, SearchFilters
from app.platforms.ebay.auth import EbayTokenManager
from app.platforms.ebay.normaliser import normalise_listing, normalise_seller
@ -67,7 +67,7 @@ BROWSE_BASE = {
class EbayAdapter(PlatformAdapter):
def __init__(self, token_manager: EbayTokenManager, shared_store: Store, env: str = "production"):
def __init__(self, token_manager: EbayTokenManager, shared_store: SharedTableProtocol, env: str = "production"):
self._tokens = token_manager
self._store = shared_store
self._env = env

View file

@ -25,7 +25,7 @@ log = logging.getLogger(__name__)
from bs4 import BeautifulSoup
from app.db.models import Listing, MarketComp, Seller
from app.db.store import Store
from app.db.protocol import SharedTableProtocol
from app.platforms import PlatformAdapter, SearchFilters
EBAY_SEARCH_URL = "https://www.ebay.com/sch/i.html"
@ -286,7 +286,7 @@ class ScrapedEbayAdapter(PlatformAdapter):
category_history) cause TrustScorer to set score_is_partial=True.
"""
def __init__(self, shared_store: Store, delay: float = 1.0):
def __init__(self, shared_store: SharedTableProtocol, delay: float = 1.0):
self._store = shared_store
self._delay = delay
@ -374,8 +374,6 @@ class ScrapedEbayAdapter(PlatformAdapter):
Does not raise failures per-seller are silently skipped so the main
search response is never blocked.
"""
db_path = self._store._db_path # capture for thread-local Store creation
def _enrich_one(item: tuple[str, str]) -> None:
seller_id, listing_id = item
try:
@ -388,7 +386,7 @@ class ScrapedEbayAdapter(PlatformAdapter):
)
if age_days is None and fb_count is None:
return # nothing new to write
thread_store = Store(db_path)
thread_store = self._store.clone()
seller = thread_store.get_seller("ebay", seller_id)
if not seller:
log.warning("BTF enrich: seller %s not found in DB", seller_id)

View file

@ -14,3 +14,26 @@ def test_store_clone_returns_new_instance(tmp_path):
assert isinstance(clone, Store)
assert clone is not s
assert clone._db_path == db
def test_ebay_adapter_accepts_protocol():
from app.platforms.ebay.adapter import EbayAdapter
import tempfile
import pathlib
from unittest.mock import MagicMock
with tempfile.TemporaryDirectory() as tmp:
s = Store(pathlib.Path(tmp) / "t.db")
adapter = EbayAdapter(token_manager=MagicMock(), shared_store=s)
assert adapter._store is s
def test_scraped_adapter_no_db_path_ref():
from app.platforms.ebay.scraper import ScrapedEbayAdapter
import tempfile
import pathlib
with tempfile.TemporaryDirectory() as tmp:
s = Store(pathlib.Path(tmp) / "t.db")
adapter = ScrapedEbayAdapter(shared_store=s)
assert not hasattr(adapter, '_db_path_ref')