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 }