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

     1  package schema
     2  
     3  import (
     4  	"encoding/json"
     5  	"testing"
     6  
     7  	"github.com/databricks/cli/libs/jsonschema"
     8  	"github.com/databricks/databricks-sdk-go/openapi"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  func TestReadSchemaForObject(t *testing.T) {
    14  	specString := `
    15  	{
    16  		"components": {
    17  			"schemas": {
    18  				"foo": {
    19  					"type": "number"
    20  				},
    21  				"fruits": {
    22  					"type": "object",
    23  					"description": "fruits that are cool",
    24  					"properties": {
    25  						"guava": {
    26  							"type": "string",
    27  							"description": "a guava for my schema"
    28  						},
    29  						"mango": {
    30  							"type": "object",
    31  							"description": "a mango for my schema",
    32  							"$ref": "#/components/schemas/mango"
    33  						}
    34  					}
    35  				},
    36  				"mango": {
    37  					"type": "object",
    38  					"properties": {
    39  						"foo": {
    40  							"$ref": "#/components/schemas/foo"
    41  						}
    42  					}
    43  				}
    44  			}
    45  		}
    46  	}
    47  	`
    48  	spec := &openapi.Specification{}
    49  	reader := &OpenapiReader{
    50  		OpenapiSpec: spec,
    51  		Memo:        make(map[string]*jsonschema.Schema),
    52  	}
    53  	err := json.Unmarshal([]byte(specString), spec)
    54  	require.NoError(t, err)
    55  
    56  	fruitsSchema, err := reader.readResolvedSchema("#/components/schemas/fruits")
    57  	require.NoError(t, err)
    58  
    59  	fruitsSchemaJson, err := json.MarshalIndent(fruitsSchema, "		", "	")
    60  	require.NoError(t, err)
    61  
    62  	expected := `{
    63  			"type": "object",
    64  			"description": "fruits that are cool",
    65  			"properties": {
    66  				"guava": {
    67  					"type": "string",
    68  					"description": "a guava for my schema"
    69  				},
    70  				"mango": {
    71  					"type": "object",
    72  					"description": "a mango for my schema",
    73  					"properties": {
    74  						"foo": {
    75  							"type": "number"
    76  						}
    77  					}
    78  				}
    79  			}
    80  		}`
    81  
    82  	t.Log("[DEBUG] actual: ", string(fruitsSchemaJson))
    83  	t.Log("[DEBUG] expected: ", expected)
    84  	assert.Equal(t, expected, string(fruitsSchemaJson))
    85  }
    86  
    87  func TestReadSchemaForArray(t *testing.T) {
    88  	specString := `
    89  	{
    90  		"components": {
    91  			"schemas": {
    92  				"fruits": {
    93  					"type": "object",
    94  					"description": "fruits that are cool",
    95  					"items": {
    96  						"description": "some papayas, because papayas are fruits too",
    97  						"$ref": "#/components/schemas/papaya"
    98  					}
    99  				},
   100  				"papaya": {
   101  					"type": "number"
   102  				}
   103  			}
   104  		}
   105  	}`
   106  	spec := &openapi.Specification{}
   107  	reader := &OpenapiReader{
   108  		OpenapiSpec: spec,
   109  		Memo:        make(map[string]*jsonschema.Schema),
   110  	}
   111  	err := json.Unmarshal([]byte(specString), spec)
   112  	require.NoError(t, err)
   113  
   114  	fruitsSchema, err := reader.readResolvedSchema("#/components/schemas/fruits")
   115  	require.NoError(t, err)
   116  
   117  	fruitsSchemaJson, err := json.MarshalIndent(fruitsSchema, "		", "	")
   118  	require.NoError(t, err)
   119  
   120  	expected := `{
   121  			"type": "object",
   122  			"description": "fruits that are cool",
   123  			"items": {
   124  				"type": "number",
   125  				"description": "some papayas, because papayas are fruits too"
   126  			}
   127  		}`
   128  
   129  	t.Log("[DEBUG] actual: ", string(fruitsSchemaJson))
   130  	t.Log("[DEBUG] expected: ", expected)
   131  	assert.Equal(t, expected, string(fruitsSchemaJson))
   132  }
   133  
   134  func TestReadSchemaForMap(t *testing.T) {
   135  	specString := `{
   136  		"components": {
   137  			"schemas": {
   138  				"fruits": {
   139  					"type": "object",
   140  					"description": "fruits that are meh",
   141  					"additionalProperties": {
   142  						"description": "watermelons. watermelons.",
   143  						"$ref": "#/components/schemas/watermelon"
   144  					}
   145  				},
   146  				"watermelon": {
   147  					"type": "number"
   148  				}
   149  			}
   150  		}
   151  	}`
   152  	spec := &openapi.Specification{}
   153  	reader := &OpenapiReader{
   154  		OpenapiSpec: spec,
   155  		Memo:        make(map[string]*jsonschema.Schema),
   156  	}
   157  	err := json.Unmarshal([]byte(specString), spec)
   158  	require.NoError(t, err)
   159  
   160  	fruitsSchema, err := reader.readResolvedSchema("#/components/schemas/fruits")
   161  	require.NoError(t, err)
   162  
   163  	fruitsSchemaJson, err := json.MarshalIndent(fruitsSchema, "		", "	")
   164  	require.NoError(t, err)
   165  
   166  	expected := `{
   167  			"type": "object",
   168  			"description": "fruits that are meh",
   169  			"additionalProperties": {
   170  				"type": "number",
   171  				"description": "watermelons. watermelons."
   172  			}
   173  		}`
   174  
   175  	t.Log("[DEBUG] actual: ", string(fruitsSchemaJson))
   176  	t.Log("[DEBUG] expected: ", expected)
   177  	assert.Equal(t, expected, string(fruitsSchemaJson))
   178  }
   179  
   180  func TestRootReferenceIsResolved(t *testing.T) {
   181  	specString := `{
   182  		"components": {
   183  			"schemas": {
   184  				"foo": {
   185  					"type": "object",
   186  					"description": "this description is ignored",
   187  					"properties": {
   188  						"abc": {
   189  							"type": "string"
   190  						}
   191  					}
   192  				},
   193  				"fruits": {
   194  					"type": "object",
   195  					"description": "foo fighters fighting fruits",
   196  					"$ref": "#/components/schemas/foo"
   197  				}
   198  			}
   199  		}
   200  	}`
   201  	spec := &openapi.Specification{}
   202  	reader := &OpenapiReader{
   203  		OpenapiSpec: spec,
   204  		Memo:        make(map[string]*jsonschema.Schema),
   205  	}
   206  	err := json.Unmarshal([]byte(specString), spec)
   207  	require.NoError(t, err)
   208  
   209  	schema, err := reader.readResolvedSchema("#/components/schemas/fruits")
   210  	require.NoError(t, err)
   211  	fruitsSchemaJson, err := json.MarshalIndent(schema, "		", "	")
   212  	require.NoError(t, err)
   213  
   214  	expected := `{
   215  			"type": "object",
   216  			"description": "foo fighters fighting fruits",
   217  			"properties": {
   218  				"abc": {
   219  					"type": "string"
   220  				}
   221  			}
   222  		}`
   223  
   224  	t.Log("[DEBUG] actual: ", string(fruitsSchemaJson))
   225  	t.Log("[DEBUG] expected: ", expected)
   226  	assert.Equal(t, expected, string(fruitsSchemaJson))
   227  }
   228  
   229  func TestSelfReferenceLoopErrors(t *testing.T) {
   230  	specString := `{
   231  		"components": {
   232  			"schemas": {
   233  				"foo": {
   234  					"type": "object",
   235  					"description": "this description is ignored",
   236  					"properties": {
   237  						"bar": {
   238  							"type": "object",
   239  							"$ref": "#/components/schemas/foo"
   240  						}
   241  					}
   242  				},
   243  				"fruits": {
   244  					"type": "object",
   245  					"description": "foo fighters fighting fruits",
   246  					"$ref": "#/components/schemas/foo"
   247  				}
   248  			}
   249  		}
   250  	}`
   251  	spec := &openapi.Specification{}
   252  	reader := &OpenapiReader{
   253  		OpenapiSpec: spec,
   254  		Memo:        make(map[string]*jsonschema.Schema),
   255  	}
   256  	err := json.Unmarshal([]byte(specString), spec)
   257  	require.NoError(t, err)
   258  
   259  	_, err = reader.readResolvedSchema("#/components/schemas/fruits")
   260  	assert.ErrorContains(t, err, "references loop detected. traversal trace:  -> #/components/schemas/fruits -> #/components/schemas/foo")
   261  }
   262  
   263  func TestCrossReferenceLoopErrors(t *testing.T) {
   264  	specString := `{
   265  		"components": {
   266  			"schemas": {
   267  				"foo": {
   268  					"type": "object",
   269  					"description": "this description is ignored",
   270  					"properties": {
   271  						"bar": {
   272  							"type": "object",
   273  							"$ref": "#/components/schemas/fruits"
   274  						}
   275  					}
   276  				},
   277  				"fruits": {
   278  					"type": "object",
   279  					"description": "foo fighters fighting fruits",
   280  					"$ref": "#/components/schemas/foo"
   281  				}
   282  			}
   283  		}
   284  	}`
   285  	spec := &openapi.Specification{}
   286  	reader := &OpenapiReader{
   287  		OpenapiSpec: spec,
   288  		Memo:        make(map[string]*jsonschema.Schema),
   289  	}
   290  	err := json.Unmarshal([]byte(specString), spec)
   291  	require.NoError(t, err)
   292  
   293  	_, err = reader.readResolvedSchema("#/components/schemas/fruits")
   294  	assert.ErrorContains(t, err, "references loop detected. traversal trace:  -> #/components/schemas/fruits -> #/components/schemas/foo")
   295  }
   296  
   297  func TestReferenceResolutionForMapInObject(t *testing.T) {
   298  	specString := `
   299  	{
   300  		"components": {
   301  			"schemas": {
   302  				"foo": {
   303  					"type": "number"
   304  				},
   305  				"fruits": {
   306  					"type": "object",
   307  					"description": "fruits that are cool",
   308  					"properties": {
   309  						"guava": {
   310  							"type": "string",
   311  							"description": "a guava for my schema"
   312  						},
   313  						"mangos": {
   314  							"type": "object",
   315  							"description": "multiple mangos",
   316  							"$ref": "#/components/schemas/mango"
   317  						}
   318  					}
   319  				},
   320  				"mango": {
   321  					"type": "object",
   322  					"additionalProperties": {
   323  						"description": "a single mango",
   324  						"$ref": "#/components/schemas/foo"
   325  					}
   326  				}
   327  			}
   328  		}
   329  	}`
   330  	spec := &openapi.Specification{}
   331  	reader := &OpenapiReader{
   332  		OpenapiSpec: spec,
   333  		Memo:        make(map[string]*jsonschema.Schema),
   334  	}
   335  	err := json.Unmarshal([]byte(specString), spec)
   336  	require.NoError(t, err)
   337  
   338  	fruitsSchema, err := reader.readResolvedSchema("#/components/schemas/fruits")
   339  	require.NoError(t, err)
   340  
   341  	fruitsSchemaJson, err := json.MarshalIndent(fruitsSchema, "		", "	")
   342  	require.NoError(t, err)
   343  
   344  	expected := `{
   345  			"type": "object",
   346  			"description": "fruits that are cool",
   347  			"properties": {
   348  				"guava": {
   349  					"type": "string",
   350  					"description": "a guava for my schema"
   351  				},
   352  				"mangos": {
   353  					"type": "object",
   354  					"description": "multiple mangos",
   355  					"additionalProperties": {
   356  						"type": "number",
   357  						"description": "a single mango"
   358  					}
   359  				}
   360  			}
   361  		}`
   362  
   363  	t.Log("[DEBUG] actual: ", string(fruitsSchemaJson))
   364  	t.Log("[DEBUG] expected: ", expected)
   365  	assert.Equal(t, expected, string(fruitsSchemaJson))
   366  }
   367  
   368  func TestReferenceResolutionForArrayInObject(t *testing.T) {
   369  	specString := `{
   370  		"components": {
   371  			"schemas": {
   372  				"foo": {
   373  					"type": "number"
   374  				},
   375  				"fruits": {
   376  					"type": "object",
   377  					"description": "fruits that are cool",
   378  					"properties": {
   379  						"guava": {
   380  							"type": "string",
   381  							"description": "a guava for my schema"
   382  						},
   383  						"mangos": {
   384  							"type": "object",
   385  							"description": "multiple mangos",
   386  							"$ref": "#/components/schemas/mango"
   387  						}
   388  					}
   389  				},
   390  				"mango": {
   391  					"type": "object",
   392  					"items": {
   393  						"description": "a single mango",
   394  						"$ref": "#/components/schemas/foo"
   395  					}
   396  				}
   397  			}
   398  		}
   399  	}`
   400  	spec := &openapi.Specification{}
   401  	reader := &OpenapiReader{
   402  		OpenapiSpec: spec,
   403  		Memo:        make(map[string]*jsonschema.Schema),
   404  	}
   405  	err := json.Unmarshal([]byte(specString), spec)
   406  	require.NoError(t, err)
   407  
   408  	fruitsSchema, err := reader.readResolvedSchema("#/components/schemas/fruits")
   409  	require.NoError(t, err)
   410  
   411  	fruitsSchemaJson, err := json.MarshalIndent(fruitsSchema, "		", "	")
   412  	require.NoError(t, err)
   413  
   414  	expected := `{
   415  			"type": "object",
   416  			"description": "fruits that are cool",
   417  			"properties": {
   418  				"guava": {
   419  					"type": "string",
   420  					"description": "a guava for my schema"
   421  				},
   422  				"mangos": {
   423  					"type": "object",
   424  					"description": "multiple mangos",
   425  					"items": {
   426  						"type": "number",
   427  						"description": "a single mango"
   428  					}
   429  				}
   430  			}
   431  		}`
   432  
   433  	t.Log("[DEBUG] actual: ", string(fruitsSchemaJson))
   434  	t.Log("[DEBUG] expected: ", expected)
   435  	assert.Equal(t, expected, string(fruitsSchemaJson))
   436  }