feat: add e2e test for envoy filter (#710)

This commit is contained in:
SJC
2023-12-29 21:39:28 +08:00
committed by GitHub
parent 85df257f4e
commit 89c72777e1
33 changed files with 1250 additions and 252 deletions

View File

@@ -18,6 +18,7 @@ import (
"testing"
"github.com/alibaba/higress/pkg/ingress/kube/configmap"
"github.com/alibaba/higress/test/e2e/conformance/utils/envoy"
"github.com/alibaba/higress/test/e2e/conformance/utils/http"
"github.com/alibaba/higress/test/e2e/conformance/utils/kubernetes"
"github.com/alibaba/higress/test/e2e/conformance/utils/suite"
@@ -25,6 +26,266 @@ import (
func init() {
Register(ConfigmapGzip)
Register(ConfigMapGzipEnvoy)
}
var testCases = []struct {
higressConfig *configmap.HigressConfig
envoyAssertion envoy.Assertion
httpAssert http.Assertion
}{
{
higressConfig: &configmap.HigressConfig{
Gzip: &configmap.Gzip{
Enable: false,
MinContentLength: 1024,
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/json", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
DisableOnEtagHeader: true,
MemoryLevel: 5,
WindowBits: 12,
ChunkSize: 4096,
CompressionLevel: "BEST_COMPRESSION",
CompressionStrategy: "DEFAULT_STRATEGY",
},
},
httpAssert: http.Assertion{
Meta: http.AssertionMeta{
TestCaseName: "case1: disable gzip output",
TargetBackend: "web-backend",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Method: "GET",
Headers: map[string]string{
"Accept-Encoding": "*",
},
},
},
Response: http.AssertionResponse{
ExpectedResponseNoRequest: true,
ExpectedResponse: http.Response{
StatusCode: 200,
AbsentHeaders: []string{"content-encoding"},
},
},
},
envoyAssertion: envoy.Assertion{
Path: "configs.#.dynamic_listeners.#.active_state.listener.filter_chains",
TargetNamespace: "higress-system",
CheckType: envoy.CheckTypeNotExist,
ExpectEnvoyConfig: map[string]interface{}{
"memory_level": 5,
"compression_level": "COMPRESSION_LEVEL_9",
"window_bits": 12,
"min_content_length": 1024,
"disable_on_etag_header": true,
"content_type": []interface{}{
"text/html",
"text/css",
"text/plain",
"text/xml",
"application/json",
"application/javascript",
"application/xhtml+xml",
"image/svg+xml",
},
},
},
},
{
higressConfig: &configmap.HigressConfig{
Gzip: &configmap.Gzip{
Enable: true,
MinContentLength: 100,
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/json", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
DisableOnEtagHeader: true,
MemoryLevel: 5,
WindowBits: 12,
ChunkSize: 4096,
CompressionLevel: "BEST_COMPRESSION",
CompressionStrategy: "DEFAULT_STRATEGY",
},
},
httpAssert: http.Assertion{
Meta: http.AssertionMeta{
TestCaseName: "case2: enable gzip output",
TargetBackend: "web-backend",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Method: "GET",
Headers: map[string]string{
"Accept-Encoding": "*",
},
},
},
Response: http.AssertionResponse{
ExpectedResponseNoRequest: true,
ExpectedResponse: http.Response{
StatusCode: 200,
},
AdditionalResponseHeaders: map[string]string{"content-encoding": "gzip"},
},
},
envoyAssertion: envoy.Assertion{
Path: "configs.#.dynamic_listeners.#.active_state.listener.filter_chains",
TargetNamespace: "higress-system",
CheckType: envoy.CheckTypeExist,
ExpectEnvoyConfig: map[string]interface{}{
"name": "envoy.filters.network.http_connection_manager",
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"stat_prefix": "outbound_0.0.0.0_80",
"memory_level": 5,
"compression_level": "COMPRESSION_LEVEL_9",
"window_bits": 12,
"min_content_length": 100,
"disable_on_etag_header": true,
"content_type": []interface{}{
"text/html",
"text/css",
"text/plain",
"text/xml",
"application/json",
"application/javascript",
"application/xhtml+xml",
"image/svg+xml",
},
},
},
},
{
higressConfig: &configmap.HigressConfig{
Gzip: &configmap.Gzip{
Enable: true,
MinContentLength: 4096,
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/json", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
DisableOnEtagHeader: true,
MemoryLevel: 5,
WindowBits: 12,
ChunkSize: 4096,
CompressionLevel: "BEST_COMPRESSION",
CompressionStrategy: "DEFAULT_STRATEGY",
},
},
httpAssert: http.Assertion{
Meta: http.AssertionMeta{
TestCaseName: "case3: disable gzip output because content length less hhan 4096 ",
TargetBackend: "web-backend",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Method: "GET",
Headers: map[string]string{
"Accept-Encoding": "*",
},
},
},
Response: http.AssertionResponse{
ExpectedResponseNoRequest: true,
ExpectedResponse: http.Response{
StatusCode: 200,
AbsentHeaders: []string{"content-encoding"},
},
},
},
envoyAssertion: envoy.Assertion{
Path: "configs.#.dynamic_listeners.#.active_state.listener.filter_chains",
TargetNamespace: "higress-system",
CheckType: envoy.CheckTypeExist,
ExpectEnvoyConfig: map[string]interface{}{
"name": "envoy.filters.network.http_connection_manager",
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"stat_prefix": "outbound_0.0.0.0_80",
"memory_level": 5,
"compression_level": "COMPRESSION_LEVEL_9",
"window_bits": 12,
"min_content_length": 4096,
"disable_on_etag_header": true,
"content_type": []interface{}{
"text/html",
"text/css",
"text/plain",
"text/xml",
"application/json",
"application/javascript",
"application/xhtml+xml",
"image/svg+xml",
},
},
},
},
{
higressConfig: &configmap.HigressConfig{
Gzip: &configmap.Gzip{
Enable: true,
MinContentLength: 100,
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
DisableOnEtagHeader: true,
MemoryLevel: 5,
WindowBits: 12,
ChunkSize: 4096,
CompressionLevel: "BEST_COMPRESSION",
CompressionStrategy: "DEFAULT_STRATEGY",
},
},
httpAssert: http.Assertion{
Meta: http.AssertionMeta{
TestCaseName: "case4: disable gzip output because application/json missed in content types ",
TargetBackend: "web-backend",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Method: "GET",
Headers: map[string]string{
"Accept-Encoding": "*",
},
},
},
Response: http.AssertionResponse{
ExpectedResponseNoRequest: true,
ExpectedResponse: http.Response{
StatusCode: 200,
AbsentHeaders: []string{"content-encoding"},
},
},
},
envoyAssertion: envoy.Assertion{
Path: "configs.#.dynamic_listeners.#.active_state.listener.filter_chains",
TargetNamespace: "higress-system",
CheckType: envoy.CheckTypeExist,
ExpectEnvoyConfig: map[string]interface{}{
"name": "envoy.filters.network.http_connection_manager",
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"stat_prefix": "outbound_0.0.0.0_80",
"memory_level": 5,
"compression_level": "COMPRESSION_LEVEL_9",
"window_bits": 12,
"min_content_length": 100,
"disable_on_etag_header": true,
"content_type": []interface{}{
"text/html",
"text/css",
"text/plain",
"text/xml",
"application/javascript",
"application/xhtml+xml",
"image/svg+xml",
},
},
},
},
}
var ConfigmapGzip = suite.ConformanceTest{
@@ -33,171 +294,9 @@ var ConfigmapGzip = suite.ConformanceTest{
Manifests: []string{"tests/configmap-gzip.yaml"},
Features: []suite.SupportedFeature{suite.HTTPConformanceFeature},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
testcases := []struct {
higressConfig *configmap.HigressConfig
httpAssert http.Assertion
}{
{
higressConfig: &configmap.HigressConfig{
Gzip: &configmap.Gzip{
Enable: false,
MinContentLength: 1024,
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/json", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
DisableOnEtagHeader: true,
MemoryLevel: 5,
WindowBits: 12,
ChunkSize: 4096,
CompressionLevel: "BEST_COMPRESSION",
CompressionStrategy: "DEFAULT_STRATEGY",
},
},
httpAssert: http.Assertion{
Meta: http.AssertionMeta{
TestCaseName: "case1: disable gzip output",
TargetBackend: "web-backend",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Method: "GET",
Headers: map[string]string{
"Accept-Encoding": "*",
},
},
},
Response: http.AssertionResponse{
ExpectedResponseNoRequest: true,
ExpectedResponse: http.Response{
StatusCode: 200,
AbsentHeaders: []string{"content-encoding"},
},
},
},
},
{
higressConfig: &configmap.HigressConfig{
Gzip: &configmap.Gzip{
Enable: true,
MinContentLength: 100,
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/json", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
DisableOnEtagHeader: true,
MemoryLevel: 5,
WindowBits: 12,
ChunkSize: 4096,
CompressionLevel: "BEST_COMPRESSION",
CompressionStrategy: "DEFAULT_STRATEGY",
},
},
httpAssert: http.Assertion{
Meta: http.AssertionMeta{
TestCaseName: "case2: enable gzip output",
TargetBackend: "web-backend",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Method: "GET",
Headers: map[string]string{
"Accept-Encoding": "*",
},
},
},
Response: http.AssertionResponse{
ExpectedResponseNoRequest: true,
ExpectedResponse: http.Response{
StatusCode: 200,
},
AdditionalResponseHeaders: map[string]string{"content-encoding": "gzip"},
},
},
},
{
higressConfig: &configmap.HigressConfig{
Gzip: &configmap.Gzip{
Enable: true,
MinContentLength: 4096,
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/json", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
DisableOnEtagHeader: true,
MemoryLevel: 5,
WindowBits: 12,
ChunkSize: 4096,
CompressionLevel: "BEST_COMPRESSION",
CompressionStrategy: "DEFAULT_STRATEGY",
},
},
httpAssert: http.Assertion{
Meta: http.AssertionMeta{
TestCaseName: "case3: disable gzip output because content length less hhan 4096 ",
TargetBackend: "web-backend",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Method: "GET",
Headers: map[string]string{
"Accept-Encoding": "*",
},
},
},
Response: http.AssertionResponse{
ExpectedResponseNoRequest: true,
ExpectedResponse: http.Response{
StatusCode: 200,
AbsentHeaders: []string{"content-encoding"},
},
},
},
},
{
higressConfig: &configmap.HigressConfig{
Gzip: &configmap.Gzip{
Enable: true,
MinContentLength: 100,
ContentType: []string{"text/html", "text/css", "text/plain", "text/xml", "application/javascript", "application/xhtml+xml", "image/svg+xml"},
DisableOnEtagHeader: true,
MemoryLevel: 5,
WindowBits: 12,
ChunkSize: 4096,
CompressionLevel: "BEST_COMPRESSION",
CompressionStrategy: "DEFAULT_STRATEGY",
},
},
httpAssert: http.Assertion{
Meta: http.AssertionMeta{
TestCaseName: "case4: disable gzip output because application/json missed in content types ",
TargetBackend: "web-backend",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Host: "foo.com",
Path: "/foo",
Method: "GET",
Headers: map[string]string{
"Accept-Encoding": "*",
},
},
},
Response: http.AssertionResponse{
ExpectedResponseNoRequest: true,
ExpectedResponse: http.Response{
StatusCode: 200,
AbsentHeaders: []string{"content-encoding"},
},
},
},
},
}
t.Run("Configmap Gzip", func(t *testing.T) {
for _, testcase := range testcases {
err := kubernetes.ApplyConfigmapDataWithYaml(suite.Client, "higress-system", "higress-config", "higress", testcase.higressConfig)
for _, testcase := range testCases {
err := kubernetes.ApplyConfigmapDataWithYaml(t, suite.Client, "higress-system", "higress-config", "higress", testcase.higressConfig)
if err != nil {
t.Fatalf("can't apply conifgmap %s in namespace %s for data key %s", "higress-config", "higress-system", "higress")
}
@@ -206,3 +305,22 @@ var ConfigmapGzip = suite.ConformanceTest{
})
},
}
var ConfigMapGzipEnvoy = suite.ConformanceTest{
ShortName: "ConfigMapGzipEnvoy",
Description: "The Envoy config should contain gzip config",
Manifests: []string{"tests/configmap-gzip.yaml"},
Features: []suite.SupportedFeature{suite.EnvoyConfigConformanceFeature},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
t.Run("ConfigMap Gzip Envoy", func(t *testing.T) {
for _, testcase := range testCases {
// apply config
err := kubernetes.ApplyConfigmapDataWithYaml(t, suite.Client, "higress-system", "higress-config", "higress", testcase.higressConfig)
if err != nil {
t.Fatalf("can't apply conifgmap %s in namespace %s for data key %s", "higress-config", "higress-system", "higress")
}
envoy.AssertEnvoyConfig(t, suite.TimeoutConfig, testcase.envoyAssertion)
}
})
},
}

View File

@@ -29,4 +29,5 @@ spec:
service:
name: infra-backend-v3
port:
number: 8080
number: 8080

View File

@@ -0,0 +1,287 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package envoy
import (
"fmt"
"reflect"
"testing"
"time"
"github.com/alibaba/higress/cmd/hgctl/config"
cfg "github.com/alibaba/higress/test/e2e/conformance/utils/config"
"github.com/tidwall/gjson"
"k8s.io/apimachinery/pkg/util/wait"
)
type CheckType string
const (
// CheckTypeMatch checks if the actual value matches the expected value.
CheckTypeMatch CheckType = "match"
// CheckTypeExist checks if the actual value exists.
CheckTypeExist CheckType = "exist"
// CheckTypeNotExist checks if the actual value does not exist.
CheckTypeNotExist CheckType = "notexist"
// defaultSuccessThreshold is the default number of times the assertion must succeed in a row.
defaultSuccessThreshold = 3
)
// Assertion defines the assertion to be made on the Envoy config.
// TODO: It can support localization judgment so that this configuration check function will be more universal.
// TODO: Can be used for general e2e tests, rather than just envoy filter scenarios.
type Assertion struct {
// Path is the path of gjson to the value to be asserted.
Path string
// CheckType is the type of assertion to be made.
CheckType CheckType
// ExpectEnvoyConfig is the expected value of the Envoy config.
ExpectEnvoyConfig map[string]interface{}
// TargetNamespace is the namespace of the Envoy pod.
TargetNamespace string
}
// AssertEnvoyConfig asserts the Envoy config.
func AssertEnvoyConfig(t *testing.T, timeoutConfig cfg.TimeoutConfig, expected Assertion) {
options := config.NewDefaultGetEnvoyConfigOptions()
options.PodNamespace = expected.TargetNamespace
waitForEnvoyConfig(t, timeoutConfig, options, expected)
}
// waitForEnvoyConfig waits for the Envoy config to be ready and asserts it.
func waitForEnvoyConfig(t *testing.T, timeoutConfig cfg.TimeoutConfig, options *config.GetEnvoyConfigOptions, expected Assertion) {
awaitConvergence(t, defaultSuccessThreshold, timeoutConfig.MaxTimeToConsistency, func(elapsed time.Duration) bool {
allEnvoyConfig := ""
err := wait.Poll(1*time.Second, 10*time.Second, func() (bool, error) {
out, err := config.GetEnvoyConfig(options)
if err != nil {
return false, err
}
allEnvoyConfig = string(out)
return true, nil
})
if err != nil {
return false
}
switch expected.CheckType {
case CheckTypeMatch:
err = assertEnvoyConfigMatch(t, allEnvoyConfig, expected)
case CheckTypeExist:
err = assertEnvoyConfigExist(t, allEnvoyConfig, expected)
case CheckTypeNotExist:
err = assertEnvoyConfigNotExist(t, allEnvoyConfig, expected)
default:
err = fmt.Errorf("unsupported check type %s", expected.CheckType)
}
if err != nil {
return false
}
return true
})
t.Logf("✅ Envoy config checked")
}
// assertEnvoyConfigNotExist asserts the Envoy config does not exist.
func assertEnvoyConfigNotExist(t *testing.T, envoyConfig string, expected Assertion) error {
result := gjson.Get(envoyConfig, expected.Path).Value()
if result == nil {
return nil
}
if !findMustNotExist(t, result, expected.ExpectEnvoyConfig) {
return fmt.Errorf("the expected value %s exists in path '%s'", expected.ExpectEnvoyConfig, expected.Path)
}
return nil
}
// assertEnvoyConfigExist asserts the Envoy config exists.
func assertEnvoyConfigExist(t *testing.T, envoyConfig string, expected Assertion) error {
result := gjson.Get(envoyConfig, expected.Path).Value()
if result == nil {
return fmt.Errorf("failed to get value from path '%s'", expected.Path)
}
if !findMustExist(t, result, expected.ExpectEnvoyConfig) {
return fmt.Errorf("the expected value %s does not exist in path '%s'", expected.ExpectEnvoyConfig, expected.Path)
}
return nil
}
// assertEnvoyConfigMatch asserts the Envoy config matches the expected value.
func assertEnvoyConfigMatch(t *testing.T, envoyConfig string, expected Assertion) error {
result := gjson.Get(envoyConfig, expected.Path).Value()
if result == nil {
return fmt.Errorf("failed to get value from path '%s'", expected.Path)
}
if !match(t, result, expected.ExpectEnvoyConfig) {
return fmt.Errorf("failed to match value from path '%s'", expected.Path)
}
t.Logf("✅ Matched value %s in path '%s'", expected.ExpectEnvoyConfig, expected.Path)
return nil
}
// awaitConvergence runs the given function until it returns 'true' `threshold` times in a row.
// Each failed attempt has a 1s delay; successful attempts have no delay.
func awaitConvergence(t *testing.T, threshold int, maxTimeToConsistency time.Duration, fn func(elapsed time.Duration) bool) {
successes := 0
attempts := 0
start := time.Now()
to := time.After(maxTimeToConsistency)
delay := time.Second
for {
select {
case <-to:
t.Fatalf("timeout while waiting after %d attempts", attempts)
default:
}
completed := fn(time.Now().Sub(start))
attempts++
if completed {
successes++
if successes >= threshold {
return
}
// Skip delay if we have a success
continue
}
successes = 0
select {
// Capture the overall timeout
case <-to:
t.Fatalf("timeout while waiting after %d attempts, %d/%d sucessess", attempts, successes, threshold)
// And the per-try delay
case <-time.After(delay):
}
}
}
// match
// 1. interface{} is a slice: if one of the slice elements matches, the assertion passes
// Notice: can recursively find slices
// 2. interface{} is a map: if all the map elements match, the assertion passes
// 3. interface{} is a field: if the field matches, the assertion passes
func match(t *testing.T, actual interface{}, expected map[string]interface{}) bool {
reflectValue := reflect.ValueOf(actual)
kind := reflectValue.Kind()
switch kind {
case reflect.Slice:
actualValueSlice := actual.([]interface{})
for _, v := range actualValueSlice {
if match(t, v, expected) {
return true
}
}
return false
case reflect.Map:
actualValueMap := actual.(map[string]interface{})
for key, expectValue := range expected {
actualValue, ok := actualValueMap[key]
if !ok {
return false
}
if !reflect.DeepEqual(actualValue, expectValue) {
return false
}
}
return true
default:
return reflect.DeepEqual(actual, expected)
}
}
// findMustExist finds the value of the given path in the given Envoy config.
func findMustExist(t *testing.T, actual interface{}, expected map[string]interface{}) bool {
for key, expectValue := range expected {
// If the key does not exist, the assertion fails.
t.Logf("🔍 Finding key %s", key)
if !findKey(actual, key, expectValue) {
t.Logf("❌ Not found key %s", key)
return false
}
t.Logf("✅ Found key %s", key)
}
return true
}
// findMustNotExist finds the value of the given path in the given Envoy config.
func findMustNotExist(t *testing.T, actual interface{}, expected map[string]interface{}) bool {
for key, expectValue := range expected {
// If the key exists, the assertion fails.
t.Logf("🔍 Finding key %s", key)
if findKey(actual, key, expectValue) {
t.Logf("❌ Found key %s", key)
return false
}
t.Logf("✅ Not found key %s", key)
}
return true
}
// findKey finds the value of the given key in the given Envoy config.
func findKey(actual interface{}, key string, expectValue interface{}) bool {
reflectValue := reflect.ValueOf(actual)
kind := reflectValue.Kind()
switch kind {
case reflect.Slice:
actualValueSlice := actual.([]interface{})
for _, v := range actualValueSlice {
if findKey(v, key, expectValue) {
return true
}
}
return false
case reflect.Map:
actualValueMap := actual.(map[string]interface{})
for actualKey, actualValue := range actualValueMap {
if actualKey == key && reflect.DeepEqual(convertType(actualValue, expectValue), expectValue) {
return true
}
if findKey(actualValue, key, expectValue) {
return true
}
}
return false
default:
if reflectValue.String() == key && reflect.DeepEqual(convertType(actual, expectValue), expectValue) {
return true
}
return false
}
}
// convertType converts the type of the given value to the type of the given target value.
func convertType(value interface{}, targetType interface{}) interface{} {
targetTypeValue := reflect.ValueOf(targetType)
targetTypeKind := targetTypeValue.Kind()
switch targetTypeKind {
case reflect.Int:
switch value.(type) {
case int:
return value
case float64:
return int(value.(float64))
}
case reflect.Float64:
switch value.(type) {
case int:
return float64(value.(int))
case float64:
return value
}
}
return value
}

View File

@@ -0,0 +1,462 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package envoy
import (
"testing"
)
func Test_match(t *testing.T) {
testCases := []struct {
name string
actual interface{}
expected map[string]interface{}
expectResult bool
}{
{
name: "case 1",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
},
map[string]interface{}{
"foo": "baz",
},
},
expected: map[string]interface{}{
"foo": "bar",
},
expectResult: true,
},
{
name: "case 2",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
},
map[string]interface{}{
"foo": "baz",
},
},
expected: map[string]interface{}{
"foo": "bay",
},
expectResult: false,
},
{
name: "case 3",
actual: map[string]interface{}{
"foo": "bar",
"bar": "baz",
"baz": "bay",
},
expected: map[string]interface{}{
"foo": "bar",
},
expectResult: true,
},
{
name: "case 4",
actual: map[string]interface{}{
"foo": "bar",
},
expected: map[string]interface{}{
"foo": "bar",
},
expectResult: true,
},
{
name: "case 5",
actual: map[string]interface{}{
"foo": "bar",
},
expected: map[string]interface{}{
"foo": "baz",
},
expectResult: false,
},
{
name: "case 6",
actual: []interface{}{
[]interface{}{
map[string]interface{}{
"foo": "bar",
},
},
},
expected: map[string]interface{}{
"foo": "bar",
},
expectResult: true,
},
{
name: "case 7",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
},
[]interface{}{
map[string]interface{}{
"foo": "baz",
},
},
},
expected: map[string]interface{}{
"foo": "baz",
},
expectResult: true,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
result := match(t, testCase.actual, testCase.expected)
if result != testCase.expectResult {
t.Errorf("expected %v, got %v", testCase.expectResult, result)
}
})
}
}
func Test_findMustExist(t *testing.T) {
testCases := []struct {
name string
actual interface{}
expected map[string]interface{}
expectResult bool
}{
{
name: "case 1",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
},
},
expected: map[string]interface{}{
"foo": "bar",
},
expectResult: true,
},
{
name: "case 2",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
},
},
expected: map[string]interface{}{
"foo": "baz",
},
expectResult: false,
},
{
name: "case 3",
actual: map[string]interface{}{
"foo": "bar",
"bar": "baz",
"baz": "bay",
},
expected: map[string]interface{}{
"foo": "bar",
},
expectResult: true,
},
{
name: "case 4",
actual: map[string]interface{}{
"foo": "bar",
},
expected: map[string]interface{}{
"foo": "baz",
},
expectResult: false,
},
{
name: "case 5",
actual: []interface{}{
[]interface{}{
map[string]interface{}{
"foo": "bar",
},
},
},
expected: map[string]interface{}{
"foo": "bar",
},
expectResult: true,
},
{
name: "case 6",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
},
[]interface{}{
map[string]interface{}{
"foo": "baz",
},
},
},
expected: map[string]interface{}{
"foo": "baz",
},
expectResult: true,
},
{
name: "case 7",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
},
[]interface{}{
map[string]interface{}{
"test": "baz",
},
},
},
expected: map[string]interface{}{
"foo": "bar",
"test": "baz",
},
expectResult: true,
},
{
name: "case 8",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
"test": "baz",
},
[]interface{}{
map[string]interface{}{
"foo": "baz",
},
},
},
expected: map[string]interface{}{
"foo": "baz",
"test": "baz",
},
expectResult: true,
},
{
name: "case 9",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
},
[]interface{}{
[]interface{}{
map[string]interface{}{
"foo": "baz",
},
},
},
},
expected: map[string]interface{}{
"foo": "baz",
},
expectResult: true,
},
{
name: "case 9",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
},
[]interface{}{
[]interface{}{
map[string]interface{}{
"foo": "baz",
},
},
},
map[string]interface{}{
"content": []interface{}{
"one",
"two",
},
},
},
expected: map[string]interface{}{
"foo": "baz",
"content": []interface{}{
"one",
"two",
},
},
expectResult: true,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
result := findMustExist(t, testCase.actual, testCase.expected)
if result != testCase.expectResult {
t.Errorf("expected %v, got %v", testCase.expectResult, result)
}
})
}
}
func Test_findMustNotExist(t *testing.T) {
testCases := []struct {
name string
actual interface{}
expected map[string]interface{}
expectResult bool
}{
{
name: "case 1",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
},
},
expected: map[string]interface{}{
"foo": "bar",
},
expectResult: false,
},
{
name: "case 2",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
},
},
expected: map[string]interface{}{
"foo": "baz",
},
expectResult: true,
},
{
name: "case 3",
actual: map[string]interface{}{
"foo": "bar",
"bar": "baz",
"baz": "bay",
},
expected: map[string]interface{}{
"foo": "bar",
},
expectResult: false,
},
{
name: "case 4",
actual: map[string]interface{}{
"foo": "bar",
},
expected: map[string]interface{}{
"foo": "baz",
},
expectResult: true,
},
{
name: "case 5",
actual: []interface{}{
[]interface{}{
map[string]interface{}{
"foo": "bar",
},
},
},
expected: map[string]interface{}{
"foo": "bar",
},
expectResult: false,
},
{
name: "case 6",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
},
[]interface{}{
map[string]interface{}{
"foo": "baz",
},
},
},
expected: map[string]interface{}{
"foo": "baz",
},
expectResult: false,
},
{
name: "case 7",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
},
[]interface{}{
map[string]interface{}{
"test": "baz",
},
},
},
expected: map[string]interface{}{
"foo": "bar",
},
expectResult: false,
},
{
name: "case 8",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
"test": "baz",
},
[]interface{}{
map[string]interface{}{
"foo": "baz",
},
},
},
expected: map[string]interface{}{
"foo": "baz",
"test": "baz",
},
expectResult: false,
},
{
name: "case 9",
actual: []interface{}{
map[string]interface{}{
"foo": "bar",
},
[]interface{}{
[]interface{}{
map[string]interface{}{
"foo": "baz",
},
},
},
},
expected: map[string]interface{}{
"foo": "baz",
},
expectResult: false,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
result := findMustNotExist(t, testCase.actual, testCase.expected)
if result != testCase.expectResult {
t.Errorf("expected %v, got %v", testCase.expectResult, result)
}
})
}
}

View File

@@ -26,4 +26,5 @@ var (
IsWasmPluginTest = flag.Bool("isWasmPluginTest", false, "Determine if run wasm plugin conformance test")
WasmPluginType = flag.String("wasmPluginType", "GO", "Define wasm plugin type, currently supports GO, CPP")
WasmPluginName = flag.String("wasmPluginName", "", "Define wasm plugin name")
IsEnvoyConfigTest = flag.Bool("isEnvoyConfigTest", false, "Determine if run envoy config conformance test")
)

View File

@@ -121,7 +121,7 @@ func FindPodConditionInList(t *testing.T, conditions []v1.PodCondition, condName
return false
}
func ApplyConfigmapDataWithYaml(c client.Client, namespace string, name string, key string, val any) error {
func ApplyConfigmapDataWithYaml(t *testing.T, c client.Client, namespace string, name string, key string, val any) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
@@ -140,8 +140,11 @@ func ApplyConfigmapDataWithYaml(c client.Client, namespace string, name string,
}
cm.Data[key] = data
t.Logf("🏗 Updating %s %s", name, namespace)
if err := c.Update(ctx, cm); err != nil {
return err
}
return nil
}

View File

@@ -31,6 +31,9 @@ const (
EurekaConformanceFeature SupportedFeature = "eureka"
ConsulConformanceFeature SupportedFeature = "consul"
NacosConformanceFeature SupportedFeature = "nacos"
// extended: envoy config
EnvoyConfigConformanceFeature SupportedFeature = "envoy-config"
)
var AllFeatures = sets.Set{}.
@@ -38,7 +41,8 @@ var AllFeatures = sets.Set{}.
Insert(string(DubboConformanceFeature)).
Insert(string(EurekaConformanceFeature)).
Insert(string(ConsulConformanceFeature)).
Insert(string(NacosConformanceFeature))
Insert(string(NacosConformanceFeature)).
Insert(string(EnvoyConfigConformanceFeature))
var ExperimentFeatures = sets.Set{}.
Insert(string(WASMGoConformanceFeature)).

View File

@@ -60,6 +60,9 @@ type Options struct {
// resources such as Gateways should be cleaned up after the run.
CleanupBaseResources bool
TimeoutConfig config.TimeoutConfig
// IsEnvoyConfigTest indicates whether or not the test is for envoy config
IsEnvoyConfigTest bool
}
type WASMOptions struct {
@@ -87,6 +90,8 @@ func New(s Options) *ConformanceTestSuite {
} else {
s.SupportedFeatures.Insert(string(WASMGoConformanceFeature))
}
} else if s.IsEnvoyConfigTest {
s.SupportedFeatures.Insert(string(EnvoyConfigConformanceFeature))
} else if s.EnableAllSupportedFeatures {
s.SupportedFeatures = AllFeatures
}

View File

@@ -51,6 +51,7 @@ func TestHigressConformanceTests(t *testing.T) {
},
GatewayAddress: "localhost",
EnableAllSupportedFeatures: true,
IsEnvoyConfigTest: *flags.IsEnvoyConfigTest,
})
cSuite.Setup(t)