go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/examples/appengine/tq/main_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 main 16 17 import ( 18 "context" 19 "testing" 20 "time" 21 22 "go.chromium.org/luci/gae/filter/txndefer" 23 "go.chromium.org/luci/gae/impl/memory" 24 "go.chromium.org/luci/gae/service/datastore" 25 "google.golang.org/protobuf/reflect/protoreflect" 26 27 "go.chromium.org/luci/common/clock" 28 "go.chromium.org/luci/common/clock/testclock" 29 "go.chromium.org/luci/examples/appengine/tq/taskspb" 30 31 "go.chromium.org/luci/server/tq" 32 "go.chromium.org/luci/server/tq/tqtesting" 33 34 . "github.com/smartystreets/goconvey/convey" 35 . "go.chromium.org/luci/common/testing/assertions" 36 ) 37 38 func TestQueue(t *testing.T) { 39 t.Parallel() 40 41 Convey("Chain works", t, func() { 42 var epoch = time.Unix(1500000000, 0).UTC() 43 44 // Need the test clock to emulate delayed tasks. Tick it whenever TQ waits. 45 ctx, tc := testclock.UseTime(context.Background(), epoch) 46 tc.SetTimerCallback(func(d time.Duration, t clock.Timer) { 47 if testclock.HasTags(t, tqtesting.ClockTag) { 48 tc.Add(d) 49 } 50 }) 51 52 // Need the datastore fake with txndefer filter installed. This filter is 53 // required when using server/tq with transactional tasks. AddTask calls 54 // will panic otherwise. It is installed in production server contexts by 55 // default. 56 ctx = txndefer.FilterRDS(memory.Use(ctx)) 57 58 // Put a Cloud Tasks scheduler fake to be used by AddTask. 59 ctx, sched := tq.TestingContext(ctx, nil) 60 61 var succeeded tqtesting.TaskList 62 63 // Can tweak it more, if necessary. 64 sched.TaskSucceeded = tqtesting.TasksCollector(&succeeded) 65 sched.TaskFailed = func(ctx context.Context, task *tqtesting.Task) { panic("should not fail") } 66 67 // Enqueue the first task. 68 So(EnqueueCountDown(ctx, 5), ShouldBeNil) 69 70 // Examine currently enqueue tasks. 71 So(sched.Tasks().Payloads(), ShouldResembleProto, []protoreflect.ProtoMessage{ 72 &taskspb.CountDownTask{Number: 5}, 73 }) 74 75 // Simulate the Cloud Tasks run loop until there's no more pending or 76 // executing tasks left 77 sched.Run(ctx, tqtesting.StopWhenDrained()) 78 79 // Verify all expected entities have been created, and when expected. 80 numbers := map[int64]time.Duration{} 81 datastore.GetTestable(ctx).CatchupIndexes() 82 datastore.Run(ctx, datastore.NewQuery("ExampleEntity"), func(e *ExampleEntity) { 83 numbers[e.ID] = e.LastUpdate.Sub(epoch) 84 }) 85 So(numbers, ShouldResemble, map[int64]time.Duration{ 86 5: 100 * time.Millisecond, 87 4: 200 * time.Millisecond, 88 3: 300 * time.Millisecond, 89 2: 400 * time.Millisecond, 90 1: 500 * time.Millisecond, 91 }) 92 93 // Can also examine all executed tasks. 94 So(succeeded.Payloads(), ShouldResembleProto, []protoreflect.ProtoMessage{ 95 &taskspb.CountDownTask{Number: 5}, 96 &taskspb.CountDownTask{Number: 4}, 97 &taskspb.CountDownTask{Number: 3}, 98 &taskspb.CountDownTask{Number: 2}, 99 &taskspb.CountDownTask{Number: 1}, 100 &taskspb.CountDownTask{Number: 0}, 101 }) 102 }) 103 }