openmeter / pkg /models /validationissue_test.go
Leon4gr45's picture
Upload folder using huggingface_hub (part 10)
6380833 verified
Raw
History Blame Contribute Delete
11.3 kB
package models
import (
"encoding/json"
"errors"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestValidationIssue_JSON(t *testing.T) {
tests := []struct {
name string
issue ValidationIssue
expectedJSON string
}{
{
name: "empty",
issue: ValidationIssue{},
expectedJSON: `{"message":"", "severity":"critical"}`,
},
{
name: "bare",
issue: ValidationIssue{
message: "error message",
},
expectedJSON: `{"message":"error message", "severity":"critical"}`,
},
{
name: "generic",
issue: ValidationIssue{
message: "error message",
field: NewFieldSelectorGroup(NewFieldSelector("field_name")),
code: "invalid_param",
component: "openmeter",
severity: ErrorSeverityCritical,
},
expectedJSON: `{
"code":"invalid_param",
"component":"openmeter",
"message":"error message",
"field":"$.field_name",
"severity":"critical"
}`,
},
{
name: "with attributes",
issue: ValidationIssue{
message: "error message",
field: NewFieldSelectorGroup(NewFieldSelector("field_name").WithExpression(
NewFieldAttrValue("key", "value"),
)),
code: "invalid_param",
component: "openmeter",
severity: ErrorSeverityWarning,
attributes: Attributes{
"attr1": "value1",
},
},
expectedJSON: `{
"code":"invalid_param",
"component":"openmeter",
"message":"error message",
"field":"$.field_name[?(@.key=='value')]",
"severity":"warning",
"attr1": "value1"
}`,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
b, err := json.Marshal(test.issue)
require.NoErrorf(t, err, "marshaling ValidationIssue to JSON must not fail")
assert.JSONEqf(t, test.expectedJSON, string(b), "JSON representation must match")
})
}
}
var errTestValidationIssue = ValidationIssue{
code: "test_validation_issue",
message: "test validation issue",
field: NewFieldSelectorGroup(NewFieldSelector("field")),
severity: ErrorSeverityCritical,
}
func TestValidationIssue_WithAttrs(t *testing.T) {
tests := []struct {
name string
issue ValidationIssue
attrs Attributes
}{
{
name: "empty",
issue: errTestValidationIssue,
attrs: nil,
},
{
name: "with attributes",
issue: errTestValidationIssue,
attrs: Attributes{
"key1": "value1",
"key2": "value2",
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := test.issue.WithAttrs(test.attrs)
assert.Emptyf(t, errTestValidationIssue.Attributes(), "original attributes must be empty")
assert.Equalf(t, test.attrs, actual.Attributes(), "attributes must match")
})
}
}
func TestValidationIssue_WithField(t *testing.T) {
t.Run("Should UNSET field if nil is passed", func(t *testing.T) {
issue := errTestValidationIssue.Clone()
withField := issue.WithField()
assert.Equal(t, "", withField.Field().String(), "field must match")
assert.Equal(t, "", withField.Field().JSONPath(), "field path must match")
})
t.Run("Should override field if non-nil path is passed", func(t *testing.T) {
issue := errTestValidationIssue.Clone()
desc := NewFieldSelectorGroup(NewFieldSelector("field_name"))
withField := issue.WithField(desc)
assert.Equal(t, desc.String(), withField.Field().String(), "field must match")
assert.Equal(t, "$.field_name", withField.Field().JSONPath(), "field path must match")
})
}
func TestValidationIssue_Clone(t *testing.T) {
tests := []struct {
name string
issue ValidationIssue
expectedIssue ValidationIssue
}{
{
name: "clone",
issue: errTestValidationIssue,
expectedIssue: ValidationIssue{
attributes: errTestValidationIssue.attributes,
code: errTestValidationIssue.code,
component: errTestValidationIssue.component,
message: errTestValidationIssue.message,
field: errTestValidationIssue.field,
severity: errTestValidationIssue.severity,
wraps: errTestValidationIssue,
},
},
{
name: "empty",
issue: ValidationIssue{},
expectedIssue: ValidationIssue{wraps: ValidationIssue{}},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := test.issue.Clone()
assert.Equalf(t, test.expectedIssue, actual, "must match after clone")
})
}
}
func TestValidationIssue_UnwrapAndErrorsIs(t *testing.T) {
base := errTestValidationIssue
withField := base.WithField(NewFieldSelectorGroup(NewFieldSelector("field_name")))
withAttrs := withField.WithAttr("key", "value")
require.ErrorIs(t, withField, base)
require.ErrorIs(t, withAttrs, withField)
require.ErrorIs(t, withAttrs, base)
assert.Equal(t, base.Error(), withField.Error())
assert.Equal(t, withField.Error(), withAttrs.Error())
}
func TestValidationIssue_ErrorsIsWithJoin(t *testing.T) {
base := NewValidationError("joined_code", "joined message")
derived := base.WithPathString("field_name")
joined := errors.Join(errors.New("other error"), derived)
require.ErrorIs(t, joined, derived)
require.ErrorIs(t, joined, base)
}
func TestAsValidationIssues(t *testing.T) {
tests := []struct {
name string
err error
expectError bool
expectedIssues ValidationIssues
}{
{
name: "nil",
err: nil,
expectError: false,
expectedIssues: nil,
},
{
name: "error",
err: errors.New("error"),
expectError: true,
expectedIssues: nil,
},
{
name: "error/wrapped",
err: fmt.Errorf("test: %w", errors.New("error")),
expectError: true,
expectedIssues: nil,
},
{
name: "mixed",
err: fmt.Errorf("test: %w",
errors.Join(
errors.New("error 1"),
NewValidationError("errcode1", "error message 1"),
NewValidationWarning("errcode2", "error message 2"),
),
),
expectError: true,
expectedIssues: nil,
},
{
name: "mixed",
err: fmt.Errorf("test: %w",
errors.Join(
errors.New("error 1"),
NewValidationError("errcode1", "error message 1"),
NewValidationWarning("errcode2", "error message 2"),
),
),
expectError: true,
expectedIssues: nil,
},
{
name: "unknown wrapped",
err: fmt.Errorf("test: %w",
errors.Join(
ErrorWithComponent("component1",
errors.New("error 1"),
),
NewValidationError("errcode1", "error message 1"),
NewValidationWarning("errcode2", "error message 2"),
),
),
expectError: false,
expectedIssues: ValidationIssues{
ValidationIssue{
component: "component1",
message: "error 1",
severity: ErrorSeverityCritical,
},
ValidationIssue{
code: "errcode1",
message: "error message 1",
severity: ErrorSeverityCritical,
},
ValidationIssue{
code: "errcode2",
message: "error message 2",
severity: ErrorSeverityWarning,
},
},
},
{
name: "nested and wrapped with unknown",
err: fmt.Errorf("test: %w",
errors.Join(
ErrorWithComponent("component1",
errors.New("error 1"),
),
ErrorWithComponent("component2",
ErrorWithFieldPrefix(NewFieldSelectorGroup(NewFieldSelector("prefix")),
errors.Join(
NewValidationIssue("errcode1", "error message 1",
WithFieldString("field1"),
WithCriticalSeverity(),
),
NewValidationIssue("errcode2", "error message 2",
WithFieldString("field2"),
WithWarningSeverity(),
),
ErrorWithFieldPrefix(NewFieldSelectorGroup(NewFieldSelector("prefix2")),
ErrorWithComponent("component3",
errors.Join(
NewValidationIssue("errcode3", "error message 3",
WithFieldString("field3"),
WithCriticalSeverity(),
),
NewValidationIssue("errcode4", "error message 4",
WithField(
NewFieldSelector("prefix3"),
NewFieldSelector("field4"),
),
WithWarningSeverity(),
WithAttributes(Attributes{
"attr1": "value1",
}),
),
),
),
),
),
),
),
NewValidationIssue("errcode5", "error message 5",
WithField(NewFieldSelector("field5")),
WithCriticalSeverity(),
WithComponent("component4"),
),
),
),
expectError: false,
expectedIssues: ValidationIssues{
ValidationIssue{
component: "component1",
message: "error 1",
severity: ErrorSeverityCritical,
},
ValidationIssue{
code: "errcode1",
component: "component2",
message: "error message 1",
field: NewFieldSelectorGroup(
NewFieldSelector("prefix"),
NewFieldSelector("field1"),
),
severity: ErrorSeverityCritical,
},
ValidationIssue{
code: "errcode2",
component: "component2",
message: "error message 2",
field: NewFieldSelectorGroup(
NewFieldSelector("prefix"),
NewFieldSelector("field2"),
),
severity: ErrorSeverityWarning,
},
ValidationIssue{
code: "errcode3",
component: "component3",
message: "error message 3",
field: NewFieldSelectorGroup(
NewFieldSelector("prefix"),
NewFieldSelector("prefix2"),
NewFieldSelector("field3"),
),
severity: ErrorSeverityCritical,
},
ValidationIssue{
code: "errcode4",
component: "component3",
message: "error message 4",
field: NewFieldSelectorGroup(
NewFieldSelector("prefix"),
NewFieldSelector("prefix2"),
NewFieldSelector("prefix3"),
NewFieldSelector("field4"),
),
severity: ErrorSeverityWarning,
attributes: Attributes{
"attr1": "value1",
},
},
ValidationIssue{
code: "errcode5",
component: "component4",
message: "error message 5",
field: NewFieldSelectorGroup(
NewFieldSelector("field5"),
),
severity: ErrorSeverityCritical,
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual, err := AsValidationIssues(test.err)
if test.expectError {
require.EqualErrorf(t, err, test.err.Error(), "error must match")
} else {
require.NoError(t, err, "error must be nil")
}
RequireValidationIssuesMatch(t, test.expectedIssues, actual)
})
}
}
func TestValidationIssues_AsError(t *testing.T) {
issues := ValidationIssues{
{
severity: ErrorSeverityCritical,
message: "error1",
component: "component1",
field: NewFieldSelectorGroup(NewFieldSelector("field1")),
},
}
err := issues.AsError()
validationIssues, err := AsValidationIssues(err)
require.NoError(t, err)
RequireValidationIssuesMatch(t, issues, validationIssues)
}
func TestAsValidationIssues_PointerValidationIssue(t *testing.T) {
issue := NewValidationIssue("errcode1", "error message 1",
WithFieldString("field1"),
WithWarningSeverity(),
WithAttributes(Attributes{
"attr1": "value1",
}),
)
actual, err := AsValidationIssues(&issue)
require.NoError(t, err)
RequireValidationIssuesMatch(t, ValidationIssues{issue}, actual)
}