Skip to content

Commit 341b113

Browse files
tobias-tenglermichaelstaib
authored andcommitted
[Fusion] Add tests for @remove (#7727)
1 parent af4afaf commit 341b113

7 files changed

+310
-8
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using HotChocolate.Fusion.Shared;
2+
using Xunit.Abstractions;
3+
4+
namespace HotChocolate.Fusion.Composition;
5+
6+
public class RemoveTests(ITestOutputHelper output)
7+
{
8+
[Fact]
9+
public async Task One_Subgraph_Removes_Field_That_Is_Present_In_Another_Subgraph()
10+
{
11+
// arrange
12+
var subgraphA = await TestSubgraph.CreateAsync(
13+
"""
14+
type Query {
15+
field: String!
16+
}
17+
""",
18+
"""
19+
schema @remove(coordinate: "Query.field") {
20+
}
21+
"""
22+
);
23+
24+
var subgraphB = await TestSubgraph.CreateAsync(
25+
"""
26+
type Query {
27+
field: String!
28+
}
29+
"""
30+
);
31+
32+
using var subgraphs = new TestSubgraphCollection(output, [subgraphA, subgraphB]);
33+
34+
// act
35+
var fusionGraph = await subgraphs.GetFusionGraphAsync();
36+
37+
// assert
38+
fusionGraph.MatchSnapshot();
39+
}
40+
41+
[Fact]
42+
public async Task Subgraph_Removes_A_Field_Exclusively_Owned_By_It()
43+
{
44+
// arrange
45+
var subgraphA = await TestSubgraph.CreateAsync(
46+
"""
47+
type Query {
48+
someField: SomeObject!
49+
}
50+
51+
type SomeObject {
52+
property: String!
53+
}
54+
""",
55+
"""
56+
schema @remove(coordinate: "Query.someField") {
57+
}
58+
"""
59+
);
60+
61+
var subgraphB = await TestSubgraph.CreateAsync(
62+
"""
63+
type Query {
64+
otherField: AnotherObject!
65+
}
66+
67+
type AnotherObject {
68+
property: String!
69+
}
70+
"""
71+
);
72+
73+
using var subgraphs = new TestSubgraphCollection(output, [subgraphA, subgraphB]);
74+
75+
// act
76+
var fusionGraph = await subgraphs.GetFusionGraphAsync();
77+
78+
// assert
79+
fusionGraph.MatchSnapshot();
80+
}
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
schema
2+
@fusion(version: 1)
3+
@transport(subgraph: "Subgraph_1", location: "http:\/\/localhost:5000\/graphql", kind: "HTTP")
4+
@transport(subgraph: "Subgraph_2", location: "http:\/\/localhost:5000\/graphql", kind: "HTTP") {
5+
query: Query
6+
}
7+
8+
type Query {
9+
field: String!
10+
@resolver(subgraph: "Subgraph_2", select: "{ field }")
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
schema
2+
@fusion(version: 1)
3+
@transport(subgraph: "Subgraph_1", location: "http:\/\/localhost:5000\/graphql", kind: "HTTP")
4+
@transport(subgraph: "Subgraph_2", location: "http:\/\/localhost:5000\/graphql", kind: "HTTP") {
5+
query: Query
6+
}
7+
8+
type Query {
9+
otherField: AnotherObject!
10+
@resolver(subgraph: "Subgraph_2", select: "{ otherField }")
11+
}
12+
13+
type AnotherObject {
14+
property: String!
15+
@source(subgraph: "Subgraph_2")
16+
}
17+
18+
type SomeObject {
19+
property: String!
20+
@source(subgraph: "Subgraph_1")
21+
}

src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,100 @@ namespace HotChocolate.Fusion;
1919

2020
public class RequestPlannerTests(ITestOutputHelper output)
2121
{
22+
[Fact]
23+
public async Task Same_Field_On_Two_Subgraphs_One_Removes_It()
24+
{
25+
// arrange
26+
var subgraphA = await TestSubgraph.CreateAsync(
27+
"""
28+
interface Node {
29+
id: ID!
30+
}
31+
32+
type BlogAuthor {
33+
fullName: String!
34+
}
35+
36+
type Query {
37+
node(id: ID!): Node
38+
}
39+
40+
type User implements Node {
41+
followedBlogAuthors(first: Int!): [BlogAuthor]!
42+
someField: String!
43+
otherField: Int!
44+
anotherField: Float!
45+
id: ID!
46+
}
47+
""",
48+
"""
49+
schema @remove(coordinate: "User.followedBlogAuthors") {
50+
}
51+
"""
52+
);
53+
54+
var subgraphB = await TestSubgraph.CreateAsync(
55+
"""
56+
interface Node {
57+
id: ID!
58+
}
59+
60+
type BlogAuthor {
61+
fullName: String!
62+
}
63+
64+
type Query {
65+
node(id: ID!): Node
66+
}
67+
68+
type User implements Node {
69+
followedBlogAuthors(first: Int!): [BlogAuthor]!
70+
id: ID!
71+
}
72+
"""
73+
);
74+
75+
var subgraphC = await TestSubgraph.CreateAsync(
76+
"""
77+
type Query {
78+
userBySlug(slug: String!): User
79+
}
80+
81+
type User {
82+
id: ID!
83+
}
84+
""");
85+
86+
using var subgraphs = new TestSubgraphCollection(output, [subgraphA, subgraphB, subgraphC]);
87+
var fusionGraph = await subgraphs.GetFusionGraphAsync();
88+
89+
// act
90+
var result = await CreateQueryPlanAsync(
91+
fusionGraph,
92+
"""
93+
query {
94+
userBySlug(slug: "me") {
95+
...likedAuthors
96+
}
97+
}
98+
99+
fragment likedAuthors on User {
100+
someField
101+
otherField
102+
anotherField
103+
followedBlogAuthors(first: 3) {
104+
fullName
105+
}
106+
}
107+
""");
108+
109+
// assert
110+
var snapshot = new Snapshot();
111+
snapshot.Add(result.UserRequest, nameof(result.UserRequest));
112+
snapshot.Add(result.QueryPlan, nameof(result.QueryPlan));
113+
await snapshot.MatchMarkdownAsync();
114+
}
115+
22116
[Fact]
23117
public async Task Fragment_Deduplication_1()
24118
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Same_Field_On_Two_Subgraphs_One_Removes_It
2+
3+
## UserRequest
4+
5+
```graphql
6+
{
7+
userBySlug(slug: "me") {
8+
... likedAuthors
9+
}
10+
}
11+
12+
fragment likedAuthors on User {
13+
someField
14+
otherField
15+
anotherField
16+
followedBlogAuthors(first: 3) {
17+
fullName
18+
}
19+
}
20+
```
21+
22+
## QueryPlan
23+
24+
```json
25+
{
26+
"document": "{ userBySlug(slug: \u0022me\u0022) { ... likedAuthors } } fragment likedAuthors on User { someField otherField anotherField followedBlogAuthors(first: 3) { fullName } }",
27+
"rootNode": {
28+
"type": "Sequence",
29+
"nodes": [
30+
{
31+
"type": "Resolve",
32+
"subgraph": "Subgraph_3",
33+
"document": "query fetch_userBySlug_1 { userBySlug(slug: \u0022me\u0022) { __fusion_exports__1: id } }",
34+
"selectionSetId": 0,
35+
"provides": [
36+
{
37+
"variable": "__fusion_exports__1"
38+
}
39+
]
40+
},
41+
{
42+
"type": "Compose",
43+
"selectionSetIds": [
44+
0
45+
]
46+
},
47+
{
48+
"type": "Parallel",
49+
"nodes": [
50+
{
51+
"type": "Resolve",
52+
"subgraph": "Subgraph_1",
53+
"document": "query fetch_userBySlug_2($__fusion_exports__1: ID!) { node(id: $__fusion_exports__1) { ... on User { someField otherField anotherField } } }",
54+
"selectionSetId": 1,
55+
"path": [
56+
"node"
57+
],
58+
"requires": [
59+
{
60+
"variable": "__fusion_exports__1"
61+
}
62+
]
63+
},
64+
{
65+
"type": "Resolve",
66+
"subgraph": "Subgraph_2",
67+
"document": "query fetch_userBySlug_3($__fusion_exports__1: ID!) { node(id: $__fusion_exports__1) { ... on User { followedBlogAuthors(first: 3) { fullName } } } }",
68+
"selectionSetId": 1,
69+
"path": [
70+
"node"
71+
],
72+
"requires": [
73+
{
74+
"variable": "__fusion_exports__1"
75+
}
76+
]
77+
}
78+
]
79+
},
80+
{
81+
"type": "Compose",
82+
"selectionSetIds": [
83+
1
84+
]
85+
}
86+
]
87+
},
88+
"state": {
89+
"__fusion_exports__1": "User_id"
90+
}
91+
}
92+
```
93+

src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ public record TestSubgraph(
1717
{
1818
public static Task<TestSubgraph> CreateAsync(
1919
[StringSyntax("graphql")] string schemaText,
20+
[StringSyntax("graphql")] string extensions = "",
2021
bool isOffline = false)
2122
=> CreateAsync(
2223
configure: builder => builder
2324
.AddDocumentFromString(schemaText)
2425
.AddResolverMocking()
2526
.AddTestDirectives(),
27+
extensions: extensions,
2628
isOffline: isOffline);
2729

2830
public static async Task<TestSubgraph> CreateAsync(

src/HotChocolate/Fusion/test/Shared/TestSubgraphCollection.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,6 @@ namespace HotChocolate.Fusion.Shared;
1111

1212
public class TestSubgraphCollection(ITestOutputHelper outputHelper, TestSubgraph[] subgraphs) : IDisposable
1313
{
14-
public IHttpClientFactory GetHttpClientFactory()
15-
{
16-
var subgraphsDictionary = GetSubgraphs()
17-
.ToDictionary(s => s.SubgraphName, s => s.Subgraph);
18-
19-
return new TestSubgraphCollectionHttpClientFactory(subgraphsDictionary);
20-
}
21-
2214
public async Task<IRequestExecutor> GetExecutorAsync(
2315
FusionFeatureCollection? features = null,
2416
Action<FusionGatewayBuilder>? configure = null)
@@ -74,6 +66,14 @@ private async Task<IRequestExecutor> GetExecutorAsync(
7466
return await builder.BuildRequestExecutorAsync();
7567
}
7668

69+
private IHttpClientFactory GetHttpClientFactory()
70+
{
71+
var subgraphsDictionary = GetSubgraphs()
72+
.ToDictionary(s => s.SubgraphName, s => s.Subgraph);
73+
74+
return new TestSubgraphCollectionHttpClientFactory(subgraphsDictionary);
75+
}
76+
7777
private IEnumerable<(string SubgraphName, TestSubgraph Subgraph)> GetSubgraphs()
7878
=> subgraphs.Select((s, i) => ($"Subgraph_{++i}", s));
7979

0 commit comments

Comments
 (0)