Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Reject every fixed demo-rate literal, including the previously exempted
daily allowance, and direct examples to the reviewed live product facts.
- Reject common fixed request and API-call rate spellings, including prefix,
suffix, and hyphenated daily/hourly/minute forms, and direct examples to the
reviewed live product facts.

## [1.12.3] - 2026-08-11

Expand Down
20 changes: 15 additions & 5 deletions scripts/validate_storefront_claims.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@
".pyo",
".so",
}
_RATE_COUNT = r"\d[\d,]*"
_RATE_ACTION = r"(?:(?:api[- ]+)?(?:requests?|calls?)|reqs?\.?)"
_RATE_PERIOD = r"(?:minutes?|mins?\.?|hours?|hrs?\.?|days?)"
_RATE_FREQUENCY = r"(?:minutely|hourly|daily|per[- ]+(?:minute|hour|day))"
FIXED_RATE = re.compile(
rf"\b{_RATE_COUNT}[- ]+{_RATE_ACTION}"
rf"(?:(?:[- ]*(?:per|an?|each|every)[- ]+|[- ]*/[- ]*){_RATE_PERIOD}\b|"
rf"[- ]+{_RATE_FREQUENCY}\b)|"
rf"\b{_RATE_FREQUENCY}[- ]+"
rf"(?:(?:api[- ]+)?(?:requests?|calls?)[- ]+)?"
rf"(?:limit|allowance|quota|cap)\s*(?:of|is|:|=)?\s*"
rf"{_RATE_COUNT}[- ]+{_RATE_ACTION}\b",
re.IGNORECASE,
)
BLOCKED: Sequence[Tuple[str, Pattern[str]]] = (
("real-time claim", re.compile(r"\breal[ -]?time\b", re.IGNORECASE)),
(
Expand Down Expand Up @@ -87,11 +101,7 @@
),
(
"fixed demo rate",
re.compile(
r"\b\d+\s+(?:requests?|reqs?\.?)\s*(?:(?:per|an?)\s+|/\s*)"
r"(?:minutes?|mins?|hours?|hrs?|days?)\b",
re.IGNORECASE,
),
FIXED_RATE,
),
)

Expand Down
63 changes: 63 additions & 0 deletions tests/test_storefront_claims.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from pathlib import Path
from typing import List

import pytest

from scripts.validate_storefront_claims import (
discover_installed_surfaces,
Expand All @@ -10,6 +13,28 @@
ROOT = Path(__file__).resolve().parents[1]


def _installed_text_failures(tmp_path: Path, text: str) -> List[str]:
package = tmp_path / "oilpriceapi"
dist_info = tmp_path / "oilpriceapi-9.9.9.dist-info"
package.mkdir()
dist_info.mkdir()
(package / "version.py").write_text('__version__ = "9.9.9"\n')
(package / "future.txt").write_text(text)
(dist_info / "METADATA").write_text(
"Metadata-Version: 2.1\n"
"Name: oilpriceapi\n"
"Version: 9.9.9\n\n"
"https://api.oilpriceapi.com/product-facts.json\n"
)
(dist_info / "RECORD").write_text(
"oilpriceapi/version.py,,\n"
"oilpriceapi/future.txt,,\n"
"oilpriceapi-9.9.9.dist-info/METADATA,,\n"
"oilpriceapi-9.9.9.dist-info/RECORD,,\n"
)
return validate_package(tmp_path)


def test_storefront_claims_match_reviewed_contract() -> None:
assert validate() == []

Expand Down Expand Up @@ -115,3 +140,41 @@ def test_rejects_claim_in_future_installed_package_data(tmp_path: Path) -> None:
and "matched '50 requests/day'" in failure
for failure in failures
)


@pytest.mark.parametrize(
"claim",
[
"50 API calls/day",
"50 calls per day",
"50 requests daily",
"daily limit of 50 requests",
"50-request daily allowance",
"100 API calls hourly",
"hourly request quota: 100 calls",
"3 reqs/minute",
"50 calls each day",
"100 API requests every hour",
"daily cap is 50 calls",
"50-call-per-day allowance",
],
)
def test_rejects_fixed_rate_aliases_in_installed_text(tmp_path: Path, claim: str) -> None:
failures = _installed_text_failures(tmp_path, claim)

assert any("fixed demo rate" in failure for failure in failures), failures


@pytest.mark.parametrize(
"text",
[
"SDK version 1.12.4 supports Python 3.8.",
"Run 50 tests daily.",
"The response contains 50 records per page.",
"Retry attempt 50 failed.",
],
)
def test_fixed_rate_aliases_do_not_match_versions_or_test_counts(
tmp_path: Path, text: str
) -> None:
assert _installed_text_failures(tmp_path, text) == []