Skip to content

Commit ff3c039

Browse files
committed
observability: add tests
Some tests to check the behavior but also to always ensure we have spans and metrics produced and no regressions.
1 parent a3a3940 commit ff3c039

2 files changed

Lines changed: 194 additions & 9 deletions

File tree

observability.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,19 @@ var (
3333

3434
var mLatencyMs = stats.Float64("latency", "The latency in milliseconds", "ms")
3535

36+
var latencyDistribution = view.Distribution(
37+
// [0ms, 0.001ms, 0.005ms, 0.01ms, 0.05ms, 0.1ms, 0.5ms, 1ms, 1.5ms, 2ms, 2.5ms, 5ms, 10ms, 25ms, 50ms, 100ms, 200ms,
38+
// 400ms, 600ms, 800ms, 1s, 1.5s, 2s, 2.5s, 5s, 10s, 20s, 40s, 100s, 200s, 500s, 1000s]
39+
//
40+
0, 0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 1.5, 2, 2.5, 5, 10, 25, 50, 100, 200,
41+
400, 600, 800, 1000, 1500, 2000, 2500, 5000, 10000, 20000, 40000, 100000, 200000, 500000, 1000000)
42+
3643
var allViews = []*view.View{
3744
{
3845
Name: "mongo/client/latency", Description: "The latency of the various calls",
39-
Measure: mLatencyMs,
40-
// [0ms, 0.001ms, 0.005ms, 0.01ms, 0.05ms, 0.1ms, 0.5ms, 1ms, 1.5ms, 2ms, 2.5ms, 5ms, 10ms, 25ms, 50ms, 100ms, 200ms,
41-
// 400ms, 600ms, 800ms, 1s, 1.5s, 2s, 2.5s, 5s, 10s, 20s, 40s, 100s, 200s, 500s, 1000s]
42-
//
43-
Aggregation: view.Distribution(0, 0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 1.5, 2, 2.5, 5, 10, 25, 50, 100, 200,
44-
400, 600, 800, 1000, 1500, 2000, 2500, 5000, 10000, 20000, 40000, 100000, 200000, 500000, 1000000),
45-
TagKeys: []tag.Key{keyMethod, keyStatus, keyError},
46+
Measure: mLatencyMs,
47+
Aggregation: latencyDistribution,
48+
TagKeys: []tag.Key{keyMethod, keyStatus, keyError},
4649
},
4750
{
4851
Name: "mongo/client/calls", Description: "The various calls",
@@ -68,8 +71,8 @@ type spanWithMetrics struct {
6871
endOnce sync.Once
6972
}
7073

71-
func roundtripTrackingSpan(ctx context.Context, methodName string) (context.Context, *spanWithMetrics) {
72-
ctx, span := trace.StartSpan(ctx, methodName)
74+
func roundtripTrackingSpan(ctx context.Context, methodName string, traceOpts ...trace.StartOption) (context.Context, *spanWithMetrics) {
75+
ctx, span := trace.StartSpan(ctx, methodName, traceOpts...)
7376
return ctx, &spanWithMetrics{span: span, startTime: time.Now()}
7477
}
7578

observability_test.go

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
// Copyright 2018, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package mongowrapper
16+
17+
import (
18+
"context"
19+
"errors"
20+
"reflect"
21+
"strings"
22+
"testing"
23+
"time"
24+
25+
"go.opencensus.io/stats/view"
26+
"go.opencensus.io/tag"
27+
"go.opencensus.io/trace"
28+
)
29+
30+
func TestUnitRoundtripTrackingOperation(t *testing.T) {
31+
if err := RegisterAllViews(); err != nil {
32+
t.Fatalf("Failed to register all the views: %v", err)
33+
}
34+
defer UnregisterAllViews()
35+
36+
reportingPeriod := 200 * time.Millisecond
37+
view.SetReportingPeriod(reportingPeriod)
38+
defer view.SetReportingPeriod(time.Minute)
39+
40+
viewDataChan := make(chan *view.Data, 1)
41+
spanDataChan := make(chan *trace.SpanData, 1)
42+
exp := &mockExporter{viewDataChan: viewDataChan, spanDataChan: spanDataChan}
43+
view.RegisterExporter(exp)
44+
defer view.UnregisterExporter(exp)
45+
46+
trace.RegisterExporter(exp)
47+
defer trace.UnregisterExporter(exp)
48+
49+
pausePeriod := 28 * time.Millisecond
50+
deadline := time.Now().Add(reportingPeriod)
51+
_, rts := roundtripTrackingSpan(context.Background(), "a.b.c/D.Foo", trace.WithSampler(trace.AlwaysSample()))
52+
<-time.After(pausePeriod / 2)
53+
errMsg := "This is an error"
54+
rts.setError(errors.New(errMsg))
55+
<-time.After(pausePeriod / 2)
56+
rts.end(context.Background())
57+
58+
// Verifying the spans since those don't
59+
// operate on a frequency.
60+
sd0 := <-spanDataChan
61+
// Comparing the name
62+
if g, w := sd0.Name, "a.b.c/D.Foo"; g != w {
63+
t.Errorf("SpanData.Name mismatch:: Got %q Want %q", g, w)
64+
}
65+
wantStatus := trace.Status{Code: trace.StatusCodeUnknown, Message: errMsg}
66+
if g, w := sd0.Status, wantStatus; g != w {
67+
t.Errorf("SpanData.Status mismatch:: Got %#v Want %#v", g, w)
68+
}
69+
minPeriod := pausePeriod
70+
gotPeriod := sd0.EndTime.Sub(sd0.StartTime)
71+
if gotPeriod < minPeriod {
72+
t.Errorf("SpanData.TimeSpent:: Got %s Want min: %s", gotPeriod, minPeriod)
73+
}
74+
75+
wait := deadline.Sub(time.Now()) + 3*time.Millisecond
76+
<-time.After(wait)
77+
78+
var vds []*view.Data
79+
maxWaitViewsTimer := time.NewTimer(wait)
80+
for done := false; !done; {
81+
select {
82+
case <-maxWaitViewsTimer.C:
83+
done = true
84+
break
85+
case vd := <-viewDataChan:
86+
// Great!
87+
vds = append(vds, vd)
88+
}
89+
}
90+
91+
if len(vds) < 2 {
92+
t.Errorf("Got %d ViewData; expected at least 2", len(vds))
93+
}
94+
95+
vdLatency := vds[0]
96+
vdCalls := vds[1]
97+
if strings.HasSuffix(vdCalls.View.Name, "client/latency") {
98+
// The order of reporting was wrong, so swap them.
99+
vdLatency, vdCalls = vdCalls, vdLatency
100+
}
101+
102+
// From this point on, we should have the proper views.
103+
104+
// Start examining Latency view.
105+
wantvLatency := &view.View{
106+
Name: "mongo/client/latency",
107+
Description: "The latency of the various calls",
108+
Measure: mLatencyMs,
109+
Aggregation: latencyDistribution,
110+
TagKeys: []tag.Key{keyError, keyMethod, keyStatus},
111+
}
112+
if g, w := vdLatency.View, wantvLatency; !reflect.DeepEqual(g, w) {
113+
t.Errorf("Latency.ViewData:\nGot: %#v\nWant:%#v\n", g, w)
114+
}
115+
if g, w := len(vdLatency.Rows), 1; g != w {
116+
t.Errorf("Latency.ViewData.Rows: Got %d Wanted %d", g, w)
117+
} else {
118+
r0 := vdLatency.Rows[0]
119+
// We need to have the row with the tag "error" since we ended with an error"
120+
wantTags := []tag.Tag{{Key: keyError, Value: errMsg}}
121+
if !reflect.DeepEqual(wantTags, r0.Tags) {
122+
t.Errorf("Latency.ViewData.Rows[0].Tags mismatch\nGot: %#v\nWant:%#v\n", r0.Tags, wantTags)
123+
}
124+
125+
// Compare the latency
126+
d0 := r0.Data.(*view.DistributionData)
127+
if g, w := d0.Count, int64(1); g != w {
128+
t.Errorf("DistributionData.Count:: Got %d Want %d", g, w)
129+
}
130+
if d0.Min != d0.Max || d0.Max != d0.Mean || d0.Min != d0.Mean {
131+
t.Errorf("DistributionData:: Expected all equal of these values:\nMin: %.4f Mean: %.4f Max: %.4f", d0.Min, d0.Mean, d0.Max)
132+
}
133+
if g, w := d0.Max, float64(pausePeriod)/float64(time.Millisecond); g < w {
134+
t.Errorf("Distribution.Max:: Got %.4f < Want %.4f", g, w)
135+
}
136+
}
137+
// End examining Latency view.
138+
139+
// Start examining Calls view.
140+
wantvCalls := &view.View{
141+
Name: "mongo/client/calls",
142+
Description: "The various calls",
143+
Measure: mLatencyMs,
144+
Aggregation: view.Count(),
145+
TagKeys: []tag.Key{keyError, keyMethod, keyStatus},
146+
}
147+
if g, w := vdCalls.View, wantvCalls; !reflect.DeepEqual(g, w) {
148+
t.Errorf("Calls.ViewData:\nGot: %#v\nWant:%#v\n", g, w)
149+
}
150+
if g, w := len(vdCalls.Rows), 1; g != w {
151+
t.Errorf("Calls.ViewdAta.Rows: Got %d Wanted %d", g, w)
152+
} else {
153+
r0 := vdCalls.Rows[0]
154+
wantTags := []tag.Tag{{Key: keyError, Value: errMsg}}
155+
if !reflect.DeepEqual(wantTags, r0.Tags) {
156+
t.Errorf("Calls.ViewData.Rows[0].Tags mismatch\nGot: %#v\nWant:%#v\n", r0.Tags, wantTags)
157+
}
158+
159+
// Now comparing the actual value
160+
d0 := r0.Data.(*view.CountData)
161+
if g, w := d0.Value, int64(1); g != w {
162+
t.Errorf("CountData.Count.Value:: Got %d Want %d", g, w)
163+
}
164+
}
165+
// End examining Calls view.
166+
}
167+
168+
type mockExporter struct {
169+
viewDataChan chan *view.Data
170+
spanDataChan chan *trace.SpanData
171+
}
172+
173+
var _ trace.Exporter = (*mockExporter)(nil)
174+
var _ view.Exporter = (*mockExporter)(nil)
175+
176+
func (me *mockExporter) ExportView(vd *view.Data) {
177+
me.viewDataChan <- vd
178+
}
179+
180+
func (me *mockExporter) ExportSpan(sd *trace.SpanData) {
181+
me.spanDataChan <- sd
182+
}

0 commit comments

Comments
 (0)