influxdb/database_test.go

684 lines
17 KiB
Go

package influxdb
import (
"reflect"
"regexp"
"sort"
"testing"
)
// Ensure that the index will return a sorted array of measurement names.
func TestDatabase_Names(t *testing.T) {
idx := databaseWithFixtureData()
r := idx.Names()
exp := []string{"another_thing", "cpu_load", "key_count", "queue_depth"}
if !reflect.DeepEqual(r, exp) {
t.Fatalf("Names not equal:\n got: %s\n exp: %s", r, exp)
}
}
// Ensure that we can get the measurement by the series ID.
func TestDatabase_MeasurementBySeriesID(t *testing.T) {
idx := newDatabase()
m := &Measurement{
Name: "cpu_load",
}
s := &Series{
ID: uint32(1),
Tags: map[string]string{"host": "servera.influx.com", "region": "uswest"},
}
// add it and see if we can look it up
idx.addSeriesToIndex(m.Name, s)
mm := idx.MeasurementBySeriesID(uint32(1))
if string(mustMarshalJSON(m)) != string(mustMarshalJSON(mm)) {
t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
}
// now test that we can add another
s = &Series{
ID: uint32(2),
Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}
idx.addSeriesToIndex(m.Name, s)
mm = idx.MeasurementBySeriesID(uint32(2))
if string(mustMarshalJSON(m)) != string(mustMarshalJSON(mm)) {
t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
}
mm = idx.MeasurementBySeriesID(uint32(1))
if string(mustMarshalJSON(m)) != string(mustMarshalJSON(mm)) {
t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
}
}
// Ensure that we can get an array of unique measurements by a collection of series IDs.
func TestDatabase_MeasurementsBySeriesIDs(t *testing.T) {
idx := databaseWithFixtureData()
ids := SeriesIDs([]uint32{uint32(1), uint32(4)})
names := make([]string, 0)
for _, m := range idx.MeasurementsBySeriesIDs(ids) {
names = append(names, m.Name)
}
sort.Strings(names)
expected := []string{"cpu_load", "key_count"}
if !reflect.DeepEqual(names, expected) {
t.Fatalf("wrong measurements:\n exp: %s\n got: %s", expected, names)
}
}
// Ensure that we can get the series object by the series ID.
func TestDatabase_SeriesBySeriesID(t *testing.T) {
idx := newDatabase()
// now test that we can add another
s := &Series{
ID: uint32(2),
Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}
idx.addSeriesToIndex("foo", s)
ss := idx.SeriesByID(uint32(2))
if string(mustMarshalJSON(s)) != string(mustMarshalJSON(ss)) {
t.Fatalf("series not equal:\n%s\n%s", s, ss)
}
}
// Ensure that we can get the measurement and series objects out based on measurement and tags.
func TestDatabase_MeasurementAndSeries(t *testing.T) {
idx := newDatabase()
m := &Measurement{
Name: "cpu_load",
}
s := &Series{
ID: uint32(1),
Tags: map[string]string{"host": "servera.influx.com", "region": "uswest"},
}
// add it and see if we can look it up by name and tags
idx.addSeriesToIndex(m.Name, s)
mm, ss := idx.MeasurementAndSeries(m.Name, s.Tags)
if string(mustMarshalJSON(m)) != string(mustMarshalJSON(mm)) {
t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
} else if string(mustMarshalJSON(s)) != string(mustMarshalJSON(ss)) {
t.Fatalf("series not equal:\n%s\n%s", s, ss)
}
// now test that we can add another
s = &Series{
ID: uint32(2),
Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}
idx.addSeriesToIndex(m.Name, s)
mm, ss = idx.MeasurementAndSeries(m.Name, s.Tags)
if string(mustMarshalJSON(m)) != string(mustMarshalJSON(mm)) {
t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
} else if string(mustMarshalJSON(s)) != string(mustMarshalJSON(ss)) {
t.Fatalf("series not equal:\n%s\n%s", s, ss)
}
}
// Ensure that we can get the series IDs for measurements without any filters.
func TestDatabase_SeriesIDs(t *testing.T) {
idx := newDatabase()
s := &Series{
ID: uint32(1),
Tags: map[string]string{"host": "servera.influx.com", "region": "uswest"}}
// add it and see if we can look it up
added := idx.addSeriesToIndex("cpu_load", s)
if !added {
t.Fatal("couldn't add series")
}
// test that we can't add it again
added = idx.addSeriesToIndex("cpu_load", s)
if added {
t.Fatal("shoulnd't be able to add duplicate series")
}
// now test that we can add another
s = &Series{
ID: uint32(2),
Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}
added = idx.addSeriesToIndex("cpu_load", s)
if !added {
t.Fatalf("couldn't add series")
}
l := idx.SeriesIDs([]string{"cpu_load"}, nil)
r := []uint32{1, 2}
if !l.Equals(r) {
t.Fatalf("series IDs not the same:\n%s\n%s", l, r)
}
// now add another in a different measurement
s = &Series{
ID: uint32(3),
Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}
added = idx.addSeriesToIndex("network_in", s)
if !added {
t.Fatalf("couldn't add series")
}
l = idx.SeriesIDs([]string{"cpu_load"}, nil)
r = []uint32{1, 2, 3}
if !l.Equals(r) {
t.Fatalf("series IDs not the same:\n%s\n%s", l, r)
}
}
func TestDatabase_SeriesIDsWhereTagFilter(t *testing.T) {
idx := databaseWithFixtureData()
var tests = []struct {
names []string
filters []*TagFilter
result []uint32
}{
// match against no tags
{
names: []string{"cpu_load", "redis"},
result: []uint32{uint32(1), uint32(2), uint32(3), uint32(4), uint32(5), uint32(6), uint32(7), uint32(8)},
},
// match against all tags
{
names: []string{"cpu_load"},
filters: []*TagFilter{
&TagFilter{Key: "host", Value: "servera.influx.com"},
&TagFilter{Key: "region", Value: "uswest"},
},
result: []uint32{uint32(1)},
},
// match against one tag
{
names: []string{"cpu_load"},
filters: []*TagFilter{
&TagFilter{Key: "region", Value: "uswest"},
},
result: []uint32{uint32(1), uint32(2)},
},
// match against one tag, single result
{
names: []string{"cpu_load"},
filters: []*TagFilter{
&TagFilter{Key: "host", Value: "servera.influx.com"},
},
result: []uint32{uint32(1)},
},
// query against tag key that doesn't exist returns empty
{
names: []string{"cpu_load"},
filters: []*TagFilter{
&TagFilter{Key: "foo", Value: "bar"},
},
result: []uint32{},
},
// query against tag value that doesn't exist returns empty
{
names: []string{"cpu_load"},
filters: []*TagFilter{
&TagFilter{Key: "host", Value: "foo"},
},
result: []uint32{},
},
// query against a tag NOT value
{
names: []string{"key_count"},
filters: []*TagFilter{
&TagFilter{Key: "region", Value: "useast", Not: true},
},
result: []uint32{uint32(3)},
},
// query against a tag NOT null
{
names: []string{"queue_depth"},
filters: []*TagFilter{
&TagFilter{Key: "app", Value: "", Not: true},
},
result: []uint32{uint32(6)},
},
// query against a tag value and another tag NOT value
{
names: []string{"queue_depth"},
filters: []*TagFilter{
&TagFilter{Key: "name", Value: "high priority"},
&TagFilter{Key: "app", Value: "paultown", Not: true},
},
result: []uint32{uint32(5), uint32(7)},
},
// query against a tag value matching regex
{
names: []string{"queue_depth"},
filters: []*TagFilter{
&TagFilter{Key: "app", Regex: regexp.MustCompile("paul.*")},
},
result: []uint32{uint32(6), uint32(7)},
},
// query against a tag value matching regex and other tag value matching value
{
names: []string{"queue_depth"},
filters: []*TagFilter{
&TagFilter{Key: "name", Value: "high priority"},
&TagFilter{Key: "app", Regex: regexp.MustCompile("paul.*")},
},
result: []uint32{uint32(6), uint32(7)},
},
// query against a tag value NOT matching regex
{
names: []string{"queue_depth"},
filters: []*TagFilter{
&TagFilter{Key: "app", Regex: regexp.MustCompile("paul.*"), Not: true},
},
result: []uint32{uint32(5)},
},
// query against a tag value NOT matching regex and other tag value matching value
{
names: []string{"queue_depth"},
filters: []*TagFilter{
&TagFilter{Key: "app", Regex: regexp.MustCompile("paul.*"), Not: true},
&TagFilter{Key: "name", Value: "high priority"},
},
result: []uint32{uint32(5)},
},
// query against multiple measurements
{
names: []string{"cpu_load", "key_count"},
filters: []*TagFilter{
&TagFilter{Key: "region", Value: "uswest"},
},
result: []uint32{uint32(1), uint32(2), uint32(3)},
},
}
for i, tt := range tests {
r := idx.SeriesIDs(tt.names, tt.filters)
expectedIDs := SeriesIDs(tt.result)
if !r.Equals(expectedIDs) {
t.Fatalf("%d: filters: %s: result mismatch:\n exp=%s\n got=%s", i, mustMarshalJSON(tt.filters), mustMarshalJSON(expectedIDs), mustMarshalJSON(r))
}
}
}
func TestDatabase_TagKeys(t *testing.T) {
idx := databaseWithFixtureData()
var tests = []struct {
names []string
result []string
}{
{
names: nil,
result: []string{"a", "app", "host", "name", "region", "service"},
},
{
names: []string{"cpu_load"},
result: []string{"host", "region"},
},
{
names: []string{"key_count", "queue_depth"},
result: []string{"app", "host", "name", "region", "service"},
},
}
for i, tt := range tests {
r := idx.TagKeys(tt.names)
if !reflect.DeepEqual(r, tt.result) {
t.Fatalf("%d: names: %s: result mismatch:\n exp=%s\n got=%s", i, tt.names, tt.result, r)
}
}
}
func TestDatabase_TagValuesWhereTagFilter(t *testing.T) {
idx := databaseWithFixtureData()
var tests = []struct {
names []string
key string
filters []*TagFilter
result []string
}{
// get the tag values across multiple measurements
// get the tag values for a single measurement
{
names: []string{"key_count"},
key: "region",
result: []string{"useast", "uswest"},
},
// get the tag values for a single measurement with where filter
{
names: []string{"key_count"},
key: "region",
filters: []*TagFilter{
&TagFilter{Key: "host", Value: "serverc.influx.com"},
},
result: []string{"uswest"},
},
// get the tag values for a single measurement with a not where filter
{
names: []string{"key_count"},
key: "region",
filters: []*TagFilter{
&TagFilter{Key: "host", Value: "serverc.influx.com", Not: true},
},
result: []string{"useast"},
},
// get the tag values for a single measurement with multiple where filters
{
names: []string{"key_count"},
key: "region",
filters: []*TagFilter{
&TagFilter{Key: "host", Value: "serverc.influx.com"},
&TagFilter{Key: "service", Value: "redis"},
},
result: []string{"uswest"},
},
// get the tag values for a single measurement with regex filter
{
names: []string{"queue_depth"},
key: "name",
filters: []*TagFilter{
&TagFilter{Key: "app", Regex: regexp.MustCompile("paul.*")},
},
result: []string{"high priority"},
},
// get the tag values for a single measurement with a not regex filter
{
names: []string{"key_count"},
key: "region",
filters: []*TagFilter{
&TagFilter{Key: "host", Regex: regexp.MustCompile("serverd.*"), Not: true},
},
result: []string{"uswest"},
},
}
for i, tt := range tests {
r := idx.TagValues(tt.names, tt.key, tt.filters).ToSlice()
if !reflect.DeepEqual(r, tt.result) {
t.Fatalf("%d: filters: %s: result mismatch:\n exp=%s\n got=%s", i, mustMarshalJSON(tt.filters), tt.result, r)
}
}
}
func TestDatabase_DropSeries(t *testing.T) {
t.Skip("pending")
}
func TestDatabase_DropMeasurement(t *testing.T) {
t.Skip("pending")
}
func TestDatabase_FieldKeys(t *testing.T) {
t.Skip("pending")
}
// databaseWithFixtureData returns a populated Index for use in many of the filtering tests
func databaseWithFixtureData() *database {
idx := newDatabase()
s := &Series{
ID: uint32(1),
Tags: map[string]string{"host": "servera.influx.com", "region": "uswest"}}
added := idx.addSeriesToIndex("cpu_load", s)
if !added {
return nil
}
s = &Series{
ID: uint32(2),
Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}
added = idx.addSeriesToIndex("cpu_load", s)
if !added {
return nil
}
s = &Series{
ID: uint32(3),
Tags: map[string]string{"host": "serverc.influx.com", "region": "uswest", "service": "redis"}}
added = idx.addSeriesToIndex("key_count", s)
if !added {
return nil
}
s = &Series{
ID: uint32(4),
Tags: map[string]string{"host": "serverd.influx.com", "region": "useast", "service": "redis"}}
added = idx.addSeriesToIndex("key_count", s)
if !added {
return nil
}
s = &Series{
ID: uint32(5),
Tags: map[string]string{"name": "high priority"}}
added = idx.addSeriesToIndex("queue_depth", s)
if !added {
return nil
}
s = &Series{
ID: uint32(6),
Tags: map[string]string{"name": "high priority", "app": "paultown"}}
added = idx.addSeriesToIndex("queue_depth", s)
if !added {
return nil
}
s = &Series{
ID: uint32(7),
Tags: map[string]string{"name": "high priority", "app": "paulcountry"}}
added = idx.addSeriesToIndex("queue_depth", s)
if !added {
return nil
}
s = &Series{
ID: uint32(8),
Tags: map[string]string{"a": "b"}}
added = idx.addSeriesToIndex("another_thing", s)
if !added {
return nil
}
return idx
}
func TestDatabase_SeriesIDsIntersect(t *testing.T) {
var tests = []struct {
expected []uint32
left []uint32
right []uint32
}{
// both sets empty
{
expected: []uint32{},
left: []uint32{},
right: []uint32{},
},
// right set empty
{
expected: []uint32{},
left: []uint32{uint32(1)},
right: []uint32{},
},
// left set empty
{
expected: []uint32{},
left: []uint32{},
right: []uint32{uint32(1)},
},
// both sides same size
{
expected: []uint32{uint32(1), uint32(4)},
left: []uint32{uint32(1), uint32(2), uint32(4), uint32(5)},
right: []uint32{uint32(1), uint32(3), uint32(4), uint32(7)},
},
// left side bigger
{
expected: []uint32{uint32(2)},
left: []uint32{uint32(1), uint32(2), uint32(3)},
right: []uint32{uint32(2)},
},
// right side bigger
{
expected: []uint32{uint32(4), uint32(8)},
left: []uint32{uint32(2), uint32(3), uint32(4), uint32(8)},
right: []uint32{uint32(1), uint32(4), uint32(7), uint32(8), uint32(9)},
},
}
for i, tt := range tests {
a := SeriesIDs(tt.left).Intersect(tt.right)
if !a.Equals(tt.expected) {
t.Fatalf("%d: %s intersect %s: result mismatch:\n exp=%s\n got=%s", i, SeriesIDs(tt.left), SeriesIDs(tt.right), SeriesIDs(tt.expected), SeriesIDs(a))
}
}
}
func TestDatabase_SeriesIDsUnion(t *testing.T) {
var tests = []struct {
expected []uint32
left []uint32
right []uint32
}{
// both sets empty
{
expected: []uint32{},
left: []uint32{},
right: []uint32{},
},
// right set empty
{
expected: []uint32{uint32(1)},
left: []uint32{uint32(1)},
right: []uint32{},
},
// left set empty
{
expected: []uint32{uint32(1)},
left: []uint32{},
right: []uint32{uint32(1)},
},
// both sides same size
{
expected: []uint32{uint32(1), uint32(2), uint32(3), uint32(4), uint32(5), uint32(7)},
left: []uint32{uint32(1), uint32(2), uint32(4), uint32(5)},
right: []uint32{uint32(1), uint32(3), uint32(4), uint32(7)},
},
// left side bigger
{
expected: []uint32{uint32(1), uint32(2), uint32(3)},
left: []uint32{uint32(1), uint32(2), uint32(3)},
right: []uint32{uint32(2)},
},
// right side bigger
{
expected: []uint32{uint32(1), uint32(2), uint32(3), uint32(4), uint32(7), uint32(8), uint32(9)},
left: []uint32{uint32(2), uint32(3), uint32(4), uint32(8)},
right: []uint32{uint32(1), uint32(4), uint32(7), uint32(8), uint32(9)},
},
}
for i, tt := range tests {
a := SeriesIDs(tt.left).Union(tt.right)
if !a.Equals(tt.expected) {
t.Fatalf("%d: %s union %s: result mismatch:\n exp=%s\n got=%s", i, SeriesIDs(tt.left), SeriesIDs(tt.right), SeriesIDs(tt.expected), SeriesIDs(a))
}
}
}
func TestDatabase_SeriesIDsReject(t *testing.T) {
var tests = []struct {
expected []uint32
left []uint32
right []uint32
}{
// both sets empty
{
expected: []uint32{},
left: []uint32{},
right: []uint32{},
},
// right set empty
{
expected: []uint32{uint32(1)},
left: []uint32{uint32(1)},
right: []uint32{},
},
// left set empty
{
expected: []uint32{},
left: []uint32{},
right: []uint32{uint32(1)},
},
// both sides same size
{
expected: []uint32{uint32(2), uint32(5)},
left: []uint32{uint32(1), uint32(2), uint32(4), uint32(5)},
right: []uint32{uint32(1), uint32(3), uint32(4), uint32(7)},
},
// left side bigger
{
expected: []uint32{uint32(1), uint32(3)},
left: []uint32{uint32(1), uint32(2), uint32(3)},
right: []uint32{uint32(2)},
},
// right side bigger
{
expected: []uint32{uint32(2), uint32(3)},
left: []uint32{uint32(2), uint32(3), uint32(4), uint32(8)},
right: []uint32{uint32(1), uint32(4), uint32(7), uint32(8), uint32(9)},
},
}
for i, tt := range tests {
a := SeriesIDs(tt.left).Reject(tt.right)
if !a.Equals(tt.expected) {
t.Fatalf("%d: %s reject %s: result mismatch:\n exp=%s\n got=%s", i, SeriesIDs(tt.left), SeriesIDs(tt.right), SeriesIDs(tt.expected), SeriesIDs(a))
}
}
}