mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2025-03-10 10:47:20 +00:00
Fix NodeFeatureRule templating in cases where multiple matchFeatures terms are targeting the same feature. Previously, only matched feature elements from the last matcher terms were used as the input to the template. However, the input should contain all matched elements from all matcher terms. For example, consider the example rule snippet below: ... labelsTemplate: | {{ range .pci.device }}vendor.io/pci-device.{{ .class }}-{{ .device }}=exists {{ end }} matchFeatures: - feature: pci.device matchExpressions: class: {op: InRegexp, value: ["^03"]} vendor: {op: In, value: ["1234"]} - feature: pci.device matchExpressions: class: {op: InRegexp, value: ["^12"]} This rule matches if both a pci device of class 03 from vendor 1234 exists and a pci device of class 12 (from any vendor) exists. Previously, the template would only generate labels from the devices in class 12 (as that's the last term). With this patch the template creates device labels from devices in both classes 03 and 12.
471 lines
19 KiB
Go
471 lines
19 KiB
Go
/*
|
|
Copyright 2021 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 v1alpha1_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"sigs.k8s.io/yaml"
|
|
|
|
api "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
|
)
|
|
|
|
type BoolAssertionFuncf func(assert.TestingT, bool, string, ...interface{}) bool
|
|
|
|
type ValueAssertionFuncf func(assert.TestingT, interface{}, string, ...interface{}) bool
|
|
|
|
func TestCreateMatchExpression(t *testing.T) {
|
|
type V = api.MatchValue
|
|
type TC struct {
|
|
op api.MatchOp
|
|
values V
|
|
err ValueAssertionFuncf
|
|
}
|
|
|
|
tcs := []TC{
|
|
{op: api.MatchAny, err: assert.Nilf}, // #0
|
|
{op: api.MatchAny, values: V{"1"}, err: assert.NotNilf},
|
|
|
|
{op: api.MatchIn, err: assert.NotNilf},
|
|
{op: api.MatchIn, values: V{"1"}, err: assert.Nilf},
|
|
{op: api.MatchIn, values: V{"1", "2", "3", "4"}, err: assert.Nilf},
|
|
|
|
{op: api.MatchNotIn, err: assert.NotNilf},
|
|
{op: api.MatchNotIn, values: V{"1"}, err: assert.Nilf},
|
|
{op: api.MatchNotIn, values: V{"1", "2"}, err: assert.Nilf},
|
|
|
|
{op: api.MatchInRegexp, err: assert.NotNilf},
|
|
{op: api.MatchInRegexp, values: V{"1"}, err: assert.Nilf},
|
|
{op: api.MatchInRegexp, values: V{"()", "2", "3"}, err: assert.Nilf},
|
|
{op: api.MatchInRegexp, values: V{"("}, err: assert.NotNilf},
|
|
|
|
{op: api.MatchExists, err: assert.Nilf},
|
|
{op: api.MatchExists, values: V{"1"}, err: assert.NotNilf},
|
|
|
|
{op: api.MatchDoesNotExist, err: assert.Nilf},
|
|
{op: api.MatchDoesNotExist, values: V{"1"}, err: assert.NotNilf},
|
|
|
|
{op: api.MatchGt, err: assert.NotNilf},
|
|
{op: api.MatchGt, values: V{"1"}, err: assert.Nilf},
|
|
{op: api.MatchGt, values: V{"-10"}, err: assert.Nilf},
|
|
{op: api.MatchGt, values: V{"1", "2"}, err: assert.NotNilf},
|
|
{op: api.MatchGt, values: V{""}, err: assert.NotNilf},
|
|
|
|
{op: api.MatchLt, err: assert.NotNilf},
|
|
{op: api.MatchLt, values: V{"1"}, err: assert.Nilf},
|
|
{op: api.MatchLt, values: V{"-1"}, err: assert.Nilf},
|
|
{op: api.MatchLt, values: V{"1", "2", "3"}, err: assert.NotNilf},
|
|
{op: api.MatchLt, values: V{"a"}, err: assert.NotNilf},
|
|
|
|
{op: api.MatchGtLt, err: assert.NotNilf},
|
|
{op: api.MatchGtLt, values: V{"1"}, err: assert.NotNilf},
|
|
{op: api.MatchGtLt, values: V{"1", "2"}, err: assert.Nilf},
|
|
{op: api.MatchGtLt, values: V{"2", "1"}, err: assert.NotNilf},
|
|
{op: api.MatchGtLt, values: V{"1", "2", "3"}, err: assert.NotNilf},
|
|
{op: api.MatchGtLt, values: V{"a", "2"}, err: assert.NotNilf},
|
|
|
|
{op: api.MatchIsTrue, err: assert.Nilf},
|
|
{op: api.MatchIsTrue, values: V{"1"}, err: assert.NotNilf},
|
|
|
|
{op: api.MatchIsFalse, err: assert.Nilf},
|
|
{op: api.MatchIsFalse, values: V{"1", "2"}, err: assert.NotNilf},
|
|
}
|
|
|
|
for i, tc := range tcs {
|
|
_, err := api.CreateMatchExpression(tc.op, tc.values...)
|
|
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
|
}
|
|
}
|
|
|
|
func TestMatch(t *testing.T) {
|
|
type V = api.MatchValue
|
|
type TC struct {
|
|
op api.MatchOp
|
|
values V
|
|
input interface{}
|
|
valid bool
|
|
result BoolAssertionFuncf
|
|
err ValueAssertionFuncf
|
|
}
|
|
|
|
tcs := []TC{
|
|
{op: api.MatchAny, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchAny, input: "2", valid: false, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{op: api.MatchIn, values: V{"1"}, input: "2", valid: false, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchIn, values: V{"1"}, input: "2", valid: true, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchIn, values: V{"1", "2", "3"}, input: "2", valid: false, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchIn, values: V{"1", "2", "3"}, input: "2", valid: true, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{op: api.MatchNotIn, values: V{"2"}, input: 2, valid: false, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchNotIn, values: V{"1"}, input: 2, valid: true, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchNotIn, values: V{"1", "2", "3"}, input: "2", valid: false, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchNotIn, values: V{"1", "2", "3"}, input: "2", valid: true, result: assert.Falsef, err: assert.Nilf},
|
|
|
|
{op: api.MatchInRegexp, values: V{"val-[0-9]$"}, input: "val-1", valid: false, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchInRegexp, values: V{"val-[0-9]$"}, input: "val-1", valid: true, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchInRegexp, values: V{"val-[0-9]$"}, input: "val-12", valid: true, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchInRegexp, values: V{"val-[0-9]$", "al-[1-9]"}, input: "val-12", valid: true, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{op: api.MatchExists, input: nil, valid: false, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchExists, input: nil, valid: true, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{op: api.MatchDoesNotExist, input: false, valid: false, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchDoesNotExist, input: false, valid: true, result: assert.Falsef, err: assert.Nilf},
|
|
|
|
{op: api.MatchGt, values: V{"2"}, input: 3, valid: false, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchGt, values: V{"2"}, input: 2, valid: true, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchGt, values: V{"2"}, input: 3, valid: true, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchGt, values: V{"-10"}, input: -3, valid: true, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchGt, values: V{"2"}, input: "3a", valid: true, result: assert.Falsef, err: assert.NotNilf},
|
|
|
|
{op: api.MatchLt, values: V{"2"}, input: "1", valid: false, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchLt, values: V{"2"}, input: "2", valid: true, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchLt, values: V{"-10"}, input: -3, valid: true, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchLt, values: V{"2"}, input: "1", valid: true, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchLt, values: V{"2"}, input: "1.0", valid: true, result: assert.Falsef, err: assert.NotNilf},
|
|
|
|
{op: api.MatchGtLt, values: V{"1", "10"}, input: "1", valid: false, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchGtLt, values: V{"1", "10"}, input: "1", valid: true, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchGtLt, values: V{"1", "10"}, input: "10", valid: true, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchGtLt, values: V{"1", "10"}, input: "2", valid: true, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchGtLt, values: V{"1", "10"}, input: "1.0", valid: true, result: assert.Falsef, err: assert.NotNilf},
|
|
|
|
{op: api.MatchIsTrue, input: true, valid: false, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchIsTrue, input: true, valid: true, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchIsTrue, input: false, valid: true, result: assert.Falsef, err: assert.Nilf},
|
|
|
|
{op: api.MatchIsFalse, input: "false", valid: false, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchIsFalse, input: "false", valid: true, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchIsFalse, input: "true", valid: true, result: assert.Falsef, err: assert.Nilf},
|
|
}
|
|
|
|
for i, tc := range tcs {
|
|
me := api.MustCreateMatchExpression(tc.op, tc.values...)
|
|
res, err := me.Match(tc.valid, tc.input)
|
|
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
|
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
|
}
|
|
|
|
// Check some special error cases separately because MustCreateMatch panics
|
|
tcs = []TC{
|
|
|
|
{op: api.MatchGt, values: V{"3.0"}, input: 1, valid: true},
|
|
{op: api.MatchLt, values: V{"0x2"}, input: 1, valid: true},
|
|
{op: api.MatchGtLt, values: V{"1", "str"}, input: 1, valid: true},
|
|
{op: "non-existent-op", values: V{"1"}, input: 1, valid: true},
|
|
}
|
|
|
|
for i, tc := range tcs {
|
|
me := api.MatchExpression{Op: tc.op, Value: tc.values}
|
|
res, err := me.Match(tc.valid, tc.input)
|
|
assert.Falsef(t, res, "err test case #%d (%v) failed", i, tc)
|
|
assert.NotNilf(t, err, "err test case #%d (%v) failed", i, tc)
|
|
}
|
|
}
|
|
|
|
func TestMatchKeys(t *testing.T) {
|
|
type V = api.MatchValue
|
|
type I = map[string]api.Nil
|
|
type TC struct {
|
|
op api.MatchOp
|
|
values V
|
|
name string
|
|
input I
|
|
result BoolAssertionFuncf
|
|
err ValueAssertionFuncf
|
|
}
|
|
|
|
tcs := []TC{
|
|
{op: api.MatchAny, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{op: api.MatchExists, name: "foo", input: nil, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchExists, name: "foo", input: I{"bar": {}}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchExists, name: "foo", input: I{"bar": {}, "foo": {}}, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{op: api.MatchDoesNotExist, name: "foo", input: nil, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchDoesNotExist, name: "foo", input: I{}, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchDoesNotExist, name: "foo", input: I{"bar": {}}, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchDoesNotExist, name: "foo", input: I{"bar": {}, "foo": {}}, result: assert.Falsef, err: assert.Nilf},
|
|
|
|
// All other ops should return an error
|
|
{op: api.MatchIn, values: V{"foo"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
|
{op: api.MatchNotIn, values: V{"foo"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
|
{op: api.MatchInRegexp, values: V{"foo"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
|
{op: api.MatchGt, values: V{"1"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
|
{op: api.MatchLt, values: V{"1"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
|
{op: api.MatchGtLt, values: V{"1", "10"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
|
{op: api.MatchIsTrue, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
|
{op: api.MatchIsFalse, name: "foo", result: assert.Falsef, err: assert.NotNilf},
|
|
}
|
|
|
|
for i, tc := range tcs {
|
|
me := api.MustCreateMatchExpression(tc.op, tc.values...)
|
|
res, err := me.MatchKeys(tc.name, tc.input)
|
|
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
|
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
|
}
|
|
}
|
|
|
|
func TestMatchValues(t *testing.T) {
|
|
type V = []string
|
|
type I = map[string]string
|
|
|
|
type TC struct {
|
|
op api.MatchOp
|
|
values V
|
|
name string
|
|
input I
|
|
result BoolAssertionFuncf
|
|
err ValueAssertionFuncf
|
|
}
|
|
|
|
tcs := []TC{
|
|
{op: api.MatchAny, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{op: api.MatchIn, values: V{"1", "2"}, name: "foo", input: I{"bar": "2"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchIn, values: V{"1", "2"}, name: "foo", input: I{"foo": "3"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchIn, values: V{"1", "2"}, name: "foo", input: I{"foo": "2"}, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{op: api.MatchNotIn, values: V{"1", "2"}, name: "foo", input: I{"bar": "2"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchNotIn, values: V{"1", "2"}, name: "foo", input: I{"foo": "3"}, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchNotIn, values: V{"1", "2"}, name: "foo", input: I{"foo": "2"}, result: assert.Falsef, err: assert.Nilf},
|
|
|
|
{op: api.MatchInRegexp, values: V{"1", "2"}, name: "foo", input: I{"bar": "2"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchInRegexp, values: V{"1", "[0-8]"}, name: "foo", input: I{"foo": "9"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchInRegexp, values: V{"1", "[0-8]"}, name: "foo", input: I{"foo": "2"}, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{op: api.MatchExists, name: "foo", input: I{"bar": "1"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchExists, name: "foo", input: I{"foo": "1"}, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{op: api.MatchDoesNotExist, name: "foo", input: nil, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchDoesNotExist, name: "foo", input: I{"foo": "1"}, result: assert.Falsef, err: assert.Nilf},
|
|
|
|
{op: api.MatchGt, values: V{"2"}, name: "foo", input: I{"bar": "3"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchGt, values: V{"2"}, name: "foo", input: I{"bar": "3", "foo": "2"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchGt, values: V{"2"}, name: "foo", input: I{"bar": "3", "foo": "3"}, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchGt, values: V{"2"}, name: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.Falsef, err: assert.NotNilf},
|
|
|
|
{op: api.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "1"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "1", "foo": "2"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.Falsef, err: assert.NotNilf},
|
|
|
|
{op: api.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1", "foo": "11"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1", "foo": "-11"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.Truef, err: assert.Nilf},
|
|
{op: api.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.Falsef, err: assert.NotNilf},
|
|
|
|
{op: api.MatchIsTrue, name: "foo", result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchIsTrue, name: "foo", input: I{"foo": "1"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchIsTrue, name: "foo", input: I{"foo": "true"}, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{op: api.MatchIsFalse, name: "foo", input: I{"foo": "true"}, result: assert.Falsef, err: assert.Nilf},
|
|
{op: api.MatchIsFalse, name: "foo", input: I{"foo": "false"}, result: assert.Truef, err: assert.Nilf},
|
|
}
|
|
|
|
for i, tc := range tcs {
|
|
me := api.MustCreateMatchExpression(tc.op, tc.values...)
|
|
res, err := me.MatchValues(tc.name, tc.input)
|
|
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
|
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
|
}
|
|
}
|
|
|
|
func TestMESMatchKeys(t *testing.T) {
|
|
type I = map[string]api.Nil
|
|
type ME = api.MatchedElement
|
|
type O = []ME
|
|
type TC struct {
|
|
mes string
|
|
input I
|
|
output O
|
|
result BoolAssertionFuncf
|
|
err ValueAssertionFuncf
|
|
}
|
|
|
|
tcs := []TC{
|
|
{output: O{}, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{input: I{}, output: O{}, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{input: I{"foo": {}}, output: O{}, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{mes: `
|
|
foo: { op: DoesNotExist }
|
|
bar: { op: Exists }
|
|
`,
|
|
input: I{"bar": {}, "baz": {}, "buzz": {}},
|
|
output: O{ME{"Name": "bar"}, ME{"Name": "foo"}},
|
|
result: assert.Truef, err: assert.Nilf},
|
|
|
|
{mes: `
|
|
foo: { op: DoesNotExist }
|
|
bar: { op: Exists }
|
|
`,
|
|
input: I{"foo": {}, "bar": {}, "baz": {}},
|
|
output: nil,
|
|
result: assert.Falsef, err: assert.Nilf},
|
|
|
|
{mes: `
|
|
foo: { op: In, value: ["bar"] }
|
|
bar: { op: Exists }
|
|
`,
|
|
input: I{"bar": {}, "baz": {}},
|
|
output: nil,
|
|
result: assert.Falsef, err: assert.NotNilf},
|
|
}
|
|
|
|
for i, tc := range tcs {
|
|
mes := &api.MatchExpressionSet{}
|
|
if err := yaml.Unmarshal([]byte(tc.mes), mes); err != nil {
|
|
t.Fatalf("failed to parse data of test case #%d (%v): %v", i, tc, err)
|
|
}
|
|
|
|
res, out, err := mes.MatchGetKeys(tc.input)
|
|
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
|
assert.Equalf(t, tc.output, out, "test case #%d (%v) failed", i, tc)
|
|
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
|
|
|
res, err = mes.MatchKeys(tc.input)
|
|
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
|
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
|
}
|
|
}
|
|
|
|
func TestMESMatchValues(t *testing.T) {
|
|
type I = map[string]string
|
|
type ME = api.MatchedElement
|
|
type O = []ME
|
|
type TC struct {
|
|
mes string
|
|
input I
|
|
output O
|
|
result BoolAssertionFuncf
|
|
err ValueAssertionFuncf
|
|
}
|
|
|
|
tcs := []TC{
|
|
{output: O{}, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{input: I{}, output: O{}, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{input: I{"foo": "bar"}, output: O{}, result: assert.Truef, err: assert.Nilf},
|
|
|
|
{mes: `
|
|
foo: { op: Exists }
|
|
bar: { op: In, value: ["val", "wal"] }
|
|
baz: { op: Gt, value: ["10"] }
|
|
`,
|
|
input: I{"bar": "val"},
|
|
result: assert.Falsef, err: assert.Nilf},
|
|
|
|
{mes: `
|
|
foo: { op: Exists }
|
|
bar: { op: In, value: ["val", "wal"] }
|
|
baz: { op: Gt, value: ["10"] }
|
|
`,
|
|
input: I{"foo": "1", "bar": "val", "baz": "123", "buzz": "light"},
|
|
output: O{ME{"Name": "bar", "Value": "val"}, ME{"Name": "baz", "Value": "123"}, ME{"Name": "foo", "Value": "1"}},
|
|
result: assert.Truef, err: assert.Nilf},
|
|
|
|
{mes: `
|
|
foo: { op: Exists }
|
|
bar: { op: In, value: ["val"] }
|
|
baz: { op: Gt, value: ["10"] }
|
|
`,
|
|
input: I{"foo": "1", "bar": "val", "baz": "123.0"},
|
|
result: assert.Falsef, err: assert.NotNilf},
|
|
}
|
|
|
|
for i, tc := range tcs {
|
|
mes := &api.MatchExpressionSet{}
|
|
if err := yaml.Unmarshal([]byte(tc.mes), mes); err != nil {
|
|
t.Fatalf("failed to parse data of test case #%d (%v): %v", i, tc, err)
|
|
}
|
|
|
|
res, out, err := mes.MatchGetValues(tc.input)
|
|
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
|
assert.Equalf(t, tc.output, out, "test case #%d (%v) failed", i, tc)
|
|
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
|
|
|
res, err = mes.MatchValues(tc.input)
|
|
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
|
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
|
}
|
|
}
|
|
|
|
func TestMESMatchInstances(t *testing.T) {
|
|
type I = api.InstanceFeature
|
|
type ME = api.MatchedElement
|
|
type O = []ME
|
|
type A = map[string]string
|
|
type TC struct {
|
|
mes string
|
|
input []I
|
|
output O
|
|
result BoolAssertionFuncf
|
|
err ValueAssertionFuncf
|
|
}
|
|
|
|
tcs := []TC{
|
|
{output: O{}, result: assert.Falsef, err: assert.Nilf}, // nil instances -> false
|
|
|
|
{input: []I{}, output: O{}, result: assert.Falsef, err: assert.Nilf}, // zero instances -> false
|
|
|
|
{input: []I{I{Attributes: A{}}}, output: O{A{}}, result: assert.Truef, err: assert.Nilf}, // one "empty" instance
|
|
|
|
{mes: `
|
|
foo: { op: Exists }
|
|
bar: { op: Lt, value: ["10"] }
|
|
`,
|
|
input: []I{I{Attributes: A{"foo": "1"}}, I{Attributes: A{"bar": "1"}}},
|
|
output: O{},
|
|
result: assert.Falsef, err: assert.Nilf},
|
|
|
|
{mes: `
|
|
foo: { op: Exists }
|
|
bar: { op: Lt, value: ["10"] }
|
|
`,
|
|
input: []I{I{Attributes: A{"foo": "1"}}, I{Attributes: A{"foo": "2", "bar": "1"}}},
|
|
output: O{A{"foo": "2", "bar": "1"}},
|
|
result: assert.Truef, err: assert.Nilf},
|
|
|
|
{mes: `
|
|
bar: { op: Lt, value: ["10"] }
|
|
`,
|
|
input: []I{I{Attributes: A{"foo": "1"}}, I{Attributes: A{"bar": "0x1"}}},
|
|
result: assert.Falsef, err: assert.NotNilf},
|
|
}
|
|
|
|
for i, tc := range tcs {
|
|
mes := &api.MatchExpressionSet{}
|
|
if err := yaml.Unmarshal([]byte(tc.mes), mes); err != nil {
|
|
t.Fatalf("failed to parse data of test case #%d (%v): %v", i, tc, err)
|
|
}
|
|
|
|
out, err := mes.MatchGetInstances(tc.input)
|
|
assert.Equalf(t, tc.output, out, "test case #%d (%v) failed", i, tc)
|
|
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
|
|
|
res, err := mes.MatchInstances(tc.input)
|
|
tc.result(t, res, "test case #%d (%v) failed", i, tc)
|
|
tc.err(t, err, "test case #%d (%v) failed", i, tc)
|
|
}
|
|
}
|