Skip to content
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
2 changes: 1 addition & 1 deletion pkg/gofr/http/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type Middleware func(handler http.Handler) http.Handler

// NewRouter creates a new Router instance.
func NewRouter() *Router {
muxRouter := mux.NewRouter().StrictSlash(false)
muxRouter := mux.NewRouter().StrictSlash(true)
routes := make([]string, 0)
r := &Router{
Router: *muxRouter,
Expand Down
200 changes: 22 additions & 178 deletions pkg/gofr/http/router_test.go
Original file line number Diff line number Diff line change
@@ -1,196 +1,40 @@
package http

import (
"fmt"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"

"github.com/stretchr/testify/assert"

"gofr.dev/pkg/gofr/config"
"gofr.dev/pkg/gofr/container"
"gofr.dev/pkg/gofr/logging"
"gofr.dev/pkg/gofr/testutil"
)

func TestRouter(t *testing.T) {
port := testutil.GetFreePort(t)

cfg := map[string]string{"HTTP_PORT": fmt.Sprint(port), "LOG_LEVEL": "INFO"}
c := container.NewContainer(config.NewMockConfig(cfg))

c.Metrics().NewCounter("test-counter", "test")

// Create a new router instance using the mock container
router := NewRouter()

// Add a test handler to the router
router.Add(http.MethodGet, "/test", http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
}))

// Send a request to the test handler
req := httptest.NewRequest(http.MethodGet, "/test", http.NoBody)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)

// Verify the response
assert.Equal(t, http.StatusOK, rec.Code)
}

func TestRouterWithMiddleware(t *testing.T) {
port := testutil.GetFreePort(t)

cfg := map[string]string{"HTTP_PORT": fmt.Sprint(port), "LOG_LEVEL": "INFO"}
c := container.NewContainer(config.NewMockConfig(cfg))

c.Metrics().NewCounter("test-counter", "test")

// Create a new router instance using the mock container
func TestDoubleSlashRouting(t *testing.T) {
router := NewRouter()

router.UseMiddleware(func(inner http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Test-Middleware", "applied")
inner.ServeHTTP(w, r)
})
getHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

Check failure on line 12 in pkg/gofr/http/router_test.go

View workflow job for this annotation

GitHub Actions / Code Quality🎖️

unused-parameter: parameter 'r' seems to be unused, consider removing or renaming it as _ (revive)
w.Write([]byte("GET"))

Check failure on line 13 in pkg/gofr/http/router_test.go

View workflow job for this annotation

GitHub Actions / Code Quality🎖️

Error return value of `w.Write` is not checked (errcheck)
})

Check failure on line 15 in pkg/gofr/http/router_test.go

View workflow job for this annotation

GitHub Actions / Code Quality🎖️

File is not properly formatted (gci)
postHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

Check failure on line 16 in pkg/gofr/http/router_test.go

View workflow job for this annotation

GitHub Actions / Code Quality🎖️

unused-parameter: parameter 'r' seems to be unused, consider removing or renaming it as _ (revive)
w.Write([]byte("POST"))

Check failure on line 17 in pkg/gofr/http/router_test.go

View workflow job for this annotation

GitHub Actions / Code Quality🎖️

Error return value of `w.Write` is not checked (errcheck)
})

// Add a test handler to the router
router.Add(http.MethodGet, "/test", http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
}))

// Send a request to the test handler
req := httptest.NewRequest(http.MethodGet, "/test", http.NoBody)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)

// Verify the response
assert.Equal(t, http.StatusOK, rec.Code)
// checking if the testMiddleware has added the required header in the response properly.
testHeaderValue := rec.Header().Get("X-Test-Middleware")
assert.Equal(t, "applied", testHeaderValue, "Test_UseMiddleware Failed! header value mismatch.")
}
router.Add("GET", "/hello", getHandler)
router.Add("POST", "/hello", postHandler)

func Test_StaticFileServing_Static(t *testing.T) {
tempDir := t.TempDir()
// Test POST with double slash - should redirect with 301
req := httptest.NewRequest("POST", "//hello", nil)

Check failure on line 24 in pkg/gofr/http/router_test.go

View workflow job for this annotation

GitHub Actions / Code Quality🎖️

httpNoBody: http.NoBody should be preferred to the nil request body (gocritic)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)

testCases := []struct {
name string
setupFiles func() error
path string
staticServerPath string
expectedCode int
expectedBody string
}{
{
name: "Serve existing file from /static",
setupFiles: func() error {
return os.WriteFile(filepath.Join(tempDir, "test.txt"), []byte("Hello, World!"), 0600)
},
path: "/static/test.txt",
staticServerPath: "/static",
expectedCode: http.StatusOK,
expectedBody: "Hello, World!",
},
{
name: "Serve existing file from /",
setupFiles: func() error {
return os.WriteFile(filepath.Join(tempDir, "test.txt"), []byte("Hello, Root!"), 0600)
},
path: "/test.txt",
staticServerPath: "/",
expectedCode: http.StatusOK,
expectedBody: "Hello, Root!",
},
{
name: "Serve existing file from /public",
setupFiles: func() error {
return os.WriteFile(filepath.Join(tempDir, "test.txt"), []byte("Hello, Public!"), 0600)
},
path: "/public/test.txt",
staticServerPath: "/public",
expectedCode: http.StatusOK,
expectedBody: "Hello, Public!",
},
{
name: "Serve 404.html for non-existent file",
setupFiles: func() error {
return os.WriteFile(filepath.Join(tempDir, "404.html"), []byte("<html>404 Not Found</html>"), 0600)
},
path: "/static/nonexistent.html",
staticServerPath: "/static",
expectedCode: http.StatusNotFound,
expectedBody: "<html>404 Not Found</html>",
},
{
name: "Serve default 404 message when 404.html is missing",
setupFiles: func() error {
return os.Remove(filepath.Join(tempDir, "404.html"))
},
path: "/static/nonexistent.html",
staticServerPath: "/static",
expectedCode: http.StatusNotFound,
expectedBody: "404 Not Found",
},
{
name: "Access forbidden OpenAPI JSON",
setupFiles: func() error {
return os.WriteFile(filepath.Join(tempDir, DefaultSwaggerFileName), []byte(`{"openapi": "3.0.0"}`), 0600)
},
path: "/static/openapi.json",
staticServerPath: "/static",
expectedCode: http.StatusForbidden,
expectedBody: "403 Forbidden",
},
{
name: "Serving File with no Read permission",
setupFiles: func() error {
return os.WriteFile(filepath.Join(tempDir, "restricted.txt"), []byte("Restricted content"), 0000)
},
path: "/static/restricted.txt",
staticServerPath: "/static",
expectedCode: http.StatusInternalServerError,
expectedBody: "500 Internal Server Error",
},
if w.Code != http.StatusMovedPermanently {
t.Errorf("Expected 301 for POST //hello, got %d", w.Code)
}

runStaticFileTests(t, tempDir, testCases)
}

func runStaticFileTests(t *testing.T, tempDir string, testCases []struct {
name string
setupFiles func() error
path string
staticServerPath string
expectedCode int
expectedBody string
}) {
t.Helper()

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if err := tc.setupFiles(); err != nil {
t.Fatalf("Failed to set up files: %v", err)
}

logger := logging.NewMockLogger(logging.DEBUG)

router := NewRouter()
router.AddStaticFiles(logger, tc.staticServerPath, tempDir)

req := httptest.NewRequest(http.MethodGet, tc.path, http.NoBody)
w := httptest.NewRecorder()

router.ServeHTTP(w, req)
// Test GET with double slash - should redirect with 301
req2 := httptest.NewRequest("GET", "//hello", nil)

Check failure on line 33 in pkg/gofr/http/router_test.go

View workflow job for this annotation

GitHub Actions / Code Quality🎖️

httpNoBody: http.NoBody should be preferred to the nil request body (gocritic)
w2 := httptest.NewRecorder()
router.ServeHTTP(w2, req2)

assert.Equal(t, tc.expectedCode, w.Code)
assert.Equal(t, tc.expectedBody, strings.TrimSpace(w.Body.String()))
})
if w2.Code != http.StatusMovedPermanently {
t.Errorf("Expected 301 for GET //hello, got %d", w2.Code)
}
}
}
Loading