Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion internal/assertions/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,12 @@ func containsElement(list any, element any) (ok, found bool) {

// isList checks that the provided value is array or slice.
func isList(t T, list any, msgAndArgs ...any) (ok bool) {
kind := reflect.TypeOf(list).Kind()
listType := reflect.TypeOf(list)
if listType == nil {
return Fail(t, fmt.Sprintf("%q has an unsupported type <nil>, expecting array or slice", list),
msgAndArgs...)
}
kind := listType.Kind()
if kind != reflect.Array && kind != reflect.Slice {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s, expecting array or slice", list, kind),
msgAndArgs...)
Expand Down
12 changes: 10 additions & 2 deletions internal/assertions/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,11 @@ func Positive(t T, e any, msgAndArgs ...any) bool {
if h, ok := t.(H); ok {
h.Helper()
}
zero := reflect.Zero(reflect.TypeOf(e))
eType := reflect.TypeOf(e)
if eType == nil {
return Fail(t, fmt.Sprintf("\"%v\" is not positive", e), msgAndArgs...)
}
zero := reflect.Zero(eType)
failMessage := fmt.Sprintf("\"%v\" is not positive", e)
return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, failMessage, msgAndArgs...)
}
Expand Down Expand Up @@ -322,7 +326,11 @@ func Negative(t T, e any, msgAndArgs ...any) bool {
if h, ok := t.(H); ok {
h.Helper()
}
zero := reflect.Zero(reflect.TypeOf(e))
eType := reflect.TypeOf(e)
if eType == nil {
return Fail(t, fmt.Sprintf("\"%v\" is not negative", e), msgAndArgs...)
}
zero := reflect.Zero(eType)
failMessage := fmt.Sprintf("\"%v\" is not negative", e)
return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, failMessage, msgAndArgs...)
}
Expand Down
7 changes: 6 additions & 1 deletion internal/assertions/order.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package assertions

import (
"errors"
"fmt"
"reflect"
"slices"
Expand Down Expand Up @@ -320,7 +321,11 @@ func IsNonDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice
// It returns an error if the object can't be ordered.
// When not strictly ordered, it returns the first 2 offending values found.
func isStrictlyOrdered(object any, reverseOrder bool) ([]any, bool, error) {
objKind := reflect.TypeOf(object).Kind()
objType := reflect.TypeOf(object)
if objType == nil {
return nil, false, errors.New("object <nil> is not an ordered collection")
}
objKind := objType.Kind()
if objKind != reflect.Slice && objKind != reflect.Array {
return nil, false, fmt.Errorf("object %T is not an ordered collection", object)
}
Expand Down
12 changes: 10 additions & 2 deletions internal/assertions/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ func Implements(t T, interfaceObject any, object any, msgAndArgs ...any) bool {
if h, ok := t.(H); ok {
h.Helper()
}
interfaceType := reflect.TypeOf(interfaceObject).Elem()
ifType := reflect.TypeOf(interfaceObject)
if ifType == nil {
return Fail(t, "interfaceObject must be a pointer to an interface type", msgAndArgs...)
}
interfaceType := ifType.Elem()

if object == nil {
return Fail(t, fmt.Sprintf("Cannot check if nil implements %v", interfaceType), msgAndArgs...)
Expand All @@ -51,7 +55,11 @@ func NotImplements(t T, interfaceObject any, object any, msgAndArgs ...any) bool
if h, ok := t.(H); ok {
h.Helper()
}
interfaceType := reflect.TypeOf(interfaceObject).Elem()
ifType := reflect.TypeOf(interfaceObject)
if ifType == nil {
return Fail(t, "interfaceObject must be a pointer to an interface type", msgAndArgs...)
}
interfaceType := ifType.Elem()

if object == nil {
return Fail(t, fmt.Sprintf("Cannot check if nil does not implement %v", interfaceType), msgAndArgs...)
Expand Down
56 changes: 56 additions & 0 deletions internal/testintegration/assertions/assertions_fuzz_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers
// SPDX-License-Identifier: Apache-2.0

package assertions

import (
"testing"

"pgregory.net/rapid"

"github.com/go-openapi/testify/v2/internal/assertions"
)

// FuzzAssertionsNilSafety is the fuzzed equivalent of TestNilSafetyUnary
// and TestNilSafetyBinary.
//
// Given a high number of values of different types generated randomly,
// the fuzz engine will alter these values and run assertion functions.
//
// # Property
//
// No assertion function should ever panic, regardless of the inputs.
func FuzzAssertionsNilSafety(f *testing.F) {
prop := func(rt *rapid.T) {
value := genAny().Draw(rt, "value")
other := genAny().Draw(rt, "other")
mock := silentT{}

noPanic(rt, "Nil", func() { sink = assertions.Nil(mock, value) })
noPanic(rt, "NotNil", func() { sink = assertions.NotNil(mock, value) })
noPanic(rt, "Empty", func() { sink = assertions.Empty(mock, value) })
noPanic(rt, "NotEmpty", func() { sink = assertions.NotEmpty(mock, value) })
noPanic(rt, "Zero", func() { sink = assertions.Zero(mock, value) })
noPanic(rt, "NotZero", func() { sink = assertions.NotZero(mock, value) })
noPanic(rt, "Len", func() { sink = assertions.Len(mock, value, 0) })
noPanic(rt, "Equal", func() { sink = assertions.Equal(mock, value, other) })
noPanic(rt, "NotEqual", func() { sink = assertions.NotEqual(mock, value, other) })
noPanic(rt, "EqualValues", func() { sink = assertions.EqualValues(mock, value, other) })
noPanic(rt, "Contains", func() { sink = assertions.Contains(mock, value, other) })
noPanic(rt, "NotContains", func() { sink = assertions.NotContains(mock, value, other) })
noPanic(rt, "IsType", func() { sink = assertions.IsType(mock, value, other) })
noPanic(rt, "IsNotType", func() { sink = assertions.IsNotType(mock, value, other) })
}

f.Fuzz(rapid.MakeFuzz(prop))
}

// noPanic wraps a function call and reports a test error if it panics.
func noPanic(t *rapid.T, name string, fn func()) {
defer func() {
if r := recover(); r != nil {
t.Fatalf("%s panicked: %v", name, r)
}
}()
fn()
}
Loading
Loading