ab1aaed845
## Summary: We typically name our types `OperationFieldTypeFieldType`, but if a type's name matches the preceding field-name, we omit the type-name. In #71 I changed the behavior such that we no longer do that in the case where the type's name matches some suffix of the name-so-far that's longer than just the leaf field-name. This was semi-intentional; I assumed it didn't matter and would be more predictable this way. But it turns out that was a feature, both in the sense that almost any change to the type-name-generator is breaking, and in the sense that it made the names uglier. Plus, now that we have better conflict-detection (#94), the possibility that some tricksy type-names could cause problems is no longer as much of an issue, so we can be a little less careful here. (Although I think this is no less safe than before; the field-names are the important part.) So in this commit I revert the change. Specifically, this comes up a lot at Khan where we do ``` mutation ForcePhantom { forcePhantom { # type: ForcePhantom error { ... } # type: ForcePhantomError } } ``` Before #71, and again after this change, we'll generate `ForcePhantomForcePhantomError` for `error`; before we'd generate `ForcePhantomForcePhantomErrorForcePhantomError`. Issue: https://github.com/Khan/genqlient/issues/109 ## Test plan: make tesc Author: benjaminjkraft Reviewers: csilvers, aberkan, dnerdy, jvoll, mahtabsabet, MiguelCastillo, StevenACoffman Required Reviewers: Approved By: csilvers Checks: ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Lint, ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Lint Pull Request URL: https://github.com/Khan/genqlient/pull/110
107 lines
2.9 KiB
Go
107 lines
2.9 KiB
Go
package generate
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/vektah/gqlparser/v2/ast"
|
|
)
|
|
|
|
func fakeField(containingTypeName, fieldName string) *ast.Field {
|
|
// (just the fields we need, probably not usable outside this file)
|
|
return &ast.Field{
|
|
Alias: fieldName,
|
|
ObjectDefinition: &ast.Definition{Name: containingTypeName},
|
|
}
|
|
}
|
|
|
|
func TestTypeNames(t *testing.T) {
|
|
tests := []struct {
|
|
expectedTypeName string
|
|
fields []*ast.Field
|
|
leafTypeName string
|
|
}{{
|
|
"OperationFieldType",
|
|
[]*ast.Field{fakeField("Query", "field")},
|
|
"Type",
|
|
}, {
|
|
"OperationUser",
|
|
[]*ast.Field{fakeField("Query", "user")},
|
|
"User",
|
|
}, {
|
|
// We don't shorten field-names.
|
|
"OperationOperationUser",
|
|
[]*ast.Field{fakeField("Query", "operationUser")},
|
|
"User",
|
|
}, {
|
|
// We do shorten across multiple prefixes.
|
|
"OperationUser",
|
|
[]*ast.Field{fakeField("Query", "user")},
|
|
"OperationUser",
|
|
}, {
|
|
"OperationFavoriteUser",
|
|
[]*ast.Field{fakeField("Query", "favoriteUser")},
|
|
"User",
|
|
}, {
|
|
"OperationField1Type1Field2Type2",
|
|
[]*ast.Field{fakeField("Query", "field1"), fakeField("Type1", "field2")},
|
|
"Type2",
|
|
}, {
|
|
"OperationUpperFieldLowerType",
|
|
// This is legal GraphQL!
|
|
[]*ast.Field{fakeField("Query", "UpperField")},
|
|
"lowerType",
|
|
}, {
|
|
"OperationUpperLowerUpperLower",
|
|
[]*ast.Field{fakeField("Query", "Upper"), fakeField("lower", "Upper")},
|
|
"lower",
|
|
}}
|
|
for _, test := range tests {
|
|
test := test
|
|
t.Run(test.expectedTypeName, func(t *testing.T) {
|
|
prefix := newPrefixList("Operation")
|
|
for _, field := range test.fields {
|
|
prefix = nextPrefix(prefix, field)
|
|
}
|
|
actualTypeName := makeTypeName(prefix, test.leafTypeName)
|
|
if actualTypeName != test.expectedTypeName {
|
|
t.Errorf("name mismatch:\ngot: %s\nwant: %s",
|
|
actualTypeName, test.expectedTypeName)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTypeNameCollisions(t *testing.T) {
|
|
tests := []struct {
|
|
fields []*ast.Field
|
|
leafTypeName string
|
|
}{
|
|
{[]*ast.Field{fakeField("Query", "user")}, "UserInterface"},
|
|
{[]*ast.Field{fakeField("Query", "user")}, "User"},
|
|
{[]*ast.Field{fakeField("Query", "user")}, "QueryUser"},
|
|
{[]*ast.Field{fakeField("Query", "queryUser")}, "User"},
|
|
// Known issues, described in names.go file-documentation:
|
|
// Interface/implementation collision:
|
|
// {[]*ast.Field{fakeField("Query", "queryUser")}, "QueryUser"},
|
|
// Case collision:
|
|
// {[]*ast.Field{fakeField("Query", "QueryUser")}, "User"},
|
|
// Overlapping-parts collision:
|
|
// {[]*ast.Field{fakeField("Query", "userQuery")}, "User"},
|
|
}
|
|
seen := map[string]int{} // name -> index of test that had it
|
|
for i, test := range tests {
|
|
prefix := newPrefixList("Operation")
|
|
for _, field := range test.fields {
|
|
prefix = nextPrefix(prefix, field)
|
|
}
|
|
actualTypeName := makeTypeName(prefix, test.leafTypeName)
|
|
|
|
otherIndex, ok := seen[actualTypeName]
|
|
if ok {
|
|
t.Errorf("name collision:\ncase %2d: %#v\ncase %2d: %#v",
|
|
i, test, otherIndex, tests[otherIndex])
|
|
}
|
|
seen[actualTypeName] = i
|
|
}
|
|
}
|