From 07721bda40b9086f5cd05ba90f6a8a7d33cb9105 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Sun, 19 Jul 2026 08:49:10 +0200 Subject: [PATCH] test(walmart): cover capability registry, schemas, and executors for both verbs --- .../unit/capabilities/walmart/__init__.py | 0 .../capabilities/walmart/reviews/__init__.py | 0 .../walmart/reviews/test_executor.py | 38 ++++++++++++ .../walmart/reviews/test_schemas.py | 37 ++++++++++++ .../capabilities/walmart/scrape/__init__.py | 0 .../walmart/scrape/test_executor.py | 42 ++++++++++++++ .../walmart/scrape/test_schemas.py | 58 +++++++++++++++++++ .../capabilities/walmart/test_registry.py | 22 +++++++ 8 files changed, 197 insertions(+) create mode 100644 surfsense_backend/tests/unit/capabilities/walmart/__init__.py create mode 100644 surfsense_backend/tests/unit/capabilities/walmart/reviews/__init__.py create mode 100644 surfsense_backend/tests/unit/capabilities/walmart/reviews/test_executor.py create mode 100644 surfsense_backend/tests/unit/capabilities/walmart/reviews/test_schemas.py create mode 100644 surfsense_backend/tests/unit/capabilities/walmart/scrape/__init__.py create mode 100644 surfsense_backend/tests/unit/capabilities/walmart/scrape/test_executor.py create mode 100644 surfsense_backend/tests/unit/capabilities/walmart/scrape/test_schemas.py create mode 100644 surfsense_backend/tests/unit/capabilities/walmart/test_registry.py diff --git a/surfsense_backend/tests/unit/capabilities/walmart/__init__.py b/surfsense_backend/tests/unit/capabilities/walmart/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/surfsense_backend/tests/unit/capabilities/walmart/reviews/__init__.py b/surfsense_backend/tests/unit/capabilities/walmart/reviews/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/surfsense_backend/tests/unit/capabilities/walmart/reviews/test_executor.py b/surfsense_backend/tests/unit/capabilities/walmart/reviews/test_executor.py new file mode 100644 index 000000000..d9d325780 --- /dev/null +++ b/surfsense_backend/tests/unit/capabilities/walmart/reviews/test_executor.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +from app.capabilities.walmart.reviews.executor import build_reviews_executor +from app.capabilities.walmart.reviews.schemas import ReviewsInput +from app.proprietary.platforms.walmart import WalmartReviewsInput + + +class _FakeScraper: + def __init__(self) -> None: + self.calls: list[tuple[WalmartReviewsInput, int | None]] = [] + + async def __call__( + self, input_model: WalmartReviewsInput, *, limit: int | None = None + ) -> list[dict]: + self.calls.append((input_model, limit)) + return [{"reviewId": "r1", "rating": 5}] + + +async def test_executor_maps_agent_input_to_scraper_input(): + scraper = _FakeScraper() + execute = build_reviews_executor(scraper) + + output = await execute( + ReviewsInput( + urls=["https://www.walmart.com/ip/123456"], + item_ids=["222"], + max_reviews=50, + sort_by="most-helpful", + ) + ) + + assert output.items[0].reviewId == "r1" + input_model, limit = scraper.calls[0] + assert input_model.itemIds == ["https://www.walmart.com/ip/123456", "222"] + assert input_model.maxReviews == 50 + assert input_model.sort == "most-helpful" + # limit is the pre-flight worst case: 2 sources * 50 reviews + assert limit == 100 diff --git a/surfsense_backend/tests/unit/capabilities/walmart/reviews/test_schemas.py b/surfsense_backend/tests/unit/capabilities/walmart/reviews/test_schemas.py new file mode 100644 index 000000000..2d35ab191 --- /dev/null +++ b/surfsense_backend/tests/unit/capabilities/walmart/reviews/test_schemas.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +import pytest +from pydantic import ValidationError + +from app.capabilities.walmart.reviews.schemas import ReviewsInput, ReviewsOutput + + +def test_estimated_units_scale_with_sources_and_max_reviews(): + payload = ReviewsInput( + urls=["https://www.walmart.com/ip/1"], item_ids=["222"], max_reviews=150 + ) + + # 2 sources * 150 reviews each + assert payload.estimated_units == 300 + + +def test_at_least_one_source_is_required(): + with pytest.raises(ValidationError): + ReviewsInput() + + +def test_sources_merge_urls_then_item_ids(): + payload = ReviewsInput(urls=["https://www.walmart.com/ip/1"], item_ids=["222"]) + + assert payload.sources() == ["https://www.walmart.com/ip/1", "222"] + + +def test_error_items_are_not_billable(): + output = ReviewsOutput( + items=[ + {"reviewId": "r1", "rating": 5}, + {"error": "reviews_not_found"}, + ] + ) + + assert output.billable_units == 1 diff --git a/surfsense_backend/tests/unit/capabilities/walmart/scrape/__init__.py b/surfsense_backend/tests/unit/capabilities/walmart/scrape/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/surfsense_backend/tests/unit/capabilities/walmart/scrape/test_executor.py b/surfsense_backend/tests/unit/capabilities/walmart/scrape/test_executor.py new file mode 100644 index 000000000..e31e4b638 --- /dev/null +++ b/surfsense_backend/tests/unit/capabilities/walmart/scrape/test_executor.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from app.capabilities.walmart.scrape.executor import build_scrape_executor +from app.capabilities.walmart.scrape.schemas import MAX_WALMART_RESULTS, ScrapeInput +from app.proprietary.platforms.walmart import WalmartScrapeInput + + +class _FakeScraper: + def __init__(self) -> None: + self.calls: list[tuple[WalmartScrapeInput, int | None]] = [] + + async def __call__( + self, input_model: WalmartScrapeInput, *, limit: int | None = None + ) -> list[dict]: + self.calls.append((input_model, limit)) + return [{"usItemId": "123", "name": "Product"}] + + +async def test_executor_maps_agent_input_to_scraper_input(): + scraper = _FakeScraper() + execute = build_scrape_executor(scraper) + + output = await execute( + ScrapeInput( + search_terms=["air fryer"], + urls=["https://www.walmart.com/ip/123456"], + max_items=5, + include_details=False, + include_reviews_sample=False, + ) + ) + + assert output.items[0].usItemId == "123" + input_model, limit = scraper.calls[0] + assert input_model.startUrls == [ + "https://www.walmart.com/ip/123456", + "https://www.walmart.com/search?q=air+fryer", + ] + assert input_model.maxItemsPerStartUrl == 5 + assert input_model.includeDetails is False + assert input_model.includeReviewsSample is False + assert limit == MAX_WALMART_RESULTS diff --git a/surfsense_backend/tests/unit/capabilities/walmart/scrape/test_schemas.py b/surfsense_backend/tests/unit/capabilities/walmart/scrape/test_schemas.py new file mode 100644 index 000000000..c7ce4ff9e --- /dev/null +++ b/surfsense_backend/tests/unit/capabilities/walmart/scrape/test_schemas.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +import pytest +from pydantic import ValidationError + +from app.capabilities.walmart.scrape.schemas import ( + MAX_WALMART_RESULTS, + ScrapeInput, + ScrapeOutput, +) + + +def test_estimated_units_cover_search_and_direct_sources(): + payload = ScrapeInput( + search_terms=["air fryer", "blender"], + urls=["https://www.walmart.com/ip/123456"], + max_items=20, + ) + + # (2 search + 1 direct source) * 20 items each + assert payload.estimated_units == 60 + + +def test_estimated_units_respect_hard_run_ceiling(): + payload = ScrapeInput(search_terms=["x"] * 20, max_items=100) + assert payload.estimated_units == MAX_WALMART_RESULTS + + +def test_at_least_one_source_is_required(): + with pytest.raises(ValidationError): + ScrapeInput() + + +def test_combined_sources_are_capped(): + with pytest.raises(ValidationError): + ScrapeInput(urls=["u"] * 11, search_terms=["t"] * 10) + + +def test_start_urls_synthesize_a_search_url_per_term(): + payload = ScrapeInput( + urls=["https://www.walmart.com/ip/1"], search_terms=["air fryer"] + ) + + assert payload.start_urls() == [ + "https://www.walmart.com/ip/1", + "https://www.walmart.com/search?q=air+fryer", + ] + + +def test_error_items_are_not_billable(): + output = ScrapeOutput( + items=[ + {"usItemId": "123", "name": "Product"}, + {"error": "product_not_found", "errorDescription": "Missing"}, + ] + ) + + assert output.billable_units == 1 diff --git a/surfsense_backend/tests/unit/capabilities/walmart/test_registry.py b/surfsense_backend/tests/unit/capabilities/walmart/test_registry.py new file mode 100644 index 000000000..647ad7976 --- /dev/null +++ b/surfsense_backend/tests/unit/capabilities/walmart/test_registry.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +from app.capabilities.core import BillingUnit +from app.capabilities.core.store import get_capability +from app.capabilities.walmart.reviews.schemas import ReviewsInput, ReviewsOutput +from app.capabilities.walmart.scrape.schemas import ScrapeInput, ScrapeOutput + + +def test_walmart_scrape_is_registered_and_metered(): + capability = get_capability("walmart.scrape") + + assert capability.input_schema is ScrapeInput + assert capability.output_schema is ScrapeOutput + assert capability.billing_unit is BillingUnit.WALMART_PRODUCT + + +def test_walmart_reviews_is_registered_and_metered(): + capability = get_capability("walmart.reviews") + + assert capability.input_schema is ReviewsInput + assert capability.output_schema is ReviewsOutput + assert capability.billing_unit is BillingUnit.WALMART_REVIEW