github.com/databricks/cli@v0.203.0/bundle/schema/schema_test.go (about)

     1  package schema
     2  
     3  import (
     4  	"encoding/json"
     5  	"reflect"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestIntSchema(t *testing.T) {
    13  	var elemInt int
    14  
    15  	expected :=
    16  		`{
    17  			"type": "number"
    18  		}`
    19  
    20  	schema, err := New(reflect.TypeOf(elemInt), nil)
    21  	require.NoError(t, err)
    22  
    23  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
    24  	assert.NoError(t, err)
    25  
    26  	t.Log("[DEBUG] actual: ", string(jsonSchema))
    27  	t.Log("[DEBUG] expected: ", expected)
    28  	assert.Equal(t, expected, string(jsonSchema))
    29  }
    30  
    31  func TestBooleanSchema(t *testing.T) {
    32  	var elem bool
    33  
    34  	expected :=
    35  		`{
    36  			"type": "boolean"
    37  		}`
    38  
    39  	schema, err := New(reflect.TypeOf(elem), nil)
    40  	require.NoError(t, err)
    41  
    42  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
    43  	assert.NoError(t, err)
    44  
    45  	t.Log("[DEBUG] actual: ", string(jsonSchema))
    46  	t.Log("[DEBUG] expected: ", expected)
    47  	assert.Equal(t, expected, string(jsonSchema))
    48  }
    49  
    50  func TestStringSchema(t *testing.T) {
    51  	var elem string
    52  
    53  	expected :=
    54  		`{
    55  			"type": "string"
    56  		}`
    57  
    58  	schema, err := New(reflect.TypeOf(elem), nil)
    59  	require.NoError(t, err)
    60  
    61  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
    62  	assert.NoError(t, err)
    63  
    64  	t.Log("[DEBUG] actual: ", string(jsonSchema))
    65  	t.Log("[DEBUG] expected: ", expected)
    66  	assert.Equal(t, expected, string(jsonSchema))
    67  }
    68  
    69  func TestStructOfPrimitivesSchema(t *testing.T) {
    70  	type Foo struct {
    71  		IntVal   int   `json:"int_val"`
    72  		Int8Val  int8  `json:"int8_val"`
    73  		Int16Val int16 `json:"int16_val"`
    74  		Int32Val int32 `json:"int32_val"`
    75  		Int64Val int64 `json:"int64_val"`
    76  
    77  		UIntVal   uint   `json:"uint_val"`
    78  		Uint8Val  uint8  `json:"uint8_val"`
    79  		Uint16Val uint16 `json:"uint16_val"`
    80  		Uint32Val uint32 `json:"uint32_val"`
    81  		Uint64Val uint64 `json:"uint64_val"`
    82  
    83  		Float32Val float32 `json:"float32_val"`
    84  		Float64Val float64 `json:"float64_val"`
    85  
    86  		StringVal string `json:"string_val"`
    87  
    88  		BoolVal bool `json:"bool_val"`
    89  	}
    90  
    91  	elem := Foo{}
    92  
    93  	schema, err := New(reflect.TypeOf(elem), nil)
    94  	assert.NoError(t, err)
    95  
    96  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
    97  	assert.NoError(t, err)
    98  
    99  	expected :=
   100  		`{
   101  			"type": "object",
   102  			"properties": {
   103  				"bool_val": {
   104  					"type": "boolean"
   105  				},
   106  				"float32_val": {
   107  					"type": "number"
   108  				},
   109  				"float64_val": {
   110  					"type": "number"
   111  				},
   112  				"int16_val": {
   113  					"type": "number"
   114  				},
   115  				"int32_val": {
   116  					"type": "number"
   117  				},
   118  				"int64_val": {
   119  					"type": "number"
   120  				},
   121  				"int8_val": {
   122  					"type": "number"
   123  				},
   124  				"int_val": {
   125  					"type": "number"
   126  				},
   127  				"string_val": {
   128  					"type": "string"
   129  				},
   130  				"uint16_val": {
   131  					"type": "number"
   132  				},
   133  				"uint32_val": {
   134  					"type": "number"
   135  				},
   136  				"uint64_val": {
   137  					"type": "number"
   138  				},
   139  				"uint8_val": {
   140  					"type": "number"
   141  				},
   142  				"uint_val": {
   143  					"type": "number"
   144  				}
   145  			},
   146  			"additionalProperties": false,
   147  			"required": [
   148  				"int_val",
   149  				"int8_val",
   150  				"int16_val",
   151  				"int32_val",
   152  				"int64_val",
   153  				"uint_val",
   154  				"uint8_val",
   155  				"uint16_val",
   156  				"uint32_val",
   157  				"uint64_val",
   158  				"float32_val",
   159  				"float64_val",
   160  				"string_val",
   161  				"bool_val"
   162  			]
   163  		}`
   164  
   165  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   166  	t.Log("[DEBUG] expected: ", expected)
   167  	assert.Equal(t, expected, string(jsonSchema))
   168  }
   169  
   170  func TestStructOfStructsSchema(t *testing.T) {
   171  	type Bar struct {
   172  		A int    `json:"a"`
   173  		B string `json:"b,string"`
   174  	}
   175  
   176  	type Foo struct {
   177  		Bar Bar `json:"bar"`
   178  	}
   179  
   180  	type MyStruct struct {
   181  		Foo Foo `json:"foo"`
   182  	}
   183  
   184  	elem := MyStruct{}
   185  
   186  	schema, err := New(reflect.TypeOf(elem), nil)
   187  	assert.NoError(t, err)
   188  
   189  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   190  	assert.NoError(t, err)
   191  
   192  	expected :=
   193  		`{
   194  			"type": "object",
   195  			"properties": {
   196  				"foo": {
   197  					"type": "object",
   198  					"properties": {
   199  						"bar": {
   200  							"type": "object",
   201  							"properties": {
   202  								"a": {
   203  									"type": "number"
   204  								},
   205  								"b": {
   206  									"type": "string"
   207  								}
   208  							},
   209  							"additionalProperties": false,
   210  							"required": [
   211  								"a",
   212  								"b"
   213  							]
   214  						}
   215  					},
   216  					"additionalProperties": false,
   217  					"required": [
   218  						"bar"
   219  					]
   220  				}
   221  			},
   222  			"additionalProperties": false,
   223  			"required": [
   224  				"foo"
   225  			]
   226  		}`
   227  
   228  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   229  	t.Log("[DEBUG] expected: ", expected)
   230  	assert.Equal(t, expected, string(jsonSchema))
   231  }
   232  
   233  func TestStructOfMapsSchema(t *testing.T) {
   234  	type Bar struct {
   235  		MyMap map[string]int `json:"my_map"`
   236  	}
   237  
   238  	type Foo struct {
   239  		Bar Bar `json:"bar"`
   240  	}
   241  
   242  	elem := Foo{}
   243  
   244  	schema, err := New(reflect.TypeOf(elem), nil)
   245  	assert.NoError(t, err)
   246  
   247  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   248  	assert.NoError(t, err)
   249  
   250  	expected :=
   251  		`{
   252  			"type": "object",
   253  			"properties": {
   254  				"bar": {
   255  					"type": "object",
   256  					"properties": {
   257  						"my_map": {
   258  							"type": "object",
   259  							"additionalProperties": {
   260  								"type": "number"
   261  							}
   262  						}
   263  					},
   264  					"additionalProperties": false,
   265  					"required": [
   266  						"my_map"
   267  					]
   268  				}
   269  			},
   270  			"additionalProperties": false,
   271  			"required": [
   272  				"bar"
   273  			]
   274  		}`
   275  
   276  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   277  	t.Log("[DEBUG] expected: ", expected)
   278  	assert.Equal(t, expected, string(jsonSchema))
   279  }
   280  
   281  func TestStructOfSliceSchema(t *testing.T) {
   282  	type Bar struct {
   283  		MySlice []string `json:"my_slice"`
   284  	}
   285  
   286  	type Foo struct {
   287  		Bar Bar `json:"bar"`
   288  	}
   289  
   290  	elem := Foo{}
   291  
   292  	schema, err := New(reflect.TypeOf(elem), nil)
   293  	assert.NoError(t, err)
   294  
   295  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   296  	assert.NoError(t, err)
   297  
   298  	expected :=
   299  		`{
   300  			"type": "object",
   301  			"properties": {
   302  				"bar": {
   303  					"type": "object",
   304  					"properties": {
   305  						"my_slice": {
   306  							"type": "array",
   307  							"items": {
   308  								"type": "string"
   309  							}
   310  						}
   311  					},
   312  					"additionalProperties": false,
   313  					"required": [
   314  						"my_slice"
   315  					]
   316  				}
   317  			},
   318  			"additionalProperties": false,
   319  			"required": [
   320  				"bar"
   321  			]
   322  		}`
   323  
   324  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   325  	t.Log("[DEBUG] expected: ", expected)
   326  	assert.Equal(t, expected, string(jsonSchema))
   327  }
   328  
   329  func TestMapOfPrimitivesSchema(t *testing.T) {
   330  	var elem map[string]int
   331  
   332  	schema, err := New(reflect.TypeOf(elem), nil)
   333  	assert.NoError(t, err)
   334  
   335  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   336  	assert.NoError(t, err)
   337  
   338  	expected :=
   339  		`{
   340  			"type": "object",
   341  			"additionalProperties": {
   342  				"type": "number"
   343  			}
   344  		}`
   345  
   346  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   347  	t.Log("[DEBUG] expected: ", expected)
   348  	assert.Equal(t, expected, string(jsonSchema))
   349  }
   350  
   351  func TestMapOfStructSchema(t *testing.T) {
   352  	type Foo struct {
   353  		MyInt int `json:"my_int"`
   354  	}
   355  
   356  	var elem map[string]Foo
   357  
   358  	schema, err := New(reflect.TypeOf(elem), nil)
   359  	assert.NoError(t, err)
   360  
   361  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   362  	assert.NoError(t, err)
   363  
   364  	expected :=
   365  		`{
   366  			"type": "object",
   367  			"additionalProperties": {
   368  				"type": "object",
   369  				"properties": {
   370  					"my_int": {
   371  						"type": "number"
   372  					}
   373  				},
   374  				"additionalProperties": false,
   375  				"required": [
   376  					"my_int"
   377  				]
   378  			}
   379  		}`
   380  
   381  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   382  	t.Log("[DEBUG] expected: ", expected)
   383  	assert.Equal(t, expected, string(jsonSchema))
   384  }
   385  
   386  func TestMapOfMapSchema(t *testing.T) {
   387  	var elem map[string]map[string]int
   388  
   389  	schema, err := New(reflect.TypeOf(elem), nil)
   390  	assert.NoError(t, err)
   391  
   392  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   393  	assert.NoError(t, err)
   394  
   395  	expected :=
   396  		`{
   397  			"type": "object",
   398  			"additionalProperties": {
   399  				"type": "object",
   400  				"additionalProperties": {
   401  					"type": "number"
   402  				}
   403  			}
   404  		}`
   405  
   406  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   407  	t.Log("[DEBUG] expected: ", expected)
   408  	assert.Equal(t, expected, string(jsonSchema))
   409  }
   410  
   411  func TestMapOfSliceSchema(t *testing.T) {
   412  	var elem map[string][]string
   413  
   414  	schema, err := New(reflect.TypeOf(elem), nil)
   415  	assert.NoError(t, err)
   416  
   417  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   418  	assert.NoError(t, err)
   419  
   420  	expected :=
   421  		`{
   422  			"type": "object",
   423  			"additionalProperties": {
   424  				"type": "array",
   425  				"items": {
   426  					"type": "string"
   427  				}
   428  			}
   429  		}`
   430  
   431  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   432  	t.Log("[DEBUG] expected: ", expected)
   433  	assert.Equal(t, expected, string(jsonSchema))
   434  }
   435  
   436  func TestSliceOfPrimitivesSchema(t *testing.T) {
   437  	var elem []float32
   438  
   439  	schema, err := New(reflect.TypeOf(elem), nil)
   440  	assert.NoError(t, err)
   441  
   442  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   443  	assert.NoError(t, err)
   444  
   445  	expected :=
   446  		`{
   447  			"type": "array",
   448  			"items": {
   449  				"type": "number"
   450  			}
   451  		}`
   452  
   453  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   454  	t.Log("[DEBUG] expected: ", expected)
   455  	assert.Equal(t, expected, string(jsonSchema))
   456  }
   457  
   458  func TestSliceOfSliceSchema(t *testing.T) {
   459  	var elem [][]string
   460  
   461  	schema, err := New(reflect.TypeOf(elem), nil)
   462  	assert.NoError(t, err)
   463  
   464  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   465  	assert.NoError(t, err)
   466  
   467  	expected :=
   468  		`{
   469  			"type": "array",
   470  			"items": {
   471  				"type": "array",
   472  				"items": {
   473  					"type": "string"
   474  				}
   475  			}
   476  		}`
   477  
   478  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   479  	t.Log("[DEBUG] expected: ", expected)
   480  	assert.Equal(t, expected, string(jsonSchema))
   481  }
   482  
   483  func TestSliceOfMapSchema(t *testing.T) {
   484  	var elem []map[string]int
   485  
   486  	schema, err := New(reflect.TypeOf(elem), nil)
   487  	assert.NoError(t, err)
   488  
   489  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   490  	assert.NoError(t, err)
   491  
   492  	expected :=
   493  		`{
   494  			"type": "array",
   495  			"items": {
   496  				"type": "object",
   497  				"additionalProperties": {
   498  					"type": "number"
   499  				}
   500  			}
   501  		}`
   502  
   503  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   504  	t.Log("[DEBUG] expected: ", expected)
   505  	assert.Equal(t, expected, string(jsonSchema))
   506  }
   507  
   508  func TestSliceOfStructSchema(t *testing.T) {
   509  	type Foo struct {
   510  		MyInt int `json:"my_int"`
   511  	}
   512  
   513  	var elem []Foo
   514  
   515  	schema, err := New(reflect.TypeOf(elem), nil)
   516  	assert.NoError(t, err)
   517  
   518  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   519  	assert.NoError(t, err)
   520  
   521  	expected :=
   522  		`{
   523  			"type": "array",
   524  			"items": {
   525  				"type": "object",
   526  				"properties": {
   527  					"my_int": {
   528  						"type": "number"
   529  					}
   530  				},
   531  				"additionalProperties": false,
   532  				"required": [
   533  					"my_int"
   534  				]
   535  			}
   536  		}`
   537  
   538  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   539  	t.Log("[DEBUG] expected: ", expected)
   540  	assert.Equal(t, expected, string(jsonSchema))
   541  }
   542  
   543  func TestEmbeddedStructSchema(t *testing.T) {
   544  	type Location struct {
   545  		Country string `json:"country"`
   546  		State   string `json:"state,omitempty"`
   547  	}
   548  
   549  	type Person struct {
   550  		Name string   `json:"name"`
   551  		Age  int      `json:"age,omitempty"`
   552  		Home Location `json:"home"`
   553  	}
   554  
   555  	type Plot struct {
   556  		Events map[string]Person `json:"events"`
   557  	}
   558  
   559  	type Story struct {
   560  		Plot Plot `json:"plot"`
   561  		*Person
   562  		Location
   563  	}
   564  
   565  	elem := Story{}
   566  
   567  	schema, err := New(reflect.TypeOf(elem), nil)
   568  	assert.NoError(t, err)
   569  
   570  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   571  	assert.NoError(t, err)
   572  
   573  	expected :=
   574  		`{
   575  			"type": "object",
   576  			"properties": {
   577  				"age": {
   578  					"type": "number"
   579  				},
   580  				"country": {
   581  					"type": "string"
   582  				},
   583  				"home": {
   584  					"type": "object",
   585  					"properties": {
   586  						"country": {
   587  							"type": "string"
   588  						},
   589  						"state": {
   590  							"type": "string"
   591  						}
   592  					},
   593  					"additionalProperties": false,
   594  					"required": [
   595  						"country"
   596  					]
   597  				},
   598  				"name": {
   599  					"type": "string"
   600  				},
   601  				"plot": {
   602  					"type": "object",
   603  					"properties": {
   604  						"events": {
   605  							"type": "object",
   606  							"additionalProperties": {
   607  								"type": "object",
   608  								"properties": {
   609  									"age": {
   610  										"type": "number"
   611  									},
   612  									"home": {
   613  										"type": "object",
   614  										"properties": {
   615  											"country": {
   616  												"type": "string"
   617  											},
   618  											"state": {
   619  												"type": "string"
   620  											}
   621  										},
   622  										"additionalProperties": false,
   623  										"required": [
   624  											"country"
   625  										]
   626  									},
   627  									"name": {
   628  										"type": "string"
   629  									}
   630  								},
   631  								"additionalProperties": false,
   632  								"required": [
   633  									"name",
   634  									"home"
   635  								]
   636  							}
   637  						}
   638  					},
   639  					"additionalProperties": false,
   640  					"required": [
   641  						"events"
   642  					]
   643  				},
   644  				"state": {
   645  					"type": "string"
   646  				}
   647  			},
   648  			"additionalProperties": false,
   649  			"required": [
   650  				"plot",
   651  				"name",
   652  				"home",
   653  				"country"
   654  			]
   655  		}`
   656  
   657  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   658  	t.Log("[DEBUG] expected: ", expected)
   659  	assert.Equal(t, expected, string(jsonSchema))
   660  }
   661  
   662  func TestErrorWithTrace(t *testing.T) {
   663  	tracker := newTracker()
   664  	dummyType := reflect.TypeOf(struct{}{})
   665  	err := tracker.errWithTrace("with empty trace", "root")
   666  	assert.ErrorContains(t, err, "with empty trace. traversal trace: root")
   667  
   668  	tracker.push(dummyType, "resources")
   669  	err = tracker.errWithTrace("with depth = 1", "root")
   670  	assert.ErrorContains(t, err, "with depth = 1. traversal trace: root -> resources")
   671  
   672  	tracker.push(dummyType, "pipelines")
   673  	tracker.push(dummyType, "datasets")
   674  	err = tracker.errWithTrace("with depth = 4", "root")
   675  	assert.ErrorContains(t, err, "with depth = 4. traversal trace: root -> resources -> pipelines -> datasets")
   676  }
   677  
   678  func TestNonAnnotatedFieldsAreSkipped(t *testing.T) {
   679  	type MyStruct struct {
   680  		Foo string
   681  		Bar int `json:"bar"`
   682  	}
   683  
   684  	elem := MyStruct{}
   685  
   686  	schema, err := New(reflect.TypeOf(elem), nil)
   687  	require.NoError(t, err)
   688  
   689  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   690  	assert.NoError(t, err)
   691  
   692  	expectedSchema :=
   693  		`{
   694  			"type": "object",
   695  			"properties": {
   696  				"bar": {
   697  					"type": "number"
   698  				}
   699  			},
   700  			"additionalProperties": false,
   701  			"required": [
   702  				"bar"
   703  			]
   704  		}`
   705  
   706  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   707  	t.Log("[DEBUG] expected: ", expectedSchema)
   708  
   709  	assert.Equal(t, expectedSchema, string(jsonSchema))
   710  }
   711  
   712  func TestDashFieldsAreSkipped(t *testing.T) {
   713  	type MyStruct struct {
   714  		Foo string `json:"-"`
   715  		Bar int    `json:"bar"`
   716  	}
   717  
   718  	elem := MyStruct{}
   719  
   720  	schema, err := New(reflect.TypeOf(elem), nil)
   721  	require.NoError(t, err)
   722  
   723  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   724  	assert.NoError(t, err)
   725  
   726  	expectedSchema :=
   727  		`{
   728  			"type": "object",
   729  			"properties": {
   730  				"bar": {
   731  					"type": "number"
   732  				}
   733  			},
   734  			"additionalProperties": false,
   735  			"required": [
   736  				"bar"
   737  			]
   738  		}`
   739  
   740  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   741  	t.Log("[DEBUG] expected: ", expectedSchema)
   742  
   743  	assert.Equal(t, expectedSchema, string(jsonSchema))
   744  }
   745  
   746  func TestPointerInStructSchema(t *testing.T) {
   747  
   748  	type Bar struct {
   749  		PtrVal2 *int `json:"ptr_val2"`
   750  	}
   751  
   752  	type Foo struct {
   753  		PtrInt    *int    `json:"ptr_int"`
   754  		PtrString *string `json:"ptr_string"`
   755  		FloatVal  float32 `json:"float_val"`
   756  		PtrBar    *Bar    `json:"ptr_bar"`
   757  		Bar       *Bar    `json:"bar"`
   758  	}
   759  
   760  	elem := Foo{}
   761  
   762  	schema, err := New(reflect.TypeOf(elem), nil)
   763  	require.NoError(t, err)
   764  
   765  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   766  	assert.NoError(t, err)
   767  
   768  	expectedSchema :=
   769  		`{
   770  			"type": "object",
   771  			"properties": {
   772  				"bar": {
   773  					"type": "object",
   774  					"properties": {
   775  						"ptr_val2": {
   776  							"type": "number"
   777  						}
   778  					},
   779  					"additionalProperties": false,
   780  					"required": [
   781  						"ptr_val2"
   782  					]
   783  				},
   784  				"float_val": {
   785  					"type": "number"
   786  				},
   787  				"ptr_bar": {
   788  					"type": "object",
   789  					"properties": {
   790  						"ptr_val2": {
   791  							"type": "number"
   792  						}
   793  					},
   794  					"additionalProperties": false,
   795  					"required": [
   796  						"ptr_val2"
   797  					]
   798  				},
   799  				"ptr_int": {
   800  					"type": "number"
   801  				},
   802  				"ptr_string": {
   803  					"type": "string"
   804  				}
   805  			},
   806  			"additionalProperties": false,
   807  			"required": [
   808  				"ptr_int",
   809  				"ptr_string",
   810  				"float_val",
   811  				"ptr_bar",
   812  				"bar"
   813  			]
   814  		}`
   815  
   816  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   817  	t.Log("[DEBUG] expected: ", expectedSchema)
   818  
   819  	assert.Equal(t, expectedSchema, string(jsonSchema))
   820  }
   821  
   822  func TestGenericSchema(t *testing.T) {
   823  	type Person struct {
   824  		Name string `json:"name"`
   825  		Age  int    `json:"age,omitempty"`
   826  	}
   827  
   828  	type Plot struct {
   829  		Stakes  []string          `json:"stakes"`
   830  		Deaths  []Person          `json:"deaths"`
   831  		Murders map[string]Person `json:"murders"`
   832  	}
   833  
   834  	type Wedding struct {
   835  		Hidden string `json:","`
   836  		Groom  Person `json:"groom"`
   837  		Bride  Person `json:"bride"`
   838  		Plots  []Plot `json:"plots"`
   839  	}
   840  
   841  	type Story struct {
   842  		Hero     *Person   `json:"hero"`
   843  		Villian  Person    `json:"villian,omitempty"`
   844  		Weddings []Wedding `json:"weddings"`
   845  	}
   846  
   847  	elem := Story{}
   848  
   849  	schema, err := New(reflect.TypeOf(elem), nil)
   850  	assert.NoError(t, err)
   851  
   852  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
   853  	assert.NoError(t, err)
   854  
   855  	expected :=
   856  		`{
   857  			"type": "object",
   858  			"properties": {
   859  				"hero": {
   860  					"type": "object",
   861  					"properties": {
   862  						"age": {
   863  							"type": "number"
   864  						},
   865  						"name": {
   866  							"type": "string"
   867  						}
   868  					},
   869  					"additionalProperties": false,
   870  					"required": [
   871  						"name"
   872  					]
   873  				},
   874  				"villian": {
   875  					"type": "object",
   876  					"properties": {
   877  						"age": {
   878  							"type": "number"
   879  						},
   880  						"name": {
   881  							"type": "string"
   882  						}
   883  					},
   884  					"additionalProperties": false,
   885  					"required": [
   886  						"name"
   887  					]
   888  				},
   889  				"weddings": {
   890  					"type": "array",
   891  					"items": {
   892  						"type": "object",
   893  						"properties": {
   894  							"bride": {
   895  								"type": "object",
   896  								"properties": {
   897  									"age": {
   898  										"type": "number"
   899  									},
   900  									"name": {
   901  										"type": "string"
   902  									}
   903  								},
   904  								"additionalProperties": false,
   905  								"required": [
   906  									"name"
   907  								]
   908  							},
   909  							"groom": {
   910  								"type": "object",
   911  								"properties": {
   912  									"age": {
   913  										"type": "number"
   914  									},
   915  									"name": {
   916  										"type": "string"
   917  									}
   918  								},
   919  								"additionalProperties": false,
   920  								"required": [
   921  									"name"
   922  								]
   923  							},
   924  							"plots": {
   925  								"type": "array",
   926  								"items": {
   927  									"type": "object",
   928  									"properties": {
   929  										"deaths": {
   930  											"type": "array",
   931  											"items": {
   932  												"type": "object",
   933  												"properties": {
   934  													"age": {
   935  														"type": "number"
   936  													},
   937  													"name": {
   938  														"type": "string"
   939  													}
   940  												},
   941  												"additionalProperties": false,
   942  												"required": [
   943  													"name"
   944  												]
   945  											}
   946  										},
   947  										"murders": {
   948  											"type": "object",
   949  											"additionalProperties": {
   950  												"type": "object",
   951  												"properties": {
   952  													"age": {
   953  														"type": "number"
   954  													},
   955  													"name": {
   956  														"type": "string"
   957  													}
   958  												},
   959  												"additionalProperties": false,
   960  												"required": [
   961  													"name"
   962  												]
   963  											}
   964  										},
   965  										"stakes": {
   966  											"type": "array",
   967  											"items": {
   968  												"type": "string"
   969  											}
   970  										}
   971  									},
   972  									"additionalProperties": false,
   973  									"required": [
   974  										"stakes",
   975  										"deaths",
   976  										"murders"
   977  									]
   978  								}
   979  							}
   980  						},
   981  						"additionalProperties": false,
   982  						"required": [
   983  							"groom",
   984  							"bride",
   985  							"plots"
   986  						]
   987  					}
   988  				}
   989  			},
   990  			"additionalProperties": false,
   991  			"required": [
   992  				"hero",
   993  				"weddings"
   994  			]
   995  		}`
   996  
   997  	t.Log("[DEBUG] actual: ", string(jsonSchema))
   998  	t.Log("[DEBUG] expected: ", expected)
   999  	assert.Equal(t, expected, string(jsonSchema))
  1000  }
  1001  
  1002  func TestFieldsWithoutOmitEmptyAreRequired(t *testing.T) {
  1003  
  1004  	type Papaya struct {
  1005  		A int    `json:"a,string,omitempty"`
  1006  		B string `json:"b"`
  1007  	}
  1008  
  1009  	type MyStruct struct {
  1010  		Foo    string  `json:"-,omitempty"`
  1011  		Bar    int     `json:"bar"`
  1012  		Apple  int     `json:"apple,omitempty"`
  1013  		Mango  int     `json:",omitempty"`
  1014  		Guava  int     `json:","`
  1015  		Papaya *Papaya `json:"papaya,"`
  1016  	}
  1017  
  1018  	elem := MyStruct{}
  1019  
  1020  	schema, err := New(reflect.TypeOf(elem), nil)
  1021  	require.NoError(t, err)
  1022  
  1023  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
  1024  	assert.NoError(t, err)
  1025  
  1026  	expectedSchema :=
  1027  		`{
  1028  			"type": "object",
  1029  			"properties": {
  1030  				"apple": {
  1031  					"type": "number"
  1032  				},
  1033  				"bar": {
  1034  					"type": "number"
  1035  				},
  1036  				"papaya": {
  1037  					"type": "object",
  1038  					"properties": {
  1039  						"a": {
  1040  							"type": "number"
  1041  						},
  1042  						"b": {
  1043  							"type": "string"
  1044  						}
  1045  					},
  1046  					"additionalProperties": false,
  1047  					"required": [
  1048  						"b"
  1049  					]
  1050  				}
  1051  			},
  1052  			"additionalProperties": false,
  1053  			"required": [
  1054  				"bar",
  1055  				"papaya"
  1056  			]
  1057  		}`
  1058  
  1059  	t.Log("[DEBUG] actual: ", string(jsonSchema))
  1060  	t.Log("[DEBUG] expected: ", expectedSchema)
  1061  
  1062  	assert.Equal(t, expectedSchema, string(jsonSchema))
  1063  }
  1064  
  1065  func TestDocIngestionForObject(t *testing.T) {
  1066  	docs := &Docs{
  1067  		Description: "docs for root",
  1068  		Properties: map[string]*Docs{
  1069  			"my_struct": {
  1070  				Description: "docs for my struct",
  1071  				Properties: map[string]*Docs{
  1072  					"a": {
  1073  						Description: "docs for a",
  1074  					},
  1075  					"c": {
  1076  						Description: "docs for c which does not exist on my_struct",
  1077  					},
  1078  				},
  1079  			},
  1080  		},
  1081  	}
  1082  
  1083  	type MyStruct struct {
  1084  		A string `json:"a"`
  1085  		B int    `json:"b"`
  1086  	}
  1087  
  1088  	type Root struct {
  1089  		MyStruct *MyStruct `json:"my_struct"`
  1090  	}
  1091  
  1092  	elem := Root{}
  1093  
  1094  	schema, err := New(reflect.TypeOf(elem), docs)
  1095  	require.NoError(t, err)
  1096  
  1097  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
  1098  	assert.NoError(t, err)
  1099  
  1100  	expectedSchema :=
  1101  		`{
  1102  			"type": "object",
  1103  			"description": "docs for root",
  1104  			"properties": {
  1105  				"my_struct": {
  1106  					"type": "object",
  1107  					"description": "docs for my struct",
  1108  					"properties": {
  1109  						"a": {
  1110  							"type": "string",
  1111  							"description": "docs for a"
  1112  						},
  1113  						"b": {
  1114  							"type": "number"
  1115  						}
  1116  					},
  1117  					"additionalProperties": false,
  1118  					"required": [
  1119  						"a",
  1120  						"b"
  1121  					]
  1122  				}
  1123  			},
  1124  			"additionalProperties": false,
  1125  			"required": [
  1126  				"my_struct"
  1127  			]
  1128  		}`
  1129  
  1130  	t.Log("[DEBUG] actual: ", string(jsonSchema))
  1131  	t.Log("[DEBUG] expected: ", expectedSchema)
  1132  
  1133  	assert.Equal(t, expectedSchema, string(jsonSchema))
  1134  }
  1135  
  1136  func TestDocIngestionForSlice(t *testing.T) {
  1137  	docs := &Docs{
  1138  		Description: "docs for root",
  1139  		Properties: map[string]*Docs{
  1140  			"my_slice": {
  1141  				Description: "docs for my slice",
  1142  				Items: &Docs{
  1143  					Properties: map[string]*Docs{
  1144  						"guava": {
  1145  							Description: "docs for guava",
  1146  						},
  1147  						"pineapple": {
  1148  							Description: "docs for pineapple",
  1149  						},
  1150  						"watermelon": {
  1151  							Description: "docs for watermelon which does not exist in schema",
  1152  						},
  1153  					},
  1154  				},
  1155  			},
  1156  		},
  1157  	}
  1158  
  1159  	type Bar struct {
  1160  		Guava     int `json:"guava"`
  1161  		Pineapple int `json:"pineapple"`
  1162  	}
  1163  
  1164  	type Root struct {
  1165  		MySlice []Bar `json:"my_slice"`
  1166  	}
  1167  
  1168  	elem := Root{}
  1169  
  1170  	schema, err := New(reflect.TypeOf(elem), docs)
  1171  	require.NoError(t, err)
  1172  
  1173  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
  1174  	assert.NoError(t, err)
  1175  
  1176  	expectedSchema :=
  1177  		`{
  1178  			"type": "object",
  1179  			"description": "docs for root",
  1180  			"properties": {
  1181  				"my_slice": {
  1182  					"type": "array",
  1183  					"description": "docs for my slice",
  1184  					"items": {
  1185  						"type": "object",
  1186  						"properties": {
  1187  							"guava": {
  1188  								"type": "number",
  1189  								"description": "docs for guava"
  1190  							},
  1191  							"pineapple": {
  1192  								"type": "number",
  1193  								"description": "docs for pineapple"
  1194  							}
  1195  						},
  1196  						"additionalProperties": false,
  1197  						"required": [
  1198  							"guava",
  1199  							"pineapple"
  1200  						]
  1201  					}
  1202  				}
  1203  			},
  1204  			"additionalProperties": false,
  1205  			"required": [
  1206  				"my_slice"
  1207  			]
  1208  		}`
  1209  
  1210  	t.Log("[DEBUG] actual: ", string(jsonSchema))
  1211  	t.Log("[DEBUG] expected: ", expectedSchema)
  1212  
  1213  	assert.Equal(t, expectedSchema, string(jsonSchema))
  1214  }
  1215  
  1216  func TestDocIngestionForMap(t *testing.T) {
  1217  	docs := &Docs{
  1218  		Description: "docs for root",
  1219  		Properties: map[string]*Docs{
  1220  			"my_map": {
  1221  				Description: "docs for my map",
  1222  				AdditionalProperties: &Docs{
  1223  					Properties: map[string]*Docs{
  1224  						"apple": {
  1225  							Description: "docs for apple",
  1226  						},
  1227  						"mango": {
  1228  							Description: "docs for mango",
  1229  						},
  1230  						"watermelon": {
  1231  							Description: "docs for watermelon which does not exist in schema",
  1232  						},
  1233  						"papaya": {
  1234  							Description: "docs for papaya which does not exist in schema",
  1235  						},
  1236  					},
  1237  				},
  1238  			},
  1239  		},
  1240  	}
  1241  
  1242  	type Foo struct {
  1243  		Apple int `json:"apple"`
  1244  		Mango int `json:"mango"`
  1245  	}
  1246  
  1247  	type Root struct {
  1248  		MyMap map[string]*Foo `json:"my_map"`
  1249  	}
  1250  
  1251  	elem := Root{}
  1252  
  1253  	schema, err := New(reflect.TypeOf(elem), docs)
  1254  	require.NoError(t, err)
  1255  
  1256  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
  1257  	assert.NoError(t, err)
  1258  
  1259  	expectedSchema :=
  1260  		`{
  1261  			"type": "object",
  1262  			"description": "docs for root",
  1263  			"properties": {
  1264  				"my_map": {
  1265  					"type": "object",
  1266  					"description": "docs for my map",
  1267  					"additionalProperties": {
  1268  						"type": "object",
  1269  						"properties": {
  1270  							"apple": {
  1271  								"type": "number",
  1272  								"description": "docs for apple"
  1273  							},
  1274  							"mango": {
  1275  								"type": "number",
  1276  								"description": "docs for mango"
  1277  							}
  1278  						},
  1279  						"additionalProperties": false,
  1280  						"required": [
  1281  							"apple",
  1282  							"mango"
  1283  						]
  1284  					}
  1285  				}
  1286  			},
  1287  			"additionalProperties": false,
  1288  			"required": [
  1289  				"my_map"
  1290  			]
  1291  		}`
  1292  
  1293  	t.Log("[DEBUG] actual: ", string(jsonSchema))
  1294  	t.Log("[DEBUG] expected: ", expectedSchema)
  1295  
  1296  	assert.Equal(t, expectedSchema, string(jsonSchema))
  1297  }
  1298  
  1299  func TestDocIngestionForTopLevelPrimitive(t *testing.T) {
  1300  	docs := &Docs{
  1301  		Description: "docs for root",
  1302  		Properties: map[string]*Docs{
  1303  			"my_val": {
  1304  				Description: "docs for my val",
  1305  			},
  1306  		},
  1307  	}
  1308  
  1309  	type Root struct {
  1310  		MyVal int `json:"my_val"`
  1311  	}
  1312  
  1313  	elem := Root{}
  1314  
  1315  	schema, err := New(reflect.TypeOf(elem), docs)
  1316  	require.NoError(t, err)
  1317  
  1318  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
  1319  	assert.NoError(t, err)
  1320  
  1321  	expectedSchema :=
  1322  		`{
  1323  			"type": "object",
  1324  			"description": "docs for root",
  1325  			"properties": {
  1326  				"my_val": {
  1327  					"type": "number",
  1328  					"description": "docs for my val"
  1329  				}
  1330  			},
  1331  			"additionalProperties": false,
  1332  			"required": [
  1333  				"my_val"
  1334  			]
  1335  		}`
  1336  
  1337  	t.Log("[DEBUG] actual: ", string(jsonSchema))
  1338  	t.Log("[DEBUG] expected: ", expectedSchema)
  1339  
  1340  	assert.Equal(t, expectedSchema, string(jsonSchema))
  1341  }
  1342  
  1343  func TestErrorOnMapWithoutStringKey(t *testing.T) {
  1344  	type Foo struct {
  1345  		Bar map[int]string `json:"bar"`
  1346  	}
  1347  	elem := Foo{}
  1348  	_, err := New(reflect.TypeOf(elem), nil)
  1349  	assert.ErrorContains(t, err, "only strings map keys are valid. key type: int")
  1350  }
  1351  
  1352  func TestErrorIfStructRefersToItself(t *testing.T) {
  1353  	type Foo struct {
  1354  		MyFoo *Foo `json:"my_foo"`
  1355  	}
  1356  
  1357  	elem := Foo{}
  1358  	_, err := New(reflect.TypeOf(elem), nil)
  1359  	assert.ErrorContains(t, err, "cycle detected. traversal trace: root -> my_foo")
  1360  }
  1361  
  1362  func TestErrorIfStructHasLoop(t *testing.T) {
  1363  	type Apple struct {
  1364  		MyVal   int `json:"my_val"`
  1365  		MyMango struct {
  1366  			MyGuava struct {
  1367  				MyPapaya struct {
  1368  					MyApple *Apple `json:"my_apple"`
  1369  				} `json:"my_papaya"`
  1370  			} `json:"my_guava"`
  1371  		} `json:"my_mango"`
  1372  	}
  1373  
  1374  	elem := Apple{}
  1375  	_, err := New(reflect.TypeOf(elem), nil)
  1376  	assert.ErrorContains(t, err, "cycle detected. traversal trace: root -> my_mango -> my_guava -> my_papaya -> my_apple")
  1377  }
  1378  
  1379  func TestInterfaceGeneratesEmptySchema(t *testing.T) {
  1380  	type Foo struct {
  1381  		Apple int         `json:"apple"`
  1382  		Mango interface{} `json:"mango"`
  1383  	}
  1384  
  1385  	elem := Foo{}
  1386  
  1387  	schema, err := New(reflect.TypeOf(elem), nil)
  1388  	assert.NoError(t, err)
  1389  
  1390  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
  1391  	assert.NoError(t, err)
  1392  
  1393  	expected :=
  1394  		`{
  1395  			"type": "object",
  1396  			"properties": {
  1397  				"apple": {
  1398  					"type": "number"
  1399  				},
  1400  				"mango": {}
  1401  			},
  1402  			"additionalProperties": false,
  1403  			"required": [
  1404  				"apple",
  1405  				"mango"
  1406  			]
  1407  		}`
  1408  
  1409  	t.Log("[DEBUG] actual: ", string(jsonSchema))
  1410  	t.Log("[DEBUG] expected: ", expected)
  1411  	assert.Equal(t, expected, string(jsonSchema))
  1412  }
  1413  
  1414  func TestBundleReadOnlytag(t *testing.T) {
  1415  	type Pokemon struct {
  1416  		Pikachu string `json:"pikachu" bundle:"readonly"`
  1417  		Raichu  string `json:"raichu"`
  1418  	}
  1419  
  1420  	type Foo struct {
  1421  		Pokemon *Pokemon `json:"pokemon"`
  1422  		Apple   int      `json:"apple"`
  1423  		Mango   string   `json:"mango" bundle:"readonly"`
  1424  	}
  1425  
  1426  	elem := Foo{}
  1427  
  1428  	schema, err := New(reflect.TypeOf(elem), nil)
  1429  	assert.NoError(t, err)
  1430  
  1431  	jsonSchema, err := json.MarshalIndent(schema, "		", "	")
  1432  	assert.NoError(t, err)
  1433  
  1434  	expected :=
  1435  		`{
  1436  			"type": "object",
  1437  			"properties": {
  1438  				"apple": {
  1439  					"type": "number"
  1440  				},
  1441  				"pokemon": {
  1442  					"type": "object",
  1443  					"properties": {
  1444  						"raichu": {
  1445  							"type": "string"
  1446  						}
  1447  					},
  1448  					"additionalProperties": false,
  1449  					"required": [
  1450  						"raichu"
  1451  					]
  1452  				}
  1453  			},
  1454  			"additionalProperties": false,
  1455  			"required": [
  1456  				"pokemon",
  1457  				"apple"
  1458  			]
  1459  		}`
  1460  
  1461  	t.Log("[DEBUG] actual: ", string(jsonSchema))
  1462  	t.Log("[DEBUG] expected: ", expected)
  1463  	assert.Equal(t, expected, string(jsonSchema))
  1464  }