Skip to content

[vitest-pool-workers] fix: reset() now clears ratelimit binding state between tests#14409

Open
matingathani wants to merge 3 commits into
cloudflare:mainfrom
matingathani:fix/vitest-pool-workers-reset-ratelimit
Open

[vitest-pool-workers] fix: reset() now clears ratelimit binding state between tests#14409
matingathani wants to merge 3 commits into
cloudflare:mainfrom
matingathani:fix/vitest-pool-workers-reset-ratelimit

Conversation

@matingathani

@matingathani matingathani commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Fixes #14392.

Problem

reset() from cloudflare:test only called workerdUnsafe.deleteAllDurableObjects(). Ratelimit bindings (RATE_LIMITERS) store their bucket counts in a Map on an in-memory Ratelimit instance. That instance is never restarted between tests, so bucket counts bleed across test boundaries — a test that exhausts a ratelimit causes subsequent tests in the same file to see it as already exhausted.

Solution

The miniflare ratelimit extension (ratelimit.worker.ts) now:

  1. Adds a reset() method to the Ratelimit class that clears buckets and resets epoch.
  2. Maintains a module-level Set<Ratelimit> and writes it to globalThis.__cfRatelimitInstances__ so the reset helper can reach it without importing an internal module directly.

reset.ts in vitest-pool-workers now iterates that set (if present) and calls reset() on each instance after deleteAllDurableObjects(). The global is only populated when the ratelimit extension is loaded, so the check is a no-op for workers with no ratelimit bindings.

Test

The existing reset fixture (fixtures/vitest-pool-workers-examples/reset) is extended with a RATE_LIMITER binding and two new tests:

  • "exhausts ratelimit then resets between tests" — exhausts the 100-request limit in one test
  • "sees reset ratelimit state after reset" — verifies the first call in the next test succeeds again

  • Tests
    • Tests included/updated
  • Public documentation
    • Documentation not necessary because: internal behaviour fix for existing reset() API, no new user-facing API surface

Open in Devin Review

… between tests

Fixes cloudflare#14392. The `reset()` helper from `cloudflare:test` only called
`deleteAllDurableObjects()`, leaving ratelimit bucket counts intact across
test boundaries. Subsequent tests in the same file could see stale
exhaustion state from an earlier test.

The fix uses a module-level registry in the miniflare ratelimit extension:
each `Ratelimit` instance registers itself on `globalThis.__cfRatelimitInstances__`
when created. `reset()` iterates that set and calls the new `Ratelimit.reset()`
method, which clears `buckets` and resets `epoch`. The global is only populated
when ratelimit bindings are actually configured, so the check is a no-op for
workers without ratelimits.

A test fixture (`fixtures/vitest-pool-workers-examples/reset`) is updated to
verify that exhausting a ratelimit in one test does not bleed into the next.
Copilot AI review requested due to automatic review settings June 24, 2026 10:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@changeset-bot

changeset-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 681bd41

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@cloudflare/vitest-pool-workers Patch
miniflare Patch
@cloudflare/deploy-helpers Patch
@cloudflare/pages-shared Patch
@cloudflare/vite-plugin Patch
wrangler Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-project-automation github-project-automation Bot moved this to Untriaged in workers-sdk Jun 24, 2026
@workers-devprod workers-devprod requested review from a team and ascorbic and removed request for a team June 24, 2026 10:01
@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/vitest-pool-workers-reset-ratelimit.md: [@cloudflare/wrangler]
  • fixtures/vitest-pool-workers-examples/reset/src/env.d.ts: [@cloudflare/wrangler]
  • fixtures/vitest-pool-workers-examples/reset/test/reset.test.ts: [@cloudflare/wrangler]
  • fixtures/vitest-pool-workers-examples/reset/wrangler.jsonc: [@cloudflare/wrangler]
  • packages/miniflare/src/workers/ratelimit/ratelimit.worker.ts: [@cloudflare/wrangler]
  • packages/vitest-pool-workers/src/worker/reset.ts: [@cloudflare/wrangler]

@pkg-pr-new

pkg-pr-new Bot commented Jun 24, 2026

Copy link
Copy Markdown
@cloudflare/autoconfig

npm i https://pkg.pr.new/@cloudflare/autoconfig@14409

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@14409

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/@cloudflare/deploy-helpers@14409

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@14409

miniflare

npm i https://pkg.pr.new/miniflare@14409

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@14409

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@14409

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@14409

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@14409

@cloudflare/workers-auth

npm i https://pkg.pr.new/@cloudflare/workers-auth@14409

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@14409

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@14409

wrangler

npm i https://pkg.pr.new/wrangler@14409

commit: 681bd41

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment thread .changeset/vitest-pool-workers-reset-ratelimit.md
Comment on lines +96 to +99
const __ratelimitInstances__ = new Set<Ratelimit>();
(
globalThis as { __cfRatelimitInstances__?: Set<Ratelimit> }
).__cfRatelimitInstances__ = __ratelimitInstances__;

@devin-ai-integration devin-ai-integration Bot Jun 24, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Reset hook uses a global variable, violating repository coding rules

A function is attached to globalThis unconditionally (__cfRatelimitReset__ at packages/miniflare/src/workers/ratelimit/ratelimit.worker.ts:101) whenever any rate-limit binding loads, so every worker with a rate-limit binding has its global scope polluted — even outside test runs.

Impact: Violates the REVIEW.md rule "Avoid using global variables. Repository maintainers will reject code that introduces global variables."

Mechanism and context

The ratelimit extension module sets globalThis.__cfRatelimitReset__ at module load time (ratelimit.worker.ts:100-106). The consumer in packages/vitest-pool-workers/src/worker/reset.ts:10-12 reads it back from globalThis. This is the only way to communicate across the internal: true module boundary, but the REVIEW.md rule is unconditional: "Avoid using global variables. Repository maintainers will reject code that introduces global variables."

Alternative approaches might include having the vitest pool plugin inject the ratelimit reset capability through workerd bindings or configuration rather than a runtime global.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

matingathani and others added 2 commits June 25, 2026 11:19
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…approach

- Add miniflare: patch to changeset — without it the feature silently doesn't
  work because vitest-pool-workers pins to a miniflare version that lacks the
  instance registry and reset() method
- Refactor the globalThis key from an instances Set to a single reset function
  reference (__cfRatelimitReset__) to minimise what's exposed on globalThis
- The ratelimit extension module is marked `internal: true` in workerd, which
  prevents dynamic import from reset.ts; globalThis is the only cross-module
  communication path available within the same Worker isolate

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Open in Devin Review

Comment on lines 5 to +8
R2_BUCKET: R2Bucket;
DATABASE: D1Database;
COUNTER: DurableObjectNamespace<import("./index").Counter>;
RATE_LIMITER: RateLimit;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 Generated env.d.ts appears manually edited rather than regenerated

The env.d.ts file at fixtures/vitest-pool-workers-examples/reset/src/env.d.ts has a generated-file header with hash 524c60174bc1732153e84c1b8699023e (line 2), but the new RATE_LIMITER: RateLimit binding on line 8 was added without updating the hash. Since wrangler types supports ratelimit bindings (see packages/wrangler/src/type-generation/index.ts:2329), running it after updating wrangler.jsonc would regenerate this file with a correct hash. The stale hash suggests manual editing. AGENTS.md says 'Never modify generated files directly — modify the generator or config, then regenerate.' This is in a test fixture so the practical impact is minimal, but it could cause confusion if the file is later regenerated and produces unexpected diffs.

(Refers to lines 2-8)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

[vitest-pool-workers] reset helper does not affect ratelimit bindings

3 participants