01f959be97
- Surface error codes for datasource possible errors (not found, unreachabel, auth, timeout)
246 lines
7.2 KiB
Go
246 lines
7.2 KiB
Go
package validator
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"testing"
|
|
)
|
|
|
|
func TestNewDatasourceNotFoundError(t *testing.T) {
|
|
err := NewDatasourceNotFoundError("test-uid", "org-1")
|
|
|
|
if err.Code != ErrCodeDatasourceNotFound {
|
|
t.Errorf("expected error code %s, got %s", ErrCodeDatasourceNotFound, err.Code)
|
|
}
|
|
|
|
if err.StatusCode != http.StatusNotFound {
|
|
t.Errorf("expected status code %d, got %d", http.StatusNotFound, err.StatusCode)
|
|
}
|
|
|
|
if err.Details["datasourceUID"] != "test-uid" {
|
|
t.Errorf("expected datasourceUID detail to be 'test-uid', got %v", err.Details["datasourceUID"])
|
|
}
|
|
|
|
if err.Details["namespace"] != "org-1" {
|
|
t.Errorf("expected namespace detail to be 'org-1', got %v", err.Details["namespace"])
|
|
}
|
|
}
|
|
|
|
func TestNewDatasourceWrongTypeError(t *testing.T) {
|
|
err := NewDatasourceWrongTypeError("test-uid", "prometheus", "influxdb")
|
|
|
|
if err.Code != ErrCodeDatasourceWrongType {
|
|
t.Errorf("expected error code %s, got %s", ErrCodeDatasourceWrongType, err.Code)
|
|
}
|
|
|
|
if err.StatusCode != http.StatusBadRequest {
|
|
t.Errorf("expected status code %d, got %d", http.StatusBadRequest, err.StatusCode)
|
|
}
|
|
|
|
if err.Details["expectedType"] != "prometheus" {
|
|
t.Errorf("expected expectedType detail to be 'prometheus', got %v", err.Details["expectedType"])
|
|
}
|
|
|
|
if err.Details["actualType"] != "influxdb" {
|
|
t.Errorf("expected actualType detail to be 'influxdb', got %v", err.Details["actualType"])
|
|
}
|
|
}
|
|
|
|
func TestNewDatasourceUnreachableError(t *testing.T) {
|
|
cause := errors.New("connection refused")
|
|
err := NewDatasourceUnreachableError("test-uid", "http://localhost:9090", cause)
|
|
|
|
if err.Code != ErrCodeDatasourceUnreachable {
|
|
t.Errorf("expected error code %s, got %s", ErrCodeDatasourceUnreachable, err.Code)
|
|
}
|
|
|
|
if err.StatusCode != http.StatusServiceUnavailable {
|
|
t.Errorf("expected status code %d, got %d", http.StatusServiceUnavailable, err.StatusCode)
|
|
}
|
|
|
|
if err.Cause != cause {
|
|
t.Errorf("expected cause to be set")
|
|
}
|
|
|
|
if err.Details["url"] != "http://localhost:9090" {
|
|
t.Errorf("expected url detail to be 'http://localhost:9090', got %v", err.Details["url"])
|
|
}
|
|
}
|
|
|
|
func TestNewAPIUnavailableError(t *testing.T) {
|
|
err := NewAPIUnavailableError(503, "service unavailable", nil)
|
|
|
|
if err.Code != ErrCodeAPIUnavailable {
|
|
t.Errorf("expected error code %s, got %s", ErrCodeAPIUnavailable, err.Code)
|
|
}
|
|
|
|
if err.StatusCode != http.StatusBadGateway {
|
|
t.Errorf("expected status code %d, got %d", http.StatusBadGateway, err.StatusCode)
|
|
}
|
|
|
|
if err.Details["upstreamStatus"] != 503 {
|
|
t.Errorf("expected upstreamStatus detail to be 503, got %v", err.Details["upstreamStatus"])
|
|
}
|
|
}
|
|
|
|
func TestNewAPIInvalidResponseError(t *testing.T) {
|
|
cause := errors.New("invalid JSON")
|
|
err := NewAPIInvalidResponseError("missing data field", cause)
|
|
|
|
if err.Code != ErrCodeAPIInvalidResponse {
|
|
t.Errorf("expected error code %s, got %s", ErrCodeAPIInvalidResponse, err.Code)
|
|
}
|
|
|
|
if err.StatusCode != http.StatusBadGateway {
|
|
t.Errorf("expected status code %d, got %d", http.StatusBadGateway, err.StatusCode)
|
|
}
|
|
|
|
if err.Cause != cause {
|
|
t.Errorf("expected cause to be set")
|
|
}
|
|
}
|
|
|
|
func TestNewAPITimeoutError(t *testing.T) {
|
|
cause := errors.New("context deadline exceeded")
|
|
err := NewAPITimeoutError("http://localhost:9090/api/v1/query", cause)
|
|
|
|
if err.Code != ErrCodeAPITimeout {
|
|
t.Errorf("expected error code %s, got %s", ErrCodeAPITimeout, err.Code)
|
|
}
|
|
|
|
if err.StatusCode != http.StatusGatewayTimeout {
|
|
t.Errorf("expected status code %d, got %d", http.StatusGatewayTimeout, err.StatusCode)
|
|
}
|
|
|
|
if err.Cause != cause {
|
|
t.Errorf("expected cause to be set")
|
|
}
|
|
}
|
|
|
|
func TestNewDatasourceAuthError(t *testing.T) {
|
|
err := NewDatasourceAuthError("test-uid", 401)
|
|
|
|
if err.Code != ErrCodeDatasourceAuth {
|
|
t.Errorf("expected error code %s, got %s", ErrCodeDatasourceAuth, err.Code)
|
|
}
|
|
|
|
if err.StatusCode != http.StatusUnauthorized {
|
|
t.Errorf("expected status code %d, got %d", http.StatusUnauthorized, err.StatusCode)
|
|
}
|
|
|
|
if err.Details["upstreamStatus"] != 401 {
|
|
t.Errorf("expected upstreamStatus detail to be 401, got %v", err.Details["upstreamStatus"])
|
|
}
|
|
}
|
|
|
|
func TestValidationErrorChaining(t *testing.T) {
|
|
cause := errors.New("network error")
|
|
err := NewValidationError(ErrCodeInternal, "test error", http.StatusInternalServerError).
|
|
WithCause(cause).
|
|
WithDetail("key1", "value1").
|
|
WithDetail("key2", 123)
|
|
|
|
if err.Cause != cause {
|
|
t.Errorf("expected cause to be set")
|
|
}
|
|
|
|
if err.Details["key1"] != "value1" {
|
|
t.Errorf("expected detail key1 to be 'value1', got %v", err.Details["key1"])
|
|
}
|
|
|
|
if err.Details["key2"] != 123 {
|
|
t.Errorf("expected detail key2 to be 123, got %v", err.Details["key2"])
|
|
}
|
|
}
|
|
|
|
func TestIsValidationError(t *testing.T) {
|
|
validationErr := NewDatasourceNotFoundError("test-uid", "org-1")
|
|
regularErr := errors.New("regular error")
|
|
|
|
if !IsValidationError(validationErr) {
|
|
t.Errorf("expected IsValidationError to return true for ValidationError")
|
|
}
|
|
|
|
if IsValidationError(regularErr) {
|
|
t.Errorf("expected IsValidationError to return false for regular error")
|
|
}
|
|
}
|
|
|
|
func TestGetValidationError(t *testing.T) {
|
|
validationErr := NewDatasourceNotFoundError("test-uid", "org-1")
|
|
regularErr := errors.New("regular error")
|
|
|
|
retrieved := GetValidationError(validationErr)
|
|
if retrieved == nil {
|
|
t.Errorf("expected GetValidationError to return the ValidationError")
|
|
}
|
|
if retrieved.Code != ErrCodeDatasourceNotFound {
|
|
t.Errorf("expected retrieved error to have correct code")
|
|
}
|
|
|
|
retrieved = GetValidationError(regularErr)
|
|
if retrieved != nil {
|
|
t.Errorf("expected GetValidationError to return nil for regular error")
|
|
}
|
|
}
|
|
|
|
func TestGetHTTPStatusCode(t *testing.T) {
|
|
validationErr := NewDatasourceNotFoundError("test-uid", "org-1")
|
|
regularErr := errors.New("regular error")
|
|
|
|
statusCode := GetHTTPStatusCode(validationErr)
|
|
if statusCode != http.StatusNotFound {
|
|
t.Errorf("expected status code %d, got %d", http.StatusNotFound, statusCode)
|
|
}
|
|
|
|
statusCode = GetHTTPStatusCode(regularErr)
|
|
if statusCode != http.StatusInternalServerError {
|
|
t.Errorf("expected default status code %d for regular error, got %d", http.StatusInternalServerError, statusCode)
|
|
}
|
|
}
|
|
|
|
func TestErrorUnwrap(t *testing.T) {
|
|
cause := errors.New("underlying error")
|
|
err := NewDatasourceUnreachableError("test-uid", "http://localhost:9090", cause)
|
|
|
|
unwrapped := errors.Unwrap(err)
|
|
if unwrapped != cause {
|
|
t.Errorf("expected Unwrap to return the cause")
|
|
}
|
|
}
|
|
|
|
func TestErrorErrorMethod(t *testing.T) {
|
|
// Test without cause
|
|
err1 := NewDatasourceNotFoundError("test-uid", "org-1")
|
|
errMsg1 := err1.Error()
|
|
if errMsg1 == "" {
|
|
t.Errorf("expected non-empty error message")
|
|
}
|
|
|
|
// Test with cause
|
|
cause := errors.New("underlying error")
|
|
err2 := NewDatasourceUnreachableError("test-uid", "http://localhost:9090", cause)
|
|
errMsg2 := err2.Error()
|
|
if errMsg2 == "" {
|
|
t.Errorf("expected non-empty error message")
|
|
}
|
|
// Error message should include the cause
|
|
if !contains(errMsg2, "underlying error") {
|
|
t.Errorf("expected error message to include cause, got: %s", errMsg2)
|
|
}
|
|
}
|
|
|
|
// Helper function to check if a string contains a substring
|
|
func contains(s, substr string) bool {
|
|
return len(s) >= len(substr) && (s == substr || len(s) > len(substr) && containsHelper(s, substr))
|
|
}
|
|
|
|
func containsHelper(s, substr string) bool {
|
|
for i := 0; i <= len(s)-len(substr); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|