diff --git a/pkg/federation/sdlmerge/remove_duplicate_fielded_value_types_test.go b/pkg/federation/sdlmerge/remove_duplicate_fielded_value_types_test.go index 7b2988d09..c2ce86dce 100644 --- a/pkg/federation/sdlmerge/remove_duplicate_fielded_value_types_test.go +++ b/pkg/federation/sdlmerge/remove_duplicate_fielded_value_types_test.go @@ -42,10 +42,35 @@ func TestRemoveDuplicateFieldedValueTypes(t *testing.T) { ) }) + t.Run("Identical same name inputs are merged into a single input regardless of field order", func(t *testing.T) { + run(t, newRemoveDuplicateFieldedValueTypesVisitor(), ` + input Trainer { + age: Int! + name: String! + } + + input Trainer { + name: String! + age: Int! + } + + input Trainer { + name: String! + age: Int! + } + `, ` + input Trainer { + age: Int! + name: String! + } + `, + ) + }) + t.Run("Groups of identical same name inputs are respectively merged into single inputs", func(t *testing.T) { run(t, newRemoveDuplicateFieldedValueTypesVisitor(), ` input Pokemon { - type: Type! + type: [Type!]! isEvolved: Boolean! } @@ -65,12 +90,12 @@ func TestRemoveDuplicateFieldedValueTypes(t *testing.T) { } input Pokemon { - type: Type! + type: [Type!]! isEvolved: Boolean! } `, ` input Pokemon { - type: Type! + type: [Type!]! isEvolved: Boolean! } @@ -121,6 +146,41 @@ func TestRemoveDuplicateFieldedValueTypes(t *testing.T) { `, FederatingValueTypeErrorMessage("Trainer")) }) + t.Run("Same name inputs with a slight difference in nested field values return an error", func(t *testing.T) { + runAndExpectError(t, newRemoveDuplicateFieldedValueTypesVisitor(), ` + input Pokemon { + type: [[[[Type!]]!]!]! + } + + input Pokemon { + type: [[[[Type!]]]!]! + } + + input Pokemon { + type: [[[[Type!]]!]!]! + } + `, FederatingValueTypeErrorMessage("Pokemon")) + }) + + t.Run("Same name inputs with different non-nullable field values return an error", func(t *testing.T) { + runAndExpectError(t, newRemoveDuplicateFieldedValueTypesVisitor(), ` + input Trainer { + name: String! + age: Int! + } + + input Trainer { + name: String! + age: Int! + } + + input Trainer { + name: String! + age: String! + } + `, FederatingValueTypeErrorMessage("Trainer")) + }) + t.Run("Same name empty interfaces are merged into a single interface", func(t *testing.T) { run(t, newRemoveDuplicateFieldedValueTypesVisitor(), ` interface Trainer { @@ -160,10 +220,35 @@ func TestRemoveDuplicateFieldedValueTypes(t *testing.T) { ) }) + t.Run("Identical same name interfaces are merged into a single input regardless of field order", func(t *testing.T) { + run(t, newRemoveDuplicateFieldedValueTypesVisitor(), ` + interface Trainer { + age: Int! + name: String! + } + + interface Trainer { + name: String! + age: Int! + } + + interface Trainer { + name: String! + age: Int! + } + `, ` + interface Trainer { + age: Int! + name: String! + } + `, + ) + }) + t.Run("Groups of identical same name interfaces are respectively merged into single interfaces", func(t *testing.T) { run(t, newRemoveDuplicateFieldedValueTypesVisitor(), ` interface Pokemon { - type: Type! + type: [Type!]! isEvolved: Boolean! } @@ -183,12 +268,12 @@ func TestRemoveDuplicateFieldedValueTypes(t *testing.T) { } interface Pokemon { - type: Type! + type: [Type!]! isEvolved: Boolean! } `, ` interface Pokemon { - type: Type! + type: [Type!]! isEvolved: Boolean! } @@ -239,6 +324,41 @@ func TestRemoveDuplicateFieldedValueTypes(t *testing.T) { `, FederatingValueTypeErrorMessage("Trainer")) }) + t.Run("Same name interfaces with a slight difference in nested field values return an error", func(t *testing.T) { + runAndExpectError(t, newRemoveDuplicateFieldedValueTypesVisitor(), ` + interface Pokemon { + type: [[[[Type!]]!]!]! + } + + interface Pokemon { + type: [[[[Type!]]]!]! + } + + interface Pokemon { + type: [[[[Type!]]!]!]! + } + `, FederatingValueTypeErrorMessage("Pokemon")) + }) + + t.Run("Same name interfaces with different non-nullable field values return an error", func(t *testing.T) { + runAndExpectError(t, newRemoveDuplicateFieldedValueTypesVisitor(), ` + interface Trainer { + name: String! + age: Int! + } + + interface Trainer { + name: String! + age: Int! + } + + interface Trainer { + name: String! + age: String! + } + `, FederatingValueTypeErrorMessage("Trainer")) + }) + t.Run("Same name empty objects are merged into a single object", func(t *testing.T) { run(t, newRemoveDuplicateFieldedValueTypesVisitor(), ` type Trainer { @@ -278,10 +398,35 @@ func TestRemoveDuplicateFieldedValueTypes(t *testing.T) { ) }) + t.Run("Identical same name objects are merged into a single input regardless of field order", func(t *testing.T) { + run(t, newRemoveDuplicateFieldedValueTypesVisitor(), ` + type Trainer { + age: Int! + name: String! + } + + type Trainer { + name: String! + age: Int! + } + + type Trainer { + name: String! + age: Int! + } + `, ` + type Trainer { + age: Int! + name: String! + } + `, + ) + }) + t.Run("Groups of identical same name objects are respectively merged into single objects", func(t *testing.T) { run(t, newRemoveDuplicateFieldedValueTypesVisitor(), ` type Pokemon { - type: Type! + type: [Type!]! isEvolved: Boolean! } @@ -301,12 +446,12 @@ func TestRemoveDuplicateFieldedValueTypes(t *testing.T) { } type Pokemon { - type: Type! + type: [Type!]! isEvolved: Boolean! } `, ` type Pokemon { - type: Type! + type: [Type!]! isEvolved: Boolean! } @@ -356,4 +501,39 @@ func TestRemoveDuplicateFieldedValueTypes(t *testing.T) { } `, FederatingValueTypeErrorMessage("Trainer")) }) + + t.Run("Same name objects with a slight difference in nested field values return an error", func(t *testing.T) { + runAndExpectError(t, newRemoveDuplicateFieldedValueTypesVisitor(), ` + type Pokemon { + type: [[[[Type!]]!]!]! + } + + type Pokemon { + type: [[[[Type!]]]!]! + } + + type Pokemon { + type: [[[[Type!]]!]!]! + } + `, FederatingValueTypeErrorMessage("Pokemon")) + }) + + t.Run("Same name objects with different non-nullable field values return an error", func(t *testing.T) { + runAndExpectError(t, newRemoveDuplicateFieldedValueTypesVisitor(), ` + type Trainer { + name: String! + age: Int! + } + + type Trainer { + name: String! + age: Int! + } + + type Trainer { + name: String! + age: String! + } + `, FederatingValueTypeErrorMessage("Trainer")) + }) } diff --git a/pkg/federation/sdlmerge/sdlmerge.go b/pkg/federation/sdlmerge/sdlmerge.go index 54d2a915f..1df2fcbde 100644 --- a/pkg/federation/sdlmerge/sdlmerge.go +++ b/pkg/federation/sdlmerge/sdlmerge.go @@ -107,7 +107,7 @@ type FieldedValueType struct { document *ast.Document fieldKind ast.NodeKind fieldRefs []int - fieldSet map[string]string + fieldSet map[string]int } func NewFieldedValueType(document *ast.Document, fieldKind ast.NodeKind, fieldRefs []int) FieldedValueType { @@ -127,12 +127,12 @@ func (f FieldedValueType) AreFieldsIdentical(fieldRefsToCompare []int) bool { } for _, fieldRef := range fieldRefsToCompare { actualFieldName := f.fieldName(fieldRef) - expectedTypeName, exists := f.fieldSet[actualFieldName] + expectedTypeRef, exists := f.fieldSet[actualFieldName] if !exists { return false } - actualTypeName := f.fieldTypeName(fieldRef) - if expectedTypeName != actualTypeName { + actualTypeRef := f.fieldTypeRef(fieldRef) + if !f.document.TypesAreCompatibleDeep(expectedTypeRef, actualTypeRef) { return false } } @@ -140,9 +140,9 @@ func (f FieldedValueType) AreFieldsIdentical(fieldRefsToCompare []int) bool { } func (f *FieldedValueType) createFieldSet() { - fieldSet := make(map[string]string) + fieldSet := make(map[string]int) for _, fieldRef := range f.fieldRefs { - fieldSet[f.fieldName(fieldRef)] = f.fieldTypeName(fieldRef) + fieldSet[f.fieldName(fieldRef)] = f.fieldTypeRef(fieldRef) } f.fieldSet = fieldSet } @@ -156,13 +156,13 @@ func (f FieldedValueType) fieldName(ref int) string { } } -func (f FieldedValueType) fieldTypeName(ref int) string { +func (f FieldedValueType) fieldTypeRef(ref int) int { document := f.document switch f.fieldKind { case ast.NodeKindInputValueDefinition: - return document.TypeNameString(document.InputValueDefinitions[ref].Type) + return document.InputValueDefinitions[ref].Type default: - return document.TypeNameString(document.FieldDefinitions[ref].Type) + return document.FieldDefinitions[ref].Type } }