test(beacon-node): cover REST query string array parsing at boundary sizes - #9712
test(beacon-node): cover REST query string array parsing at boundary sizes#9712lodekeeper wants to merge 2 commits into
Conversation
…sizes Adds regression coverage for ChainSafe#9672, where more than 20 comma-separated validator `id` values started failing with `id must be array` (400) after the qs bump lowered the effective array limit below the beacon-API `maxItems` (64). ChainSafe#9673 fixed it by raising the querystring `arrayLimit` to `NUMBER_OF_COLUMNS`. The test drives the real `RestApiServer` so it is bound to the production querystring parser in `base.ts`, rather than a stand-alone Fastify instance with a copied config. If the `arrayLimit` override regresses, the 21/64/65 item cases fail exactly as ChainSafe#9672 did. Uses representative boundary sizes (1, 20, 21, 64, 65, NUMBER_OF_COLUMNS) instead of exhaustive enumeration, and asserts that more than 64 items are still accepted — Lodestar intentionally does not hard-cap validator `id` arrays server-side at the spec `maxItems`. 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 25a81582bd
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| for (const n of ACCEPTED_COUNTS) { | ||
| it(`parses ${n} comma-separated ids as an array`, async () => { | ||
| const res = await server.fastify.inject({method: "GET", url: `/validators?${commaQuery("id", n)}`}); | ||
| expect(res.statusCode).toBe(200); |
There was a problem hiding this comment.
Add messages to assertions inside the boundary loop
The assertions in both test cases generated by this loop omit assertion messages. Add messages identifying the wire form and boundary count so failures retain the required context; repository guidance explicitly requires assertion messages for looped or repeated assertions.
AGENTS.md reference: AGENTS.md:L294-L299
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 9d90312 — added diagnostic messages naming the wire form (comma-separated / repeated) and item count to each assertion inside the ACCEPTED_COUNTS boundary loop, per the AGENTS.md looped-assertion convention:
expect(res.statusCode, `comma-separated id query with ${n} items should be accepted (200)`).toBe(200);
expect(Array.isArray(id), `comma-separated id query with ${n} items should parse as an array`).toBe(true);
expect(id, `comma-separated id query should preserve all ${n} parsed items`).toHaveLength(n);(and the same for the repeated-form it).
Address Codex review r3650324925 on ChainSafe#9712: the assertions inside the ACCEPTED_COUNTS boundary loop omitted diagnostic messages. Add messages identifying the wire form (comma-separated/repeated) and item count to each, per the repo convention requiring messages on looped assertions. 🤖 Generated with AI assistance
…ests (#9713) ## Motivation Follow-up to #9672 and the sibling unit-test PR #9712. Per the request in #9672, this adds an **e2e** suite that spins up a real beacon node and verifies our REST server is beacon-API spec compliant for query-string array lengths, so a regression that makes us non-compliant (like the `qs` `arrayLimit` regression, where >20 comma-separated `id` values returned `id must be array`/400) is caught end-to-end, not just at the parser level. This is PR 2 of 2 (PR 1 = #9712, `RestApiServer` unit coverage). ## Description Boots a dev beacon node with the REST API enabled and exercises the validator-id query endpoints via the typed `@lodestar/api` client and a raw request: - `getStateValidators` and `getStateValidatorBalances` with **64** ids (the beacon-API `maxItems`) and **`NUMBER_OF_COLUMNS`** ids (the configured server cap). - The exact comma-separated wire form from the #9672 report (`?id=a,b,c,...`) via a raw request — the `@lodestar/api` client serializes arrays as repeat-format (`id=a&id=b`), so the comma form is tested directly. ## Scope — beacon-API query-array audit Nico asked to check "other cases too". I audited every query-string array param across the beacon-API route definitions. Only the validator-`id` queries carry more than ~20 items in normal client usage (a VC resolving its managed validators), and they are the exact #9672 surface: | Endpoint | Param | Schema | maxItems | |---|---|---|---| | `getStateValidators` | `id` | `UintOrStringArray` | 64 | | `getStateValidatorBalances` | `id` | `UintOrStringArray` | 64 | | `getDebugDataColumnSidecars` | `indices` | `UintArray` | `NUMBER_OF_COLUMNS` (128) | All other query arrays — event `topics`, validator `status`, peer `state`/`direction`, blob `indices`, `versioned_hashes` — are enum- or per-block-bounded well under the old default limit (20) and never tripped the regression. The data-column `indices` path needs a Fulu block carrying columns to exercise end-to-end; it's covered at the unit level in #9712 and noted in-file as a follow-up here. Test-only change; no source behavior is modified. 🤖 Generated with AI assistance --------- Co-authored-by: lodekeeper <lodekeeper@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
can we close this since we merged #9713? those should cover the same cases just with a different testing strategy, merging both prs may be redundant |
|
Compared the two — they overlap heavily, but #9712 isn't a strict duplicate. Breakdown: Both cover: validator Only in #9712 (unit):
So closing #9712 would drop the My lean: keep #9712 as the unit half of the pair — it's cheap (no node spin-up) and covers what the e2e suite deliberately doesn't. If you'd rather not maintain two files, I can instead trim the overlapping |
…ests (ChainSafe#9713) ## Motivation Follow-up to ChainSafe#9672 and the sibling unit-test PR ChainSafe#9712. Per the request in ChainSafe#9672, this adds an **e2e** suite that spins up a real beacon node and verifies our REST server is beacon-API spec compliant for query-string array lengths, so a regression that makes us non-compliant (like the `qs` `arrayLimit` regression, where >20 comma-separated `id` values returned `id must be array`/400) is caught end-to-end, not just at the parser level. This is PR 2 of 2 (PR 1 = ChainSafe#9712, `RestApiServer` unit coverage). ## Description Boots a dev beacon node with the REST API enabled and exercises the validator-id query endpoints via the typed `@lodestar/api` client and a raw request: - `getStateValidators` and `getStateValidatorBalances` with **64** ids (the beacon-API `maxItems`) and **`NUMBER_OF_COLUMNS`** ids (the configured server cap). - The exact comma-separated wire form from the ChainSafe#9672 report (`?id=a,b,c,...`) via a raw request — the `@lodestar/api` client serializes arrays as repeat-format (`id=a&id=b`), so the comma form is tested directly. ## Scope — beacon-API query-array audit Nico asked to check "other cases too". I audited every query-string array param across the beacon-API route definitions. Only the validator-`id` queries carry more than ~20 items in normal client usage (a VC resolving its managed validators), and they are the exact ChainSafe#9672 surface: | Endpoint | Param | Schema | maxItems | |---|---|---|---| | `getStateValidators` | `id` | `UintOrStringArray` | 64 | | `getStateValidatorBalances` | `id` | `UintOrStringArray` | 64 | | `getDebugDataColumnSidecars` | `indices` | `UintArray` | `NUMBER_OF_COLUMNS` (128) | All other query arrays — event `topics`, validator `status`, peer `state`/`direction`, blob `indices`, `versioned_hashes` — are enum- or per-block-bounded well under the old default limit (20) and never tripped the regression. The data-column `indices` path needs a Fulu block carrying columns to exercise end-to-end; it's covered at the unit level in ChainSafe#9712 and noted in-file as a follow-up here. Test-only change; no source behavior is modified. 🤖 Generated with AI assistance --------- Co-authored-by: lodekeeper <lodekeeper@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Motivation
Regression coverage for #9672. After the v1.44.0
qsbump,GET /eth/v1/beacon/states/{state_id}/validators?id=a,b,c,...with more than 20 comma-separated ids started failing withid must be array(400):qs's defaultarrayLimit(20) is below the beacon-APImaxItems(64) for the validatoridquery, so anything over 20 items was parsed as an object instead of an array and rejected by thetype: "array"schema. #9673 fixed it by raising the querystringarrayLimittoNUMBER_OF_COLUMNS, but no test guards the behavior.This is PR 1 of 2 for the ask in #9672 (unit coverage that would have caught the regression); a separate PR adds e2e coverage against a real running node.
Description
Adds a
RestApiServer-level unit test that exercises query-string array parsing at representative boundary sizes.Key point: the test drives the real
RestApiServer, so it is bound to the production querystring parser configured inbase.ts(rather than a stand-alone Fastify instance with a copied parser config). If thearrayLimitoverride regresses, the 21/64/65-item cases fail exactly as #9672 did.idquery (Schema.UintOrStringArray,maxItems=64) and the data-columnindicesquery (Schema.UintArray,maxItems=NUMBER_OF_COLUMNS).1, 20, 21, 64, 65, NUMBER_OF_COLUMNS— for both comma-separated (?id=a,b,c) and repeated (?id=a&id=b) wire forms, rather than exhaustively enumerating1..64.65passes): Lodestar intentionally does not hard-cap validatoridarrays server-side at the specmaxItems. The only cap is theqsarrayLimit(NUMBER_OF_COLUMNS), beyond whichqsreturns an object and schema validation rejects it (129→ 400 with the exactid must be arraymessage from getStateValidators returns 400"id must be array"for >20 comma-separatedidvalues (regression in v1.44.0) #9672).Test-only change; no source behavior is modified.
🤖 Generated with AI assistance