Files
godoxy/internal/api/v1/route/playground_test.go

230 lines
6.0 KiB
Go

package routeApi
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
)
func TestPlayground(t *testing.T) {
gin.SetMode(gin.TestMode)
tests := []struct {
name string
request PlaygroundRequest
wantStatusCode int
checkResponse func(t *testing.T, resp PlaygroundResponse)
}{
{
name: "simple path matching rule",
request: PlaygroundRequest{
Rules: []RawRule{
{
Name: "test rule",
On: "path /api",
Do: "pass",
},
},
MockRequest: MockRequest{
Method: "GET",
Path: "/api",
},
},
wantStatusCode: http.StatusOK,
checkResponse: func(t *testing.T, resp PlaygroundResponse) {
if len(resp.ParsedRules) != 1 {
t.Errorf("expected 1 parsed rule, got %d", len(resp.ParsedRules))
}
if resp.ParsedRules[0].ValidationError != nil {
t.Errorf("expected rule to be valid, got error: %v", resp.ParsedRules[0].ValidationError)
}
if len(resp.MatchedRules) != 1 || resp.MatchedRules[0] != "test rule" {
t.Errorf("expected matched rules to be ['test rule'], got %v", resp.MatchedRules)
}
if !resp.UpstreamCalled {
t.Error("expected upstream to be called")
}
},
},
{
name: "header matching rule",
request: PlaygroundRequest{
Rules: []RawRule{
{
Name: "check user agent",
On: "header User-Agent Chrome",
Do: "error 403 Forbidden",
},
},
MockRequest: MockRequest{
Method: "GET",
Path: "/",
Headers: map[string][]string{
"User-Agent": {"Chrome"},
},
},
},
wantStatusCode: http.StatusOK,
checkResponse: func(t *testing.T, resp PlaygroundResponse) {
if len(resp.ParsedRules) != 1 {
t.Errorf("expected 1 parsed rule, got %d", len(resp.ParsedRules))
}
if resp.ParsedRules[0].ValidationError != nil {
t.Errorf("expected rule to be valid, got error: %v", resp.ParsedRules[0].ValidationError)
}
if len(resp.MatchedRules) != 1 {
t.Errorf("expected 1 matched rule, got %d", len(resp.MatchedRules))
}
if resp.FinalResponse.StatusCode != http.StatusForbidden {
t.Errorf("expected status 403, got %d", resp.FinalResponse.StatusCode)
}
if resp.UpstreamCalled {
t.Error("expected upstream not to be called")
}
},
},
{
name: "invalid rule syntax",
request: PlaygroundRequest{
Rules: []RawRule{
{
Name: "bad rule",
On: "invalid_checker something",
Do: "pass",
},
},
MockRequest: MockRequest{
Method: "GET",
Path: "/",
},
},
wantStatusCode: http.StatusOK,
checkResponse: func(t *testing.T, resp PlaygroundResponse) {
if len(resp.ParsedRules) != 1 {
t.Errorf("expected 1 parsed rule, got %d", len(resp.ParsedRules))
}
if resp.ParsedRules[0].ValidationError == nil {
t.Error("expected validation error to be set")
}
},
},
{
name: "rewrite path rule",
request: PlaygroundRequest{
Rules: []RawRule{
{
Name: "rewrite rule",
On: "path glob(/api/*)",
Do: "rewrite /api/ /v1/",
},
},
MockRequest: MockRequest{
Method: "GET",
Path: "/api/users",
},
},
wantStatusCode: http.StatusOK,
checkResponse: func(t *testing.T, resp PlaygroundResponse) {
if len(resp.ParsedRules) != 1 {
t.Errorf("expected 1 parsed rule, got %d", len(resp.ParsedRules))
}
if resp.ParsedRules[0].ValidationError != nil {
t.Errorf("expected rule to be valid, got error: %v", resp.ParsedRules[0].ValidationError)
}
if !resp.UpstreamCalled {
t.Error("expected upstream to be called")
}
if resp.FinalRequest.Path != "/v1/users" {
t.Errorf("expected path to be rewritten to /v1/users, got %s", resp.FinalRequest.Path)
}
// Note: matched rules tracking has limitations with fresh ResponseModifier
// The important thing is that the rewrite actually worked
},
},
{
name: "method matching rule",
request: PlaygroundRequest{
Rules: []RawRule{
{
Name: "block POST",
On: "method POST",
Do: `error "405" "Method Not Allowed"`,
},
},
MockRequest: MockRequest{
Method: "POST",
Path: "/api",
},
},
wantStatusCode: http.StatusOK,
checkResponse: func(t *testing.T, resp PlaygroundResponse) {
if resp.ParsedRules[0].ValidationError != nil {
t.Errorf("expected rule to be valid, got error: %v", resp.ParsedRules[0].ValidationError)
}
if len(resp.MatchedRules) != 1 {
t.Errorf("expected 1 matched rule, got %d", len(resp.MatchedRules))
}
if resp.FinalResponse.StatusCode != http.StatusMethodNotAllowed {
t.Errorf("expected status 405, got %d", resp.FinalResponse.StatusCode)
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create request
body, _ := json.Marshal(tt.request)
req := httptest.NewRequest(http.MethodPost, "/api/v1/route/playground", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
// Create response recorder
w := httptest.NewRecorder()
// Create gin context
c, _ := gin.CreateTestContext(w)
c.Request = req
// Call handler
Playground(c)
// Check status code
if w.Code != tt.wantStatusCode {
t.Errorf("expected status code %d, got %d", tt.wantStatusCode, w.Code)
}
respAny, ok := c.Get("response")
if !ok {
t.Fatalf("expected response to be set")
}
resp := respAny.(PlaygroundResponse)
// Run custom checks
if tt.checkResponse != nil {
tt.checkResponse(t, resp)
}
})
}
}
func TestPlaygroundInvalidRequest(t *testing.T) {
gin.SetMode(gin.TestMode)
req := httptest.NewRequest(http.MethodPost, "/api/v1/route/playground", bytes.NewReader([]byte(`{}`)))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = req
Playground(c)
if w.Code != http.StatusBadRequest {
t.Errorf("expected status code %d, got %d", http.StatusBadRequest, w.Code)
}
}