go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/internal/testresults/gerritchangelists/span_test.go (about)

     1  // Copyright 2023 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 gerritchangelists
    16  
    17  import (
    18  	"context"
    19  	"strings"
    20  	"testing"
    21  	"time"
    22  
    23  	"go.chromium.org/luci/server/span"
    24  
    25  	"go.chromium.org/luci/analysis/internal/testutil"
    26  	analysispb "go.chromium.org/luci/analysis/proto/v1"
    27  
    28  	. "github.com/smartystreets/goconvey/convey"
    29  	. "go.chromium.org/luci/common/testing/assertions"
    30  )
    31  
    32  func TestSpan(t *testing.T) {
    33  	Convey("With Spanner Test Database", t, func() {
    34  		ctx := testutil.IntegrationTestContext(t)
    35  
    36  		Convey("With test data", func() {
    37  			clsToCreate := []*GerritChangelist{
    38  				{
    39  					Project:      "testprojecta",
    40  					Host:         "mysource-review.googlesource.com",
    41  					Change:       123456,
    42  					OwnerKind:    analysispb.ChangelistOwnerKind_AUTOMATION,
    43  					CreationTime: time.Date(2021, 2, 3, 4, 5, 6, 0, time.UTC),
    44  				},
    45  				{
    46  					Project:      "testprojectb",
    47  					Host:         "mysource-internal-review.googlesource.com",
    48  					Change:       123456,
    49  					OwnerKind:    analysispb.ChangelistOwnerKind_HUMAN,
    50  					CreationTime: time.Date(2023, 2, 3, 4, 5, 6, 0, time.UTC),
    51  				},
    52  				{
    53  					Project:      "testprojectb",
    54  					Host:         "mysource-internal-review.googlesource.com",
    55  					Change:       123457,
    56  					OwnerKind:    analysispb.ChangelistOwnerKind_HUMAN,
    57  					CreationTime: time.Date(2020, 2, 3, 4, 5, 6, 0, time.UTC),
    58  				},
    59  				{
    60  					Project:      "testprojectb",
    61  					Host:         "mysource-review.googlesource.com",
    62  					Change:       123456,
    63  					OwnerKind:    analysispb.ChangelistOwnerKind_CHANGELIST_OWNER_UNSPECIFIED,
    64  					CreationTime: time.Date(2022, 2, 3, 4, 5, 6, 0, time.UTC),
    65  				},
    66  			}
    67  			err := SetGerritChangelistsForTesting(ctx, clsToCreate)
    68  			So(err, ShouldBeNil)
    69  
    70  			Convey("ReadAll", func() {
    71  				cls, err := ReadAll(span.Single(ctx))
    72  				So(err, ShouldBeNil)
    73  				So(cls, ShouldResemble, clsToCreate)
    74  			})
    75  			Convey("Read", func() {
    76  				expectedCLs := make(map[Key]*GerritChangelist)
    77  				keys := map[Key]struct{}{}
    78  				Convey("Zero", func() {
    79  					cls, err := Read(span.Single(ctx), keys)
    80  					So(err, ShouldBeNil)
    81  					So(cls, ShouldResemble, expectedCLs)
    82  				})
    83  				Convey("One", func() {
    84  					k := Key{
    85  						Project: "testprojectb",
    86  						Host:    "mysource-internal-review.googlesource.com",
    87  						Change:  123456,
    88  					}
    89  					keys[k] = struct{}{}
    90  					expectedCLs[k] = &GerritChangelist{
    91  						Project:      "testprojectb",
    92  						Host:         "mysource-internal-review.googlesource.com",
    93  						Change:       123456,
    94  						OwnerKind:    analysispb.ChangelistOwnerKind_HUMAN,
    95  						CreationTime: time.Date(2023, 2, 3, 4, 5, 6, 0, time.UTC),
    96  					}
    97  
    98  					cls, err := Read(span.Single(ctx), keys)
    99  					So(err, ShouldBeNil)
   100  					So(cls, ShouldResemble, expectedCLs)
   101  				})
   102  				Convey("Many", func() {
   103  					for _, entry := range clsToCreate {
   104  						key := Key{entry.Project, entry.Host, entry.Change}
   105  						keys[key] = struct{}{}
   106  						expectedCLs[key] = entry
   107  					}
   108  
   109  					cls, err := Read(span.Single(ctx), keys)
   110  					So(err, ShouldBeNil)
   111  					So(cls, ShouldResemble, expectedCLs)
   112  				})
   113  			})
   114  		})
   115  		Convey("CreateOrUpdate", func() {
   116  			save := func(g *GerritChangelist) (time.Time, error) {
   117  				commitTime, err := span.ReadWriteTransaction(ctx, func(ctx context.Context) error {
   118  					m, err := CreateOrUpdate(g)
   119  					if err != nil {
   120  						return err
   121  					}
   122  					span.BufferWrite(ctx, m)
   123  					return nil
   124  				})
   125  				return commitTime.In(time.UTC), err
   126  			}
   127  
   128  			cl := GerritChangelist{
   129  				Project:   "testprojectb",
   130  				Host:      "mysource-internal-review.googlesource.com",
   131  				Change:    123456,
   132  				OwnerKind: analysispb.ChangelistOwnerKind_HUMAN,
   133  			}
   134  			key := Key{
   135  				Project: "testprojectb",
   136  				Host:    "mysource-internal-review.googlesource.com",
   137  				Change:  123456,
   138  			}
   139  			keys := map[Key]struct{}{}
   140  			keys[key] = struct{}{}
   141  
   142  			Convey("Baseline", func() {
   143  				expectedCL := cl
   144  
   145  				commitTime, err := save(&cl)
   146  				So(err, ShouldBeNil)
   147  				expectedCL.CreationTime = commitTime
   148  
   149  				cls, err := Read(span.Single(ctx), keys)
   150  				So(err, ShouldBeNil)
   151  				So(cls, ShouldHaveLength, 1)
   152  				So(cls[key], ShouldResemble, &expectedCL)
   153  			})
   154  			Convey("Owner kind unset", func() {
   155  				// If we do not have permission to read the changelist, we still may
   156  				// write a cache entry to avoid battering gerrit with requests.
   157  				// In this case, the owner kind will be UNSPECIFIED.
   158  				cl.OwnerKind = analysispb.ChangelistOwnerKind_CHANGELIST_OWNER_UNSPECIFIED
   159  
   160  				expectedCL := cl
   161  
   162  				commitTime, err := save(&cl)
   163  				So(err, ShouldBeNil)
   164  				expectedCL.CreationTime = commitTime
   165  
   166  				cls, err := Read(span.Single(ctx), keys)
   167  				So(err, ShouldBeNil)
   168  				So(cls, ShouldHaveLength, 1)
   169  				So(cls[key], ShouldResemble, &expectedCL)
   170  			})
   171  
   172  			Convey("Invalid GerritChangelists are rejected", func() {
   173  				Convey("Project is empty", func() {
   174  					cl.Project = ""
   175  					_, err := save(&cl)
   176  					So(err, ShouldErrLike, "project: unspecified")
   177  				})
   178  				Convey("Project is invalid", func() {
   179  					cl.Project = "!invalid"
   180  					_, err := save(&cl)
   181  					So(err, ShouldErrLike, `project: must match ^[a-z0-9\-]{1,40}$`)
   182  				})
   183  				Convey("Host is empty", func() {
   184  					cl.Host = ""
   185  					_, err := save(&cl)
   186  					So(err, ShouldErrLike, "host: must have a length between 1 and 255")
   187  				})
   188  				Convey("Host is too long", func() {
   189  					cl.Host = strings.Repeat("h", 256)
   190  					_, err := save(&cl)
   191  					So(err, ShouldErrLike, "host: must have a length between 1 and 255")
   192  				})
   193  				Convey("Change is empty", func() {
   194  					cl.Change = 0
   195  					_, err := save(&cl)
   196  					So(err, ShouldErrLike, "change: must be set and positive")
   197  				})
   198  				Convey("Change is negative", func() {
   199  					cl.Change = -1
   200  					_, err := save(&cl)
   201  					So(err, ShouldErrLike, "change: must be set and positive")
   202  				})
   203  			})
   204  		})
   205  	})
   206  }