// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT

package model

import (
	"context"
	"os"
	"testing"

	"github.com/pb33f/libopenapi/datamodel/low"
	v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
	v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
	"github.com/stretchr/testify/assert"
	"go.yaml.in/yaml/v4"
)

// TestMain clears the hash cache before and after running tests to prevent test pollution
func TestMain(m *testing.M) {
	// Clear hash cache before tests
	low.ClearHashCache()

	// Run tests
	code := m.Run()

	// Clean up after tests
	low.ClearHashCache()

	os.Exit(code)
}

// cleanHashCacheForTest clears the hash cache and sets up cleanup for individual tests
func cleanHashCacheForTest(t *testing.T) {
	low.ClearHashCache()
	t.Cleanup(func() {
		low.ClearHashCache()
	})
}

func TestComparePathItem_V2(t *testing.T) {
	cleanHashCacheForTest(t)

	left := `get:
  description: get me
put:
  description: put me
post:
  description: post me
delete:
  description: delete me
patch:
  description: patch me
options:
  description: options
head:
  description: head
parameters:
  - in: head
x-thing: thang.`

	right := left

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v2.PathItem
	var rDoc v2.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Nil(t, extChanges)
}

func TestComparePathItem_V2_ModifyOps(t *testing.T) {
	left := `get:
  description: get me
put:
  description: put me
post:
  description: post me
delete:
  description: delete me
patch:
  description: patch me
options:
  description: options
head:
  description: head
parameters:
  - in: head
x-thing: thang.`

	right := `get:
  description: get me out
put:
  description: put me in
post:
  description: post me there
delete:
  description: delete me please
patch:
  description: patch me now
options:
  description: options vested
head:
  description: heads up
parameters:
  - in: head
x-thing: ding-a-ling`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v2.PathItem
	var rDoc v2.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 8, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 8)
	assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V2_ModifyParam(t *testing.T) {
	cleanHashCacheForTest(t)

	left := `get:
  description: get me
parameters:
  - name: cake
    in: space
  - in: head
    name: eggs`

	right := `get:
  description: get me
parameters:
  - name: cake
    in: love
  - in: code
    name: eggs`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v2.PathItem
	var rDoc v2.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 2, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 2)
	assert.Equal(t, 2, extChanges.TotalBreakingChanges())
	assert.Equal(t, Modified, extChanges.ParameterChanges[0].Changes[0].ChangeType)
	assert.Equal(t, v3.InLabel, extChanges.ParameterChanges[0].Changes[0].Property)
}

func TestComparePathItem_V2_AddParam(t *testing.T) {
	left := `get:
  description: get me
parameters:
  - name: cake
    in: love
  - in: code
    name: eggs`

	right := `get:
  description: get me
parameters:
  - name: cake
    in: love
  - in: code
    name: eggs
  - in: tune
    name: melody
    required: true`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v2.PathItem
	var rDoc v2.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 1)
	assert.Equal(t, 1, extChanges.TotalBreakingChanges())
	assert.Equal(t, ObjectAdded, extChanges.Changes[0].ChangeType)
}

func TestComparePathItem_V2_RemoveParam(t *testing.T) {
	left := `get:
  description: get me
parameters:
  - name: cake
    in: love
  - in: code
    name: eggs`

	right := `get:
  description: get me
parameters:
  - name: cake
    in: love
  - in: code
    name: eggs
  - in: tune
    name: melody`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v2.PathItem
	var rDoc v2.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&rDoc, &lDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 1)
	assert.Equal(t, 1, extChanges.TotalBreakingChanges())
	assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType)
}

func TestComparePathItem_V2_AddParametersToPath(t *testing.T) {
	left := `get:
  description: get me`

	right := `get:
  description: get me
parameters:
  - name: cake
    in: love
  - in: code
    name: eggs
  - in: tune
    name: melody`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v2.PathItem
	var rDoc v2.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Equal(t, 0, extChanges.TotalBreakingChanges())
	assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType)
	assert.Equal(t, v3.ParametersLabel, extChanges.Changes[0].Property)
}

func TestComparePathItem_V2_RemoveParametersToPath(t *testing.T) {
	left := `get:
  description: get me
parameters:
  - name: cake
    in: love
  - in: code
    name: eggs
  - in: tune
    name: melody`

	right := `get:
  description: get me`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v2.PathItem
	var rDoc v2.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Equal(t, 1, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V2_AddMethods(t *testing.T) {
	left := `parameters:
  - in: head`

	right := `get:
  description: get me out
put:
  description: put me in
post:
  description: post me there
delete:
  description: delete me please
patch:
  description: patch me now
options:
  description: options vested
head:
  description: heads up
parameters:
  - in: head`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v2.PathItem
	var rDoc v2.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 7, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 7)
	assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V2_RemoveMethods(t *testing.T) {
	left := `parameters:
  - in: head`

	right := `get:
  description: get me out
put:
  description: put me in
post:
  description: post me there
delete:
  description: delete me please
patch:
  description: patch me now
options:
  description: options vested
head:
  description: heads up
parameters:
  - in: head`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v2.PathItem
	var rDoc v2.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&rDoc, &lDoc)
	assert.Equal(t, 7, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 7)
	assert.Equal(t, 7, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V3(t *testing.T) {
	left := `summary: something
description: nice
get:
  description: get me
put:
  description: put me
post:
  description: post me
delete:
  description: delete me
patch:
  description: patch me
options:
  description: options
head:
  description: head
trace:
  description: outerspace
servers:
  - url: https://pb33f.io
parameters:
  - in: head
x-thing: thang.`

	right := left

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v3.PathItem
	var rDoc v3.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 0, len(extChanges.GetAllChanges()))
}

func TestComparePathItem_V3_Modify(t *testing.T) {
	left := `summary: something
description: nice
get:
  description: get me
put:
  description: put me
post:
  description: post me
delete:
  description: delete me
patch:
  description: patch me
options:
  description: options
head:
  description: head
trace:
  description: outerspace
servers:
  - url: https://pb33f.io
parameters:
  - in: head
x-thing: thang.
additionalOperations:
  spinner:
    description: spanner`

	right := `summary: something cute
description: nice puppy
get:
  description: get me out
put:
  description: put me in
post:
  description: post me back
delete:
  description: delete me please
patch:
  description: patch me now
options:
  description: options when
head:
  description: head down
trace:
  description: outerspace race
servers:
  - url: https://pb33f.io
    description: beefy goodness
parameters:
  - in: head
x-thing: dang.
additionalOperations:
  spinner:
    description: lemons`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v3.PathItem
	var rDoc v3.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 13, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 13)
	assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V3_AdditionalMethodsRemove(t *testing.T) {
	left := `additionalOperations:
  spinner:
    description: spanner
  dinner:
    description: dinner time`

	right := `
additionalOperations:
  spinner:
    description: spanner`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v3.PathItem
	var rDoc v3.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 1)
	assert.Equal(t, 1, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V3_AdditionalMethodsAdd(t *testing.T) {
	left := `additionalOperations:
  spinner:
    description: spanner
  dinner:
    description: dinner time`

	right := `
additionalOperations:
  spinner:
    description: spanner`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v3.PathItem
	var rDoc v3.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&rDoc, &lDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 1)
	assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V3_AdditionalMethodsAddTop(t *testing.T) {
	left := `description: nice shoes!`

	right := `description: nice shoes!
additionalOperations:
  spinner:
    description: spanner`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v3.PathItem
	var rDoc v3.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 1)
	assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V3_AdditionalMethodsRemoveTop(t *testing.T) {
	left := `description: nice shoes!`

	right := `description: nice shoes!
additionalOperations:
  spinner:
    description: spanner`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v3.PathItem
	var rDoc v3.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&rDoc, &lDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 1)
	assert.Equal(t, 1, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V3_AddParams(t *testing.T) {
	left := `summary: something`

	right := `summary: something
parameters:
  - in: head`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v3.PathItem
	var rDoc v3.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 1)
	assert.Equal(t, 0, extChanges.TotalBreakingChanges())
	assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType)
	assert.Equal(t, v3.ParametersLabel, extChanges.Changes[0].Property)
}

func TestComparePathItem_V3_RemoveParams(t *testing.T) {
	left := `summary: something`

	right := `summary: something
parameters:
  - in: head`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v3.PathItem
	var rDoc v3.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItemsV3(&rDoc, &lDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 1)
	assert.Equal(t, 1, extChanges.TotalBreakingChanges())
	assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType)
	assert.Equal(t, v3.ParametersLabel, extChanges.Changes[0].Property)
}

func TestComparePathItem_V3_AddMethods(t *testing.T) {
	left := `summary: something`

	right := `summary: something
get:
  description: get me out
put:
  description: put me in
post:
  description: post me back
delete:
  description: delete me please
patch:
  description: patch me now
options:
  description: options when
head:
  description: head down
trace:
  description: outerspace race`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v3.PathItem
	var rDoc v3.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 8, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 8)
	assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V3_RemoveMethods(t *testing.T) {
	left := `summary: something`

	right := `summary: something
get:
  description: get me out
put:
  description: put me in
post:
  description: post me back
delete:
  description: delete me please
patch:
  description: patch me now
options:
  description: options when
head:
  description: head down
trace:
  description: outerspace race`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v3.PathItem
	var rDoc v3.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&rDoc, &lDoc)
	assert.Equal(t, 8, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 8)
	assert.Equal(t, 8, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V3_AddParam(t *testing.T) {
	left := `summary: hello`

	right := `summary: hello
parameters:
  - in: head
    name: burgerId`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v3.PathItem
	var rDoc v3.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 1)
	assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V2_AddParamOptional(t *testing.T) {
	left := `summary: hello`

	right := `summary: hello
parameters:
  - in: head
    name: burgerId`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v2.PathItem
	var rDoc v2.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 1)
	assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V3_AddParamRequired(t *testing.T) {
	left := `summary: hello`

	right := `summary: hello
parameters:
  - in: head
    name: burgerId
    required: true`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v3.PathItem
	var rDoc v3.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 1)
	assert.Equal(t, 1, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V2_AddParamRequired(t *testing.T) {
	left := `summary: hello`

	right := `summary: hello
parameters:
  - in: head
    name: burgerId
    required: true`

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v2.PathItem
	var rDoc v2.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 1)
	assert.Equal(t, 1, extChanges.TotalBreakingChanges())
}

func TestComparePathItem_V3_AddEndpointLevelOptionalQueryParam(t *testing.T) {
	left := []byte(`get: {}
parameters:
  - in: query
    name: test
    required: true
    schema:
      type: string
`)

	right := []byte(`get: {}
parameters:
  - in: query
    name: test
    required: true
    schema:
      type: string
  - in: query
    name: test2
    required: false
    schema:
      type: string
`)

	var lNode, rNode yaml.Node
	_ = yaml.Unmarshal([]byte(left), &lNode)
	_ = yaml.Unmarshal([]byte(right), &rNode)

	// create low level objects
	var lDoc v3.PathItem
	var rDoc v3.PathItem
	_ = low.BuildModel(lNode.Content[0], &lDoc)
	_ = low.BuildModel(rNode.Content[0], &rDoc)
	_ = lDoc.Build(context.Background(), nil, lNode.Content[0], nil)
	_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)

	// compare.
	extChanges := ComparePathItems(&lDoc, &rDoc)
	assert.Equal(t, 1, extChanges.TotalChanges())
	assert.Len(t, extChanges.GetAllChanges(), 1)
	assert.Equal(t, 0, extChanges.TotalBreakingChanges())
}
