284 lines
6.6 KiB
Go
284 lines
6.6 KiB
Go
// Licensed to Elasticsearch B.V. under one or more contributor
|
|
// license agreements. See the NOTICE file distributed with
|
|
// this work for additional information regarding copyright
|
|
// ownership. Elasticsearch B.V. licenses this file to you under
|
|
// the Apache License, Version 2.0 (the "License"); you may
|
|
// not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
package consumergroup
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/elastic/beats/libbeat/common"
|
|
)
|
|
|
|
func TestFetchGroupInfo(t *testing.T) {
|
|
noEvents := func(events []common.MapStr) {
|
|
assert.Len(t, events, 0)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
client client
|
|
groups []string
|
|
topics []string
|
|
err error
|
|
expected []common.MapStr
|
|
validate func([]common.MapStr)
|
|
}{
|
|
{
|
|
name: "Test all groups",
|
|
client: defaultMockClient(mockState{
|
|
partitions: map[string]map[string][]int64{
|
|
"group1": {
|
|
"topic1": {10, 11, 12},
|
|
"topic3": {6, 7},
|
|
},
|
|
"group2": {
|
|
"topic2": {3},
|
|
"topic3": {9, 10},
|
|
},
|
|
},
|
|
groups: map[string][]map[string][]int32{
|
|
"group1": {
|
|
{"topic1": {0, 2}, "topic3": {1}},
|
|
{"topic1": {1}, "topic3": {0}},
|
|
},
|
|
"group2": {
|
|
{"topic2": {0}, "topic3": {0, 1}},
|
|
},
|
|
},
|
|
}),
|
|
expected: []common.MapStr{
|
|
testEvent("group1", "topic1", 0, common.MapStr{
|
|
"client": clientMeta(0),
|
|
"offset": int64(10),
|
|
}),
|
|
testEvent("group1", "topic1", 1, common.MapStr{
|
|
"client": clientMeta(1),
|
|
"offset": int64(11),
|
|
}),
|
|
testEvent("group1", "topic1", 2, common.MapStr{
|
|
"client": clientMeta(0),
|
|
"offset": int64(12),
|
|
}),
|
|
testEvent("group1", "topic3", 0, common.MapStr{
|
|
"client": clientMeta(1),
|
|
"offset": int64(6),
|
|
}),
|
|
testEvent("group1", "topic3", 1, common.MapStr{
|
|
"client": clientMeta(0),
|
|
"offset": int64(7),
|
|
}),
|
|
testEvent("group2", "topic2", 0, common.MapStr{
|
|
"client": clientMeta(0),
|
|
"offset": int64(3),
|
|
}),
|
|
testEvent("group2", "topic3", 0, common.MapStr{
|
|
"client": clientMeta(0),
|
|
"offset": int64(9),
|
|
}),
|
|
testEvent("group2", "topic3", 1, common.MapStr{
|
|
"client": clientMeta(0),
|
|
"offset": int64(10),
|
|
}),
|
|
},
|
|
},
|
|
|
|
{
|
|
name: "filter topics and groups",
|
|
client: defaultMockClient(mockState{
|
|
partitions: map[string]map[string][]int64{
|
|
"group1": {
|
|
"topic1": {1, 2},
|
|
"topic2": {3, 4},
|
|
},
|
|
"group2": {
|
|
"topic2": {5, 6},
|
|
"topic3": {7, 8},
|
|
},
|
|
},
|
|
groups: map[string][]map[string][]int32{
|
|
"group1": {
|
|
{"topic1": {0, 1}, "topic2": {0, 1}},
|
|
},
|
|
"group2": {
|
|
{"topic1": {0, 1}, "topic2": {0, 1}},
|
|
},
|
|
},
|
|
}),
|
|
groups: []string{"group1"},
|
|
topics: []string{"topic1"},
|
|
expected: []common.MapStr{
|
|
testEvent("group1", "topic1", 0, common.MapStr{
|
|
"client": clientMeta(0),
|
|
"offset": int64(1),
|
|
}),
|
|
testEvent("group1", "topic1", 1, common.MapStr{
|
|
"client": clientMeta(0),
|
|
"offset": int64(2),
|
|
}),
|
|
},
|
|
},
|
|
|
|
{
|
|
name: "no events on empty group",
|
|
client: defaultMockClient(mockState{}),
|
|
validate: noEvents,
|
|
},
|
|
|
|
{
|
|
name: "fail to list groups",
|
|
client: defaultMockClient(mockState{}).with(func(c *mockClient) {
|
|
c.listGroups = func() ([]string, error) {
|
|
return nil, io.EOF
|
|
}
|
|
}),
|
|
err: io.EOF,
|
|
validate: noEvents,
|
|
},
|
|
|
|
{
|
|
name: "fail if assignment query fails",
|
|
client: defaultMockClient(mockState{
|
|
partitions: map[string]map[string][]int64{
|
|
"group1": {"topic1": {1}},
|
|
},
|
|
groups: map[string][]map[string][]int32{
|
|
"group1": {{"topic1": {0}}},
|
|
},
|
|
}).with(func(c *mockClient) {
|
|
c.describeGroups = makeDescribeGroupsFail(io.EOF)
|
|
}),
|
|
err: io.EOF,
|
|
validate: noEvents,
|
|
},
|
|
|
|
{
|
|
name: "fail when fetching group offsets",
|
|
client: defaultMockClient(mockState{
|
|
partitions: map[string]map[string][]int64{
|
|
"group1": {"topic1": {1}},
|
|
},
|
|
groups: map[string][]map[string][]int32{
|
|
"group1": {{"topic1": {0}}},
|
|
},
|
|
}).with(func(c *mockClient) {
|
|
c.fetchGroupOffsets = makeFetchGroupOffsetsFail(io.EOF)
|
|
}),
|
|
err: io.EOF,
|
|
validate: noEvents,
|
|
},
|
|
}
|
|
|
|
for i, test := range tests {
|
|
t.Logf("run test (%v): %v", i, test.name)
|
|
|
|
var events []common.MapStr
|
|
collectEvents := func(event common.MapStr) {
|
|
t.Logf("new event: %v", event)
|
|
events = append(events, event)
|
|
}
|
|
|
|
indexEvents := func(events []common.MapStr) map[string]common.MapStr {
|
|
index := map[string]common.MapStr{}
|
|
for _, e := range events {
|
|
key := fmt.Sprintf("%v::%v::%v",
|
|
e["id"], e["topic"], e["partition"],
|
|
)
|
|
index[key] = e
|
|
}
|
|
return index
|
|
}
|
|
|
|
groups := makeNameSet(test.groups...).pred()
|
|
topics := makeNameSet(test.topics...).pred()
|
|
err := fetchGroupInfo(collectEvents, test.client, groups, topics)
|
|
if err != nil {
|
|
switch {
|
|
case test.err == nil:
|
|
t.Fatal(err)
|
|
case test.err != err:
|
|
t.Error(err)
|
|
}
|
|
continue
|
|
}
|
|
|
|
indexed := indexEvents(events)
|
|
for key, expected := range indexEvents(test.expected) {
|
|
event, found := indexed[key]
|
|
if !found {
|
|
t.Errorf("Missing event: %v", key)
|
|
continue
|
|
}
|
|
assertEvent(t, expected, event)
|
|
}
|
|
|
|
if test.validate != nil {
|
|
test.validate(events)
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertEvent(t *testing.T, expected, event common.MapStr) {
|
|
for field, exp := range expected {
|
|
val, found := event[field]
|
|
if !found {
|
|
t.Errorf("Missing field: %v", field)
|
|
continue
|
|
}
|
|
|
|
if sub, ok := exp.(common.MapStr); ok {
|
|
assertEvent(t, sub, val.(common.MapStr))
|
|
} else {
|
|
if !assert.Equal(t, exp, val) {
|
|
t.Logf("failed in field: %v", field)
|
|
t.Logf("type expected: %v", reflect.TypeOf(exp))
|
|
t.Logf("type event: %v", reflect.TypeOf(val))
|
|
t.Logf("------------------------------")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func testEvent(
|
|
group, topic string,
|
|
partition int,
|
|
fields ...common.MapStr,
|
|
) common.MapStr {
|
|
event := common.MapStr{
|
|
"id": group,
|
|
"topic": topic,
|
|
"partition": int32(partition),
|
|
}
|
|
|
|
for _, extra := range fields {
|
|
for k, v := range extra {
|
|
event[k] = v
|
|
}
|
|
}
|
|
return event
|
|
}
|
|
|
|
func clientMeta(id int) common.MapStr {
|
|
return common.MapStr{
|
|
"id": fmt.Sprintf("consumer-%v", id),
|
|
}
|
|
}
|