...

Source file src/github.com/go-openapi/validate/jsonschema_test.go

Documentation: github.com/go-openapi/validate

     1  // Copyright 2015 go-swagger maintainers
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package validate
    16  
    17  import (
    18  	"encoding/json"
    19  	"net/http"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/go-openapi/spec"
    27  	"github.com/go-openapi/strfmt"
    28  	"github.com/go-openapi/swag"
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  // Data structure for jsonschema-suite fixtures
    34  type schemaTestT struct {
    35  	Description string       `json:"description"`
    36  	Schema      *spec.Schema `json:"schema"`
    37  	Tests       []struct {
    38  		Description string      `json:"description"`
    39  		Data        interface{} `json:"data"`
    40  		Valid       bool        `json:"valid"`
    41  	}
    42  }
    43  
    44  var jsonSchemaFixturesPath = filepath.Join("fixtures", "jsonschema_suite")
    45  var schemaFixturesPath = filepath.Join("fixtures", "schemas")
    46  var formatFixturesPath = filepath.Join("fixtures", "formats")
    47  
    48  func enabled() []string {
    49  	// Standard fixtures from JSON schema suite
    50  	return []string{
    51  		"minLength",
    52  		"maxLength",
    53  		"pattern",
    54  		"type",
    55  		"minimum",
    56  		"maximum",
    57  		"multipleOf",
    58  		"enum",
    59  		"default",
    60  		"dependencies",
    61  		"items",
    62  		"maxItems",
    63  		"maxProperties",
    64  		"minItems",
    65  		"minProperties",
    66  		"patternProperties",
    67  		"required",
    68  		"additionalItems",
    69  		"uniqueItems",
    70  		"properties",
    71  		"additionalProperties",
    72  		"allOf",
    73  		"not",
    74  		"oneOf",
    75  		"anyOf",
    76  		"ref",
    77  		"definitions",
    78  		"refRemote",
    79  		"format",
    80  	}
    81  }
    82  
    83  var optionalFixtures = []string{
    84  	// Optional fixtures from JSON schema suite: at the moment, these are disabled
    85  	// "zeroTerminatedFloats",
    86  	// "format",	/* error on strict URI formatting */
    87  	// "bignum",
    88  	// "ecmascript-regex",
    89  }
    90  
    91  var extendedFixtures = []string{
    92  	"extended-format",
    93  }
    94  
    95  func isEnabled(nm string) bool {
    96  	return swag.ContainsStringsCI(enabled(), nm)
    97  }
    98  
    99  func isOptionalEnabled(nm string) bool {
   100  	return swag.ContainsStringsCI(optionalFixtures, nm)
   101  }
   102  
   103  func isExtendedEnabled(nm string) bool {
   104  	return swag.ContainsStringsCI(extendedFixtures, nm)
   105  }
   106  
   107  func TestJSONSchemaSuite(t *testing.T) {
   108  	// Internal local server to serve remote $ref
   109  	go func() {
   110  		svr := &http.Server{
   111  			ReadTimeout:  5 * time.Second,
   112  			WriteTimeout: 10 * time.Second,
   113  			Addr:         "localhost:1234",
   114  			Handler:      http.FileServer(http.Dir(jsonSchemaFixturesPath + "/remotes")),
   115  		}
   116  		err := svr.ListenAndServe()
   117  		if err != nil {
   118  			panic(err.Error())
   119  		}
   120  	}()
   121  
   122  	files, err := os.ReadDir(jsonSchemaFixturesPath)
   123  	if err != nil {
   124  		t.Fatal(err)
   125  	}
   126  
   127  	for _, f := range files {
   128  		if f.IsDir() {
   129  			continue
   130  		}
   131  		fileName := f.Name()
   132  		t.Run(fileName, func(t *testing.T) {
   133  			t.Parallel()
   134  			specName := strings.TrimSuffix(fileName, filepath.Ext(fileName))
   135  			if !isEnabled(specName) {
   136  				t.Logf("WARNING: fixture from jsonschema-test-suite not enabled: %s", specName)
   137  				return
   138  			}
   139  			t.Log("Running " + specName)
   140  			b, _ := os.ReadFile(filepath.Join(jsonSchemaFixturesPath, fileName))
   141  			doTestSchemaSuite(t, b)
   142  		})
   143  	}
   144  }
   145  
   146  func TestSchemaFixtures(t *testing.T) {
   147  	files, err := os.ReadDir(schemaFixturesPath)
   148  	if err != nil {
   149  		t.Fatal(err)
   150  	}
   151  
   152  	for _, f := range files {
   153  		if f.IsDir() {
   154  			continue
   155  		}
   156  		fileName := f.Name()
   157  		t.Run(fileName, func(t *testing.T) {
   158  			t.Parallel()
   159  			specName := strings.TrimSuffix(fileName, filepath.Ext(fileName))
   160  			t.Log("Running " + specName)
   161  			b, _ := os.ReadFile(filepath.Join(schemaFixturesPath, fileName))
   162  			doTestSchemaSuite(t, b)
   163  		})
   164  	}
   165  }
   166  
   167  func expandOpts(base string) *spec.ExpandOptions {
   168  	return &spec.ExpandOptions{
   169  		RelativeBase:    base,
   170  		SkipSchemas:     false,
   171  		ContinueOnError: false,
   172  	}
   173  }
   174  
   175  func TestOptionalJSONSchemaSuite(t *testing.T) {
   176  	jsonOptionalSchemaFixturesPath := filepath.Join(jsonSchemaFixturesPath, "optional")
   177  	files, err := os.ReadDir(jsonOptionalSchemaFixturesPath)
   178  	if err != nil {
   179  		t.Fatal(err)
   180  	}
   181  
   182  	for _, f := range files {
   183  		if f.IsDir() {
   184  			continue
   185  		}
   186  		fileName := f.Name()
   187  		t.Run(fileName, func(t *testing.T) {
   188  			t.Parallel()
   189  			specName := strings.TrimSuffix(fileName, filepath.Ext(fileName))
   190  			if !isOptionalEnabled(specName) {
   191  				t.Logf("INFO: fixture from jsonschema-test-suite [optional] not enabled: %s", specName)
   192  				return
   193  			}
   194  			t.Log("Running [optional] " + specName)
   195  			b, _ := os.ReadFile(filepath.Join(jsonOptionalSchemaFixturesPath, fileName))
   196  			doTestSchemaSuite(t, b)
   197  		})
   198  	}
   199  }
   200  
   201  // Further testing with all formats recognized by strfmt
   202  func TestFormat_JSONSchemaExtended(t *testing.T) {
   203  	files, err := os.ReadDir(formatFixturesPath)
   204  	if err != nil {
   205  		t.Fatal(err)
   206  	}
   207  
   208  	for _, f := range files {
   209  		if f.IsDir() {
   210  			continue
   211  		}
   212  		fileName := f.Name()
   213  		t.Run(fileName, func(t *testing.T) {
   214  			t.Parallel()
   215  			specName := strings.TrimSuffix(fileName, filepath.Ext(fileName))
   216  			if !isExtendedEnabled(specName) {
   217  				t.Logf("INFO: fixture from extended tests suite [formats] not enabled: %s", specName)
   218  				return
   219  			}
   220  			t.Log("Running [extended formats] " + specName)
   221  			b, _ := os.ReadFile(filepath.Join(formatFixturesPath, fileName))
   222  			doTestSchemaSuite(t, b)
   223  		})
   224  	}
   225  }
   226  
   227  func doTestSchemaSuite(t *testing.T, doc []byte) {
   228  	// run a test formatted as per jsonschema-test-suite
   229  	var testDescriptions []schemaTestT
   230  	eru := json.Unmarshal(doc, &testDescriptions)
   231  	require.NoError(t, eru)
   232  
   233  	for _, testDescription := range testDescriptions {
   234  		b, _ := testDescription.Schema.MarshalJSON()
   235  		tmpFile, err := os.CreateTemp(os.TempDir(), "validate-test")
   236  		require.NoError(t, err)
   237  		_, _ = tmpFile.Write(b)
   238  		tmpFile.Close()
   239  		defer func() { _ = os.Remove(tmpFile.Name()) }()
   240  		err = spec.ExpandSchemaWithBasePath(testDescription.Schema, nil, expandOpts(tmpFile.Name()))
   241  		require.NoError(t, err, testDescription.Description+" should expand cleanly")
   242  
   243  		validator := NewSchemaValidator(testDescription.Schema, nil, "data", strfmt.Default)
   244  		for _, test := range testDescription.Tests {
   245  			result := validator.Validate(test.Data)
   246  			assert.NotNil(t, result, test.Description+" should validate")
   247  
   248  			if test.Valid {
   249  				assert.Empty(t, result.Errors, test.Description+" should not have errors")
   250  			} else {
   251  				assert.NotEmpty(t, result.Errors, test.Description+" should have errors")
   252  			}
   253  		}
   254  	}
   255  }
   256  

View as plain text