// 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.CreateOpenAPIIndexConfig())

	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())
}
