Skip to content

Commit

Permalink
Add Storage Integration Testing (#236)
Browse files Browse the repository at this point in the history
* create integration test framework for storage + create elasticsearch integration test

* refactor queries format

* rename comparison fns to fit its behavior

* fix rand.Uint64() (does not exist in go1.7)

* revert env var check

* refactor trace w/ query checker to separate file + test

* import list reorder

* change itest direction

* change integration test format

* make fmt/lint

* make fmt/lint

* prettify output when test fails

* fmt

* delete query to trace checker

* remove unnecessary code

* add integration test script

* fix syntax

* add comment about fixtures and queries

* fix travis yml to run es integ test

* remove es integ test from install travis

* fix typo

* fix err check bug

* fmt

* move sorting fns to model

* fix imports

* fix code reviews

* change trace fixtures to describe the trace

* revert idl

* trust people

* sort refactored

* fix trace comparison test + raise timeout for es-integration test

* code review

* remove numtraces check

* add multiple-trace test case

* code review
  • Loading branch information
mh-park committed Jul 10, 2017
1 parent 3581384 commit 433b7e0
Show file tree
Hide file tree
Showing 36 changed files with 2,282 additions and 9 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ matrix:
- go: 1.7
env:
- DOCKER=true
- go: 1.7
env:
- ES_INTEGRATION_TEST=true
# TODO 1.8 tests take way too long to run 900s vs 250s for 1.7
# - go: 1.8
# env:
Expand Down Expand Up @@ -49,3 +52,4 @@ script:
- if [ "$ALL_IN_ONE" == true ]; then bash ./travis/build-all-in-one-image.sh ; else echo 'skipping all_in_one'; fi
- if [ "$CROSSDOCK" == true ]; then bash ./travis/build-crossdock.sh ; else echo 'skipping crossdock'; fi
- if [ "$DOCKER" == true ]; then bash ./travis/build-docker-images.sh ; else echo 'skipping docker images'; fi
- if [ "$ES_INTEGRATION_TEST" == true ]; then bash ./travis/es-integration-test.sh ; else echo 'skipping elastic search integration test'; fi
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ test: go-gen
integration-test: go-gen
$(GOTEST) -tags=integration ./cmd/standalone/...

.PHONY: es-integration-test
es-integration-test: go-gen
$(GOTEST) ./plugin/storage/integration/...

.PHONY: fmt
fmt:
$(GOFMT) -e -s -l -w $(ALL_SRC)
Expand Down
97 changes: 97 additions & 0 deletions model/sort.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package model

import (
"sort"
)

type traceByTraceID []*Trace

func (s traceByTraceID) Len() int { return len(s) }
func (s traceByTraceID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s traceByTraceID) Less(i, j int) bool {
if len(s[i].Spans) == 0 {
return true
} else if len(s[j].Spans) == 0 {
return false
} else {
return s[i].Spans[0].TraceID.Low < s[j].Spans[0].TraceID.Low
}
}

// SortTraces deep sorts a list of traces by TraceID.
func SortTraces(traces []*Trace) {
sort.Sort(traceByTraceID(traces))
for _, trace := range traces {
SortTrace(trace)
}
}

type spanBySpanID []*Span

func (s spanBySpanID) Len() int { return len(s) }
func (s spanBySpanID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s spanBySpanID) Less(i, j int) bool { return s[i].SpanID < s[j].SpanID }

// SortTrace deep sorts a trace's spans by SpanID.
func SortTrace(trace *Trace) {
sort.Sort(spanBySpanID(trace.Spans))
for _, span := range trace.Spans {
sortSpan(span)
}
}

func sortSpan(span *Span) {
span.NormalizeTimestamps()
sortTags(span.Tags)
sortLogs(span.Logs)
sortProcess(span.Process)
}

type tagByKey []KeyValue

func (t tagByKey) Len() int { return len(t) }
func (t tagByKey) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
func (t tagByKey) Less(i, j int) bool { return t[i].Key < t[j].Key }

func sortTags(tags []KeyValue) {
sort.Sort(tagByKey(tags))
}

type logByTimestamp []Log

func (t logByTimestamp) Len() int { return len(t) }
func (t logByTimestamp) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
func (t logByTimestamp) Less(i, j int) bool { return t[i].Timestamp.Before(t[j].Timestamp) }

func sortLogs(logs []Log) {
sort.Sort(logByTimestamp(logs))
for _, log := range logs {
sortTags(log.Fields)
}
}

func sortProcess(process *Process) {
if process != nil {
sortTags(process.Tags)
}
}
126 changes: 126 additions & 0 deletions model/sort_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package model

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
)

var (
currTime = time.Now()
)

func TestSortTraces(t *testing.T) {
t1 := &Trace{
Spans: []*Span{
{
TraceID: TraceID{Low: 1},
SpanID: SpanID(2),
Tags: []KeyValue{{Key: "world"}, {Key: "hello"}},
Process: &Process{
ServiceName: "hello",
Tags: []KeyValue{{Key: "hello"}, {Key: "world"}},
},
},
{
TraceID: TraceID{Low: 1},
SpanID: SpanID(1),
Logs: []Log{
{
Timestamp: currTime,
Fields: []KeyValue{{Key: "world"}, {Key: "hello"}},
},
{
Timestamp: currTime.Add(-time.Hour),
Fields: []KeyValue{{Key: "hello"}, {Key: "world"}},
},
},
},
},
}
t2 := &Trace{
Spans: []*Span{
{
TraceID: TraceID{Low: 1},
SpanID: SpanID(2),
Tags: []KeyValue{{Key: "world"}, {Key: "hello"}},
Process: &Process{
ServiceName: "hello",
Tags: []KeyValue{{Key: "hello"}, {Key: "world"}},
},
},
{
TraceID: TraceID{Low: 1},
SpanID: SpanID(1),
Logs: []Log{
{
Timestamp: currTime.Add(-time.Hour),
Fields: []KeyValue{{Key: "world"}, {Key: "hello"}},
},
{
Timestamp: currTime,
Fields: []KeyValue{{Key: "hello"}, {Key: "world"}},
},
},
},
},
}
SortTrace(t1)
SortTrace(t2)
assert.EqualValues(t, t1, t2)
}

func TestSortListOfTraces(t *testing.T) {
t1 := &Trace{
Spans: []*Span{
{
TraceID: TraceID{Low: 1},
},
{
TraceID: TraceID{Low: 1},
},
},
}
t2 := &Trace{
Spans: []*Span{
{
TraceID: TraceID{Low: 2},
},
},
}
t3 := &Trace{
Spans: []*Span{
{
TraceID: TraceID{Low: 3},
},
},
}
t4 := &Trace{}

list1 := []*Trace{t1, t4, t2, t3}
list2 := []*Trace{t4, t2, t1, t3}
SortTraces(list1)
SortTraces(list2)
assert.EqualValues(t, list1, list2)
}
7 changes: 4 additions & 3 deletions plugin/storage/es/spanstore/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const (
tagKeyField = "key"
tagValueField = "value"

defaultDocCount = 3000
defaultDocCount = 10000 // the default elasticsearch allowed limit
defaultNumTraces = 100
)

Expand Down Expand Up @@ -177,7 +177,7 @@ func (s *SpanReader) findIndices(traceQuery *spanstore.TraceQueryParameters) []s
var indices []string
current := traceQuery.StartTimeMax
for current.After(traceQuery.StartTimeMin) && current.After(threeDaysAgo) {
index := indexWithDate(current)
index := IndexWithDate(current)
exists, _ := s.client.IndexExists(index).Do(s.ctx) // Don't care about error, if it's an error, exists will be false anyway
if exists {
indices = append(indices, index)
Expand All @@ -187,7 +187,8 @@ func (s *SpanReader) findIndices(traceQuery *spanstore.TraceQueryParameters) []s
return indices
}

func indexWithDate(date time.Time) string {
// IndexWithDate returns the index name formatted to date.
func IndexWithDate(date time.Time) string {
return indexPrefix + date.Format("2006-01-02")
}

Expand Down
12 changes: 6 additions & 6 deletions plugin/storage/es/spanstore/reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,9 +287,9 @@ func TestSpanReader_findIndicesEmptyQuery(t *testing.T) {
twoDaysAgo := today.AddDate(0, 0, -2)

expected := []string{
indexWithDate(today),
indexWithDate(yesterday),
indexWithDate(twoDaysAgo),
IndexWithDate(today),
IndexWithDate(yesterday),
IndexWithDate(twoDaysAgo),
}

assert.EqualValues(t, expected, actual)
Expand Down Expand Up @@ -327,8 +327,8 @@ func TestSpanReader_findIndicesOnlyRecent(t *testing.T) {
twoDaysAgo := today.AddDate(0, 0, -2)

expected := []string{
indexWithDate(yesterday),
indexWithDate(twoDaysAgo),
IndexWithDate(yesterday),
IndexWithDate(twoDaysAgo),
}

assert.EqualValues(t, expected, actual)
Expand All @@ -337,7 +337,7 @@ func TestSpanReader_findIndicesOnlyRecent(t *testing.T) {

func TestSpanReader_indexWithDate(t *testing.T) {
withSpanReader(func(r *spanReaderTest) {
actual := indexWithDate(time.Date(1995, time.April, 21, 4, 21, 19, 95, time.UTC))
actual := IndexWithDate(time.Date(1995, time.April, 21, 4, 21, 19, 95, time.UTC))
assert.Equal(t, "jaeger-1995-04-21", actual)
})
}
Expand Down
80 changes: 80 additions & 0 deletions plugin/storage/integration/domain_trace_compare_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package integration

import (
"encoding/json"
"testing"

"github.com/kr/pretty"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/uber/jaeger/model"
)

func CompareSliceOfTraces(t *testing.T, expected []*model.Trace, actual []*model.Trace) {
require.Equal(t, len(expected), len(actual))
model.SortTraces(expected)
model.SortTraces(actual)
for i := range expected {
checkSize(t, expected[i], actual[i])
}
if !assert.EqualValues(t, expected, actual) {
for _, err := range pretty.Diff(expected, actual) {
t.Log(err)
}
out, err := json.Marshal(actual)
assert.NoError(t, err)
t.Logf("Actual traces: %s", string(out))
}
}

func CompareTraces(t *testing.T, expected *model.Trace, actual *model.Trace) {
if expected.Spans == nil {
require.Nil(t, actual.Spans)
return
}
model.SortTrace(expected)
model.SortTrace(actual)
checkSize(t, expected, actual)
if !assert.EqualValues(t, expected, actual) {
for _, err := range pretty.Diff(expected, actual) {
t.Log(err)
}
out, err := json.Marshal(actual)
assert.NoError(t, err)
t.Logf("Actual trace: %s", string(out))
}
}

func checkSize(t *testing.T, expected *model.Trace, actual *model.Trace) {
require.True(t, len(expected.Spans) == len(actual.Spans))
for i := range expected.Spans {
expectedSpan := expected.Spans[i]
actualSpan := actual.Spans[i]
require.True(t, len(expectedSpan.Tags) == len(actualSpan.Tags))
require.True(t, len(expectedSpan.Logs) == len(actualSpan.Logs))
if expectedSpan.Process != nil && actualSpan.Process != nil {
require.True(t, len(expectedSpan.Process.Tags) == len(actualSpan.Process.Tags))
}
}
}
Loading

0 comments on commit 433b7e0

Please sign in to comment.