mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-22 23:31:12 +02:00
test(walmart): cover capability registry, schemas, and executors for both verbs
This commit is contained in:
parent
b5cddcfb2b
commit
07721bda40
8 changed files with 197 additions and 0 deletions
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue