Skip to content

Commit 2777d2f

Browse files
committed
fix: recognize structured rate claim windows
1 parent 6e3e543 commit 2777d2f

2 files changed

Lines changed: 52 additions & 11 deletions

File tree

‎scripts/validate_storefront_claims.py‎

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,41 @@
2121
".so",
2222
}
2323
_RATE_COUNT = r"\d[\d,]*"
24-
_RATE_ACTION = r"(?:(?:api[- ]+)?(?:requests?|calls?)|reqs?\.?)"
25-
_RATE_PERIOD = r"(?:minutes?|mins?\.?|hours?|hrs?\.?|days?)"
26-
_RATE_FREQUENCY = r"(?:minutely|hourly|daily|per[- ]+(?:minute|hour|day))"
24+
_RATE_ACTION = r"(?:(?:api[- ]+)?(?:requests?|calls?|queries?|hits?|credits?)|reqs?\.?)"
25+
_RATE_UNIT_SINGULAR = r"(?:second|sec|minute|min|hour|hr|day|week|month|year)"
26+
_RATE_UNIT = rf"{_RATE_UNIT_SINGULAR}s?\.?"
27+
_RATE_ADVERB = r"(?:secondly|minutely|hourly|daily|weekly|monthly|yearly)"
28+
_RATE_DURATION = rf"(?:(?:a|an|one|any|rolling|{_RATE_COUNT})[- ]+){{0,3}}{_RATE_UNIT}"
29+
_RATE_WINDOW = (
30+
rf"(?:(?:/[- ]*|(?:per|each|every|in|within|over|during|for)[- ]+)"
31+
rf"{_RATE_DURATION}|"
32+
rf"(?:a|an)[- ]+{_RATE_UNIT})"
33+
)
34+
_RATE_FREQUENCY = (
35+
rf"(?:{_RATE_ADVERB}|per[- ]+{_RATE_UNIT_SINGULAR}|"
36+
rf"(?:one|{_RATE_COUNT})[- ]+{_RATE_UNIT_SINGULAR})"
37+
)
38+
_RATE_SCOPE = r"(?:(?:api[- ]+)?(?:requests?|calls?|queries?|hits?|credits?)[- ]+|api[- ]+)?"
39+
_RATE_LIMIT = r"(?:rate[- ]+limit|limit|allowance|quota|cap)"
40+
_RATE_ASSIGNMENT = r"(?:of|is|at|to|:|=)?"
2741
FIXED_RATE = re.compile(
2842
rf"\b{_RATE_COUNT}[- ]+{_RATE_ACTION}"
29-
rf"(?:(?:[- ]*(?:per|an?|each|every)[- ]+|[- ]*/[- ]*){_RATE_PERIOD}\b|"
30-
rf"[- ]+{_RATE_FREQUENCY}\b)|"
31-
rf"\b{_RATE_FREQUENCY}[- ]+"
32-
rf"(?:(?:api[- ]+)?(?:requests?|calls?)[- ]+)?"
33-
rf"(?:limit|allowance|quota|cap)\s*(?:of|is|:|=)?\s*"
34-
rf"{_RATE_COUNT}[- ]+{_RATE_ACTION}\b",
43+
rf"(?:[- ]*{_RATE_WINDOW}\b|[- ]+{_RATE_ADVERB}\b)|"
44+
rf"\b{_RATE_COUNT}[- ]*{_RATE_WINDOW}[- ]+{_RATE_ACTION}\b|"
45+
rf"\b{_RATE_COUNT}[- ]+{_RATE_ADVERB}[- ]+{_RATE_ACTION}\b|"
46+
rf"\b{_RATE_FREQUENCY}[- ]+{_RATE_COUNT}[- ]+{_RATE_ACTION}\b|"
47+
rf"\b{_RATE_ACTION}\s*(?::|=|is|of)?\s*{_RATE_COUNT}[- ]*"
48+
rf"{_RATE_WINDOW}\b|"
49+
rf"\b{_RATE_ACTION}[- ]+{_RATE_ADVERB}\s*(?::|=|is)?\s*"
50+
rf"{_RATE_COUNT}\b|"
51+
rf"\b{_RATE_FREQUENCY}[- ]+{_RATE_ACTION}\s*(?::|=|is)?\s*"
52+
rf"{_RATE_COUNT}\b|"
53+
rf"\b{_RATE_ACTION}[- ]*{_RATE_WINDOW}\s*(?::|=|is)?\s*"
54+
rf"{_RATE_COUNT}\b|"
55+
rf"\b{_RATE_FREQUENCY}[- ]+{_RATE_SCOPE}{_RATE_LIMIT}\s*"
56+
rf"{_RATE_ASSIGNMENT}\s*{_RATE_COUNT}(?:[- ]+{_RATE_ACTION})?\b|"
57+
rf"\b{_RATE_SCOPE}{_RATE_LIMIT}\s*{_RATE_ASSIGNMENT}\s*{_RATE_COUNT}"
58+
rf"(?:[- ]+{_RATE_ACTION})?(?:[- ]*{_RATE_WINDOW}\b|[- ]+{_RATE_ADVERB}\b)",
3559
re.IGNORECASE,
3660
)
3761
BLOCKED: Sequence[Tuple[str, Pattern[str]]] = (
@@ -151,8 +175,7 @@ def _claim_failures(root: Path, surfaces: Iterable[Path]) -> List[str]:
151175
for path in surfaces:
152176
text = path.read_text(encoding="utf-8")
153177
for label, pattern in BLOCKED:
154-
match = pattern.search(text)
155-
if match:
178+
for match in pattern.finditer(text):
156179
failures.append(
157180
f"{path.relative_to(root)}: {label} matched {match.group(0)!r}"
158181
)

‎tests/test_storefront_claims.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,21 @@ def test_rejects_claim_in_future_installed_package_data(tmp_path: Path) -> None:
157157
"100 API requests every hour",
158158
"daily cap is 50 calls",
159159
"50-call-per-day allowance",
160+
"50 requests per 24 hours",
161+
"50 requests every 24 hours",
162+
"50 API calls in a day",
163+
"daily 50-request limit",
164+
"24-hour quota of 50 calls",
165+
"API rate limit is 200 every hour",
166+
"weekly 5,000-credit allowance",
167+
"2,000 queries per 30 days",
168+
"50/day API calls",
169+
"50 daily API calls",
170+
"API calls: 50 per day",
171+
"API calls daily: 50",
172+
"daily API calls: 50",
173+
"50 requests over a rolling 24-hour window",
174+
"50 API calls during any one-hour period",
160175
],
161176
)
162177
def test_rejects_fixed_rate_aliases_in_installed_text(tmp_path: Path, claim: str) -> None:
@@ -172,6 +187,9 @@ def test_rejects_fixed_rate_aliases_in_installed_text(tmp_path: Path, claim: str
172187
"Run 50 tests daily.",
173188
"The response contains 50 records per page.",
174189
"Retry attempt 50 failed.",
190+
"Daily 50-test limit.",
191+
"A 24-hour test window contains 50 assertions.",
192+
"The monthly report contains 50 records.",
175193
],
176194
)
177195
def test_fixed_rate_aliases_do_not_match_versions_or_test_counts(

0 commit comments

Comments
 (0)