Remove ExpectInvalid()

All use cases are converted.

Co-Authored-by: Yongrui Lin <yongrlin@outlook.com>
This commit is contained in:
Tim Hockin
2025-08-02 18:34:06 -07:00
committed by yongruilin
parent 8b08c8e59c
commit 199c9ac77a
26 changed files with 218 additions and 312 deletions

View File

@@ -64,16 +64,16 @@ func Test(t *testing.T) {
st.Value(&structA).OldValue(&structA2).ExpectValid()
st.Value(&structA2).OldValue(&structA).ExpectValid()
st.Value(&structA).OldValue(&structB).ExpectInvalid(
field.Forbidden(field.NewPath("stringField"), "field is immutable"),
field.Forbidden(field.NewPath("stringPtrField"), "field is immutable"),
field.Forbidden(field.NewPath("structField"), "field is immutable"),
field.Forbidden(field.NewPath("structPtrField"), "field is immutable"),
field.Forbidden(field.NewPath("noncomparableStructField"), "field is immutable"),
field.Forbidden(field.NewPath("noncomparableStructPtrField"), "field is immutable"),
field.Forbidden(field.NewPath("sliceField"), "field is immutable"),
field.Forbidden(field.NewPath("mapField"), "field is immutable"),
field.Forbidden(field.NewPath("immutableField"), "field is immutable"),
field.Forbidden(field.NewPath("immutablePtrField"), "field is immutable"),
)
st.Value(&structA).OldValue(&structB).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Forbidden(field.NewPath("stringField"), "immutable"),
field.Forbidden(field.NewPath("stringPtrField"), "immutable"),
field.Forbidden(field.NewPath("structField"), "immutable"),
field.Forbidden(field.NewPath("structPtrField"), "immutable"),
field.Forbidden(field.NewPath("noncomparableStructField"), "immutable"),
field.Forbidden(field.NewPath("noncomparableStructPtrField"), "immutable"),
field.Forbidden(field.NewPath("sliceField"), "immutable"),
field.Forbidden(field.NewPath("mapField"), "immutable"),
field.Forbidden(field.NewPath("immutableField"), "immutable"),
field.Forbidden(field.NewPath("immutablePtrField"), "immutable"),
})
}

View File

@@ -41,15 +41,15 @@ func Test(t *testing.T) {
},
}
st.Value(new).OldValue(old).ExpectInvalid(
field.Forbidden(field.NewPath("listField").Index(0), "field is immutable"),
field.Forbidden(field.NewPath("listField").Index(1).Child("stringField"), "field is immutable"),
)
st.Value(new).OldValue(old).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Forbidden(field.NewPath("listField").Index(0), "immutable"),
field.Forbidden(field.NewPath("listField").Index(1).Child("stringField"), "immutable"),
})
st.Value(new).OldValue(&Struct{ListField: []Item{}}).ExpectInvalid(
field.Forbidden(field.NewPath("listField").Index(0), "field is immutable"),
field.Forbidden(field.NewPath("listField").Index(1).Child("stringField"), "field is immutable"),
)
st.Value(new).OldValue(&Struct{ListField: []Item{}}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Forbidden(field.NewPath("listField").Index(0), "immutable"),
field.Forbidden(field.NewPath("listField").Index(1).Child("stringField"), "immutable"),
})
// Test that "c" can change independently
st.Value(&Struct{

View File

@@ -44,9 +44,9 @@ func Test(t *testing.T) {
RatchetItems: []RatchetItem{
{Key: "ratchet", Status: "forbidden", Version: 1},
},
}).ExpectInvalid(
field.Invalid(field.NewPath("ratchetItems").Index(0).Child("status"), "forbidden", "must not be equal to \"forbidden\""),
)
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring(), field.ErrorList{
field.Invalid(field.NewPath("ratchetItems").Index(0).Child("status"), nil, ""),
})
st.Value(&Struct{
RatchetItems: []RatchetItem{
@@ -74,7 +74,7 @@ func Test(t *testing.T) {
RatchetItems: []RatchetItem{
{Key: "ratchet", Status: "allowed", Version: 1},
},
}).ExpectInvalid(
field.Invalid(field.NewPath("ratchetItems").Index(0).Child("status"), "forbidden", "must not be equal to \"forbidden\""),
)
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring(), field.ErrorList{
field.Invalid(field.NewPath("ratchetItems").Index(0).Child("status"), nil, ""),
})
}

View File

@@ -52,9 +52,9 @@ func Test(t *testing.T) {
{Key: "immutable", Data: "changed"},
},
}
st.Value(newStruct).OldValue(oldStruct).ExpectInvalid(
field.Forbidden(field.NewPath("typedefItems").Index(0), "field is immutable"),
)
st.Value(newStruct).OldValue(oldStruct).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Forbidden(field.NewPath("typedefItems").Index(0), "immutable"),
})
// Test nested typedef (typedef of typedef).
st.Value(&Struct{

View File

@@ -105,26 +105,26 @@ func TestUpdateCorrelation(t *testing.T) {
st.Value(&structA2).OldValue(&structA1).ExpectValid()
st.Value(&structA1).OldValue(&structB).ExpectInvalid(
field.Forbidden(field.NewPath("listField").Index(0), "field is immutable"),
field.Forbidden(field.NewPath("listField").Index(1), "field is immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(0), "field is immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(1), "field is immutable"),
field.Forbidden(field.NewPath("typedefField").Index(0), "field is immutable"),
field.Forbidden(field.NewPath("typedefField").Index(1), "field is immutable"),
)
st.Value(&structA1).OldValue(&structB).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Forbidden(field.NewPath("listField").Index(0), "immutable"),
field.Forbidden(field.NewPath("listField").Index(1), "immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(0), "immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(1), "immutable"),
field.Forbidden(field.NewPath("typedefField").Index(0), "immutable"),
field.Forbidden(field.NewPath("typedefField").Index(1), "immutable"),
})
st.Value(&structB).OldValue(&structA1).ExpectInvalid(
field.Forbidden(field.NewPath("listField").Index(0), "field is immutable"),
field.Forbidden(field.NewPath("listField").Index(1), "field is immutable"),
field.Forbidden(field.NewPath("listField").Index(2), "field is immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(0), "field is immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(1), "field is immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(2), "field is immutable"),
field.Forbidden(field.NewPath("typedefField").Index(0), "field is immutable"),
field.Forbidden(field.NewPath("typedefField").Index(1), "field is immutable"),
field.Forbidden(field.NewPath("typedefField").Index(2), "field is immutable"),
)
st.Value(&structB).OldValue(&structA1).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Forbidden(field.NewPath("listField").Index(0), "immutable"),
field.Forbidden(field.NewPath("listField").Index(1), "immutable"),
field.Forbidden(field.NewPath("listField").Index(2), "immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(0), "immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(1), "immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(2), "immutable"),
field.Forbidden(field.NewPath("typedefField").Index(0), "immutable"),
field.Forbidden(field.NewPath("typedefField").Index(1), "immutable"),
field.Forbidden(field.NewPath("typedefField").Index(2), "immutable"),
})
}
func TestRatcheting(t *testing.T) {

View File

@@ -106,26 +106,26 @@ func TestUpdateCorrelation(t *testing.T) {
st.Value(&structA2).OldValue(&structA1).ExpectValid()
st.Value(&structA1).OldValue(&structB).ExpectInvalid(
field.Forbidden(field.NewPath("listField").Index(0), "field is immutable"),
field.Forbidden(field.NewPath("listField").Index(1), "field is immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(0), "field is immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(1), "field is immutable"),
field.Forbidden(field.NewPath("typedefField").Index(0), "field is immutable"),
field.Forbidden(field.NewPath("typedefField").Index(1), "field is immutable"),
)
st.Value(&structA1).OldValue(&structB).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Forbidden(field.NewPath("listField").Index(0), "immutable"),
field.Forbidden(field.NewPath("listField").Index(1), "immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(0), "immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(1), "immutable"),
field.Forbidden(field.NewPath("typedefField").Index(0), "immutable"),
field.Forbidden(field.NewPath("typedefField").Index(1), "immutable"),
})
st.Value(&structB).OldValue(&structA1).ExpectInvalid(
field.Forbidden(field.NewPath("listField").Index(0), "field is immutable"),
field.Forbidden(field.NewPath("listField").Index(1), "field is immutable"),
field.Forbidden(field.NewPath("listField").Index(2), "field is immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(0), "field is immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(1), "field is immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(2), "field is immutable"),
field.Forbidden(field.NewPath("typedefField").Index(0), "field is immutable"),
field.Forbidden(field.NewPath("typedefField").Index(1), "field is immutable"),
field.Forbidden(field.NewPath("typedefField").Index(2), "field is immutable"),
)
st.Value(&structB).OldValue(&structA1).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Forbidden(field.NewPath("listField").Index(0), "immutable"),
field.Forbidden(field.NewPath("listField").Index(1), "immutable"),
field.Forbidden(field.NewPath("listField").Index(2), "immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(0), "immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(1), "immutable"),
field.Forbidden(field.NewPath("listTypedefField").Index(2), "immutable"),
field.Forbidden(field.NewPath("typedefField").Index(0), "immutable"),
field.Forbidden(field.NewPath("typedefField").Index(1), "immutable"),
field.Forbidden(field.NewPath("typedefField").Index(2), "immutable"),
})
}
func TestRatcheting(t *testing.T) {

View File

@@ -56,21 +56,21 @@ func Test(t *testing.T) {
{StringPtrField: ptrS1},
{StringPtrField: ptrS2},
},
}).ExpectInvalid(
field.Duplicate(field.NewPath("sliceStringField").Index(3), "ccc"),
field.Duplicate(field.NewPath("sliceStringField").Index(4), "bbb"),
field.Duplicate(field.NewPath("sliceStringField").Index(5), "aaa"),
field.Duplicate(field.NewPath("sliceIntField").Index(3), 3),
field.Duplicate(field.NewPath("sliceIntField").Index(4), 2),
field.Duplicate(field.NewPath("sliceIntField").Index(5), 1),
field.Duplicate(field.NewPath("sliceComparableField").Index(3), ComparableStruct{"ccc"}),
field.Duplicate(field.NewPath("sliceComparableField").Index(4), ComparableStruct{"bbb"}),
field.Duplicate(field.NewPath("sliceComparableField").Index(5), ComparableStruct{"aaa"}),
field.Duplicate(field.NewPath("sliceNonComparableField").Index(3), NonComparableStruct{[]string{"ccc", "333"}}),
field.Duplicate(field.NewPath("sliceNonComparableField").Index(4), NonComparableStruct{[]string{"bbb", "222"}}),
field.Duplicate(field.NewPath("sliceNonComparableField").Index(5), NonComparableStruct{[]string{"aaa", "111"}}),
field.Duplicate(field.NewPath("sliceFalselyComparableField").Index(1), FalselyComparableStruct{StringPtrField: ptrS2}),
)
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Duplicate(field.NewPath("sliceStringField").Index(3), nil),
field.Duplicate(field.NewPath("sliceStringField").Index(4), nil),
field.Duplicate(field.NewPath("sliceStringField").Index(5), nil),
field.Duplicate(field.NewPath("sliceIntField").Index(3), nil),
field.Duplicate(field.NewPath("sliceIntField").Index(4), nil),
field.Duplicate(field.NewPath("sliceIntField").Index(5), nil),
field.Duplicate(field.NewPath("sliceComparableField").Index(3), nil),
field.Duplicate(field.NewPath("sliceComparableField").Index(4), nil),
field.Duplicate(field.NewPath("sliceComparableField").Index(5), nil),
field.Duplicate(field.NewPath("sliceNonComparableField").Index(3), nil),
field.Duplicate(field.NewPath("sliceNonComparableField").Index(4), nil),
field.Duplicate(field.NewPath("sliceNonComparableField").Index(5), nil),
field.Duplicate(field.NewPath("sliceFalselyComparableField").Index(1), nil),
})
}
func TestSetCorrelation(t *testing.T) {

View File

@@ -53,9 +53,7 @@ type Struct struct {
// +k8s:minimum=1
Uint64Field uint64 `json:"uint64Field"`
// +k8s:minimum=1
TypedefField IntType `json:"typedefField"`
// +k8s:minimum=1
TypedefField IntType `json:"typedefField"`
TypedefPtrField *IntType `json:"typedefPtrField"`
}

View File

@@ -19,7 +19,6 @@ package minimum
import (
"testing"
"k8s.io/apimachinery/pkg/api/validate/content"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/utils/ptr"
)
@@ -32,20 +31,20 @@ func Test(t *testing.T) {
IntPtrField: ptr.To(0),
UintPtrField: ptr.To(uint(0)),
TypedefPtrField: ptr.To(IntType(0)),
}).ExpectInvalid(
field.Invalid(field.NewPath("intField"), 0, content.MinError(1)),
field.Invalid(field.NewPath("intPtrField"), 0, content.MinError(1)),
field.Invalid(field.NewPath("int16Field"), 0, content.MinError(1)),
field.Invalid(field.NewPath("int32Field"), 0, content.MinError(1)),
field.Invalid(field.NewPath("int64Field"), 0, content.MinError(1)),
field.Invalid(field.NewPath("uintField"), uint(0), content.MinError(1)),
field.Invalid(field.NewPath("uintPtrField"), uint(0), content.MinError(1)),
field.Invalid(field.NewPath("uint16Field"), uint(0), content.MinError(1)),
field.Invalid(field.NewPath("uint32Field"), uint(0), content.MinError(1)),
field.Invalid(field.NewPath("uint64Field"), uint(0), content.MinError(1)),
field.Invalid(field.NewPath("typedefField"), 0, content.MinError(1)),
field.Invalid(field.NewPath("typedefPtrField"), 0, content.MinError(1)),
)
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring(), field.ErrorList{
field.Invalid(field.NewPath("intField"), nil, ""),
field.Invalid(field.NewPath("intPtrField"), nil, ""),
field.Invalid(field.NewPath("int16Field"), nil, ""),
field.Invalid(field.NewPath("int32Field"), nil, ""),
field.Invalid(field.NewPath("int64Field"), nil, ""),
field.Invalid(field.NewPath("uintField"), nil, ""),
field.Invalid(field.NewPath("uintPtrField"), nil, ""),
field.Invalid(field.NewPath("uint16Field"), nil, ""),
field.Invalid(field.NewPath("uint32Field"), nil, ""),
field.Invalid(field.NewPath("uint64Field"), nil, ""),
field.Invalid(field.NewPath("typedefField"), nil, ""),
field.Invalid(field.NewPath("typedefPtrField"), nil, ""),
})
// Test validation ratcheting
st.Value(&Struct{
IntPtrField: ptr.To(0),

View File

@@ -188,8 +188,6 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
return nil
}
// call field-attached validations
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
// call the type's validation function
errs = append(errs, Validate_IntType(ctx, op, fldPath, obj, oldObj)...)
return
@@ -202,8 +200,6 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
return nil
}
// call field-attached validations
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
// call the type's validation function
errs = append(errs, Validate_IntType(ctx, op, fldPath, obj, oldObj)...)
return

View File

@@ -27,13 +27,13 @@ func TestRegisterValidations(t *testing.T) {
st := localSchemeBuilder.Test(t)
t1 := &T1{}
st.Value(t1).ExpectInvalid(
field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", t1, "/")),
)
st.Value(t1).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.InternalError(nil, fmt.Errorf("")),
})
st.Value(t1).Subresources([]string{"scale"}).ExpectValid()
st.Value(t1).Subresources([]string{"unknown"}).ExpectInvalid(
field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", t1, "/unknown")),
)
st.Value(t1).Subresources([]string{"unknown"}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.InternalError(nil, fmt.Errorf("")),
})
}

View File

@@ -30,11 +30,11 @@ func TestRegisterValidations(t *testing.T) {
st.Value(t1).ExpectValid()
st.Value(t1).Subresources([]string{"scale"}).ExpectInvalid(
field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", t1, "/scale")),
)
st.Value(t1).Subresources([]string{"scale"}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.InternalError(nil, fmt.Errorf("")),
})
st.Value(t1).Subresources([]string{"x", "y"}).ExpectInvalid(
field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", t1, "/x/y")),
)
st.Value(t1).Subresources([]string{"x", "y"}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.InternalError(nil, fmt.Errorf("")),
})
}

View File

@@ -34,17 +34,17 @@ func TestRegisterValidations(t *testing.T) {
st.Value(t1).Subresources([]string{"scale"}).ExpectValid()
st.Value(t1).Subresources([]string{"x", "y"}).ExpectValid()
st.Value(t1).Subresources([]string{"status", "unknown"}).ExpectInvalid(
field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", t1, "/status/unknown")),
)
st.Value(t1).Subresources([]string{"status", "unknown"}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.InternalError(nil, fmt.Errorf("")),
})
st.Value(t1).Subresources([]string{"unknown"}).ExpectInvalid(
field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", t1, "/unknown")),
)
st.Value(t1).Subresources([]string{"x", "unknown"}).ExpectInvalid(
field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", t1, "/x/unknown")),
)
st.Value(t1).Subresources([]string{"x", "y", "unknown"}).ExpectInvalid(
field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", t1, "/x/y/unknown")),
)
st.Value(t1).Subresources([]string{"unknown"}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.InternalError(nil, fmt.Errorf("")),
})
st.Value(t1).Subresources([]string{"x", "unknown"}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.InternalError(nil, fmt.Errorf("")),
})
st.Value(t1).Subresources([]string{"x", "y", "unknown"}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.InternalError(nil, fmt.Errorf("")),
})
}

View File

@@ -28,13 +28,13 @@ func Test(t *testing.T) {
st.Value(&Struct{D: DM1, M1: &M1{}}).ExpectValid()
st.Value(&Struct{D: DM2, M2: &M2{}}).ExpectValid()
st.Value(&Struct{D: DM2, M1: &M1{}, M2: &M2{}}).ExpectInvalid(
field.Invalid(field.NewPath("m1"), "", "may only be specified when `d` is \"CustomM1\""),
)
st.Value(&Struct{D: DM2, M1: &M1{}, M2: &M2{}}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(field.NewPath("m1"), nil, "may only be specified when"),
})
st.Value(&Struct{D: DM1}).ExpectInvalid(
field.Invalid(field.NewPath("m1"), "", "must be specified when `d` is \"CustomM1\""),
)
st.Value(&Struct{D: DM1}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(field.NewPath("m1"), nil, "must be specified when"),
})
// Test validation ratcheting
st.Value(&Struct{D: DM2, M1: &M1{}, M2: &M2{}}).OldValue(&Struct{D: DM2, M1: &M1{}, M2: &M2{}}).ExpectValid()

View File

@@ -40,26 +40,26 @@ func Test(t *testing.T) {
st.Value(&Struct{
D1: U1M2, U1M1: &M1{}, U1M2: &M2{},
D2: U2M2, // no value
}).ExpectInvalid(
field.Invalid(field.NewPath("u1m1"), "", "may only be specified when `d1` is \"U1M1\""),
field.Invalid(field.NewPath("u2m2"), "", "must be specified when `d2` is \"U2M2\""),
)
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(field.NewPath("u1m1"), nil, "may only be specified when"),
field.Invalid(field.NewPath("u2m2"), nil, "must be specified when"),
})
st.Value(&Struct{
D1: U1M2, // no value
D2: U2M2, U2M1: &M1{}, U2M2: &M2{},
}).ExpectInvalid(
field.Invalid(field.NewPath("u1m2"), "", "must be specified when `d1` is \"U1M2\""),
field.Invalid(field.NewPath("u2m1"), "", "may only be specified when `d2` is \"U2M1\""),
)
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(field.NewPath("u1m2"), nil, "must be specified when"),
field.Invalid(field.NewPath("u2m1"), nil, "may only be specified when"),
})
st.Value(&Struct{
D1: U1M2, // no value
D2: U2M2, // no value
}).ExpectInvalid(
field.Invalid(field.NewPath("u1m2"), "", "must be specified when `d1` is \"U1M2\""),
field.Invalid(field.NewPath("u2m2"), "", "must be specified when `d2` is \"U2M2\""),
)
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(field.NewPath("u1m2"), nil, "must be specified when"),
field.Invalid(field.NewPath("u2m2"), nil, "must be specified when"),
})
// Test validation ratcheting
st.Value(&Struct{

View File

@@ -30,13 +30,13 @@ func Test(t *testing.T) {
st.Value(&Struct{D: DM1, M1: &M1{}}).ExpectValid()
st.Value(&Struct{D: DM2, M2: &M2{}}).ExpectValid()
st.Value(&Struct{D: DM2, M1: &M1{}, M2: &M2{}}).ExpectInvalid(
field.Invalid(field.NewPath("m1"), "", "may only be specified when `d` is \"M1\""),
)
st.Value(&Struct{D: DM2, M1: &M1{}, M2: &M2{}}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(field.NewPath("m1"), nil, "may only be specified when"),
})
st.Value(&Struct{D: DM1}).ExpectInvalid(
field.Invalid(field.NewPath("m1"), "", "must be specified when `d` is \"M1\""),
)
st.Value(&Struct{D: DM1}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(field.NewPath("m1"), nil, "must be specified when"),
})
// Test validation ratcheting
st.Value(&Struct{D: DM2, M1: &M1{}, M2: &M2{}}).OldValue(&Struct{D: DM2, M1: &M1{}, M2: &M2{}}).ExpectValid()

View File

@@ -30,13 +30,13 @@ func Test(t *testing.T) {
st.Value(&Struct{D: DM1, M1: &M1{}}).ExpectValid()
st.Value(&Struct{D: DM2}).ExpectValid()
st.Value(&Struct{D: DM2, M1: &M1{}}).ExpectInvalid(
field.Invalid(field.NewPath("m1"), "", "may only be specified when `d` is \"M1\""),
)
st.Value(&Struct{D: DM2, M1: &M1{}}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(field.NewPath("m1"), nil, "may only be specified when"),
})
st.Value(&Struct{D: DM1}).ExpectInvalid(
field.Invalid(field.NewPath("m1"), "", "must be specified when `d` is \"M1\""),
)
st.Value(&Struct{D: DM1}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(field.NewPath("m1"), nil, "must be specified when"),
})
// Test validation ratcheting
st.Value(&Struct{D: DM2, M1: &M1{}}).OldValue(&Struct{D: DM2, M1: &M1{}}).ExpectValid()

View File

@@ -28,12 +28,12 @@ func Test(t *testing.T) {
st.Value(&Struct{M1: &M1{}}).ExpectValid()
st.Value(&Struct{M2: &M2{}}).ExpectValid()
st.Value(&Struct{M1: &M1{}, M2: &M2{}}).ExpectInvalid(
field.Invalid(nil, "{m1, m2}", "must specify exactly one of: `m1`, `m2`"),
)
st.Value(&Struct{}).ExpectInvalid(
field.Invalid(nil, "", "must specify one of: `m1`, `m2`"),
)
st.Value(&Struct{M1: &M1{}, M2: &M2{}}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify exactly one of"),
})
st.Value(&Struct{}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify one of"),
})
// Test validation ratcheting
st.Value(&Struct{M1: &M1{}, M2: &M2{}}).OldValue(&Struct{M1: &M1{}, M2: &M2{}}).ExpectValid()

View File

@@ -25,31 +25,31 @@ import (
func Test(t *testing.T) {
st := localSchemeBuilder.Test(t)
st.Value(&Struct{}).ExpectInvalid(
field.Invalid(nil, "", "must specify one of: `u1m1`, `u1m2`"),
field.Invalid(nil, "", "must specify one of: `u2m1`, `u2m2`"),
)
st.Value(&Struct{}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify one of"),
field.Invalid(nil, nil, "must specify one of"),
})
st.Value(&Struct{U1M1: &M1{}, U2M1: &M1{}}).ExpectValid()
st.Value(&Struct{U1M2: &M2{}, U2M2: &M2{}}).ExpectValid()
st.Value(&Struct{U1M1: &M1{}, U1M2: &M2{}}).ExpectInvalid(
field.Invalid(nil, "{u1m1, u1m2}", "must specify exactly one of: `u1m1`, `u1m2`"),
field.Invalid(nil, "", "must specify one of: `u2m1`, `u2m2`"),
)
st.Value(&Struct{U1M1: &M1{}, U1M2: &M2{}}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify exactly one of"),
field.Invalid(nil, nil, "must specify one of"),
})
st.Value(&Struct{U2M1: &M1{}, U2M2: &M2{}}).ExpectInvalid(
field.Invalid(nil, "", "must specify one of: `u1m1`, `u1m2`"),
field.Invalid(nil, "{u2m1, u2m2}", "must specify exactly one of: `u2m1`, `u2m2`"),
)
st.Value(&Struct{U2M1: &M1{}, U2M2: &M2{}}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify one of"),
field.Invalid(nil, nil, "must specify exactly one of"),
})
st.Value(&Struct{
U1M1: &M1{}, U1M2: &M2{},
U2M1: &M1{}, U2M2: &M2{},
}).ExpectInvalid(
field.Invalid(nil, "{u1m1, u1m2}", "must specify exactly one of: `u1m1`, `u1m2`"),
field.Invalid(nil, "{u2m1, u2m2}", "must specify exactly one of: `u2m1`, `u2m2`"),
)
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify exactly one of"),
field.Invalid(nil, nil, "must specify exactly one of"),
})
// Test validation ratcheting
st.Value(&Struct{}).OldValue(&Struct{}).ExpectValid()

View File

@@ -26,24 +26,24 @@ import (
func Test(t *testing.T) {
st := localSchemeBuilder.Test(t)
st.Value(&Struct{}).ExpectInvalid(
field.Invalid(nil, "", "must specify one of: `m1`, `m2`, `m3`, `m4`"),
)
st.Value(&Struct{}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify one of"),
})
st.Value(&Struct{M1: &M1{}}).ExpectValid()
st.Value(&Struct{M2: &M2{}}).ExpectValid()
st.Value(&Struct{M3: "a string"}).ExpectValid()
st.Value(&Struct{M4: ptr.To("a string")}).ExpectValid()
st.Value(&Struct{M1: &M1{}, M2: &M2{}}).ExpectInvalid(
field.Invalid(nil, "{m1, m2}", "must specify exactly one of: `m1`, `m2`, `m3`, `m4`"),
)
st.Value(&Struct{M1: &M1{}, M3: "a string"}).ExpectInvalid(
field.Invalid(nil, "{m1, m3}", "must specify exactly one of: `m1`, `m2`, `m3`, `m4`"),
)
st.Value(&Struct{M1: &M1{}, M4: ptr.To("a string")}).ExpectInvalid(
field.Invalid(nil, "{m1, m4}", "must specify exactly one of: `m1`, `m2`, `m3`, `m4`"),
)
st.Value(&Struct{M1: &M1{}, M2: &M2{}}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify exactly one of"),
})
st.Value(&Struct{M1: &M1{}, M3: "a string"}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify exactly one of"),
})
st.Value(&Struct{M1: &M1{}, M4: ptr.To("a string")}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify exactly one of"),
})
// Update only considers whether a field was set, not the value.
st.Value(&Struct{M3: "a string"}).OldValue(&Struct{M3: "different string"}).ExpectValid()

View File

@@ -31,9 +31,9 @@ func Test(t *testing.T) {
st.Value(&Struct{M1: &M1{}}).ExpectValid()
st.Value(&Struct{M2: &M2{}}).ExpectValid()
st.Value(&Struct{M1: &M1{}, M2: &M2{}}).ExpectInvalid(
field.Invalid(nil, "{m1, m2}", "must specify at most one of: `m1`, `m2`"),
)
st.Value(&Struct{M1: &M1{}, M2: &M2{}}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring(), field.ErrorList{
field.Invalid(nil, nil, "must specify at most one of"),
})
// Test validation ratcheting
st.Value(&Struct{M1: &M1{}, M2: &M2{}}).OldValue(&Struct{M1: &M1{}, M2: &M2{}}).ExpectValid()

View File

@@ -37,21 +37,20 @@ func Test(t *testing.T) {
st.Value(&Struct{U2M2: &M2{}}).ExpectValid()
// Multiple members in one union
st.Value(&Struct{U1M1: &M1{}, U1M2: &M2{}}).ExpectInvalid(
field.Invalid(nil, "{u1m1, u1m2}", "must specify at most one of: `u1m1`, `u1m2`"),
)
st.Value(&Struct{U1M1: &M1{}, U1M2: &M2{}}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify at most one of").WithOrigin("zeroOrOneOf"),
})
st.Value(&Struct{U2M1: &M1{}, U2M2: &M2{}}).ExpectInvalid(
field.Invalid(nil, "{u2m1, u2m2}", "must specify at most one of: `u2m1`, `u2m2`"),
)
st.Value(&Struct{U2M1: &M1{}, U2M2: &M2{}}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify at most one of").WithOrigin("zeroOrOneOf"),
})
st.Value(&Struct{
U1M1: &M1{}, U1M2: &M2{},
U2M1: &M1{}, U2M2: &M2{},
}).ExpectInvalid(
field.Invalid(nil, "{u1m1, u1m2}", "must specify at most one of: `u1m1`, `u1m2`"),
field.Invalid(nil, "{u2m1, u2m2}", "must specify at most one of: `u2m1`, `u2m2`"),
)
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify at most one of").WithOrigin("zeroOrOneOf"),
})
// Test validation ratcheting
st.Value(&Struct{}).OldValue(&Struct{}).ExpectValid()

View File

@@ -34,15 +34,15 @@ func Test(t *testing.T) {
st.Value(&Struct{M3: "a string"}).ExpectValid()
st.Value(&Struct{M4: ptr.To("a string")}).ExpectValid()
st.Value(&Struct{M1: &M1{}, M2: &M2{}}).ExpectInvalid(
field.Invalid(nil, "{m1, m2}", "must specify at most one of: `m1`, `m2`, `m3`, `m4`"),
)
st.Value(&Struct{M1: &M1{}, M3: "a string"}).ExpectInvalid(
field.Invalid(nil, "{m1, m3}", "must specify at most one of: `m1`, `m2`, `m3`, `m4`"),
)
st.Value(&Struct{M1: &M1{}, M4: ptr.To("a string")}).ExpectInvalid(
field.Invalid(nil, "{m1, m4}", "must specify at most one of: `m1`, `m2`, `m3`, `m4`"),
)
st.Value(&Struct{M1: &M1{}, M2: &M2{}}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify at most one of").WithOrigin("zeroOrOneOf"),
})
st.Value(&Struct{M1: &M1{}, M3: "a string"}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify at most one of").WithOrigin("zeroOrOneOf"),
})
st.Value(&Struct{M1: &M1{}, M4: ptr.To("a string")}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Invalid(nil, nil, "must specify at most one of").WithOrigin("zeroOrOneOf"),
})
// Update only considers whether a field was set, not the value.
st.Value(&Struct{M3: "a string"}).OldValue(&Struct{M3: "different string"}).ExpectValid()

View File

@@ -52,10 +52,10 @@ func Test(t *testing.T) {
st.Value(&structA1).OldValue(&structA2).ExpectValid()
st.Value(&structA1).OldValue(&structB).ExpectInvalid(
field.Forbidden(field.NewPath("sp"), "field is immutable"),
field.Forbidden(field.NewPath("ip"), "field is immutable"),
field.Forbidden(field.NewPath("bp"), "field is immutable"),
field.Forbidden(field.NewPath("fp"), "field is immutable"),
)
st.Value(&structA1).OldValue(&structB).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Forbidden(field.NewPath("sp"), ""),
field.Forbidden(field.NewPath("ip"), ""),
field.Forbidden(field.NewPath("bp"), ""),
field.Forbidden(field.NewPath("fp"), ""),
})
}

View File

@@ -42,10 +42,10 @@ func Test(t *testing.T) {
st.Value(&structA).OldValue(&structA).ExpectValid()
st.Value(&structA).OldValue(&structB).ExpectInvalid(
field.Forbidden(field.NewPath("s"), "field is immutable"),
field.Forbidden(field.NewPath("i"), "field is immutable"),
field.Forbidden(field.NewPath("b"), "field is immutable"),
field.Forbidden(field.NewPath("f"), "field is immutable"),
)
st.Value(&structA).OldValue(&structB).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring().ByOrigin(), field.ErrorList{
field.Forbidden(field.NewPath("s"), ""),
field.Forbidden(field.NewPath("i"), ""),
field.Forbidden(field.NewPath("b"), ""),
field.Forbidden(field.NewPath("f"), ""),
})
}

View File

@@ -310,14 +310,6 @@ func (v *ValidationTester) ExpectValid() *ValidationTester {
return v
}
// ExpectInvalid validates the value and calls t.Errorf if want does not match the actual errors.
// Returns ValidationTester to support call chaining.
func (v *ValidationTester) ExpectInvalid(want ...*field.Error) *ValidationTester {
v.T.Helper()
return v.expectInvalid(byFullError, want...)
}
func (v *ValidationTester) ExpectValidateFalseByPath(expectedByPath map[string][]string) *ValidationTester {
v.T.Helper()
@@ -368,84 +360,6 @@ func (v *ValidationTester) ExpectMatches(matcher field.ErrorMatcher, expected fi
return v
}
func (v *ValidationTester) getErrorsByPath() map[string][]string {
byPath := map[string][]string{}
errs := v.validate()
for _, e := range errs {
f := e.Field
if f == "<nil>" {
f = ""
}
byPath[f] = append(byPath[f], e.ErrorBody())
}
// ensure args are sorted
for _, args := range byPath {
sort.Strings(args)
}
return byPath
}
func renderByPath(byPath map[string][]string) string {
keys := []string{}
for key := range byPath {
keys = append(keys, key)
}
sort.Strings(keys)
for _, vals := range byPath {
sort.Strings(vals)
}
buf := strings.Builder{}
for _, key := range keys {
vals := byPath[key]
for _, val := range vals {
buf.WriteString(fmt.Sprintf("\t%s: %q\n", key, val))
}
}
return buf.String()
}
func renderList(list []string) string {
buf := strings.Builder{}
for _, item := range list {
buf.WriteString(fmt.Sprintf("\t%q\n", item))
}
return buf.String()
}
func (v *ValidationTester) expectInvalid(matcher matcher, errs ...*field.Error) *ValidationTester {
v.T.Helper()
v.T.Run(fmt.Sprintf("%T", v.value), func(t *testing.T) {
t.Helper()
want := sets.New[string]()
for _, e := range errs {
want.Insert(matcher(e))
}
got := sets.New[string]()
for _, e := range v.validate() {
got.Insert(matcher(e))
}
if !got.Equal(want) {
t.Errorf("validation errors differed from expected:\n%v\n", cmp.Diff(want, got, cmpopts.SortMaps(stdcmp.Less[string])))
for x := range got.Difference(want) {
fmt.Printf("%q,\n", strings.TrimPrefix(x, "forced failure: "))
}
}
})
return v
}
type matcher func(err *field.Error) string
func byFullError(err *field.Error) string {
return err.Error()
}
func (v *ValidationTester) validate() field.ErrorList {
var errs field.ErrorList
if v.oldValue == nil {