484 строки
16 KiB
Go
484 строки
16 KiB
Go
package appinsights
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
const test_ikey = "01234567-0000-89ab-cdef-000000000000"
|
|
|
|
func TestJsonSerializerEvents(t *testing.T) {
|
|
mockClock(time.Unix(1511001321, 0))
|
|
defer resetClock()
|
|
|
|
var buffer telemetryBufferItems
|
|
|
|
buffer.add(
|
|
NewTraceTelemetry("testing", Error),
|
|
NewEventTelemetry("an-event"),
|
|
NewMetricTelemetry("a-metric", 567),
|
|
)
|
|
|
|
req := NewRequestTelemetry("method", "my-url", time.Minute, "204")
|
|
req.Name = "req-name"
|
|
req.Id = "my-id"
|
|
buffer.add(req)
|
|
|
|
agg := NewAggregateMetricTelemetry("agg-metric")
|
|
agg.AddData([]float64{1, 2, 3})
|
|
buffer.add(agg)
|
|
|
|
remdep := NewRemoteDependencyTelemetry("bing-remote-dep", "http", "www.bing.com", false)
|
|
remdep.Data = "some-data"
|
|
remdep.ResultCode = "arg"
|
|
remdep.Duration = 4 * time.Second
|
|
remdep.Properties["hi"] = "hello"
|
|
buffer.add(remdep)
|
|
|
|
avail := NewAvailabilityTelemetry("webtest", 8*time.Second, true)
|
|
avail.RunLocation = "jupiter"
|
|
avail.Message = "ok."
|
|
avail.Measurements["measure"] = 88.0
|
|
avail.Id = "avail-id"
|
|
buffer.add(avail)
|
|
|
|
view := NewPageViewTelemetry("name", "http://bing.com")
|
|
view.Duration = 4 * time.Minute
|
|
buffer.add(view)
|
|
|
|
j, err := parsePayload(buffer.serialize())
|
|
if err != nil {
|
|
t.Errorf("Error parsing payload: %s", err.Error())
|
|
}
|
|
|
|
if len(j) != 8 {
|
|
t.Fatal("Unexpected event count")
|
|
}
|
|
|
|
// Trace
|
|
j[0].assertPath(t, "iKey", test_ikey)
|
|
j[0].assertPath(t, "name", "Microsoft.ApplicationInsights.Message")
|
|
j[0].assertPath(t, "time", "2017-11-18T10:35:21Z")
|
|
j[0].assertPath(t, "sampleRate", 100.0)
|
|
j[0].assertPath(t, "data.baseType", "MessageData")
|
|
j[0].assertPath(t, "data.baseData.message", "testing")
|
|
j[0].assertPath(t, "data.baseData.severityLevel", 3)
|
|
j[0].assertPath(t, "data.baseData.ver", 2)
|
|
|
|
// Event
|
|
j[1].assertPath(t, "iKey", test_ikey)
|
|
j[1].assertPath(t, "name", "Microsoft.ApplicationInsights.Event")
|
|
j[1].assertPath(t, "time", "2017-11-18T10:35:21Z")
|
|
j[1].assertPath(t, "sampleRate", 100.0)
|
|
j[1].assertPath(t, "data.baseType", "EventData")
|
|
j[1].assertPath(t, "data.baseData.name", "an-event")
|
|
j[1].assertPath(t, "data.baseData.ver", 2)
|
|
|
|
// Metric
|
|
j[2].assertPath(t, "iKey", test_ikey)
|
|
j[2].assertPath(t, "name", "Microsoft.ApplicationInsights.Metric")
|
|
j[2].assertPath(t, "time", "2017-11-18T10:35:21Z")
|
|
j[2].assertPath(t, "sampleRate", 100.0)
|
|
j[2].assertPath(t, "data.baseType", "MetricData")
|
|
j[2].assertPath(t, "data.baseData.metrics.<len>", 1)
|
|
j[2].assertPath(t, "data.baseData.metrics.[0].value", 567)
|
|
j[2].assertPath(t, "data.baseData.metrics.[0].count", 1)
|
|
j[2].assertPath(t, "data.baseData.metrics.[0].kind", 0)
|
|
j[2].assertPath(t, "data.baseData.metrics.[0].name", "a-metric")
|
|
j[2].assertPath(t, "data.baseData.ver", 2)
|
|
|
|
// Request
|
|
j[3].assertPath(t, "iKey", test_ikey)
|
|
j[3].assertPath(t, "name", "Microsoft.ApplicationInsights.Request")
|
|
j[3].assertPath(t, "time", "2017-11-18T10:34:21Z") // Constructor subtracts duration
|
|
j[3].assertPath(t, "sampleRate", 100.0)
|
|
j[3].assertPath(t, "data.baseType", "RequestData")
|
|
j[3].assertPath(t, "data.baseData.name", "req-name")
|
|
j[3].assertPath(t, "data.baseData.duration", "0.00:01:00.0000000")
|
|
j[3].assertPath(t, "data.baseData.responseCode", "204")
|
|
j[3].assertPath(t, "data.baseData.success", true)
|
|
j[3].assertPath(t, "data.baseData.id", "my-id")
|
|
j[3].assertPath(t, "data.baseData.url", "my-url")
|
|
j[3].assertPath(t, "data.baseData.ver", 2)
|
|
|
|
// Aggregate metric
|
|
j[4].assertPath(t, "iKey", test_ikey)
|
|
j[4].assertPath(t, "name", "Microsoft.ApplicationInsights.Metric")
|
|
j[4].assertPath(t, "time", "2017-11-18T10:35:21Z")
|
|
j[4].assertPath(t, "sampleRate", 100.0)
|
|
j[4].assertPath(t, "data.baseType", "MetricData")
|
|
j[4].assertPath(t, "data.baseData.metrics.<len>", 1)
|
|
j[4].assertPath(t, "data.baseData.metrics.[0].value", 6)
|
|
j[4].assertPath(t, "data.baseData.metrics.[0].count", 3)
|
|
j[4].assertPath(t, "data.baseData.metrics.[0].kind", 1)
|
|
j[4].assertPath(t, "data.baseData.metrics.[0].min", 1)
|
|
j[4].assertPath(t, "data.baseData.metrics.[0].max", 3)
|
|
j[4].assertPath(t, "data.baseData.metrics.[0].stdDev", 0.8164)
|
|
j[4].assertPath(t, "data.baseData.metrics.[0].name", "agg-metric")
|
|
j[4].assertPath(t, "data.baseData.ver", 2)
|
|
|
|
// Remote dependency
|
|
j[5].assertPath(t, "iKey", test_ikey)
|
|
j[5].assertPath(t, "name", "Microsoft.ApplicationInsights.RemoteDependency")
|
|
j[5].assertPath(t, "time", "2017-11-18T10:35:21Z")
|
|
j[5].assertPath(t, "sampleRate", 100.0)
|
|
j[5].assertPath(t, "data.baseType", "RemoteDependencyData")
|
|
j[5].assertPath(t, "data.baseData.name", "bing-remote-dep")
|
|
j[5].assertPath(t, "data.baseData.id", "")
|
|
j[5].assertPath(t, "data.baseData.resultCode", "arg")
|
|
j[5].assertPath(t, "data.baseData.duration", "0.00:00:04.0000000")
|
|
j[5].assertPath(t, "data.baseData.success", false)
|
|
j[5].assertPath(t, "data.baseData.data", "some-data")
|
|
j[5].assertPath(t, "data.baseData.target", "www.bing.com")
|
|
j[5].assertPath(t, "data.baseData.type", "http")
|
|
j[5].assertPath(t, "data.baseData.properties.hi", "hello")
|
|
j[5].assertPath(t, "data.baseData.ver", 2)
|
|
|
|
// Availability
|
|
j[6].assertPath(t, "iKey", test_ikey)
|
|
j[6].assertPath(t, "name", "Microsoft.ApplicationInsights.Availability")
|
|
j[6].assertPath(t, "time", "2017-11-18T10:35:21Z")
|
|
j[6].assertPath(t, "sampleRate", 100.0)
|
|
j[6].assertPath(t, "data.baseType", "AvailabilityData")
|
|
j[6].assertPath(t, "data.baseData.name", "webtest")
|
|
j[6].assertPath(t, "data.baseData.duration", "0.00:00:08.0000000")
|
|
j[6].assertPath(t, "data.baseData.success", true)
|
|
j[6].assertPath(t, "data.baseData.runLocation", "jupiter")
|
|
j[6].assertPath(t, "data.baseData.message", "ok.")
|
|
j[6].assertPath(t, "data.baseData.id", "avail-id")
|
|
j[6].assertPath(t, "data.baseData.ver", 2)
|
|
j[6].assertPath(t, "data.baseData.measurements.measure", 88)
|
|
|
|
// Page view
|
|
j[7].assertPath(t, "iKey", test_ikey)
|
|
j[7].assertPath(t, "name", "Microsoft.ApplicationInsights.PageView")
|
|
j[7].assertPath(t, "time", "2017-11-18T10:35:21Z")
|
|
j[7].assertPath(t, "sampleRate", 100.0)
|
|
j[7].assertPath(t, "data.baseType", "PageViewData")
|
|
j[7].assertPath(t, "data.baseData.name", "name")
|
|
j[7].assertPath(t, "data.baseData.url", "http://bing.com")
|
|
j[7].assertPath(t, "data.baseData.duration", "0.00:04:00.0000000")
|
|
j[7].assertPath(t, "data.baseData.ver", 2)
|
|
}
|
|
|
|
func TestJsonSerializerNakedEvents(t *testing.T) {
|
|
mockClock(time.Unix(1511001321, 0))
|
|
defer resetClock()
|
|
|
|
var buffer telemetryBufferItems
|
|
|
|
buffer.add(
|
|
&TraceTelemetry{
|
|
Message: "Naked telemetry",
|
|
SeverityLevel: Warning,
|
|
},
|
|
&EventTelemetry{
|
|
Name: "Naked event",
|
|
},
|
|
&MetricTelemetry{
|
|
Name: "my-metric",
|
|
Value: 456.0,
|
|
},
|
|
&AggregateMetricTelemetry{
|
|
Name: "agg-metric",
|
|
Value: 50,
|
|
Min: 2,
|
|
Max: 7,
|
|
Count: 9,
|
|
StdDev: 3,
|
|
},
|
|
&RequestTelemetry{
|
|
Name: "req-name",
|
|
Url: "req-url",
|
|
Duration: time.Minute,
|
|
ResponseCode: "Response",
|
|
Success: true,
|
|
Source: "localhost",
|
|
},
|
|
&RemoteDependencyTelemetry{
|
|
Name: "dep-name",
|
|
ResultCode: "ok.",
|
|
Duration: time.Hour,
|
|
Success: true,
|
|
Data: "dep-data",
|
|
Type: "dep-type",
|
|
Target: "dep-target",
|
|
},
|
|
&AvailabilityTelemetry{
|
|
Name: "avail-name",
|
|
Duration: 3 * time.Minute,
|
|
Success: true,
|
|
RunLocation: "run-loc",
|
|
Message: "avail-msg",
|
|
},
|
|
&PageViewTelemetry{
|
|
Url: "page-view-url",
|
|
Duration: 4 * time.Second,
|
|
Name: "page-view-name",
|
|
},
|
|
)
|
|
|
|
j, err := parsePayload(buffer.serialize())
|
|
if err != nil {
|
|
t.Errorf("Error parsing payload: %s", err.Error())
|
|
}
|
|
|
|
if len(j) != 8 {
|
|
t.Fatal("Unexpected event count")
|
|
}
|
|
|
|
// Trace
|
|
j[0].assertPath(t, "iKey", test_ikey)
|
|
j[0].assertPath(t, "name", "Microsoft.ApplicationInsights.Message")
|
|
j[0].assertPath(t, "time", "2017-11-18T10:35:21Z")
|
|
j[0].assertPath(t, "sampleRate", 100)
|
|
j[0].assertPath(t, "data.baseType", "MessageData")
|
|
j[0].assertPath(t, "data.baseData.message", "Naked telemetry")
|
|
j[0].assertPath(t, "data.baseData.severityLevel", 2)
|
|
j[0].assertPath(t, "data.baseData.ver", 2)
|
|
|
|
// Event
|
|
j[1].assertPath(t, "iKey", test_ikey)
|
|
j[1].assertPath(t, "name", "Microsoft.ApplicationInsights.Event")
|
|
j[1].assertPath(t, "time", "2017-11-18T10:35:21Z")
|
|
j[1].assertPath(t, "sampleRate", 100)
|
|
j[1].assertPath(t, "data.baseType", "EventData")
|
|
j[1].assertPath(t, "data.baseData.name", "Naked event")
|
|
j[1].assertPath(t, "data.baseData.ver", 2)
|
|
|
|
// Metric
|
|
j[2].assertPath(t, "iKey", test_ikey)
|
|
j[2].assertPath(t, "name", "Microsoft.ApplicationInsights.Metric")
|
|
j[2].assertPath(t, "time", "2017-11-18T10:35:21Z")
|
|
j[2].assertPath(t, "sampleRate", 100)
|
|
j[2].assertPath(t, "data.baseType", "MetricData")
|
|
j[2].assertPath(t, "data.baseData.metrics.<len>", 1)
|
|
j[2].assertPath(t, "data.baseData.metrics.[0].value", 456)
|
|
j[2].assertPath(t, "data.baseData.metrics.[0].count", 1)
|
|
j[2].assertPath(t, "data.baseData.metrics.[0].kind", 0)
|
|
j[2].assertPath(t, "data.baseData.metrics.[0].name", "my-metric")
|
|
j[2].assertPath(t, "data.baseData.ver", 2)
|
|
|
|
// Aggregate metric
|
|
j[3].assertPath(t, "iKey", test_ikey)
|
|
j[3].assertPath(t, "name", "Microsoft.ApplicationInsights.Metric")
|
|
j[3].assertPath(t, "time", "2017-11-18T10:35:21Z")
|
|
j[3].assertPath(t, "sampleRate", 100.0)
|
|
j[3].assertPath(t, "data.baseType", "MetricData")
|
|
j[3].assertPath(t, "data.baseData.metrics.<len>", 1)
|
|
j[3].assertPath(t, "data.baseData.metrics.[0].value", 50)
|
|
j[3].assertPath(t, "data.baseData.metrics.[0].count", 9)
|
|
j[3].assertPath(t, "data.baseData.metrics.[0].kind", 1)
|
|
j[3].assertPath(t, "data.baseData.metrics.[0].min", 2)
|
|
j[3].assertPath(t, "data.baseData.metrics.[0].max", 7)
|
|
j[3].assertPath(t, "data.baseData.metrics.[0].stdDev", 3)
|
|
j[3].assertPath(t, "data.baseData.metrics.[0].name", "agg-metric")
|
|
j[3].assertPath(t, "data.baseData.ver", 2)
|
|
|
|
// Request
|
|
j[4].assertPath(t, "iKey", test_ikey)
|
|
j[4].assertPath(t, "name", "Microsoft.ApplicationInsights.Request")
|
|
j[4].assertPath(t, "time", "2017-11-18T10:35:21Z") // Context takes current time since it's not supplied
|
|
j[4].assertPath(t, "sampleRate", 100.0)
|
|
j[4].assertPath(t, "data.baseType", "RequestData")
|
|
j[4].assertPath(t, "data.baseData.name", "req-name")
|
|
j[4].assertPath(t, "data.baseData.duration", "0.00:01:00.0000000")
|
|
j[4].assertPath(t, "data.baseData.responseCode", "Response")
|
|
j[4].assertPath(t, "data.baseData.success", true)
|
|
j[4].assertPath(t, "data.baseData.url", "req-url")
|
|
j[4].assertPath(t, "data.baseData.source", "localhost")
|
|
j[4].assertPath(t, "data.baseData.ver", 2)
|
|
|
|
if id, err := j[4].getPath("data.baseData.id"); err != nil {
|
|
t.Errorf("Id not present")
|
|
} else if len(id.(string)) == 0 {
|
|
t.Errorf("Empty request id")
|
|
}
|
|
|
|
// Remote dependency
|
|
j[5].assertPath(t, "iKey", test_ikey)
|
|
j[5].assertPath(t, "name", "Microsoft.ApplicationInsights.RemoteDependency")
|
|
j[5].assertPath(t, "time", "2017-11-18T10:35:21Z")
|
|
j[5].assertPath(t, "sampleRate", 100.0)
|
|
j[5].assertPath(t, "data.baseType", "RemoteDependencyData")
|
|
j[5].assertPath(t, "data.baseData.name", "dep-name")
|
|
j[5].assertPath(t, "data.baseData.id", "")
|
|
j[5].assertPath(t, "data.baseData.resultCode", "ok.")
|
|
j[5].assertPath(t, "data.baseData.duration", "0.01:00:00.0000000")
|
|
j[5].assertPath(t, "data.baseData.success", true)
|
|
j[5].assertPath(t, "data.baseData.data", "dep-data")
|
|
j[5].assertPath(t, "data.baseData.target", "dep-target")
|
|
j[5].assertPath(t, "data.baseData.type", "dep-type")
|
|
j[5].assertPath(t, "data.baseData.ver", 2)
|
|
|
|
// Availability
|
|
j[6].assertPath(t, "iKey", test_ikey)
|
|
j[6].assertPath(t, "name", "Microsoft.ApplicationInsights.Availability")
|
|
j[6].assertPath(t, "time", "2017-11-18T10:35:21Z")
|
|
j[6].assertPath(t, "sampleRate", 100.0)
|
|
j[6].assertPath(t, "data.baseType", "AvailabilityData")
|
|
j[6].assertPath(t, "data.baseData.name", "avail-name")
|
|
j[6].assertPath(t, "data.baseData.duration", "0.00:03:00.0000000")
|
|
j[6].assertPath(t, "data.baseData.success", true)
|
|
j[6].assertPath(t, "data.baseData.runLocation", "run-loc")
|
|
j[6].assertPath(t, "data.baseData.message", "avail-msg")
|
|
j[6].assertPath(t, "data.baseData.id", "")
|
|
j[6].assertPath(t, "data.baseData.ver", 2)
|
|
|
|
// Page view
|
|
j[7].assertPath(t, "iKey", test_ikey)
|
|
j[7].assertPath(t, "name", "Microsoft.ApplicationInsights.PageView")
|
|
j[7].assertPath(t, "time", "2017-11-18T10:35:21Z")
|
|
j[7].assertPath(t, "sampleRate", 100.0)
|
|
j[7].assertPath(t, "data.baseType", "PageViewData")
|
|
j[7].assertPath(t, "data.baseData.name", "page-view-name")
|
|
j[7].assertPath(t, "data.baseData.url", "page-view-url")
|
|
j[7].assertPath(t, "data.baseData.duration", "0.00:00:04.0000000")
|
|
j[7].assertPath(t, "data.baseData.ver", 2)
|
|
}
|
|
|
|
// Test helpers...
|
|
|
|
func telemetryBuffer(items ...Telemetry) telemetryBufferItems {
|
|
ctx := NewTelemetryContext()
|
|
ctx.iKey = test_ikey
|
|
|
|
var result telemetryBufferItems
|
|
for _, item := range items {
|
|
result = append(result, ctx.envelop(item))
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (buffer *telemetryBufferItems) add(items ...Telemetry) {
|
|
*buffer = append(*buffer, telemetryBuffer(items...)...)
|
|
}
|
|
|
|
type jsonMessage map[string]interface{}
|
|
type jsonPayload []jsonMessage
|
|
|
|
func parsePayload(payload []byte) (jsonPayload, error) {
|
|
// json.Decoder can detect line endings for us but I'd like to explicitly find them.
|
|
var result jsonPayload
|
|
for _, item := range bytes.Split(payload, []byte("\n")) {
|
|
if len(item) == 0 {
|
|
continue
|
|
}
|
|
|
|
decoder := json.NewDecoder(bytes.NewReader(item))
|
|
msg := make(jsonMessage)
|
|
if err := decoder.Decode(&msg); err == nil {
|
|
result = append(result, msg)
|
|
} else {
|
|
return result, err
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (msg jsonMessage) assertPath(t *testing.T, path string, value interface{}) {
|
|
const tolerance = 0.0001
|
|
v, err := msg.getPath(path)
|
|
if err != nil {
|
|
t.Error(err.Error())
|
|
return
|
|
}
|
|
|
|
if num, ok := value.(int); ok {
|
|
if vnum, ok := v.(float64); ok {
|
|
if math.Abs(float64(num)-vnum) > tolerance {
|
|
t.Errorf("Data was unexpected at %s. Got %g want %d", path, vnum, num)
|
|
}
|
|
} else if vnum, ok := v.(int); ok {
|
|
if vnum != num {
|
|
t.Errorf("Data was unexpected at %s. Got %d want %d", path, vnum, num)
|
|
}
|
|
} else {
|
|
t.Errorf("Expected value at %s to be a number, but was %T", path, v)
|
|
}
|
|
} else if num, ok := value.(float64); ok {
|
|
if vnum, ok := v.(float64); ok {
|
|
if math.Abs(num-vnum) > tolerance {
|
|
t.Errorf("Data was unexpected at %s. Got %g want %g", path, vnum, num)
|
|
}
|
|
} else if vnum, ok := v.(int); ok {
|
|
if math.Abs(num-float64(vnum)) > tolerance {
|
|
t.Errorf("Data was unexpected at %s. Got %d want %g", path, vnum, num)
|
|
}
|
|
} else {
|
|
t.Errorf("Expected value at %s to be a number, but was %T", path, v)
|
|
}
|
|
} else if str, ok := value.(string); ok {
|
|
if vstr, ok := v.(string); ok {
|
|
if str != vstr {
|
|
t.Errorf("Data was unexpected at %s. Got '%s' want '%s'", path, vstr, str)
|
|
}
|
|
} else {
|
|
t.Errorf("Expected value at %s to be a string, but was %T", path, v)
|
|
}
|
|
} else if bl, ok := value.(bool); ok {
|
|
if vbool, ok := v.(bool); ok {
|
|
if bl != vbool {
|
|
t.Errorf("Data was unexpected at %s. Got %t want %t", path, vbool, bl)
|
|
}
|
|
} else {
|
|
t.Errorf("Expected value at %s to be a bool, but was %T", path, v)
|
|
}
|
|
} else {
|
|
t.Errorf("Unsupported type: %#v", value)
|
|
}
|
|
}
|
|
|
|
func (msg jsonMessage) getPath(path string) (interface{}, error) {
|
|
parts := strings.Split(path, ".")
|
|
var obj interface{} = msg
|
|
for i, part := range parts {
|
|
if strings.HasPrefix(part, "[") && strings.HasSuffix(part, "]") {
|
|
// Array
|
|
idxstr := part[1 : len(part)-2]
|
|
idx, _ := strconv.Atoi(idxstr)
|
|
|
|
if ar, ok := obj.([]interface{}); ok {
|
|
if idx >= len(ar) {
|
|
return nil, fmt.Errorf("Index out of bounds: %s", strings.Join(parts[0:i+1], "."))
|
|
}
|
|
|
|
obj = ar[idx]
|
|
} else {
|
|
return nil, fmt.Errorf("Path %s is not an array", strings.Join(parts[0:i], "."))
|
|
}
|
|
} else if part == "<len>" {
|
|
if ar, ok := obj.([]interface{}); ok {
|
|
return len(ar), nil
|
|
}
|
|
} else {
|
|
// Map
|
|
if dict, ok := obj.(jsonMessage); ok {
|
|
if val, ok := dict[part]; ok {
|
|
obj = val
|
|
} else {
|
|
return nil, fmt.Errorf("Key %s not found in %s", part, strings.Join(parts[0:i], "."))
|
|
}
|
|
} else if dict, ok := obj.(map[string]interface{}); ok {
|
|
if val, ok := dict[part]; ok {
|
|
obj = val
|
|
} else {
|
|
return nil, fmt.Errorf("Key %s not found in %s", part, strings.Join(parts[0:i], "."))
|
|
}
|
|
} else {
|
|
return nil, fmt.Errorf("Path %s is not a map", strings.Join(parts[0:i], "."))
|
|
}
|
|
}
|
|
}
|
|
|
|
return obj, nil
|
|
}
|