agent_patch
runs/2026-02-28__13-38-10__gpt-5-3-codex/flux-pr-1087/flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/agent-logs/agent.patch
9185 bytes
diff --git a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/datasourcetesting/datasourcetesting.go b/app/v2/pkg/engine/datasourcetesting/datasourcetesting.go
index 9a02e3e..a783615 100644
--- a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/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 {
@@ -225,39 +223,23 @@ 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
+ return permutations.Order(dataSources, order)
}
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),
+ perms := permutations.Generate(dataSources)
+ result := make([]*Permutation, 0, len(perms))
+ for _, p := range perms {
+ result = append(result, &Permutation{
+ Order: p.Order,
+ DataSources: p.Values,
})
}
-
- return permutations
+ return result
}
type Permutation struct {
diff --git a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/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..7612c1b 100644
--- a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/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 {
+ keyConfigurations := keys.FilterByTypeAndResolvability(typeName, false)
+ if len(keyConfigurations) == 0 {
+ return false
+ }
+
+ return !slices.ContainsFunc(keyConfigurations, func(k FederationFieldConfiguration) bool {
return !k.DisableEntityResolver
})
}
diff --git a/app/v2/pkg/engine/plan/datasource_filter_regression_test.go b/app/v2/pkg/engine/plan/datasource_filter_regression_test.go
new file mode 100644
index 0000000..687e06e
--- /dev/null
+++ b/app/v2/pkg/engine/plan/datasource_filter_regression_test.go
@@ -0,0 +1,87 @@
+package plan
+
+import (
+ "testing"
+
+ "github.com/kingledion/go-tools/tree"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestAllKeysHasDisabledEntityResolver(t *testing.T) {
+ t.Run("no keys should not be treated as disabled entity resolver", func(t *testing.T) {
+ visitor := &collectNodesVisitor{
+ dataSource: dsb().
+ Hash(1).
+ RootNode("User", "id").
+ DS(),
+ }
+
+ assert.False(t, visitor.allKeysHasDisabledEntityResolver("User"))
+ })
+
+ t.Run("all keys disabled should be treated as disabled entity resolver", func(t *testing.T) {
+ visitor := &collectNodesVisitor{
+ dataSource: dsb().
+ Hash(1).
+ RootNode("User", "id").
+ KeysMetadata(FederationFieldConfigurations{
+ {TypeName: "User", SelectionSet: "id", DisableEntityResolver: true},
+ }).
+ DS(),
+ }
+
+ assert.True(t, visitor.allKeysHasDisabledEntityResolver("User"))
+ })
+
+ t.Run("at least one enabled key should not be treated as disabled entity resolver", func(t *testing.T) {
+ visitor := &collectNodesVisitor{
+ dataSource: dsb().
+ Hash(1).
+ RootNode("User", "id").
+ KeysMetadata(FederationFieldConfigurations{
+ {TypeName: "User", SelectionSet: "id", DisableEntityResolver: true},
+ {TypeName: "User", SelectionSet: "email", DisableEntityResolver: false},
+ }).
+ DS(),
+ }
+
+ assert.False(t, visitor.allKeysHasDisabledEntityResolver("User"))
+ })
+}
+
+func TestSelectUniqNodeParentsUpToRootNodeStopsAtExternalParent(t *testing.T) {
+ nodes := NewNodeSuggestionsWithSize(3)
+ nodes.items = []*NodeSuggestion{
+ {
+ FieldRef: 1,
+ DataSourceHash: 11,
+ IsRootNode: true,
+ },
+ {
+ FieldRef: 2,
+ DataSourceHash: 11,
+ IsExternal: true,
+ IsProvided: false,
+ },
+ {
+ FieldRef: 3,
+ DataSourceHash: 11,
+ },
+ }
+
+ responseTree := tree.Empty[[]int]()
+ responseTree.Add(treeRootID, 0, nil)
+ responseTree.Add(TreeNodeID(1), treeRootID, []int{0})
+ responseTree.Add(TreeNodeID(2), TreeNodeID(1), []int{1})
+ responseTree.Add(TreeNodeID(3), TreeNodeID(2), []int{2})
+ nodes.responseTree = *responseTree
+
+ filter := &DataSourceFilter{
+ nodes: nodes,
+ }
+
+ filter.selectUniqNodeParentsUpToRootNode(2)
+
+ assert.False(t, nodes.items[1].Selected)
+ assert.False(t, nodes.items[0].Selected)
+}
diff --git a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/plan/datasource_filter_visitor.go b/app/v2/pkg/engine/plan/datasource_filter_visitor.go
index f96b8e8..df2a1f8 100644
--- a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/plan/datasource_filter_visitor.go
+++ b/app/v2/pkg/engine/plan/datasource_filter_visitor.go
@@ -206,6 +206,13 @@ func (f *DataSourceFilter) selectUniqNodeParentsUpToRootNode(i int) {
if !ok {
break
}
+
+ // External nodes are owned by a different datasource at this path, so parent selection
+ // must not continue through them.
+ if f.nodes.items[parentIdx].IsExternal && !f.nodes.items[parentIdx].IsProvided {
+ break
+ }
+
f.nodes.items[parentIdx].selectWithReason(ReasonStage1SameSourceParent, f.enableSelectionReasons)
current = parentIdx
diff --git a/app/v2/pkg/testing/permutations/permutations.go b/app/v2/pkg/testing/permutations/permutations.go
new file mode 100644
index 0000000..10e7649
--- /dev/null
+++ b/app/v2/pkg/testing/permutations/permutations.go
@@ -0,0 +1,49 @@
+package permutations
+
+import (
+ "math/rand"
+ "time"
+
+ "gonum.org/v1/gonum/stat/combin"
+)
+
+type Permutation[T any] struct {
+ Order []int
+ Values []T
+}
+
+// Shuffle randomizes the order of elements in place and returns the same slice.
+func Shuffle[T any](items []T) []T {
+ rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
+ rnd.Shuffle(len(items), func(i, j int) {
+ items[i], items[j] = items[j], items[i]
+ })
+
+ return items
+}
+
+func Order[T any](items []T, order []int) (out []T) {
+ out = make([]T, 0, len(items))
+
+ for _, i := range order {
+ out = append(out, items[i])
+ }
+
+ return out
+}
+
+func Generate[T any](items []T) []*Permutation[T] {
+ size := len(items)
+ elementsCount := len(items)
+ list := combin.Permutations(size, elementsCount)
+
+ permutations := make([]*Permutation[T], 0, len(list))
+ for _, v := range list {
+ permutations = append(permutations, &Permutation[T]{
+ Order: v,
+ Values: Order(items, v),
+ })
+ }
+
+ 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..419937d
--- /dev/null
+++ b/app/v2/pkg/testing/permutations/permutations_test.go
@@ -0,0 +1,20 @@
+package permutations
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGenerate(t *testing.T) {
+ perms := Generate([]string{"a", "b", "c"})
+
+ assert.Len(t, perms, 6)
+
+ assert.Equal(t, []int{0, 1, 2}, perms[0].Order)
+ assert.Equal(t, []string{"a", "b", "c"}, perms[0].Values)
+}
+
+func TestOrder(t *testing.T) {
+ assert.Equal(t, []int{3, 1, 2}, Order([]int{1, 2, 3}, []int{2, 0, 1}))
+}