From 71835abedcd5e92bdfa19108d8fca26f4b715df1 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sun, 1 Mar 2026 20:58:09 +1100 Subject: [PATCH 1/2] Add sampler test with removed reference and union reference that remains --- ...dVisibilitySchemaTransformationTest.groovy | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/test/groovy/graphql/schema/transform/FieldVisibilitySchemaTransformationTest.groovy b/src/test/groovy/graphql/schema/transform/FieldVisibilitySchemaTransformationTest.groovy index d5b05d6d89..9296120427 100644 --- a/src/test/groovy/graphql/schema/transform/FieldVisibilitySchemaTransformationTest.groovy +++ b/src/test/groovy/graphql/schema/transform/FieldVisibilitySchemaTransformationTest.groovy @@ -2010,4 +2010,82 @@ class FieldVisibilitySchemaTransformationTest extends Specification { assertSchemaIsValid(restrictedSchema) } + def "union member type remains valid after its only private path is removed"() { + given: "a schema where PizzaTopping is reachable via a private creation payload and is also a union member" + GraphQLSchema schema = TestUtil.schema(""" + + directive @private on FIELD_DEFINITION + + type Query { + dummy: String + createPizzaTopping( + name: String! + ): PizzaToppingCreationPayload @private + toBeRemoved: PizzaNoReference @private + } + + union PizzaPolicy = + | PizzaTopping + | PizzaCrust + | PizzaDiscount + | PizzaAllergen + + interface Payload { + success: Boolean! + } + + type PizzaToppingCreationPayload implements Payload { + topping: PizzaTopping + success: Boolean! + } + + type PizzaTopping { + id: ID + name: String + } + + type PizzaCrust { + id: ID + name: String + } + + type PizzaDiscount { + id: ID + percentage: Float + } + + type PizzaAllergen { + id: ID + allergen: String + } + + type PizzaNoReference { + id: ID + } + """) + + when: + GraphQLSchema restrictedSchema = visibilitySchemaTransformation.apply(schema) + + then: "the private field is removed from Query" + (restrictedSchema.getType("Query") as GraphQLObjectType).getFieldDefinition("createPizzaTopping") == null + + and: "the creation payload type is removed as it is only reachable via the private field" + restrictedSchema.getType("PizzaToppingCreationPayload") == null + + and: "PizzaTopping is still present because it is reachable as a union member of the public PizzaPolicy" + restrictedSchema.getType("PizzaTopping") != null + + and: "the union and all its members remain intact" + restrictedSchema.getType("PizzaPolicy") != null + restrictedSchema.getType("PizzaCrust") != null + restrictedSchema.getType("PizzaDiscount") != null + restrictedSchema.getType("PizzaAllergen") != null + + restrictedSchema.getType("PizzaNoReference") == null + + and: "the resulting schema is valid - previously this would produce an invalid schema" + assertSchemaIsValid(restrictedSchema) + } + } From 24696d9f618dc8b8c0ae6ab6426d3d6651e4f39f Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Mon, 2 Mar 2026 06:08:55 +1100 Subject: [PATCH 2/2] Add the case where the non-union member is deleted --- ...dVisibilitySchemaTransformationTest.groovy | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/test/groovy/graphql/schema/transform/FieldVisibilitySchemaTransformationTest.groovy b/src/test/groovy/graphql/schema/transform/FieldVisibilitySchemaTransformationTest.groovy index 9296120427..0124e77b49 100644 --- a/src/test/groovy/graphql/schema/transform/FieldVisibilitySchemaTransformationTest.groovy +++ b/src/test/groovy/graphql/schema/transform/FieldVisibilitySchemaTransformationTest.groovy @@ -2010,18 +2010,16 @@ class FieldVisibilitySchemaTransformationTest extends Specification { assertSchemaIsValid(restrictedSchema) } - def "union member type remains valid after its only private path is removed"() { - given: "a schema where PizzaTopping is reachable via a private creation payload and is also a union member" + def "type is not deleted if remains a union member after its only private field path is removed"() { + given: GraphQLSchema schema = TestUtil.schema(""" directive @private on FIELD_DEFINITION type Query { - dummy: String - createPizzaTopping( - name: String! - ): PizzaToppingCreationPayload @private - toBeRemoved: PizzaNoReference @private + hello: String + createPizzaTopping(name: String!): PizzaToppingCreationPayload @private + toBeRemoved: PizzaToBeRemoved @private } union PizzaPolicy = @@ -2059,7 +2057,7 @@ class FieldVisibilitySchemaTransformationTest extends Specification { allergen: String } - type PizzaNoReference { + type PizzaToBeRemoved { id: ID } """) @@ -2067,22 +2065,15 @@ class FieldVisibilitySchemaTransformationTest extends Specification { when: GraphQLSchema restrictedSchema = visibilitySchemaTransformation.apply(schema) - then: "the private field is removed from Query" + then: "the private field paths are removed" (restrictedSchema.getType("Query") as GraphQLObjectType).getFieldDefinition("createPizzaTopping") == null - - and: "the creation payload type is removed as it is only reachable via the private field" restrictedSchema.getType("PizzaToppingCreationPayload") == null and: "PizzaTopping is still present because it is reachable as a union member of the public PizzaPolicy" restrictedSchema.getType("PizzaTopping") != null - and: "the union and all its members remain intact" - restrictedSchema.getType("PizzaPolicy") != null - restrictedSchema.getType("PizzaCrust") != null - restrictedSchema.getType("PizzaDiscount") != null - restrictedSchema.getType("PizzaAllergen") != null - - restrictedSchema.getType("PizzaNoReference") == null + and: "Types with no reference at all (not a union member) is correctly removed" + restrictedSchema.getType("PizzaToBeRemoved") == null and: "the resulting schema is valid - previously this would produce an invalid schema" assertSchemaIsValid(restrictedSchema)