Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion router-tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ require (
github.com/wundergraph/cosmo/router v0.0.0-20260710155145-803a4bc06d92
github.com/wundergraph/cosmo/router-plugin v0.0.0-20250808194725-de123ba1c65e
github.com/wundergraph/cosmo/speedtrap v0.0.0-00010101000000-000000000000
github.com/wundergraph/graphql-go-tools/v2 v2.14.2
github.com/wundergraph/graphql-go-tools/v2 v2.14.3-0.20260807144454-b725dd116b4e
go.opentelemetry.io/otel v1.44.0
go.opentelemetry.io/otel/sdk v1.44.0
go.opentelemetry.io/otel/sdk/metric v1.44.0
Expand Down
4 changes: 2 additions & 2 deletions router-tests/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -386,8 +386,8 @@ github.com/wundergraph/astjson v1.1.0 h1:xORDosrZ87zQFJwNGe/HIHXqzpdHOFmqWgykCLV
github.com/wundergraph/astjson v1.1.0/go.mod h1:h12D/dxxnedtLzsKyBLK7/Oe4TAoGpRVC9nDpDrZSWw=
github.com/wundergraph/go-arena v1.3.0 h1:n0ng5a1vbd8YGq1u3rMr0vPU5f6AZ1BXIiUhL1UIok8=
github.com/wundergraph/go-arena v1.3.0/go.mod h1:ROOysEHWJjLQ8FSfNxZCziagb7Qw2nXY3/vgKRh7eWw=
github.com/wundergraph/graphql-go-tools/v2 v2.14.2 h1:zPqpmGdVLtTR3nc/H7kPWgMN+a/DR5ccFLGyS1WtxeQ=
github.com/wundergraph/graphql-go-tools/v2 v2.14.2/go.mod h1:zREIKLmpjfNcGSubndaW/913r0Y8XbbYOXQeZFkwHdo=
github.com/wundergraph/graphql-go-tools/v2 v2.14.3-0.20260807144454-b725dd116b4e h1:JNVTWXsC6YwWSIWPI17B/AOjV9qzciKq9mkQvURqHZs=
github.com/wundergraph/graphql-go-tools/v2 v2.14.3-0.20260807144454-b725dd116b4e/go.mod h1:zREIKLmpjfNcGSubndaW/913r0Y8XbbYOXQeZFkwHdo=
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg=
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
Expand Down
23 changes: 13 additions & 10 deletions router-tests/modules/query_stats_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

queryStatsModule "github.com/wundergraph/cosmo/router-tests/modules/custom-query-stats"
"github.com/wundergraph/cosmo/router-tests/testenv"
"github.com/wundergraph/cosmo/router/cmd/custom/module"
Expand Down Expand Up @@ -156,19 +157,21 @@ func TestCustomModuleQueryStats(t *testing.T) {
assert.Equal(t, 200, res.Response.StatusCode)

testenv.AwaitChannelWithT(t, 10*time.Second, resultsChan, func(t *testing.T, qps core.QueryPlanStats) {
assert.Equal(t, 10, qps.TotalSubgraphFetches)
assert.Equal(t, 6, qps.TotalSubgraphFetches)

expectedSubgraphFetches := map[string]int{
"availability": 2,
"employees": 3,
"availability": 1,
"employees": 2,
"family": 1,
"mood": 2,
"products": 2,
"mood": 1,
"products": 1,
}

assert.Equal(t, expectedSubgraphFetches, qps.SubgraphFetches)

assert.Equal(t, []core.SubgraphRootField{
// The list is deduplicated in fetch-tree traversal order, which is
// an implementation detail of the schedule tree shape: assert as a set.
assert.ElementsMatch(t, []core.SubgraphRootField{
{
SubgraphName: "employees",
TypeName: "Query",
Expand All @@ -179,19 +182,19 @@ func TestCustomModuleQueryStats(t *testing.T) {
SubgraphName: "products",
TypeName: "Query",
FieldName: "_entities",
Count: 2,
Count: 1,
},
{
SubgraphName: "mood",
TypeName: "Query",
FieldName: "_entities",
Count: 2,
Count: 1,
},
{
SubgraphName: "availability",
TypeName: "Query",
FieldName: "_entities",
Count: 2,
Count: 1,
},
{
SubgraphName: "family",
Expand All @@ -203,7 +206,7 @@ func TestCustomModuleQueryStats(t *testing.T) {
SubgraphName: "employees",
TypeName: "Query",
FieldName: "_entities",
Count: 2,
Count: 1,
},
}, qps.SubgraphRootFields)
})
Expand Down
68 changes: 68 additions & 0 deletions router-tests/operations/multi_fetch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package integration

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/wundergraph/cosmo/router-tests/testenv"
"github.com/wundergraph/cosmo/router/pkg/config"
)

func TestMultiFetch(t *testing.T) {
t.Parallel()

// multiFetchQuery resolves Consultancy.lead from the employees subgraph, then needs
// two follow-up entity fetches: Employee.derivedMood and Consultancy.isLeadAvailable
const multiFetchQuery = `query Requires {
products {
__typename
... on Consultancy {
lead {
__typename
id
derivedMood
}
isLeadAvailable
}
}
}`
const multiFetchExpectedResponse = `{"data":{"products":[{"__typename":"Consultancy","lead":{"__typename":"Employee","id":1,"derivedMood":"HAPPY"},"isLeadAvailable":false},{"__typename":"Cosmo"},{"__typename":"SDK"}]}}`

t.Run("merges same-wave entity fetches to the same subgraph by default", func(t *testing.T) {
t.Parallel()

testenv.Run(t, &testenv.Config{}, func(t *testing.T, xEnv *testenv.Environment) {
res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{Query: multiFetchQuery})
require.JSONEq(t, multiFetchExpectedResponse, res.Body)

// Root fetch + one merged entity fetch.
require.EqualValues(t, 2, xEnv.SubgraphRequestCount.Employees.Load())

require.EqualValues(t, 1, xEnv.SubgraphRequestCount.Mood.Load())
require.EqualValues(t, 1, xEnv.SubgraphRequestCount.Availability.Load())
require.EqualValues(t, 4, xEnv.SubgraphRequestCount.Global.Load())
})
})

t.Run("sends one request per entity fetch when multi-fetch is disabled", func(t *testing.T) {
t.Parallel()

testenv.Run(t, &testenv.Config{
ModifyEngineExecutionConfiguration: func(cfg *config.EngineExecutionConfiguration) {
cfg.DisableMultiFetch = true
},
}, func(t *testing.T, xEnv *testenv.Environment) {
res := xEnv.MakeGraphQLRequestOK(testenv.GraphQLRequest{Query: multiFetchQuery})
// Merging must not change the result: same response as the default case above.
require.JSONEq(t, multiFetchExpectedResponse, res.Body)

// Root fetch + two separate entity fetches.
require.EqualValues(t, 3, xEnv.SubgraphRequestCount.Employees.Load())

require.EqualValues(t, 1, xEnv.SubgraphRequestCount.Mood.Load())
require.EqualValues(t, 1, xEnv.SubgraphRequestCount.Availability.Load())
require.EqualValues(t, 5, xEnv.SubgraphRequestCount.Global.Load())
})
})
}
Loading
Loading