Skip to content

Commit 50e87a7

Browse files
committed
feat(mock): Add helper methods for setting query params with OpenAPI validation
1 parent bc99b13 commit 50e87a7

File tree

3 files changed

+90
-25
lines changed

3 files changed

+90
-25
lines changed

documentation/docs/guides/testing.md

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ func TestMyController(t *testing.T) {
1212
ctx := fuego.NewMockContext[MyRequestType](MyRequestType{
1313
Name: "John",
1414
Age: 30,
15-
}
15+
})
16+
17+
// Add query parameters with OpenAPI validation
18+
ctx.WithQueryParamInt("page", 1,
19+
fuego.ParamDescription("Page number"),
20+
fuego.ParamDefault(1))
1621

1722
// Call your controller
1823
response, err := MyController(ctx)
@@ -60,7 +65,7 @@ func TestSearchUsersController(t *testing.T) {
6065
tests := []struct {
6166
name string
6267
body UserSearchRequest
63-
queryParams url.Values
68+
setupContext func(*fuego.MockContext[UserSearchRequest])
6469
expectedError string
6570
expected UserSearchResponse
6671
}{
@@ -71,31 +76,30 @@ func TestSearchUsersController(t *testing.T) {
7176
MaxAge: 35,
7277
NameQuery: "John",
7378
},
74-
queryParams: url.Values{
75-
"page": {"1"},
79+
setupContext: func(ctx *fuego.MockContext[UserSearchRequest]) {
80+
// Add query parameters with OpenAPI validation
81+
ctx.WithQueryParamInt("page", 1,
82+
fuego.ParamDescription("Page number"),
83+
fuego.ParamDefault(1))
84+
ctx.WithQueryParamInt("perPage", 20,
85+
fuego.ParamDescription("Items per page"),
86+
fuego.ParamDefault(20))
7687
},
7788
expected: UserSearchResponse{
7889
// ... expected response
7990
},
8091
},
81-
{
82-
name: "invalid age range",
83-
body: UserSearchRequest{
84-
MinAge: 40,
85-
MaxAge: 20,
86-
NameQuery: "John",
87-
},
88-
expectedError: "minAge cannot be greater than maxAge",
89-
},
9092
}
9193

9294
for _, tt := range tests {
9395
t.Run(tt.name, func(t *testing.T) {
9496
// Create mock context with the test body
9597
ctx := fuego.NewMockContext[UserSearchRequest](tt.body)
9698

97-
// Set query parameters directly
98-
ctx.UrlValues = tt.queryParams
99+
// Set up context with query parameters
100+
if tt.setupContext != nil {
101+
tt.setupContext(ctx)
102+
}
99103

100104
// Call the controller
101105
response, err := SearchUsersController(ctx)
@@ -114,15 +118,29 @@ func TestSearchUsersController(t *testing.T) {
114118
}
115119
```
116120

117-
## Available Fields
121+
## Available Fields and Methods
122+
123+
The `MockContext` type provides the following:
118124

119-
The `MockContext` type provides the following public fields for testing:
125+
Public Fields:
120126

121127
- `RequestBody` - The request body of type B
122128
- `Headers` - HTTP headers
123129
- `PathParams` - URL path parameters
124130
- `Cookies` - HTTP cookies
125-
- `UrlValues` - Query parameters
131+
132+
Helper Methods for Query Parameters:
133+
134+
- `WithQueryParam(name, value string, options ...func(*OpenAPIParam))` - Add a string query parameter
135+
- `WithQueryParamInt(name string, value int, options ...func(*OpenAPIParam))` - Add an integer query parameter
136+
- `WithQueryParamBool(name string, value bool, options ...func(*OpenAPIParam))` - Add a boolean query parameter
137+
138+
Each helper method accepts OpenAPI parameter options like:
139+
140+
- `ParamDescription(description string)` - Add parameter description
141+
- `ParamDefault(value any)` - Set default value
142+
- `ParamRequired()` - Mark parameter as required
143+
- `ParamExample(name string, value any)` - Add example value
126144

127145
## Best Practices
128146

mock_context.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package fuego
22

33
import (
44
"context"
5+
"fmt"
56
"net/http"
67
"net/url"
78
"strings"
@@ -136,3 +137,48 @@ func (m *MockContext[B]) Redirect(code int, url string) (any, error) {
136137
func (m *MockContext[B]) Render(templateToExecute string, data any, templateGlobsToOverride ...string) (CtxRenderer, error) {
137138
panic("not implemented")
138139
}
140+
141+
// WithQueryParam adds a query parameter to the mock context with OpenAPI validation
142+
func (m *MockContext[B]) WithQueryParam(name string, value string, options ...func(*OpenAPIParam)) *MockContext[B] {
143+
param := OpenAPIParam{
144+
Name: name,
145+
GoType: "string",
146+
Type: "query",
147+
}
148+
for _, option := range options {
149+
option(&param)
150+
}
151+
m.CommonContext.OpenAPIParams[name] = param
152+
m.CommonContext.UrlValues.Set(name, value)
153+
return m
154+
}
155+
156+
// WithQueryParamInt adds an integer query parameter to the mock context with OpenAPI validation
157+
func (m *MockContext[B]) WithQueryParamInt(name string, value int, options ...func(*OpenAPIParam)) *MockContext[B] {
158+
param := OpenAPIParam{
159+
Name: name,
160+
GoType: "integer",
161+
Type: "query",
162+
}
163+
for _, option := range options {
164+
option(&param)
165+
}
166+
m.CommonContext.OpenAPIParams[name] = param
167+
m.CommonContext.UrlValues.Set(name, fmt.Sprintf("%d", value))
168+
return m
169+
}
170+
171+
// WithQueryParamBool adds a boolean query parameter to the mock context with OpenAPI validation
172+
func (m *MockContext[B]) WithQueryParamBool(name string, value bool, options ...func(*OpenAPIParam)) *MockContext[B] {
173+
param := OpenAPIParam{
174+
Name: name,
175+
GoType: "boolean",
176+
Type: "query",
177+
}
178+
for _, option := range options {
179+
option(&param)
180+
}
181+
m.CommonContext.OpenAPIParams[name] = param
182+
m.CommonContext.UrlValues.Set(name, fmt.Sprintf("%t", value))
183+
return m
184+
}

mock_context_test.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package fuego_test
22

33
import (
44
"errors"
5-
"net/url"
65
"testing"
76

87
"github.com/stretchr/testify/assert"
@@ -81,7 +80,7 @@ func TestSearchUsersController(t *testing.T) {
8180
tests := []struct {
8281
name string
8382
body UserSearchRequest
84-
queryParams url.Values
83+
setupContext func(*fuego.MockContext[UserSearchRequest])
8584
expectedError string
8685
expected UserSearchResponse
8786
}{
@@ -92,9 +91,9 @@ func TestSearchUsersController(t *testing.T) {
9291
MaxAge: 35,
9392
NameQuery: "John",
9493
},
95-
queryParams: url.Values{
96-
"page": {"1"},
97-
"perPage": {"20"},
94+
setupContext: func(ctx *fuego.MockContext[UserSearchRequest]) {
95+
ctx.WithQueryParamInt("page", 1, fuego.ParamDescription("Page number"), fuego.ParamDefault(1))
96+
ctx.WithQueryParamInt("perPage", 20, fuego.ParamDescription("Items per page"), fuego.ParamDefault(20))
9897
},
9998
expected: UserSearchResponse{
10099
Users: []UserProfile{
@@ -137,8 +136,10 @@ func TestSearchUsersController(t *testing.T) {
137136
// Create mock context with the test body
138137
ctx := fuego.NewMockContext(tt.body)
139138

140-
// Set query parameters directly
141-
ctx.UrlValues = tt.queryParams
139+
// Set up context with query parameters if provided
140+
if tt.setupContext != nil {
141+
tt.setupContext(ctx)
142+
}
142143

143144
// Call the controller
144145
response, err := SearchUsersController(ctx)

0 commit comments

Comments
 (0)