agent_patch
runs/2026-02-28__13-38-10__gpt-5-4/flux-pr-1087/flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/agent-logs/agent.patch
12565 bytes
diff --git a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasourcetesting/datasourcetesting.go b/app/v2/pkg/engine/datasourcetesting/datasourcetesting.go
index 9a02e3e..a2505bf 100644
--- a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasourcetesting/datasourcetesting.go
+++ b/app/v2/pkg/engine/datasourcetesting/datasourcetesting.go
@@ -3,17 +3,14 @@ package datasourcetesting
import (
"encoding/json"
"fmt"
- "math/rand"
"reflect"
"slices"
"testing"
- "time"
"github.com/kylelemons/godebug/diff"
"github.com/kylelemons/godebug/pretty"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "gonum.org/v1/gonum/stat/combin"
"github.com/wundergraph/graphql-go-tools/v2/pkg/internal/unsafeprinter"
@@ -25,6 +22,7 @@ import (
"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/postprocess"
"github.com/wundergraph/graphql-go-tools/v2/pkg/internal/unsafeparser"
"github.com/wundergraph/graphql-go-tools/v2/pkg/operationreport"
+ "github.com/wundergraph/graphql-go-tools/v2/pkg/testing/permutations"
)
type testOptions struct {
@@ -76,7 +74,7 @@ func RunWithPermutations(t *testing.T, definition, operation, operationName stri
permutation := dataSourcePermutations[i]
t.Run(fmt.Sprintf("permutation %v", permutation.Order), func(t *testing.T) {
permutationPlanConfiguration := config
- permutationPlanConfiguration.DataSources = permutation.DataSources
+ permutationPlanConfiguration.DataSources = permutation.Items
t.Run("run", RunTest(
definition,
@@ -101,7 +99,7 @@ func RunWithPermutationsVariants(t *testing.T, definition, operation, operationN
permutation := dataSourcePermutations[i]
t.Run(fmt.Sprintf("permutation %v", permutation.Order), func(t *testing.T) {
permutationPlanConfiguration := config
- permutationPlanConfiguration.DataSources = permutation.DataSources
+ permutationPlanConfiguration.DataSources = permutation.Items
t.Run("run", RunTest(
definition,
@@ -225,42 +223,13 @@ func RunTestWithVariables(definition, operation, operationName, variables string
// ShuffleDS randomizes the order of the data sources
// to ensure that the order doesn't matter
func ShuffleDS(dataSources []plan.DataSource) []plan.DataSource {
- rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
- rnd.Shuffle(len(dataSources), func(i, j int) {
- dataSources[i], dataSources[j] = dataSources[j], dataSources[i]
- })
-
- return dataSources
+ return permutations.Shuffle(dataSources)
}
func OrderDS(dataSources []plan.DataSource, order []int) (out []plan.DataSource) {
- out = make([]plan.DataSource, 0, len(dataSources))
-
- for _, i := range order {
- out = append(out, dataSources[i])
- }
-
- return out
-}
-
-func DataSourcePermutations(dataSources []plan.DataSource) []*Permutation {
- size := len(dataSources)
- elementsCount := len(dataSources)
- list := combin.Permutations(size, elementsCount)
-
- permutations := make([]*Permutation, 0, len(list))
-
- for _, v := range list {
- permutations = append(permutations, &Permutation{
- Order: v,
- DataSources: OrderDS(dataSources, v),
- })
- }
-
- return permutations
+ return permutations.Order(dataSources, order)
}
-type Permutation struct {
- Order []int
- DataSources []plan.DataSource
+func DataSourcePermutations(dataSources []plan.DataSource) []permutations.Permutation[plan.DataSource] {
+ return permutations.Generate(dataSources)
}
diff --git a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/datasource_filter_collect_nodes_visitor.go b/app/v2/pkg/engine/plan/datasource_filter_collect_nodes_visitor.go
index 1262c2e..d292ba2 100644
--- a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/datasource_filter_collect_nodes_visitor.go
+++ b/app/v2/pkg/engine/plan/datasource_filter_collect_nodes_visitor.go
@@ -198,7 +198,12 @@ func (f *collectNodesVisitor) isInterfaceObject(typeName string) bool {
// has disabled entity resolver
func (f *collectNodesVisitor) allKeysHasDisabledEntityResolver(typeName string) bool {
keys := f.dataSource.FederationConfiguration().Keys
- return !slices.ContainsFunc(keys.FilterByTypeAndResolvability(typeName, false), func(k FederationFieldConfiguration) bool {
+ typeKeys := keys.FilterByTypeAndResolvability(typeName, false)
+ if len(typeKeys) == 0 {
+ return false
+ }
+
+ return !slices.ContainsFunc(typeKeys, func(k FederationFieldConfiguration) bool {
return !k.DisableEntityResolver
})
}
diff --git a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/datasource_filter_visitor.go b/app/v2/pkg/engine/plan/datasource_filter_visitor.go
index f96b8e8..955d8b3 100644
--- a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/datasource_filter_visitor.go
+++ b/app/v2/pkg/engine/plan/datasource_filter_visitor.go
@@ -206,6 +206,11 @@ func (f *DataSourceFilter) selectUniqNodeParentsUpToRootNode(i int) {
if !ok {
break
}
+
+ if f.nodes.items[parentIdx].IsExternal && !f.nodes.items[parentIdx].IsProvided {
+ break
+ }
+
f.nodes.items[parentIdx].selectWithReason(ReasonStage1SameSourceParent, f.enableSelectionReasons)
current = parentIdx
@@ -421,6 +426,10 @@ func (f *DataSourceFilter) findPossibleParents(i int) (parentIds []int) {
parentIdx, _ := f.nodes.parentNodeOnSameSource(i)
for parentIdx != -1 {
+ if f.nodes.items[parentIdx].IsExternal && !f.nodes.items[parentIdx].IsProvided {
+ break
+ }
+
nodesIdsToSelect = append(nodesIdsToSelect, parentIdx)
// for the parent node we are checking if it is a root node and has enabled entity resolver.
diff --git a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/datasource_filter_visitor_test.go b/app/v2/pkg/engine/plan/datasource_filter_visitor_test.go
index 89b48b8..7bbcdb2 100644
--- a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/datasource_filter_visitor_test.go
+++ b/app/v2/pkg/engine/plan/datasource_filter_visitor_test.go
@@ -2,10 +2,8 @@ package plan
import (
"fmt"
- "math/rand"
"slices"
"testing"
- "time"
"github.com/kylelemons/godebug/pretty"
"github.com/stretchr/testify/assert"
@@ -13,6 +11,7 @@ import (
"github.com/wundergraph/graphql-go-tools/v2/pkg/astvalidation"
"github.com/wundergraph/graphql-go-tools/v2/pkg/internal/unsafeparser"
"github.com/wundergraph/graphql-go-tools/v2/pkg/operationreport"
+ "github.com/wundergraph/graphql-go-tools/v2/pkg/testing/permutations"
)
type dsBuilder struct {
@@ -960,25 +959,147 @@ func TestFindBestDataSourceSet(t *testing.T) {
}
}
-// shuffleDS randomizes the order of the data sources
-// to ensure that the order doesn't matter
-func shuffleDS(dataSources []DataSource) []DataSource {
- rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
- rnd.Shuffle(len(dataSources), func(i, j int) {
- dataSources[i], dataSources[j] = dataSources[j], dataSources[i]
+func TestCollectNodesVisitor_AllKeysHasDisabledEntityResolver(t *testing.T) {
+ t.Run("no keys", func(t *testing.T) {
+ visitor := &collectNodesVisitor{
+ dataSource: dsb().Hash(11).Schema(`
+ type User {
+ name: String!
+ }
+ `).RootNode("User", "name").DS(),
+ }
+
+ assert.False(t, visitor.allKeysHasDisabledEntityResolver("User"))
})
- return dataSources
+ t.Run("all keys disabled", func(t *testing.T) {
+ visitor := &collectNodesVisitor{
+ dataSource: dsb().Hash(11).Schema(`
+ type User {
+ id: ID!
+ }
+ `).RootNode("User", "id").WithMetadata(func(data *FederationMetaData) {
+ data.Keys = FederationFieldConfigurations{
+ {
+ TypeName: "User",
+ SelectionSet: "id",
+ DisableEntityResolver: true,
+ },
+ }
+ }).DS(),
+ }
+
+ assert.True(t, visitor.allKeysHasDisabledEntityResolver("User"))
+ })
}
func orderDS(dataSources []DataSource, order []int) (out []DataSource) {
- out = make([]DataSource, 0, len(dataSources))
+ return permutations.Order(dataSources, order)
+}
+
+// shuffleDS randomizes the order of the data sources
+// to ensure that the order doesn't matter
+func shuffleDS(dataSources []DataSource) []DataSource {
+ return permutations.Shuffle(dataSources)
+}
+
+func TestFindBestDataSourceSet_DoesNotSelectExternalParentsOfUniqueNode(t *testing.T) {
+ definition := unsafeparser.ParseGraphqlDocumentStringWithBaseSchema(`
+ type Query {
+ me: User!
+ }
+
+ type User {
+ id: ID!
+ details: Details!
+ }
- for _, i := range order {
- out = append(out, dataSources[i])
+ type Details {
+ age: Int!
+ }
+ `)
+ operation := unsafeparser.ParseGraphqlDocumentString(`
+ query {
+ me {
+ details {
+ age
+ }
+ }
+ }
+ `)
+ report := operationreport.Report{}
+
+ astvalidation.DefaultOperationValidator().Validate(&operation, &definition, &report)
+ if report.HasErrors() {
+ t.Fatal(report.Error())
+ }
+
+ ds1 := dsb().Hash(11).Schema(`
+ type Query {
+ me: User!
+ }
+
+ type User @key(fields: "id") {
+ id: ID!
+ details: Details!
+ }
+
+ type Details {
+ noop: Boolean
+ }
+ `).RootNode("Query", "me").
+ RootNode("User", "id", "details").
+ DS()
+
+ ds2Builder := dsb().Hash(22).Schema(`
+ type User @key(fields: "id") {
+ id: ID!
+ details: Details! @external
+ }
+
+ type Details {
+ age: Int!
+ }
+ `).RootNode("User", "id").
+ ChildNode("Details", "age")
+ ds2Builder.ds.RootNodes[0].ExternalFieldNames = []string{"details"}
+ ds2 := ds2Builder.DS()
+
+ filter := NewDataSourceFilter(&operation, &definition, &report)
+ filter.EnableSelectionReasons()
+ filter.dataSources = []DataSource{ds1, ds2}
+
+ planned, _ := filter.findBestDataSourceSet(nil, nil)
+ if report.HasErrors() {
+ t.Fatal(report.Error())
+ }
+
+ var (
+ ds1DetailsSelected bool
+ ds2DetailsSelected bool
+ ds2AgeSelected bool
+ )
+
+ for _, item := range planned.items {
+ if !item.Selected {
+ continue
+ }
+
+ if item.TypeName == "User" && item.FieldName == "details" && item.DataSourceHash == 11 {
+ ds1DetailsSelected = true
+ }
+ if item.TypeName == "User" && item.FieldName == "details" && item.DataSourceHash == 22 {
+ ds2DetailsSelected = true
+ }
+ if item.TypeName == "Details" && item.FieldName == "age" && item.DataSourceHash == 22 {
+ ds2AgeSelected = true
+ assert.Equal(t, []string{ReasonStage1Unique}, item.SelectionReasons)
+ }
}
- return out
+ assert.True(t, ds1DetailsSelected)
+ assert.False(t, ds2DetailsSelected)
+ assert.True(t, ds2AgeSelected)
}
const shareableDefinition = `
diff --git a/app/v2/pkg/testing/permutations/permutations.go b/app/v2/pkg/testing/permutations/permutations.go
new file mode 100644
index 0000000..a18bd75
--- /dev/null
+++ b/app/v2/pkg/testing/permutations/permutations.go
@@ -0,0 +1,47 @@
+package permutations
+
+import (
+ "math/rand"
+ "time"
+
+ "gonum.org/v1/gonum/stat/combin"
+)
+
+type Permutation[T any] struct {
+ Order []int
+ Items []T
+}
+
+func Shuffle[T any](items []T) []T {
+ shuffled := append([]T(nil), items...)
+ rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
+ rnd.Shuffle(len(shuffled), func(i, j int) {
+ shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
+ })
+
+ return shuffled
+}
+
+func Order[T any](items []T, order []int) []T {
+ ordered := make([]T, 0, len(items))
+
+ for _, i := range order {
+ ordered = append(ordered, items[i])
+ }
+
+ return ordered
+}
+
+func Generate[T any](items []T) []Permutation[T] {
+ list := combin.Permutations(len(items), len(items))
+ permutations := make([]Permutation[T], 0, len(list))
+
+ for _, order := range list {
+ permutations = append(permutations, Permutation[T]{
+ Order: order,
+ Items: Order(items, order),
+ })
+ }
+
+ return permutations
+}
diff --git a/app/v2/pkg/testing/permutations/permutations_test.go b/app/v2/pkg/testing/permutations/permutations_test.go
new file mode 100644
index 0000000..537d683
--- /dev/null
+++ b/app/v2/pkg/testing/permutations/permutations_test.go
@@ -0,0 +1,17 @@
+package permutations
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGenerate(t *testing.T) {
+ perms := Generate([]int{1, 2, 3})
+
+ assert.Len(t, perms, 6)
+ assert.Equal(t, []int{0, 1, 2}, perms[0].Order)
+ assert.Equal(t, []int{1, 2, 3}, perms[0].Items)
+ assert.Equal(t, []int{2, 1, 0}, perms[len(perms)-1].Order)
+ assert.Equal(t, []int{3, 2, 1}, perms[len(perms)-1].Items)
+}