2023-01-20 15:55:44 +00:00
|
|
|
package schema
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestIntSchema(t *testing.T) {
|
|
|
|
var elemInt int
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}`
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elemInt), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBooleanSchema(t *testing.T) {
|
|
|
|
var elem bool
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "boolean"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}`
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStringSchema(t *testing.T) {
|
|
|
|
var elem string
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "string"
|
|
|
|
}`
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStructOfPrimitivesSchema(t *testing.T) {
|
|
|
|
type Foo struct {
|
|
|
|
IntVal int `json:"int_val"`
|
|
|
|
Int8Val int8 `json:"int8_val"`
|
|
|
|
Int16Val int16 `json:"int16_val"`
|
|
|
|
Int32Val int32 `json:"int32_val"`
|
|
|
|
Int64Val int64 `json:"int64_val"`
|
|
|
|
|
|
|
|
UIntVal uint `json:"uint_val"`
|
|
|
|
Uint8Val uint8 `json:"uint8_val"`
|
|
|
|
Uint16Val uint16 `json:"uint16_val"`
|
|
|
|
Uint32Val uint32 `json:"uint32_val"`
|
|
|
|
Uint64Val uint64 `json:"uint64_val"`
|
|
|
|
|
|
|
|
Float32Val float32 `json:"float32_val"`
|
|
|
|
Float64Val float64 `json:"float64_val"`
|
|
|
|
|
|
|
|
StringVal string `json:"string_val"`
|
|
|
|
|
|
|
|
BoolVal bool `json:"bool_val"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := Foo{}
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"bool_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "boolean"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"float32_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"float64_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"int16_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"int32_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"int64_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"int8_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"int_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"string_val": {
|
|
|
|
"type": "string"
|
|
|
|
},
|
|
|
|
"uint16_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"uint32_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"uint64_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"uint8_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"uint_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"int_val",
|
|
|
|
"int8_val",
|
|
|
|
"int16_val",
|
|
|
|
"int32_val",
|
|
|
|
"int64_val",
|
|
|
|
"uint_val",
|
|
|
|
"uint8_val",
|
|
|
|
"uint16_val",
|
|
|
|
"uint32_val",
|
|
|
|
"uint64_val",
|
|
|
|
"float32_val",
|
|
|
|
"float64_val",
|
|
|
|
"string_val",
|
|
|
|
"bool_val"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStructOfStructsSchema(t *testing.T) {
|
|
|
|
type Bar struct {
|
|
|
|
A int `json:"a"`
|
|
|
|
B string `json:"b,string"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Foo struct {
|
|
|
|
Bar Bar `json:"bar"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type MyStruct struct {
|
|
|
|
Foo Foo `json:"foo"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := MyStruct{}
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"foo": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"bar": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"a": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"b": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"a",
|
|
|
|
"b"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"bar"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"foo"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStructOfMapsSchema(t *testing.T) {
|
|
|
|
type Bar struct {
|
|
|
|
MyMap map[string]int `json:"my_map"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Foo struct {
|
|
|
|
Bar Bar `json:"bar"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := Foo{}
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"bar": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"my_map": {
|
|
|
|
"type": "object",
|
|
|
|
"additionalProperties": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"my_map"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"bar"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStructOfSliceSchema(t *testing.T) {
|
|
|
|
type Bar struct {
|
|
|
|
MySlice []string `json:"my_slice"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Foo struct {
|
|
|
|
Bar Bar `json:"bar"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := Foo{}
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"bar": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"my_slice": {
|
|
|
|
"type": "array",
|
|
|
|
"items": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"my_slice"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"bar"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMapOfPrimitivesSchema(t *testing.T) {
|
|
|
|
var elem map[string]int
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"additionalProperties": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMapOfStructSchema(t *testing.T) {
|
|
|
|
type Foo struct {
|
|
|
|
MyInt int `json:"my_int"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var elem map[string]Foo
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"additionalProperties": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"my_int": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"my_int"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMapOfMapSchema(t *testing.T) {
|
|
|
|
var elem map[string]map[string]int
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"additionalProperties": {
|
|
|
|
"type": "object",
|
|
|
|
"additionalProperties": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMapOfSliceSchema(t *testing.T) {
|
|
|
|
var elem map[string][]string
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"additionalProperties": {
|
|
|
|
"type": "array",
|
|
|
|
"items": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSliceOfPrimitivesSchema(t *testing.T) {
|
|
|
|
var elem []float32
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "array",
|
|
|
|
"items": {
|
|
|
|
"type": "number"
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSliceOfSliceSchema(t *testing.T) {
|
|
|
|
var elem [][]string
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "array",
|
|
|
|
"items": {
|
|
|
|
"type": "array",
|
|
|
|
"items": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSliceOfMapSchema(t *testing.T) {
|
|
|
|
var elem []map[string]int
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "array",
|
|
|
|
"items": {
|
|
|
|
"type": "object",
|
|
|
|
"additionalProperties": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSliceOfStructSchema(t *testing.T) {
|
|
|
|
type Foo struct {
|
|
|
|
MyInt int `json:"my_int"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var elem []Foo
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "array",
|
|
|
|
"items": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"my_int": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"my_int"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestEmbeddedStructSchema(t *testing.T) {
|
|
|
|
type Location struct {
|
|
|
|
Country string `json:"country"`
|
|
|
|
State string `json:"state,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Person struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
Age int `json:"age,omitempty"`
|
|
|
|
Home Location `json:"home"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Plot struct {
|
|
|
|
Events map[string]Person `json:"events"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Story struct {
|
|
|
|
Plot Plot `json:"plot"`
|
|
|
|
*Person
|
|
|
|
Location
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := Story{}
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"age": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"country": {
|
|
|
|
"type": "string"
|
|
|
|
},
|
|
|
|
"home": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"country": {
|
|
|
|
"type": "string"
|
|
|
|
},
|
|
|
|
"state": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"country"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"name": {
|
|
|
|
"type": "string"
|
|
|
|
},
|
|
|
|
"plot": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"events": {
|
|
|
|
"type": "object",
|
|
|
|
"additionalProperties": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"age": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"home": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"country": {
|
|
|
|
"type": "string"
|
|
|
|
},
|
|
|
|
"state": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"country"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"name": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"name",
|
|
|
|
"home"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"events"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"state": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"plot",
|
|
|
|
"name",
|
|
|
|
"home",
|
|
|
|
"country"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestErrorWithTrace(t *testing.T) {
|
|
|
|
tracker := newTracker()
|
|
|
|
dummyType := reflect.TypeOf(struct{}{})
|
2023-03-16 11:57:57 +00:00
|
|
|
err := tracker.errWithTrace("with empty trace", "root")
|
|
|
|
assert.ErrorContains(t, err, "with empty trace. traversal trace: root")
|
2023-01-20 15:55:44 +00:00
|
|
|
|
|
|
|
tracker.push(dummyType, "resources")
|
2023-03-16 11:57:57 +00:00
|
|
|
err = tracker.errWithTrace("with depth = 1", "root")
|
|
|
|
assert.ErrorContains(t, err, "with depth = 1. traversal trace: root -> resources")
|
2023-01-20 15:55:44 +00:00
|
|
|
|
|
|
|
tracker.push(dummyType, "pipelines")
|
|
|
|
tracker.push(dummyType, "datasets")
|
2023-03-16 11:57:57 +00:00
|
|
|
err = tracker.errWithTrace("with depth = 4", "root")
|
|
|
|
assert.ErrorContains(t, err, "with depth = 4. traversal trace: root -> resources -> pipelines -> datasets")
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestNonAnnotatedFieldsAreSkipped(t *testing.T) {
|
|
|
|
type MyStruct struct {
|
|
|
|
Foo string
|
|
|
|
Bar int `json:"bar"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := MyStruct{}
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expectedSchema :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"bar": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"bar"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expectedSchema)
|
|
|
|
|
|
|
|
assert.Equal(t, expectedSchema, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDashFieldsAreSkipped(t *testing.T) {
|
|
|
|
type MyStruct struct {
|
|
|
|
Foo string `json:"-"`
|
|
|
|
Bar int `json:"bar"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := MyStruct{}
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expectedSchema :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"bar": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"bar"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expectedSchema)
|
|
|
|
|
|
|
|
assert.Equal(t, expectedSchema, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPointerInStructSchema(t *testing.T) {
|
|
|
|
|
|
|
|
type Bar struct {
|
|
|
|
PtrVal2 *int `json:"ptr_val2"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Foo struct {
|
|
|
|
PtrInt *int `json:"ptr_int"`
|
|
|
|
PtrString *string `json:"ptr_string"`
|
|
|
|
FloatVal float32 `json:"float_val"`
|
|
|
|
PtrBar *Bar `json:"ptr_bar"`
|
|
|
|
Bar *Bar `json:"bar"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := Foo{}
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expectedSchema :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"bar": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"ptr_val2": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"ptr_val2"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"float_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"ptr_bar": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"ptr_val2": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"ptr_val2"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"ptr_int": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"ptr_string": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"ptr_int",
|
|
|
|
"ptr_string",
|
|
|
|
"float_val",
|
|
|
|
"ptr_bar",
|
|
|
|
"bar"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expectedSchema)
|
|
|
|
|
|
|
|
assert.Equal(t, expectedSchema, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGenericSchema(t *testing.T) {
|
|
|
|
type Person struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
Age int `json:"age,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Plot struct {
|
|
|
|
Stakes []string `json:"stakes"`
|
|
|
|
Deaths []Person `json:"deaths"`
|
|
|
|
Murders map[string]Person `json:"murders"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Wedding struct {
|
|
|
|
Hidden string `json:","`
|
|
|
|
Groom Person `json:"groom"`
|
|
|
|
Bride Person `json:"bride"`
|
|
|
|
Plots []Plot `json:"plots"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Story struct {
|
|
|
|
Hero *Person `json:"hero"`
|
|
|
|
Villian Person `json:"villian,omitempty"`
|
|
|
|
Weddings []Wedding `json:"weddings"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := Story{}
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"hero": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"age": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"name": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"name"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"villian": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"age": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"name": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"name"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"weddings": {
|
|
|
|
"type": "array",
|
|
|
|
"items": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"bride": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"age": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"name": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"name"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"groom": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"age": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"name": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"name"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"plots": {
|
|
|
|
"type": "array",
|
|
|
|
"items": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"deaths": {
|
|
|
|
"type": "array",
|
|
|
|
"items": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"age": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"name": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"name"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"murders": {
|
|
|
|
"type": "object",
|
|
|
|
"additionalProperties": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"age": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"name": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"name"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"stakes": {
|
|
|
|
"type": "array",
|
|
|
|
"items": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"stakes",
|
|
|
|
"deaths",
|
|
|
|
"murders"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"groom",
|
|
|
|
"bride",
|
|
|
|
"plots"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"hero",
|
|
|
|
"weddings"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFieldsWithoutOmitEmptyAreRequired(t *testing.T) {
|
|
|
|
|
|
|
|
type Papaya struct {
|
|
|
|
A int `json:"a,string,omitempty"`
|
|
|
|
B string `json:"b"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type MyStruct struct {
|
|
|
|
Foo string `json:"-,omitempty"`
|
|
|
|
Bar int `json:"bar"`
|
|
|
|
Apple int `json:"apple,omitempty"`
|
|
|
|
Mango int `json:",omitempty"`
|
|
|
|
Guava int `json:","`
|
|
|
|
Papaya *Papaya `json:"papaya,"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := MyStruct{}
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expectedSchema :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"apple": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"bar": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"papaya": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"a": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"b": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"b"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"bar",
|
|
|
|
"papaya"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expectedSchema)
|
|
|
|
|
|
|
|
assert.Equal(t, expectedSchema, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
2023-03-15 02:18:51 +00:00
|
|
|
func TestDocIngestionForObject(t *testing.T) {
|
2023-01-20 15:55:44 +00:00
|
|
|
docs := &Docs{
|
2023-03-15 02:18:51 +00:00
|
|
|
Description: "docs for root",
|
|
|
|
Properties: map[string]*Docs{
|
2023-01-20 15:55:44 +00:00
|
|
|
"my_struct": {
|
2023-03-15 02:18:51 +00:00
|
|
|
Description: "docs for my struct",
|
|
|
|
Properties: map[string]*Docs{
|
|
|
|
"a": {
|
|
|
|
Description: "docs for a",
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
2023-03-15 02:18:51 +00:00
|
|
|
"c": {
|
|
|
|
Description: "docs for c which does not exist on my_struct",
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2023-03-15 02:18:51 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
type MyStruct struct {
|
|
|
|
A string `json:"a"`
|
|
|
|
B int `json:"b"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Root struct {
|
|
|
|
MyStruct *MyStruct `json:"my_struct"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := Root{}
|
|
|
|
|
|
|
|
schema, err := New(reflect.TypeOf(elem), docs)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expectedSchema :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"description": "docs for root",
|
|
|
|
"properties": {
|
|
|
|
"my_struct": {
|
|
|
|
"type": "object",
|
|
|
|
"description": "docs for my struct",
|
|
|
|
"properties": {
|
|
|
|
"a": {
|
|
|
|
"type": "string",
|
|
|
|
"description": "docs for a"
|
|
|
|
},
|
|
|
|
"b": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-03-15 02:18:51 +00:00
|
|
|
}
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
2023-03-15 02:18:51 +00:00
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"a",
|
|
|
|
"b"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"my_struct"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expectedSchema)
|
|
|
|
|
|
|
|
assert.Equal(t, expectedSchema, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDocIngestionForSlice(t *testing.T) {
|
|
|
|
docs := &Docs{
|
|
|
|
Description: "docs for root",
|
|
|
|
Properties: map[string]*Docs{
|
|
|
|
"my_slice": {
|
|
|
|
Description: "docs for my slice",
|
|
|
|
Items: &Docs{
|
|
|
|
Properties: map[string]*Docs{
|
|
|
|
"guava": {
|
|
|
|
Description: "docs for guava",
|
|
|
|
},
|
|
|
|
"pineapple": {
|
|
|
|
Description: "docs for pineapple",
|
|
|
|
},
|
|
|
|
"watermelon": {
|
|
|
|
Description: "docs for watermelon which does not exist in schema",
|
|
|
|
},
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
type Bar struct {
|
|
|
|
Guava int `json:"guava"`
|
|
|
|
Pineapple int `json:"pineapple"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Root struct {
|
2023-03-15 02:18:51 +00:00
|
|
|
MySlice []Bar `json:"my_slice"`
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
elem := Root{}
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), docs)
|
2023-01-20 15:55:44 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expectedSchema :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"description": "docs for root",
|
|
|
|
"properties": {
|
|
|
|
"my_slice": {
|
|
|
|
"type": "array",
|
|
|
|
"description": "docs for my slice",
|
|
|
|
"items": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"guava": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"description": "docs for guava",
|
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"pineapple": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"description": "docs for pineapple",
|
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"guava",
|
|
|
|
"pineapple"
|
|
|
|
]
|
|
|
|
}
|
2023-03-15 02:18:51 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"my_slice"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expectedSchema)
|
|
|
|
|
|
|
|
assert.Equal(t, expectedSchema, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDocIngestionForMap(t *testing.T) {
|
|
|
|
docs := &Docs{
|
|
|
|
Description: "docs for root",
|
|
|
|
Properties: map[string]*Docs{
|
|
|
|
"my_map": {
|
|
|
|
Description: "docs for my map",
|
|
|
|
AdditionalProperties: &Docs{
|
|
|
|
Properties: map[string]*Docs{
|
|
|
|
"apple": {
|
|
|
|
Description: "docs for apple",
|
|
|
|
},
|
|
|
|
"mango": {
|
|
|
|
Description: "docs for mango",
|
|
|
|
},
|
|
|
|
"watermelon": {
|
|
|
|
Description: "docs for watermelon which does not exist in schema",
|
|
|
|
},
|
|
|
|
"papaya": {
|
|
|
|
Description: "docs for papaya which does not exist in schema",
|
|
|
|
},
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
},
|
2023-03-15 02:18:51 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
type Foo struct {
|
|
|
|
Apple int `json:"apple"`
|
|
|
|
Mango int `json:"mango"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Root struct {
|
|
|
|
MyMap map[string]*Foo `json:"my_map"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := Root{}
|
|
|
|
|
|
|
|
schema, err := New(reflect.TypeOf(elem), docs)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expectedSchema :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"description": "docs for root",
|
|
|
|
"properties": {
|
|
|
|
"my_map": {
|
|
|
|
"type": "object",
|
|
|
|
"description": "docs for my map",
|
|
|
|
"additionalProperties": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"apple": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"description": "docs for apple",
|
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-03-15 02:18:51 +00:00
|
|
|
},
|
|
|
|
"mango": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"description": "docs for mango",
|
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-03-15 02:18:51 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"apple",
|
|
|
|
"mango"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"my_map"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expectedSchema)
|
|
|
|
|
|
|
|
assert.Equal(t, expectedSchema, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDocIngestionForTopLevelPrimitive(t *testing.T) {
|
|
|
|
docs := &Docs{
|
|
|
|
Description: "docs for root",
|
|
|
|
Properties: map[string]*Docs{
|
|
|
|
"my_val": {
|
|
|
|
Description: "docs for my val",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
type Root struct {
|
|
|
|
MyVal int `json:"my_val"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := Root{}
|
|
|
|
|
|
|
|
schema, err := New(reflect.TypeOf(elem), docs)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expectedSchema :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"description": "docs for root",
|
|
|
|
"properties": {
|
2023-01-20 15:55:44 +00:00
|
|
|
"my_val": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"description": "docs for my val",
|
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
2023-03-15 02:18:51 +00:00
|
|
|
"my_val"
|
2023-01-20 15:55:44 +00:00
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expectedSchema)
|
|
|
|
|
|
|
|
assert.Equal(t, expectedSchema, string(jsonSchema))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestErrorOnMapWithoutStringKey(t *testing.T) {
|
|
|
|
type Foo struct {
|
|
|
|
Bar map[int]string `json:"bar"`
|
|
|
|
}
|
|
|
|
elem := Foo{}
|
2023-01-23 14:00:11 +00:00
|
|
|
_, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.ErrorContains(t, err, "only strings map keys are valid. key type: int")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestErrorIfStructRefersToItself(t *testing.T) {
|
|
|
|
type Foo struct {
|
|
|
|
MyFoo *Foo `json:"my_foo"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := Foo{}
|
2023-01-23 14:00:11 +00:00
|
|
|
_, err := New(reflect.TypeOf(elem), nil)
|
2023-03-16 11:57:57 +00:00
|
|
|
assert.ErrorContains(t, err, "cycle detected. traversal trace: root -> my_foo")
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestErrorIfStructHasLoop(t *testing.T) {
|
|
|
|
type Apple struct {
|
|
|
|
MyVal int `json:"my_val"`
|
|
|
|
MyMango struct {
|
|
|
|
MyGuava struct {
|
|
|
|
MyPapaya struct {
|
|
|
|
MyApple *Apple `json:"my_apple"`
|
|
|
|
} `json:"my_papaya"`
|
|
|
|
} `json:"my_guava"`
|
|
|
|
} `json:"my_mango"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := Apple{}
|
2023-01-23 14:00:11 +00:00
|
|
|
_, err := New(reflect.TypeOf(elem), nil)
|
2023-03-16 11:57:57 +00:00
|
|
|
assert.ErrorContains(t, err, "cycle detected. traversal trace: root -> my_mango -> my_guava -> my_papaya -> my_apple")
|
2023-01-20 15:55:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestInterfaceGeneratesEmptySchema(t *testing.T) {
|
|
|
|
type Foo struct {
|
|
|
|
Apple int `json:"apple"`
|
|
|
|
Mango interface{} `json:"mango"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := Foo{}
|
|
|
|
|
2023-01-23 14:00:11 +00:00
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
2023-01-20 15:55:44 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"apple": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-01-20 15:55:44 +00:00
|
|
|
},
|
|
|
|
"mango": {}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"apple",
|
|
|
|
"mango"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
2023-04-04 10:16:07 +00:00
|
|
|
|
|
|
|
func TestBundleReadOnlytag(t *testing.T) {
|
|
|
|
type Pokemon struct {
|
|
|
|
Pikachu string `json:"pikachu" bundle:"readonly"`
|
|
|
|
Raichu string `json:"raichu"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Foo struct {
|
|
|
|
Pokemon *Pokemon `json:"pokemon"`
|
|
|
|
Apple int `json:"apple"`
|
|
|
|
Mango string `json:"mango" bundle:"readonly"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := Foo{}
|
|
|
|
|
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"apple": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-04-04 10:16:07 +00:00
|
|
|
},
|
|
|
|
"pokemon": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"raichu": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"raichu"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"pokemon",
|
|
|
|
"apple"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|
2023-08-10 10:03:52 +00:00
|
|
|
|
|
|
|
func TestBundleInternalTag(t *testing.T) {
|
|
|
|
type Pokemon struct {
|
|
|
|
Pikachu string `json:"pikachu" bundle:"internal"`
|
|
|
|
Raichu string `json:"raichu"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Foo struct {
|
|
|
|
Pokemon *Pokemon `json:"pokemon"`
|
|
|
|
Apple int `json:"apple"`
|
|
|
|
Mango string `json:"mango" bundle:"internal"`
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := Foo{}
|
|
|
|
|
|
|
|
schema, err := New(reflect.TypeOf(elem), nil)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
expected :=
|
|
|
|
`{
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"apple": {
|
2024-04-25 11:20:45 +00:00
|
|
|
"anyOf": [
|
|
|
|
{
|
|
|
|
"type": "number"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "string",
|
|
|
|
"pattern": "\\$\\{([a-zA-Z]+([-_]?[a-zA-Z0-9]+)*(\\.[a-zA-Z]+([-_]?[a-zA-Z0-9]+)*)*)\\}"
|
|
|
|
}
|
|
|
|
]
|
2023-08-10 10:03:52 +00:00
|
|
|
},
|
|
|
|
"pokemon": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"raichu": {
|
|
|
|
"type": "string"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"raichu"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"additionalProperties": false,
|
|
|
|
"required": [
|
|
|
|
"pokemon",
|
|
|
|
"apple"
|
|
|
|
]
|
|
|
|
}`
|
|
|
|
|
|
|
|
t.Log("[DEBUG] actual: ", string(jsonSchema))
|
|
|
|
t.Log("[DEBUG] expected: ", expected)
|
|
|
|
assert.Equal(t, expected, string(jsonSchema))
|
|
|
|
}
|