mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 04:20:11 +00:00
1446 lines
36 KiB
Go
1446 lines
36 KiB
Go
package index
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/pb33f/libopenapi/datamodel"
|
|
"github.com/pb33f/libopenapi/utils"
|
|
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
func TestNewResolver(t *testing.T) {
|
|
assert.Nil(t, NewResolver(nil))
|
|
}
|
|
|
|
func TestResolvingError_Error(t *testing.T) {
|
|
|
|
errs := []error{
|
|
&ResolvingError{
|
|
Path: "$.test1",
|
|
ErrorRef: errors.New("test1"),
|
|
Node: &yaml.Node{
|
|
Line: 1,
|
|
Column: 1,
|
|
},
|
|
},
|
|
&ResolvingError{
|
|
Path: "$.test2",
|
|
ErrorRef: errors.New("test2"),
|
|
Node: &yaml.Node{
|
|
Line: 1,
|
|
Column: 1,
|
|
},
|
|
},
|
|
}
|
|
|
|
assert.Equal(t, "test1: $.test1 [1:1]", errs[0].Error())
|
|
assert.Equal(t, "test2: $.test2 [1:1]", errs[1].Error())
|
|
}
|
|
|
|
func TestResolvingError_Error_Index(t *testing.T) {
|
|
|
|
errs := []error{
|
|
&ResolvingError{
|
|
ErrorRef: errors.Join(&IndexingError{
|
|
Path: "$.test1",
|
|
Err: errors.New("test1"),
|
|
Node: &yaml.Node{
|
|
Line: 1,
|
|
Column: 1,
|
|
},
|
|
}),
|
|
Node: &yaml.Node{
|
|
Line: 1,
|
|
Column: 1,
|
|
},
|
|
},
|
|
&ResolvingError{
|
|
ErrorRef: errors.Join(&IndexingError{
|
|
Path: "$.test2",
|
|
Err: errors.New("test2"),
|
|
Node: &yaml.Node{
|
|
Line: 1,
|
|
Column: 1,
|
|
},
|
|
}),
|
|
Node: &yaml.Node{
|
|
Line: 1,
|
|
Column: 1,
|
|
},
|
|
},
|
|
}
|
|
|
|
assert.Equal(t, "test1: $.test1 [1:1]", errs[0].Error())
|
|
assert.Equal(t, "test2: $.test2 [1:1]", errs[1].Error())
|
|
}
|
|
|
|
func Benchmark_ResolveDocumentStripe(b *testing.B) {
|
|
baseDir := "../test_specs/stripe.yaml"
|
|
resolveFile, _ := os.ReadFile(baseDir)
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(resolveFile, &rootNode)
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
|
|
cf := CreateOpenAPIIndexConfig()
|
|
|
|
rolo := NewRolodex(cf)
|
|
rolo.SetRootNode(&rootNode)
|
|
|
|
indexedErr := rolo.IndexTheRolodex()
|
|
assert.Len(b, utils.UnwrapErrors(indexedErr), 1)
|
|
|
|
}
|
|
}
|
|
|
|
func TestResolver_ResolveComponents_CircularSpec(t *testing.T) {
|
|
circular, _ := os.ReadFile("../test_specs/circular-tests.yaml")
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(circular, &rootNode)
|
|
|
|
cf := CreateClosedAPIIndexConfig()
|
|
cf.AvoidCircularReferenceCheck = true
|
|
rolo := NewRolodex(cf)
|
|
rolo.SetRootNode(&rootNode)
|
|
|
|
indexedErr := rolo.IndexTheRolodex()
|
|
assert.NoError(t, indexedErr)
|
|
|
|
rolo.Resolve()
|
|
assert.Len(t, rolo.GetCaughtErrors(), 3)
|
|
|
|
_, err := yaml.Marshal(rolo.GetRootIndex().GetResolver().resolvedRoot)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestResolver_CheckForCircularReferences(t *testing.T) {
|
|
circular, _ := os.ReadFile("../test_specs/circular-tests.yaml")
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(circular, &rootNode)
|
|
|
|
cf := CreateClosedAPIIndexConfig()
|
|
|
|
rolo := NewRolodex(cf)
|
|
rolo.SetRootNode(&rootNode)
|
|
|
|
indexedErr := rolo.IndexTheRolodex()
|
|
assert.Error(t, indexedErr)
|
|
assert.Len(t, utils.UnwrapErrors(indexedErr), 3)
|
|
|
|
rolo.CheckForCircularReferences()
|
|
|
|
assert.Len(t, rolo.GetCaughtErrors(), 3)
|
|
assert.Len(t, rolo.GetRootIndex().GetResolver().GetResolvingErrors(), 3)
|
|
assert.Len(t, rolo.GetRootIndex().GetResolver().GetInfiniteCircularReferences(), 3)
|
|
|
|
}
|
|
|
|
func TestResolver_CheckForCircularReferences_CatchArray(t *testing.T) {
|
|
circular := []byte(`openapi: 3.0.0
|
|
components:
|
|
schemas:
|
|
ProductCategory:
|
|
type: "object"
|
|
properties:
|
|
name:
|
|
type: "string"
|
|
children:
|
|
type: "array"
|
|
items:
|
|
$ref: "#/components/schemas/ProductCategory"
|
|
description: "Array of sub-categories in the same format."
|
|
required:
|
|
- "name"
|
|
- "children"`)
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(circular, &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.CheckForCircularReferences()
|
|
assert.Len(t, circ, 1)
|
|
assert.Len(t, resolver.GetResolvingErrors(), 1) // infinite loop is a resolving error.
|
|
assert.Len(t, resolver.GetInfiniteCircularReferences(), 1)
|
|
assert.True(t, resolver.GetInfiniteCircularReferences()[0].IsArrayResult)
|
|
|
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestResolver_CheckForCircularReferences_IgnoreArray(t *testing.T) {
|
|
circular := []byte(`openapi: 3.0.0
|
|
components:
|
|
schemas:
|
|
ProductCategory:
|
|
type: "object"
|
|
properties:
|
|
name:
|
|
type: "string"
|
|
children:
|
|
type: "array"
|
|
items:
|
|
$ref: "#/components/schemas/ProductCategory"
|
|
description: "Array of sub-categories in the same format."
|
|
required:
|
|
- "name"
|
|
- "children"`)
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(circular, &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
resolver.IgnoreArrayCircularReferences()
|
|
|
|
circ := resolver.CheckForCircularReferences()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
|
assert.Len(t, resolver.GetCircularReferences(), 0)
|
|
|
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestResolver_CheckForCircularReferences_IgnorePoly_Any(t *testing.T) {
|
|
circular := []byte(`openapi: 3.1.0
|
|
paths:
|
|
/one:
|
|
get:
|
|
responses:
|
|
'200':
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/One'
|
|
components:
|
|
schemas:
|
|
One:
|
|
properties:
|
|
thing:
|
|
oneOf:
|
|
- "$ref": "#/components/schemas/Two"
|
|
- "$ref": "#/components/schemas/Three"
|
|
required:
|
|
- thing
|
|
Two:
|
|
description: "test two"
|
|
properties:
|
|
testThing:
|
|
"$ref": "#/components/schemas/One"
|
|
Three:
|
|
description: "test three"
|
|
properties:
|
|
testThing:
|
|
"$ref": "#/components/schemas/One"`)
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(circular, &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
resolver.IgnorePolymorphicCircularReferences()
|
|
|
|
circ := resolver.CheckForCircularReferences()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
|
assert.Len(t, resolver.GetCircularReferences(), 0)
|
|
|
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestResolver_CheckForCircularReferences_IgnorePoly_All(t *testing.T) {
|
|
circular := []byte(`openapi: 3.0.0
|
|
components:
|
|
schemas:
|
|
ProductCategory:
|
|
type: "object"
|
|
properties:
|
|
name:
|
|
type: "string"
|
|
children:
|
|
type: "object"
|
|
allOf:
|
|
- $ref: "#/components/schemas/ProductCategory"
|
|
description: "Array of sub-categories in the same format."
|
|
required:
|
|
- "name"
|
|
- "children"`)
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(circular, &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
resolver.IgnorePolymorphicCircularReferences()
|
|
|
|
circ := resolver.CheckForCircularReferences()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
|
assert.Len(t, resolver.GetCircularReferences(), 0)
|
|
|
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestResolver_CheckForCircularReferences_IgnorePoly_One(t *testing.T) {
|
|
circular := []byte(`openapi: 3.0.0
|
|
components:
|
|
schemas:
|
|
ProductCategory:
|
|
type: "object"
|
|
properties:
|
|
name:
|
|
type: "string"
|
|
children:
|
|
type: "object"
|
|
oneOf:
|
|
- $ref: "#/components/schemas/ProductCategory"
|
|
description: "Array of sub-categories in the same format."
|
|
required:
|
|
- "name"
|
|
- "children"`)
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(circular, &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
resolver.IgnorePolymorphicCircularReferences()
|
|
|
|
circ := resolver.CheckForCircularReferences()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
|
assert.Len(t, resolver.GetCircularReferences(), 0)
|
|
|
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestResolver_CheckForCircularReferences_CatchPoly_Any(t *testing.T) {
|
|
circular := []byte(`openapi: 3.0.0
|
|
components:
|
|
schemas:
|
|
ProductCategory:
|
|
type: "object"
|
|
properties:
|
|
name:
|
|
type: "string"
|
|
children:
|
|
type: "object"
|
|
anyOf:
|
|
- $ref: "#/components/schemas/ProductCategory"
|
|
description: "Array of sub-categories in the same format."
|
|
required:
|
|
- "name"
|
|
- "children"`)
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(circular, &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.CheckForCircularReferences()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetResolvingErrors(), 0) // not an infinite loop if poly.
|
|
assert.Len(t, resolver.GetCircularReferences(), 1)
|
|
assert.Equal(t, "anyOf", resolver.GetCircularReferences()[0].PolymorphicType)
|
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestResolver_CheckForCircularReferences_CatchPoly_All(t *testing.T) {
|
|
circular := []byte(`openapi: 3.0.0
|
|
components:
|
|
schemas:
|
|
ProductCategory:
|
|
type: "object"
|
|
properties:
|
|
name:
|
|
type: "string"
|
|
children:
|
|
type: "object"
|
|
allOf:
|
|
- $ref: "#/components/schemas/ProductCategory"
|
|
description: "Array of sub-categories in the same format."
|
|
required:
|
|
- "name"
|
|
- "children"`)
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(circular, &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.CheckForCircularReferences()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetResolvingErrors(), 0) // not an infinite loop if poly.
|
|
assert.Len(t, resolver.GetCircularReferences(), 1)
|
|
assert.Equal(t, "allOf", resolver.GetCircularReferences()[0].PolymorphicType)
|
|
assert.True(t, resolver.GetCircularReferences()[0].IsPolymorphicResult)
|
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestResolver_CircularReferencesRequiredValid(t *testing.T) {
|
|
circular, _ := os.ReadFile("../test_specs/swagger-valid-recursive-model.yaml")
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(circular, &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.CheckForCircularReferences()
|
|
assert.Len(t, circ, 0)
|
|
|
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestResolver_CircularReferencesRequiredInvalid(t *testing.T) {
|
|
circular, _ := os.ReadFile("../test_specs/swagger-invalid-recursive-model.yaml")
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(circular, &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.CheckForCircularReferences()
|
|
assert.Len(t, circ, 2)
|
|
|
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestResolver_DeepJourney(t *testing.T) {
|
|
var journey []*Reference
|
|
for f := 0; f < 200; f++ {
|
|
journey = append(journey, nil)
|
|
}
|
|
idx := NewSpecIndexWithConfig(nil, CreateClosedAPIIndexConfig())
|
|
resolver := NewResolver(idx)
|
|
assert.Nil(t, resolver.extractRelatives(nil, nil, nil, nil, journey, nil, false, 0))
|
|
}
|
|
|
|
func TestResolver_DeepDepth(t *testing.T) {
|
|
var refA, refB *yaml.Node
|
|
|
|
refA = &yaml.Node{
|
|
Value: "A",
|
|
Tag: "!!seq",
|
|
}
|
|
|
|
refB = &yaml.Node{
|
|
Value: "B",
|
|
Tag: "!!seq",
|
|
}
|
|
|
|
refA.Content = append(refA.Content, refB)
|
|
refB.Content = append(refB.Content, refA)
|
|
|
|
idx := NewSpecIndexWithConfig(nil, CreateClosedAPIIndexConfig())
|
|
resolver := NewResolver(idx)
|
|
|
|
// add a logger
|
|
var log []byte
|
|
buf := bytes.NewBuffer(log)
|
|
logger := slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
}))
|
|
idx.logger = logger
|
|
|
|
ref := &Reference{
|
|
FullDefinition: "#/components/schemas/A",
|
|
}
|
|
found := resolver.extractRelatives(ref, refA, nil, nil, nil, nil, false, 0)
|
|
|
|
assert.Nil(t, found)
|
|
assert.Contains(t, buf.String(), "libopenapi resolver: relative depth exceeded 100 levels")
|
|
}
|
|
|
|
func TestResolver_ResolveComponents_Stripe_NoRolodex(t *testing.T) {
|
|
baseDir := "../test_specs/stripe.yaml"
|
|
|
|
resolveFile, _ := os.ReadFile(baseDir)
|
|
|
|
var stripeRoot yaml.Node
|
|
_ = yaml.Unmarshal(resolveFile, &stripeRoot)
|
|
|
|
info, _ := datamodel.ExtractSpecInfoWithDocumentCheck(resolveFile, true)
|
|
|
|
cf := CreateOpenAPIIndexConfig()
|
|
cf.SpecInfo = info
|
|
|
|
idx := NewSpecIndexWithConfig(&stripeRoot, cf)
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.CheckForCircularReferences()
|
|
assert.Len(t, circ, 1)
|
|
|
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestResolver_ResolveComponents_Stripe(t *testing.T) {
|
|
baseDir := "../test_specs/stripe.yaml"
|
|
|
|
resolveFile, _ := os.ReadFile(baseDir)
|
|
|
|
var stripeRoot yaml.Node
|
|
_ = yaml.Unmarshal(resolveFile, &stripeRoot)
|
|
|
|
info, _ := datamodel.ExtractSpecInfoWithDocumentCheck(resolveFile, true)
|
|
|
|
cf := CreateOpenAPIIndexConfig()
|
|
cf.SpecInfo = info
|
|
cf.AvoidCircularReferenceCheck = true
|
|
|
|
rolo := NewRolodex(cf)
|
|
rolo.SetRootNode(&stripeRoot)
|
|
|
|
indexedErr := rolo.IndexTheRolodex()
|
|
assert.NoError(t, indexedErr)
|
|
|
|
// after resolving, the rolodex will have errors.
|
|
rolo.Resolve()
|
|
|
|
assert.Len(t, rolo.GetCaughtErrors(), 1)
|
|
assert.Len(t, rolo.GetRootIndex().GetResolver().GetNonPolymorphicCircularErrors(), 1)
|
|
assert.Len(t, rolo.GetRootIndex().GetResolver().GetPolymorphicCircularErrors(), 0)
|
|
assert.Len(t, rolo.GetRootIndex().GetResolver().GetSafeCircularReferences(), 25)
|
|
|
|
}
|
|
|
|
func TestResolver_ResolveComponents_BurgerShop(t *testing.T) {
|
|
mixedref, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml")
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(mixedref, &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.Resolve()
|
|
assert.Len(t, circ, 0)
|
|
}
|
|
|
|
func TestResolver_ResolveComponents_PolyNonCircRef(t *testing.T) {
|
|
yml := `paths:
|
|
/hey:
|
|
get:
|
|
responses:
|
|
"200":
|
|
$ref: '#/components/schemas/crackers'
|
|
components:
|
|
schemas:
|
|
cheese:
|
|
description: cheese
|
|
anyOf:
|
|
items:
|
|
$ref: '#/components/schemas/crackers'
|
|
crackers:
|
|
description: crackers
|
|
allOf:
|
|
- $ref: '#/components/schemas/tea'
|
|
tea:
|
|
description: tea`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(yml), &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.CheckForCircularReferences()
|
|
assert.Len(t, circ, 0)
|
|
}
|
|
|
|
func TestResolver_ResolveComponents_PolyCircRef(t *testing.T) {
|
|
yml := `openapi: 3.1.0
|
|
components:
|
|
schemas:
|
|
cheese:
|
|
description: cheese
|
|
anyOf:
|
|
- $ref: '#/components/schemas/crackers'
|
|
crackers:
|
|
description: crackers
|
|
anyOf:
|
|
- $ref: '#/components/schemas/cheese'
|
|
tea:
|
|
description: tea`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(yml), &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
_ = resolver.CheckForCircularReferences()
|
|
resolver.circularReferences[0].IsInfiniteLoop = true // override
|
|
assert.Len(t, idx.GetCircularReferences(), 1)
|
|
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 1)
|
|
assert.Equal(t, 2, idx.GetCircularReferences()[0].LoopIndex)
|
|
|
|
}
|
|
|
|
func TestResolver_ResolveComponents_Missing(t *testing.T) {
|
|
yml := `paths:
|
|
/hey:
|
|
get:
|
|
responses:
|
|
"200":
|
|
$ref: '#/components/schemas/crackers'
|
|
components:
|
|
schemas:
|
|
cheese:
|
|
description: cheese
|
|
properties:
|
|
thang:
|
|
$ref: '#/components/schemas/crackers'
|
|
crackers:
|
|
description: crackers
|
|
properties:
|
|
butter:
|
|
$ref: 'go home, I am drunk'`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(yml), &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
err := resolver.Resolve()
|
|
assert.Len(t, err, 2)
|
|
assert.Equal(t, "cannot resolve reference `go home, I am drunk`, it's missing: $go home, I am drunk [18:11]", err[0].Error())
|
|
}
|
|
|
|
func TestResolver_ResolveThroughPaths(t *testing.T) {
|
|
yml := `paths:
|
|
/pizza/{cake}/{pizza}/pie:
|
|
parameters:
|
|
- name: juicy
|
|
/companies/{companyId}/data/payments/{paymentId}:
|
|
get:
|
|
tags:
|
|
- Accounts receivable
|
|
parameters:
|
|
- $ref: '#/paths/~1pizza~1%7Bcake%7D~1%7Bpizza%7D~1pie/parameters/0'`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(yml), &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
err := resolver.Resolve()
|
|
assert.Len(t, err, 0)
|
|
}
|
|
|
|
func TestResolver_ResolveComponents_MixedRef(t *testing.T) {
|
|
mixedref, _ := os.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml")
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(mixedref, &rootNode)
|
|
|
|
// create a test server.
|
|
//server := test_buildMixedRefServer()
|
|
//defer server.Close()
|
|
|
|
// create a new config that allows local and remote to be mixed up.
|
|
cf := CreateOpenAPIIndexConfig()
|
|
cf.AvoidBuildIndex = true
|
|
cf.AllowRemoteLookup = true
|
|
cf.AvoidCircularReferenceCheck = true
|
|
cf.BasePath = "../test_specs"
|
|
cf.SpecAbsolutePath, _ = filepath.Abs("../test_specs/mixedref-burgershop.openapi.yaml")
|
|
cf.ExtractRefsSequentially = true
|
|
|
|
// setting this baseURL will override the base
|
|
cf.BaseURL, _ = url.Parse("https://raw.githubusercontent.com/daveshanley/vacuum/main/model/test_files/")
|
|
|
|
// create a new rolodex
|
|
rolo := NewRolodex(cf)
|
|
|
|
// set the rolodex root node to the root node of the spec.
|
|
rolo.SetRootNode(&rootNode)
|
|
|
|
// create a new remote fs and set the config for indexing.
|
|
remoteFS, _ := NewRemoteFSWithConfig(cf)
|
|
remoteFS.SetIndexConfig(cf)
|
|
|
|
// set our remote handler func
|
|
|
|
c := http.Client{}
|
|
|
|
remoteFS.RemoteHandlerFunc = c.Get
|
|
|
|
// configure the local filesystem.
|
|
fsCfg := LocalFSConfig{
|
|
BaseDirectory: cf.BasePath,
|
|
IndexConfig: cf,
|
|
}
|
|
|
|
// create a new local filesystem.
|
|
fileFS, err := NewLocalFSWithConfig(&fsCfg)
|
|
assert.NoError(t, err)
|
|
|
|
// add file systems to the rolodex
|
|
rolo.AddLocalFS(cf.BasePath, fileFS)
|
|
rolo.AddRemoteFS("https://raw.githubusercontent.com/daveshanley/vacuum/main/model/test_files/", remoteFS)
|
|
|
|
// index the rolodex.
|
|
indexedErr := rolo.IndexTheRolodex()
|
|
|
|
assert.NoError(t, indexedErr)
|
|
|
|
rolo.Resolve()
|
|
index := rolo.GetRootIndex
|
|
resolver := index().GetResolver()
|
|
|
|
assert.Len(t, resolver.GetCircularReferences(), 0)
|
|
assert.Equal(t, 9, resolver.GetIndexesVisited())
|
|
|
|
// in v0.8.2 a new check was added when indexing, to prevent re-indexing the same file multiple times.
|
|
assert.Equal(t, 6, resolver.GetRelativesSeen())
|
|
assert.Equal(t, 15, resolver.GetJourneysTaken())
|
|
assert.Equal(t, 17, resolver.GetReferenceVisited())
|
|
}
|
|
|
|
func TestResolver_ResolveComponents_k8s(t *testing.T) {
|
|
k8s, _ := os.ReadFile("../test_specs/k8s.json")
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal(k8s, &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.Resolve()
|
|
assert.Len(t, circ, 0)
|
|
}
|
|
|
|
// Example of how to resolve the Stripe OpenAPI specification, and check for circular reference errors
|
|
func ExampleNewResolver() {
|
|
// create a yaml.Node reference as a root node.
|
|
var rootNode yaml.Node
|
|
|
|
// load in the Stripe OpenAPI spec (lots of polymorphic complexity in here)
|
|
stripeBytes, _ := os.ReadFile("../test_specs/stripe.yaml")
|
|
|
|
// unmarshal bytes into our rootNode.
|
|
_ = yaml.Unmarshal(stripeBytes, &rootNode)
|
|
|
|
// create a new spec index (resolver depends on it)
|
|
indexConfig := CreateClosedAPIIndexConfig()
|
|
idx := NewSpecIndexWithConfig(&rootNode, indexConfig)
|
|
|
|
// create a new resolver using the index.
|
|
resolver := NewResolver(idx)
|
|
|
|
// resolve the document, if there are circular reference errors, they are returned/
|
|
// WARNING: this is a destructive action and the rootNode will be PERMANENTLY altered and cannot be unresolved
|
|
circularErrors := resolver.Resolve()
|
|
|
|
// The Stripe API has a bunch of circular reference problems, mainly from polymorphism.
|
|
// So let's print them out.
|
|
//
|
|
fmt.Printf("There is %d circular reference error, %d of them are polymorphic errors, %d are not\n"+
|
|
"with a total pf %d safe circular references.\n",
|
|
len(circularErrors), len(resolver.GetPolymorphicCircularErrors()), len(resolver.GetNonPolymorphicCircularErrors()),
|
|
len(resolver.GetSafeCircularReferences()))
|
|
// Output: There is 1 circular reference error, 0 of them are polymorphic errors, 1 are not
|
|
// with a total pf 25 safe circular references.
|
|
|
|
}
|
|
|
|
func ExampleResolvingError() {
|
|
re := ResolvingError{
|
|
ErrorRef: errors.New("je suis une erreur"),
|
|
Node: &yaml.Node{
|
|
Line: 5,
|
|
Column: 21,
|
|
},
|
|
Path: "#/definitions/JeSuisUneErreur",
|
|
CircularReference: &CircularReferenceResult{},
|
|
}
|
|
|
|
fmt.Printf("%s", re.Error())
|
|
// Output: je suis une erreur: #/definitions/JeSuisUneErreur [5:21]
|
|
}
|
|
|
|
func TestDocument_IgnoreArrayCircularReferences(t *testing.T) {
|
|
|
|
var d = `openapi: 3.1.0
|
|
components:
|
|
schemas:
|
|
ProductCategory:
|
|
type: "object"
|
|
properties:
|
|
name:
|
|
type: "string"
|
|
children:
|
|
type: "array"
|
|
items:
|
|
$ref: "#/components/schemas/ProductCategory"
|
|
description: "Array of sub-categories in the same format."
|
|
required:
|
|
- "name"
|
|
- "children"`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
resolver.IgnoreArrayCircularReferences()
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.Resolve()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetIgnoredCircularArrayReferences(), 1)
|
|
|
|
}
|
|
|
|
func TestDocument_IgnorePolyCircularReferences(t *testing.T) {
|
|
|
|
var d = `openapi: 3.1.0
|
|
components:
|
|
schemas:
|
|
ProductCategory:
|
|
type: "object"
|
|
properties:
|
|
name:
|
|
type: "string"
|
|
children:
|
|
type: "object"
|
|
anyOf:
|
|
- $ref: "#/components/schemas/ProductCategory"
|
|
description: "Array of sub-categories in the same format."
|
|
required:
|
|
- "name"
|
|
- "children"`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
resolver.IgnorePolymorphicCircularReferences()
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.Resolve()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetIgnoredCircularPolyReferences(), 1)
|
|
|
|
}
|
|
|
|
func TestDocument_IgnorePolyCircularReferences_NoArrayForRef(t *testing.T) {
|
|
|
|
var d = `openapi: 3.1.0
|
|
components:
|
|
schemas:
|
|
bingo:
|
|
type: object
|
|
properties:
|
|
bango:
|
|
$ref: "#/components/schemas/ProductCategory"
|
|
ProductCategory:
|
|
type: "object"
|
|
properties:
|
|
name:
|
|
type: "string"
|
|
children:
|
|
type: "object"
|
|
items:
|
|
anyOf:
|
|
items:
|
|
$ref: "#/components/schemas/ProductCategory"
|
|
description: "Array of sub-categories in the same format."
|
|
required:
|
|
- "name"
|
|
- "children"`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
resolver.IgnorePolymorphicCircularReferences()
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.Resolve()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetIgnoredCircularPolyReferences(), 1)
|
|
|
|
}
|
|
|
|
func TestResolver_isInfiniteCircularDep_NoRef(t *testing.T) {
|
|
resolver := NewResolver(nil)
|
|
a, b := resolver.isInfiniteCircularDependency(nil, nil, nil)
|
|
assert.False(t, a)
|
|
assert.Nil(t, b)
|
|
}
|
|
|
|
func TestResolver_AllowedCircle(t *testing.T) {
|
|
|
|
d := `openapi: 3.1.0
|
|
paths:
|
|
/test:
|
|
get:
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
components:
|
|
schemas:
|
|
Obj:
|
|
type: object
|
|
properties:
|
|
other:
|
|
$ref: '#/components/schemas/Obj2'
|
|
Obj2:
|
|
type: object
|
|
properties:
|
|
other:
|
|
$ref: '#/components/schemas/Obj'
|
|
required:
|
|
- other`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.Resolve()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetInfiniteCircularReferences(), 0)
|
|
assert.Len(t, resolver.GetSafeCircularReferences(), 1)
|
|
|
|
}
|
|
|
|
func TestResolver_AllowedCircle_Array(t *testing.T) {
|
|
|
|
d := `openapi: 3.1.0
|
|
components:
|
|
schemas:
|
|
Obj:
|
|
type: object
|
|
properties:
|
|
other:
|
|
$ref: '#/components/schemas/Obj2'
|
|
required:
|
|
- other
|
|
Obj2:
|
|
type: object
|
|
properties:
|
|
children:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Obj'
|
|
required:
|
|
- children`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
|
|
|
cf := CreateClosedAPIIndexConfig()
|
|
cf.IgnoreArrayCircularReferences = true
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, cf)
|
|
|
|
resolver := NewResolver(idx)
|
|
resolver.IgnoreArrayCircularReferences()
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.Resolve()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetInfiniteCircularReferences(), 0)
|
|
assert.Len(t, resolver.GetSafeCircularReferences(), 0)
|
|
assert.Len(t, resolver.GetIgnoredCircularArrayReferences(), 1)
|
|
|
|
}
|
|
|
|
func TestResolver_CatchInvalidMapPolyCircle(t *testing.T) {
|
|
|
|
d := `openapi: 3.1.0
|
|
paths:
|
|
/test:
|
|
get:
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
components:
|
|
schemas:
|
|
ObjectWithOneOf:
|
|
type: object
|
|
properties:
|
|
child:
|
|
oneOf:
|
|
$ref: '#/components/schemas/ObjectWithOneOf'
|
|
required:
|
|
- child
|
|
`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
|
|
|
cf := CreateClosedAPIIndexConfig()
|
|
idx := NewSpecIndexWithConfig(&rootNode, cf)
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.Resolve()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetInfiniteCircularReferences(), 0)
|
|
assert.Len(t, resolver.GetSafeCircularReferences(), 1)
|
|
assert.Len(t, resolver.GetIgnoredCircularPolyReferences(), 0)
|
|
|
|
}
|
|
|
|
func TestResolver_CatchInvalidMapPolyCircle_Ignored(t *testing.T) {
|
|
|
|
d := `openapi: 3.1.0
|
|
paths:
|
|
/test:
|
|
get:
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
components:
|
|
schemas:
|
|
ObjectWithOneOf:
|
|
type: object
|
|
properties:
|
|
child:
|
|
oneOf:
|
|
$ref: '#/components/schemas/ObjectWithOneOf'
|
|
required:
|
|
- child
|
|
`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
|
|
|
cf := CreateClosedAPIIndexConfig()
|
|
cf.IgnorePolymorphicCircularReferences = true
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, cf)
|
|
|
|
resolver := NewResolver(idx)
|
|
resolver.IgnorePolymorphicCircularReferences()
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.Resolve()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetInfiniteCircularReferences(), 0)
|
|
assert.Len(t, resolver.GetSafeCircularReferences(), 0)
|
|
assert.Len(t, resolver.GetIgnoredCircularPolyReferences(), 1)
|
|
|
|
}
|
|
|
|
func TestResolver_CatchInvalidMapPoly(t *testing.T) {
|
|
|
|
d := `openapi: 3.1.0
|
|
paths:
|
|
/test:
|
|
get:
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
components:
|
|
schemas:
|
|
Anything:
|
|
type: object
|
|
ObjectWithOneOf:
|
|
type: object
|
|
properties:
|
|
child:
|
|
oneOf:
|
|
$ref: '#/components/schemas/Anything'
|
|
required:
|
|
- child
|
|
`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
|
|
|
cf := CreateClosedAPIIndexConfig()
|
|
cf.IgnorePolymorphicCircularReferences = true
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, cf)
|
|
|
|
resolver := NewResolver(idx)
|
|
resolver.IgnorePolymorphicCircularReferences()
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.Resolve()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetInfiniteCircularReferences(), 0)
|
|
assert.Len(t, resolver.GetSafeCircularReferences(), 0)
|
|
assert.Len(t, resolver.GetIgnoredCircularPolyReferences(), 0)
|
|
|
|
}
|
|
|
|
func TestResolver_NotAllowedDeepCircle(t *testing.T) {
|
|
|
|
d := `openapi: 3.0
|
|
components:
|
|
schemas:
|
|
Three:
|
|
description: "test three"
|
|
properties:
|
|
bester:
|
|
"$ref": "#/components/schemas/Seven"
|
|
required:
|
|
- bester
|
|
Seven:
|
|
properties:
|
|
wow:
|
|
"$ref": "#/components/schemas/Three"
|
|
required:
|
|
- wow`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
|
|
|
idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig())
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.Resolve()
|
|
assert.Len(t, circ, 1)
|
|
assert.Len(t, resolver.GetInfiniteCircularReferences(), 1)
|
|
assert.Len(t, resolver.GetSafeCircularReferences(), 0)
|
|
|
|
}
|
|
|
|
func TestLocateRefEnd_WithResolve(t *testing.T) {
|
|
|
|
yml, _ := os.ReadFile("../../test_specs/first.yaml")
|
|
var bsn yaml.Node
|
|
_ = yaml.Unmarshal(yml, &bsn)
|
|
|
|
cf := CreateOpenAPIIndexConfig()
|
|
cf.BasePath = "../test_specs"
|
|
|
|
localFSConfig := &LocalFSConfig{
|
|
BaseDirectory: cf.BasePath,
|
|
FileFilters: []string{"first.yaml", "second.yaml", "third.yaml", "fourth.yaml"},
|
|
DirFS: os.DirFS(cf.BasePath),
|
|
}
|
|
localFs, _ := NewLocalFSWithConfig(localFSConfig)
|
|
rolo := NewRolodex(cf)
|
|
rolo.AddLocalFS(cf.BasePath, localFs)
|
|
rolo.SetRootNode(&bsn)
|
|
rolo.IndexTheRolodex()
|
|
|
|
wd, _ := os.Getwd()
|
|
cp, _ := filepath.Abs(filepath.Join(wd, "../test_specs/third.yaml"))
|
|
third := localFs.GetFiles()[cp]
|
|
refs := third.GetIndex().GetMappedReferences()
|
|
fullDef := fmt.Sprintf("%s#/properties/property/properties/statistics", cp)
|
|
ref := refs[fullDef]
|
|
|
|
assert.Equal(t, "statistics", ref.Name)
|
|
isRef, _, _ := utils.IsNodeRefValue(ref.Node)
|
|
assert.True(t, isRef)
|
|
|
|
// resolve the stack, it should convert the ref to a node.
|
|
rolo.Resolve()
|
|
|
|
isRef, _, _ = utils.IsNodeRefValue(ref.Node)
|
|
assert.False(t, isRef)
|
|
}
|
|
|
|
func TestResolveDoc_Issue195(t *testing.T) {
|
|
|
|
spec := `openapi: 3.0.1
|
|
info:
|
|
title: Some Example!
|
|
paths:
|
|
"/pet/findByStatus":
|
|
get:
|
|
responses:
|
|
default:
|
|
content:
|
|
application/json:
|
|
schema:
|
|
"$ref": https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore.yaml#/components/schemas/Error`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(spec), &rootNode)
|
|
|
|
// create an index config
|
|
config := CreateOpenAPIIndexConfig()
|
|
|
|
// the rolodex will automatically try and check for circular references, you don't want to do this
|
|
// if you're resolving the spec, as the node tree is marked as 'seen' and you won't be able to resolve
|
|
// correctly.
|
|
config.AvoidCircularReferenceCheck = true
|
|
|
|
// new in 0.13+ is the ability to add remote and local file systems to the index
|
|
// requires a new part, the rolodex. It holds all the indexes and knows where to find
|
|
// every reference across local and remote files.
|
|
rolodex := NewRolodex(config)
|
|
|
|
// add a new remote file system.
|
|
remoteFS, _ := NewRemoteFSWithConfig(config)
|
|
|
|
// add the remote file system to the rolodex
|
|
rolodex.AddRemoteFS("", remoteFS)
|
|
|
|
// set the root node of the rolodex, this is your spec.
|
|
rolodex.SetRootNode(&rootNode)
|
|
|
|
// index the rolodex
|
|
indexingError := rolodex.IndexTheRolodex()
|
|
if indexingError != nil {
|
|
panic(indexingError)
|
|
}
|
|
|
|
// resolve the rolodex
|
|
rolodex.Resolve()
|
|
|
|
// there should be no errors at this point
|
|
resolvingErrors := rolodex.GetCaughtErrors()
|
|
if resolvingErrors != nil {
|
|
panic(resolvingErrors)
|
|
}
|
|
|
|
// perform some lookups.
|
|
var nodes []*yaml.Node
|
|
|
|
// pull out schema type
|
|
path, _ := yamlpath.NewPath("$.paths./pet/findByStatus.get.responses.default.content['application/json'].schema.type")
|
|
nodes, _ = path.Find(&rootNode)
|
|
assert.Equal(t, nodes[0].Value, "object")
|
|
|
|
// pull out required array
|
|
path, _ = yamlpath.NewPath("$.paths./pet/findByStatus.get.responses.default.content['application/json'].schema.required")
|
|
nodes, _ = path.Find(&rootNode)
|
|
assert.Equal(t, nodes[0].Content[0].Value, "code")
|
|
assert.Equal(t, nodes[0].Content[1].Value, "message")
|
|
|
|
}
|
|
|
|
func TestDocument_LoopThroughAnArray(t *testing.T) {
|
|
|
|
var d = `openapi: "3.0.1"
|
|
components:
|
|
schemas:
|
|
B:
|
|
type: object
|
|
properties:
|
|
children:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/B'`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
|
|
|
config := CreateClosedAPIIndexConfig()
|
|
config.IgnoreArrayCircularReferences = true
|
|
idx := NewSpecIndexWithConfig(&rootNode, config)
|
|
|
|
resolver := NewResolver(idx)
|
|
resolver.IgnoreArrayCircularReferences()
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.Resolve()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetIgnoredCircularArrayReferences(), 1)
|
|
|
|
}
|
|
|
|
func TestDocument_ObjectWithPolyAndArray(t *testing.T) {
|
|
|
|
var d = `openapi: "3.0.1"
|
|
components:
|
|
schemas:
|
|
A:
|
|
type: object
|
|
properties: {}
|
|
B:
|
|
type: object
|
|
allOf:
|
|
- $ref: '#/components/schemas/A'
|
|
properties:
|
|
children:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/B'`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
|
|
|
config := CreateClosedAPIIndexConfig()
|
|
config.IgnoreArrayCircularReferences = true
|
|
idx := NewSpecIndexWithConfig(&rootNode, config)
|
|
|
|
resolver := NewResolver(idx)
|
|
resolver.IgnoreArrayCircularReferences()
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.Resolve()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetIgnoredCircularArrayReferences(), 1)
|
|
|
|
}
|
|
|
|
func TestDocument_ObjectWithMultiPolyAndArray(t *testing.T) {
|
|
|
|
var d = `openapi: "3.0.1"
|
|
components:
|
|
schemas:
|
|
A:
|
|
type: object
|
|
properties: {}
|
|
B:
|
|
type: object
|
|
allOf:
|
|
- $ref: '#/components/schemas/A'
|
|
oneOf:
|
|
- $ref: '#/components/schemas/B'
|
|
properties:
|
|
children:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/B'`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
|
|
|
config := CreateClosedAPIIndexConfig()
|
|
config.IgnoreArrayCircularReferences = true
|
|
idx := NewSpecIndexWithConfig(&rootNode, config)
|
|
|
|
resolver := NewResolver(idx)
|
|
resolver.IgnoreArrayCircularReferences()
|
|
assert.NotNil(t, resolver)
|
|
|
|
circ := resolver.Resolve()
|
|
assert.Len(t, circ, 0)
|
|
assert.Len(t, resolver.GetSafeCircularReferences(), 1)
|
|
assert.Len(t, resolver.GetIgnoredCircularArrayReferences(), 0)
|
|
|
|
}
|
|
|
|
func TestIssue_259(t *testing.T) {
|
|
|
|
var d = `openapi: 3.0.3
|
|
info:
|
|
description: test
|
|
title: test
|
|
version: test
|
|
paths: {}
|
|
components:
|
|
schemas:
|
|
test:
|
|
type: object
|
|
properties:
|
|
Reference:
|
|
$ref: '#/components/schemas/ReferenceType'
|
|
ReferenceType:
|
|
type: object
|
|
required: [$ref]
|
|
properties:
|
|
$ref:
|
|
type: string`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
|
|
|
config := CreateClosedAPIIndexConfig()
|
|
config.IgnoreArrayCircularReferences = true
|
|
idx := NewSpecIndexWithConfig(&rootNode, config)
|
|
|
|
resolver := NewResolver(idx)
|
|
resolver.IgnoreArrayCircularReferences()
|
|
assert.NotNil(t, resolver)
|
|
|
|
errs := resolver.Resolve()
|
|
assert.Len(t, errs, 0)
|
|
|
|
}
|
|
|
|
func TestIssue_481(t *testing.T) {
|
|
|
|
var d = `openapi: 3.0.1
|
|
components:
|
|
schemas:
|
|
PetPot:
|
|
type: object
|
|
properties:
|
|
value:
|
|
oneOf:
|
|
- type: array
|
|
items:
|
|
type: object
|
|
required:
|
|
- $ref
|
|
- value`
|
|
|
|
var rootNode yaml.Node
|
|
_ = yaml.Unmarshal([]byte(d), &rootNode)
|
|
|
|
config := CreateClosedAPIIndexConfig()
|
|
config.IgnoreArrayCircularReferences = true
|
|
idx := NewSpecIndexWithConfig(&rootNode, config)
|
|
|
|
resolver := NewResolver(idx)
|
|
assert.NotNil(t, resolver)
|
|
|
|
errs := resolver.Resolve()
|
|
assert.Len(t, errs, 0)
|
|
|
|
}
|