go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/gerrit/cfgmatcher/matcher_test.go (about)

     1  // Copyright 2020 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 cfgmatcher
    16  
    17  import (
    18  	"testing"
    19  
    20  	"google.golang.org/protobuf/encoding/prototext"
    21  
    22  	cfgpb "go.chromium.org/luci/cv/api/config/v2"
    23  	"go.chromium.org/luci/cv/internal/configs/prjcfg"
    24  	"go.chromium.org/luci/cv/internal/configs/prjcfg/prjcfgtest"
    25  	"go.chromium.org/luci/cv/internal/cvtesting"
    26  
    27  	. "github.com/smartystreets/goconvey/convey"
    28  )
    29  
    30  func TestPartitionConfig(t *testing.T) {
    31  	t.Parallel()
    32  
    33  	Convey("Matcher works", t, func() {
    34  		ct := cvtesting.Test{}
    35  		ctx, cancel := ct.SetUp(t)
    36  		defer cancel()
    37  
    38  		const cfgText = `
    39  			config_groups {
    40  				name: "g1"
    41  				gerrit {
    42  					url: "https://1.example.com"
    43  					projects {
    44  						name: "luci/go"
    45  						ref_regexp:         "refs/heads/.+"
    46  						ref_regexp_exclude: "refs/heads/e.+ed"
    47  					}
    48  					projects {
    49  						name: "default"
    50  						# Default regexp is "refs/heads/master".
    51  					}
    52  				}
    53  			}
    54  			config_groups {
    55  				name: "fallback"
    56  				fallback: YES
    57  				gerrit {
    58  					url: "https://1.example.com"
    59  					projects {
    60  						name: "luci/go"
    61  						ref_regexp:         "refs/heads/.+"
    62  						# NOTE: \\d is for proto parser s.t. actual regexp is just \d
    63  						ref_regexp:         "refs/branch-heads/[\\d]{3,4}"
    64  					}
    65  				}
    66  			}
    67  			config_groups {
    68  				name: "g2"
    69  				gerrit {
    70  					url: "https://1.example.com"
    71  					projects {
    72  						name: "luci/go"
    73  						ref_regexp:         "refs/heads/g2"
    74  					}
    75  				}
    76  				gerrit {
    77  					url: "https://2.example.com"
    78  					projects {
    79  						name: "fo/rk"
    80  						ref_regexp:         "refs/heads/main"
    81  					}
    82  				}
    83  			}`
    84  		cfg := &cfgpb.Config{}
    85  		So(prototext.Unmarshal([]byte(cfgText), cfg), ShouldBeNil)
    86  
    87  		const luciProject = "luci"
    88  		const gHost1 = "1.example.com"
    89  		const gHost2 = "2.example.com"
    90  
    91  		prjcfgtest.Create(ctx, luciProject, cfg)
    92  		meta := prjcfgtest.MustExist(ctx, luciProject)
    93  		hash := meta.Hash()
    94  
    95  		ids := func(ids ...string) []prjcfg.ConfigGroupID {
    96  			ret := make([]prjcfg.ConfigGroupID, len(ids))
    97  			for i, id := range ids {
    98  				ret[i] = prjcfg.MakeConfigGroupID(hash, id)
    99  			}
   100  			return ret
   101  		}
   102  
   103  		testMatches := func(m *Matcher) {
   104  			Convey("matching works", func() {
   105  				So(m.Match("random.host", "luci/go", "refs/heads/main"), ShouldBeEmpty)
   106  				So(m.Match(gHost1, "random/repo", "refs/heads/main"), ShouldBeEmpty)
   107  				So(m.Match(gHost1, "luci/go", "refs/nei/ther"), ShouldBeEmpty)
   108  
   109  				So(m.Match(gHost1, "luci/go", "refs/heads/main"), ShouldResemble, ids("g1"))
   110  				So(m.Match(gHost1, "luci/go", "refs/heads/exclud-not-really"), ShouldResemble, ids("g1"))
   111  				So(m.Match(gHost1, "luci/go", "refs/heads/excluded"), ShouldResemble, ids("fallback"))
   112  
   113  				So(m.Match(gHost1, "luci/go", "refs/branch-heads/12"), ShouldBeEmpty)
   114  				So(m.Match(gHost1, "luci/go", "refs/branch-heads/123"), ShouldResemble, ids("fallback"))
   115  				So(m.Match(gHost1, "luci/go", "refs/branch-heads/1234"), ShouldResemble, ids("fallback"))
   116  				So(m.Match(gHost1, "luci/go", "refs/branch-heads/12345"), ShouldBeEmpty)
   117  
   118  				So(m.Match(gHost1, "luci/go", "refs/heads/g2"), ShouldResemble, ids("g1", "g2"))
   119  				So(m.Match(gHost1, "luci/go", "refs/heads/g2-not-any-more"), ShouldResemble, ids("g1"))
   120  				// Test default.
   121  				So(m.Match(gHost1, "default", "refs/heads/stuff"), ShouldBeEmpty)
   122  				So(m.Match(gHost1, "default", "refs/heads/main"), ShouldBeEmpty)
   123  				So(m.Match(gHost1, "default", "refs/heads/master"), ShouldResemble, ids("g1"))
   124  
   125  				// 2nd host with 2 hosts in the same config group.
   126  				So(m.Match(gHost2, "fo/rk", "refs/heads/main"), ShouldResemble, ids("g2"))
   127  			})
   128  		}
   129  
   130  		Convey("load from Datstore", func() {
   131  			m, err := LoadMatcher(ctx, luciProject, hash)
   132  			So(err, ShouldBeNil)
   133  			testMatches(m)
   134  
   135  			Convey("Serialize/Deserialize", func() {
   136  				bytes, err := m.Serialize()
   137  				So(err, ShouldBeNil)
   138  				m, err := Deserialize(bytes)
   139  				So(err, ShouldBeNil)
   140  				testMatches(m)
   141  			})
   142  		})
   143  	})
   144  }