Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🔥 Feature: Add Req and Res API #2894

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ type DefaultCtx struct {
fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx
bind *Bind // Default bind reference
redirect *Redirect // Default redirect reference
req *DefaultReq // Default request api reference
res *DefaultRes // Default response api reference
values [maxParams]string // Route parameter values
viewBindMap sync.Map // Default view map to bind template engine
method string // HTTP method
Expand Down Expand Up @@ -1463,6 +1465,18 @@ func (c *DefaultCtx) renderExtensions(bind any) {
}
}

// Req returns a convenience type whose API is limited to operations
// on the incoming request.
func (c *DefaultCtx) Req() Req {
return c.req
}

// Res returns a convenience type whose API is limited to operations
// on the outgoing response.
func (c *DefaultCtx) Res() Res {
return c.res
}
Comment on lines +1474 to +1478
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Add test coverage for the Res() method

The review comment is correct. The codebase search reveals that while there are interface and implementation definitions for Res, there is no direct test coverage for the Res() method of DefaultCtx. A test should be added to verify that:

  1. The method returns the internal res field
  2. The returned value implements the Res interface
  3. Operations on the returned value affect the context's response
🔗 Analysis chain

Add test coverage for the Res() method

While the implementation is correct and the documentation is clear, test coverage is missing for this method. Please add unit tests to ensure the method correctly returns the res field.

Would you like me to help generate unit tests for this method?

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify test coverage for Res() method
# Test: Search for test cases covering Res(). Expect: Test cases for the method.
rg --type go 'func Test.*Res\(' app/

Length of output: 83


Script:

#!/bin/bash
# First, let's find the correct location of test files
fd -e go test

# Then search for any existing test coverage of Res() method
rg --type go 'func Test.*Res\('

# Also search for any usage of Res() in test files to catch potential indirect testing
rg --type go '\.Res\(' -g '*test.go'

Length of output: 2109


Script:

#!/bin/bash
# Let's check ctx_test.go specifically since Res() is defined in ctx.go
cat ctx_test.go | grep -A 10 -B 10 "Res()"

# Also check for any indirect testing of the response functionality
rg --type go "c\.res\b" ctx_test.go

# Let's also look for any tests that might be using the Res type
rg --type go "type.*Res\b" -A 5

Length of output: 620

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 1476-1477: ctx.go#L1476-L1477
Added lines #L1476 - L1477 were not covered by tests


// Route returns the matched Route struct.
func (c *DefaultCtx) Route() *Route {
if c.route == nil {
Expand Down
6 changes: 5 additions & 1 deletion ctx_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,14 @@ type CustomCtx interface {

func NewDefaultCtx(app *App) *DefaultCtx {
// return ctx
return &DefaultCtx{
ctx := &DefaultCtx{
// Set app reference
app: app,
}
ctx.req = &DefaultReq{ctx: ctx}
ctx.res = &DefaultRes{ctx: ctx}

return ctx
}

func (app *App) newCtx() Ctx {
Expand Down
6 changes: 6 additions & 0 deletions ctx_interface_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 34 additions & 34 deletions ctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func Test_Ctx_Accepts(t *testing.T) {

c.Request().Header.Set(HeaderAccept, "text/html,application/xhtml+xml,application/xml;q=0.9")
require.Equal(t, "", c.Accepts(""))
require.Equal(t, "", c.Accepts())
require.Equal(t, "", c.Req().Accepts())
require.Equal(t, ".xml", c.Accepts(".xml"))
require.Equal(t, "", c.Accepts(".john"))
require.Equal(t, "application/xhtml+xml", c.Accepts("application/xml", "application/xml+rss", "application/yaml", "application/xhtml+xml"), "must use client-preferred mime type")
Expand All @@ -57,13 +57,13 @@ func Test_Ctx_Accepts(t *testing.T) {
c.Request().Header.Set(HeaderAccept, "text/*, application/json")
require.Equal(t, "html", c.Accepts("html"))
require.Equal(t, "text/html", c.Accepts("text/html"))
require.Equal(t, "json", c.Accepts("json", "text"))
require.Equal(t, "json", c.Req().Accepts("json", "text"))
require.Equal(t, "application/json", c.Accepts("application/json"))
require.Equal(t, "", c.Accepts("image/png"))
require.Equal(t, "", c.Accepts("png"))

c.Request().Header.Set(HeaderAccept, "text/html, application/json")
require.Equal(t, "text/*", c.Accepts("text/*"))
require.Equal(t, "text/*", c.Req().Accepts("text/*"))

c.Request().Header.Set(HeaderAccept, "*/*")
require.Equal(t, "html", c.Accepts("html"))
Expand Down Expand Up @@ -968,46 +968,46 @@ func Test_Ctx_Cookie(t *testing.T) {
Expires: expire,
// SameSite: CookieSameSiteStrictMode, // default is "lax"
}
c.Cookie(cookie)
c.Res().Cookie(cookie)
expect := "username=john; expires=" + httpdate + "; path=/; SameSite=Lax"
require.Equal(t, expect, string(c.Response().Header.Peek(HeaderSetCookie)))
require.Equal(t, expect, c.Res().Get(HeaderSetCookie))

expect = "username=john; expires=" + httpdate + "; path=/"
cookie.SameSite = CookieSameSiteDisabled
c.Cookie(cookie)
require.Equal(t, expect, string(c.Response().Header.Peek(HeaderSetCookie)))
c.Res().Cookie(cookie)
require.Equal(t, expect, c.Res().Get(HeaderSetCookie))

expect = "username=john; expires=" + httpdate + "; path=/; SameSite=Strict"
cookie.SameSite = CookieSameSiteStrictMode
c.Cookie(cookie)
require.Equal(t, expect, string(c.Response().Header.Peek(HeaderSetCookie)))
c.Res().Cookie(cookie)
require.Equal(t, expect, c.Res().Get(HeaderSetCookie))

expect = "username=john; expires=" + httpdate + "; path=/; secure; SameSite=None"
cookie.Secure = true
cookie.SameSite = CookieSameSiteNoneMode
c.Cookie(cookie)
require.Equal(t, expect, string(c.Response().Header.Peek(HeaderSetCookie)))
c.Res().Cookie(cookie)
require.Equal(t, expect, c.Res().Get(HeaderSetCookie))

expect = "username=john; path=/; secure; SameSite=None"
// should remove expires and max-age headers
cookie.SessionOnly = true
cookie.Expires = expire
cookie.MaxAge = 10000
c.Cookie(cookie)
require.Equal(t, expect, string(c.Response().Header.Peek(HeaderSetCookie)))
c.Res().Cookie(cookie)
require.Equal(t, expect, c.Res().Get(HeaderSetCookie))

expect = "username=john; path=/; secure; SameSite=None"
// should remove expires and max-age headers when no expire and no MaxAge (default time)
cookie.SessionOnly = false
cookie.Expires = time.Time{}
cookie.MaxAge = 0
c.Cookie(cookie)
require.Equal(t, expect, string(c.Response().Header.Peek(HeaderSetCookie)))
c.Res().Cookie(cookie)
require.Equal(t, expect, c.Res().Get(HeaderSetCookie))

expect = "username=john; path=/; secure; SameSite=None; Partitioned"
cookie.Partitioned = true
c.Cookie(cookie)
require.Equal(t, expect, string(c.Response().Header.Peek(HeaderSetCookie)))
c.Res().Cookie(cookie)
require.Equal(t, expect, c.Res().Get(HeaderSetCookie))
}

// go test -v -run=^$ -bench=Benchmark_Ctx_Cookie -benchmem -count=4
Expand All @@ -1033,8 +1033,8 @@ func Test_Ctx_Cookies(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})

c.Request().Header.Set("Cookie", "john=doe")
require.Equal(t, "doe", c.Cookies("john"))
require.Equal(t, "default", c.Cookies("unknown", "default"))
require.Equal(t, "doe", c.Req().Cookies("john"))
require.Equal(t, "default", c.Req().Cookies("unknown", "default"))
}

// go test -run Test_Ctx_Format
Expand All @@ -1058,13 +1058,13 @@ func Test_Ctx_Format(t *testing.T) {
}

c.Request().Header.Set(HeaderAccept, `text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7`)
err := c.Format(formatHandlers("application/xhtml+xml", "application/xml", "foo/bar")...)
err := c.Res().Format(formatHandlers("application/xhtml+xml", "application/xml", "foo/bar")...)
require.Equal(t, "application/xhtml+xml", accepted)
require.Equal(t, "application/xhtml+xml", c.GetRespHeader(HeaderContentType))
require.NoError(t, err)
require.NotEqual(t, StatusNotAcceptable, c.Response().StatusCode())

err = c.Format(formatHandlers("foo/bar;a=b")...)
err = c.Res().Format(formatHandlers("foo/bar;a=b")...)
require.Equal(t, "foo/bar;a=b", accepted)
require.Equal(t, "foo/bar;a=b", c.GetRespHeader(HeaderContentType))
require.NoError(t, err)
Expand Down Expand Up @@ -1165,7 +1165,7 @@ func Test_Ctx_AutoFormat(t *testing.T) {
require.Equal(t, "Hello, World!", string(c.Response().Body()))

c.Request().Header.Set(HeaderAccept, MIMETextHTML)
err = c.AutoFormat("Hello, World!")
err = c.Res().AutoFormat("Hello, World!")
require.NoError(t, err)
require.Equal(t, "<p>Hello, World!</p>", string(c.Response().Body()))

Expand All @@ -1175,7 +1175,7 @@ func Test_Ctx_AutoFormat(t *testing.T) {
require.Equal(t, `"Hello, World!"`, string(c.Response().Body()))

c.Request().Header.Set(HeaderAccept, MIMETextPlain)
err = c.AutoFormat(complex(1, 1))
err = c.Res().AutoFormat(complex(1, 1))
require.NoError(t, err)
require.Equal(t, "(1+1i)", string(c.Response().Body()))

Expand Down Expand Up @@ -2939,7 +2939,7 @@ func Test_Ctx_SaveFile(t *testing.T) {
app := New()

app.Post("/test", func(c Ctx) error {
fh, err := c.FormFile("file")
fh, err := c.Req().FormFile("file")
require.NoError(t, err)

tempFile, err := os.CreateTemp(os.TempDir(), "test-")
Expand Down Expand Up @@ -3075,7 +3075,7 @@ func Test_Ctx_ClearCookie(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})

c.Request().Header.Set(HeaderCookie, "john=doe")
c.ClearCookie("john")
c.Res().ClearCookie("john")
require.True(t, strings.HasPrefix(string(c.Response().Header.Peek(HeaderSetCookie)), "john=; expires="))

c.Request().Header.Set(HeaderCookie, "test1=dummy")
Expand Down Expand Up @@ -3104,7 +3104,7 @@ func Test_Ctx_Download(t *testing.T) {
require.Equal(t, expect, c.Response().Body())
require.Equal(t, `attachment; filename="Awesome+File%21"`, string(c.Response().Header.Peek(HeaderContentDisposition)))

require.NoError(t, c.Download("ctx.go"))
require.NoError(t, c.Res().Download("ctx.go"))
require.Equal(t, `attachment; filename="ctx.go"`, string(c.Response().Header.Peek(HeaderContentDisposition)))
}

Expand Down Expand Up @@ -3136,7 +3136,7 @@ func Test_Ctx_SendFile(t *testing.T) {

// test with custom error code
c = app.AcquireCtx(&fasthttp.RequestCtx{})
err = c.Status(StatusInternalServerError).SendFile("ctx.go")
err = c.Res().Status(StatusInternalServerError).SendFile("ctx.go")
// check expectation
require.NoError(t, err)
require.Equal(t, expectFileContent, c.Response().Body())
Expand All @@ -3161,7 +3161,7 @@ func Test_Ctx_SendFile_ContentType(t *testing.T) {

// 1) simple case
c := app.AcquireCtx(&fasthttp.RequestCtx{})
err := c.SendFile("./.github/testdata/fs/img/fiber.png")
err := c.Res().SendFile("./.github/testdata/fs/img/fiber.png")
// check expectation
require.NoError(t, err)
require.Equal(t, StatusOK, c.Response().StatusCode())
Expand Down Expand Up @@ -3782,7 +3782,7 @@ func Test_Ctx_JSONP(t *testing.T) {
require.Equal(t, `callback({"Age":20,"Name":"Grame"});`, string(c.Response().Body()))
require.Equal(t, "text/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type")))

err = c.JSONP(Map{
err = c.Res().JSONP(Map{
"Name": "Grame",
"Age": 20,
}, "john")
Expand Down Expand Up @@ -4006,7 +4006,7 @@ func Test_Ctx_Render(t *testing.T) {
err = c.Render("./.github/testdata/template-non-exists.html", nil)
require.Error(t, err)

err = c.Render("./.github/testdata/template-invalid.html", nil)
err = c.Res().Render("./.github/testdata/template-invalid.html", nil)
require.Error(t, err)
}

Expand Down Expand Up @@ -4907,7 +4907,7 @@ func Test_Ctx_Queries(t *testing.T) {

c.Request().URI().SetQueryString("tags=apple,orange,banana&filters[tags]=apple,orange,banana&filters[category][name]=fruits&filters.tags=apple,orange,banana&filters.category.name=fruits")

queries = c.Queries()
queries = c.Req().Queries()
require.Equal(t, "apple,orange,banana", queries["tags"])
require.Equal(t, "apple,orange,banana", queries["filters[tags]"])
require.Equal(t, "fruits", queries["filters[category][name]"])
Expand Down Expand Up @@ -5055,7 +5055,7 @@ func Test_Ctx_IsFromLocal_X_Forwarded(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Header.Set(HeaderXForwardedFor, "93.46.8.90")

require.False(t, c.IsFromLocal())
require.False(t, c.Req().IsFromLocal())
}
}

Expand Down Expand Up @@ -5088,8 +5088,8 @@ func Test_Ctx_IsFromLocal_RemoteAddr(t *testing.T) {
fastCtx := &fasthttp.RequestCtx{}
fastCtx.SetRemoteAddr(localIPv6)
c := app.AcquireCtx(fastCtx)
require.Equal(t, "::1", c.IP())
require.True(t, c.IsFromLocal())
require.Equal(t, "::1", c.Req().IP())
require.True(t, c.Req().IsFromLocal())
}
// Test for the case fasthttp remoteAddr is set to "0:0:0:0:0:0:0:1".
{
Expand Down
Loading
Loading