Repository: getkin/kin-openapi
Pull request #658 (2022-11-07 11:50:42)

Fix lost error types in oneOf
Now we can get error types when using oneOf #657 


Patch:
diff --git a/openapi3/errors.go b/openapi3/errors.go
index b530101..74baab9 100644
--- a/openapi3/errors.go
+++ b/openapi3/errors.go
@@ -10,11 +10,15 @@ import (
 type MultiError []error
 
 func (me MultiError) Error() string {
+	return spliceErr(" | ", me)
+}
+
+func spliceErr(sep string, errs []error) string {
 	buff := &bytes.Buffer{}
-	for i, e := range me {
+	for i, e := range errs {
 		buff.WriteString(e.Error())
-		if i != len(me)-1 {
-			buff.WriteString(" | ")
+		if i != len(errs)-1 {
+			buff.WriteString(sep)
 		}
 	}
 	return buff.String()
@@ -43,3 +47,13 @@ func (me MultiError) As(target interface{}) bool {
 	}
 	return false
 }
+
+type multiErrorForOneOf MultiError
+
+func (meo multiErrorForOneOf) Error() string {
+	return spliceErr(" Or ", meo)
+}
+
+func (meo multiErrorForOneOf) Unwrap() error {
+	return MultiError(meo)
+}
diff --git a/openapi3/schema.go b/openapi3/schema.go
index ba846d0..e5cded8 100644
--- a/openapi3/schema.go
+++ b/openapi3/schema.go
@@ -926,7 +926,7 @@ func (schema *Schema) visitSetOperations(settings *schemaValidationSettings, val
 
 		var (
 			ok               = 0
-			validationErrors = []error{}
+			validationErrors = multiErrorForOneOf{}
 			matchedOneOfIdx  = 0
 			tempValue        = value
 		)
@@ -955,14 +955,7 @@ func (schema *Schema) visitSetOperations(settings *schemaValidationSettings, val
 
 		if ok != 1 {
 			if len(validationErrors) > 1 {
-				errorMessage := ""
-				for _, err := range validationErrors {
-					if errorMessage != "" {
-						errorMessage += " Or "
-					}
-					errorMessage += err.Error()
-				}
-				return errors.New("doesn't match schema due to: " + errorMessage)
+				return fmt.Errorf("doesn't match schema due to: %w", validationErrors)
 			}
 			if settings.failfast {
 				return errSchema


Test patch:
diff --git a/openapi3/issue657_test.go b/openapi3/issue657_test.go
new file mode 100644
index 0000000..195ccd1
--- /dev/null
+++ b/openapi3/issue657_test.go
@@ -0,0 +1,79 @@
+package openapi3_test
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/getkin/kin-openapi/openapi3"
+)
+
+func TestOneOf_Warning_Errors(t *testing.T) {
+	t.Parallel()
+
+	loader := openapi3.NewLoader()
+	spec := `
+components:
+  schemas:
+    Something:
+      type: object
+      properties:
+        field:
+          title: Some field
+          oneOf:
+            - title: First rule
+              type: string
+              minLength: 10
+              maxLength: 10
+            - title: Second rule
+              type: string
+              minLength: 15
+              maxLength: 15
+`[1:]
+
+	doc, err := loader.LoadFromData([]byte(spec))
+	require.NoError(t, err)
+
+	tests := [...]struct {
+		name     string
+		value    string
+		checkErr require.ErrorAssertionFunc
+	}{
+		{
+			name:     "valid value",
+			value:    "ABCDE01234",
+			checkErr: require.NoError,
+		},
+		{
+			name:     "valid value",
+			value:    "ABCDE0123456789",
+			checkErr: require.NoError,
+		},
+		{
+			name:  "no valid value",
+			value: "ABCDE",
+			checkErr: func(t require.TestingT, err error, i ...interface{}) {
+				require.Equal(t, "doesn't match schema due to: minimum string length is 10\nSchema:\n  {\n    \"maxLength\": 10,\n    \"minLength\": 10,\n    \"title\": \"First rule\",\n    \"type\": \"string\"\n  }\n\nValue:\n  \"ABCDE\"\n Or minimum string length is 15\nSchema:\n  {\n    \"maxLength\": 15,\n    \"minLength\": 15,\n    \"title\": \"Second rule\",\n    \"type\": \"string\"\n  }\n\nValue:\n  \"ABCDE\"\n", err.Error())
+
+				wErr := &openapi3.MultiError{}
+				require.ErrorAs(t, err, wErr)
+
+				require.Len(t, *wErr, 2)
+
+				require.Equal(t, "minimum string length is 10", (*wErr)[0].(*openapi3.SchemaError).Reason)
+				require.Equal(t, "minimum string length is 15", (*wErr)[1].(*openapi3.SchemaError).Reason)
+			},
+		},
+	}
+
+	for _, test := range tests {
+		test := test
+		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
+
+			err = doc.Components.Schemas["Something"].Value.Properties["field"].Value.VisitJSON(test.value)
+
+			test.checkErr(t, err)
+		})
+	}
+}