// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley // SPDX-License-Identifier: MIT package datamodel import ( "fmt" "os" "testing" "github.com/pb33f/libopenapi/utils" "github.com/stretchr/testify/assert" ) const ( // OpenApi3 is used by all OpenAPI 3+ docs OpenApi3 = "openapi" // OpenApi2 is used by all OpenAPI 2 docs, formerly known as swagger. OpenApi2 = "swagger" // AsyncApi is used by akk AsyncAPI docs, all versions. AsyncApi = "asyncapi" ) var ( goodJSON = `{"name":"kitty", "noises":["meow","purrrr","gggrrraaaaaooooww"]}` badJSON = `{"name":"kitty, "noises":[{"meow","purrrr","gggrrraaaaaooooww"]}}` goodYAML = `name: kitty noises: - meow - purrr - gggggrrraaaaaaaaaooooooowwwwwww ` ) var badYAML = `name: kitty noises: - meow - purrr - gggggrrraaaaaaaaaooooooowwwwwww ` var OpenApiWat = `openapi: 3.2 info: title: Test API, valid, but not quite valid servers: - url: https://quobix.com/api` var OpenApi31 = `openapi: 3.1 info: title: Test API, valid, but not quite valid servers: - url: https://quobix.com/api` var OpenApiFalse = `openapi: false info: title: Test API version is a bool? servers: - url: https://quobix.com/api` var OpenApiOne = `openapi: 1.0.1 info: title: Test API version is what version? servers: - url: https://quobix.com/api` var OpenApi3Spec = `openapi: 3.0.1 info: title: Test API tags: - name: "Test" - name: "Test 2" servers: - url: https://quobix.com/api` var OpenApi2Spec = `swagger: 2.0.1 info: title: Test API tags: - name: "Test" servers: - url: https://quobix.com/api` var OpenApi2SpecOdd = `swagger: 3.0.1 info: title: Test API tags: - name: "Test" servers: - url: https://quobix.com/api` var AsyncAPISpec = `asyncapi: 2.0.0 info: title: Hello world application version: '0.1.0' channels: hello: publish: message: payload: type: string pattern: '^hello .+$'` var AsyncAPISpecOdd = `asyncapi: 3.0.0 info: title: Hello world application version: '0.1.0'` func TestExtractSpecInfo_ValidJSON(t *testing.T) { r, e := ExtractSpecInfo([]byte(goodJSON)) assert.Greater(t, len(*r.SpecJSONBytes), 0) assert.Error(t, e) } func TestExtractSpecInfo_InvalidJSON(t *testing.T) { _, e := ExtractSpecInfo([]byte(badJSON)) assert.Error(t, e) } func TestExtractSpecInfo_Nothing(t *testing.T) { _, e := ExtractSpecInfo([]byte("")) assert.Error(t, e) } func TestExtractSpecInfo_ValidYAML(t *testing.T) { r, e := ExtractSpecInfo([]byte(goodYAML)) assert.Greater(t, len(*r.SpecJSONBytes), 0) assert.Error(t, e) } func TestExtractSpecInfo_InvalidYAML(t *testing.T) { _, e := ExtractSpecInfo([]byte(badYAML)) assert.Error(t, e) } func TestExtractSpecInfo_InvalidOpenAPIVersion(t *testing.T) { _, e := ExtractSpecInfo([]byte(OpenApiOne)) assert.Error(t, e) } func TestExtractSpecInfo_OpenAPI3(t *testing.T) { r, e := ExtractSpecInfo([]byte(OpenApi3Spec)) assert.Nil(t, e) assert.Equal(t, utils.OpenApi3, r.SpecType) assert.Equal(t, "3.0.1", r.Version) assert.Greater(t, len(*r.SpecJSONBytes), 0) assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.0/schema/2021-09-28") } func TestExtractSpecInfo_OpenAPIWat(t *testing.T) { r, e := ExtractSpecInfo([]byte(OpenApiWat)) assert.Nil(t, e) assert.Equal(t, OpenApi3, r.SpecType) assert.Equal(t, "3.2", r.Version) } func TestExtractSpecInfo_OpenAPI31(t *testing.T) { r, e := ExtractSpecInfo([]byte(OpenApi31)) assert.Nil(t, e) assert.Equal(t, OpenApi3, r.SpecType) assert.Equal(t, "3.1", r.Version) assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.1/schema/2022-10-07") } func TestExtractSpecInfo_AnyDocument(t *testing.T) { random := `something: yeah nothing: - one - two why: yes: no` r, e := ExtractSpecInfoWithDocumentCheck([]byte(random), true) assert.Nil(t, e) assert.NotNil(t, r.RootNode) assert.Equal(t, "something", r.RootNode.Content[0].Content[0].Value) assert.Len(t, *r.SpecBytes, 55) } func TestExtractSpecInfo_AnyDocument_Sync(t *testing.T) { random := `something: yeah nothing: - one - two why: yes: no` r, e := ExtractSpecInfoWithDocumentCheckSync([]byte(random), true) assert.Nil(t, e) assert.NotNil(t, r.RootNode) assert.Equal(t, "something", r.RootNode.Content[0].Content[0].Value) assert.Len(t, *r.SpecBytes, 55) } func TestExtractSpecInfo_AnyDocument_JSON(t *testing.T) { random := `{ "something" : "yeah"}` r, e := ExtractSpecInfoWithDocumentCheck([]byte(random), true) assert.Nil(t, e) assert.NotNil(t, r.RootNode) assert.Equal(t, "something", r.RootNode.Content[0].Content[0].Value) assert.Len(t, *r.SpecBytes, 23) } func TestExtractSpecInfo_AnyDocumentFromConfig(t *testing.T) { random := `something: yeah nothing: - one - two why: yes: no` r, e := ExtractSpecInfoWithConfig([]byte(random), &DocumentConfiguration{ BypassDocumentCheck: true, }) assert.Nil(t, e) assert.NotNil(t, r.RootNode) assert.Equal(t, "something", r.RootNode.Content[0].Content[0].Value) assert.Len(t, *r.SpecBytes, 55) } func TestExtractSpecInfo_OpenAPIFalse(t *testing.T) { spec, e := ExtractSpecInfo([]byte(OpenApiFalse)) assert.NoError(t, e) assert.Equal(t, "false", spec.Version) } func TestExtractSpecInfo_OpenAPI2(t *testing.T) { r, e := ExtractSpecInfo([]byte(OpenApi2Spec)) assert.Nil(t, e) assert.Equal(t, OpenApi2, r.SpecType) assert.Equal(t, "2.0.1", r.Version) assert.Greater(t, len(*r.SpecJSONBytes), 0) assert.Contains(t, r.APISchema, "http://swagger.io/v2/schema.json#") } func TestExtractSpecInfo_OpenAPI2_OddVersion(t *testing.T) { _, e := ExtractSpecInfo([]byte(OpenApi2SpecOdd)) assert.NotNil(t, e) assert.Equal(t, "spec is defined as a swagger (openapi 2.0) spec, but is an openapi 3 or unknown version", e.Error()) } func TestExtractSpecInfo_AsyncAPI(t *testing.T) { r, e := ExtractSpecInfo([]byte(AsyncAPISpec)) assert.Nil(t, e) assert.Equal(t, AsyncApi, r.SpecType) assert.Equal(t, "2.0.0", r.Version) assert.Greater(t, len(*r.SpecJSONBytes), 0) } func TestExtractSpecInfo_AsyncAPI_OddVersion(t *testing.T) { _, e := ExtractSpecInfo([]byte(AsyncAPISpecOdd)) assert.NotNil(t, e) assert.Equal(t, "spec is defined as asyncapi, but has a major version that is invalid", e.Error()) } func TestExtractSpecInfo_BadVersion_OpenAPI3(t *testing.T) { yml := `openapi: should: fail` _, err := ExtractSpecInfo([]byte(yml)) assert.Error(t, err) } func TestExtractSpecInfo_BadVersion_Swagger(t *testing.T) { yml := `swagger: should: fail` _, err := ExtractSpecInfo([]byte(yml)) assert.Error(t, err) } func TestExtractSpecInfo_BadVersion_AsyncAPI(t *testing.T) { yml := `asyncapi: should: fail` _, err := ExtractSpecInfo([]byte(yml)) assert.Error(t, err) } func ExampleExtractSpecInfo() { // load bytes from openapi spec file. bytes, _ := os.ReadFile("../test_specs/petstorev3.json") // create a new *SpecInfo instance from loaded bytes specInfo, err := ExtractSpecInfo(bytes) if err != nil { panic(fmt.Sprintf("cannot extract spec info: %e", err)) } // print out the version, format and filetype fmt.Printf("the version of the spec is %s, the format is %s and the file type is %s", specInfo.Version, specInfo.SpecFormat, specInfo.SpecFileType) // Output: the version of the spec is 3.0.2, the format is oas3 and the file type is json } func TestExtractSpecInfoSync_Error(t *testing.T) { random := `` _, e := ExtractSpecInfoWithDocumentCheckSync([]byte(random), true) assert.Error(t, e) }