🧹 chore: Expand Binder tests coverage#3714
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughChange refactors per-iteration error handling in multiple Binder.Bind implementations to use inline checks that return immediately on formatBindData errors; adds tests covering parse-error paths, multipart bracket errors, and expanded mapping/decoder behaviors. Changes
Sequence Diagram(s)sequenceDiagram
participant Req as Request/Response
participant Binder as Binder.Bind
participant FBD as formatBindData
participant Parse as parse
Note over Binder: New common flow (cookie/form/header/resp_header)
Binder->>Req: Iterate fields/headers/cookies
Binder->>FBD: formatBindData(key, value, data)
alt formatBindData error
Binder-->>Req: return error (early)
else success
Binder-->>Req: continue
end
Binder->>Parse: parse(name, out, data)
Parse-->>Binder: error or nil
Binder-->>Req: return
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3714 +/- ##
==========================================
+ Coverage 91.77% 92.05% +0.28%
==========================================
Files 115 115
Lines 11551 11535 -16
==========================================
+ Hits 10601 10619 +18
+ Misses 687 664 -23
+ Partials 263 252 -11
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull Request Overview
This PR expands test coverage for the Binder module by adding error path tests and broadening mapping tests. The changes focus on testing error scenarios and edge cases that were previously uncovered.
Key changes:
- Add parse error tests for cookie, header, and response header binders to test error handling when invalid data is provided
- Extend mapping tests with comprehensive coverage of decoder builder functionality and edge cases
- Simplify error handling in binder implementations by removing redundant error variable declarations
Reviewed Changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| binder/resp_header_test.go | Adds parse error test for response header binding |
| binder/resp_header.go | Simplifies error handling by removing redundant error variable |
| binder/mapping_test.go | Adds comprehensive tests for decoder builder, field caching, and edge cases |
| binder/header_test.go | Adds parse error test for header binding |
| binder/header.go | Simplifies error handling and adds errcheck nolint comment |
| binder/form_test.go | Adds parse error tests for form binding including multipart scenarios |
| binder/form.go | Simplifies error handling by removing redundant error variable |
| binder/cookie_test.go | Adds parse error test for cookie binding |
| binder/cookie.go | Simplifies error handling by removing redundant error variable |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (13)
binder/form.go (1)
34-35: Fail-fast on formatBindData error clarifies control flow; verify no partial writes and cross-binder intentReturning immediately on formatBindData errors improves readability and avoids doing unnecessary work. Two quick follow-ups:
- Please confirm formatBindData is transactional with respect to the destination map (i.e., it doesn’t partially mutate data before returning an error). If it can, we may end up with partially-bound inputs before parse runs.
- Header/respHeader binders reportedly ignore formatBindData errors, while form/cookie now short-circuit. If this difference is intentional, consider documenting it in each binder to avoid surprises for users relying on consistent binder semantics.
binder/cookie_test.go (1)
89-100: Strengthen the negative-path test: run in parallel and assert no partial bindingThe test correctly exercises the parse error path. Two small improvements:
- Make the test parallel to match the style used elsewhere and speed up CI.
- Assert that no partial binding occurred (Age remains zero-value).
func Test_CookieBinder_Bind_ParseError(t *testing.T) { - b := &CookieBinding{} + t.Parallel() + b := &CookieBinding{} type User struct { Age int `cookie:"age"` } var user User req := fasthttp.AcquireRequest() req.Header.SetCookie("age", "invalid") t.Cleanup(func() { fasthttp.ReleaseRequest(req) }) err := b.Bind(req, &user) require.Error(t, err) + require.Zero(t, user.Age) }binder/header_test.go (1)
89-100: Make the test parallel and remove case-ambiguity in header name (nitpick)
- Add t.Parallel for consistency and speed.
- Optional: set the header using the same casing as the struct tag ("Age") to reduce ambiguity when reading the test (the binder likely treats headers case-insensitively anyway).
func Test_HeaderBinder_Bind_ParseError(t *testing.T) { - b := &HeaderBinding{} + t.Parallel() + b := &HeaderBinding{} type User struct { Age int `header:"Age"` } var user User req := fasthttp.AcquireRequest() - req.Header.Set("age", "invalid") + // Use same casing as the tag for clarity (binder is case-insensitive) + req.Header.Set("Age", "invalid") t.Cleanup(func() { fasthttp.ReleaseRequest(req) }) err := b.Bind(req, &user) require.Error(t, err) }binder/cookie.go (1)
25-26: Early return on formatBindData error is consistent with fail-fast policy; confirm intended divergence from header binderThis change aligns cookie binding with a fail-fast approach (vs. header binder which reportedly ignores formatBindData errors). If this divergence is intentional (e.g., to surface malformed cookie shapes earlier), please document it to set expectations for users switching between binders.
Also, similar to form binder, please confirm formatBindData doesn’t partially mutate data when returning an error.
binder/resp_header_test.go (1)
79-90: Parallelize and assert no partial binding for the negative pathGood coverage of the parse error. Recommend:
- Add t.Parallel for consistency.
- Assert that the destination field remains the zero-value when binding fails.
func Test_RespHeaderBinder_Bind_ParseError(t *testing.T) { - b := &RespHeaderBinding{} + t.Parallel() + b := &RespHeaderBinding{} type User struct { Age int `respHeader:"age"` } var user User resp := fasthttp.AcquireResponse() resp.Header.Set("age", "invalid") t.Cleanup(func() { fasthttp.ReleaseResponse(resp) }) err := b.Bind(resp, &user) require.Error(t, err) + require.Zero(t, user.Age) }binder/resp_header.go (1)
25-26: Align error-handling with HeaderBinding (formatBindData can't fail here).Given bracket translation is disabled (last arg = false) and
vis a string,formatBindDatacannot return an error for response headers. Returning early on a theoretically impossible error makes this binder’s behavior diverge from HeaderBinding, which ignores the error with a clear comment. Consider mirroring HeaderBinding for consistency.Apply this diff to align behavior:
- if err := formatBindData(b.Name(), out, data, k, v, b.EnableSplitting, false); err != nil { - return err - } + _ = formatBindData(b.Name(), out, data, k, v, b.EnableSplitting, false) //nolint:errcheck // always returns nil for string values with bracket translation disabledOptional (perf/readability): switch to the zero-allocation visitor and the narrow helper since headers are strings only:
// Optional: replace the loop with VisitAll + assignBindData resp.Header.VisitAll(func(key, val []byte) { assignBindData(b.Name(), out, data, utils.UnsafeString(key), utils.UnsafeString(val), b.EnableSplitting) })binder/header.go (1)
24-24: LGTM; add precondition note or use assignBindData to avoid nolint.Ignoring
formatBindDataerrors here is correct because: value type is string and bracket translation is disabled. To make this future-proof, consider either:
- Adding a short precondition comment (value is string; bracket=false ⇒ guaranteed nil), or
- Using
assignBindData, which matches the exact needs and removes the nolint.Example using assignBindData (optional):
for key, val := range req.Header.All() { assignBindData(b.Name(), out, data, utils.UnsafeString(key), utils.UnsafeString(val), b.EnableSplitting) }Optional (perf): prefer
VisitAllto avoid map allocations, mirroring Fiber’s zero-allocation goals.binder/form_test.go (3)
57-70: Strengthen the assertion to validate the error content.The test currently checks only that an error occurs. Verifying the message guards against regressions in error surfacing.
err := b.Bind(req, &user) -require.Error(t, err) +require.Error(t, err) +require.ErrorContains(t, err, "error converting value for \"age\"")
208-222: Prefer ErrorContains over string Contains on err.Error().
require.ErrorContainsreads cleaner and avoids manual.Error()calls.- require.Error(t, err) - require.Contains(t, err.Error(), "unmatched brackets") + require.Error(t, err) + require.ErrorContains(t, err, "unmatched brackets")
223-240: Ditto: use ErrorContains for consistency and clarity.Same rationale as above.
- require.Error(t, err) - require.Contains(t, err.Error(), "unmatched brackets") + require.Error(t, err) + require.ErrorContains(t, err, "unmatched brackets")binder/mapping_test.go (3)
379-398: Test is a bit weak for map[string]int; assert observable behavior.The
map[string]intbranch doesn’t validate outputs. IfparseToMapis expected to succeed, asserting the resulting contents avoids a vacuous pass. If values aren’t convertible, clarify the intended fallback (omit keys vs. zero values).Proposed strengthening:
m3 := make(map[string]int) err = parseToMap(m3, data) require.NoError(t, err) +// If non-convertible values are skipped: +_, ok := m3["key1"] +require.False(t, ok) +// Always present but zeroed if empty input: +v, ok := m3["empty"] +require.True(t, ok) +require.Equal(t, 0, v)If the intended behavior differs (e.g., attempt conversion and error), please adjust the assertions accordingly to reflect the contract you expect from
parseToMap.
400-407: Return decoders to the pool to avoid perturbing later tests.Not strictly necessary here, but putting the decoder back is cheap and keeps the pool state tidy for subsequent tests.
for _, tag := range tags { decAny := decoderPoolMap[tag].Get() dec, ok := decAny.(*schema.Decoder) require.True(t, ok) require.NotNil(t, dec) + decoderPoolMap[tag].Put(dec) }
438-454: Unexported-field scenario is covered; verify intention on nestedKinds.Asserting
nestedKinds[reflect.Int]may be brittle if internal representation changes. If the goal is to ensure unexported fields do not break traversal, consider asserting on observed, stable facets (e.g., presence of exported fields under the correct names) rather than internal-kind bookkeeping.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (9)
binder/cookie.go(1 hunks)binder/cookie_test.go(1 hunks)binder/form.go(1 hunks)binder/form_test.go(2 hunks)binder/header.go(1 hunks)binder/header_test.go(1 hunks)binder/mapping_test.go(2 hunks)binder/resp_header.go(1 hunks)binder/resp_header_test.go(1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2024-10-08T19:06:06.583Z
Learnt from: gaby
PR: gofiber/fiber#3056
File: middleware/encryptcookie/utils.go:22-25
Timestamp: 2024-10-08T19:06:06.583Z
Learning: The `encryptcookie_test.go` file contains unit tests that validate key lengths for both `EncryptCookie` and `DecryptCookie` functions, ensuring that invalid key lengths raise appropriate errors.
Applied to files:
binder/cookie_test.go
📚 Learning: 2024-10-02T23:03:31.727Z
Learnt from: sixcolors
PR: gofiber/fiber#3016
File: middleware/session/store.go:164-167
Timestamp: 2024-10-02T23:03:31.727Z
Learning: Unit tests in this project use testify require.
Applied to files:
binder/mapping_test.go
📚 Learning: 2024-12-13T08:14:22.851Z
Learnt from: efectn
PR: gofiber/fiber#3162
File: hooks_test.go:228-228
Timestamp: 2024-12-13T08:14:22.851Z
Learning: In Go test files, prefer using the `require` methods from the `testify` package for assertions instead of manual comparisons and calls to `t.Fatal` or `t.Fatalf`.
Applied to files:
binder/mapping_test.go
📚 Learning: 2024-10-08T19:06:06.583Z
Learnt from: sixcolors
PR: gofiber/fiber#2922
File: middleware/cors/utils.go:63-71
Timestamp: 2024-10-08T19:06:06.583Z
Learning: The project uses the testify/assert package for assertions in unit tests.
Applied to files:
binder/mapping_test.go
🧬 Code graph analysis (5)
binder/resp_header_test.go (2)
binder/resp_header.go (2)
RespHeaderBinding(9-11)RespHeaderBinding(14-16)client/response.go (2)
AcquireResponse(197-203)ReleaseResponse(207-210)
binder/cookie_test.go (2)
binder/cookie.go (2)
CookieBinding(9-11)CookieBinding(14-16)client/request.go (2)
AcquireRequest(950-956)ReleaseRequest(960-963)
binder/header_test.go (1)
binder/header.go (2)
HeaderBinding(9-11)HeaderBinding(14-16)
binder/mapping_test.go (2)
binder/mapping.go (2)
ParserConfig(19-24)ParserType(28-31)binder/binder.go (1)
ErrMapNotConvertible(11-11)
binder/form_test.go (2)
binder/form.go (2)
FormBinding(13-15)FormBinding(18-20)client/request.go (2)
AcquireRequest(950-956)ReleaseRequest(960-963)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: repeated
- GitHub Check: Compare
🔇 Additional comments (7)
binder/mapping_test.go (7)
7-7: Imports look correct.
strconvandgithub.com/gofiber/schemaare required by the new tests.Also applies to: 10-10
349-377: Custom converter usage is correct for decoderBuilder.Good validation of alias tag wiring and custom type conversion. Using a panic inside the converter is consistent with schema’s converter contract (no error return), so keeping the input controlled here is appropriate.
409-419: Field-cache coverage looks good.Nice sanity checks across known binders plus a panic path for unknown cache keys.
420-425: Map support check is appropriate.Confirms
equalFieldTypehandles map destinations. Good addition.
426-436: Safe cache-mismatch probe without t.Parallel.Storing a sentinel into the shared cache can be racy; you’ve intentionally avoided
t.Parallel()here, which is the right call. Cleanup viadefer cache.Delete(typ)is also good.
455-462: Bracket notation success case is valuable.Good positive-path complement to the error-path tests. LGTM.
464-471: Type-mismatch error is asserted correctly.Nice guardrail for file-header handling when the destination map has an incompatible element type.

Summary