func TestQStatus(t *T) { client := newClient() queues := []string{ clients.RandQueueName(), clients.RandQueueName(), } Dispatch(client, "qstatus", queues) readAndAssertQStatus(t, client, []queueInfo{ queueInfo{queues[0], 0, 0, 0}, queueInfo{queues[1], 0, 0, 0}, }) // Make sure that when a client is registered to a queue that it shows up in // the full list of queues, even if it doesn't have any items emptyQueue := clients.RandQueueName() Dispatch(client, "qregister", []string{emptyQueue}) readAndAssertStr(t, client, "OK") Dispatch(client, "qstatus", []string{}) infos, err := read(t, client).Array() require.Nil(t, err) found := 0 for _, infoM := range infos { a, err := infoM.Array() require.Nil(t, err) require.True(t, len(a) == 4) queueName, err := a[0].Str() require.Nil(t, err) if queueName == emptyQueue { found++ } } assert.Equal(t, 1, found) }
func TestQRegister(t *T) { client := newClient() queues := []string{ clients.RandQueueName(), clients.RandQueueName(), } Dispatch(client, "qregister", queues) readAndAssertStr(t, client, "OK") }
func TestPeeks(t *T) { client := newClient() queue := clients.RandQueueName() events := []struct{ eventID, event string }{ {"0", "foo"}, {"1", "bar"}, {"2", "baz"}, } eventFirst := events[0] eventLast := events[len(events)-1] Dispatch(client, "qrpeek", []string{queue}) readAndAssertNil(t, client) Dispatch(client, "qlpeek", []string{queue}) readAndAssertNil(t, client) for i := range events { Dispatch(client, "qlpush", []string{queue, events[i].eventID, events[i].event}) readAndAssertStr(t, client, "OK") } Dispatch(client, "qrpeek", []string{queue}) readAndAssertArr(t, client, []string{eventFirst.eventID, eventFirst.event}) Dispatch(client, "qlpeek", []string{queue}) readAndAssertArr(t, client, []string{eventLast.eventID, eventLast.event}) // Make sure the queue hasn't been affected Dispatch(client, "qstatus", []string{queue}) readAndAssertQStatus(t, client, []queueInfo{ queueInfo{queue, int64(len(events)), 0, 0}, }) }
// QINFO is a human readable version of QSTATUS, so the testing of it is not // nearly as strenuous func TestQInfo(t *T) { client := newClient() // We register a queue to make sure at least one queue shows up in the // results when doing a QINFO with no arguments emptyQueue := clients.RandQueueName() Dispatch(client, "qregister", []string{emptyQueue}) readAndAssertStr(t, client, "OK") Dispatch(client, "qinfo", []string{}) infos, err := read(t, client).Array() require.Nil(t, err) found := 0 for _, infoM := range infos { line, err := infoM.Str() require.Nil(t, err) if strings.HasPrefix(line, emptyQueue) { found++ } } assert.Equal(t, 1, found) // Now we make sure qinfo returns a line starting with the expected queue // name when we give it as an argument Dispatch(client, "qinfo", []string{emptyQueue}) infos, err = read(t, client).Array() require.Nil(t, err) require.Equal(t, 1, len(infos)) line, err := infos[0].Str() require.Nil(t, err) assert.True(t, strings.HasPrefix(line, emptyQueue)) }
// Test adding events and removing them func TestBasicFunctionality(t *T) { client := newClient() queue := clients.RandQueueName() events := []struct{ eventID, event string }{ {"0", "foo"}, {"1", "bar"}, {"2", "baz"}, } for i := range events { Dispatch(client, "qlpush", []string{queue, events[i].eventID, events[i].event}) readAndAssertStr(t, client, "OK") } for i := range events { Dispatch(client, "qrpop", []string{queue}) readAndAssertArr(t, client, []string{events[i].eventID, events[i].event}) Dispatch(client, "qack", []string{queue, events[i].eventID}) readAndAssertInt(t, client, 1) } // Make sure qrpop on an empty queue does the right thing Dispatch(client, "qrpop", []string{queue}) readAndAssertNil(t, client) }
func TestStaleCleanup(t *T) { queue := clients.RandQueueName() client := clients.NewClient(clients.NewFakeClientConn()) err := UpdateQueues(client, []string{queue}) require.Nil(t, err) // Make sure the queue has this clientId as a consumer key := db.ConsumersKey(queue) res := db.Inst.Cmd("ZRANK", key, client.ID) assert.Equal(t, true, res.IsType(redis.Int), "res: %s", res) // Remove all knowledge about this client from the consumer state callCh <- func(s *state) { s.removeClientQueues(client, []string{queue}) } // Wait a little bit and try to remove stale consumers manually time.Sleep(2 * time.Second) err = removeStaleConsumers(1 * time.Second) require.Nil(t, err) // Make sure this client is no longer a consumer res = db.Inst.Cmd("ZRANK", key, client.ID) assert.Equal(t, true, res.IsType(redis.Nil), "key: %s clientId: %s res: %s", key, client.ID, res) }
func TestUpdateQueues(t *T) { queues := []string{ clients.RandQueueName(), clients.RandQueueName(), clients.RandQueueName(), } client := clients.NewClient(clients.NewFakeClientConn()) err := UpdateQueues(client, queues) require.Nil(t, err) // Make sure the client.Id appears in the consumers set for those queues for i := range queues { key := db.ConsumersKey(queues[i]) res := db.Inst.Cmd("ZRANK", key, client.ID) assert.Equal(t, true, res.IsType(redis.Int), "res: %s", res) } err = UpdateQueues(client, queues[1:]) require.Nil(t, err) // Make sure the first queue had this clientId removed from it key := db.ConsumersKey(queues[0]) res := db.Inst.Cmd("ZRANK", key, client.ID) assert.Equal(t, true, res.IsType(redis.Nil), "res: %s", res) // Make sure the rest of the queues still have it for i := range queues[1:] { key := db.ConsumersKey(queues[1:][i]) res := db.Inst.Cmd("ZRANK", key, client.ID) assert.Equal(t, true, res.IsType(redis.Int), "res: %s", res) } err = UpdateQueues(client, []string{}) require.Nil(t, err) // Make sure the clientId appears nowhere for i := range queues { key := db.ConsumersKey(queues[i]) res := db.Inst.Cmd("ZRANK", key, client.ID) assert.Equal(t, true, res.IsType(redis.Nil), "res: %s", res) } }
func TestPush(t *T) { client := newClient() queue := clients.RandQueueName() Dispatch(client, "qlpush", []string{queue, "0", "foo"}) readAndAssertStr(t, client, "OK") Dispatch(client, "qrpush", []string{queue, "1", "bar"}) readAndAssertStr(t, client, "OK") Dispatch(client, "qrpeek", []string{queue}) readAndAssertArr(t, client, []string{"1", "bar"}) Dispatch(client, "qstatus", []string{queue}) readAndAssertQStatus(t, client, []queueInfo{ queueInfo{queue, 2, 0, 0}, }) }
func TestQNotify(t *T) { client := newClient() queue := clients.RandQueueName() Dispatch(client, "qregister", []string{queue}) readAndAssertStr(t, client, "OK") Dispatch(client, "qnotify", []string{"1"}) readAndAssertNil(t, client) // Spawn a routine which will trigger a notify. We don't need to read the // response of the QLPUSH, it'll all just get garbage collected later go func() { time.Sleep(100 * time.Millisecond) Dispatch(newClient(), "qlpush", []string{queue, "0", "foo"}) }() Dispatch(client, "qnotify", []string{"10"}) readAndAssertStr(t, client, queue) }
func TestPushNoBlock(t *T) { client := newClient() queue := clients.RandQueueName() Dispatch(client, "qlpush", []string{queue, "0", "foo", "NOBLOCK"}) readAndAssertStr(t, client, "OK") time.Sleep(50 * time.Millisecond) Dispatch(client, "qrpush", []string{queue, "1", "bar", "NOBLOCK"}) readAndAssertStr(t, client, "OK") time.Sleep(50 * time.Millisecond) Dispatch(client, "qrpeek", []string{queue}) readAndAssertArr(t, client, []string{"1", "bar"}) Dispatch(client, "qstatus", []string{queue}) readAndAssertQStatus(t, client, []queueInfo{ queueInfo{queue, 2, 0, 0}, }) }
func TestQFlush(t *T) { client := newClient() queue := clients.RandQueueName() // Ensure adding a single event to a queue and then flushing destroys it Dispatch(client, "qlpush", []string{queue, "0", "foo"}) readAndAssertStr(t, client, "OK") Dispatch(client, "qflush", []string{queue}) readAndAssertStr(t, client, "OK") Dispatch(client, "qstatus", []string{queue}) readAndAssertQStatus(t, client, []queueInfo{ queueInfo{queue, 0, 0, 0}, }) // Ensure adding a multiple items, having one in the claimed queue, and then // flushing still destroys everything Dispatch(client, "qlpush", []string{queue, "0", "foo"}) readAndAssertStr(t, client, "OK") Dispatch(client, "qlpush", []string{queue, "1", "foo"}) readAndAssertStr(t, client, "OK") Dispatch(client, "qrpop", []string{queue}) readAndAssertArr(t, client, []string{"0", "foo"}) Dispatch(client, "qflush", []string{queue}) readAndAssertStr(t, client, "OK") Dispatch(client, "qstatus", []string{queue}) readAndAssertQStatus(t, client, []queueInfo{ queueInfo{queue, 0, 0, 0}, }) // Make sure the actual redis keys are destroyed keys := []string{ db.UnclaimedKey(queue), db.ClaimedKey(queue), db.ItemsKey(queue), } for _, key := range keys { exists, err := db.Inst.Cmd("EXISTS", key).Int() assert.Nil(t, err) assert.Equal(t, 0, exists) } }
// There's a race condition where an item get's ackd just after being found by // the restore process. The process checks if the item lock exists, it doesn't // because the ack just deleted it, and then attempts to move the item from the // claimed to the unclaimed queue. We need to make sure it doesn't appear in the // unclaimed queue in this case func TestRestoreRace(t *T) { q := clients.RandQueueName() unclaimedKey := db.UnclaimedKey(q) claimedKey := db.ClaimedKey(q) require.Nil(t, db.Inst.Cmd("LPUSH", unclaimedKey, "buz", "boz").Err) require.Nil(t, db.Inst.Cmd("LPUSH", claimedKey, "bar", "baz").Err) // restore an event which is in the claimed list, and one which is not require.Nil(t, restoreEventToQueue(q, "bar")) require.Nil(t, restoreEventToQueue(q, "foo")) l, err := db.Inst.Cmd("LRANGE", claimedKey, 0, -1).List() require.Nil(t, err) assert.Equal(t, []string{"baz"}, l) l, err = db.Inst.Cmd("LRANGE", unclaimedKey, 0, -1).List() require.Nil(t, err) assert.Equal(t, []string{"boz", "buz", "bar"}, l) }
func TestRestore(t *T) { // Add an item to claimed list and call validateClaimedEvents. Since it // doesn't have a lock it should be moved to the unclaimed list q := clients.RandQueueName() unclaimedKey := db.UnclaimedKey(q) claimedKey := db.ClaimedKey(q) itemsKey := db.ItemsKey(q) require.Nil(t, db.Inst.Cmd("HSET", itemsKey, "foo", "bar").Err) require.Nil(t, db.Inst.Cmd("LPUSH", claimedKey, "foo").Err) validateClaimedEvents() l, err := db.Inst.Cmd("LRANGE", claimedKey, 0, -1).List() require.Nil(t, err) assert.Empty(t, l) l, err = db.Inst.Cmd("LRANGE", unclaimedKey, 0, -1).List() require.Nil(t, err) assert.Equal(t, []string{"foo"}, l) }