2
0
mirror of https://github.com/hibiken/asynq.git synced 2024-12-27 16:13:40 +08:00

Rewrite test for DeleteAggregationSet function with a new pattern

This commit is contained in:
Ken Hibino 2022-03-12 15:18:32 -08:00
parent e63f41fb24
commit 4b716780ef

View File

@ -3356,65 +3356,131 @@ func TestAggregationCheck(t *testing.T) {
} }
} }
// TODO: Rewrite this test with the new pattern of using redis key-value as data.
func TestDeleteAggregationSet(t *testing.T) { func TestDeleteAggregationSet(t *testing.T) {
r := setup(t) r := setup(t)
defer r.Close() defer r.Close()
now := time.Now() now := time.Now()
ctx := context.Background()
setID := uuid.NewString() setID := uuid.NewString()
msg1 := h.NewTaskMessageBuilder().SetType("foo").SetQueue("default").SetGroup("mygroup").Build() otherSetID := uuid.NewString()
msg2 := h.NewTaskMessageBuilder().SetType("bar").SetQueue("default").SetGroup("mygroup").Build() m1 := h.NewTaskMessageBuilder().SetQueue("default").SetGroup("mygroup").Build()
msg3 := h.NewTaskMessageBuilder().SetType("baz").SetQueue("default").SetGroup("mygroup").Build() m2 := h.NewTaskMessageBuilder().SetQueue("default").SetGroup("mygroup").Build()
m3 := h.NewTaskMessageBuilder().SetQueue("default").SetGroup("mygroup").Build()
tests := []struct { tests := []struct {
aggregationSet []base.Z desc string
qname string // initial data
gname string tasks []*taskData
setID string aggregationSets map[string][]*redis.Z
allAggregationSets map[string][]*redis.Z
// args
ctx context.Context
qname string
gname string
setID string
// expectations
wantDeletedKeys []string // redis key to check for non existence
wantAggregationSets map[string][]redis.Z
wantAllAggregationSets map[string][]redis.Z
}{ }{
{ {
aggregationSet: []base.Z{ desc: "with a sigle active aggregation set",
{msg1, now.Add(-3 * time.Minute).Unix()}, tasks: []*taskData{
{msg2, now.Add(-2 * time.Minute).Unix()}, {msg: m1, state: base.TaskStateAggregating},
{msg3, now.Add(-1 * time.Minute).Unix()}, {msg: m2, state: base.TaskStateAggregating},
{msg: m3, state: base.TaskStateAggregating},
}, },
aggregationSets: map[string][]*redis.Z{
base.AggregationSetKey("default", "mygroup", setID): {
{Member: m1.ID, Score: float64(now.Add(-5 * time.Minute).Unix())},
{Member: m2.ID, Score: float64(now.Add(-4 * time.Minute).Unix())},
{Member: m3.ID, Score: float64(now.Add(-3 * time.Minute).Unix())},
},
},
allAggregationSets: map[string][]*redis.Z{
base.AllAggregationSets("default"): {
{Member: base.AggregationSetKey("default", "mygroup", setID), Score: float64(now.Add(aggregationTimeout).Unix())},
},
},
ctx: context.Background(),
qname: "default", qname: "default",
gname: "mygroup", gname: "mygroup",
setID: setID, setID: setID,
wantDeletedKeys: []string{
base.AggregationSetKey("default", "mygroup", setID),
base.TaskKey(m1.Queue, m1.ID),
base.TaskKey(m2.Queue, m2.ID),
base.TaskKey(m3.Queue, m3.ID),
},
wantAggregationSets: map[string][]redis.Z{},
wantAllAggregationSets: map[string][]redis.Z{
base.AllAggregationSets("default"): {},
},
},
{
desc: "with multiple active aggregation sets",
tasks: []*taskData{
{msg: m1, state: base.TaskStateAggregating},
{msg: m2, state: base.TaskStateAggregating},
{msg: m3, state: base.TaskStateAggregating},
},
aggregationSets: map[string][]*redis.Z{
base.AggregationSetKey("default", "mygroup", setID): {
{Member: m1.ID, Score: float64(now.Add(-5 * time.Minute).Unix())},
},
base.AggregationSetKey("default", "mygroup", otherSetID): {
{Member: m2.ID, Score: float64(now.Add(-4 * time.Minute).Unix())},
{Member: m3.ID, Score: float64(now.Add(-3 * time.Minute).Unix())},
},
},
allAggregationSets: map[string][]*redis.Z{
base.AllAggregationSets("default"): {
{Member: base.AggregationSetKey("default", "mygroup", setID), Score: float64(now.Add(aggregationTimeout).Unix())},
{Member: base.AggregationSetKey("default", "mygroup", otherSetID), Score: float64(now.Add(aggregationTimeout).Unix())},
},
},
ctx: context.Background(),
qname: "default",
gname: "mygroup",
setID: setID,
wantDeletedKeys: []string{
base.AggregationSetKey("default", "mygroup", setID),
base.TaskKey(m1.Queue, m1.ID),
},
wantAggregationSets: map[string][]redis.Z{
base.AggregationSetKey("default", "mygroup", otherSetID): {
{Member: m2.ID, Score: float64(now.Add(-4 * time.Minute).Unix())},
{Member: m3.ID, Score: float64(now.Add(-3 * time.Minute).Unix())},
},
},
wantAllAggregationSets: map[string][]redis.Z{
base.AllAggregationSets("default"): {
{Member: base.AggregationSetKey("default", "mygroup", otherSetID), Score: float64(now.Add(aggregationTimeout).Unix())},
},
},
}, },
} }
for _, tc := range tests { for _, tc := range tests {
h.FlushDB(t, r.client) h.FlushDB(t, r.client)
h.SeedAggregationSet(t, r.client, tc.aggregationSet, tc.qname, tc.gname, tc.setID) t.Run(tc.desc, func(t *testing.T) {
key := base.AggregationSetKey(tc.qname, tc.gname, tc.setID) SeedTasks(t, r.client, tc.tasks)
if err := r.client.ZAdd(context.Background(), SeedZSets(t, r.client, tc.aggregationSets)
base.AllAggregationSets(tc.qname), SeedZSets(t, r.client, tc.allAggregationSets)
&redis.Z{Member: key, Score: float64(now.Add(aggregationTimeout).Unix())}).Err(); err != nil {
t.Fatal(err)
}
if err := r.DeleteAggregationSet(ctx, tc.qname, tc.gname, tc.setID); err != nil { if err := r.DeleteAggregationSet(tc.ctx, tc.qname, tc.gname, tc.setID); err != nil {
t.Fatalf("DeleteAggregationSet returned error: %v", err) t.Fatalf("DeleteAggregationSet returned error: %v", err)
}
// Check if the set is deleted.
if r.client.Exists(ctx, key).Val() != 0 {
t.Errorf("aggregation set key %q still exists", key)
}
// Check all tasks in the set are deleted.
for _, z := range tc.aggregationSet {
taskKey := base.TaskKey(z.Message.Queue, z.Message.ID)
if r.client.Exists(ctx, taskKey).Val() != 0 {
t.Errorf("task key %q still exists", taskKey)
} }
}
if _, err := r.client.ZScore(ctx, base.AllAggregationSets(tc.qname), key).Result(); err != redis.Nil { for _, key := range tc.wantDeletedKeys {
t.Errorf("aggregation_set key %q is still in key %q", key, base.AllAggregationSets(tc.qname)) if r.client.Exists(context.Background(), key).Val() != 0 {
} t.Errorf("key=%q still exists, want deleted", key)
}
}
AssertZSets(t, r.client, tc.wantAllAggregationSets)
})
} }
} }
@ -3505,6 +3571,40 @@ func TestReclaimStaleAggregationSets(t *testing.T) {
} }
} }
// taskData holds the data required to seed tasks under the task key in test.
type taskData struct {
msg *base.TaskMessage
state base.TaskState
pendingSince time.Time
}
// TODO: move this helper somewhere more canonical
func SeedTasks(tb testing.TB, r redis.UniversalClient, taskData []*taskData) {
for _, data := range taskData {
msg := data.msg
ctx := context.Background()
key := base.TaskKey(msg.Queue, msg.ID)
v := map[string]interface{}{
"msg": h.MustMarshal(tb, msg),
"state": data.state.String(),
"unique_key": msg.UniqueKey,
"group": msg.GroupKey,
}
if !data.pendingSince.IsZero() {
v["pending_since"] = data.pendingSince.Unix()
}
if err := r.HSet(ctx, key, v).Err(); err != nil {
tb.Fatalf("Failed to write task data in redis: %v", err)
}
if len(msg.UniqueKey) > 0 {
err := r.SetNX(ctx, msg.UniqueKey, msg.ID, 1*time.Minute).Err()
if err != nil {
tb.Fatalf("Failed to set unique lock in redis: %v", err)
}
}
}
}
// TODO: move this helper somewhere more canonical // TODO: move this helper somewhere more canonical
func SeedZSets(tb testing.TB, r redis.UniversalClient, zsets map[string][]*redis.Z) { func SeedZSets(tb testing.TB, r redis.UniversalClient, zsets map[string][]*redis.Z) {
for key, zs := range zsets { for key, zs := range zsets {