go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/config/validation/rules_test.go (about)

     1  // Copyright 2018 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package validation
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"testing"
    21  
    22  	"go.chromium.org/luci/common/errors"
    23  
    24  	. "github.com/smartystreets/goconvey/convey"
    25  	. "go.chromium.org/luci/common/testing/assertions"
    26  )
    27  
    28  func TestRuleSet(t *testing.T) {
    29  	t.Parallel()
    30  
    31  	Convey("With validator callback", t, func() {
    32  		ctx := context.Background()
    33  
    34  		type call struct {
    35  			validatorID string
    36  			configSet   string
    37  			path        string
    38  		}
    39  		calls := []call{}
    40  
    41  		validator := func(id string) Func {
    42  			return func(ctx *Context, configSet, path string, content []byte) error {
    43  				calls = append(calls, call{id, configSet, path})
    44  				return nil
    45  			}
    46  		}
    47  
    48  		Convey("Works", func() {
    49  			r := NewRuleSet()
    50  
    51  			r.Vars.Register("a", func(context.Context) (string, error) { return "a_val", nil })
    52  			r.Vars.Register("b", func(context.Context) (string, error) { return "b_val", nil })
    53  
    54  			r.Add("services/${a}", "paths/a", validator("rule_1"))
    55  			r.Add("services/${a}", "paths/${b}", validator("rule_2"))
    56  			r.Add("services/c", "paths/c", validator("rule_3"))
    57  			r.Add("regex:services/.*", "paths/c", validator("rule_4"))
    58  
    59  			patterns, err := r.ConfigPatterns(ctx)
    60  			So(err, ShouldBeNil)
    61  
    62  			type pair struct {
    63  				configSet string
    64  				path      string
    65  			}
    66  			asPairs := make([]pair, len(patterns))
    67  			for i, p := range patterns {
    68  				asPairs[i] = pair{p.ConfigSet.String(), p.Path.String()}
    69  			}
    70  			So(asPairs, ShouldResemble, []pair{
    71  				{"exact:services/a_val", "exact:paths/a"},
    72  				{"exact:services/a_val", "exact:paths/b_val"},
    73  				{"exact:services/c", "exact:paths/c"},
    74  				{"regex:^services/.*$", "exact:paths/c"},
    75  			})
    76  
    77  			callValidator := func(configSet, path string) {
    78  				c := Context{Context: ctx}
    79  				So(r.ValidateConfig(&c, configSet, path, []byte("body")), ShouldBeNil)
    80  			}
    81  
    82  			callValidator("services/unknown", "paths/a")
    83  			callValidator("services/a_val", "paths/a")
    84  			callValidator("services/a_val", "paths/b_val")
    85  			callValidator("services/a_val", "paths/c")
    86  			callValidator("services/c", "paths/c")
    87  
    88  			So(calls, ShouldResemble, []call{
    89  				{"rule_1", "services/a_val", "paths/a"},
    90  				{"rule_2", "services/a_val", "paths/b_val"},
    91  				{"rule_4", "services/a_val", "paths/c"},
    92  				{"rule_3", "services/c", "paths/c"},
    93  				{"rule_4", "services/c", "paths/c"},
    94  			})
    95  		})
    96  
    97  		Convey("Error in the var callback", func() {
    98  			r := NewRuleSet()
    99  			r.Vars.Register("a", func(context.Context) (string, error) { return "", fmt.Errorf("boom") })
   100  			r.Add("services/${a}", "paths/a", validator("rule_1"))
   101  			err := r.ValidateConfig(&Context{Context: ctx}, "services/zzz", "some path", []byte("body"))
   102  			So(err, ShouldErrLike, "boom")
   103  		})
   104  
   105  		Convey("Missing variables", func() {
   106  			r := NewRuleSet()
   107  			r.Vars.Register("a", func(context.Context) (string, error) { return "a_val", nil })
   108  			r.Add("${zzz}", "a", validator("1"))
   109  			r.Add("a", "${zzz}", validator("1"))
   110  
   111  			_, err := r.ConfigPatterns(ctx)
   112  			merr := err.(errors.MultiError)
   113  			So(merr, ShouldHaveLength, 2)
   114  			So(merr[0], ShouldErrLike,
   115  				`config set pattern "exact:${zzz}": no placeholder named "zzz" is registered`)
   116  			So(merr[1], ShouldErrLike,
   117  				`path pattern "exact:${zzz}": no placeholder named "zzz" is registered`)
   118  
   119  			err = r.ValidateConfig(&Context{Context: ctx}, "set", "path", nil)
   120  			merr = err.(errors.MultiError)
   121  			So(merr, ShouldHaveLength, 2)
   122  			So(merr[0], ShouldErrLike,
   123  				`config set pattern "exact:${zzz}": no placeholder named "zzz" is registered`)
   124  			So(merr[1], ShouldErrLike,
   125  				`path pattern "exact:${zzz}": no placeholder named "zzz" is registered`)
   126  		})
   127  
   128  		Convey("Pattern is validated", func() {
   129  			r := NewRuleSet()
   130  			r.Vars.Register("a", func(context.Context) (string, error) { return "a_val", nil })
   131  			r.Add("unknown:${a}", "a", validator("1"))
   132  			r.Add("a", "unknown:${a}", validator("1"))
   133  
   134  			_, err := r.ConfigPatterns(ctx)
   135  			merr := err.(errors.MultiError)
   136  			So(merr, ShouldHaveLength, 2)
   137  			So(merr[0], ShouldErrLike,
   138  				`config set pattern "unknown:${a}": unknown pattern kind: "unknown"`)
   139  			So(merr[1], ShouldErrLike,
   140  				`path pattern "unknown:${a}": unknown pattern kind: "unknown"`)
   141  
   142  			err = r.ValidateConfig(&Context{Context: ctx}, "set", "path", nil)
   143  			merr = err.(errors.MultiError)
   144  			So(merr, ShouldHaveLength, 2)
   145  			So(merr[0], ShouldErrLike,
   146  				`config set pattern "unknown:${a}": unknown pattern kind: "unknown"`)
   147  			So(merr[1], ShouldErrLike,
   148  				`path pattern "unknown:${a}": unknown pattern kind: "unknown"`)
   149  		})
   150  	})
   151  }