Skip to content

Commit a8f7259

Browse files
committed
creation of a new functionality for testing in the quick platform, Qtest. The core functions of Quick and Ctx were refactored.
The PATCH and OPTIONS methods were created.
1 parent a26334b commit a8f7259

File tree

10 files changed

+963
-122
lines changed

10 files changed

+963
-122
lines changed

README.md

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@
3232
| 📚 Docs | yes | 🟡 | 40% |
3333

3434

35-
36-
3735
## 🗺️| Development Rodmap
3836

3937
| Task | Progress |
@@ -94,21 +92,19 @@
9492
| Documentation Tests Examples PKG Go | 45.% |
9593
| Test Coverage go test -cover | 74.6% |
9694
| Regex feature coverage, but possibilities | 0.% |
97-
| Develop for OPTIONS METHOD | 0.% |
95+
| Develop for OPTIONS METHOD | 100% |
9896
| Develop for CONNECT METHOD [See more](https://www.rfc-editor.org/rfc/rfc9110.html#name-connect) | 0.% |
9997
| Develop method for ListenAndServeTLS (http2) | 0.% |
100-
| Develop Static Files support | 0.% |
101-
| Create a CLI (Command Line Interface) Quick. | 0.% |
98+
| Develop Static Files support | 100% |
10299
| WebSocket Support | 0.% |
103100
| Rate Limiter Support | 0.% |
104101
| Template Engines | 0.% |
105102
| Documentation Tests Examples PKG Go | 45. % |
106103
| Test coverage go test -cover | 75.5% |
107104
| Coverage of Regex resources, but possibilities | 0.% |
108-
| Develop for METHOD OPTIONS | 0.% |
105+
| Develop for METHOD OPTIONS | 100% |
109106
| Develop for CONNECT METHOD [See more](https://www.rfc-editor.org/rfc/rfc9110.html#name-connect) | 0.% |
110107
| Develop method for ListenAndServeTLS (http2) | 0.% |
111-
| Develops Static Files support | 0.% |
112108
| Create a CLI (Command Line Interface) Quick. | 0.% |
113109

114110

@@ -1018,6 +1014,75 @@ func main() {
10181014
```
10191015
---
10201016

1017+
# Qtest - HTTP Testing Utility for Quick Framework
1018+
1019+
Qtest is an **advanced HTTP testing function** designed to simplify route validation within the **Quick** framework. It enables seamless testing of simulated HTTP requests using `httptest`, supporting:
1020+
1021+
- **Custom HTTP methods** (`GET`, `POST`, `PUT`, `DELETE`, etc.).
1022+
- **Custom headers**.
1023+
- **Query parameters**.
1024+
- **Request body**.
1025+
- **Cookies**.
1026+
- **Built-in validation methods** for status codes, headers, and response bodies.
1027+
1028+
## 📌 Overview
1029+
The `Qtest` function takes a `QuickTestOptions` struct containing request parameters, executes the request, and returns a `QtestReturn` object, which provides methods for analyzing and validating the result.
1030+
1031+
1032+
```go
1033+
func TestQTest_Options_POST(t *testing.T) {
1034+
// start Quick
1035+
q := New()
1036+
1037+
// Define the POST route
1038+
q.Post("/v1/user/api", func(c *Ctx) error {
1039+
c.Set("Content-Type", "application/json") // Simplified header setting
1040+
return c.Status(StatusOK).String(`{"message":"Success"}`)
1041+
})
1042+
1043+
// Configure test parameters
1044+
opts := QuickTestOptions{
1045+
Method: "POST",
1046+
URI: "/v1/user/api",
1047+
QueryParams: map[string]string{
1048+
"param1": "value1",
1049+
"param2": "value2",
1050+
},
1051+
Body: []byte(`{"key":"value"}`),
1052+
Headers: map[string]string{
1053+
"Content-Type": "application/json",
1054+
},
1055+
Cookies: []*http.Cookie{
1056+
{Name: "session", Value: "abc123"},
1057+
},
1058+
LogDetails: true, // Enables detailed logging
1059+
}
1060+
1061+
// Execute test
1062+
result, err := q.Qtest(opts)
1063+
if err != nil {
1064+
t.Fatalf("Error in Qtest: %v", err)
1065+
}
1066+
1067+
// Validations
1068+
if err := result.AssertStatus(StatusOK); err != nil {
1069+
t.Errorf("Status assertion failed: %v", err)
1070+
}
1071+
1072+
if err := result.AssertHeader("Content-Type", "application/json"); err != nil {
1073+
t.Errorf("Header assertion failed: %v", err)
1074+
}
1075+
1076+
if err := result.AssertBodyContains("Success"); err != nil {
1077+
t.Errorf("Body assertion failed: %v", err)
1078+
}
1079+
}
1080+
```
1081+
1082+
🚀 **More details here [Qtest - Quick](https://github.com/jeffotoni/quick/tree/main/quickTest)**
1083+
1084+
---
1085+
10211086
## 📚| More Examples
10221087

10231088
This directory contains practical examples of the Quick Framework, a fast and lightweight web framework developed in Go. The examples are organized in separate folders, each containing a complete example of using the framework in a simple web application. If you have some interesting example of using the Quick Framework, feel free to send a pull request with your contribution. The Quick Framework example repository can be found at [here](https://github.com/jeffotoni/quick/tree/main/example).

ctx_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package quick
22

33
import (
4+
"bytes"
45
"io"
56
"net/http"
67
"net/http/httptest"
@@ -205,6 +206,31 @@ func TestCtx_BodyString(t *testing.T) {
205206
}
206207
}
207208

209+
func TestCtx_Methods_JSON(t *testing.T) {
210+
211+
q := New()
212+
213+
q.Post("/json", func(c *Ctx) error {
214+
data := map[string]string{"message": "Hello, JSON!"}
215+
return c.JSON(data)
216+
})
217+
218+
data, err := q.QuickTest("POST", "/json", nil)
219+
if err != nil {
220+
t.Errorf("Error during QuickTest: %v", err)
221+
return
222+
}
223+
224+
if data.StatusCode() != http.StatusOK {
225+
t.Errorf("Expected status %d, got %d", http.StatusOK, data.StatusCode())
226+
}
227+
228+
expected := `{"message":"Hello, JSON!"}`
229+
if !bytes.Equal(bytes.TrimSpace(data.Body()), []byte(expected)) {
230+
t.Errorf("Expected JSON body '%s', got '%s'", expected, data.BodyStr())
231+
}
232+
}
233+
208234
func TestCtx_JSON(t *testing.T) {
209235
type fields struct {
210236
Response http.ResponseWriter

quick.go

Lines changed: 77 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -147,75 +147,106 @@ func (q *Quick) Use(mw any, nf ...string) {
147147
q.mws2 = append(q.mws2, mw)
148148
}
149149

150-
// Get function is an HTTP route with the GET method on the Quick server
151-
// The result will Get(pattern string, handlerFunc HandleFunc)
152-
func (q *Quick) Get(pattern string, handlerFunc HandleFunc) {
153-
path, params, partternExist := extractParamsPattern(pattern)
154-
150+
// registerRoute is a helper function to centralize route registration logic.
151+
func (q *Quick) registerRoute(method, pattern string, handlerFunc HandleFunc) {
152+
path, params, patternExist := extractParamsPattern(pattern)
153+
formattedPath := concat.String(strings.ToLower(method)+"#", pattern)
155154
route := Route{
156-
Pattern: partternExist,
155+
Pattern: patternExist,
157156
Path: path,
158157
Params: params,
159-
handler: extractParamsGet(q, path, params, handlerFunc),
160-
Method: MethodGet,
158+
handler: extractHandler(q, method, path, params, handlerFunc),
159+
Method: method,
161160
}
161+
162162
q.appendRoute(&route)
163-
q.mux.HandleFunc(path, route.handler)
163+
q.mux.HandleFunc(formattedPath, route.handler)
164+
}
165+
166+
// Get function is an HTTP route with the GET method on the Quick server
167+
// The result will Get(pattern string, handlerFunc HandleFunc)
168+
func (q *Quick) Get(pattern string, handlerFunc HandleFunc) {
169+
q.registerRoute(MethodGet, pattern, handlerFunc)
164170
}
165171

166172
// Post function registers an HTTP route with the POST method on the Quick server
167173
// The result will Post(pattern string, handlerFunc HandleFunc)
168174
func (q *Quick) Post(pattern string, handlerFunc HandleFunc) {
169-
_, params, partternExist := extractParamsPattern(pattern)
170-
pathPost := concat.String("post#", pattern)
171-
172-
route := Route{
173-
Pattern: partternExist,
174-
Params: params,
175-
Path: pattern,
176-
handler: extractParamsPost(q, handlerFunc),
177-
Method: MethodPost,
178-
}
179-
180-
q.appendRoute(&route)
181-
q.mux.HandleFunc(pathPost, route.handler)
175+
q.registerRoute(MethodPost, pattern, handlerFunc)
182176
}
183177

184178
// Put function registers an HTTP route with the PUT method on the Quick server.
185179
// The result will Put(pattern string, handlerFunc HandleFunc)
186180
func (q *Quick) Put(pattern string, handlerFunc HandleFunc) {
187-
_, params, partternExist := extractParamsPattern(pattern)
188-
189-
pathPut := concat.String("put#", pattern)
190-
191-
route := Route{
192-
Pattern: partternExist,
193-
Path: pattern,
194-
handler: extractParamsPut(q, handlerFunc),
195-
Method: MethodPut,
196-
Params: params,
197-
}
198-
199-
q.appendRoute(&route)
200-
q.mux.HandleFunc(pathPut, route.handler)
181+
q.registerRoute(MethodPut, pattern, handlerFunc)
201182
}
202183

203184
// Delete function registers an HTTP route with the DELETE method on the Quick server.
204185
// The result will Delete(pattern string, handlerFunc HandleFunc)
205186
func (q *Quick) Delete(pattern string, handlerFunc HandleFunc) {
206-
_, params, partternExist := extractParamsPattern(pattern)
207-
pathDelete := concat.String("delete#", pattern)
187+
q.registerRoute(MethodDelete, pattern, handlerFunc)
188+
}
208189

209-
route := Route{
210-
Pattern: partternExist,
211-
Path: pattern,
212-
Params: params,
213-
handler: extractParamsDelete(q, handlerFunc),
214-
Method: MethodDelete,
190+
// Path function registers an HTTP route with the PATH method on the Quick server.
191+
// The result will Path(pattern string, handlerFunc HandleFunc)
192+
func (q *Quick) Patch(pattern string, handlerFunc HandleFunc) {
193+
q.registerRoute(MethodPatch, pattern, handlerFunc)
194+
}
195+
196+
// Options function registers an HTTP route with the Options method on the Quick server.
197+
// The result will Options(pattern string, handlerFunc HandleFunc)
198+
func (q *Quick) Options(pattern string, handlerFunc HandleFunc) {
199+
q.registerRoute(MethodOptions, pattern, handlerFunc)
200+
}
201+
202+
// Generic handler extractor to minimize repeated logic across HTTP methods
203+
// The result will extractHandler(q *Quick, method, path, params string, handlerFunc HandleFunc) http.HandlerFunc
204+
func extractHandler(q *Quick, method, path, params string, handlerFunc HandleFunc) http.HandlerFunc {
205+
switch method {
206+
case MethodGet:
207+
return extractParamsGet(q, path, params, handlerFunc)
208+
case MethodPost:
209+
return extractParamsPost(q, handlerFunc)
210+
case MethodPut:
211+
return extractParamsPut(q, handlerFunc)
212+
case MethodDelete:
213+
return extractParamsDelete(q, handlerFunc)
214+
case MethodPatch:
215+
return extractParamsPatch(q, handlerFunc) // same as PUT
216+
case MethodOptions:
217+
return extractParamsOptions(q, method, path, handlerFunc)
215218
}
219+
return nil
220+
}
216221

217-
q.appendRoute(&route)
218-
q.mux.HandleFunc(pathDelete, route.handler)
222+
func extractParamsPatch(q *Quick, handlerFunc HandleFunc) http.HandlerFunc {
223+
return extractParamsPut(q, handlerFunc)
224+
}
225+
226+
// extractParamsOptions processes an HTTP request for a dynamic route, extracting query parameters, headers, and handling the request using the provided handler function
227+
// The result will extractParamsOptions(q *Quick, method, path string, handlerFunc HandleFunc) http.HandlerFunc
228+
func extractParamsOptions(q *Quick, method, path string, handlerFunc HandleFunc) http.HandlerFunc {
229+
return func(w http.ResponseWriter, r *http.Request) {
230+
// Define allowed HTTP methods
231+
allowMethods := []string{MethodGet, MethodPost, MethodPut, MethodDelete, MethodPatch, MethodOptions}
232+
w.Header().Set("Allow", strings.Join(allowMethods, ", "))
233+
234+
// Add CORS headers
235+
w.Header().Set("Access-Control-Allow-Origin", "*")
236+
w.Header().Set("Access-Control-Allow-Methods", strings.Join(allowMethods, ", "))
237+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
238+
239+
// If a handlerFunc exists, execute it
240+
if handlerFunc != nil {
241+
err := handlerFunc(&Ctx{Response: w, Request: r})
242+
if err != nil {
243+
http.Error(w, err.Error(), http.StatusInternalServerError)
244+
return
245+
}
246+
} else {
247+
w.WriteHeader(http.StatusNoContent) // Use 204 if no body is needed
248+
}
249+
}
219250
}
220251

221252
// extractHeaders extracts all headers from an HTTP request and returns them

0 commit comments

Comments
 (0)