go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/swarming/server/bq/bqconverter_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 bq 16 17 import ( 18 "context" 19 "testing" 20 "time" 21 22 "google.golang.org/protobuf/types/known/timestamppb" 23 24 "go.chromium.org/luci/gae/impl/memory" 25 "go.chromium.org/luci/gae/service/datastore" 26 27 bqpb "go.chromium.org/luci/swarming/proto/api" 28 "go.chromium.org/luci/swarming/proto/api_v2" 29 configpb "go.chromium.org/luci/swarming/proto/config" 30 "go.chromium.org/luci/swarming/server/model" 31 32 . "github.com/smartystreets/goconvey/convey" 33 . "go.chromium.org/luci/common/testing/assertions" 34 ) 35 36 func createSampleTaskRequest(key *datastore.Key, testTime time.Time) model.TaskRequest { 37 taskSlice := func(val string, exp time.Time) model.TaskSlice { 38 return model.TaskSlice{ 39 Properties: model.TaskProperties{ 40 Idempotent: true, 41 Dimensions: model.TaskDimensions{ 42 "d1": {"v1", "v2"}, 43 "d2": {val}, 44 }, 45 ExecutionTimeoutSecs: 123, 46 GracePeriodSecs: 456, 47 IOTimeoutSecs: 789, 48 Command: []string{"run", val}, 49 RelativeCwd: "./rel/cwd", 50 Env: model.Env{ 51 "k1": "v1", 52 "k2": val, 53 }, 54 EnvPrefixes: model.EnvPrefixes{ 55 "p1": {"v1", "v2"}, 56 "p2": {val}, 57 }, 58 Caches: []model.CacheEntry{ 59 {Name: "n1", Path: "p1"}, 60 {Name: "n2", Path: "p2"}, 61 }, 62 CASInputRoot: model.CASReference{ 63 CASInstance: "cas-inst", 64 Digest: model.CASDigest{ 65 Hash: "cas-hash", 66 SizeBytes: 1234, 67 }, 68 }, 69 CIPDInput: model.CIPDInput{ 70 Server: "server", 71 ClientPackage: model.CIPDPackage{ 72 PackageName: "client-package", 73 Version: "client-version", 74 }, 75 Packages: []model.CIPDPackage{ 76 { 77 PackageName: "pkg1", 78 Version: "ver1", 79 Path: "path1", 80 }, 81 { 82 PackageName: "pkg2", 83 Version: "ver2", 84 Path: "path2", 85 }, 86 }, 87 }, 88 Outputs: []string{"o1", "o2"}, 89 HasSecretBytes: true, 90 Containment: model.Containment{ 91 LowerPriority: true, 92 ContainmentType: apipb.ContainmentType_NOT_SPECIFIED, 93 LimitProcesses: 456, 94 LimitTotalCommittedMemory: 789, 95 }, 96 }, 97 ExpirationSecs: int64(exp.Sub(testTime).Seconds()), 98 WaitForCapacity: true, 99 } 100 } 101 return model.TaskRequest{ 102 Key: key, 103 TxnUUID: "txn-uuid", 104 TaskSlices: []model.TaskSlice{ 105 taskSlice("a", testTime.Add(10*time.Minute)), 106 taskSlice("b", testTime.Add(20*time.Minute)), 107 }, 108 Created: testTime, 109 Expiration: testTime.Add(20 * time.Minute), 110 Name: "name", 111 ParentTaskID: datastore.Nullable[string, datastore.Indexed]{}, 112 Authenticated: "user:authenticated", 113 User: "user", 114 Tags: []string{"tag1", "tag2"}, 115 ManualTags: []string{"tag1"}, 116 ServiceAccount: "service-account", 117 Realm: "realm", 118 RealmsEnabled: true, 119 SchedulingAlgorithm: configpb.Pool_SCHEDULING_ALGORITHM_FIFO, 120 Priority: 123, 121 BotPingToleranceSecs: 456, 122 RBEInstance: "rbe-instance", 123 PubSubTopic: "pubsub-topic", 124 PubSubAuthToken: "pubsub-auth-token", 125 PubSubUserData: "pubsub-user-data", 126 ResultDBUpdateToken: "resultdb-update-token", 127 ResultDB: model.ResultDBConfig{Enable: true}, 128 HasBuildTask: true, 129 } 130 } 131 132 func createSampleBQTaskRequest(taskID string, testTime time.Time) *bqpb.TaskRequest { 133 taskSlice := func(val string, exp time.Time) *bqpb.TaskSlice { 134 return &bqpb.TaskSlice{ 135 Properties: &bqpb.TaskProperties{ 136 Idempotent: true, 137 Dimensions: []*bqpb.StringListPair{ 138 { 139 Key: "d1", 140 Values: []string{"v1", "v2"}, 141 }, 142 { 143 Key: "d2", 144 Values: []string{val}, 145 }, 146 }, 147 ExecutionTimeout: seconds(123), 148 GracePeriod: seconds(456), 149 IoTimeout: seconds(789), 150 Command: []string{"run", val}, 151 RelativeCwd: "./rel/cwd", 152 Env: []*bqpb.StringPair{ 153 { 154 Key: "k1", 155 Value: "v1", 156 }, 157 { 158 Key: "k2", 159 Value: val, 160 }, 161 }, 162 EnvPaths: []*bqpb.StringListPair{ 163 { 164 Key: "p1", 165 Values: []string{"v1", "v2"}, 166 }, 167 { 168 Key: "p2", 169 Values: []string{val}, 170 }, 171 }, 172 NamedCaches: []*bqpb.NamedCacheEntry{ 173 {Name: "n1", DestPath: "p1"}, 174 {Name: "n2", DestPath: "p2"}, 175 }, 176 CasInputRoot: &bqpb.CASReference{ 177 CasInstance: "cas-inst", 178 Digest: &bqpb.Digest{ 179 Hash: "cas-hash", 180 SizeBytes: 1234, 181 }, 182 }, 183 CipdInputs: []*bqpb.CIPDPackage{ 184 { 185 PackageName: "pkg1", 186 Version: "ver1", 187 DestPath: "path1", 188 }, 189 { 190 PackageName: "pkg2", 191 Version: "ver2", 192 DestPath: "path2", 193 }, 194 }, 195 Outputs: []string{"o1", "o2"}, 196 HasSecretBytes: true, 197 Containment: &bqpb.Containment{ 198 ContainmentType: bqpb.Containment_NOT_SPECIFIED, 199 }, 200 }, 201 Expiration: seconds(int64(exp.Sub(testTime).Seconds())), 202 WaitForCapacity: true, 203 } 204 } 205 return &bqpb.TaskRequest{ 206 TaskId: taskID, 207 TaskSlices: []*bqpb.TaskSlice{ 208 taskSlice("a", testTime.Add(10*time.Minute)), 209 taskSlice("b", testTime.Add(20*time.Minute)), 210 }, 211 Name: "name", 212 CreateTime: timestamppb.New(testTime), 213 ParentTaskId: "", 214 Authenticated: "user:authenticated", 215 User: "user", 216 Tags: []string{"tag1", "tag2"}, 217 ServiceAccount: "service-account", 218 Realm: "realm", 219 Priority: 123, 220 BotPingTolerance: seconds(456), 221 PubsubNotification: &bqpb.PubSub{ 222 Topic: "pubsub-topic", 223 Userdata: "pubsub-user-data", 224 }, 225 Resultdb: &bqpb.ResultDBCfg{Enable: true}, 226 } 227 } 228 229 func TestTaskRequestConversion(t *testing.T) { 230 t.Parallel() 231 var testTime = time.Date(2023, time.January, 1, 2, 3, 4, 0, time.UTC) 232 Convey("Convert TaskRequest with empty parent task", t, func() { 233 ctx := memory.Use(context.Background()) 234 taskID := "65aba3a3e6b99310" 235 key, err := model.TaskIDToRequestKey(ctx, taskID) 236 So(err, ShouldBeNil) 237 sampleRequest := createSampleTaskRequest(key, testTime) 238 expected := createSampleBQTaskRequest(taskID, testTime) 239 actual := taskRequestToBQ(&sampleRequest) 240 So(actual, ShouldResembleProto, expected) 241 }) 242 243 Convey("Converting empty EnvPrefixes works", t, func() { 244 ctx := memory.Use(context.Background()) 245 taskID := "65aba3a3e6b99310" 246 key, err := model.TaskIDToRequestKey(ctx, taskID) 247 So(err, ShouldBeNil) 248 sampleRequest := createSampleTaskRequest(key, testTime) 249 // Set this field to zero 250 sampleRequest.TaskSlices[0].Properties.EnvPrefixes = make(model.EnvPrefixes) 251 252 // Set this list to zero too 253 expected := createSampleBQTaskRequest(taskID, testTime) 254 expected.TaskSlices[0].Properties.EnvPaths = make([]*bqpb.StringListPair, 0) 255 actual := taskRequestToBQ(&sampleRequest) 256 So(err, ShouldBeNil) 257 So(actual, ShouldResembleProto, expected) 258 }) 259 }