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 }