mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-27 18:57:04 +02:00
feat: middleware bypass overlay (#221)
* **New Features** * Routes can promote route-local bypass rules into matching entrypoint middleware, layering route-specific bypasses onto existing entrypoint rules and avoiding duplicate evaluation. * **Behavior Changes** * Entrypoint middleware updates now refresh per-route overlays at runtime; overlay compilation failures result in HTTP 500 (errors are not exposed verbatim). * Route middleware accessors now return safe clones. * **Documentation** * Clarified promotion, consumption, merging and qualification semantics with examples. * **Tests** * Added tests covering promotion, cache invalidation, consumption semantics, and error handling. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
109
internal/net/gphttp/middleware/entrypoint_overlay_test.go
Normal file
109
internal/net/gphttp/middleware/entrypoint_overlay_test.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
"github.com/yusing/godoxy/internal/route/rules"
|
||||
)
|
||||
|
||||
func TestBuildEntrypointRouteOverlayReturnsSentinelWhenNoPromotionOccurs(t *testing.T) {
|
||||
t.Run("no_matching_entrypoint_middleware", func(t *testing.T) {
|
||||
overlay, err := BuildEntrypointRouteOverlay(
|
||||
"entrypoint",
|
||||
[]map[string]any{{
|
||||
"use": "response",
|
||||
}},
|
||||
"test-route",
|
||||
map[string]OptionsRaw{
|
||||
"redirectHTTP": {
|
||||
"bypass": []string{"path /health"},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
require.Nil(t, overlay)
|
||||
require.ErrorIs(t, err, ErrNoEntrypointRouteOverlay)
|
||||
})
|
||||
|
||||
t.Run("empty_route_middlewares", func(t *testing.T) {
|
||||
overlay, err := BuildEntrypointRouteOverlay(
|
||||
"entrypoint",
|
||||
[]map[string]any{{
|
||||
"use": "response",
|
||||
}},
|
||||
"test-route",
|
||||
nil,
|
||||
)
|
||||
|
||||
require.Nil(t, overlay)
|
||||
require.ErrorIs(t, err, ErrNoEntrypointRouteOverlay)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildEntrypointRouteOverlayPromotesRouteBypass(t *testing.T) {
|
||||
overlay, err := BuildEntrypointRouteOverlay(
|
||||
"entrypoint",
|
||||
[]map[string]any{{
|
||||
"use": "redirectHTTP",
|
||||
}},
|
||||
"test-route",
|
||||
map[string]OptionsRaw{
|
||||
"redirectHTTP": {
|
||||
"bypass": []string{"path /health"},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, overlay)
|
||||
require.NotNil(t, overlay.Middleware)
|
||||
require.Contains(t, overlay.ConsumedBypass, "redirecthttp")
|
||||
require.Contains(t, overlay.ConsumedMiddlewares, "redirecthttp")
|
||||
}
|
||||
|
||||
func TestBuildEntrypointRouteOverlayKeepsNonBypassRouteMiddlewareActive(t *testing.T) {
|
||||
overlay, err := BuildEntrypointRouteOverlay(
|
||||
"entrypoint",
|
||||
[]map[string]any{{
|
||||
"use": "redirectHTTP",
|
||||
}},
|
||||
"test-route",
|
||||
map[string]OptionsRaw{
|
||||
"redirectHTTP": {
|
||||
"bypass": []string{"path /health"},
|
||||
"redirectHTTP": "https://example.com",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, overlay)
|
||||
require.NotNil(t, overlay.Middleware)
|
||||
require.Contains(t, overlay.ConsumedBypass, "redirecthttp")
|
||||
require.Empty(t, overlay.ConsumedMiddlewares)
|
||||
}
|
||||
|
||||
func TestQualifyBypassWithRoutePreservesCompositeRuleSemantics(t *testing.T) {
|
||||
var composite rules.RuleOn
|
||||
require.NoError(t, composite.Parse("path /health | path /status"))
|
||||
|
||||
qualified, err := qualifyBypassWithRoute("test-route", Bypass{composite})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, qualified, 1)
|
||||
|
||||
matches := func(path, routeName string) bool {
|
||||
req := httptest.NewRequest("GET", "http://example.com"+path, nil)
|
||||
if routeName != "" {
|
||||
req = routes.WithRouteContext(req, fakeMiddlewareHTTPRoute{name: routeName})
|
||||
}
|
||||
return qualified[0].Check(httptest.NewRecorder(), req)
|
||||
}
|
||||
|
||||
require.True(t, matches("/health", "test-route"))
|
||||
require.True(t, matches("/status", "test-route"))
|
||||
require.False(t, matches("/health", "other-route"))
|
||||
require.False(t, matches("/metrics", "test-route"))
|
||||
}
|
||||
Reference in New Issue
Block a user