package snapshot
import (
"context"
"testing"
)
// NOTE: Assume MockLogger and SnapshotManager are defined for the test to run.
// The actual implementation of LoadFilterChainFromYAML is assumed to be available
// to the test file.
// TestLoadFilterChainFromYAML_ComplexInput tests the functionality of LoadFilterChainFromYAML
func TestLoadFilterChainFromYAML_ComplexInput(t *testing.T) {
ctx := context.Background()
// The user's provided, valid YAML for a single FilterChain object
validComplexYAML := `
filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
codec_type: AUTO
upgrade_configs:
- upgrade_type: websocket
stream_idle_timeout: 0s
normalize_path: true
merge_slashes: true
route_config:
virtual_hosts:
- name: printer_service
domains: ["printer.jerxie.com"]
routes:
- match: { prefix: "/webcam" }
route: { prefix_rewrite: "/", cluster: "_3d_printer_camera", max_stream_duration: {grpc_timeout_header_max: 0s} }
- match: { prefix: "/" }
route: { cluster: "_3d_printer_console"}
http_filters:
- name: envoy.filters.http.oauth2
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
config:
token_endpoint:
cluster: _auth_server
uri: auth.jerxie.com/token
timeout: 3s
authorization_endpoint: https://auth.jerxie.com/auth
redirect_uri: "%REQ(x-forwarded-proto)%://%REQ(:authority)%/callback"
redirect_path_matcher:
path:
exact: /callback
signout_path:
path:
exact: /signout
forward_bearer_token: true
credentials:
client_id: octoprint-portal
token_secret:
name: token
sds_config:
path: "/etc/envoy/token-secret.yaml"
hmac_secret:
name: hmac
sds_config:
path: "/etc/envoy/hmac-secret.yaml"
auth_scopes:
- openid
- email
- name: envoy.filters.http.jwt_authn
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
providers:
provider1:
remote_jwks:
http_uri:
uri: "https://auth.jerxie.com/keys"
cluster: _auth_server
timeout: 5s
cache_duration: 600s
from_headers:
- name: Authorization
value_prefix: "Bearer "
payload_in_metadata: jwt_payload
rules:
- match:
prefix: /
requires:
provider_name: provider1
- name: envoy.filters.http.lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inline_code: |
email = ""
function envoy_on_request(request_handle)
email = ""
local meta = request_handle:streamInfo():dynamicMetadata()
for key, value in pairs(meta:get("envoy.filters.http.jwt_authn")) do
if key == "jwt_payload" then
for k, v in pairs(value) do
if k == "email" then
print("login octoprint: "..v)
email = v
request_handle:headers():add("ENVOY_AUTHENTICATED_USER", v)
end
end
end
end
end
function envoy_on_response(response_handle)
if email ~="" and email ~= "axieyangb@gmail.com" then
response_handle:logInfo("Got unauthorized user, return 403 for user " ..email)
response_handle:headers():add("set-cookie", "BearerToken=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT")
response_handle:headers():add("set-cookie", "OauthHMAC=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT")
response_handle:headers():add("set-cookie", "IdToken=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT")
response_handle:headers():add("set-cookie", "OauthExpires=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT")
end
email = ""
end
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
filter_chain_match:
server_names: ["printer.jerxie.com", "printer.local"]
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_certificates:
- certificate_chain: { filename: "/etc/certs/downstream/printer.jerxie.com/fullchain.pem" }
private_key: { filename: "/etc/certs/downstream/printer.jerxie.com/privkey.pem" }
`
tests := []struct {
name string
yamlStr string
expectError bool
expectedLen int // Expected number of network filters (top-level filters array)
}{
{
name: "Success_ComplexSingleFilterChain",
yamlStr: validComplexYAML,
expectError: false,
expectedLen: 1, // Only one top-level network filter: http_connection_manager
},
// Re-include sanity checks for robust testing
{
name: "Error_NoFiltersInChain",
yamlStr: `filter_chain_match: { server_names: ["empty"] }`,
expectError: true,
expectedLen: 0,
},
{
name: "Error_InputIsAList",
yamlStr: `- filters: []`,
expectError: true, // Should fail unmarshaling a list into a single struct
expectedLen: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
chain, err := LoadFilterChainFromYAML(ctx, tt.yamlStr)
if tt.expectError {
if err == nil {
t.Errorf("Expected an error but got nil")
}
if chain != nil {
t.Errorf("Expected nil chain on error, but got non-nil")
}
} else {
if err != nil {
t.Fatalf("Expected no error but got: %v", err)
}
if chain == nil {
t.Fatal("Expected non-nil filter chain, but got nil")
}
// 1. Check top-level filter count
if len(chain.Filters) != tt.expectedLen {
t.Errorf("Top-level filter count mismatch. Got %d, want %d", len(chain.Filters), tt.expectedLen)
}
// 2. Check a deeply nested value to ensure complex unmarshaling worked
if len(chain.FilterChainMatch.ServerNames) == 0 || chain.FilterChainMatch.ServerNames[0] != "printer.jerxie.com" {
t.Errorf("FilterChainMatch assertion failed. Expected server name 'printer.jerxie.com'")
}
// 3. Check the name of the top-level filter
if chain.Filters[0].Name != "envoy.filters.network.http_connection_manager" {
t.Errorf("Top-level filter name mismatch. Got %s", chain.Filters[0].Name)
}
}
})
}
}