mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 04:22:39 +08:00
389 lines
10 KiB
Go
389 lines
10 KiB
Go
package zipkin
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"testing"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
|
"github.com/openzipkin/zipkin-go/model"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestZipkinClient_Services(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
mockResponse string
|
|
mockStatusCode int
|
|
mockStatus string
|
|
expectedResult []string
|
|
expectError bool
|
|
expectedError error
|
|
}{
|
|
{
|
|
name: "Successful response",
|
|
mockResponse: `["service1", "service2"]`,
|
|
mockStatusCode: http.StatusOK,
|
|
mockStatus: "OK",
|
|
expectedResult: []string{"service1", "service2"},
|
|
expectError: false,
|
|
expectedError: nil,
|
|
},
|
|
{
|
|
name: "Non-200 response",
|
|
mockResponse: "",
|
|
mockStatusCode: http.StatusInternalServerError,
|
|
mockStatus: "Internal Server Error",
|
|
expectedResult: []string{},
|
|
expectError: true,
|
|
expectedError: backend.DownstreamError(fmt.Errorf("request failed: Internal Server Error")),
|
|
},
|
|
{
|
|
name: "Invalid JSON response",
|
|
mockResponse: `{invalid json`,
|
|
mockStatusCode: http.StatusOK,
|
|
mockStatus: "OK",
|
|
expectedResult: []string{},
|
|
expectError: true,
|
|
expectedError: errors.New("invalid character 'i' looking for beginning of value"),
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Equal(t, "/api/v2/services", r.URL.Path)
|
|
w.WriteHeader(tt.mockStatusCode)
|
|
_, _ = w.Write([]byte(tt.mockResponse))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client, _ := New(server.URL, server.Client(), log.New())
|
|
services, err := client.Services()
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
assert.Equal(t, tt.expectedResult, services)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestZipkinClient_Spans(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
serviceName string
|
|
mockResponse string
|
|
mockStatusCode int
|
|
expectedResult []string
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "Successful response",
|
|
serviceName: "service1",
|
|
mockResponse: `["span1", "span2"]`,
|
|
mockStatusCode: http.StatusOK,
|
|
expectedResult: []string{"span1", "span2"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Non-200 response",
|
|
serviceName: "service1",
|
|
mockResponse: "",
|
|
mockStatusCode: http.StatusNotFound,
|
|
expectedResult: []string{},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "Invalid JSON response",
|
|
serviceName: "service1",
|
|
mockResponse: `{invalid json`,
|
|
mockStatusCode: http.StatusOK,
|
|
expectedResult: []string{},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "Empty serviceName",
|
|
serviceName: "",
|
|
mockResponse: "",
|
|
mockStatusCode: http.StatusOK,
|
|
expectedResult: []string{},
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Equal(t, "/api/v2/spans", r.URL.Path)
|
|
w.WriteHeader(tt.mockStatusCode)
|
|
_, _ = w.Write([]byte(tt.mockResponse))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client, _ := New(server.URL, server.Client(), log.New())
|
|
spans, err := client.Spans(tt.serviceName)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
assert.Equal(t, tt.expectedResult, spans)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestZipkinClient_Traces(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
serviceName string
|
|
spanName string
|
|
mockResponse interface{}
|
|
mockStatusCode int
|
|
expectedResult [][]model.SpanModel
|
|
expectError bool
|
|
expectedError string
|
|
}{
|
|
{
|
|
name: "Successful response",
|
|
serviceName: "service1",
|
|
spanName: "span1",
|
|
mockResponse: [][]model.SpanModel{{{SpanContext: model.SpanContext{TraceID: model.TraceID{Low: 1234}, ID: 1}, Name: "operation1", Tags: map[string]string{"key1": "value1"}}}},
|
|
mockStatusCode: http.StatusOK,
|
|
expectedResult: [][]model.SpanModel{{{SpanContext: model.SpanContext{TraceID: model.TraceID{Low: 1234}, ID: 1}, Name: "operation1", Tags: map[string]string{"key1": "value1"}}}},
|
|
expectError: false,
|
|
expectedError: "",
|
|
},
|
|
{
|
|
name: "Non-200 response",
|
|
serviceName: "service1",
|
|
spanName: "span1",
|
|
mockResponse: nil,
|
|
mockStatusCode: http.StatusForbidden,
|
|
expectedResult: [][]model.SpanModel{},
|
|
expectError: true,
|
|
expectedError: "EOF",
|
|
},
|
|
{
|
|
name: "Empty serviceName",
|
|
serviceName: "",
|
|
spanName: "span1",
|
|
mockResponse: nil,
|
|
mockStatusCode: http.StatusOK,
|
|
expectedResult: [][]model.SpanModel{},
|
|
expectError: true,
|
|
expectedError: "invalid/empty serviceName",
|
|
},
|
|
{
|
|
name: "Empty spanName",
|
|
serviceName: "service1",
|
|
spanName: "",
|
|
mockResponse: nil,
|
|
mockStatusCode: http.StatusOK,
|
|
expectedResult: [][]model.SpanModel{},
|
|
expectError: true,
|
|
expectedError: "invalid/empty spanName",
|
|
},
|
|
{
|
|
name: "Valid response with empty trace list",
|
|
serviceName: "service1",
|
|
spanName: "span1",
|
|
mockResponse: [][]model.SpanModel{},
|
|
mockStatusCode: http.StatusOK,
|
|
expectedResult: [][]model.SpanModel{},
|
|
expectError: false,
|
|
expectedError: "",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var response []byte
|
|
if mockData, ok := tt.mockResponse.([][]model.SpanModel); ok {
|
|
response, _ = json.Marshal(mockData)
|
|
} else if str, ok := tt.mockResponse.(string); ok {
|
|
response = []byte(str)
|
|
}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Equal(t, "/api/v2/traces", r.URL.Path)
|
|
w.WriteHeader(tt.mockStatusCode)
|
|
_, _ = w.Write(response)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client, _ := New(server.URL, server.Client(), log.New())
|
|
traces, err := client.Traces(tt.serviceName, tt.spanName)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
assert.Equal(t, err.Error(), tt.expectedError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
assert.Equal(t, tt.expectedResult, traces)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestZipkinClient_Trace(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
traceID string
|
|
mockResponse string
|
|
mockStatusCode int
|
|
expectedResult []model.SpanModel
|
|
expectError bool
|
|
expectedError string
|
|
}{
|
|
{
|
|
name: "Successful response",
|
|
traceID: "trace-id",
|
|
mockResponse: `[{"traceId":"00000000000004d2","id":"0000000000000001","name":"operation1","tags":{"key1":"value1"}}]`,
|
|
mockStatusCode: http.StatusOK,
|
|
expectedResult: []model.SpanModel{
|
|
{
|
|
SpanContext: model.SpanContext{
|
|
TraceID: model.TraceID{Low: 1234},
|
|
ID: model.ID(1),
|
|
},
|
|
Name: "operation1",
|
|
Tags: map[string]string{"key1": "value1"},
|
|
},
|
|
},
|
|
expectError: false,
|
|
expectedError: "",
|
|
},
|
|
{
|
|
name: "Invalid traceID",
|
|
traceID: "",
|
|
mockResponse: "",
|
|
mockStatusCode: http.StatusOK,
|
|
expectedResult: []model.SpanModel{},
|
|
expectError: true,
|
|
expectedError: "invalid/empty traceId",
|
|
},
|
|
{
|
|
name: "Special characters traceID",
|
|
traceID: "a/b",
|
|
mockResponse: `[{"traceId":"00000000000004d2","id":"0000000000000001","name":"operation1","tags":{"key1":"value1"}}]`,
|
|
mockStatusCode: http.StatusOK,
|
|
expectedResult: []model.SpanModel{
|
|
{
|
|
SpanContext: model.SpanContext{
|
|
TraceID: model.TraceID{Low: 1234},
|
|
ID: model.ID(1),
|
|
},
|
|
Name: "operation1",
|
|
Tags: map[string]string{"key1": "value1"},
|
|
},
|
|
},
|
|
expectError: false,
|
|
expectedError: "",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var client ZipkinClient
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Contains(t, r.URL.String(), "/api/v2/trace/"+url.QueryEscape(tt.traceID))
|
|
w.WriteHeader(tt.mockStatusCode)
|
|
_, _ = w.Write([]byte(tt.mockResponse))
|
|
}))
|
|
defer server.Close()
|
|
client, _ = New(server.URL, server.Client(), log.New())
|
|
|
|
trace, err := client.Trace(tt.traceID)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
assert.Empty(t, trace)
|
|
assert.Equal(t, tt.expectedError, err.Error())
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tt.expectedResult, trace)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCreateZipkinURL(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
baseURL string
|
|
path string
|
|
params map[string]string
|
|
expected string
|
|
shouldErr bool
|
|
}{
|
|
{
|
|
name: "WithPathAndParams",
|
|
baseURL: "http://example.com",
|
|
path: "api/v1/trace",
|
|
params: map[string]string{"key1": "value1", "key2": "value2"},
|
|
expected: "http://example.com/api/v1/trace?key1=value1&key2=value2",
|
|
},
|
|
{
|
|
name: "OnlyParams",
|
|
baseURL: "http://example.com",
|
|
path: "",
|
|
params: map[string]string{"key1": "value1"},
|
|
expected: "http://example.com?key1=value1",
|
|
},
|
|
{
|
|
name: "NoParams",
|
|
baseURL: "http://example.com",
|
|
path: "api/v1/trace",
|
|
params: map[string]string{},
|
|
expected: "http://example.com/api/v1/trace",
|
|
},
|
|
{
|
|
name: "InvalidBaseURL",
|
|
baseURL: "http://example .com",
|
|
path: "api/v1/trace",
|
|
params: map[string]string{},
|
|
shouldErr: true,
|
|
},
|
|
{
|
|
name: "BaseURLWithPath",
|
|
baseURL: "http://example.com/base",
|
|
path: "api/v1/trace",
|
|
params: map[string]string{"key1": "value1"},
|
|
expected: "http://example.com/base/api/v1/trace?key1=value1",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result, err := createZipkinURL(tc.baseURL, tc.path, tc.params)
|
|
|
|
if tc.shouldErr {
|
|
if err == nil {
|
|
t.Fatalf("Expected error, but got nil")
|
|
}
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
if result != tc.expected {
|
|
t.Errorf("Expected %s, got %s", tc.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|