github.com/kaptinlin/jsonschema@v0.4.6/tests/format_test.go (about)

     1  package tests
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/kaptinlin/jsonschema"
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  )
    10  
    11  // TestFormatForTestSuite executes the format validation tests for Schema Test Suite.
    12  func TestFormatForTestSuite(t *testing.T) {
    13  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/format.json",
    14  		"idn-email format",
    15  		"idn-hostname format")
    16  }
    17  
    18  func TestFormatDateTimeForTestSuite(t *testing.T) {
    19  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/date-time.json")
    20  }
    21  
    22  func TestFormatDateForTestSuite(t *testing.T) {
    23  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/date.json")
    24  }
    25  
    26  func TestFormatDurationForTestSuite(t *testing.T) {
    27  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/duration.json")
    28  }
    29  
    30  func TestFormatEmailForTestSuite(t *testing.T) {
    31  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/email.json")
    32  }
    33  
    34  func TestFormatHostnameForTestSuite(t *testing.T) {
    35  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/hostname.json")
    36  }
    37  
    38  func TestFormatIpv4ForTestSuite(t *testing.T) {
    39  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/ipv4.json")
    40  }
    41  
    42  func TestFormatIpv6ForTestSuite(t *testing.T) {
    43  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/ipv6.json")
    44  }
    45  
    46  func TestFormatIriReferenceForTestSuite(t *testing.T) {
    47  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/iri-reference.json")
    48  }
    49  
    50  func TestFormatIriForTestSuite(t *testing.T) {
    51  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/iri.json")
    52  }
    53  
    54  func TestFormatJsonPointerForTestSuite(t *testing.T) {
    55  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/json-pointer.json")
    56  }
    57  
    58  func TestFormatRegexForTestSuite(t *testing.T) {
    59  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/regex.json")
    60  }
    61  
    62  func TestFormatRelativeJsonPointerForTestSuite(t *testing.T) {
    63  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/relative-json-pointer.json")
    64  }
    65  
    66  func TestFormatTimeForTestSuite(t *testing.T) {
    67  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/time.json")
    68  }
    69  
    70  func TestFormatUnknowForTestSuite(t *testing.T) {
    71  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/unknown.json")
    72  }
    73  
    74  func TestFormatUriReferenceForTestSuite(t *testing.T) {
    75  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/uri-reference.json")
    76  }
    77  
    78  func TestFormatUriTemplateForTestSuite(t *testing.T) {
    79  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/uri-template.json")
    80  }
    81  
    82  func TestFormatUriForTestSuite(t *testing.T) {
    83  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/uri.json")
    84  }
    85  
    86  func TestFormatUuidForTestSuite(t *testing.T) {
    87  	testJSONSchemaTestSuiteWithFilePath(t, "../testdata/JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/uuid.json")
    88  }
    89  
    90  // TestCompileBatchFormatValidation tests that format validation works correctly
    91  // when using CompileBatch method
    92  func TestCompileBatchFormatValidation(t *testing.T) {
    93  	compiler := jsonschema.NewCompiler()
    94  	compiler.SetAssertFormat(true) // Enable format validation
    95  	compiler.RegisterFormat("ipv4", jsonschema.IsIPV4, "string")
    96  
    97  	schemas := map[string][]byte{
    98  		"schema1": []byte(`{
    99  			"$schema": "https://json-schema.org/draft/2020-12/schema",
   100  			"type": "object",
   101  			"properties": {
   102  				"ip_addr": {
   103  					"type": "string",
   104  					"format": "ipv4"
   105  				}
   106  			}
   107  		}`),
   108  	}
   109  
   110  	compiledSchemas, err := compiler.CompileBatch(schemas)
   111  	require.NoError(t, err, "CompileBatch should not fail")
   112  	require.Len(t, compiledSchemas, 1, "Should compile one schema")
   113  
   114  	schema1 := compiledSchemas["schema1"]
   115  	require.NotNil(t, schema1, "Schema should not be nil")
   116  
   117  	// Test valid IPv4 address
   118  	validData := map[string]interface{}{
   119  		"ip_addr": "192.168.1.1",
   120  	}
   121  	result := schema1.Validate(validData)
   122  	assert.True(t, result.IsValid(), "Valid IPv4 address should pass validation")
   123  
   124  	// Test invalid IPv4 address - this should fail when AssertFormat is true
   125  	invalidData := map[string]interface{}{
   126  		"ip_addr": "256.256.256.256",
   127  	}
   128  	result = schema1.Validate(invalidData)
   129  	assert.False(t, result.IsValid(), "Invalid IPv4 address should fail validation when AssertFormat is enabled")
   130  
   131  	// Verify the error contains format information (check both top level and details)
   132  	hasFormatError := false
   133  
   134  	// Check top level errors
   135  	for _, err := range result.Errors {
   136  		if err.Keyword == "format" {
   137  			hasFormatError = true
   138  			break
   139  		}
   140  	}
   141  
   142  	// Check detailed errors recursively
   143  	if !hasFormatError {
   144  		var checkDetails func([]*jsonschema.EvaluationResult) bool
   145  		checkDetails = func(details []*jsonschema.EvaluationResult) bool {
   146  			for _, detail := range details {
   147  				for _, err := range detail.Errors {
   148  					if err.Keyword == "format" {
   149  						return true
   150  					}
   151  				}
   152  				if checkDetails(detail.Details) {
   153  					return true
   154  				}
   155  			}
   156  			return false
   157  		}
   158  		hasFormatError = checkDetails(result.Details)
   159  	}
   160  
   161  	assert.True(t, hasFormatError, "Should have format validation error")
   162  }
   163  
   164  // TestCompileBatchVsRegularCompileFormatValidation compares format validation
   165  // between CompileBatch and regular Compile methods
   166  func TestCompileBatchVsRegularCompileFormatValidation(t *testing.T) {
   167  	compiler1 := jsonschema.NewCompiler()
   168  	compiler1.SetAssertFormat(true)
   169  	compiler1.RegisterFormat("email", jsonschema.IsEmail, "string")
   170  	compiler1.RegisterFormat("ipv4", jsonschema.IsIPV4, "string")
   171  
   172  	compiler2 := jsonschema.NewCompiler()
   173  	compiler2.SetAssertFormat(true)
   174  	compiler2.RegisterFormat("email", jsonschema.IsEmail, "string")
   175  	compiler2.RegisterFormat("ipv4", jsonschema.IsIPV4, "string")
   176  
   177  	schemaBytes := []byte(`{
   178  		"$schema": "https://json-schema.org/draft/2020-12/schema",
   179  		"type": "object",
   180  		"properties": {
   181  			"email": {
   182  				"type": "string",
   183  				"format": "email"
   184  			},
   185  			"ip_addr": {
   186  				"type": "string",
   187  				"format": "ipv4"
   188  			}
   189  		}
   190  	}`)
   191  
   192  	// Compile using regular Compile method
   193  	regularSchema, err := compiler1.Compile(schemaBytes)
   194  	require.NoError(t, err, "Regular compilation should not fail")
   195  
   196  	// Compile using CompileBatch method
   197  	batchSchemas, err := compiler2.CompileBatch(map[string][]byte{
   198  		"test": schemaBytes,
   199  	})
   200  	require.NoError(t, err, "Batch compilation should not fail")
   201  	batchSchema := batchSchemas["test"]
   202  
   203  	// Test data with invalid formats
   204  	testData := map[string]interface{}{
   205  		"email":   "invalid-email",
   206  		"ip_addr": "256.256.256.256",
   207  	}
   208  
   209  	// Both should behave the same way
   210  	regularResult := regularSchema.Validate(testData)
   211  	batchResult := batchSchema.Validate(testData)
   212  
   213  	assert.Equal(t, regularResult.IsValid(), batchResult.IsValid(),
   214  		"Regular and batch compiled schemas should have same validation result")
   215  
   216  	// Both should fail validation due to invalid formats
   217  	assert.False(t, regularResult.IsValid(), "Regular compiled schema should reject invalid formats")
   218  	assert.False(t, batchResult.IsValid(), "Batch compiled schema should reject invalid formats")
   219  
   220  	// Count format errors in both results (including detailed errors)
   221  	regularFormatErrors := 0
   222  	batchFormatErrors := 0
   223  
   224  	// Helper function to count format errors recursively
   225  	var countFormatErrors func([]*jsonschema.EvaluationResult) int
   226  	countFormatErrors = func(details []*jsonschema.EvaluationResult) int {
   227  		count := 0
   228  		for _, detail := range details {
   229  			for _, err := range detail.Errors {
   230  				if err.Keyword == "format" {
   231  					count++
   232  				}
   233  			}
   234  			count += countFormatErrors(detail.Details)
   235  		}
   236  		return count
   237  	}
   238  
   239  	// Count regular result errors
   240  	for _, err := range regularResult.Errors {
   241  		if err.Keyword == "format" {
   242  			regularFormatErrors++
   243  		}
   244  	}
   245  	regularFormatErrors += countFormatErrors(regularResult.Details)
   246  
   247  	// Count batch result errors
   248  	for _, err := range batchResult.Errors {
   249  		if err.Keyword == "format" {
   250  			batchFormatErrors++
   251  		}
   252  	}
   253  	batchFormatErrors += countFormatErrors(batchResult.Details)
   254  
   255  	assert.Equal(t, regularFormatErrors, batchFormatErrors,
   256  		"Both methods should produce the same number of format errors")
   257  	assert.Greater(t, regularFormatErrors, 0, "Should have format errors")
   258  }