// Copyright 2022-2023 Princess B33f Heavy Industries / Dave Shanley // SPDX-License-Identifier: MIT package low import ( "crypto/sha256" "fmt" "os" "strings" "testing" "github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/resolver" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" ) func TestFindItemInMap(t *testing.T) { v := make(map[KeyReference[string]]ValueReference[string]) v[KeyReference[string]{ Value: "pizza", }] = ValueReference[string]{ Value: "pie", } assert.Equal(t, "pie", FindItemInMap("pizza", v).Value) } func TestFindItemInMap_WrongCase(t *testing.T) { v := make(map[KeyReference[string]]ValueReference[string]) v[KeyReference[string]{ Value: "pizza", }] = ValueReference[string]{ Value: "pie", } assert.Equal(t, "pie", FindItemInMap("PIZZA", v).Value) } func TestFindItemInMap_Error(t *testing.T) { v := make(map[KeyReference[string]]ValueReference[string]) v[KeyReference[string]{ Value: "pizza", }] = ValueReference[string]{ Value: "pie", } assert.Nil(t, FindItemInMap("nuggets", v)) } func TestLocateRefNode(t *testing.T) { yml := `components: schemas: cake: description: hello` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `$ref: '#/components/schemas/cake'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) located, _ := LocateRefNode(cNode.Content[0], idx) assert.NotNil(t, located) } func TestLocateRefNode_BadNode(t *testing.T) { yml := `components: schemas: cake: description: hello` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `yes: mate` // useless. var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) located, err := LocateRefNode(cNode.Content[0], idx) // should both be empty. assert.Nil(t, located) assert.Nil(t, err) } func TestLocateRefNode_Path(t *testing.T) { yml := `paths: /burger/time: description: hello` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `$ref: '#/paths/~1burger~1time'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) located, _ := LocateRefNode(cNode.Content[0], idx) assert.NotNil(t, located) } func TestLocateRefNode_Path_NotFound(t *testing.T) { yml := `paths: /burger/time: description: hello` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `$ref: '#/paths/~1burger~1time-somethingsomethingdarkside-somethingsomethingcomplete'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) located, err := LocateRefNode(cNode.Content[0], idx) assert.Nil(t, located) assert.Error(t, err) } type pizza struct { Description NodeReference[string] } func (p *pizza) Build(_ *yaml.Node, _ *index.SpecIndex) error { return nil } func TestExtractObject(t *testing.T) { yml := `components: schemas: pizza: description: hello` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `tags: description: hello pizza` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) tag, err := ExtractObject[*pizza]("tags", &cNode, idx) assert.NoError(t, err) assert.NotNil(t, tag) assert.Equal(t, "hello pizza", tag.Value.Description.Value) } func TestExtractObject_Ref(t *testing.T) { yml := `components: schemas: pizza: description: hello pizza` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `tags: $ref: '#/components/schemas/pizza'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) tag, err := ExtractObject[*pizza]("tags", &cNode, idx) assert.NoError(t, err) assert.NotNil(t, tag) assert.Equal(t, "hello pizza", tag.Value.Description.Value) } func TestExtractObject_DoubleRef(t *testing.T) { yml := `components: schemas: cake: description: cake time! pizza: $ref: '#/components/schemas/cake'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `tags: $ref: '#/components/schemas/pizza'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) tag, err := ExtractObject[*pizza]("tags", &cNode, idx) assert.NoError(t, err) assert.NotNil(t, tag) assert.Equal(t, "cake time!", tag.Value.Description.Value) } func TestExtractObject_DoubleRef_Circular(t *testing.T) { yml := `components: schemas: loopy: $ref: '#/components/schemas/cake' cake: $ref: '#/components/schemas/loopy' pizza: $ref: '#/components/schemas/cake'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) // circular references are detected by the resolver, so lets run it! resolv := resolver.NewResolver(idx) assert.Len(t, resolv.CheckForCircularReferences(), 1) yml = `tags: $ref: '#/components/schemas/pizza'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) _, err := ExtractObject[*pizza]("tags", &cNode, idx) assert.Error(t, err) assert.Equal(t, "cake -> loopy -> cake", idx.GetCircularReferences()[0].GenerateJourneyPath()) } func TestExtractObject_DoubleRef_Circular_Fail(t *testing.T) { yml := `components: schemas: loopy: $ref: '#/components/schemas/cake' cake: $ref: '#/components/schemas/loopy' pizza: $ref: '#/components/schemas/cake'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) // circular references are detected by the resolver, so lets run it! resolv := resolver.NewResolver(idx) assert.Len(t, resolv.CheckForCircularReferences(), 1) yml = `tags: $ref: #BORK` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) _, err := ExtractObject[*pizza]("tags", &cNode, idx) assert.Error(t, err) } func TestExtractObject_DoubleRef_Circular_Direct(t *testing.T) { yml := `components: schemas: loopy: $ref: '#/components/schemas/cake' cake: $ref: '#/components/schemas/loopy' pizza: $ref: '#/components/schemas/cake'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) // circular references are detected by the resolver, so lets run it! resolv := resolver.NewResolver(idx) assert.Len(t, resolv.CheckForCircularReferences(), 1) yml = `$ref: '#/components/schemas/pizza'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) _, err := ExtractObject[*pizza]("tags", cNode.Content[0], idx) assert.Error(t, err) assert.Equal(t, "cake -> loopy -> cake", idx.GetCircularReferences()[0].GenerateJourneyPath()) } func TestExtractObject_DoubleRef_Circular_Direct_Fail(t *testing.T) { yml := `components: schemas: loopy: $ref: '#/components/schemas/cake' cake: $ref: '#/components/schemas/loopy' pizza: $ref: '#/components/schemas/cake'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) // circular references are detected by the resolver, so lets run it! resolv := resolver.NewResolver(idx) assert.Len(t, resolv.CheckForCircularReferences(), 1) yml = `$ref: '#/components/schemas/why-did-westworld-have-to-end-so-poorly-ffs'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) _, err := ExtractObject[*pizza]("tags", cNode.Content[0], idx) assert.Error(t, err) } type test_borked struct { DontWork int } func (t test_borked) Build(root *yaml.Node, idx *index.SpecIndex) error { return fmt.Errorf("I am always going to fail, every thing") } type test_noGood struct { DontWork int } func (t *test_noGood) Build(root *yaml.Node, idx *index.SpecIndex) error { return fmt.Errorf("I am always going to fail a core build") } type test_almostGood struct { AlmostWork NodeReference[int] } func (t *test_almostGood) Build(root *yaml.Node, idx *index.SpecIndex) error { return fmt.Errorf("I am always going to fail a build out") } type test_Good struct { AlmostWork NodeReference[int] } func (t *test_Good) Build(root *yaml.Node, idx *index.SpecIndex) error { return nil } func TestExtractObject_BadLowLevelModel(t *testing.T) { yml := `components: schemas: hey:` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `thing: dontWork: 123` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) _, err := ExtractObject[*test_noGood]("thing", &cNode, idx) assert.Error(t, err) } func TestExtractObject_BadBuild(t *testing.T) { yml := `components: schemas: hey:` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `thing: dontWork: 123` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) _, err := ExtractObject[*test_almostGood]("thing", &cNode, idx) assert.Error(t, err) } func TestExtractObject_BadLabel(t *testing.T) { yml := `components: schemas: hey:` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `thing: dontWork: 123` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) res, err := ExtractObject[*test_almostGood]("ding", &cNode, idx) assert.Nil(t, res.Value) assert.NoError(t, err) } func TestExtractObject_PathIsCircular(t *testing.T) { // first we need an index. yml := `paths: '/something/here': post: $ref: '#/paths/~1something~1there/post' '/something/there': post: $ref: '#/paths/~1something~1here/post'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) resolve := resolver.NewResolver(idx) errs := resolve.CheckForCircularReferences() assert.Len(t, errs, 1) yml = `thing: $ref: '#/paths/~1something~1here/post'` var rootNode yaml.Node mErr = yaml.Unmarshal([]byte(yml), &rootNode) assert.NoError(t, mErr) res, err := ExtractObject[*test_Good]("thing", &rootNode, idx) assert.NotNil(t, res.Value) assert.Error(t, err) // circular error would have been thrown. } func TestExtractObject_PathIsCircular_IgnoreErrors(t *testing.T) { // first we need an index. yml := `paths: '/something/here': post: $ref: '#/paths/~1something~1there/post' '/something/there': post: $ref: '#/paths/~1something~1here/post'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) // disable circular ref checking. idx.SetAllowCircularReferenceResolving(true) resolve := resolver.NewResolver(idx) errs := resolve.CheckForCircularReferences() assert.Len(t, errs, 1) yml = `thing: $ref: '#/paths/~1something~1here/post'` var rootNode yaml.Node mErr = yaml.Unmarshal([]byte(yml), &rootNode) assert.NoError(t, mErr) res, err := ExtractObject[*test_Good]("thing", &rootNode, idx) assert.NotNil(t, res.Value) assert.NoError(t, err) // circular error would have been thrown, but we're ignoring them. } func TestExtractObjectRaw(t *testing.T) { yml := `components: schemas: pizza: description: hello` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `description: hello pizza` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) tag, err, _, _ := ExtractObjectRaw[*pizza](cNode.Content[0], idx) assert.NoError(t, err) assert.NotNil(t, tag) assert.Equal(t, "hello pizza", tag.Description.Value) } func TestExtractObjectRaw_With_Ref(t *testing.T) { yml := `components: schemas: pizza: description: hello` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `$ref: '#/components/schemas/pizza'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) tag, err, isRef, rv := ExtractObjectRaw[*pizza](cNode.Content[0], idx) assert.NoError(t, err) assert.NotNil(t, tag) assert.Equal(t, "hello", tag.Description.Value) assert.True(t, isRef) assert.Equal(t, "#/components/schemas/pizza", rv) } func TestExtractObjectRaw_Ref_Circular(t *testing.T) { yml := `components: schemas: pizza: $ref: '#/components/schemas/pie' pie: $ref: '#/components/schemas/pizza'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) resolve := resolver.NewResolver(idx) errs := resolve.CheckForCircularReferences() assert.Len(t, errs, 1) yml = `$ref: '#/components/schemas/pizza'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) tag, err, _, _ := ExtractObjectRaw[*pizza](cNode.Content[0], idx) assert.Error(t, err) assert.NotNil(t, tag) } func TestExtractObjectRaw_RefBroken(t *testing.T) { yml := `components: schemas: pizza: description: hey!` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `$ref: '#/components/schemas/lost-in-space'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) tag, err, _, _ := ExtractObjectRaw[*pizza](cNode.Content[0], idx) assert.Error(t, err) assert.Nil(t, tag) } func TestExtractObjectRaw_Ref_NonBuildable(t *testing.T) { yml := `components: schemas: pizza: description: hey!` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `dontWork: 1'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) _, err, _, _ := ExtractObjectRaw[*test_noGood](cNode.Content[0], idx) assert.Error(t, err) } func TestExtractObjectRaw_Ref_AlmostBuildable(t *testing.T) { yml := `components: schemas: pizza: description: hey!` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `almostWork: 1'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) _, err, _, _ := ExtractObjectRaw[*test_almostGood](cNode.Content[0], idx) assert.Error(t, err) } func TestExtractArray(t *testing.T) { yml := `components: schemas: pizza: description: hello` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `things: - description: one - description: two - description: three` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) things, _, _, err := ExtractArray[*pizza]("things", cNode.Content[0], idx) assert.NoError(t, err) assert.NotNil(t, things) assert.Equal(t, "one", things[0].Value.Description.Value) assert.Equal(t, "two", things[1].Value.Description.Value) assert.Equal(t, "three", things[2].Value.Description.Value) } func TestExtractArray_Ref(t *testing.T) { yml := `components: schemas: things: - description: one - description: two - description: three` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `$ref: '#/components/schemas/things'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) things, _, _, err := ExtractArray[*pizza]("things", cNode.Content[0], idx) assert.NoError(t, err) assert.NotNil(t, things) assert.Equal(t, "one", things[0].Value.Description.Value) assert.Equal(t, "two", things[1].Value.Description.Value) assert.Equal(t, "three", things[2].Value.Description.Value) } func TestExtractArray_Ref_Unbuildable(t *testing.T) { yml := `components: schemas: things: - description: one - description: two - description: three` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `$ref: '#/components/schemas/things'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) things, _, _, err := ExtractArray[*test_noGood]("", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestExtractArray_Ref_Circular(t *testing.T) { yml := `components: schemas: thongs: $ref: '#/components/schemas/things' things: $ref: '#/components/schemas/thongs'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) resolve := resolver.NewResolver(idx) errs := resolve.CheckForCircularReferences() assert.Len(t, errs, 1) yml = `$ref: '#/components/schemas/things'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) things, _, _, err := ExtractArray[*test_Good]("", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestExtractArray_Ref_Bad(t *testing.T) { yml := `components: schemas: thongs: $ref: '#/components/schemas/things' things: $ref: '#/components/schemas/thongs'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) resolve := resolver.NewResolver(idx) errs := resolve.CheckForCircularReferences() assert.Len(t, errs, 1) yml = `$ref: '#/components/schemas/let-us-eat-cake'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) things, _, _, err := ExtractArray[*test_Good]("", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestExtractArray_Ref_Nested(t *testing.T) { yml := `components: schemas: thongs: $ref: '#/components/schemas/things' things: $ref: '#/components/schemas/thongs'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) resolve := resolver.NewResolver(idx) errs := resolve.CheckForCircularReferences() assert.Len(t, errs, 1) yml = `limes: $ref: '#/components/schemas/let-us-eat-cake'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) things, _, _, err := ExtractArray[*test_Good]("limes", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestExtractArray_Ref_Nested_Circular(t *testing.T) { yml := `components: schemas: thongs: $ref: '#/components/schemas/things' things: $ref: '#/components/schemas/thongs'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) resolve := resolver.NewResolver(idx) errs := resolve.CheckForCircularReferences() assert.Len(t, errs, 1) yml = `limes: - $ref: '#/components/schemas/things'` var cNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &cNode) things, _, _, err := ExtractArray[*test_Good]("limes", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 1) } func TestExtractArray_Ref_Nested_BadRef(t *testing.T) { yml := `components: schemas: thongs: allOf: - $ref: '#/components/schemas/things' things: oneOf: - type: string` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `limes: - $ref: '#/components/schemas/thangs'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractArray[*test_Good]("limes", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestExtractArray_Ref_Nested_CircularFlat(t *testing.T) { yml := `components: schemas: thongs: $ref: '#/components/schemas/things' things: $ref: '#/components/schemas/thongs'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) resolve := resolver.NewResolver(idx) errs := resolve.CheckForCircularReferences() assert.Len(t, errs, 1) yml = `limes: $ref: '#/components/schemas/things'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractArray[*test_Good]("limes", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestExtractArray_BadBuild(t *testing.T) { yml := `components: schemas: thongs:` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `limes: - dontWork: 1` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractArray[*test_noGood]("limes", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestExtractExample_String(t *testing.T) { yml := `hi` var e yaml.Node _ = yaml.Unmarshal([]byte(yml), &e) exp := ExtractExample(e.Content[0], e.Content[0]) assert.NotNil(t, exp.Value) assert.Equal(t, "hi", exp.Value) } func TestExtractExample_Map(t *testing.T) { yml := `one: two` var e yaml.Node _ = yaml.Unmarshal([]byte(yml), &e) exp := ExtractExample(e.Content[0], e.Content[0]) assert.NotNil(t, exp.Value) if n, ok := exp.Value.(map[string]interface{}); ok { assert.Equal(t, "two", n["one"]) } else { panic("example unpacked incorrectly.") } } func TestExtractExample_Array(t *testing.T) { yml := `- hello` var e yaml.Node _ = yaml.Unmarshal([]byte(yml), &e) exp := ExtractExample(e.Content[0], e.Content[0]) assert.NotNil(t, exp.Value) if n, ok := exp.Value.([]interface{}); ok { assert.Equal(t, "hello", n[0]) } else { panic("example unpacked incorrectly.") } } func TestExtractMapFlatNoLookup(t *testing.T) { yml := `components:` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `x-hey: you one: description: two` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, err := ExtractMapNoLookup[*test_Good](cNode.Content[0], idx) assert.NoError(t, err) assert.Len(t, things, 1) } func TestExtractMap_NoLookupWithExtensions(t *testing.T) { yml := `components:` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `x-hey: you one: x-choo: choo` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, err := ExtractMapNoLookupExtensions[*test_Good](cNode.Content[0], idx, true) assert.NoError(t, err) assert.Len(t, things, 2) for k, v := range things { if k.Value == "x-hey" { continue } assert.Equal(t, "one", k.Value) assert.Len(t, v.ValueNode.Content, 2) } } func TestExtractMap_NoLookupWithExtensions_UsingMerge(t *testing.T) { yml := `components:` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `x-yeah: &yeah night: fun x-hey: you one: x-choo: choo <<: *yeah` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, err := ExtractMapNoLookupExtensions[*test_Good](cNode.Content[0], idx, true) assert.NoError(t, err) assert.Len(t, things, 4) } func TestExtractMap_NoLookupWithoutExtensions(t *testing.T) { yml := `components:` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `x-hey: you one: x-choo: choo` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, err := ExtractMapNoLookupExtensions[*test_Good](cNode.Content[0], idx, false) assert.NoError(t, err) assert.Len(t, things, 1) for k := range things { assert.Equal(t, "one", k.Value) } } func TestExtractMap_WithExtensions(t *testing.T) { yml := `components:` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `x-hey: you one: x-choo: choo` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMapExtensions[*test_Good]("one", cNode.Content[0], idx, true) assert.NoError(t, err) assert.Len(t, things, 1) } func TestExtractMap_WithoutExtensions(t *testing.T) { yml := `components:` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `x-hey: you one: x-choo: choo` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMapExtensions[*test_Good]("one", cNode.Content[0], idx, false) assert.NoError(t, err) assert.Len(t, things, 0) } func TestExtractMapFlatNoLookup_Ref(t *testing.T) { yml := `components: schemas: pizza: description: tasty!` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `x-hey: you one: $ref: '#/components/schemas/pizza'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, err := ExtractMapNoLookup[*test_Good](cNode.Content[0], idx) assert.NoError(t, err) assert.Len(t, things, 1) } func TestExtractMapFlatNoLookup_Ref_Bad(t *testing.T) { yml := `components: schemas: pizza: description: tasty!` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `x-hey: you one: $ref: '#/components/schemas/no-where-out-there'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, err := ExtractMapNoLookup[*test_Good](cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestExtractMapFlatNoLookup_Ref_Circular(t *testing.T) { yml := `components: schemas: thongs: $ref: '#/components/schemas/things' things: $ref: '#/components/schemas/thongs'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) resolve := resolver.NewResolver(idx) errs := resolve.CheckForCircularReferences() assert.Len(t, errs, 1) yml = `x-hey: you one: $ref: '#/components/schemas/things'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, err := ExtractMapNoLookup[*test_Good](cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 1) } func TestExtractMapFlatNoLookup_Ref_BadBuild(t *testing.T) { yml := `components: schemas: pizza: dontWork: 1` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `x-hey: you hello: $ref: '#/components/schemas/pizza'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, err := ExtractMapNoLookup[*test_noGood](cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestExtractMapFlatNoLookup_Ref_AlmostBuild(t *testing.T) { yml := `components: schemas: pizza: description: tasty!` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `x-hey: you one: $ref: '#/components/schemas/pizza'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, err := ExtractMapNoLookup[*test_almostGood](cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestExtractMapFlat(t *testing.T) { yml := `components:` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `x-hey: you one: description: two` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMap[*test_Good]("one", cNode.Content[0], idx) assert.NoError(t, err) assert.Len(t, things, 1) } func TestExtractMapFlat_Ref(t *testing.T) { yml := `components: schemas: stank: things: almostWork: 99` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `x-hey: you one: $ref: '#/components/schemas/stank'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMap[*test_Good]("one", cNode.Content[0], idx) assert.NoError(t, err) assert.Len(t, things, 1) for k := range things { assert.Equal(t, 99, things[k].Value.AlmostWork.Value) } } func TestExtractMapFlat_DoubleRef(t *testing.T) { yml := `components: schemas: stank: almostWork: 99` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `one: nice: $ref: '#/components/schemas/stank'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMap[*test_Good]("one", cNode.Content[0], idx) assert.NoError(t, err) assert.Len(t, things, 1) for k := range things { assert.Equal(t, 99, things[k].Value.AlmostWork.Value) } } func TestExtractMapFlat_DoubleRef_Error(t *testing.T) { yml := `components: schemas: stank: things: almostWork: 99` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `one: nice: $ref: '#/components/schemas/stank'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMap[*test_almostGood]("one", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestExtractMapFlat_DoubleRef_Error_NotFound(t *testing.T) { yml := `components: schemas: stank: things: almostWork: 99` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `one: nice: $ref: '#/components/schemas/stanky-panky'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMap[*test_almostGood]("one", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestExtractMapFlat_DoubleRef_Circles(t *testing.T) { yml := `components: schemas: stonk: $ref: '#/components/schemas/stank' stank: $ref: '#/components/schemas/stonk'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) resolve := resolver.NewResolver(idx) errs := resolve.CheckForCircularReferences() assert.Len(t, errs, 1) yml = `one: nice: $ref: '#/components/schemas/stank'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMap[*test_Good]("one", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 1) } func TestExtractMapFlat_Ref_Error(t *testing.T) { yml := `components: schemas: stank: x-smells: bad things: almostWork: 99` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `one: $ref: '#/components/schemas/stank'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMap[*test_almostGood]("one", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestExtractMapFlat_Ref_Circ_Error(t *testing.T) { yml := `components: schemas: stink: $ref: '#/components/schemas/stank' stank: $ref: '#/components/schemas/stink'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) resolve := resolver.NewResolver(idx) errs := resolve.CheckForCircularReferences() assert.Len(t, errs, 1) yml = `$ref: '#/components/schemas/stank'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMap[*test_Good]("one", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 1) } func TestExtractMapFlat_Ref_Nested_Circ_Error(t *testing.T) { yml := `components: schemas: stink: $ref: '#/components/schemas/stank' stank: $ref: '#/components/schemas/stink'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) resolve := resolver.NewResolver(idx) errs := resolve.CheckForCircularReferences() assert.Len(t, errs, 1) yml = `one: $ref: '#/components/schemas/stank'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMap[*test_Good]("one", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 1) } func TestExtractMapFlat_Ref_Nested_Error(t *testing.T) { yml := `components: schemas: stink: $ref: '#/components/schemas/stank' stank: $ref: '#/components/schemas/none'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `one: $ref: '#/components/schemas/somewhere-else'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMap[*test_Good]("one", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestExtractMapFlat_BadKey_Ref_Nested_Error(t *testing.T) { yml := `components: schemas: stink: $ref: '#/components/schemas/stank' stank: $ref: '#/components/schemas/none'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) yml = `one: $ref: '#/components/schemas/somewhere-else'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMap[*test_Good]("not-even-there", cNode.Content[0], idx) assert.NoError(t, err) assert.Len(t, things, 0) } func TestExtractMapFlat_Ref_Bad(t *testing.T) { yml := `components: schemas: stink: $ref: '#/components/schemas/stank' stank: $ref: '#/components/schemas/none'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(yml), &idxNode) assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) resolve := resolver.NewResolver(idx) errs := resolve.CheckForCircularReferences() assert.Len(t, errs, 1) yml = `$ref: '#/components/schemas/somewhere-else'` var cNode yaml.Node e := yaml.Unmarshal([]byte(yml), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMap[*test_Good]("one", cNode.Content[0], idx) assert.Error(t, err) assert.Len(t, things, 0) } func TestLocateRefNode_RemoteFile(t *testing.T) { ymlFile := fmt.Sprintf(`components: schemas: hey: $ref: '%s#/components/schemas/hey'`, "remote.yaml") ymlRemote := `components: schemas: hey: AlmostWork: 999` _ = os.WriteFile("remote.yaml", []byte(ymlRemote), 0665) defer os.Remove("remote.yaml") ymlLocal := `$ref: '#/components/schemas/hey'` var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(ymlFile), &idxNode) // an empty index. assert.NoError(t, mErr) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) var cNode yaml.Node e := yaml.Unmarshal([]byte(ymlLocal), &cNode) assert.NoError(t, e) things, _, _, err := ExtractMap[*test_Good]("one", cNode.Content[0], idx) assert.NoError(t, err) assert.Len(t, things, 1) } func TestExtractExtensions(t *testing.T) { yml := `x-bing: ding x-bong: 1 x-ling: true x-long: 0.99 x-fish: woo: yeah x-tacos: [1,2,3]` var idxNode yaml.Node _ = yaml.Unmarshal([]byte(yml), &idxNode) r := ExtractExtensions(idxNode.Content[0]) assert.Len(t, r, 6) for i := range r { switch i.Value { case "x-bing": assert.Equal(t, "ding", r[i].Value) case "x-bong": assert.Equal(t, int64(1), r[i].Value) case "x-ling": assert.Equal(t, true, r[i].Value) case "x-long": assert.Equal(t, 0.99, r[i].Value) case "x-fish": if a, ok := r[i].Value.(map[string]interface{}); ok { assert.Equal(t, "yeah", a["woo"]) } else { panic("should not fail casting") } case "x-tacos": assert.Len(t, r[i].Value, 3) } } } type test_fresh struct { val string thang *bool } func (f test_fresh) Hash() [32]byte { var data []string if f.val != "" { data = append(data, f.val) } if f.thang != nil { data = append(data, fmt.Sprintf("%v", *f.thang)) } return sha256.Sum256([]byte(strings.Join(data, "|"))) } func TestAreEqual(t *testing.T) { assert.True(t, AreEqual(test_fresh{val: "hello"}, test_fresh{val: "hello"})) assert.False(t, AreEqual(test_fresh{val: "hello"}, test_fresh{val: "goodbye"})) assert.False(t, AreEqual(nil, nil)) } func TestGenerateHashString(t *testing.T) { assert.Equal(t, "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", GenerateHashString(test_fresh{val: "hello"})) assert.Equal(t, "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", GenerateHashString("hello")) } func TestGenerateHashString_Pointer(t *testing.T) { val := true assert.Equal(t, "b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b", GenerateHashString(test_fresh{thang: &val})) assert.Equal(t, "b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b", GenerateHashString(&val)) } func TestSetReference(t *testing.T) { type testObj struct { *Reference } n := testObj{Reference: &Reference{}} SetReference(&n, "#/pigeon/street") assert.Equal(t, "#/pigeon/street", n.GetReference()) } func TestSetReference_nil(t *testing.T) { type testObj struct { *Reference } n := testObj{Reference: &Reference{}} SetReference(nil, "#/pigeon/street") assert.NotEqual(t, "#/pigeon/street", n.GetReference()) }