mirror of
https://github.com/gofiber/fiber.git
synced 2025-09-04 19:35:47 +00:00
705 lines
21 KiB
Go
705 lines
21 KiB
Go
package extractors
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/gofiber/fiber/v3"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/valyala/fasthttp"
|
|
)
|
|
|
|
// go test -run Test_Extractors_Missing
|
|
func Test_Extractors_Missing(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
// Add a route to test the missing param
|
|
app.Get("/test", func(c fiber.Ctx) error {
|
|
token, err := FromParam("token").Extract(c)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
return nil
|
|
})
|
|
_, err := app.Test(newRequest(fiber.MethodGet, "/test"))
|
|
require.NoError(t, err)
|
|
|
|
ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx)
|
|
|
|
// Missing form
|
|
token, err := FromForm("token").Extract(ctx)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// Missing query
|
|
token, err = FromQuery("token").Extract(ctx)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// Missing header
|
|
token, err = FromHeader("X-Token").Extract(ctx)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// Missing Auth header
|
|
token, err = FromAuthHeader("Bearer").Extract(ctx)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// Missing cookie
|
|
token, err = FromCookie("token").Extract(ctx)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
}
|
|
|
|
// newRequest creates a new *http.Request for Fiber's app.Test
|
|
func newRequest(method, target string) *http.Request {
|
|
req, err := http.NewRequestWithContext(context.Background(), method, target, nil)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return req
|
|
}
|
|
|
|
// go test -run Test_Extractors
|
|
func Test_Extractors(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
|
|
// FromParam
|
|
app.Get("/test/:token", func(c fiber.Ctx) error {
|
|
token, err := FromParam("token").Extract(c)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "token_from_param", token)
|
|
return nil
|
|
})
|
|
_, err := app.Test(newRequest(fiber.MethodGet, "/test/token_from_param"))
|
|
require.NoError(t, err)
|
|
|
|
// FromForm
|
|
ctx1 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx1)
|
|
ctx1.Request().Header.SetContentType(fiber.MIMEApplicationForm)
|
|
ctx1.Request().Header.SetMethod(fiber.MethodPost)
|
|
ctx1.Request().SetBodyString("token=token_from_form")
|
|
token, err := FromForm("token").Extract(ctx1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "token_from_form", token)
|
|
|
|
// FromQuery
|
|
ctx2 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx2)
|
|
ctx2.Request().SetRequestURI("/?token=token_from_query")
|
|
token, err = FromQuery("token").Extract(ctx2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "token_from_query", token)
|
|
|
|
// FromHeader
|
|
ctx3 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx3)
|
|
ctx3.Request().Header.Set("X-Token", "token_from_header")
|
|
token, err = FromHeader("X-Token").Extract(ctx3)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "token_from_header", token)
|
|
|
|
// FromAuthHeader
|
|
ctx4 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx4)
|
|
ctx4.Request().Header.Set(fiber.HeaderAuthorization, "Bearer token_from_auth_header")
|
|
token, err = FromAuthHeader("Bearer").Extract(ctx4)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "token_from_auth_header", token)
|
|
|
|
// FromCookie
|
|
ctx5 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx5)
|
|
ctx5.Request().Header.SetCookie("token", "token_from_cookie")
|
|
token, err = FromCookie("token").Extract(ctx5)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "token_from_cookie", token)
|
|
}
|
|
|
|
// go test -run Test_Extractor_Chain
|
|
func Test_Extractor_Chain(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
|
|
// No extractors
|
|
ctx1 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx1)
|
|
token, err := Chain().Extract(ctx1)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// First extractor succeeds
|
|
ctx2 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx2)
|
|
ctx2.Request().Header.Set("X-Token", "token_from_header")
|
|
ctx2.Request().SetRequestURI("/?token=token_from_query")
|
|
token, err = Chain(FromHeader("X-Token"), FromQuery("token")).Extract(ctx2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "token_from_header", token)
|
|
|
|
// Second extractor succeeds
|
|
ctx3 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx3)
|
|
ctx3.Request().SetRequestURI("/?token=token_from_query")
|
|
token, err = Chain(FromHeader("X-Token"), FromQuery("token")).Extract(ctx3)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "token_from_query", token)
|
|
|
|
// All extractors fail, should return the last error
|
|
ctx4 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx4)
|
|
token, err = Chain(FromHeader("X-Token"), FromQuery("token")).Extract(ctx4)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// All extractors find nothing (return empty string and nil error), should return ErrNotFound
|
|
ctx5 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx5)
|
|
// This extractor will return "", nil
|
|
dummyExtractor := Extractor{
|
|
Extract: func(_ fiber.Ctx) (string, error) {
|
|
return "", nil
|
|
},
|
|
Source: SourceCustom,
|
|
Key: "token",
|
|
}
|
|
token, err = Chain(dummyExtractor).Extract(ctx5)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
}
|
|
|
|
// go test -run Test_Extractor_FromAuthHeader_EdgeCases
|
|
func Test_Extractor_FromAuthHeader_EdgeCases(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
|
|
// Test case: Authorization header exists but doesn't match the expected scheme
|
|
ctx1 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx1)
|
|
ctx1.Request().Header.Set(fiber.HeaderAuthorization, "Basic dXNlcjpwYXNz") // Basic auth instead of Bearer
|
|
token, err := FromAuthHeader("Bearer").Extract(ctx1)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// Test case: Authorization header exists but has wrong format
|
|
ctx2 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx2)
|
|
ctx2.Request().Header.Set(fiber.HeaderAuthorization, "Bearertoken") // Missing space after Bearer
|
|
token, err = FromAuthHeader("Bearer").Extract(ctx2)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// Test case: Authorization header exists but scheme doesn't match case-insensitively
|
|
ctx3 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx3)
|
|
ctx3.Request().Header.Set(fiber.HeaderAuthorization, "bearer token") // lowercase bearer
|
|
token, err = FromAuthHeader("Bearer").Extract(ctx3)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "token", token)
|
|
}
|
|
|
|
// go test -run Test_Extractor_Chain_Introspection
|
|
func Test_Extractor_Chain_Introspection(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Test chain introspection
|
|
extractor1 := FromHeader("X-Token")
|
|
extractor2 := FromQuery("token")
|
|
extractor3 := FromCookie("auth")
|
|
|
|
chainExtractor := Chain(extractor1, extractor2, extractor3)
|
|
|
|
// Verify chain metadata
|
|
require.Equal(t, SourceHeader, chainExtractor.Source)
|
|
require.Equal(t, "X-Token", chainExtractor.Key)
|
|
require.Len(t, chainExtractor.Chain, 3)
|
|
|
|
// Verify individual extractors in chain
|
|
require.Equal(t, SourceHeader, chainExtractor.Chain[0].Source)
|
|
require.Equal(t, "X-Token", chainExtractor.Chain[0].Key)
|
|
require.Equal(t, SourceQuery, chainExtractor.Chain[1].Source)
|
|
require.Equal(t, "token", chainExtractor.Chain[1].Key)
|
|
require.Equal(t, SourceCookie, chainExtractor.Chain[2].Source)
|
|
require.Equal(t, "auth", chainExtractor.Chain[2].Key)
|
|
}
|
|
|
|
// go test -run Test_Extractor_FromCustom
|
|
func Test_Extractor_FromCustom(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
|
|
// Test successful extraction with FromCustom
|
|
ctx1 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx1)
|
|
ctx1.Request().Header.Set("X-Custom", "custom-value")
|
|
|
|
customExtractor := FromCustom("X-Custom", func(c fiber.Ctx) (string, error) {
|
|
value := c.Get("X-Custom")
|
|
if value == "" {
|
|
return "", ErrNotFound
|
|
}
|
|
return strings.ToUpper(value), nil
|
|
})
|
|
|
|
token, err := customExtractor.Extract(ctx1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "CUSTOM-VALUE", token)
|
|
|
|
// Verify metadata
|
|
require.Equal(t, SourceCustom, customExtractor.Source)
|
|
require.Equal(t, "X-Custom", customExtractor.Key)
|
|
require.Equal(t, "", customExtractor.AuthScheme)
|
|
|
|
// Test FromCustom with error
|
|
ctx2 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx2)
|
|
|
|
errorExtractor := FromCustom("test", func(_ fiber.Ctx) (string, error) {
|
|
return "", fiber.NewError(fiber.StatusBadRequest, "Custom error")
|
|
})
|
|
|
|
token, err = errorExtractor.Extract(ctx2)
|
|
require.Empty(t, token)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "Custom error")
|
|
|
|
// Test FromCustom returning empty string
|
|
ctx3 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx3)
|
|
|
|
emptyExtractor := FromCustom("empty", func(_ fiber.Ctx) (string, error) {
|
|
return "", nil
|
|
})
|
|
|
|
token, err = emptyExtractor.Extract(ctx3)
|
|
require.Empty(t, token)
|
|
require.NoError(t, err) // Should return empty string with no error
|
|
|
|
// Test FromCustom with nil function
|
|
nilExtractor := FromCustom("nil", nil)
|
|
|
|
token, err = nilExtractor.Extract(ctx3)
|
|
require.Empty(t, token)
|
|
require.ErrorIs(t, err, ErrNotFound) // Should return ErrNotFound for nil function
|
|
}
|
|
|
|
// go test -run Test_Extractor_Chain_Error_Propagation
|
|
func Test_Extractor_Chain_Error_Propagation(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
|
|
// Create extractors that return different errors
|
|
errorExtractor1 := Extractor{
|
|
Extract: func(_ fiber.Ctx) (string, error) {
|
|
return "", fiber.NewError(fiber.StatusBadRequest, "First error")
|
|
},
|
|
Key: "error1",
|
|
Source: SourceCustom,
|
|
}
|
|
|
|
errorExtractor2 := Extractor{
|
|
Extract: func(_ fiber.Ctx) (string, error) {
|
|
return "", fiber.NewError(fiber.StatusUnauthorized, "Second error")
|
|
},
|
|
Key: "error2",
|
|
Source: SourceCustom,
|
|
}
|
|
|
|
chainExtractor := Chain(errorExtractor1, errorExtractor2)
|
|
|
|
ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx)
|
|
|
|
token, err := chainExtractor.Extract(ctx)
|
|
require.Empty(t, token)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "Second error") // Should return the last error
|
|
var fe *fiber.Error
|
|
require.ErrorAs(t, err, &fe)
|
|
require.Equal(t, fiber.StatusUnauthorized, fe.Code)
|
|
}
|
|
|
|
// go test -run Test_Extractor_Chain_With_Success
|
|
func Test_Extractor_Chain_With_Success(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
|
|
// First extractor fails, second succeeds
|
|
failingExtractor := Extractor{
|
|
Extract: func(_ fiber.Ctx) (string, error) {
|
|
return "", ErrNotFound
|
|
},
|
|
Key: "fail",
|
|
Source: SourceCustom,
|
|
}
|
|
|
|
successExtractor := Extractor{
|
|
Extract: func(_ fiber.Ctx) (string, error) {
|
|
return "success-token", nil
|
|
},
|
|
Key: "success",
|
|
Source: SourceCustom,
|
|
}
|
|
|
|
chainExtractor := Chain(failingExtractor, successExtractor)
|
|
|
|
ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx)
|
|
|
|
token, err := chainExtractor.Extract(ctx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "success-token", token)
|
|
}
|
|
|
|
// go test -run Test_Extractor_FromAuthHeader_CustomScheme
|
|
func Test_Extractor_FromAuthHeader_CustomScheme(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
|
|
// Test with custom auth scheme
|
|
ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx)
|
|
ctx.Request().Header.Set(fiber.HeaderAuthorization, "CustomScheme my-token")
|
|
|
|
extractor := FromAuthHeader("CustomScheme")
|
|
token, err := extractor.Extract(ctx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "my-token", token)
|
|
|
|
// Verify metadata
|
|
require.Equal(t, SourceAuthHeader, extractor.Source)
|
|
require.Equal(t, fiber.HeaderAuthorization, extractor.Key)
|
|
require.Equal(t, "CustomScheme", extractor.AuthScheme)
|
|
}
|
|
|
|
// go test -run Test_Extractor_FromAuthHeader_WhitespaceToken
|
|
func Test_Extractor_FromAuthHeader_WhitespaceToken(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
|
|
// Test with token containing whitespace (should be preserved)
|
|
ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx)
|
|
ctx.Request().Header.Set(fiber.HeaderAuthorization, "Bearer token with spaces and\ttabs")
|
|
|
|
extractor := FromAuthHeader("Bearer")
|
|
token, err := extractor.Extract(ctx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "token with spaces and\ttabs", token)
|
|
|
|
// Verify metadata
|
|
require.Equal(t, SourceAuthHeader, extractor.Source)
|
|
require.Equal(t, fiber.HeaderAuthorization, extractor.Key)
|
|
require.Equal(t, "Bearer", extractor.AuthScheme)
|
|
}
|
|
|
|
// go test -run Test_Extractor_FromAuthHeader_RFC_Compliance
|
|
func Test_Extractor_FromAuthHeader_RFC_Compliance(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
|
|
// Test RFC 7235: Tab character after scheme (should be accepted)
|
|
ctx1 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx1)
|
|
ctx1.Request().Header.Set(fiber.HeaderAuthorization, "Bearer\ttoken") // Tab after Bearer
|
|
token, err := FromAuthHeader("Bearer").Extract(ctx1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "token", token)
|
|
|
|
// Test RFC 7235: Multiple spaces after scheme
|
|
ctx2 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx2)
|
|
ctx2.Request().Header.Set(fiber.HeaderAuthorization, "Bearer token") // Multiple spaces
|
|
token, err = FromAuthHeader("Bearer").Extract(ctx2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "token", token)
|
|
|
|
// Test RFC 7235: Mixed whitespace after scheme
|
|
ctx3 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx3)
|
|
ctx3.Request().Header.Set(fiber.HeaderAuthorization, "Bearer \t \ttoken") // Space + tabs
|
|
token, err = FromAuthHeader("Bearer").Extract(ctx3)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "token", token)
|
|
|
|
// Test RFC 7235: No whitespace after scheme (should fail)
|
|
ctx4 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx4)
|
|
ctx4.Request().Header.Set(fiber.HeaderAuthorization, "Bearertoken") // No space
|
|
token, err = FromAuthHeader("Bearer").Extract(ctx4)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// Test RFC 7235: Header too short for scheme + space + token
|
|
ctx5 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx5)
|
|
ctx5.Request().Header.Set(fiber.HeaderAuthorization, "Bearer") // Just scheme, no space or token
|
|
token, err = FromAuthHeader("Bearer").Extract(ctx5)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// Test RFC 7235: Only whitespace after scheme (should fail)
|
|
ctx6 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx6)
|
|
ctx6.Request().Header.Set(fiber.HeaderAuthorization, "Bearer \t ") // Only whitespace
|
|
token, err = FromAuthHeader("Bearer").Extract(ctx6)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// Test RFC 7235: Case-insensitive scheme matching
|
|
ctx7 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx7)
|
|
ctx7.Request().Header.Set(fiber.HeaderAuthorization, "BEARER token") // Uppercase
|
|
token, err = FromAuthHeader("bearer").Extract(ctx7)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "token", token)
|
|
}
|
|
|
|
// go test -run Test_Extractor_FromAuthHeader_NoScheme
|
|
func Test_Extractor_FromAuthHeader_NoScheme(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
|
|
// Test with no auth scheme (empty string) - should return trimmed header value
|
|
ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx)
|
|
ctx.Request().Header.Set(fiber.HeaderAuthorization, " some-token-value ")
|
|
|
|
extractor := FromAuthHeader("") // No scheme
|
|
token, err := extractor.Extract(ctx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "some-token-value", token)
|
|
|
|
// Verify metadata
|
|
require.Equal(t, SourceAuthHeader, extractor.Source)
|
|
require.Equal(t, fiber.HeaderAuthorization, extractor.Key)
|
|
require.Equal(t, "", extractor.AuthScheme)
|
|
|
|
// Test with no auth scheme and empty header (should return ErrNotFound)
|
|
ctx2 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx2)
|
|
// No Authorization header set
|
|
|
|
token, err = extractor.Extract(ctx2)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// Test with no auth scheme and whitespace-only header (should return ErrNotFound)
|
|
ctx3 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx3)
|
|
ctx3.Request().Header.Set(fiber.HeaderAuthorization, " \t ") // Only whitespace
|
|
|
|
token, err = extractor.Extract(ctx3)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
}
|
|
|
|
// go test -run Test_Extractor_EdgeCases
|
|
func Test_Extractor_EdgeCases(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
|
|
// Test empty/whitespace-only values
|
|
ctx1 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx1)
|
|
ctx1.Request().Header.Set("X-Empty", " \t ") // Only whitespace
|
|
|
|
token, err := FromHeader("X-Empty").Extract(ctx1)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// Test cookie with only whitespace
|
|
ctx2 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx2)
|
|
ctx2.Request().Header.SetCookie("empty", " ")
|
|
|
|
token, err = FromCookie("empty").Extract(ctx2)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// Test query param with only whitespace
|
|
ctx3 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx3)
|
|
ctx3.Request().SetRequestURI("/?param=%20%20%20") // URL-encoded spaces
|
|
|
|
token, err = FromQuery("param").Extract(ctx3)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
|
|
// Test form field with only whitespace
|
|
ctx4 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx4)
|
|
ctx4.Request().Header.SetContentType(fiber.MIMEApplicationForm)
|
|
ctx4.Request().SetBodyString("field=%20%20%20")
|
|
|
|
token, err = FromForm("field").Extract(ctx4)
|
|
require.Empty(t, token)
|
|
require.Equal(t, ErrNotFound, err)
|
|
}
|
|
|
|
// go test -run Test_Extractor_Chain_NilFunctions
|
|
func Test_Extractor_Chain_NilFunctions(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
|
|
// Test chain with nil extractor functions
|
|
nilExtractor := Extractor{
|
|
Extract: nil,
|
|
Key: "nil",
|
|
Source: SourceCustom,
|
|
}
|
|
|
|
validExtractor := Extractor{
|
|
Extract: func(_ fiber.Ctx) (string, error) {
|
|
return "valid-token", nil
|
|
},
|
|
Key: "valid",
|
|
Source: SourceCustom,
|
|
}
|
|
|
|
chainExtractor := Chain(nilExtractor, validExtractor)
|
|
|
|
ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx)
|
|
|
|
token, err := chainExtractor.Extract(ctx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "valid-token", token)
|
|
}
|
|
|
|
// go test -run Test_Extractor_Chain_AllErrors
|
|
func Test_Extractor_Chain_AllErrors(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
|
|
// Test chain where all extractors return errors
|
|
errorExtractor1 := Extractor{
|
|
Extract: func(_ fiber.Ctx) (string, error) {
|
|
return "", fiber.NewError(fiber.StatusUnauthorized, "First auth error")
|
|
},
|
|
Key: "error1",
|
|
Source: SourceCustom,
|
|
}
|
|
|
|
errorExtractor2 := Extractor{
|
|
Extract: func(_ fiber.Ctx) (string, error) {
|
|
return "", fiber.NewError(fiber.StatusForbidden, "Second auth error")
|
|
},
|
|
Key: "error2",
|
|
Source: SourceCustom,
|
|
}
|
|
|
|
chainExtractor := Chain(errorExtractor1, errorExtractor2)
|
|
|
|
ctx := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx)
|
|
|
|
token, err := chainExtractor.Extract(ctx)
|
|
require.Empty(t, token)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "Second auth error") // Should return last error
|
|
|
|
var fe *fiber.Error
|
|
require.ErrorAs(t, err, &fe)
|
|
require.Equal(t, fiber.StatusForbidden, fe.Code)
|
|
}
|
|
|
|
// go test -run Test_Extractor_Chain_MixedScenarios
|
|
func Test_Extractor_Chain_MixedScenarios(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
app := fiber.New()
|
|
|
|
// Test chain with mixed success/error scenarios
|
|
failingExtractor := Extractor{
|
|
Extract: func(_ fiber.Ctx) (string, error) {
|
|
return "", ErrNotFound
|
|
},
|
|
Key: "fail",
|
|
Source: SourceCustom,
|
|
}
|
|
|
|
errorExtractor := Extractor{
|
|
Extract: func(_ fiber.Ctx) (string, error) {
|
|
return "", fiber.NewError(fiber.StatusBadRequest, "Bad request")
|
|
},
|
|
Key: "error",
|
|
Source: SourceCustom,
|
|
}
|
|
|
|
successExtractor := Extractor{
|
|
Extract: func(_ fiber.Ctx) (string, error) {
|
|
return "success", nil
|
|
},
|
|
Key: "success",
|
|
Source: SourceCustom,
|
|
}
|
|
|
|
// Test: error then success (should return success)
|
|
chain1 := Chain(errorExtractor, successExtractor)
|
|
ctx1 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx1)
|
|
|
|
token, err := chain1.Extract(ctx1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "success", token)
|
|
|
|
// Test: fail then error then success (should return success)
|
|
chain2 := Chain(failingExtractor, errorExtractor, successExtractor)
|
|
ctx2 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx2)
|
|
|
|
token, err = chain2.Extract(ctx2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "success", token)
|
|
|
|
// Test: fail then error (should return last error)
|
|
chain3 := Chain(failingExtractor, errorExtractor)
|
|
ctx3 := app.AcquireCtx(&fasthttp.RequestCtx{})
|
|
defer app.ReleaseCtx(ctx3)
|
|
|
|
token, err = chain3.Extract(ctx3)
|
|
require.Empty(t, token)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "Bad request")
|
|
}
|
|
|
|
// go test -run Test_Extractor_SourceTypes
|
|
func Test_Extractor_SourceTypes(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Test that all source types are properly set
|
|
require.Equal(t, SourceHeader, FromHeader("test").Source)
|
|
require.Equal(t, SourceAuthHeader, FromAuthHeader("Bearer").Source)
|
|
require.Equal(t, SourceAuthHeader, FromAuthHeader("").Source) // Empty scheme should still be SourceAuthHeader
|
|
require.Equal(t, SourceForm, FromForm("test").Source)
|
|
require.Equal(t, SourceQuery, FromQuery("test").Source)
|
|
require.Equal(t, SourceParam, FromParam("test").Source)
|
|
require.Equal(t, SourceCookie, FromCookie("test").Source)
|
|
require.Equal(t, SourceCustom, FromCustom("test", func(_ fiber.Ctx) (string, error) { return "test", nil }).Source)
|
|
|
|
// Test chain source (should use first extractor's source)
|
|
chain := Chain(FromHeader("X-Test"), FromQuery("test"))
|
|
require.Equal(t, SourceHeader, chain.Source)
|
|
require.Equal(t, "X-Test", chain.Key)
|
|
}
|