From 0a40089a44e28a26a44dffbc4cbd067f73d3a7f2 Mon Sep 17 00:00:00 2001 From: AislingHPE Date: Fri, 30 Aug 2024 13:13:00 +0100 Subject: [PATCH] Add tests for the other kinds of schema This tests to make sure that the `additionalProperties: false` and `--nullable-all` schemas validate different sets of values to to the ones that are validated by the default `schema.json`. In particular, `schema-nullable-all.json` should (unless disallowed by certain fields such as `enum`) all a file with all variables defined as `null`. `schema-disallow-additional.json` should disallow additional properties, including within nested objects. --- README.md | 2 +- pkg/jsonschema/json-schema_test.go | 133 ++++++++++++++++++ .../sample-input/test-input-all.json | 3 +- .../sample-input/test-input-all.json | 3 +- .../sample-input/test-input-all.json | 3 +- .../simple/sample-input/test-input-all.json | 3 +- 6 files changed, 142 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0ae9e89..4779ee6 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ running Terraform. - `--stdout`: Print schema to stdout and prevent all other logging unless an error occurs. Does not create a file. Overrides `--debug` and `--output`. -- `--export-variables`: Export the variables in JSON format directly and do not create a JSON Schema. This provides similar functionality to applications such as terraform-docs, where the input variables can be output to a machine-readable format such as JSON. The `type` field is converted to a type constraint based on the type definition, and the `default` field is translated to its literal value. `condition` inside the `validation` block is left as a string, because it is difficult to represent arbitrary (ie unevaluated) hcl Expressions in JSON. +- `--export-variables`: Export the variables in JSON format directly and do not create a JSON Schema. This provides similar functionality to applications such as terraform-docs, where the input variables can be output to a machine-readable format such as JSON. The `type` field is converted to a type constraint based on the type definition, and the `default` field is translated to its literal value. `condition` inside the `validation` block is left as a string, because it is difficult to represent arbitrary (ie unevaluated) HCL Expressions in JSON. - `--escape-json`: Escape special characters in the JSON (`<`,`>` and `&`) so that the schema can be used in a web context. By default, this behaviour is disabled so the JSON file can be read more easily, though it does not effect external programs such as `jq`. diff --git a/pkg/jsonschema/json-schema_test.go b/pkg/jsonschema/json-schema_test.go index 2f7b00e..5d047a3 100644 --- a/pkg/jsonschema/json-schema_test.go +++ b/pkg/jsonschema/json-schema_test.go @@ -86,6 +86,7 @@ func getErrorLocationsFromValidationErr(t *testing.T, valErr *jsonschema.Validat return keywordLocations } +//nolint:funlen,maintidx func TestSampleInput(t *testing.T) { t.Parallel() @@ -95,6 +96,7 @@ func TestSampleInput(t *testing.T) { filePath string keywordLocations []errorLocation }{ + // Minimum input { name: "empty minimum input", filePath: "../../test/expected/empty/sample-input/test-input-min.json", @@ -125,6 +127,7 @@ func TestSampleInput(t *testing.T) { schemaPath: "../../test/expected/custom-validation/schema.json", keywordLocations: nil, }, + // Maximum input plus an unknown variable, additionalProperties is true { name: "simple full input", filePath: "../../test/expected/simple/sample-input/test-input-all.json", @@ -149,6 +152,41 @@ func TestSampleInput(t *testing.T) { schemaPath: "../../test/expected/custom-validation/schema.json", keywordLocations: nil, }, + // Maximum input plus an unknown variable, additionalProperties is false + { + name: "simple full input additionalProperties false", + filePath: "../../test/expected/simple/sample-input/test-input-all.json", + schemaPath: "../../test/expected/simple/schema-disallow-additional.json", + keywordLocations: []errorLocation{ + {name: "/additionalProperties"}, + }, + }, + { + name: "simple-types full input additionalProperties false", + filePath: "../../test/expected/simple-types/sample-input/test-input-all.json", + schemaPath: "../../test/expected/simple-types/schema-disallow-additional.json", + keywordLocations: []errorLocation{ + {name: "/additionalProperties"}, + }, + }, + { + name: "complex-types full input additionalProperties false", + filePath: "../../test/expected/complex-types/sample-input/test-input-all.json", + schemaPath: "../../test/expected/complex-types/schema-disallow-additional.json", + keywordLocations: []errorLocation{ + {name: "/additionalProperties"}, + }, + }, + { + name: "custom-validation full input additionalProperties false", + filePath: "../../test/expected/custom-validation/sample-input/test-input-all.json", + schemaPath: "../../test/expected/custom-validation/schema-disallow-additional.json", + keywordLocations: []errorLocation{ + {name: "/additionalProperties"}, + {name: "/properties/an_object_maximum_minimum_items/additionalProperties"}, + }, + }, + // bad input on all fields { name: "simple bad input", filePath: "../../test/expected/simple/sample-input/test-input-bad.json", @@ -271,9 +309,104 @@ func TestSampleInput(t *testing.T) { }, }, }, + // null input on all fields, nullableAll is false + { + name: "simple null input nullableAll false", + filePath: "../../test/expected/simple/sample-input/test-input-null.json", + schemaPath: "../../test/expected/simple/schema.json", + keywordLocations: []errorLocation{ + {name: "/properties/age/type"}, + {name: "/properties/name/type"}, + }, + }, + { + name: "simple-types null input nullableAll false", + filePath: "../../test/expected/simple-types/sample-input/test-input-null.json", + schemaPath: "../../test/expected/simple-types/schema.json", + keywordLocations: []errorLocation{ + {name: "/properties/a_bool/type"}, + {name: "/properties/a_list/type"}, + {name: "/properties/a_map_of_strings/type"}, + {name: "/properties/a_number/type"}, + {name: "/properties/a_set/type"}, + {name: "/properties/a_string/type"}, + {name: "/properties/a_tuple/type"}, + {name: "/properties/a_variable_in_another_file/type"}, + {name: "/properties/an_object/type"}, + }, + }, + { + name: "complex-types null input nullableAll false", + filePath: "../../test/expected/complex-types/sample-input/test-input-null.json", + schemaPath: "../../test/expected/complex-types/schema.json", + keywordLocations: []errorLocation{ + {name: "/properties/a_very_complicated_object/type"}, + {name: "/properties/an_object_with_optional/type"}, + }, + }, + { + name: "custom-validation null input nullableAll false", + filePath: "../../test/expected/custom-validation/sample-input/test-input-null.json", + schemaPath: "../../test/expected/custom-validation/schema.json", + keywordLocations: []errorLocation{ + {name: "/properties/a_list_maximum_minimum_length/type"}, + {name: "/properties/a_map_maximum_minimum_entries/type"}, + {name: "/properties/a_number_enum_kind_1/type"}, + {name: "/properties/a_number_enum_kind_2/type"}, + {name: "/properties/a_number_exclusive_maximum_minimum/type"}, + {name: "/properties/a_number_maximum_minimum/type"}, + {name: "/properties/a_set_maximum_minimum_items/type"}, + {name: "/properties/a_string_enum_escaped_characters_kind_1/type"}, + {name: "/properties/a_string_enum_escaped_characters_kind_2/type"}, + {name: "/properties/a_string_enum_kind_1/type"}, + {name: "/properties/a_string_enum_kind_2/type"}, + {name: "/properties/a_string_length_over_defined/type"}, + {name: "/properties/a_string_maximum_minimum_length/type"}, + {name: "/properties/a_string_pattern_1/type"}, + {name: "/properties/a_string_pattern_2/type"}, + {name: "/properties/a_string_set_length/type"}, + {name: "/properties/an_object_maximum_minimum_items/type"}, + }, + }, + // null input on all fields, nullableAll is true + { + name: "simple null input nullableAll true", + filePath: "../../test/expected/simple/sample-input/test-input-null.json", + schemaPath: "../../test/expected/simple/schema-nullable-all.json", + keywordLocations: nil, + }, + { + name: "simple-types null input nullableAll true", + filePath: "../../test/expected/simple-types/sample-input/test-input-null.json", + schemaPath: "../../test/expected/simple-types/schema-nullable-all.json", + keywordLocations: nil, + }, + { + name: "complex-types null input nullableAll true", + filePath: "../../test/expected/complex-types/sample-input/test-input-null.json", + schemaPath: "../../test/expected/complex-types/schema-nullable-all.json", + keywordLocations: nil, + }, + { + // of note: custom validation still applies to nullable fields, and sometimes 'null' doesn't satisfy the + // condition, meaning these fields effectively can't be null. + // This seems to primarily be the case for "enum" fields. Other fields tend to ignore this error, in JSON Schema. + name: "custom-validation null input nullableAll true", + filePath: "../../test/expected/custom-validation/sample-input/test-input-null.json", + schemaPath: "../../test/expected/custom-validation/schema-nullable-all.json", + keywordLocations: []errorLocation{ + {name: "/properties/a_number_enum_kind_1/enum"}, + {name: "/properties/a_number_enum_kind_2/enum"}, + {name: "/properties/a_string_enum_kind_1/enum"}, + {name: "/properties/a_string_enum_kind_2/enum"}, + }, + }, } for i := range testCases { tc := testCases[i] + if tc.name != "custom-validation null input nullableAll true" { + continue + } t.Run(tc.name, func(t *testing.T) { t.Parallel() path, err := filepath.Abs(tc.schemaPath) diff --git a/test/expected/complex-types/sample-input/test-input-all.json b/test/expected/complex-types/sample-input/test-input-all.json index 62cf394..b948641 100644 --- a/test/expected/complex-types/sample-input/test-input-all.json +++ b/test/expected/complex-types/sample-input/test-input-all.json @@ -53,5 +53,6 @@ "b": 2, "c": false, "d": "Optional string" - } + }, + "something_else": "string" } \ No newline at end of file diff --git a/test/expected/custom-validation/sample-input/test-input-all.json b/test/expected/custom-validation/sample-input/test-input-all.json index 958aeb3..ad620f2 100644 --- a/test/expected/custom-validation/sample-input/test-input-all.json +++ b/test/expected/custom-validation/sample-input/test-input-all.json @@ -22,5 +22,6 @@ "an_object_maximum_minimum_items": { "name": "a", "other_name": "this is an additional property, terraform will ignore it" - } + }, + "something_else": "string" } \ No newline at end of file diff --git a/test/expected/simple-types/sample-input/test-input-all.json b/test/expected/simple-types/sample-input/test-input-all.json index e92fecf..e3320b4 100644 --- a/test/expected/simple-types/sample-input/test-input-all.json +++ b/test/expected/simple-types/sample-input/test-input-all.json @@ -29,5 +29,6 @@ "a": "d", "b": 2, "c": false - } + }, + "something_else": "string" } \ No newline at end of file diff --git a/test/expected/simple/sample-input/test-input-all.json b/test/expected/simple/sample-input/test-input-all.json index 67fb9c8..b4ef8f1 100644 --- a/test/expected/simple/sample-input/test-input-all.json +++ b/test/expected/simple/sample-input/test-input-all.json @@ -1,5 +1,6 @@ { "$schema": "../schema.json", "name": "Aisling", - "age": 24 + "age": 24, + "something_else": "string" } \ No newline at end of file