test(wasm-plugins): lift unit-test coverage to ≥90% across 9 plugins (#3879)

Signed-off-by: jingze <daijingze.djz@alibaba-inc.com>
Co-authored-by: woody <yaodiwu618@gmail.com>
This commit is contained in:
Jingze
2026-06-15 20:33:10 +08:00
committed by GitHub
parent 3065d4e071
commit 45fc5a31bc
11 changed files with 2270 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
// Copyright (c) 2025 Alibaba Group Holding Ltd.
//
// 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 main
import (
"testing"
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
"github.com/higress-group/wasm-go/pkg/test"
"github.com/stretchr/testify/require"
)
// === Module A — parseConfig validation & wildcard short-circuits =========
//
// parseConfig is 88.7% in baseline. Four reachable uncovered branches
// pinned below; together they exercise the full validation contract for
// user-supplied attributes lists and the `*` wildcards on the two enable
// gates. Without these tests:
// - a malformed attribute object would silently survive ParseConfig
// - an unknown rule string ("bogus") would propagate downstream and only
// fail at attribute application time
// - the `*` wildcard would behave as if it were a literal path suffix
//
// All four are driven through ParseConfig+NewTestHost rather than calling
// parseConfig directly so the wasm logger is initialized.
// `attributes: [42]` ⇒ gjson Array yields one element whose .Raw is "42";
// json.Unmarshal of that into Attribute struct returns
// "json: cannot unmarshal number into Go value of type main.Attribute" →
// parseConfig returns the error → host start fails. Pins main.go:581-584.
func TestParseConfig_AttributeNotObject_StartFails(t *testing.T) {
test.RunGoTest(t, func(t *testing.T) {
host, status := test.NewTestHost([]byte(`{
"attributes": [42]
}`))
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusFailed, status)
})
}
// `rule` value not in the allowed enum ⇒ parseConfig returns
// "value of rule must be one of [nil, first, replace, append]" →
// host start fails. Pins main.go:585-587. The existing main_test.go
// fixtures only ever use the four legal rule values.
func TestParseConfig_InvalidRule_StartFails(t *testing.T) {
test.RunGoTest(t, func(t *testing.T) {
host, status := test.NewTestHost([]byte(`{
"attributes": [
{
"key": "x",
"value_source": "fixed_value",
"value": "y",
"rule": "bogus"
}
]
}`))
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusFailed, status)
})
}
// `enable_path_suffixes: ["*"]` ⇒ wildcard short-circuit clears the list
// and breaks out of the loop, leaving an empty enabledSuffixes list which
// isPathEnabled treats as "all paths enabled". Distinct from the existing
// "default path suffixes" tests where the suffixes are a literal list.
// Pins main.go:635-638.
func TestParseConfig_PathSuffixWildcard_EnablesAllPaths(t *testing.T) {
test.RunGoTest(t, func(t *testing.T) {
host, status := test.NewTestHost([]byte(`{
"enable_path_suffixes": ["*"]
}`))
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusOK, status)
conf, err := host.GetMatchConfig()
require.NoError(t, err)
c := conf.(*AIStatisticsConfig)
// Wildcard must collapse to empty slice — isPathEnabled then
// returns true for any path (per main.go:512-514).
require.Len(t, c.enablePathSuffixes, 0)
require.True(t, isPathEnabled("/anything", c.enablePathSuffixes))
})
}
// Same wildcard contract on the content-type gate. Pins main.go:650-653.
func TestParseConfig_ContentTypeWildcard_EnablesAllContentTypes(t *testing.T) {
test.RunGoTest(t, func(t *testing.T) {
host, status := test.NewTestHost([]byte(`{
"enable_content_types": ["*"]
}`))
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusOK, status)
conf, err := host.GetMatchConfig()
require.NoError(t, err)
c := conf.(*AIStatisticsConfig)
require.Len(t, c.enableContentTypes, 0)
require.True(t, isContentTypeEnabled("text/anything", c.enableContentTypes))
})
}
// === Module B — convertToUInt unsupported types =========================
//
// convertToUInt is 100% per existing tests, BUT the existing
// TestConvertToUInt only exercises the documented numeric types and one
// `"10"` string for the default branch. Pin two more default-branch
// shapes that are realistic in production (nil from a missing user
// attribute, slice from a malformed type assertion) so a future "support
// strings via Atoi" change can't sneak past unnoticed.
func TestConvertToUInt_NilAndSlice_FallToDefault(t *testing.T) {
v, ok := convertToUInt(nil)
require.False(t, ok)
require.Equal(t, uint64(0), v)
v, ok = convertToUInt([]int{1, 2, 3})
require.False(t, ok)
require.Equal(t, uint64(0), v)
}