226 lines
6.2 KiB
Go
226 lines
6.2 KiB
Go
package caddy
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestNormalizeAdvancedConfig_MapWithNestedHandles(t *testing.T) {
|
|
// Build a map with nested 'handle' array containing headers with string values
|
|
raw := map[string]any{
|
|
"handler": "subroute",
|
|
"routes": []any{
|
|
map[string]any{
|
|
"handle": []any{
|
|
map[string]any{
|
|
"handler": "headers",
|
|
"request": map[string]any{
|
|
"set": map[string]any{"Upgrade": "websocket"},
|
|
},
|
|
"response": map[string]any{
|
|
"set": map[string]any{"X-Obj": "1"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
out := NormalizeAdvancedConfig(raw)
|
|
// Verify nested header values normalized
|
|
outMap, ok := out.(map[string]any)
|
|
require.True(t, ok)
|
|
routes := outMap["routes"].([]any)
|
|
require.Len(t, routes, 1)
|
|
r := routes[0].(map[string]any)
|
|
handles := r["handle"].([]any)
|
|
require.Len(t, handles, 1)
|
|
hdr := handles[0].(map[string]any)
|
|
|
|
// request.set.Upgrade
|
|
req := hdr["request"].(map[string]any)
|
|
set := req["set"].(map[string]any)
|
|
// Could be []any or []string depending on code path; normalize to []string representation
|
|
switch v := set["Upgrade"].(type) {
|
|
case []any:
|
|
var outArr []string
|
|
for _, it := range v {
|
|
outArr = append(outArr, fmt.Sprintf("%v", it))
|
|
}
|
|
require.Equal(t, []string{"websocket"}, outArr)
|
|
case []string:
|
|
require.Equal(t, []string{"websocket"}, v)
|
|
default:
|
|
t.Fatalf("unexpected type for Upgrade: %T", v)
|
|
}
|
|
|
|
// response.set.X-Obj
|
|
resp := hdr["response"].(map[string]any)
|
|
rset := resp["set"].(map[string]any)
|
|
switch v := rset["X-Obj"].(type) {
|
|
case []any:
|
|
var outArr []string
|
|
for _, it := range v {
|
|
outArr = append(outArr, fmt.Sprintf("%v", it))
|
|
}
|
|
require.Equal(t, []string{"1"}, outArr)
|
|
case []string:
|
|
require.Equal(t, []string{"1"}, v)
|
|
default:
|
|
t.Fatalf("unexpected type for X-Obj: %T", v)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeAdvancedConfig_ArrayTopLevel(t *testing.T) {
|
|
// Top-level array containing a headers handler with array value as []any
|
|
raw := []any{
|
|
map[string]any{
|
|
"handler": "headers",
|
|
"response": map[string]any{
|
|
"set": map[string]any{"X-Obj": []any{"1"}},
|
|
},
|
|
},
|
|
}
|
|
out := NormalizeAdvancedConfig(raw)
|
|
outArr := out.([]any)
|
|
require.Len(t, outArr, 1)
|
|
hdr := outArr[0].(map[string]any)
|
|
resp := hdr["response"].(map[string]any)
|
|
set := resp["set"].(map[string]any)
|
|
switch v := set["X-Obj"].(type) {
|
|
case []any:
|
|
var outArr2 []string
|
|
for _, it := range v {
|
|
outArr2 = append(outArr2, fmt.Sprintf("%v", it))
|
|
}
|
|
require.Equal(t, []string{"1"}, outArr2)
|
|
case []string:
|
|
require.Equal(t, []string{"1"}, v)
|
|
default:
|
|
t.Fatalf("unexpected type for X-Obj: %T", v)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeAdvancedConfig_DefaultPrimitives(t *testing.T) {
|
|
// Ensure primitive values remain unchanged
|
|
v := NormalizeAdvancedConfig(42)
|
|
require.Equal(t, 42, v)
|
|
v2 := NormalizeAdvancedConfig("hello")
|
|
require.Equal(t, "hello", v2)
|
|
}
|
|
|
|
func TestNormalizeAdvancedConfig_CoerceNonStandardTypes(t *testing.T) {
|
|
// Use a header value that is numeric and ensure it's coerced to string
|
|
raw := map[string]any{"handler": "headers", "response": map[string]any{"set": map[string]any{"X-Num": 1}}}
|
|
out := NormalizeAdvancedConfig(raw).(map[string]any)
|
|
resp := out["response"].(map[string]any)
|
|
set := resp["set"].(map[string]any)
|
|
// Should be a []string with "1"
|
|
switch v := set["X-Num"].(type) {
|
|
case []any:
|
|
var outArr []string
|
|
for _, it := range v {
|
|
outArr = append(outArr, fmt.Sprintf("%v", it))
|
|
}
|
|
require.Equal(t, []string{"1"}, outArr)
|
|
case []string:
|
|
require.Equal(t, []string{"1"}, v)
|
|
default:
|
|
t.Fatalf("unexpected type for X-Num: %T", v)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeAdvancedConfig_JSONRoundtrip(t *testing.T) {
|
|
// Ensure normalized config can be marshaled back to JSON and unmarshaled
|
|
raw := map[string]any{"handler": "headers", "request": map[string]any{"set": map[string]any{"Upgrade": "websocket"}}}
|
|
out := NormalizeAdvancedConfig(raw)
|
|
b, err := json.Marshal(out)
|
|
require.NoError(t, err)
|
|
// Marshal back and read result
|
|
var parsed any
|
|
require.NoError(t, json.Unmarshal(b, &parsed))
|
|
}
|
|
|
|
func TestNormalizeAdvancedConfig_TopLevelHeaders(t *testing.T) {
|
|
// Top-level 'headers' key should be normalized similar to request/response
|
|
raw := map[string]any{
|
|
"handler": "headers",
|
|
"headers": map[string]any{
|
|
"set": map[string]any{"Upgrade": "websocket"},
|
|
},
|
|
}
|
|
out := NormalizeAdvancedConfig(raw).(map[string]any)
|
|
hdrs := out["headers"].(map[string]any)
|
|
set := hdrs["set"].(map[string]any)
|
|
switch v := set["Upgrade"].(type) {
|
|
case []any:
|
|
var outArr []string
|
|
for _, it := range v {
|
|
outArr = append(outArr, fmt.Sprintf("%v", it))
|
|
}
|
|
require.Equal(t, []string{"websocket"}, outArr)
|
|
case []string:
|
|
require.Equal(t, []string{"websocket"}, v)
|
|
default:
|
|
t.Fatalf("unexpected type for Upgrade: %T", v)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeAdvancedConfig_HeadersAlreadyArray(t *testing.T) {
|
|
// If the header value is already a []string it should be left as-is
|
|
raw := map[string]any{
|
|
"handler": "headers",
|
|
"headers": map[string]any{
|
|
"set": map[string]any{"X-Test": []string{"a", "b"}},
|
|
},
|
|
}
|
|
out := NormalizeAdvancedConfig(raw).(map[string]any)
|
|
hdrs := out["headers"].(map[string]any)
|
|
set := hdrs["set"].(map[string]any)
|
|
switch v := set["X-Test"].(type) {
|
|
case []any:
|
|
var outArr []string
|
|
for _, it := range v {
|
|
outArr = append(outArr, fmt.Sprintf("%v", it))
|
|
}
|
|
require.Equal(t, []string{"a", "b"}, outArr)
|
|
case []string:
|
|
require.Equal(t, []string{"a", "b"}, v)
|
|
default:
|
|
t.Fatalf("unexpected type for X-Test: %T", v)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeAdvancedConfig_MapWithTopLevelHandle(t *testing.T) {
|
|
raw := map[string]any{
|
|
"handler": "subroute",
|
|
"handle": []any{
|
|
map[string]any{
|
|
"handler": "headers",
|
|
"request": map[string]any{"set": map[string]any{"Upgrade": "websocket"}},
|
|
},
|
|
},
|
|
}
|
|
out := NormalizeAdvancedConfig(raw).(map[string]any)
|
|
handles := out["handle"].([]any)
|
|
require.Len(t, handles, 1)
|
|
hdr := handles[0].(map[string]any)
|
|
req := hdr["request"].(map[string]any)
|
|
set := req["set"].(map[string]any)
|
|
switch v := set["Upgrade"].(type) {
|
|
case []any:
|
|
var outArr []string
|
|
for _, it := range v {
|
|
outArr = append(outArr, fmt.Sprintf("%v", it))
|
|
}
|
|
require.Equal(t, []string{"websocket"}, outArr)
|
|
case []string:
|
|
require.Equal(t, []string{"websocket"}, v)
|
|
default:
|
|
t.Fatalf("unexpected type for Upgrade: %T", v)
|
|
}
|
|
}
|