From 8c4676ac065ebb3b6ff24068f79c77baeefe0299 Mon Sep 17 00:00:00 2001 From: Oryan M Date: Mon, 22 Feb 2021 10:40:12 -0500 Subject: [PATCH 1/5] Replace groovy tests with kotlin --- .../kickstart/tools/BuiltInIdSpec.groovy | 134 ---- .../kickstart/tools/EndToEndSpec.groovy | 653 ------------------ .../tools/EnumDefaultValueSpec.groovy | 59 -- .../tools/EnumListParameterSpec.groovy | 76 -- .../tools/FieldResolverScannerSpec.groovy | 141 ---- .../tools/GenericResolverSpec.groovy | 84 --- .../tools/InterfaceImplementation.groovy | 11 - .../MethodFieldResolverDataFetcherSpec.groovy | 254 ------- .../kickstart/tools/MultiResolverSpec.groovy | 80 --- .../kickstart/tools/MultipleInterfaces.groovy | 17 - .../kickstart/tools/NamedResource.groovy | 5 - .../tools/NestedInputTypesSpec.groovy | 127 ---- .../tools/ParameterizedGetterSpec.groovy | 70 -- .../tools/SchemaClassScannerSpec.groovy | 512 -------------- .../tools/SchemaParserBuilderSpec.groovy | 28 - .../kickstart/tools/SchemaParserSpec.groovy | 459 ------------ .../tools/SuperclassResolverSpec.groovy | 58 -- .../kickstart/tools/TestInterfaces.groovy | 13 - .../tools/TypeClassMatcherSpec.groovy | 167 ----- .../graphql/kickstart/tools/Utils.groovy | 28 - .../kickstart/tools/VersionedResource.groovy | 5 - .../graphql/kickstart/tools/BuiltInIdTest.kt | 116 ++++ .../graphql/kickstart/tools/EndToEndTest.kt | 641 +++++++++++++++++ .../kickstart/tools/EnumDefaultValueTest.kt | 55 ++ .../kickstart/tools/EnumListParameterTest.kt | 71 ++ .../tools/FieldResolverScannerTest.kt | 126 ++++ .../kickstart/tools/GenericResolverTest.kt | 66 ++ .../MethodFieldResolverDataFetcherTest.kt | 188 ++++- .../kickstart/tools/MultiResolverTest.kt | 71 ++ .../kickstart/tools/NestedInputTypesTest.kt | 125 ++++ .../tools/ParameterizedGetterTest.kt | 68 ++ .../graphql/kickstart/tools/ReactiveTest.kt | 21 +- .../kickstart/tools/RelayConnectionTest.kt | 25 +- .../kickstart/tools/ResolverMethodsTest.java | 2 +- .../kickstart/tools/SchemaClassScannerTest.kt | 503 ++++++++++++++ .../tools/SchemaParserBuilderTest.kt | 34 + .../kickstart/tools/SchemaParserTest.kt | 425 ++++++++++++ .../kickstart/tools/SuperclassResolverTest.kt | 47 ++ .../graphql/kickstart/tools/TestInterfaces.kt | 16 + .../graphql/kickstart/tools/TestUtils.kt | 22 + .../kickstart/tools/TypeClassMatcherTest.kt | 182 +++++ 41 files changed, 2759 insertions(+), 3026 deletions(-) delete mode 100644 src/test/groovy/graphql/kickstart/tools/BuiltInIdSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/EndToEndSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/EnumDefaultValueSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/EnumListParameterSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/FieldResolverScannerSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/GenericResolverSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/InterfaceImplementation.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/MethodFieldResolverDataFetcherSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/MultiResolverSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/MultipleInterfaces.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/NamedResource.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/NestedInputTypesSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/ParameterizedGetterSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/SchemaClassScannerSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/SchemaParserBuilderSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/SchemaParserSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/SuperclassResolverSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/TestInterfaces.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/TypeClassMatcherSpec.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/Utils.groovy delete mode 100644 src/test/groovy/graphql/kickstart/tools/VersionedResource.groovy create mode 100644 src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/EnumDefaultValueTest.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/GenericResolverTest.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/NestedInputTypesTest.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/SchemaParserBuilderTest.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/SuperclassResolverTest.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/TestInterfaces.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/TestUtils.kt create mode 100644 src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt diff --git a/src/test/groovy/graphql/kickstart/tools/BuiltInIdSpec.groovy b/src/test/groovy/graphql/kickstart/tools/BuiltInIdSpec.groovy deleted file mode 100644 index a9bfb62f..00000000 --- a/src/test/groovy/graphql/kickstart/tools/BuiltInIdSpec.groovy +++ /dev/null @@ -1,134 +0,0 @@ -package graphql.kickstart.tools - -import graphql.GraphQL -import graphql.execution.AsyncExecutionStrategy -import graphql.schema.GraphQLSchema -import spock.lang.Shared -import spock.lang.Specification - -class BuiltInIdSpec extends Specification { - - @Shared - GraphQL gql - - def setupSpec() { - GraphQLSchema schema = SchemaParser.newParser().schemaString('''\ - type Query { - itemByLongId(id: ID!): Item1! - itemsByLongIds(ids: [ID!]!): [Item1!]! - itemByUuidId(id: ID!): Item2! - itemsByUuidIds(ids: [ID!]!): [Item2!]! - } - - type Item1 { - id: ID! - } - - type Item2 { - id: ID! - } - '''.stripIndent()) - .resolvers(new QueryWithLongItemResolver()) - .build() - .makeExecutableSchema() - gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(new AsyncExecutionStrategy()) - .build() - } - - def "supports Long as ID as input"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - itemByLongId(id: 1) { - id - } - } - ''' - } - - then: - data.itemByLongId != null - data.itemByLongId.id == "1" - } - - def "supports list of Long as ID as input"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - itemsByLongIds(ids: [1,2,3]) { - id - } - } - ''' - } - - then: - data.itemsByLongIds != null - data.itemsByLongIds.size == 3 - data.itemsByLongIds[0].id == "1" - } - - def "supports UUID as ID as input"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - itemByUuidId(id: "00000000-0000-0000-0000-000000000000") { - id - } - } - ''' - } - - then: - data.itemByUuidId != null - data.itemByUuidId.id == "00000000-0000-0000-0000-000000000000" - } - - def "supports list of UUID as ID as input"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - itemsByUuidIds(ids: ["00000000-0000-0000-0000-000000000000","11111111-1111-1111-1111-111111111111","22222222-2222-2222-2222-222222222222"]) { - id - } - } - ''' - } - - then: - data.itemsByUuidIds != null - data.itemsByUuidIds.size == 3 - data.itemsByUuidIds[0].id == "00000000-0000-0000-0000-000000000000" - } - - class QueryWithLongItemResolver implements GraphQLQueryResolver { - Item1 itemByLongId(Long id) { - new Item1(id: id) - } - - List itemsByLongIds(List ids) { - ids.collect { new Item1(id: it) } - } - - Item2 itemByUuidId(UUID id) { - new Item2(id: id) - } - - List itemsByUuidIds(List ids) { - ids.collect { new Item2(id: it) } - } - } - - class Item1 { - Long id - } - - class Item2 { - UUID id - } -} diff --git a/src/test/groovy/graphql/kickstart/tools/EndToEndSpec.groovy b/src/test/groovy/graphql/kickstart/tools/EndToEndSpec.groovy deleted file mode 100644 index e654d455..00000000 --- a/src/test/groovy/graphql/kickstart/tools/EndToEndSpec.groovy +++ /dev/null @@ -1,653 +0,0 @@ -package graphql.kickstart.tools - -import graphql.ExecutionInput -import graphql.ExecutionResult -import graphql.GraphQL -import graphql.execution.AsyncExecutionStrategy -import graphql.schema.GraphQLSchema -import org.reactivestreams.Publisher -import org.reactivestreams.Subscriber -import org.reactivestreams.tck.TestEnvironment -import spock.lang.Shared -import spock.lang.Specification - -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit - -/** - * @author Andrew Potter - */ -class EndToEndSpec extends Specification { - - @Shared - GraphQL gql - - def setupSpec() { - GraphQLSchema schema = EndToEndSpecHelperKt.createSchema() - - gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(new AsyncExecutionStrategy()) - .build() - } - - def "schema comments are used as descriptions"() { - expect: - gql.graphQLSchema.allTypesAsList.find { it.name == 'Type' }?.valueDefinitionMap?.TYPE_1?.description == "Item type 1" - } - - def "generated schema should respond to simple queries"() { - when: - Utils.assertNoGraphQlErrors(gql) { - ''' - { - items(itemsInput: {name: "item1"}) { - id - type - } - } - ''' - } - - then: - noExceptionThrown() - } - - def "generated schema should respond to simple mutations"() { - when: - def data = Utils.assertNoGraphQlErrors(gql, [name: "new1", type: Type.TYPE_2.toString()]) { - ''' - mutation addNewItem($name: String!, $type: Type!) { - addItem(newItem: {name: $name, type: $type}) { - id - name - type - } - } - ''' - } - - then: - data.addItem - } - - def "generated schema should execute the subscription query"() { - when: - def newItem = new Item(1, "item", Type.TYPE_1, UUID.randomUUID(), []) - def returnedItem = null - def data = Utils.assertNoGraphQlErrors(gql, [:], new OnItemCreatedContext(newItem)) { - ''' - subscription { - onItemCreated { - id - } - } - ''' - } - CountDownLatch latch = new CountDownLatch(1) - (data as Publisher).subscribe(new Subscriber() { - @Override - void onSubscribe(org.reactivestreams.Subscription s) { - - } - - @Override - void onNext(ExecutionResult executionResult) { - returnedItem = executionResult.data - latch.countDown() - } - - @Override - void onError(Throwable t) { - } - - @Override - void onComplete() { - } - }) - latch.await(3, TimeUnit.SECONDS) - - then: - returnedItem.get("onItemCreated").id == 1 - } - - def "generated schema should handle interface types"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - itemsByInterface { - name - type - } - } - ''' - } - - then: - data.itemsByInterface - } - - def "generated schema should handle union types"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - allItems { - ... on Item { - id - name - } - ... on OtherItem { - name - type - } - } - } - ''' - } - - then: - data.allItems - } - - def "generated schema should handle nested union types"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - nestedUnionItems { - ... on Item { - itemId: id - } - ... on OtherItem { - otherItemId: id - } - ... on ThirdItem { - thirdItemId: id - } - } - } - ''' - } - - then: - data.nestedUnionItems == [[itemId: 0], [itemId: 1], [otherItemId: 0], [otherItemId: 1], [thirdItemId: 100]] - } - - def "generated schema should handle scalar types"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - itemByUUID(uuid: "38f685f1-b460-4a54-a17f-7fd69e8cf3f8") { - uuid - } - } - ''' - } - - then: - data.itemByUUID - } - - def "generated schema should handle non nullable scalar types"() { - when: - def fileParts = [new MockPart("test.doc", "Hello"), new MockPart("test.doc", "World")] - def args = ["fileParts": fileParts] - def data = Utils.assertNoGraphQlErrors(gql, args) { - ''' - mutation ($fileParts: [Upload!]!) { echoFiles(fileParts: $fileParts)} - ''' - } - then: - (data["echoFiles"] as ArrayList).join(",") == "Hello,World" - } - - def "generated schema should handle any java.util.Map (using HashMap) types as property maps"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - propertyHashMapItems { - name - age - } - } - ''' - } - - then: - data.propertyHashMapItems == [[name: "bob", age: 55]] - } - - def "generated schema should handle any java.util.Map (using SortedMap) types as property maps"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - propertySortedMapItems { - name - age - } - } - ''' - } - - then: - data.propertySortedMapItems == [[name: "Arthur", age: 76], [name: "Jane", age: 28]] - } - - // In this test a dictionary entry for the schema type ComplexMapItem is defined - // so that it is possible for a POJO mapping to be known since the ComplexMapItem is contained - // in a property map (i.e. Map) and so the normal resolver and schema traversal code - // will not be able to find the POJO since it does not exist as a strongly typed object in - // resolver/POJO graph. - def "generated schema should handle java.util.Map types as property maps when containing complex data"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - propertyMapWithComplexItems { - nameId { - id - } - age - } - } - ''' - } - - then: - data.propertyMapWithComplexItems == [[nameId: [id: 150], age: 72]] - } - - // This behavior is consistent with PropertyDataFetcher - def "property map returns null when a property is not defined."() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - propertyMapMissingNamePropItems { - name - age - } - } - ''' - } - - then: - data.propertyMapMissingNamePropItems == [[name: null, age: 55]] - } - - // In this test a dictonary entry for the schema type NestedComplexMapItem is defined - // however we expect to not be required to define one for the transitive UndiscoveredItem object since - // the schema resolver discovery code should still be able to automatically determine the POJO that - // maps to this schema type. - def "generated schema should continue to associate resolvers for transitive types of a java.util.Map complex data type"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - propertyMapWithNestedComplexItems { - nested { - item { - id - } - } - age - } - } - ''' - } - - then: - data.propertyMapWithNestedComplexItems == [[nested: [item: [id: 63]], age: 72]] - } - - - def "generated schema should handle optional arguments"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - missing: itemsWithOptionalInput { - id - } - - present: itemsWithOptionalInput(itemsInput: {name: "item1"}) { - id - } - } - ''' - } - - then: - data.missing?.size > 1 - data.present?.size == 1 - } - - def "generated schema should handle optional arguments using java.util.Optional"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - missing: itemsWithOptionalInputExplicit { - id - } - - present: itemsWithOptionalInputExplicit(itemsInput: {name: "item1"}) { - id - } - } - ''' - } - - then: - data.missing?.size > 1 - data.present?.size == 1 - } - - def "generated schema should handle optional return types using java.util.Optional"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - missing: optionalItem(itemsInput: {name: "item?"}) { - id - } - - present: optionalItem(itemsInput: {name: "item1"}) { - id - } - } - ''' - } - - then: - data.missing == null - data.present - } - - def "generated schema should pass default arguments"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - defaultArgument - } - ''' - } - - then: - data.defaultArgument == true - } - - def "introspection shouldn't fail for arguments of type list with a default value (defaultEnumListArgument)"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - __type(name: "Query") { - name - fields { - name - args { - name - defaultValue - } - } - } - } - ''' - } - - then: - data.__type - } - - def "generated schema should return null without errors for null value with nested fields"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - complexNullableType { - first - second - third - } - } - ''' - } - - then: - data.containsKey('complexNullableType') - data.complexNullableType == null - } - - def "generated schema handles nested lists in input type fields"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - complexInputType(complexInput: [[{first: "foo", second: [[{first: "bar"}]]}]]) - } - ''' - } - - then: - data.complexInputType - } - - def "generated schema should use type extensions"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - extendedType { - first - second - } - } - ''' - } - - then: - data.extendedType - data.extendedType.first - data.extendedType.second - } - - def "generated schema uses properties if no methods are found"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - propertyField - } - ''' - } - - then: - data.propertyField - } - - def "generated schema allows enums in input types"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - enumInputType(type: TYPE_2) - } - ''' - } - - then: - data.enumInputType == "TYPE_2" - } - - def "generated schema works with custom scalars as input values"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - customScalarMapInputType(customScalarMap: { test: "me" }) - } - ''' - } - - then: - data.customScalarMapInputType == [ - test: "me" - ] - } - - def "generated schema should handle extended input types"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - mutation { - saveUser(input: {name: "John", password: "secret"}) - } - ''' - } - - then: - data.saveUser == "John/secret" - } - - def "generated schema supports generic properties"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - itemWithGenericProperties { - keys - } - } - ''' - } - - then: - data.itemWithGenericProperties == [ - keys: ["A", "B"] - ] - } - - def "generated schema supports overriding built-in scalars"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - itemByBuiltInId(id: "38f685f1-b460-4a54-a17f-7fd69e8cf3f8") { - name - } - } - ''' - } - - then: - noExceptionThrown() - data.itemByBuiltInId != null - } - - def "generated schema supports DataFetcherResult"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - dataFetcherResult { - name - } - } - ''' - } - - then: - data.dataFetcherResult.name == "item1" - } - - def "generated schema supports Kotlin suspend functions"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - coroutineItems { - id - name - } - } - ''' - } - - then: - data.coroutineItems == [[id: 0, name: "item1"], [id: 1, name: "item2"]] - } - - def "generated schema supports Kotlin coroutine channels for the subscription query"() { - when: - def newItem = new Item(1, "item", Type.TYPE_1, UUID.randomUUID(), []) - def data = Utils.assertNoGraphQlErrors(gql, [:], new OnItemCreatedContext(newItem)) { - ''' - subscription { - onItemCreatedCoroutineChannel { - id - } - } - ''' - } - def subscriber = new TestEnvironment().newManualSubscriber(data as Publisher) - - then: - subscriber.requestNextElement().data.get("onItemCreatedCoroutineChannel").id == 1 - subscriber.expectCompletion() - } - - def "generated schema supports Kotlin coroutine channels with suspend function for the subscription query"() { - when: - def newItem = new Item(1, "item", Type.TYPE_1, UUID.randomUUID(), []) - def data = Utils.assertNoGraphQlErrors(gql, [:], new OnItemCreatedContext(newItem)) { - ''' - subscription { - onItemCreatedCoroutineChannelAndSuspendFunction { - id - } - } - ''' - } - def subscriber = new TestEnvironment().newManualSubscriber(data as Publisher) - - then: - subscriber.requestNextElement().data.get("onItemCreatedCoroutineChannelAndSuspendFunction").id == 1 - subscriber.expectCompletion() - } - - def "generated schema supports arrays"() { - when: - def data = Utils.assertNoGraphQlErrors(gql) { - ''' - { - arrayItems { - name - } - } - ''' - } - - then: - data.arrayItems.collect { it.name } == ['item1', 'item2'] - } - - def "generated schema should re-throw original runtime exception when executing a resolver method"() { - when: - - gql.execute(ExecutionInput.newExecutionInput().query(''' - { - throwsIllegalArgumentException - } - ''' - )) - - then: - IllegalArgumentException - } -} diff --git a/src/test/groovy/graphql/kickstart/tools/EnumDefaultValueSpec.groovy b/src/test/groovy/graphql/kickstart/tools/EnumDefaultValueSpec.groovy deleted file mode 100644 index e9813bc2..00000000 --- a/src/test/groovy/graphql/kickstart/tools/EnumDefaultValueSpec.groovy +++ /dev/null @@ -1,59 +0,0 @@ -package graphql.kickstart.tools - -import graphql.GraphQL -import graphql.execution.AsyncExecutionStrategy -import graphql.schema.GraphQLSchema -import spock.lang.Specification - -class EnumDefaultValueSpec extends Specification { - - def "enumvalue is not passed down to graphql-java"() { - when: - GraphQLSchema schema = SchemaParser.newParser().schemaString('''\ - type Query { - test(input: MySortSpecifier): SortBy - } - input MySortSpecifier { - sortBy: SortBy = createdOn - value: Int = 10 - } - enum SortBy { - createdOn - updatedOn - } - ''').resolvers(new GraphQLQueryResolver() { - - SortBy test(MySortSpecifier input) { - return input.sortBy - } - - }) - .build() - .makeExecutableSchema() - GraphQL gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(new AsyncExecutionStrategy()) - .build() - def data = Utils.assertNoGraphQlErrors(gql, [input: [value: 1]]) { - ''' - query test($input: MySortSpecifier) { - test(input: $input) - } - ''' - } - - then: - noExceptionThrown() - data.test == 'createdOn' - } - - static class MySortSpecifier { - SortBy sortBy - int value - } - - enum SortBy { - createdOn, - updatedOn - } - -} diff --git a/src/test/groovy/graphql/kickstart/tools/EnumListParameterSpec.groovy b/src/test/groovy/graphql/kickstart/tools/EnumListParameterSpec.groovy deleted file mode 100644 index 0166f84c..00000000 --- a/src/test/groovy/graphql/kickstart/tools/EnumListParameterSpec.groovy +++ /dev/null @@ -1,76 +0,0 @@ -package graphql.kickstart.tools - -import graphql.GraphQL -import graphql.execution.AsyncExecutionStrategy -import graphql.schema.GraphQLSchema -import spock.lang.Shared -import spock.lang.Specification - -class EnumListParameterSpec extends Specification { - - @Shared - GraphQL gql - - def setupSpec() { - GraphQLSchema schema = SchemaParser.newParser().schemaString('''\ - type Query { - countries(regions: [Region!]!): [Country!]! - } - - enum Region { - EUROPE - ASIA - } - - type Country { - code: String! - name: String! - regions: [Region!] - } - '''.stripIndent()) - .resolvers(new QueryResolver()) - .build() - .makeExecutableSchema() - gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(new AsyncExecutionStrategy()) - .build() - } - - def "query with parameter type list of enums should resolve correctly"() { - when: - def data = Utils.assertNoGraphQlErrors(gql, [regions: ["EUROPE", "ASIA"]]) { - ''' - query getCountries($regions: [Region!]!) { - countries(regions: $regions){ - code - name - regions - } - } - ''' - } - - then: - data.countries == [] - } - - class QueryResolver implements GraphQLQueryResolver { - Set getCountries(Set regions) { - return Collections.emptySet() - } - } - - class Country { - String code - String name - List regions - } - - enum Region { - EUROPE, - ASIA - } - -} - - diff --git a/src/test/groovy/graphql/kickstart/tools/FieldResolverScannerSpec.groovy b/src/test/groovy/graphql/kickstart/tools/FieldResolverScannerSpec.groovy deleted file mode 100644 index e2a14b89..00000000 --- a/src/test/groovy/graphql/kickstart/tools/FieldResolverScannerSpec.groovy +++ /dev/null @@ -1,141 +0,0 @@ -package graphql.kickstart.tools - -import graphql.kickstart.tools.resolver.FieldResolverError -import graphql.kickstart.tools.resolver.FieldResolverScanner -import graphql.kickstart.tools.resolver.MethodFieldResolver -import graphql.kickstart.tools.resolver.PropertyFieldResolver -import graphql.language.FieldDefinition -import graphql.language.TypeName -import graphql.relay.Connection -import spock.lang.Specification - -/** - * @author Andrew Potter - */ -class FieldResolverScannerSpec extends Specification { - - private static final SchemaParserOptions options = SchemaParserOptions.defaultOptions() - private static final FieldResolverScanner scanner = new FieldResolverScanner(options) - - def "scanner finds fields on multiple root types"() { - setup: - def resolver = new RootResolverInfo([new RootQuery1(), new RootQuery2()], options) - - when: - def result1 = scanner.findFieldResolver(new FieldDefinition("field1", new TypeName("String")), resolver) - def result2 = scanner.findFieldResolver(new FieldDefinition("field2", new TypeName("String")), resolver) - - then: - result1.search.source != result2.search.source - } - - def "scanner throws exception when more than one resolver method is found"() { - setup: - def resolver = new RootResolverInfo([new RootQuery1(), new DuplicateQuery()], options) - - when: - scanner.findFieldResolver(new FieldDefinition("field1", new TypeName("String")), resolver) - - then: - thrown(FieldResolverError) - } - - def "scanner throws exception when no resolver methods are found"() { - setup: - def resolver = new RootResolverInfo([], options) - - when: - scanner.findFieldResolver(new FieldDefinition("field1", new TypeName("String")), resolver) - - then: - thrown(FieldResolverError) - } - - def "scanner finds properties when no method is found"() { - setup: - def resolver = new RootResolverInfo([new PropertyQuery()], options) - - when: - def name = scanner.findFieldResolver(new FieldDefinition("name", new TypeName("String")), resolver) - def version = scanner.findFieldResolver(new FieldDefinition("version", new TypeName("Integer")), resolver) - - then: - name instanceof PropertyFieldResolver - version instanceof PropertyFieldResolver - } - - def "scanner finds generic return type"() { - setup: - def resolver = new RootResolverInfo([new GenericQuery()], options) - - when: - def users = scanner.findFieldResolver(new FieldDefinition("users", new TypeName("UserConnection")), resolver) - - then: - users instanceof MethodFieldResolver - } - - def "scanner prefers concrete resolver"() { - setup: - def resolver = new DataClassResolverInfo(Kayak.class) - - when: - def meta = scanner.findFieldResolver(new FieldDefinition("information", new TypeName("VehicleInformation")), resolver) - - then: - meta instanceof MethodFieldResolver - ((MethodFieldResolver) meta).getMethod().getReturnType() == BoatInformation.class - } - - def "scanner finds field resolver method using camelCase for snake_cased field_name"() { - setup: - def resolver = new RootResolverInfo([new CamelCaseQuery1()], options) - - when: - def meta = scanner.findFieldResolver(new FieldDefinition("hull_type", new TypeName("HullType")), resolver) - - then: - meta instanceof MethodFieldResolver - ((MethodFieldResolver) meta).getMethod().getReturnType() == HullType.class - } - - class RootQuery1 implements GraphQLQueryResolver { - def field1() {} - } - - class RootQuery2 implements GraphQLQueryResolver { - def field2() {} - } - - class DuplicateQuery implements GraphQLQueryResolver { - def field1() {} - } - - class CamelCaseQuery1 implements GraphQLQueryResolver { - HullType getHullType(){} - } - - class HullType {} - - class ParentPropertyQuery { - private Integer version = 1 - } - - class PropertyQuery extends ParentPropertyQuery implements GraphQLQueryResolver { - private String name = "name" - } - - class User {} - - class GenericQuery implements GraphQLQueryResolver { - Connection getUsers() {} - } - - abstract class Boat implements Vehicle { - BoatInformation getInformation() { return this.information; } - } - - class BoatInformation implements VehicleInformation {} - - class Kayak extends Boat {} -} diff --git a/src/test/groovy/graphql/kickstart/tools/GenericResolverSpec.groovy b/src/test/groovy/graphql/kickstart/tools/GenericResolverSpec.groovy deleted file mode 100644 index ea67500a..00000000 --- a/src/test/groovy/graphql/kickstart/tools/GenericResolverSpec.groovy +++ /dev/null @@ -1,84 +0,0 @@ -package graphql.kickstart.tools - - -import spock.lang.Specification - -class GenericResolverSpec extends Specification { - - def "methods from generic resolvers are resolved"() { - when: - SchemaParser.newParser().schemaString('''\ - type Query { - bar: Bar! - } - - type Bar { - value: String - } - ''') - .resolvers(new QueryResolver1(), new BarResolver()) - .build() - .makeExecutableSchema() - - then: - noExceptionThrown() - } - - class QueryResolver1 implements GraphQLQueryResolver { - Bar getBar() { - return new Bar() - } - } - - class Bar { - } - - abstract class FooResolver implements GraphQLResolver { - String getValue(T foo) { - return "value" - } - } - - class BarResolver extends FooResolver implements GraphQLResolver { - - } - - - def "methods from generic inherited resolvers are resolved"() { - when: - SchemaParser.newParser().schemaString('''\ - type Query { - car: Car! - } - type Car { - value: String - } - ''') - .resolvers(new QueryResolver2(), new CarResolver()) - .build() - .makeExecutableSchema() - - then: - noExceptionThrown() - } - - - class QueryResolver2 implements GraphQLQueryResolver { - Car getCar() { - return new Car() - } - } - - abstract class FooGraphQLResolver implements GraphQLResolver { - String getValue(T foo) { - return "value" - } - } - - class Car { - } - - class CarResolver extends FooGraphQLResolver { - - } -} diff --git a/src/test/groovy/graphql/kickstart/tools/InterfaceImplementation.groovy b/src/test/groovy/graphql/kickstart/tools/InterfaceImplementation.groovy deleted file mode 100644 index a453f703..00000000 --- a/src/test/groovy/graphql/kickstart/tools/InterfaceImplementation.groovy +++ /dev/null @@ -1,11 +0,0 @@ -package graphql.kickstart.tools - -class InterfaceImplementation implements GraphQLQueryResolver { - NamedResource query1() { null } - - NamedResourceImpl query2() { null } - - static class NamedResourceImpl implements NamedResource { - String name() {} - } -} diff --git a/src/test/groovy/graphql/kickstart/tools/MethodFieldResolverDataFetcherSpec.groovy b/src/test/groovy/graphql/kickstart/tools/MethodFieldResolverDataFetcherSpec.groovy deleted file mode 100644 index c7ca1c34..00000000 --- a/src/test/groovy/graphql/kickstart/tools/MethodFieldResolverDataFetcherSpec.groovy +++ /dev/null @@ -1,254 +0,0 @@ -package graphql.kickstart.tools - -import graphql.Assert -import graphql.ExecutionResult -import graphql.execution.* -import graphql.execution.instrumentation.SimpleInstrumentation -import graphql.kickstart.tools.resolver.FieldResolverError -import graphql.kickstart.tools.resolver.FieldResolverScanner -import graphql.language.FieldDefinition -import graphql.language.InputValueDefinition -import graphql.language.NonNullType -import graphql.language.TypeName -import graphql.schema.DataFetcher -import graphql.schema.DataFetchingEnvironment -import graphql.schema.DataFetchingEnvironmentImpl -import spock.lang.Specification - -import java.util.concurrent.CompletableFuture - -/** - * @author Andrew Potter - */ -class MethodFieldResolverDataFetcherSpec extends Specification { - - def "data fetcher throws exception if resolver has too many arguments"() { - when: - createFetcher("active", new GraphQLQueryResolver() { - boolean active(def arg1, def arg2) { true } - }) - - then: - thrown(FieldResolverError) - } - - def "data fetcher throws exception if resolver has too few arguments"() { - when: - createFetcher("active", [new InputValueDefinition("doesNotExist", new TypeName("Boolean"))], new GraphQLQueryResolver() { - boolean active() { true } - }) - - then: - thrown(FieldResolverError) - } - - def "data fetcher prioritizes methods on the resolver"() { - setup: - def name = "Resolver Name" - def resolver = createFetcher("name", new GraphQLResolver() { - String getName(DataClass dataClass) { name } - }) - - expect: - resolver.get(createEnvironment(new DataClass())) == name - } - - def "data fetcher uses data class methods if no resolver method is given"() { - setup: - def resolver = createFetcher("name", new GraphQLResolver() {}) - - expect: - resolver.get(createEnvironment(new DataClass())) == DataClass.name - } - - def "data fetcher prioritizes methods without a prefix"() { - setup: - def name = "correct name" - def resolver = createFetcher("name", new GraphQLResolver() { - String name(DataClass dataClass) { name } - - String getName(DataClass dataClass) { "in" + name } - }) - - expect: - resolver.get(createEnvironment(new DataClass())) == name - } - - def "data fetcher uses 'is' prefix for booleans (primitive type)"() { - setup: - def resolver = createFetcher("active", new GraphQLResolver() { - boolean isActive(DataClass dataClass) { true } - - boolean getActive(DataClass dataClass) { true } - }) - - expect: - resolver.get(createEnvironment(new DataClass())) - } - - def "data fetcher uses 'is' prefix for Booleans (Object type)"() { - setup: - def resolver = createFetcher("active", new GraphQLResolver() { - Boolean isActive(DataClass dataClass) { Boolean.TRUE } - - Boolean getActive(DataClass dataClass) { Boolean.TRUE } - }) - - expect: - resolver.get(createEnvironment(new DataClass())) - } - - def "data fetcher passes environment if method has extra argument"() { - setup: - def resolver = createFetcher("active", new GraphQLResolver() { - boolean isActive(DataClass dataClass, DataFetchingEnvironment env) { - env instanceof DataFetchingEnvironment - } - }) - - expect: - resolver.get(createEnvironment(new DataClass())) - } - - def "data fetcher passes environment if method has extra argument even if context is specified"() { - setup: - def options = SchemaParserOptions.newOptions().contextClass(ContextClass).build() - def resolver = createFetcher(options, "active", new GraphQLResolver() { - boolean isActive(DataClass dataClass, DataFetchingEnvironment env) { - env instanceof DataFetchingEnvironment - } - }) - - expect: - resolver.get(createEnvironment(new ContextClass(), new DataClass())) - } - - def "data fetcher passes context if method has extra argument and context is specified"() { - setup: - def context = new ContextClass() - def options = SchemaParserOptions.newOptions().contextClass(ContextClass).build() - def resolver = createFetcher(options, "active", new GraphQLResolver() { - boolean isActive(DataClass dataClass, ContextClass ctx) { - ctx == context - } - }) - - expect: - resolver.get(createEnvironment(context, new DataClass())) - } - - def "data fetcher marshalls input object if required"() { - setup: - def name = "correct name" - def resolver = createFetcher("active", [new InputValueDefinition("input", new TypeName("InputClass"))], new GraphQLQueryResolver() { - boolean active(InputClass input) { - input instanceof InputClass && input.name == name - } - }) - - expect: - resolver.get(createEnvironment([input: [name: name]])) - } - - def "data fetcher doesn't marshall input object if not required"() { - setup: - def name = "correct name" - def resolver = createFetcher("active", [new InputValueDefinition("input", new TypeName("Map"))], new GraphQLQueryResolver() { - boolean active(Map input) { - input instanceof Map && input.name == name - } - }) - - expect: - resolver.get(createEnvironment([input: [name: name]])) - } - - def "data fetcher returns null if nullable argument is passed null"() { - setup: - def resolver = createFetcher("echo", [new InputValueDefinition("message", new TypeName("String"))], new GraphQLQueryResolver() { - String echo(String message) { - return message - } - }) - - expect: - resolver.get(createEnvironment()) == null - } - - def "data fetcher throws exception if non-null argument is passed null"() { - setup: - def resolver = createFetcher("echo", [new InputValueDefinition("message", new NonNullType(new TypeName("String")))], new GraphQLQueryResolver() { - String echo(String message) { - return message - } - }) - - when: - resolver.get(createEnvironment()) - - then: - thrown(ResolverError) - } - - private static DataFetcher createFetcher(String methodName, List arguments = [], GraphQLResolver resolver) { - return createFetcher(SchemaParserOptions.defaultOptions(), methodName, arguments, resolver) - } - - private static DataFetcher createFetcher(SchemaParserOptions options, String methodName, List arguments = [], GraphQLResolver resolver) { - def field = FieldDefinition.newFieldDefinition() - .name(methodName) - .type(new TypeName('Boolean')) - .inputValueDefinitions(arguments) - .build() - - new FieldResolverScanner(options).findFieldResolver(field, resolver instanceof GraphQLQueryResolver ? new RootResolverInfo([resolver], options) : new NormalResolverInfo(resolver, options)).createDataFetcher() - } - - private static DataFetchingEnvironment createEnvironment(Map arguments = [:]) { - createEnvironment(new Object(), arguments) - } - - private static DataFetchingEnvironment createEnvironment(Object source, Map arguments = [:]) { - createEnvironment(null, source, arguments) - } - - private static DataFetchingEnvironment createEnvironment(Object context, Object source, Map arguments = [:]) { - DataFetchingEnvironmentImpl.newDataFetchingEnvironment(buildExecutionContext()) - .source(source) - .arguments(arguments) - .context(context) - .build() - } - - private static ExecutionContext buildExecutionContext() { - ExecutionStrategy executionStrategy = new ExecutionStrategy() { - @Override - CompletableFuture execute(ExecutionContext executionContext, ExecutionStrategyParameters parameters) { - return Assert.assertShouldNeverHappen("should not be called") - } - } - ExecutionId executionId = ExecutionId.from("executionId123") - ExecutionContextBuilder.newExecutionContextBuilder() - .instrumentation(SimpleInstrumentation.INSTANCE) - .executionId(executionId) - .queryStrategy(executionStrategy) - .mutationStrategy(executionStrategy) - .subscriptionStrategy(executionStrategy) - .build() - } - - class DataClass { - private static final String name = "Data Class Name" - - String getName() { - name - } - } - - static class InputClass { - String name - } - - class ContextClass { - } -} diff --git a/src/test/groovy/graphql/kickstart/tools/MultiResolverSpec.groovy b/src/test/groovy/graphql/kickstart/tools/MultiResolverSpec.groovy deleted file mode 100644 index 00451ac1..00000000 --- a/src/test/groovy/graphql/kickstart/tools/MultiResolverSpec.groovy +++ /dev/null @@ -1,80 +0,0 @@ -package graphql.kickstart.tools - -import graphql.GraphQL -import graphql.execution.AsyncExecutionStrategy -import graphql.schema.GraphQLSchema -import spock.lang.Shared -import spock.lang.Specification - -class MultiResolverSpec extends Specification { - - @Shared - GraphQL gql - - def setupSpec() { - GraphQLSchema schema = SchemaParser.newParser().schemaString('''\ - type Query { - person: Person - } - - type Person { - name: String! - friends(friendName: String!): [Friend!]! - } - - type Friend { - name: String! - } - '''.stripIndent()) - .resolvers(new QueryWithPersonResolver(), new PersonFriendResolver(), new PersonNameResolver()) - .build() - .makeExecutableSchema() - gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(new AsyncExecutionStrategy()) - .build() - } - - def "multiple resolvers for one data class should resolve methods with arguments"() { - when: - def data = Utils.assertNoGraphQlErrors(gql, [friendName: "name"]) { - ''' - query friendOfPerson($friendName: String!) { - person { - friends(friendName: $friendName) { - name - } - } - } - ''' - } - - then: - data.person - } - - class QueryWithPersonResolver implements GraphQLQueryResolver { - Person getPerson() { - new Person() - } - } - - class Person { - - } - - class Friend { - String name - } - - class PersonFriendResolver implements GraphQLResolver { - List friends(Person person, String friendName) { - Collections.emptyList() - } - } - - class PersonNameResolver implements GraphQLResolver { - String name(Person person) { - "name" - } - } -} diff --git a/src/test/groovy/graphql/kickstart/tools/MultipleInterfaces.groovy b/src/test/groovy/graphql/kickstart/tools/MultipleInterfaces.groovy deleted file mode 100644 index 8c8783f0..00000000 --- a/src/test/groovy/graphql/kickstart/tools/MultipleInterfaces.groovy +++ /dev/null @@ -1,17 +0,0 @@ -package graphql.kickstart.tools - -class MultipleInterfaces implements GraphQLQueryResolver { - NamedResourceImpl query1() { null } - - VersionedResourceImpl query2() { null } - - static class NamedResourceImpl implements NamedResource { - String name() {} - } - - static class VersionedResourceImpl implements VersionedResource { - int version() {} - } -} - - diff --git a/src/test/groovy/graphql/kickstart/tools/NamedResource.groovy b/src/test/groovy/graphql/kickstart/tools/NamedResource.groovy deleted file mode 100644 index 0f871caf..00000000 --- a/src/test/groovy/graphql/kickstart/tools/NamedResource.groovy +++ /dev/null @@ -1,5 +0,0 @@ -package graphql.kickstart.tools - -interface NamedResource { - String name() -} diff --git a/src/test/groovy/graphql/kickstart/tools/NestedInputTypesSpec.groovy b/src/test/groovy/graphql/kickstart/tools/NestedInputTypesSpec.groovy deleted file mode 100644 index d0ce1a68..00000000 --- a/src/test/groovy/graphql/kickstart/tools/NestedInputTypesSpec.groovy +++ /dev/null @@ -1,127 +0,0 @@ -package graphql.kickstart.tools - -import graphql.GraphQL -import graphql.execution.AsyncExecutionStrategy -import graphql.schema.GraphQLSchema -import spock.lang.Specification - -class NestedInputTypesSpec extends Specification { - - def "nested input types are parsed"() { - when: - GraphQLSchema schema = SchemaParser.newParser().schemaString('''\ - type Query { - materials(filter: MaterialFilter): [Material!]! - } - - input MaterialFilter { - title: String - requestFilter: RequestFilter - } - - input RequestFilter { - and: [RequestFilter!] - or: [RequestFilter!] - discountTypeFilter: DiscountTypeFilter - } - - input DiscountTypeFilter { - name: String - } - - type Material { - id: ID! - } - ''').resolvers(new QueryResolver()) - .build() - .makeExecutableSchema() - GraphQL gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(new AsyncExecutionStrategy()) - .build() - def data = Utils.assertNoGraphQlErrors(gql, [filter: [title: "title", requestFilter: [discountTypeFilter: [name: "discount"]]]]) { - ''' - query materials($filter: MaterialFilter!) { - materials(filter: $filter) { - id - } - } - ''' - } - - then: - noExceptionThrown() - data.materials == [] - } - - def "nested input in extensions are parsed"() { - when: - GraphQLSchema schema = SchemaParser.newParser().schemaString('''\ - type Query { - materials(filter: MaterialFilter): [Material!]! - } - - input MaterialFilter { - title: String - } - - extend input MaterialFilter { - requestFilter: RequestFilter - } - - input RequestFilter { - and: [RequestFilter!] - or: [RequestFilter!] - discountTypeFilter: DiscountTypeFilter - } - - input DiscountTypeFilter { - name: String - } - - type Material { - id: ID! - } - ''').resolvers(new QueryResolver()) - .build() - .makeExecutableSchema() - GraphQL gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(new AsyncExecutionStrategy()) - .build() - def data = Utils.assertNoGraphQlErrors(gql, [filter: [title: "title", requestFilter: [discountTypeFilter: [name: "discount"]]]]) { - ''' - query materials($filter: MaterialFilter!) { - materials(filter: $filter) { - id - } - } - ''' - } - - then: - noExceptionThrown() - data.materials == [] - } - - class QueryResolver implements GraphQLQueryResolver { - List materials(MaterialFilter filter) { Collections.emptyList() } - } - - class Material { - Long id - } - - static class MaterialFilter { - String title - RequestFilter requestFilter - } - - static class RequestFilter { - List and - List or - DiscountTypeFilter discountTypeFilter - } - - static class DiscountTypeFilter { - String name - } -} diff --git a/src/test/groovy/graphql/kickstart/tools/ParameterizedGetterSpec.groovy b/src/test/groovy/graphql/kickstart/tools/ParameterizedGetterSpec.groovy deleted file mode 100644 index 0d5ccade..00000000 --- a/src/test/groovy/graphql/kickstart/tools/ParameterizedGetterSpec.groovy +++ /dev/null @@ -1,70 +0,0 @@ -package graphql.kickstart.tools - -import graphql.GraphQL -import graphql.execution.AsyncExecutionStrategy -import graphql.schema.GraphQLSchema -import spock.lang.Shared -import spock.lang.Specification - -class ParameterizedGetterSpec extends Specification { - - @Shared - GraphQL gql - - def setupSpec() { - GraphQLSchema schema = SchemaParser.newParser().schemaString('''\ - type Query { - human: Human - } - - type Human { - bestFriends: [Character!]! - allFriends(limit: Int!): [Character!]! - } - - type Character { - name: String! - } - '''.stripIndent()) - .resolvers(new QueryResolver(), new HumanResolver()) - .build() - .makeExecutableSchema() - gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(new AsyncExecutionStrategy()) - .build() - } - - def "parameterized query is resolved on data type instead of on its resolver"() { - when: - def data = Utils.assertNoGraphQlErrors(gql, [limit: 10]) { - ''' - query allFriends($limit: Int!) { - human { - allFriends(limit: $limit) { - name - } - } - } - ''' - } - - then: - data.human - } - - class QueryResolver implements GraphQLQueryResolver { - Human human() { new Human() } - } - - class Human { - List allFriends(int limit) { Collections.emptyList() } - } - - class HumanResolver implements GraphQLResolver { - List bestFriends(Human human) { Collections.emptyList() } - } - - class Character { - String name - } -} diff --git a/src/test/groovy/graphql/kickstart/tools/SchemaClassScannerSpec.groovy b/src/test/groovy/graphql/kickstart/tools/SchemaClassScannerSpec.groovy deleted file mode 100644 index 876ba3c6..00000000 --- a/src/test/groovy/graphql/kickstart/tools/SchemaClassScannerSpec.groovy +++ /dev/null @@ -1,512 +0,0 @@ -package graphql.kickstart.tools - -import graphql.language.* -import graphql.schema.Coercing -import graphql.schema.GraphQLScalarType -import spock.lang.Specification - -import java.util.concurrent.CompletableFuture - -/** - * @author Andrew Potter - */ -class SchemaClassScannerSpec extends Specification { - - def "scanner handles futures and immediate return types"() { - when: - SchemaParser.newParser() - .resolvers(new FutureImmediateQuery()) - .schemaString(""" - type Query { - future: Int! - immediate: Int! - } - """) - .scan() - then: - noExceptionThrown() - } - - private class FutureImmediateQuery implements GraphQLQueryResolver { - CompletableFuture future() { - CompletableFuture.completedFuture(1) - } - - Integer immediate() { - 1 - } - } - - def "scanner handles primitive and boxed return types"() { - when: - SchemaParser.newParser() - .resolvers(new PrimitiveBoxedQuery()) - .schemaString(""" - type Query { - primitive: Int! - boxed: Int! - } - """) - .scan() - then: - noExceptionThrown() - } - - private class PrimitiveBoxedQuery implements GraphQLQueryResolver { - int primitive() { - 1 - } - - Integer boxed() { - 1 - } - } - - def "scanner handles different scalars with same java class"() { - when: - SchemaParser.newParser() - .resolvers(new ScalarDuplicateQuery()) - .schemaString(""" - type Query { - string: String! - id: ID! - } - """) - .scan() - - then: - noExceptionThrown() - } - - private class ScalarDuplicateQuery implements GraphQLQueryResolver { - String string() { "" } - - String id() { "" } - } - - def "scanner handles interfaces referenced by objects that aren't explicitly used"() { - when: - ScannedSchemaObjects objects = SchemaParser.newParser() - .resolvers(new InterfaceMissingQuery()) - .schemaString(""" - interface Interface { - id: ID! - } - - type Query implements Interface { - id: ID! - } - """) - .scan() - - then: - objects.definitions.find { it instanceof InterfaceTypeDefinition } != null - } - - private class InterfaceMissingQuery implements GraphQLQueryResolver { - String id() { "" } - } - - def "scanner handles input types that reference other input types"() { - when: - ScannedSchemaObjects objects = SchemaParser.newParser() - .resolvers(new MultipleInputTypeQuery()) - .schemaString(""" - input FirstInput { - id: String! - second: SecondInput! - third: ThirdInput! - } - input SecondInput { - id: String! - } - input ThirdInput { - id: String! - } - - type Query { - test(input: FirstInput): String! - } - """) - .scan() - - then: - objects.definitions.findAll { it instanceof InputObjectTypeDefinition }.size() == 3 - - } - - private class MultipleInputTypeQuery implements GraphQLQueryResolver { - - String test(FirstInput input) { "" } - - class FirstInput { - String id - - SecondInput second() { new SecondInput() } - ThirdInput third - } - - class SecondInput { - String id - } - - class ThirdInput { - String id - } - } - - def "scanner handles input types extensions"() { - when: - ScannedSchemaObjects objects = SchemaParser.newParser() - .schemaString(''' - type Query { } - - type Mutation { - save(input: UserInput!): Boolean - } - - input UserInput { - name: String - } - - extend input UserInput { - password: String - } - ''') - .resolvers( - new GraphQLMutationResolver() { - boolean save(Map map) { true } - }, - new GraphQLQueryResolver() {} - ) - .scan() - - then: - objects.definitions.findAll { (it.class == InputObjectTypeExtensionDefinition.class) }.size() == 1 - objects.definitions.findAll { (it.class == InputObjectTypeDefinition.class) }.size() == 1 - } - - def "scanner allows multiple return types for custom scalars"() { - when: - ScannedSchemaObjects objects = SchemaParser.newParser() - .resolvers(new ScalarsWithMultipleTypes()) - .scalars(new GraphQLScalarType("UUID", "Test scalars with duplicate types", new Coercing() { - @Override - Object serialize(Object input) { - return null - } - - @Override - Object parseValue(Object input) { - return null - } - - @Override - Object parseLiteral(Object input) { - return null - } - })) - .schemaString(""" - scalar UUID - - type Query { - first: UUID - second: UUID - } - """) - .scan() - - then: - objects.definitions.findAll { it instanceof ScalarTypeDefinition }.size() == 1 - } - - class ScalarsWithMultipleTypes implements GraphQLQueryResolver { - Integer first() { null } - - String second() { null } - } - - def "scanner handles multiple interfaces that are not used as field types"() { - when: - ScannedSchemaObjects objects = SchemaParser.newParser() - .resolvers(new MultipleInterfaces()) - .schemaString(""" - type Query { - query1: NamedResourceImpl - query2: VersionedResourceImpl - } - - interface NamedResource { - name: String! - } - - interface VersionedResource { - version: Int! - } - - type NamedResourceImpl implements NamedResource { - name: String! - } - - type VersionedResourceImpl implements VersionedResource { - version: Int! - } - """) - .scan() - - then: - objects.definitions.findAll { it instanceof InterfaceTypeDefinition }.size() == 2 - } - - def "scanner handles interface implementation that is not used as field type"() { - when: - ScannedSchemaObjects objects = SchemaParser.newParser() - // uncommenting the line below makes the test succeed - .dictionary(InterfaceImplementation.NamedResourceImpl.class) - .resolvers(new InterfaceImplementation()) - .schemaString(""" - type Query { - query1: NamedResource - } - - interface NamedResource { - name: String! - } - - type NamedResourceImpl implements NamedResource { - name: String! - } - """) - .scan() - - then: - objects.definitions.findAll { it instanceof InterfaceTypeDefinition }.size() == 1 - } - - def "scanner handles custom scalars when matching input types"() { - when: - GraphQLScalarType customMap = new GraphQLScalarType('customMap', '', new Coercing, Map>() { - @Override - Map serialize(Object dataFetcherResult) { - return [:] - } - - @Override - Map parseValue(Object input) { - return [:] - } - - @Override - Map parseLiteral(Object input) { - return [:] - } - }) - - ScannedSchemaObjects objects = SchemaParser.newParser() - .resolvers(new GraphQLQueryResolver() { - boolean hasRawScalar(Map rawScalar) { true } - - boolean hasMapField(HasMapField mapField) { true } - }) - .scalars(customMap) - .schemaString(""" - type Query { - hasRawScalar(customMap: customMap): Boolean - hasMapField(mapField: HasMapField): Boolean - } - - input HasMapField { - map: customMap - } - - scalar customMap - """) - .scan() - - then: - objects.definitions.findAll { it instanceof ScalarTypeDefinition }.size() == 2 // Boolean and customMap - } - - class HasMapField { - Map map - } - - def "scanner allows class to be used for object type and input object type"() { - when: - ScannedSchemaObjects objects = SchemaParser.newParser() - .resolvers(new GraphQLQueryResolver() { - Pojo test(Pojo pojo) { pojo } - }) - .schemaString(""" - type Query { - test(inPojo: InPojo): OutPojo - } - - input InPojo { - name: String - } - - type OutPojo { - name: String - } - """) - .scan() - - then: - objects.definitions - } - - class Pojo { - String name - } - - def "scanner should handle nested types in input types"() { - when: - ScannedSchemaObjects objects = SchemaParser.newParser() - .schemaString(''' - schema { - query: Query - } - - type Query { - animal: Animal - } - - interface Animal { - type: ComplexType - } - - type Dog implements Animal { - type: ComplexType - } - - type ComplexType { - id: String - } - ''') - .resolvers(new NestedInterfaceTypeQuery()) - .dictionary(NestedInterfaceTypeQuery.Dog) - .scan() - - then: - objects.definitions.findAll { it instanceof ObjectTypeDefinition }.size() == 3 - - } - - class NestedInterfaceTypeQuery implements GraphQLQueryResolver { - Animal animal() { null } - - class Dog implements Animal { - @Override - ComplexType type() { null } - } - - class ComplexType { - String id - } - } - - def "scanner should handle unused types when option is true"() { - when: - ScannedSchemaObjects objects = SchemaParser.newParser() - .schemaString(''' - # Let's say this is the Products service from Apollo Federation Introduction - - type Query { - allProducts: [Product] - } - - type Product { - name: String - } - - # these directives are defined in the Apollo Federation Specification: - # https://www.apollographql.com/docs/apollo-server/federation/federation-spec/ - type User @key(fields: "id") @extends { - id: ID! @external - recentPurchasedProducts: [Product] - address: Address - } - - type Address { - street: String - } - ''') - .resolvers(new GraphQLQueryResolver() { - List allProducts() { null } - }) - .options(SchemaParserOptions.newOptions().includeUnusedTypes(true).build()) - .dictionary(User) - .scan() - - then: - objects.definitions.find { it.name == "User" } != null - objects.definitions.find { it.name == "Address" } != null - } - - class Product { - String name - } - - class User { - String id - List recentPurchasedProducts - Address address - } - - class Address { - String street - } - - def "scanner should handle unused types with interfaces when option is true"() { - when: - ScannedSchemaObjects objects = SchemaParser.newParser() - .schemaString(''' - type Query { - whatever: Whatever - } - - type Whatever { - value: String - } - - type Unused { - someInterface: SomeInterface - } - - interface SomeInterface { - value: String - } - - type Implementation implements SomeInterface { - value: String - } - ''') - .resolvers(new GraphQLQueryResolver() { - Whatever whatever() { null } - }) - .options(SchemaParserOptions.newOptions().includeUnusedTypes(true).build()) - .dictionary(Unused, Implementation) - .scan() - - then: - objects.definitions.find { it.name == "Unused" } != null - objects.definitions.find { it.name == "SomeInterface" } != null - objects.definitions.find { it.name == "Implementation" } != null - } - - class Whatever { - String value - } - - class Unused { - SomeInterface someInterface - } - - class Implementation implements SomeInterface { - @Override - String getValue() { - return null - } - } -} diff --git a/src/test/groovy/graphql/kickstart/tools/SchemaParserBuilderSpec.groovy b/src/test/groovy/graphql/kickstart/tools/SchemaParserBuilderSpec.groovy deleted file mode 100644 index 4b60e63b..00000000 --- a/src/test/groovy/graphql/kickstart/tools/SchemaParserBuilderSpec.groovy +++ /dev/null @@ -1,28 +0,0 @@ -package graphql.kickstart.tools - -import graphql.parser.InvalidSyntaxException -import spock.lang.Specification -import spock.lang.Unroll - -/** - * @author Andrew Potter - */ -class SchemaParserBuilderSpec extends Specification { - - @Unroll - def "parser errors should be returned in full"() { - when: - SchemaParser.newParser() - .schemaString(schema) - .build() - - then: - def e = thrown(InvalidSyntaxException) - e.toString().contains(error) - - where: - schema | error - "invalid" | "offending token 'invalid' at line 1 column 1" - "type Query {\ninvalid!\n}" | "offending token '!' at line 2 column 8" - } -} diff --git a/src/test/groovy/graphql/kickstart/tools/SchemaParserSpec.groovy b/src/test/groovy/graphql/kickstart/tools/SchemaParserSpec.groovy deleted file mode 100644 index d141a903..00000000 --- a/src/test/groovy/graphql/kickstart/tools/SchemaParserSpec.groovy +++ /dev/null @@ -1,459 +0,0 @@ -package graphql.kickstart.tools - -import graphql.kickstart.tools.resolver.FieldResolverError -import graphql.language.SourceLocation -import graphql.schema.GraphQLSchema -import org.springframework.aop.framework.ProxyFactory -import spock.lang.Specification - -import java.util.concurrent.Future - -/** - * @author Andrew Potter - */ -class SchemaParserSpec extends Specification { - - SchemaParserBuilder builder - - def setup() { - builder = SchemaParser.newParser() - .schemaString(''' - type Query { - get(int: Int!): Int! - } - ''') - } - - def "builder throws FileNotFound exception when file is missing"() { - when: - builder.file("/404").build() - - then: - thrown(FileNotFoundException) - } - - def "builder doesn't throw FileNotFound exception when file is present"() { - when: - SchemaParser.newParser().file("Test.graphqls") - .resolvers(new GraphQLQueryResolver() { - String getId() { "1" } - }) - .build() - - then: - noExceptionThrown() - } - - def "parser throws SchemaError when Query resolver is missing"() { - when: - builder.build().makeExecutableSchema() - - then: - thrown(SchemaClassScannerError) - } - - def "parser throws ResolverError when Query resolver is given without correct method"() { - when: - SchemaParser.newParser() - .schemaString(''' - type Query { - get(int: Int!): Int! - } - ''') - .resolvers(new GraphQLQueryResolver() {}) - .build() - .makeExecutableSchema() - - then: - thrown(FieldResolverError) - } - - def "parser should parse correctly when Query resolver is given"() { - when: - SchemaParser.newParser() - .schemaString(''' - type Query { - get(int: Int!): Int! - } - ''') - .resolvers(new GraphQLQueryResolver() { - int get(int i) { return i } - }) - .build() - .makeExecutableSchema() - - then: - noExceptionThrown() - } - - def "parser should parse correctly when multiple query resolvers are given"() { - when: - SchemaParser.newParser() - .schemaString(''' - type Obj { - name: String - } - - type AnotherObj { - key: String - } - - type Query { - obj: Obj - anotherObj: AnotherObj - } - ''') - .resolvers(new GraphQLQueryResolver() { - Obj getObj() { return new Obj() } - }, new GraphQLQueryResolver() { - AnotherObj getAnotherObj() { return new AnotherObj() } - }) - .build() - .makeExecutableSchema() - - then: - noExceptionThrown() - } - - def "parser should parse correctly when multiple resolvers for the same data type are given"() { - when: - SchemaParser.newParser() - .schemaString(''' - type RootObj { - obj: Obj - anotherObj: AnotherObj - } - - type Obj { - name: String - } - - type AnotherObj { - key: String - } - - type Query { - rootObj: RootObj - } - ''') - .resolvers(new GraphQLQueryResolver() { - RootObj getRootObj() { return new RootObj() } - }, new GraphQLResolver() { - Obj getObj(RootObj rootObj) { return new Obj() } - }, new GraphQLResolver() { - AnotherObj getAnotherObj(RootObj rootObj) { return new AnotherObj() } - }) - .build() - .makeExecutableSchema() - - then: - noExceptionThrown() - } - - def "parser should allow setting custom generic wrappers"() { - when: - SchemaParser.newParser() - .schemaString(''' - type Query { - one: Object! - two: Object! - } - - type Object { - name: String! - } - ''') - .resolvers(new GraphQLQueryResolver() { - CustomGenericWrapper one() { null } - - Obj two() { null } - }) - .options(SchemaParserOptions.newOptions().genericWrappers(new SchemaParserOptions.GenericWrapper(CustomGenericWrapper, 1)).build()) - .build() - .makeExecutableSchema() - - then: - noExceptionThrown() - } - - def "parser should allow turning off default generic wrappers"() { - when: - SchemaParser.newParser() - .schemaString(''' - type Query { - one: Object! - two: Object! - } - - type Object { - toString: String! - } - ''') - .resolvers(new GraphQLQueryResolver() { - Future one() { null } - - Obj two() { null } - }) - .options(SchemaParserOptions.newOptions().useDefaultGenericWrappers(false).build()) - .build() - .makeExecutableSchema() - - then: - thrown(SchemaClassScannerError) - } - - def "parser should throw descriptive exception when object is used as input type incorrectly"() { - when: - SchemaParser.newParser() - .schemaString(''' - type Query { - name(filter: Filter): [String] - } - - type Filter { - filter: String - } - ''') - .resolvers(new GraphQLQueryResolver() { - List name(Filter filter) { null } - }) - .build() - .makeExecutableSchema() - - then: - def t = thrown(SchemaError) - t.message.contains("Was a type only permitted for object types incorrectly used as an input type, or vice-versa") - } - - def "parser handles spring AOP proxied resolvers by default"() { - when: - def resolver = new ProxyFactory(new ProxiedResolver()).getProxy() as GraphQLQueryResolver - - SchemaParser.newParser() - .schemaString(''' - type Query { - test: [String] - } - ''') - .resolvers(resolver) - .build() - then: - noExceptionThrown() - } - - def "parser handles enums with overridden toString method"() { - when: - SchemaParser.newParser() - .schemaString(''' - enum CustomEnum { - FOO - } - - type Query { - customEnum: CustomEnum - } - ''') - .resolvers(new GraphQLQueryResolver() { - CustomEnum customEnum() { null } - }) - .build() - .makeExecutableSchema() - then: - noExceptionThrown() - } - - def "parser should include source location for field definition"() { - when: - GraphQLSchema schema = SchemaParser.newParser() - .schemaString('''\ - |type Query { - | id: ID! - |} - '''.stripMargin()) - .resolvers(new QueryWithIdResolver()) - .build() - .makeExecutableSchema() - - then: - SourceLocation sourceLocation = schema.getObjectType("Query") - .getFieldDefinition("id") - .definition.sourceLocation - sourceLocation != null - sourceLocation.line == 2 - sourceLocation.column == 5 - sourceLocation.sourceName == null - } - - def "parser should include source location for field definition when loaded from single classpath file"() { - when: - GraphQLSchema schema = SchemaParser.newParser() - .file("Test.graphqls") - .resolvers(new QueryWithIdResolver()) - .build() - .makeExecutableSchema() - - then: - SourceLocation sourceLocation = schema.getObjectType("Query") - .getFieldDefinition("id") - .definition.sourceLocation - sourceLocation != null - sourceLocation.line == 2 - sourceLocation.column == 3 - sourceLocation.sourceName == "Test.graphqls" - } - - def "support enum types if only used as input type"() { - when: - SchemaParser.newParser().schemaString('''\ - type Query { test: Boolean } - - type Mutation { - save(input: SaveInput!): Boolean - } - - input SaveInput { - type: EnumType! - } - - enum EnumType { - TEST - } - '''.stripIndent()) - .resolvers(new GraphQLMutationResolver() { - boolean save(SaveInput input) { false } - - class SaveInput { - EnumType type; - } - - }, new GraphQLQueryResolver() { - boolean test() { false } - }) - .dictionary(EnumType.class) - .build() - .makeExecutableSchema() - - then: - noExceptionThrown() - } - - def "support enum types if only used in input Map"() { - when: - SchemaParser.newParser().schemaString('''\ - type Query { test: Boolean } - - type Mutation { - save(input: SaveInput!): Boolean - } - - input SaveInput { - age: Int - type: EnumType! - } - - enum EnumType { - TEST - } - '''.stripIndent()) - .resolvers(new GraphQLMutationResolver() { - boolean save(Map input) { false } - }, new GraphQLQueryResolver() { - boolean test() { false } - }) - .dictionary(EnumType.class) - .build() - .makeExecutableSchema() - - then: - noExceptionThrown() - } - - def "allow circular relations in input objects"() { - when: - SchemaParser.newParser().schemaString('''\ - input A { - id: ID! - b: B - } - input B { - id: ID! - a: A - } - input C { - id: ID! - c: C - } - type Query { test: Boolean } - type Mutation { - test(input: A!): Boolean - testC(input: C!): Boolean - } - '''.stripIndent()) - .resolvers(new GraphQLMutationResolver() { - static class A { - String id; - B b; - } - - static class B { - String id; - A a; - } - - static class C { - String id; - C c; - } - - boolean test(A a) { return true } - - boolean testC(C c) { return true } - }, new GraphQLQueryResolver() { - boolean test() { false } - }) - .build() - .makeExecutableSchema() - - then: - noExceptionThrown() - } - - enum EnumType { - TEST - } - - class QueryWithIdResolver implements GraphQLQueryResolver { - String getId() { null } - } - - class Filter { - String filter() { null } - } - - class CustomGenericWrapper {} - - class Obj { - def name() { null } - } - - class AnotherObj { - def key() { null } - } - - class RootObj { - } - - class ProxiedResolver implements GraphQLQueryResolver { - List test() { [] } - } - - enum CustomEnum { - FOO{ - @Override - String toString() { - return "Bar" - } - } - } - -} diff --git a/src/test/groovy/graphql/kickstart/tools/SuperclassResolverSpec.groovy b/src/test/groovy/graphql/kickstart/tools/SuperclassResolverSpec.groovy deleted file mode 100644 index 5332562b..00000000 --- a/src/test/groovy/graphql/kickstart/tools/SuperclassResolverSpec.groovy +++ /dev/null @@ -1,58 +0,0 @@ -package graphql.kickstart.tools - - -import spock.lang.Specification - -class SuperclassResolverSpec extends Specification { - - def "methods from generic resolvers are resolved"() { - when: - SchemaParser.newParser().schemaString('''\ - type Query { - bar: Bar! - } - - type Bar implements Foo{ - value: String - getValueWithSeveralParameters(arg1: Boolean!, arg2: String): String! - } - - interface Foo { - getValueWithSeveralParameters(arg1: Boolean!, arg2: String): String! - } - ''') - .resolvers(new QueryResolver(), new BarResolver()) - .build() - .makeExecutableSchema() - - then: - noExceptionThrown() - } - - class QueryResolver implements GraphQLQueryResolver { - Bar getBar() { - return new Bar() - } - } - - class Bar { - } - - abstract class FooResolver implements GraphQLResolver { - String getValue(T foo) { - return "value" - } - - String getValueWithSeveralParameters(T foo, boolean arg1, String arg2) { - if (arg1) { - return "value" - } else { - return arg2 - } - } - } - - class BarResolver extends FooResolver { - - } -} diff --git a/src/test/groovy/graphql/kickstart/tools/TestInterfaces.groovy b/src/test/groovy/graphql/kickstart/tools/TestInterfaces.groovy deleted file mode 100644 index 496dab49..00000000 --- a/src/test/groovy/graphql/kickstart/tools/TestInterfaces.groovy +++ /dev/null @@ -1,13 +0,0 @@ -package graphql.kickstart.tools - -interface Animal { - SchemaClassScannerSpec.NestedInterfaceTypeQuery.ComplexType type() -} - -interface Vehicle { - VehicleInformation getInformation(); -} - -interface VehicleInformation {} - -interface SomeInterface { String getValue() } diff --git a/src/test/groovy/graphql/kickstart/tools/TypeClassMatcherSpec.groovy b/src/test/groovy/graphql/kickstart/tools/TypeClassMatcherSpec.groovy deleted file mode 100644 index 0a3e905f..00000000 --- a/src/test/groovy/graphql/kickstart/tools/TypeClassMatcherSpec.groovy +++ /dev/null @@ -1,167 +0,0 @@ -package graphql.kickstart.tools - -import graphql.kickstart.tools.resolver.FieldResolverScanner -import graphql.language.* -import spock.lang.Specification -import spock.lang.Unroll - -import java.util.concurrent.CompletableFuture -import java.util.concurrent.Future - -/** - * @author Andrew Potter - */ -class TypeClassMatcherSpec extends Specification { - - private static final graphql.language.Type unwrappedCustomType = new TypeName("UnwrappedGenericCustomType") - private static final graphql.language.Type customType = new TypeName("CustomType") - private static final TypeDefinition customDefinition = new ObjectTypeDefinition("CustomType") - private static final TypeDefinition unwrappedCustomDefinition = new ObjectTypeDefinition("UnwrappedGenericCustomType") - - private static final TypeClassMatcher matcher = new TypeClassMatcher([ - CustomType : customDefinition, - UnwrappedGenericCustomType: unwrappedCustomDefinition - ]) - private static final SchemaParserOptions options = SchemaParserOptions.newOptions().genericWrappers( - new SchemaParserOptions.GenericWrapper( - GenericCustomType.class, - 0 - ), - SchemaParserOptions.GenericWrapper.listCollectionWithTransformer( - GenericCustomListType.class, - 0, - { x -> x } - ) - ).build() - private static final FieldResolverScanner scanner = new FieldResolverScanner(options) - - private final resolver = new RootResolverInfo([new QueryMethods()], options) - - private TypeClassMatcher.PotentialMatch createPotentialMatch(String methodName, graphql.language.Type graphQLType) { - scanner.findFieldResolver(new FieldDefinition(methodName, graphQLType), resolver) - .scanForMatches() - .find { it.location == TypeClassMatcher.Location.RETURN_TYPE } - } - - private graphql.language.Type list(graphql.language.Type other = customType) { - new ListType(other) - } - - private graphql.language.Type nonNull(graphql.language.Type other = customType) { - new NonNullType(other) - } - - @Unroll - def "matcher verifies that nested return type matches graphql definition for method #methodName"() { - when: - def match = matcher.match(createPotentialMatch(methodName, type)) - - then: - noExceptionThrown() - match.type == customDefinition - match.javaType == CustomType - - where: - methodName | type - "type" | customType - "futureType" | customType - "listType" | list() - "listListType" | list(list()) - "futureListType" | list() - "listFutureType" | list() - "listListFutureType" | list(list()) - "futureListListType" | list(list()) - "superType" | customType - "superListFutureType" | list() - "nullableType" | customType - "nullableListType" | list(nonNull(customType)) - "genericCustomType" | customType - "genericListType" | list() - } - - @Unroll - def "matcher verifies that nested return type doesn't match graphql definition for method #methodName"() { - when: - matcher.match(createPotentialMatch(methodName, type)) - - then: - thrown(SchemaClassScannerError) - - where: - methodName | type - "type" | list() - "futureType" | list() - } - - @Unroll - def "matcher verifies return value optionals are used incorrectly for method #methodName"() { - when: - matcher.match(createPotentialMatch(methodName, type)) - - then: - thrown(SchemaClassScannerError) - - where: - methodName | type - "nullableType" | nonNull(customType) - "nullableNullableType" | customType - "listNullableType" | list(customType) - } - - def "matcher allows unwrapped parameterized types as root types"() { - when: - def match = matcher.match(createPotentialMatch("genericCustomUnwrappedType", unwrappedCustomType)) - - then: - noExceptionThrown() - match.type == unwrappedCustomDefinition - match.javaType.getRawType() == UnwrappedGenericCustomType - } - - private class Super implements GraphQLQueryResolver { - Type superType() { null } - - ListFutureType superListFutureType() { null } - } - - private class QueryMethods extends Super>> { - CustomType type() { null } - - Future futureType() { null } - - List listType() { null } - - List> listListType() { null } - - CompletableFuture> futureListType() { null } - - List> listFutureType() { null } - - List>> listListFutureType() { null } - - CompletableFuture>> futureListListType() { null } - - Optional nullableType() { null } - - Optional> nullableListType() { null } - - Optional> nullableNullableType() { null } - - List> listNullableType() { null } - - GenericCustomType genericCustomType() { null } - - GenericCustomListType genericListType() { null } - - UnwrappedGenericCustomType genericCustomUnwrappedType() { null } - } - - private class CustomType {} - - private static class GenericCustomType {} - - private static class GenericCustomListType {} - - private static class UnwrappedGenericCustomType {} - -} diff --git a/src/test/groovy/graphql/kickstart/tools/Utils.groovy b/src/test/groovy/graphql/kickstart/tools/Utils.groovy deleted file mode 100644 index 075da225..00000000 --- a/src/test/groovy/graphql/kickstart/tools/Utils.groovy +++ /dev/null @@ -1,28 +0,0 @@ -package graphql.kickstart.tools - -import com.fasterxml.jackson.databind.ObjectMapper -import graphql.ExecutionInput -import graphql.GraphQL -import groovy.transform.CompileStatic - -/** - * @author Andrew Potter - */ -@CompileStatic -class Utils { - private static ObjectMapper mapper = new ObjectMapper() - - static Map assertNoGraphQlErrors(GraphQL gql, Map args = [:], Object context = new Object(), Closure closure) { - def result = gql.execute(ExecutionInput.newExecutionInput() - .query(closure()) - .context(context) - .root(context) - .variables(args)) - - if (!result.errors.isEmpty()) { - throw new AssertionError("GraphQL result contained errors!\n${result.errors.collect { mapper.writeValueAsString(it) }.join("\n")}") - } - - return result.data as Map - } -} diff --git a/src/test/groovy/graphql/kickstart/tools/VersionedResource.groovy b/src/test/groovy/graphql/kickstart/tools/VersionedResource.groovy deleted file mode 100644 index de3c9c41..00000000 --- a/src/test/groovy/graphql/kickstart/tools/VersionedResource.groovy +++ /dev/null @@ -1,5 +0,0 @@ -package graphql.kickstart.tools - -interface VersionedResource { - int version() -} diff --git a/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt b/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt new file mode 100644 index 00000000..bb516bbd --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt @@ -0,0 +1,116 @@ +package graphql.kickstart.tools + +import graphql.GraphQL +import graphql.execution.AsyncExecutionStrategy +import org.junit.Before +import org.junit.Test +import java.util.* + +class BuiltInIdTest { + + private lateinit var gql: GraphQL + + @Before + fun setup() { + val schema = SchemaParser.newParser().schemaString(""" + type Query { + itemByLongId(id: ID!): Item1! + itemsByLongIds(ids: [ID!]!): [Item1!]! + itemByUuidId(id: ID!): Item2! + itemsByUuidIds(ids: [ID!]!): [Item2!]! + } + + type Item1 { + id: ID! + } + + type Item2 { + id: ID! + } + """.trimIndent()) + .resolvers(QueryWithLongItemResolver()) + .build() + .makeExecutableSchema() + gql = GraphQL.newGraphQL(schema) + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() + + } + + @Test + fun `supports Long as ID as input`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + itemByLongId(id: 1) { + id + } + } + """ + } + + assert(data["itemByLongId"] != null) + assert((data["itemByLongId"] as Map<*, *>)["id"] == "1") + } + + @Test + fun `supports list of Long as ID as input`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + itemsByLongIds(ids: [1,2,3]) { + id + } + } + """ + } + + assert(data["itemsByLongIds"] != null) + assert(((data["itemsByLongIds"] as List<*>).size == 3)) + assert(((data["itemsByLongIds"] as List<*>)[0] as Map<*, *>)["id"] == "1") + } + + @Test + fun `supports UUID as ID as input`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + itemByUuidId(id: "00000000-0000-0000-0000-000000000000") { + id + } + } + """ + } + + assert(data["itemByUuidId"] != null) + assert((data["itemByUuidId"] as Map<*, *>)["id"] == "00000000-0000-0000-0000-000000000000") + } + + @Test + fun `supports list of UUID as ID as input`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + itemsByUuidIds(ids: ["00000000-0000-0000-0000-000000000000","11111111-1111-1111-1111-111111111111","22222222-2222-2222-2222-222222222222"]) { + id + } + } + """ + } + + assert(data["itemsByUuidIds"] != null) + assert(((data["itemsByUuidIds"] as List<*>).size == 3)) + assert(((data["itemsByUuidIds"] as List<*>)[0] as Map<*, *>)["id"] == "00000000-0000-0000-0000-000000000000") + + } + + class QueryWithLongItemResolver : GraphQLQueryResolver { + fun itemByLongId(id: Long): Item1 = Item1(id) + fun itemsByLongIds(ids: List): List = ids.map { Item1(it) } + fun itemByUuidId(id: UUID): Item2 = Item2(id) + fun itemsByUuidIds(ids: List): List = ids.map { Item2(it) } + } + + class Item1(var id: Long? = null) + class Item2(var id: UUID? = null) +} diff --git a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt new file mode 100644 index 00000000..531cd7e5 --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt @@ -0,0 +1,641 @@ +package graphql.kickstart.tools + +import graphql.* +import graphql.execution.AsyncExecutionStrategy +import graphql.schema.GraphQLEnumType +import graphql.schema.GraphQLSchema +import org.junit.Before +import org.junit.Test +import org.reactivestreams.Publisher +import org.reactivestreams.Subscriber +import org.reactivestreams.Subscription +import org.reactivestreams.tck.TestEnvironment +import java.util.* +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit + +class EndToEndTest { + + private lateinit var gql: GraphQL + private val schema: GraphQLSchema = createSchema() + + @Before + fun setup() { + gql = GraphQL.newGraphQL(schema) + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() + } + + @Test + fun `schema comments are used as descriptions`() { + val type = schema.allTypesAsList.find { it.name == "Type" } as GraphQLEnumType + assert(type.values[0].description == "Item type 1") + assert(type.values[1].description == "Item type 2") + } + + @Test + fun `generated schema should respond to simple queries`() { + assertNoGraphQlErrors(gql) { + """ + { + items(itemsInput: {name: "item1"}) { + id + type + } + } + """ + } + } + + @Test + fun `generated schema should respond to simple mutations`() { + val data = assertNoGraphQlErrors(gql, mapOf("name" to "new1", "type" to Type.TYPE_2.toString())) { + """ + mutation addNewItem(${'$'}name: String!, ${'$'}type: Type!) { + addItem(newItem: {name: ${'$'}name, type: ${'$'}type}) { + id + name + type + } + } + """ + } + + assert(data["addItem"] != null) + } + + @Test + fun `generated schema should execute the subscription query`() { + val newItem = Item(1, "item", Type.TYPE_1, UUID.randomUUID(), listOf()) + var returnedItem: Map>? = null + + val closure = { + """ + subscription { + onItemCreated { + id + } + } + """ + } + + val result = gql.execute(ExecutionInput.newExecutionInput() + .query(closure.invoke()) + .context(OnItemCreatedContext(newItem)) + .variables(mapOf())) + + val data = result.getData() as Publisher + val latch = CountDownLatch(1) + data.subscribe(object : Subscriber { + override fun onNext(item: ExecutionResult?) { + returnedItem = item?.getData() + latch.countDown() + } + + override fun onError(throwable: Throwable?) {} + override fun onComplete() {} + override fun onSubscribe(p0: Subscription?) {} + }) + latch.await(3, TimeUnit.SECONDS) + + assert(result.errors.isEmpty()) + assert(returnedItem?.get("onItemCreated")?.get("id") == 1) + } + + @Test + fun `generated schema should handle interface types`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + itemsByInterface { + name + type + } + } + """ + } + + assert(data["itemsByInterface"] != null) + } + + @Test + fun `generated schema should handle union types`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + allItems { + ... on Item { + id + name + } + ... on OtherItem { + name + type + } + } + } + """ + } + + assert(data["allItems"] != null) + } + + @Test + fun `generated schema should handle nested union types`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + nestedUnionItems { + ... on Item { + itemId: id + } + ... on OtherItem { + otherItemId: id + } + ... on ThirdItem { + thirdItemId: id + } + } + } + """ + } + + val items = data["nestedUnionItems"] as List> + assert(items[0]["itemId"] == 0) + assert(items[1]["itemId"] == 1) + assert(items[2]["otherItemId"] == 0) + assert(items[3]["otherItemId"] == 1) + assert(items[4]["thirdItemId"] == 100) + } + + @Test + fun `generated schema should handle scalar types`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + itemByUUID(uuid: "38f685f1-b460-4a54-a17f-7fd69e8cf3f8") { + uuid + } + } + """ + } + + assert(data["itemByUUID"] != null) + } + + @Test + fun `generated schema should handle non nullable scalar types`() { + val fileParts = listOf(MockPart("test.doc", "Hello"), MockPart("test.doc", "World")) + val args = mapOf("fileParts" to fileParts) + val data = assertNoGraphQlErrors(gql, args) { + """ + mutation (${'$'}fileParts: [Upload!]!) { echoFiles(fileParts: ${'$'}fileParts)} + """ + } + + assert((data["echoFiles"] as ArrayList).joinToString(",") == "Hello,World") + } + + @Test + fun `generated schema should handle any Map (using HashMap) types as property maps`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + propertyHashMapItems { + name + age + } + } + """ + } + + assert(data["propertyHashMapItems"] == listOf(mapOf("name" to "bob", "age" to 55))) + } + + @Test + fun `generated schema should handle any Map (using SortedMap) types as property maps`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + propertySortedMapItems { + name + age + } + } + """ + } + + assert(data["propertySortedMapItems"] == listOf( + mapOf("name" to "Arthur", "age" to 76), + mapOf("name" to "Jane", "age" to 28) + )) + } + + // In this test a dictionary entry for the schema type ComplexMapItem is defined + // so that it is possible for a POJO mapping to be known since the ComplexMapItem is contained + // in a property map (i.e. Map) and so the normal resolver and schema traversal code + // will not be able to find the POJO since it does not exist as a strongly typed object in + // resolver/POJO graph. + @Test + fun `generated schema should handle Map types as property maps when containing complex data`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + propertyMapWithComplexItems { + nameId { + id + } + age + } + } + """ + } + + assert(data["propertyMapWithComplexItems"] == listOf(mapOf("nameId" to mapOf("id" to 150), "age" to 72))) + } + + // This behavior is consistent with PropertyDataFetcher + @Test + fun `property map returns null when a property is not defined`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + propertyMapMissingNamePropItems { + name + age + } + } + """ + } + + assert(data["propertyMapMissingNamePropItems"] == listOf(mapOf("name" to null, "age" to 55))) + } + + // In this test a dictonary entry for the schema type NestedComplexMapItem is defined + // however we expect to not be required to define one for the transitive UndiscoveredItem object since + // the schema resolver discovery code should still be able to automatically determine the POJO that + // maps to this schema type. + @Test + fun `generated schema should continue to associate resolvers for transitive types of a Map complex data type`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + propertyMapWithNestedComplexItems { + nested { + item { + id + } + } + age + } + } + """ + } + + assert(data["propertyMapWithNestedComplexItems"] == listOf(mapOf("nested" to mapOf("item" to mapOf("id" to 63)), "age" to 72))) + } + + @Test + fun `generated schema should handle optional arguments`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + missing: itemsWithOptionalInput { + id + } + + present: itemsWithOptionalInput(itemsInput: {name: "item1"}) { + id + } + } + """ + } + + assert((data["missing"] as List<*>).size > 1) + assert((data["present"] as List<*>).size == 1) + } + + @Test + fun `generated schema should handle optional arguments using Optional`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + missing: itemsWithOptionalInputExplicit { + id + } + + present: itemsWithOptionalInputExplicit(itemsInput: {name: "item1"}) { + id + } + } + """ + } + + assert((data["missing"] as List<*>).size > 1) + assert((data["present"] as List<*>).size == 1) + } + + @Test + fun `generated schema should handle optional return types using Optional`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + missing: optionalItem(itemsInput: {name: "item?"}) { + id + } + + present: optionalItem(itemsInput: {name: "item1"}) { + id + } + } + """ + } + + assert(data["missing"] == null) + assert(data["present"] != null) + } + + @Test + fun `generated schema should pass default arguments`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + defaultArgument + } + """ + } + + assert(data["defaultArgument"] == true) + } + + @Test + fun `introspection shouldn't fail for arguments of type list with a default value (defaultEnumListArgument)`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + __type(name: "Query") { + name + fields { + name + args { + name + defaultValue + } + } + } + } + """ + } + + assert(data["__type"] != null) + } + + @Test + fun `generated schema should return null without errors for null value with nested fields`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + complexNullableType { + first + second + third + } + } + """ + } + + assert((data as Map<*, *>).containsKey("complexNullableType")) + assert(data["complexNullableType"] == null) + } + + @Test + fun `generated schema handles nested lists in input type fields`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + complexInputType(complexInput: [[{first: "foo", second: [[{first: "bar"}]]}]]) + } + """ + } + + assert(data["complexInputType"] != null) + } + + @Test + fun `generated schema should use type extensions`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + extendedType { + first + second + } + } + """ + } + + assert(data["extendedType"] != null) + assert((data["extendedType"] as Map<*, *>)["first"] != null) + assert((data["extendedType"] as Map<*, *>)["second"] != null) + } + + @Test + fun `generated schema uses properties if no methods are found`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + propertyField + } + """ + } + + assert(data["propertyField"] != null) + } + + @Test + fun `generated schema allows enums in input types`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + enumInputType(type: TYPE_2) + } + """ + } + + assert(data["enumInputType"] == "TYPE_2") + } + + @Test + fun `generated schema works with custom scalars as input values`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + customScalarMapInputType(customScalarMap: { test: "me" }) + } + """ + } + + assert(data["customScalarMapInputType"] == mapOf("test" to "me")) + } + + @Test + fun `generated schema should handle extended input types`() { + val data = assertNoGraphQlErrors(gql) { + """ + mutation { + saveUser(input: {name: "John", password: "secret"}) + } + """ + } + + assert(data["saveUser"] == "John/secret") + } + + @Test + fun `generated schema supports generic properties`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + itemWithGenericProperties { + keys + } + } + """ + } + + assert(data["itemWithGenericProperties"] == mapOf("keys" to listOf("A", "B"))) + } + + @Test + fun `generated schema supports overriding built-in scalars`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + itemByBuiltInId(id: "38f685f1-b460-4a54-a17f-7fd69e8cf3f8") { + name + } + } + """ + } + + assert(data["itemByBuiltInId"] != null) + } + + @Test + fun `generated schema supports DataFetcherResult`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + dataFetcherResult { + name + } + } + """ + } + + assert((data["dataFetcherResult"] as Map<*, *>)["name"] == "item1") + } + + @Test + fun `generated schema supports Kotlin suspend functions`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + coroutineItems { + id + name + } + } + """ + } + + assert(data["coroutineItems"] == listOf( + mapOf("id" to 0, "name" to "item1"), + mapOf("id" to 1, "name" to "item2") + )) + } + + @Test + fun `generated schema supports Kotlin coroutine channels for the subscription query`() { + val newItem = Item(1, "item", Type.TYPE_1, UUID.randomUUID(), listOf()) + val closure = { + """ + subscription { + onItemCreatedCoroutineChannel { + id + } + } + """ + } + + val result = gql.execute(ExecutionInput.newExecutionInput() + .query(closure.invoke()) + .context(OnItemCreatedContext(newItem)) + .variables(mapOf())) + + val data = result.getData() as Publisher + val subscriber = TestEnvironment().newManualSubscriber(data) + + val subscriberResult = subscriber.requestNextElement() as ExecutionResultImpl + val subscriberData = subscriberResult.getData() as Map>? + assert(result.errors.isEmpty()) + assert(subscriberData?.get("onItemCreatedCoroutineChannel")?.get("id") == 1) + subscriber.expectCompletion() + } + + @Test + fun `generated schema supports Kotlin coroutine channels with suspend function for the subscription query`() { + val newItem = Item(1, "item", Type.TYPE_1, UUID.randomUUID(), listOf()) + val closure = { + """ + subscription { + onItemCreatedCoroutineChannelAndSuspendFunction { + id + } + } + """ + } + + val result = gql.execute(ExecutionInput.newExecutionInput() + .query(closure.invoke()) + .context(OnItemCreatedContext(newItem)) + .variables(mapOf())) + + val data = result.getData() as Publisher + val subscriber = TestEnvironment().newManualSubscriber(data) + + val subscriberResult = subscriber.requestNextElement() as ExecutionResultImpl + val subscriberData = subscriberResult.getData() as Map>? + assert(result.errors.isEmpty()) + assert(subscriberData?.get("onItemCreatedCoroutineChannelAndSuspendFunction")?.get("id") == 1) + subscriber.expectCompletion() + } + + @Test + fun `generated schema supports arrays`() { + val data = assertNoGraphQlErrors(gql) { + """ + { + arrayItems { + name + } + } + """ + } + + assert((data["arrayItems"] as List<*>).filterIsInstance>().map { it["name"] } == listOf("item1", "item2")) + } + + @Test + fun `generated schema should re-throw original runtime exception when executing a resolver method`() { + val result = gql.execute(ExecutionInput.newExecutionInput().query(""" + { + throwsIllegalArgumentException + } + """ + )) + + assert(result.errors.size == 1) + assert((result.errors[0] as ExceptionWhileDataFetching).exception is IllegalArgumentException) + } +} diff --git a/src/test/kotlin/graphql/kickstart/tools/EnumDefaultValueTest.kt b/src/test/kotlin/graphql/kickstart/tools/EnumDefaultValueTest.kt new file mode 100644 index 00000000..11b62f3d --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/EnumDefaultValueTest.kt @@ -0,0 +1,55 @@ +package graphql.kickstart.tools + +import graphql.GraphQL +import graphql.execution.AsyncExecutionStrategy +import org.junit.Test + +class EnumDefaultValueTest { + + @Test + fun `enum value is not passed down to graphql-java`() { + val schema = SchemaParser.newParser() + .schemaString(""" + type Query { + test(input: MySortSpecifier): SortBy + } + input MySortSpecifier { + sortBy: SortBy = createdOn + value: Int = 10 + } + enum SortBy { + createdOn + updatedOn + } + """.trimIndent()) + .resolvers(object : GraphQLQueryResolver { + fun test(input: MySortSpecifier): SortBy? = input.sortBy + }) + .build() + .makeExecutableSchema() + + val ggl = GraphQL.newGraphQL(schema) + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() + + val data = assertNoGraphQlErrors(ggl, mapOf("input" to mapOf("value" to 1))) { + """ + query test(${'$'}input: MySortSpecifier) { + test(input: ${'$'}input) + } + """.trimIndent() + } + + assert(data["test"] == "createdOn") + } + + class MySortSpecifier { + var sortBy: SortBy? = null + var value: Int? = null + } + + enum class SortBy { + createdOn, + updatedOn + } +} diff --git a/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt b/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt new file mode 100644 index 00000000..4e48b596 --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt @@ -0,0 +1,71 @@ +package graphql.kickstart.tools + +import graphql.GraphQL +import graphql.execution.AsyncExecutionStrategy +import org.junit.Before +import org.junit.Test + +class EnumListParameterTest { + private lateinit var gql: GraphQL + + @Before + fun setup() { + val schema = SchemaParser.newParser().schemaString(""" + type Query { + countries(regions: [Region!]!): [Country!]! + } + + enum Region { + EUROPE + ASIA + } + + type Country { + code: String! + name: String! + regions: [Region!] + } + """.trimIndent()) + .resolvers(QueryResolver()) + .build() + .makeExecutableSchema() + gql = GraphQL.newGraphQL(schema) + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() + + } + + @Test + fun `query with parameter type list of enums should resolve correctly`() { + val data = assertNoGraphQlErrors(gql, mapOf("regions" to setOf("EUROPE", "ASIA"))) { + """ + query getCountries(${'$'}regions: [Region!]!) { + countries(regions: ${'$'}regions){ + code + name + regions + } + } + """ + } + + assert((data["countries"] as Collection<*>).isEmpty()) + } + + class QueryResolver : GraphQLQueryResolver { + fun getCountries(regions: Set): Set { + return setOf() + } + } + + class Country { + var code: String? = null + var name: String? = null + var regions: List? = null + } + + enum class Region { + EUROPE, + ASIA + } +} diff --git a/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt b/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt new file mode 100644 index 00000000..1ad1f26f --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt @@ -0,0 +1,126 @@ +package graphql.kickstart.tools + +import graphql.kickstart.tools.SchemaParserOptions.Companion.defaultOptions +import graphql.kickstart.tools.resolver.FieldResolverError +import graphql.kickstart.tools.resolver.FieldResolverScanner +import graphql.kickstart.tools.resolver.MethodFieldResolver +import graphql.kickstart.tools.resolver.PropertyFieldResolver +import graphql.language.FieldDefinition +import graphql.language.TypeName +import graphql.relay.Connection +import graphql.relay.DefaultConnection +import graphql.relay.DefaultPageInfo +import org.junit.Test + + +class FieldResolverScannerTest { + + private val options = defaultOptions() + private val scanner = FieldResolverScanner(options) + + @Test + fun `scanner finds fields on multiple root types`() { + val resolver = RootResolverInfo(listOf(RootQuery1(), RootQuery2()), options) + + val result1 = scanner.findFieldResolver(FieldDefinition("field1", TypeName("String")), resolver) + val result2 = scanner.findFieldResolver(FieldDefinition("field2", TypeName("String")), resolver) + + assert(result1.search.source != result2.search.source) + } + + @Test(expected = FieldResolverError::class) + fun `scanner throws exception when more than one resolver method is found`() { + val resolver = RootResolverInfo(listOf(RootQuery1(), DuplicateQuery()), options) + + scanner.findFieldResolver(FieldDefinition("field1", TypeName("String")), resolver) + } + + @Test(expected = FieldResolverError::class) + fun `scanner throws exception when no resolver methods are found`() { + val resolver = RootResolverInfo(listOf(), options) + + scanner.findFieldResolver(FieldDefinition("field1", TypeName("String")), resolver) + } + + @Test + fun `scanner finds properties when no method is found`() { + val resolver = RootResolverInfo(listOf(PropertyQuery()), options) + + val name = scanner.findFieldResolver(FieldDefinition("name", TypeName("String")), resolver) + val version = scanner.findFieldResolver(FieldDefinition("version", TypeName("Integer")), resolver) + + assert(name is PropertyFieldResolver) + assert(version is PropertyFieldResolver) + } + + @Test + fun `scanner finds generic return type`() { + val resolver = RootResolverInfo(listOf(GenericQuery()), options) + + val users = scanner.findFieldResolver(FieldDefinition("users", TypeName("UserConnection")), resolver) + + assert(users is MethodFieldResolver) + } + + @Test + fun `scanner prefers concrete resolver`() { + val resolver = DataClassResolverInfo(Kayak::class.java); + + val meta = scanner.findFieldResolver(FieldDefinition("information", TypeName("VehicleInformation")), resolver) + + assert(meta is MethodFieldResolver) + assert((meta as MethodFieldResolver).method.returnType == BoatInformation::class.java) + } + + @Test + fun `scanner finds field resolver method using camelCase for snake_cased field_name`() { + val resolver = RootResolverInfo(listOf(CamelCaseQuery1()), options) + + val meta = scanner.findFieldResolver(FieldDefinition("hull_type", TypeName("HullType")), resolver) + + assert(meta is MethodFieldResolver) + assert((meta as MethodFieldResolver).method.returnType == HullType::class.java) + } + + class RootQuery1 : GraphQLQueryResolver { + fun field1() {} + } + + class RootQuery2 : GraphQLQueryResolver { + fun field2() {} + } + + class DuplicateQuery : GraphQLQueryResolver { + fun field1() {} + } + + class CamelCaseQuery1 : GraphQLQueryResolver { + fun getHullType(): HullType = HullType() + } + + class HullType + + open class ParentPropertyQuery { + private var version: Int = 1 + } + + class PropertyQuery : ParentPropertyQuery(), GraphQLQueryResolver { + private var name: String = "name" + } + + class User {} + + class GenericQuery : GraphQLQueryResolver { + fun getUsers(): Connection { + return DefaultConnection(listOf(), DefaultPageInfo(null, null, false, false)) + } + } + + abstract class Boat : Vehicle { + override fun getInformation(): BoatInformation = this.getInformation() + } + + class BoatInformation : VehicleInformation + + class Kayak : Boat() +} diff --git a/src/test/kotlin/graphql/kickstart/tools/GenericResolverTest.kt b/src/test/kotlin/graphql/kickstart/tools/GenericResolverTest.kt new file mode 100644 index 00000000..0d05bd16 --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/GenericResolverTest.kt @@ -0,0 +1,66 @@ +package graphql.kickstart.tools + +import org.junit.Test + +class GenericResolverTest { + + @Test + fun `methods from generic resolvers are resolved`() { + SchemaParser.newParser().schemaString(""" + type Query { + bar: Bar + } + + type Bar { + value: String + } + """) + .resolvers(QueryResolver1(), BarResolver()) + .build() + .makeExecutableSchema() + + } + + class QueryResolver1 : GraphQLQueryResolver { + fun getBar(): Bar { + return Bar() + } + } + + class Bar + + abstract class FooResolver : GraphQLResolver { + fun getValue(foo: T): String = "value" + } + + class BarResolver : FooResolver(), GraphQLResolver + + @Test + fun `methods from generic inherited resolvers are resolved`() { + SchemaParser.newParser().schemaString(""" + type Query { + car: Car + } + type Car { + value: String + } + """) + .resolvers(QueryResolver2(), CarResolver()) + .build() + .makeExecutableSchema() + } + + + class QueryResolver2 : GraphQLQueryResolver { + fun getCar(): Car = Car() + } + + abstract class FooGraphQLResolver : GraphQLResolver { + fun getValue(foo: T): String = "value" + } + + class Car + + class CarResolver : FooGraphQLResolver() + +} diff --git a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt index 20966215..63824b04 100644 --- a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt @@ -3,9 +3,11 @@ package graphql.kickstart.tools import graphql.ExecutionResult import graphql.execution.* import graphql.execution.instrumentation.SimpleInstrumentation +import graphql.kickstart.tools.resolver.FieldResolverError import graphql.kickstart.tools.resolver.FieldResolverScanner import graphql.language.FieldDefinition import graphql.language.InputValueDefinition +import graphql.language.NonNullType import graphql.language.TypeName import graphql.schema.DataFetcher import graphql.schema.DataFetchingEnvironment @@ -44,13 +46,13 @@ class MethodFieldResolverDataFetcherTest { @ExperimentalCoroutinesApi val options = SchemaParserOptions.Builder() - .coroutineContext(dispatcher + job) - .build() + .coroutineContext(dispatcher + job) + .build() @Suppress("UNUSED_PARAMETER") suspend fun isActive(data: DataClass): Boolean { return coroutineContext[dispatcher.key] == dispatcher && - coroutineContext[Job] == job.children.first() + coroutineContext[Job] == job.children.first() } } @@ -102,6 +104,142 @@ class MethodFieldResolverDataFetcherTest { subscriber.expectErrorWithMessage(IllegalStateException::class.java, "Channel error") } + @Test(expected = FieldResolverError::class) + fun `data fetcher throws exception if resolver has too many arguments`() { + createFetcher("active", object : GraphQLQueryResolver { + fun active(arg1: Any, arg2: Any): Boolean = true + }) + } + + @Test(expected = FieldResolverError::class) + fun `data fetcher throws exception if resolver has too few arguments`() { + createFetcher("active", listOf(InputValueDefinition("doesNotExist", TypeName("Boolean"))), object : GraphQLQueryResolver { + fun active(): Boolean = true + }) + } + + @Test + fun `data fetcher prioritizes methods on the resolver`() { + val name = "Resolver Name" + val resolver = createFetcher("name", object : GraphQLResolver { + fun getName(dataClass: DataClass): String = name + }) + + assert(resolver.get(createEnvironment(DataClass())) == name) + } + + @Test + fun `data fetcher uses data class methods if no resolver method is given`() { + val resolver = createFetcher("name", object : GraphQLResolver {}) + + assert(resolver.get(createEnvironment(DataClass())) == DataClass().name) + } + + @Test + fun `data fetcher prioritizes methods without a prefix`() { + val name = "correct name" + val resolver = createFetcher("name", object : GraphQLResolver { + fun getName(dataClass: DataClass): String = "in$name" + fun name(dataClass: DataClass): String = name + }) + + assert(resolver.get(createEnvironment(DataClass())) == name) + } + + @Test + fun `data fetcher uses 'is' prefix for booleans (primitive type)`() { + val resolver = createFetcher("active", object : GraphQLResolver { + fun isActive(dataClass: DataClass): Boolean = true + fun getActive(dataClass: DataClass): Boolean = true + }) + + assert(resolver.get(createEnvironment(DataClass())) == true) + } + + @Test + fun `data fetcher uses 'is' prefix for Booleans (Object type)`() { + val resolver = createFetcher("active", object : GraphQLResolver { + fun isActive(dataClass: DataClass): Boolean? = null + fun getActive(dataClass: DataClass): Boolean? = null + }) + + assert(resolver.get(createEnvironment(DataClass())) == null) + } + + @Test + fun `data fetcher passes environment if method has extra argument`() { + val resolver = createFetcher("active", object : GraphQLResolver { + fun isActive(dataClass: DataClass, env: DataFetchingEnvironment): Boolean = env is DataFetchingEnvironment + }) + + assert(resolver.get(createEnvironment(DataClass())) == true) + } + + @Test + fun `data fetcher passes environment if method has extra argument even if context is specified`() { + val options = SchemaParserOptions.newOptions().contextClass(ContextClass::class).build() + val resolver = createFetcher("active", options = options, resolver = object : GraphQLResolver { + fun isActive(dataClass: DataClass, env: DataFetchingEnvironment): Boolean = env is DataFetchingEnvironment + }) + + assert(resolver.get(createEnvironment(DataClass(), context = ContextClass())) == true) + } + + @Test + fun `data fetcher passes context if method has extra argument and context is specified`() { + val context = ContextClass() + val options = SchemaParserOptions.newOptions().contextClass(ContextClass::class).build() + val resolver = createFetcher("active", options = options, resolver = object : GraphQLResolver { + fun isActive(dataClass: DataClass, ctx: ContextClass): Boolean { + return ctx == context + } + }) + + assert(resolver.get(createEnvironment(DataClass(), context = context)) == true) + } + + @Test + fun `data fetcher marshalls input object if required`() { + val name = "correct name" + val resolver = createFetcher("active", listOf(InputValueDefinition("input", TypeName("InputClass"))), object : GraphQLQueryResolver { + fun active(input: InputClass): Boolean = + input is InputClass && input.name == name + }) + + assert(resolver.get(createEnvironment(arguments = mapOf("input" to mapOf("name" to name)))) == true) + } + + @Test + fun `data fetcher doesn't marshall input object if not required`() { + val name = "correct name" + val resolver = createFetcher("active", listOf(InputValueDefinition("input", TypeName("Map"))), object : GraphQLQueryResolver { + fun active(input: Map<*, *>): Boolean = + input is Map<*, *> && input["name"] == name + }) + + assert(resolver.get(createEnvironment(arguments = mapOf("input" to mapOf("name" to name)))) == true) + } + + @Test + fun `data fetcher returns null if nullable argument is passed null`() { + val resolver = createFetcher("echo", listOf(InputValueDefinition("message", TypeName("String"))), object : GraphQLQueryResolver { + fun echo(message: String?): String? = + message + }) + + assert(resolver.get(createEnvironment()) == null) + } + + @Test(expected = ResolverError::class) + fun `data fetcher throws exception if non-null argument is passed null`() { + val resolver = createFetcher("echo", listOf(InputValueDefinition("message", NonNullType(TypeName("String")))), object : GraphQLQueryResolver { + fun echo(message: String): String = + message + }) + + resolver.get(createEnvironment()) + } + class OnDataNameChanged : GraphQLResolver { val channel = Channel(10) @@ -117,10 +255,18 @@ class MethodFieldResolverDataFetcherTest { } private fun createFetcher( - methodName: String, - resolver: GraphQLResolver<*>, - arguments: List = emptyList(), - options: SchemaParserOptions = SchemaParserOptions.defaultOptions() + methodName: String, + arguments: List = emptyList(), + resolver: GraphQLResolver<*> + ): DataFetcher<*> { + return createFetcher(methodName, resolver, arguments) + } + + private fun createFetcher( + methodName: String, + resolver: GraphQLResolver<*>, + arguments: List = emptyList(), + options: SchemaParserOptions = SchemaParserOptions.defaultOptions() ): DataFetcher<*> { val field = FieldDefinition.newFieldDefinition() .name(methodName) @@ -136,12 +282,12 @@ class MethodFieldResolverDataFetcherTest { return FieldResolverScanner(options).findFieldResolver(field, resolverInfo).createDataFetcher() } - private fun createEnvironment(source: Any, arguments: Map = emptyMap(), context: Any? = null): DataFetchingEnvironment { + private fun createEnvironment(source: Any = Object(), arguments: Map = emptyMap(), context: Any? = null): DataFetchingEnvironment { return DataFetchingEnvironmentImpl.newDataFetchingEnvironment(buildExecutionContext()) - .source(source) - .arguments(arguments) - .context(context) - .build() + .source(source) + .arguments(arguments) + .context(context) + .build() } private fun buildExecutionContext(): ExecutionContext { @@ -152,13 +298,19 @@ class MethodFieldResolverDataFetcherTest { } val executionId = ExecutionId.from("executionId123") return ExecutionContextBuilder.newExecutionContextBuilder() - .instrumentation(SimpleInstrumentation.INSTANCE) - .executionId(executionId) - .queryStrategy(executionStrategy) - .mutationStrategy(executionStrategy) - .subscriptionStrategy(executionStrategy) - .build() + .instrumentation(SimpleInstrumentation.INSTANCE) + .executionId(executionId) + .queryStrategy(executionStrategy) + .mutationStrategy(executionStrategy) + .subscriptionStrategy(executionStrategy) + .build() } data class DataClass(val name: String = "TestName") + + class InputClass { + var name: String? = null + } + + class ContextClass } diff --git a/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt b/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt new file mode 100644 index 00000000..0aa6bca0 --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt @@ -0,0 +1,71 @@ +package graphql.kickstart.tools + +import graphql.GraphQL +import graphql.execution.AsyncExecutionStrategy +import org.junit.Before +import org.junit.Test + +class MultiResolverTest { + + private lateinit var gql: GraphQL + + @Before + fun setup() { + val schema = SchemaParser.newParser().schemaString(""" + type Query { + person: Person + } + + type Person { + name: String! + friends(friendName: String!): [Friend!]! + } + + type Friend { + name: String! + } + """.trimIndent()) + .resolvers(QueryWithPersonResolver(), PersonFriendResolver(), PersonNameResolver()) + .build() + .makeExecutableSchema() + gql = GraphQL.newGraphQL(schema) + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() + + } + + @Test + fun `multiple resolvers for one data class should resolve methods with arguments`() { + val data = assertNoGraphQlErrors(gql, mapOf("friendName" to "name")) { + """ + query friendOfPerson(${'$'}friendName: String!) { + person { + friends(friendName: ${'$'}friendName) { + name + } + } + } + """ + } + + assert(data["person"] != null) + } + + class QueryWithPersonResolver : GraphQLQueryResolver { + fun getPerson(): Person = Person() + } + + class Person + + class Friend { + var name: String? = null + } + + class PersonFriendResolver : GraphQLResolver { + fun friends(person: Person, friendName: String): List = listOf() + } + + class PersonNameResolver : GraphQLResolver { + fun name(person: Person): String = "name" + } +} diff --git a/src/test/kotlin/graphql/kickstart/tools/NestedInputTypesTest.kt b/src/test/kotlin/graphql/kickstart/tools/NestedInputTypesTest.kt new file mode 100644 index 00000000..e7f7279f --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/NestedInputTypesTest.kt @@ -0,0 +1,125 @@ +package graphql.kickstart.tools + +import graphql.GraphQL +import graphql.execution.AsyncExecutionStrategy +import org.junit.Test + +class NestedInputTypesTest { + + @Test + fun `nested input types are parsed`() { + val schema = SchemaParser.newParser().schemaString(""" + type Query { + materials(filter: MaterialFilter): [Material!]! + } + + input MaterialFilter { + title: String + requestFilter: RequestFilter + } + + input RequestFilter { + and: [RequestFilter!] + or: [RequestFilter!] + discountTypeFilter: DiscountTypeFilter + } + + input DiscountTypeFilter { + name: String + } + + type Material { + id: ID! + } + """) + .resolvers(QueryResolver()) + .build() + .makeExecutableSchema() + val gql = GraphQL.newGraphQL(schema) + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() + val data = assertNoGraphQlErrors(gql, mapOf("filter" to mapOf("title" to "title", "requestFilter" to mapOf("discountTypeFilter" to mapOf("name" to "discount"))))) { + """ + query materials(${'$'}filter: MaterialFilter!) { + materials(filter: ${'$'}filter) { + id + } + } + """ + } + + assert((data["materials"] as Collection<*>).isEmpty()) + } + + @Test + fun `nested input in extensions are parsed`() { + val schema = SchemaParser.newParser().schemaString(""" + type Query { + materials(filter: MaterialFilter): [Material!]! + } + + input MaterialFilter { + title: String + } + + extend input MaterialFilter { + requestFilter: RequestFilter + } + + input RequestFilter { + and: [RequestFilter!] + or: [RequestFilter!] + discountTypeFilter: DiscountTypeFilter + } + + input DiscountTypeFilter { + name: String + } + + type Material { + id: ID! + } + """) + .resolvers(QueryResolver()) + .build() + .makeExecutableSchema() + val gql = GraphQL.newGraphQL(schema) + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() + val data = assertNoGraphQlErrors(gql, mapOf("filter" to mapOf("title" to "title", "requestFilter" to mapOf("discountTypeFilter" to mapOf("name" to "discount"))))) { + """ + query materials(${'$'}filter: MaterialFilter!) { + materials(filter: ${'$'}filter) { + id + } + } + """ + } + + assert((data["materials"] as Collection<*>).isEmpty()) + } + + class QueryResolver : GraphQLQueryResolver { + fun materials(filter: MaterialFilter): List = listOf() + } + + class Material { + var id: Long? = null + } + + class MaterialFilter { + var title: String? = null + var requestFilter: RequestFilter? = null + } + + class RequestFilter { + var and: List? = null + var or: List? = null + var discountTypeFilter: DiscountTypeFilter? = null + } + + class DiscountTypeFilter { + var name: String? = null + } + +} diff --git a/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt b/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt new file mode 100644 index 00000000..dd319bb6 --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt @@ -0,0 +1,68 @@ +package graphql.kickstart.tools + +import graphql.GraphQL +import graphql.execution.AsyncExecutionStrategy +import org.junit.Before +import org.junit.Test + +class ParameterizedGetterTest { + + private lateinit var gql: GraphQL + + @Before + fun setup() { + val schema = SchemaParser.newParser().schemaString(""" + type Query { + human: Human + } + + type Human { + bestFriends: [Character!]! + allFriends(limit: Int!): [Character!]! + } + + type Character { + name: String! + } + """.trimIndent()) + .resolvers(QueryResolver(), HumanResolver()) + .build() + .makeExecutableSchema() + gql = GraphQL.newGraphQL(schema) + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() + } + + @Test + fun `parameterized query is resolved on data type instead of on its resolver`() { + val data = assertNoGraphQlErrors(gql, mapOf("limit" to 10)) { + """ + query allFriends(${'$'}limit: Int!) { + human { + allFriends(limit: ${'$'}limit) { + name + } + } + } + """ + } + + assert(data["human"] != null) + } + + class QueryResolver : GraphQLQueryResolver { + fun human(): Human = Human() + } + + class Human { + fun allFriends(limit: Int): List = listOf() + } + + class HumanResolver : GraphQLResolver { + fun bestFriends(human: Human): List = listOf() + } + + class Character { + val name: String? = null + } +} diff --git a/src/test/kotlin/graphql/kickstart/tools/ReactiveTest.kt b/src/test/kotlin/graphql/kickstart/tools/ReactiveTest.kt index f99fde40..35b53672 100644 --- a/src/test/kotlin/graphql/kickstart/tools/ReactiveTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/ReactiveTest.kt @@ -4,7 +4,6 @@ import graphql.GraphQL import graphql.execution.AsyncExecutionStrategy import graphql.kickstart.tools.SchemaParser.Companion.newParser import graphql.kickstart.tools.SchemaParserOptions.Companion.newOptions -import groovy.lang.Closure import org.junit.Test import java.util.* import java.util.concurrent.CompletableFuture @@ -24,20 +23,18 @@ class ReactiveTest { .build() val schema = newParser().file("Reactive.graphqls") - .resolvers(Query()) - .options(options) - .build() - .makeExecutableSchema() + .resolvers(Query()) + .options(options) + .build() + .makeExecutableSchema() val gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() - Utils.assertNoGraphQlErrors(gql, HashMap(), Any(), object : Closure(null) { - override fun call(): String { - return "query { organization(organizationId: 1) { user { id } } }" - } - }) + assertNoGraphQlErrors(gql) { + "query { organization(organizationId: 1) { user { id } } }" + } } private class Query : GraphQLQueryResolver { diff --git a/src/test/kotlin/graphql/kickstart/tools/RelayConnectionTest.kt b/src/test/kotlin/graphql/kickstart/tools/RelayConnectionTest.kt index 19a93b19..6f7d9a04 100644 --- a/src/test/kotlin/graphql/kickstart/tools/RelayConnectionTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/RelayConnectionTest.kt @@ -5,7 +5,6 @@ import graphql.execution.AsyncExecutionStrategy import graphql.relay.Connection import graphql.relay.SimpleListConnection import graphql.schema.DataFetchingEnvironment -import groovy.lang.Closure import org.junit.Assert import org.junit.Test @@ -100,19 +99,18 @@ class RelayConnectionTest { @Test fun `should compile relay schema when using @connection directive`() { val schema = SchemaParser.newParser() - .file("RelayConnection.graphqls") - .resolvers(QueryResolver()) - .dictionary(User::class.java) - .build() - .makeExecutableSchema() + .file("RelayConnection.graphqls") + .resolvers(QueryResolver()) + .dictionary(User::class.java) + .build() + .makeExecutableSchema() val gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() - Utils.assertNoGraphQlErrors(gql, emptyMap(), object : Closure(null) { - override fun call(): String { - return """ + assertNoGraphQlErrors(gql) { + """ query { users { edges { @@ -130,9 +128,8 @@ class RelayConnectionTest { } } } - """ - } - }) + """ + } } private class QueryResolver : GraphQLQueryResolver { diff --git a/src/test/kotlin/graphql/kickstart/tools/ResolverMethodsTest.java b/src/test/kotlin/graphql/kickstart/tools/ResolverMethodsTest.java index dc735681..5153ee99 100644 --- a/src/test/kotlin/graphql/kickstart/tools/ResolverMethodsTest.java +++ b/src/test/kotlin/graphql/kickstart/tools/ResolverMethodsTest.java @@ -12,7 +12,7 @@ import static org.junit.Assert.assertTrue; public class ResolverMethodsTest { - // Note: don't convert this code to Kotlin or Groovy, since it's quite important that the + // Note: don't convert this code to Kotlin, since it's quite important that the // resolver method is defined with an argument of primitive type, like 'boolean', not 'Boolean': // String testOmittedBoolean(boolean value1, Boolean value2) @Test diff --git a/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt b/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt new file mode 100644 index 00000000..10ebb436 --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt @@ -0,0 +1,503 @@ +package graphql.kickstart.tools + +import graphql.schema.* +import org.junit.Test +import java.util.concurrent.CompletableFuture + +class SchemaClassScannerTest { + + @Test + fun `scanner handles futures and immediate return types`() { + SchemaParser.newParser() + .resolvers(FutureImmediateQuery()) + .schemaString(""" + type Query { + future: Int! + immediate: Int! + } + """) + .build() + } + + private class FutureImmediateQuery : GraphQLQueryResolver { + fun future(): CompletableFuture = + CompletableFuture.completedFuture(1) + + fun immediate(): Int = 1 + } + + @Test + fun `scanner handles primitive and boxed return types`() { + SchemaParser.newParser() + .resolvers(PrimitiveBoxedQuery()) + .schemaString(""" + type Query { + primitive: Int! + boxed: Int! + } + """) + .build() + } + + private class PrimitiveBoxedQuery : GraphQLQueryResolver { + fun primitive(): Int = 1 + + fun boxed(): Int? = 1 + } + + @Test + fun `scanner handles different scalars with same java class`() { + SchemaParser.newParser() + .resolvers(ScalarDuplicateQuery()) + .schemaString(""" + type Query { + string: String! + id: ID! + } + """) + .build() + } + + private class ScalarDuplicateQuery : GraphQLQueryResolver { + fun string(): String = "" + fun id(): String = "" + } + + @Test + fun `scanner handles interfaces referenced by objects that aren't explicitly used`() { + val schema = SchemaParser.newParser() + .resolvers(InterfaceMissingQuery()) + .schemaString(""" + interface Interface { + id: ID! + } + + type Query implements Interface { + id: ID! + } + """) + .build() + .makeExecutableSchema() + + assert(schema.additionalTypes.find { it is GraphQLInterfaceType } != null) + } + + private class InterfaceMissingQuery : GraphQLQueryResolver { + fun id(): String = "" + } + + @Test + fun `scanner handles input types that reference other input types`() { + val schema = SchemaParser.newParser() + .resolvers(MultipleInputTypeQuery()) + .schemaString(""" + input FirstInput { + id: String! + second: SecondInput! + third: ThirdInput! + } + input SecondInput { + id: String! + } + input ThirdInput { + id: String! + } + + type Query { + test(input: FirstInput): String! + } + """) + .build() + .makeExecutableSchema() + + assert(schema.additionalTypes.count { it is GraphQLInputType } == 3) + } + + private class MultipleInputTypeQuery : GraphQLQueryResolver { + + fun test(input: FirstInput): String = "" + + class FirstInput { + var id: String? = null + + fun second(): SecondInput = SecondInput() + var third: ThirdInput? = null + } + + class SecondInput { + var id: String? = null + } + + class ThirdInput { + var id: String? = null + } + } + + @Test + fun `scanner handles input types extensions`() { + val schema = SchemaParser.newParser() + .schemaString(""" + type Query { test: Boolean } + + type Mutation { + save(input: UserInput!): Boolean + } + + input UserInput { + name: String + } + + extend input UserInput { + password: String + } + """) + .resolvers( + object : GraphQLMutationResolver { + fun save(map: Map<*, *>): Boolean = true + }, + object : GraphQLQueryResolver { + fun test(): Boolean = true + } + ) + .build() + .makeExecutableSchema() + assert(schema.additionalTypes + .filterIsInstance() + .flatMap { it.extensionDefinitions } + .count() == 1 + ) + } + + @Test + fun `scanner allows multiple return types for custom scalars`() { + val schema = SchemaParser.newParser() + .resolvers(ScalarsWithMultipleTypes()) + .scalars(GraphQLScalarType.newScalar() + .name("UUID") + .description("Test scalars with duplicate types") + .coercing(object : Coercing { + override fun serialize(dataFetcherResult: Any?): Any? = null + override fun parseValue(input: Any?): Any? = null + override fun parseLiteral(input: Any?): Any? = null + }).build()) + .schemaString(""" + scalar UUID + + type Query { + first: UUID + second: UUID + } + """) + .build() + .makeExecutableSchema() + + assert(schema.typeMap.containsKey("UUID")) + } + + class ScalarsWithMultipleTypes : GraphQLQueryResolver { + fun first(): Int? = null + fun second(): String? = null + } + + @Test + fun `scanner handles multiple interfaces that are not used as field types`() { + val schema = SchemaParser.newParser() + .resolvers(MultipleInterfaces()) + .schemaString(""" + type Query { + query1: NamedResourceImpl + query2: VersionedResourceImpl + } + + interface NamedResource { + name: String! + } + + interface VersionedResource { + version: Int! + } + + type NamedResourceImpl implements NamedResource { + name: String! + } + + type VersionedResourceImpl implements VersionedResource { + version: Int! + } + """) + .build() + .makeExecutableSchema() + + assert(schema.additionalTypes.count { it is GraphQLInterfaceType } == 2) + } + + class MultipleInterfaces : GraphQLQueryResolver { + fun query1(): NamedResourceImpl? = null + fun query2(): VersionedResourceImpl? = null + + class NamedResourceImpl : NamedResource { + override fun name(): String? = null + } + + class VersionedResourceImpl : VersionedResource { + override fun version(): Int? = null + } + } + + interface NamedResource { + fun name(): String? + } + + interface VersionedResource { + fun version(): Int? + } + + @Test + fun `scanner handles interface implementation that is not used as field type`() { + val schema = SchemaParser.newParser() + // uncommenting the line below makes the test succeed + .dictionary(InterfaceImplementation.NamedResourceImpl::class) + .resolvers(InterfaceImplementation()) + .schemaString(""" + type Query { + query1: NamedResource + } + + interface NamedResource { + name: String! + } + + type NamedResourceImpl implements NamedResource { + name: String! + } + """) + .build() + .makeExecutableSchema() + + assert(schema.additionalTypes.count { it is GraphQLInterfaceType } == 1) + } + + class InterfaceImplementation : GraphQLQueryResolver { + fun query1(): NamedResource? = null + + fun query2(): NamedResourceImpl? = null + + class NamedResourceImpl : NamedResource { + override fun name(): String? = null + } + } + + @Test + fun `scanner handles custom scalars when matching input types`() { + val customMap = GraphQLScalarType.newScalar() + .name("customMap") + .coercing(object : Coercing, Map> { + override fun serialize(dataFetcherResult: Any?): Map = mapOf() + override fun parseValue(input: Any?): Map = mapOf() + override fun parseLiteral(input: Any?): Map = mapOf() + }).build() + + val schema = SchemaParser.newParser() + .resolvers(object : GraphQLQueryResolver { + fun hasRawScalar(rawScalar: Map): Boolean = true + fun hasMapField(mapField: HasMapField): Boolean = true + }) + .scalars(customMap) + .schemaString(""" + type Query { + hasRawScalar(customMap: customMap): Boolean + hasMapField(mapField: HasMapField): Boolean + } + + input HasMapField { + map: customMap + } + + scalar customMap + """) + .build() + .makeExecutableSchema() + + assert(schema.typeMap.containsKey("customMap")) + } + + class HasMapField { + var map: Map? = null + } + + @Test + fun `scanner allows class to be used for object type and input object type`() { + val schema = SchemaParser.newParser() + .resolvers(object : GraphQLQueryResolver { + fun test(pojo: Pojo): Pojo = pojo + }) + .schemaString(""" + type Query { + test(inPojo: InPojo): OutPojo + } + + input InPojo { + name: String + } + + type OutPojo { + name: String + } + """) + .build() + .makeExecutableSchema() + + assert(schema.additionalTypes.count() == 2) + } + + class Pojo { + var name: String? = null + } + + @Test + fun `scanner should handle nested types in input types`() { + val schema = SchemaParser.newParser() + .schemaString(""" + schema { + query: Query + } + + type Query { + animal: Animal + } + + interface Animal { + type: ComplexType + } + + type Dog implements Animal { + type: ComplexType + } + + type ComplexType { + id: String + } + """) + .resolvers(NestedInterfaceTypeQuery()) + .dictionary(NestedInterfaceTypeQuery.Dog::class) + .build() + .makeExecutableSchema() + + assert(schema.additionalTypes.count() == 3) + } + + class NestedInterfaceTypeQuery : GraphQLQueryResolver { + fun animal(): Animal? = null + + class Dog : Animal { + override fun type(): ComplexType? = null + } + + class ComplexType { + var id: String? = null + } + } + + @Test + fun `scanner should handle unused types when option is true`() { + val schema = SchemaParser.newParser() + .schemaString(""" + # Let's say this is the Products service from Apollo Federation Introduction + + type Query { + allProducts: [Product] + } + + type Product { + name: String + } + + # these directives are defined in the Apollo Federation Specification: + # https://www.apollographql.com/docs/apollo-server/federation/federation-spec/ + type User @key(fields: "id") @extends { + id: ID! @external + recentPurchasedProducts: [Product] + address: Address + } + + type Address { + street: String + } + """) + .resolvers(object : GraphQLQueryResolver { + fun allProducts(): List? = null + }) + .options(SchemaParserOptions.newOptions().includeUnusedTypes(true).build()) + .dictionary(User::class) + .build() + .makeExecutableSchema() + + assert(schema.additionalTypes.filterIsInstance().find { it.name == "User" } != null) + assert(schema.additionalTypes.filterIsInstance().find { it.name == "Address" } != null) + } + + class Product { + var name: String? = null + } + + class User { + var id: String? = null + var recentPurchasedProducts: List? = null + var address: Address? = null + } + + class Address { + var street: String? = null + } + + @Test + fun `scanner should handle unused types with interfaces when option is true`() { + val schema = SchemaParser.newParser() + .schemaString(""" + type Query { + whatever: Whatever + } + + type Whatever { + value: String + } + + type Unused { + someInterface: SomeInterface + } + + interface SomeInterface { + value: String + } + + type Implementation implements SomeInterface { + value: String + } + """) + .resolvers(object : GraphQLQueryResolver { + fun whatever(): Whatever? = null + }) + .options(SchemaParserOptions.newOptions().includeUnusedTypes(true).build()) + .dictionary(Unused::class, Implementation::class) + .build() + .makeExecutableSchema() + + assert(schema.additionalTypes.filterIsInstance().find { it.name == "Unused" } != null) + assert(schema.additionalTypes.filterIsInstance().find { it.name == "SomeInterface" } != null) + assert(schema.additionalTypes.filterIsInstance().find { it.name == "Implementation" } != null) + } + + class Whatever { + var value: String? = null + } + + class Unused { + var someInterface: SomeInterface? = null + } + + class Implementation : SomeInterface { + override fun getValue(): String? { + return null + } + } +} diff --git a/src/test/kotlin/graphql/kickstart/tools/SchemaParserBuilderTest.kt b/src/test/kotlin/graphql/kickstart/tools/SchemaParserBuilderTest.kt new file mode 100644 index 00000000..f9b1a6c7 --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/SchemaParserBuilderTest.kt @@ -0,0 +1,34 @@ +package graphql.kickstart.tools + +import graphql.kickstart.tools.SchemaParser.Companion.newParser +import graphql.parser.InvalidSyntaxException +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class SchemaParserBuilderTest(private val schema: String, private val error: String) { + + @Test + fun `parser errors should be returned in full`() { + try { + newParser() + .schemaString(schema) + .build() + } catch (e: InvalidSyntaxException) { + Assert.assertTrue(e.toString().contains(error)) + } + } + + companion object { + @Parameterized.Parameters + @JvmStatic + fun data(): Collection> { + return listOf( + arrayOf("invalid", "offending token 'invalid' at line 1 column 1"), + arrayOf("type Query {\ninvalid!\n}", "offending token '!' at line 2 column 8") + ) + } + } +} diff --git a/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt b/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt new file mode 100644 index 00000000..7569f399 --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt @@ -0,0 +1,425 @@ +package graphql.kickstart.tools + +import graphql.kickstart.tools.resolver.FieldResolverError +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.ExpectedException +import org.springframework.aop.framework.ProxyFactory +import java.io.FileNotFoundException +import java.util.concurrent.Future + + +class SchemaParserTest { + private lateinit var builder: SchemaParserBuilder + + @Rule + @JvmField + var expectedEx = ExpectedException.none() + + @Before + fun setup() { + builder = SchemaParser.newParser() + .schemaString(""" + type Query { + get(int: Int!): Int! + } + """) + } + + @Test(expected = FileNotFoundException::class) + fun `builder throws FileNotFound exception when file is missing`() { + builder.file("/404").build() + } + + @Test + fun `builder doesn't throw FileNotFound exception when file is present`() { + SchemaParser.newParser().file("Test.graphqls") + .resolvers(object : GraphQLQueryResolver { + fun getId(): String = "1" + }) + .build() + } + + @Test(expected = SchemaClassScannerError::class) + fun `parser throws SchemaError when Query resolver is missing`() { + builder.build().makeExecutableSchema() + } + + @Test(expected = FieldResolverError::class) + fun `parser throws ResolverError when Query resolver is given without correct method`() { + SchemaParser.newParser() + .schemaString(""" + type Query { + get(int: Int!): Int! + } + """) + .resolvers(object : GraphQLQueryResolver {}) + .build() + .makeExecutableSchema() + } + + @Test + fun `parser should parse correctly when Query resolver is given`() { + SchemaParser.newParser() + .schemaString(""" + type Query { + get(int: Int!): Int! + } + """) + .resolvers(object : GraphQLQueryResolver { + fun get(i: Int): Int = i + }) + .build() + .makeExecutableSchema() + } + + @Test + fun `parser should parse correctly when multiple query resolvers are given`() { + SchemaParser.newParser() + .schemaString(""" + type Obj { + name: String + } + + type AnotherObj { + key: String + } + + type Query { + obj: Obj + anotherObj: AnotherObj + } + """) + .resolvers(object : GraphQLQueryResolver { + fun getObj(): Obj = Obj() + }, object : GraphQLQueryResolver { + fun getAnotherObj(): AnotherObj = AnotherObj() + }) + .build() + .makeExecutableSchema() + } + + @Test + fun `parser should parse correctly when multiple resolvers for the same data type are given`() { + SchemaParser.newParser() + .schemaString(""" + type RootObj { + obj: Obj + anotherObj: AnotherObj + } + + type Obj { + name: String + } + + type AnotherObj { + key: String + } + + type Query { + rootObj: RootObj + } + """) + .resolvers(object : GraphQLQueryResolver { + fun getRootObj(): RootObj { + return RootObj() + } + }, object : GraphQLResolver { + fun getObj(rootObj: RootObj): Obj { + return Obj() + } + }, object : GraphQLResolver { + fun getAnotherObj(rootObj: RootObj): AnotherObj { + return AnotherObj() + } + }) + .build() + .makeExecutableSchema() + } + + @Test + fun `parser should allow setting custom generic wrappers`() { + SchemaParser.newParser() + .schemaString(""" + type Query { + one: Object! + two: Object! + } + + type Object { + name: String! + } + """) + .resolvers(object : GraphQLQueryResolver { + fun one(): CustomGenericWrapper? = null + fun two(): Obj? = null + }) + .options(SchemaParserOptions.newOptions().genericWrappers(SchemaParserOptions.GenericWrapper(CustomGenericWrapper::class, 1)).build()) + .build() + .makeExecutableSchema() + } + + @Test(expected = SchemaClassScannerError::class) + fun `parser should allow turning off default generic wrappers`() { + SchemaParser.newParser() + .schemaString(""" + type Query { + one: Object! + two: Object! + } + + type Object { + toString: String! + } + """) + .resolvers(object : GraphQLQueryResolver { + fun one(): Future? = null + fun two(): Obj? = null + }) + .options(SchemaParserOptions.newOptions().useDefaultGenericWrappers(false).build()) + .build() + .makeExecutableSchema() + } + + @Test + fun `parser should throw descriptive exception when object is used as input type incorrectly`() { + expectedEx.expect(SchemaError::class.java) + expectedEx.expectMessage("Was a type only permitted for object types incorrectly used as an input type, or vice-versa") + + SchemaParser.newParser() + .schemaString(""" + type Query { + name(filter: Filter): [String] + } + + type Filter { + filter: String + } + """) + .resolvers(object : GraphQLQueryResolver { + fun name(filter: Filter): List? = null + }) + .build() + .makeExecutableSchema() + + throw AssertionError("should not be called") + } + + @Test + fun `parser handles spring AOP proxied resolvers by default`() { + val resolver = ProxyFactory(ProxiedResolver()).proxy as GraphQLQueryResolver + + SchemaParser.newParser() + .schemaString(""" + type Query { + test: [String] + } + """) + .resolvers(resolver) + .build() + } + + @Test + fun `parser handles enums with overridden toString method`() { + SchemaParser.newParser() + .schemaString(""" + enum CustomEnum { + FOO + } + + type Query { + customEnum: CustomEnum + } + """) + .resolvers(object : GraphQLQueryResolver { + fun customEnum(): CustomEnum? = null + }) + .build() + .makeExecutableSchema() + } + + @Test + fun `parser should include source location for field definition`() { + val schema = SchemaParser.newParser() + .schemaString(""" + |type Query { + | id: ID! + |} + """.trimMargin()) + .resolvers(QueryWithIdResolver()) + .build() + .makeExecutableSchema() + + val sourceLocation = schema.getObjectType("Query") + .getFieldDefinition("id") + .definition.sourceLocation + assert(sourceLocation != null) + assert(sourceLocation.line == 2) + assert(sourceLocation.column == 5) + assert(sourceLocation.sourceName == null) + } + + @Test + fun `parser should include source location for field definition when loaded from single classpath file`() { + val schema = SchemaParser.newParser() + .file("Test.graphqls") + .resolvers(QueryWithIdResolver()) + .build() + .makeExecutableSchema() + + val sourceLocation = schema.getObjectType("Query") + .getFieldDefinition("id") + .definition.sourceLocation + assert(sourceLocation != null) + assert(sourceLocation.line == 2) + assert(sourceLocation.column == 3) + assert(sourceLocation.sourceName == "Test.graphqls") + } + + @Test + fun `support enum types if only used as input type`() { + SchemaParser.newParser().schemaString(""" + type Query { test: Boolean } + + type Mutation { + save(input: SaveInput!): Boolean + } + + input SaveInput { + type: EnumType! + } + + enum EnumType { + TEST + } + """.trimIndent()) + .resolvers(object : GraphQLMutationResolver { + fun save(input: SaveInput): Boolean = false + inner class SaveInput { + var type: EnumType? = null; + } + + }, object : GraphQLQueryResolver { + fun test(): Boolean = false + }) + .dictionary(EnumType::class) + .build() + .makeExecutableSchema() + } + + @Test + fun `support enum types if only used in input Map`() { + SchemaParser.newParser().schemaString(""" + type Query { test: Boolean } + + type Mutation { + save(input: SaveInput!): Boolean + } + + input SaveInput { + age: Int + type: EnumType! + } + + enum EnumType { + TEST + } + """.trimIndent()) + .resolvers(object : GraphQLMutationResolver { + fun save(input: Map<*, *>): Boolean = false + }, object : GraphQLQueryResolver { + fun test(): Boolean = false + }) + .dictionary(EnumType::class) + .build() + .makeExecutableSchema() + } + + @Test + fun `allow circular relations in input objects`() { + SchemaParser.newParser().schemaString(""" + input A { + id: ID! + b: B + } + input B { + id: ID! + a: A + } + input C { + id: ID! + c: C + } + type Query { test: Boolean } + type Mutation { + test(input: A!): Boolean + testC(input: C!): Boolean + } + """.trimIndent()) + .resolvers(object : GraphQLMutationResolver { + inner class A { + var id: String? = null + var b: B? = null + } + + inner class B { + var id: String? = null + var a: A? = null + } + + inner class C { + var id: String? = null + var c: C? = null + } + + fun test(a: A): Boolean { + return true + } + + fun testC(c: C): Boolean { + return true + } + }, object : GraphQLQueryResolver { + fun test(): Boolean = false + }) + .build() + .makeExecutableSchema() + } + + enum class EnumType { + TEST + } + + class QueryWithIdResolver : GraphQLQueryResolver { + fun getId(): String? = null + } + + class Filter { + fun filter(): String? = null + } + + class CustomGenericWrapper + + class Obj { + fun name() = null + } + + class AnotherObj { + fun key() = null + } + + class RootObj + + class ProxiedResolver : GraphQLQueryResolver { + fun test(): List = listOf() + } + + enum class CustomEnum { + FOO { + override fun toString(): String { + return "Bar" + } + } + } +} diff --git a/src/test/kotlin/graphql/kickstart/tools/SuperclassResolverTest.kt b/src/test/kotlin/graphql/kickstart/tools/SuperclassResolverTest.kt new file mode 100644 index 00000000..64f238d7 --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/SuperclassResolverTest.kt @@ -0,0 +1,47 @@ +package graphql.kickstart.tools + +import org.junit.Test + +class SuperclassResolverTest { + + @Test + fun `methods from generic resolvers are resolved`() { + SchemaParser.newParser().schemaString(""" + type Query { + bar: Bar! + } + + type Bar implements Foo{ + value: String + getValueWithSeveralParameters(arg1: Boolean!, arg2: String): String! + } + + interface Foo { + getValueWithSeveralParameters(arg1: Boolean!, arg2: String): String! + } + """) + .resolvers(QueryResolver(), BarResolver()) + .build() + .makeExecutableSchema() + } + + class QueryResolver : GraphQLQueryResolver { + fun getBar(): Bar = Bar() + } + + class Bar + + abstract class FooResolver : GraphQLResolver { + fun getValue(foo: T): String = "value" + + fun getValueWithSeveralParameters(foo: T, arg1: Boolean, arg2: String): String { + return if (arg1) { + "value" + } else { + arg2 + } + } + } + + class BarResolver : FooResolver() +} diff --git a/src/test/kotlin/graphql/kickstart/tools/TestInterfaces.kt b/src/test/kotlin/graphql/kickstart/tools/TestInterfaces.kt new file mode 100644 index 00000000..17a22a91 --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/TestInterfaces.kt @@ -0,0 +1,16 @@ +package graphql.kickstart.tools + +interface Animal { + fun type(): SchemaClassScannerTest.NestedInterfaceTypeQuery.ComplexType? +} + +interface Vehicle { + fun getInformation(): VehicleInformation +} + +interface VehicleInformation + +interface SomeInterface { + fun getValue(): String? +} + diff --git a/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt b/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt new file mode 100644 index 00000000..a6048770 --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt @@ -0,0 +1,22 @@ +package graphql.kickstart.tools + +import com.fasterxml.jackson.databind.ObjectMapper +import graphql.ExecutionInput +import graphql.GraphQL + +private val mapper = ObjectMapper() + +fun assertNoGraphQlErrors(gql: GraphQL, args: Map = mapOf(), context: Any = Object(), closure: () -> String): Map { + val result = gql.execute(ExecutionInput.newExecutionInput() + .query(closure.invoke()) + .context(context) + .root(context) + .variables(args)) + + if (result.errors.isNotEmpty()) { + throw AssertionError("GraphQL result contained errors!\n${result.errors.map { mapper.writeValueAsString(it) }.joinToString { "\n" }}") + } + + return result.getData() as Map +} + diff --git a/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt b/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt new file mode 100644 index 00000000..461c3d45 --- /dev/null +++ b/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt @@ -0,0 +1,182 @@ +package graphql.kickstart.tools + +import graphql.kickstart.tools.SchemaParserOptions.GenericWrapper +import graphql.kickstart.tools.SchemaParserOptions.GenericWrapper.Companion.listCollectionWithTransformer +import graphql.kickstart.tools.resolver.FieldResolverScanner +import graphql.kickstart.tools.util.ParameterizedTypeImpl +import graphql.language.* +import graphql.language.Type +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.junit.runners.Suite +import java.util.* +import java.util.concurrent.CompletableFuture +import java.util.concurrent.CompletableFuture.completedFuture +import java.util.concurrent.Future + +@RunWith(Suite::class) +@Suite.SuiteClasses( + TypeClassMatcherTest.Suit1::class, + TypeClassMatcherTest.Suit2::class, + TypeClassMatcherTest.Suit3::class, + TypeClassMatcherTest.Suit4::class +) +class TypeClassMatcherTest { + + companion object { + private val customType: Type<*> = TypeName("CustomType") + private val unwrappedCustomType: Type<*> = TypeName("UnwrappedGenericCustomType") + + private val customDefinition: TypeDefinition<*> = ObjectTypeDefinition("CustomType") + private val unwrappedCustomDefinition: TypeDefinition<*> = ObjectTypeDefinition("UnwrappedGenericCustomType") + + + private val matcher: TypeClassMatcher = TypeClassMatcher(mapOf( + "CustomType" to customDefinition, + "UnwrappedGenericCustomType" to unwrappedCustomDefinition + )) + + private val options: SchemaParserOptions = SchemaParserOptions.newOptions().genericWrappers( + GenericWrapper( + GenericCustomType::class.java, + 0 + ), + listCollectionWithTransformer( + GenericCustomListType::class.java, + 0 + ) { x -> x } + ).build() + + private val scanner: FieldResolverScanner = FieldResolverScanner(options) + private val resolver = RootResolverInfo(listOf(QueryMethods()), options) + + private fun createPotentialMatch(methodName: String, graphQLType: Type<*>): TypeClassMatcher.PotentialMatch { + return scanner.findFieldResolver(FieldDefinition(methodName, graphQLType), resolver) + .scanForMatches() + .find { it.location == TypeClassMatcher.Location.RETURN_TYPE }!! + } + + private fun list(other: Type<*> = customType): Type<*> = ListType(other) + private fun nonNull(other: Type<*> = customType): Type<*> = NonNullType(other) + } + + @RunWith(Parameterized::class) + class Suit1(private val methodName: String, private val type: Type<*>) { + + @Test + fun `matcher verifies that nested return type matches graphql definition for method`() { + val match = matcher.match(createPotentialMatch(methodName, type)) + match as TypeClassMatcher.ValidMatch + assert(match.type == customDefinition) + assert(match.javaType == CustomType::class.java) + } + + companion object { + @Parameterized.Parameters + @JvmStatic + fun data(): Collection> { + return listOf( + arrayOf("type", customType), + arrayOf("futureType", customType), + arrayOf("listType", list()), + arrayOf("listListType", list(list())), + arrayOf("futureListType", list()), + arrayOf("listFutureType", list()), + arrayOf("listListFutureType", list(list())), + arrayOf("futureListListType", list(list())), + arrayOf("superType", customType), + arrayOf("superListFutureType", list(nonNull())), + arrayOf("nullableType", customType), + arrayOf("nullableListType", list(nonNull(customType))), + arrayOf("genericCustomType", customType), + arrayOf("genericListType", list()) + ) + } + } + } + + @RunWith(Parameterized::class) + class Suit2(private val methodName: String, private val type: Type<*>) { + + @Test(expected = SchemaClassScannerError::class) + fun `matcher verifies that nested return type doesn't match graphql definition for method`() { + matcher.match(createPotentialMatch(methodName, type)) + } + + companion object { + @Parameterized.Parameters + @JvmStatic + fun data(): Collection> { + return listOf( + arrayOf("type", list()), + arrayOf("futureType", list()) + ) + } + } + } + + @RunWith(Parameterized::class) + class Suit3(private val methodName: String, private val type: Type<*>) { + + @Test(expected = SchemaClassScannerError::class) + fun `matcher verifies return value optionals are used incorrectly for method`() { + matcher.match(createPotentialMatch(methodName, type)) + } + + companion object { + @Parameterized.Parameters + @JvmStatic + fun data(): Collection> { + return listOf( + arrayOf("nullableType", nonNull(customType)), + arrayOf("nullableNullableType", customType), + arrayOf("listNullableType", list(customType)) + ) + } + } + } + + class Suit4 { + @Test + fun `matcher allows unwrapped parameterized types as root types`() { + val match = matcher.match(createPotentialMatch("genericCustomUnwrappedType", unwrappedCustomType)) + match as TypeClassMatcher.ValidMatch + assert(match.type == unwrappedCustomDefinition) + val javatype = match.javaType as ParameterizedTypeImpl + assert(javatype.rawType == UnwrappedGenericCustomType::class.java) + assert(javatype.actualTypeArguments.first() == CustomType::class.java) + } + } + + private abstract class Super : GraphQLQueryResolver { + fun superType(): Type = superType() + fun superListFutureType(): ListFutureType = superListFutureType() + } + + private class QueryMethods : Super>>() { + fun type(): CustomType = CustomType() + fun futureType(): Future = completedFuture(CustomType()) + fun listType(): List = listOf(CustomType()) + fun listListType(): List> = listOf(listOf(CustomType())) + fun futureListType(): CompletableFuture> = completedFuture(listOf(CustomType())) + fun listFutureType(): List> = listOf(completedFuture(CustomType())) + fun listListFutureType(): List>> = listOf(listOf(completedFuture(CustomType()))) + fun futureListListType(): CompletableFuture>> = completedFuture(listOf(listOf(CustomType()))) + fun nullableType(): Optional? = null + fun nullableListType(): Optional?>? = null + fun nullableNullableType(): Optional?>? = null + fun listNullableType(): List?> = listOf(null) + fun genericCustomType(): GenericCustomType = GenericCustomType() + fun genericListType(): GenericCustomListType = GenericCustomListType() + fun genericCustomUnwrappedType(): UnwrappedGenericCustomType = UnwrappedGenericCustomType() + } + + private class CustomType + + private class GenericCustomType + + private class GenericCustomListType + + private class UnwrappedGenericCustomType +} From f8600f3d3aba3b651418a12e08761e4519a81c4c Mon Sep 17 00:00:00 2001 From: Oryan M Date: Mon, 22 Feb 2021 10:47:46 -0500 Subject: [PATCH 2/5] Remove groovy dependencies --- pom.xml | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/pom.xml b/pom.xml index 0e14abc8..1528cd35 100644 --- a/pom.xml +++ b/pom.xml @@ -155,9 +155,9 @@ - org.spockframework - spock-core - 1.0-groovy-2.4 + junit + junit + 4.12 test @@ -209,7 +209,6 @@ src/test/kotlin - src/test/groovy @@ -240,23 +239,6 @@ - - - org.codehaus.gmavenplus - gmavenplus-plugin - 1.5 - - - - generateStubs - testGenerateStubs - addTestSources - testCompile - - - - - org.apache.maven.plugins maven-compiler-plugin @@ -279,7 +261,6 @@ - **/*Spec.* **/*Test.* From a4777d0fa1b8bd24d190023d33bcf8526857a565 Mon Sep 17 00:00:00 2001 From: Oryan M Date: Mon, 22 Feb 2021 11:04:36 -0500 Subject: [PATCH 3/5] Remove @Before --- .../graphql/kickstart/tools/BuiltInIdTest.kt | 22 +++++++------------ .../graphql/kickstart/tools/EndToEndTest.kt | 12 +++------- .../kickstart/tools/EnumListParameterTest.kt | 22 +++++++------------ .../kickstart/tools/MultiResolverTest.kt | 22 +++++++------------ .../tools/ParameterizedGetterTest.kt | 21 +++++++----------- 5 files changed, 35 insertions(+), 64 deletions(-) diff --git a/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt b/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt index bb516bbd..8a018537 100644 --- a/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt @@ -2,17 +2,13 @@ package graphql.kickstart.tools import graphql.GraphQL import graphql.execution.AsyncExecutionStrategy -import org.junit.Before +import graphql.schema.GraphQLSchema import org.junit.Test import java.util.* class BuiltInIdTest { - private lateinit var gql: GraphQL - - @Before - fun setup() { - val schema = SchemaParser.newParser().schemaString(""" + private val schema: GraphQLSchema = SchemaParser.newParser().schemaString(""" type Query { itemByLongId(id: ID!): Item1! itemsByLongIds(ids: [ID!]!): [Item1!]! @@ -28,14 +24,12 @@ class BuiltInIdTest { id: ID! } """.trimIndent()) - .resolvers(QueryWithLongItemResolver()) - .build() - .makeExecutableSchema() - gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() - - } + .resolvers(QueryWithLongItemResolver()) + .build() + .makeExecutableSchema() + private val gql: GraphQL = GraphQL.newGraphQL(schema) + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() @Test fun `supports Long as ID as input`() { diff --git a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt index 531cd7e5..267b8218 100644 --- a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt @@ -4,7 +4,6 @@ import graphql.* import graphql.execution.AsyncExecutionStrategy import graphql.schema.GraphQLEnumType import graphql.schema.GraphQLSchema -import org.junit.Before import org.junit.Test import org.reactivestreams.Publisher import org.reactivestreams.Subscriber @@ -16,15 +15,10 @@ import java.util.concurrent.TimeUnit class EndToEndTest { - private lateinit var gql: GraphQL private val schema: GraphQLSchema = createSchema() - - @Before - fun setup() { - gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() - } + private val gql: GraphQL = GraphQL.newGraphQL(schema) + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() @Test fun `schema comments are used as descriptions`() { diff --git a/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt b/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt index 4e48b596..2d96bec4 100644 --- a/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt @@ -2,15 +2,11 @@ package graphql.kickstart.tools import graphql.GraphQL import graphql.execution.AsyncExecutionStrategy -import org.junit.Before +import graphql.schema.GraphQLSchema import org.junit.Test class EnumListParameterTest { - private lateinit var gql: GraphQL - - @Before - fun setup() { - val schema = SchemaParser.newParser().schemaString(""" + private val schema: GraphQLSchema = SchemaParser.newParser().schemaString(""" type Query { countries(regions: [Region!]!): [Country!]! } @@ -26,14 +22,12 @@ class EnumListParameterTest { regions: [Region!] } """.trimIndent()) - .resolvers(QueryResolver()) - .build() - .makeExecutableSchema() - gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() - - } + .resolvers(QueryResolver()) + .build() + .makeExecutableSchema() + private val gql: GraphQL = GraphQL.newGraphQL(schema) + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() @Test fun `query with parameter type list of enums should resolve correctly`() { diff --git a/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt b/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt index 0aa6bca0..189a5a7f 100644 --- a/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt @@ -2,16 +2,12 @@ package graphql.kickstart.tools import graphql.GraphQL import graphql.execution.AsyncExecutionStrategy -import org.junit.Before +import graphql.schema.GraphQLSchema import org.junit.Test class MultiResolverTest { - private lateinit var gql: GraphQL - - @Before - fun setup() { - val schema = SchemaParser.newParser().schemaString(""" + private val schema: GraphQLSchema = SchemaParser.newParser().schemaString(""" type Query { person: Person } @@ -25,14 +21,12 @@ class MultiResolverTest { name: String! } """.trimIndent()) - .resolvers(QueryWithPersonResolver(), PersonFriendResolver(), PersonNameResolver()) - .build() - .makeExecutableSchema() - gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() - - } + .resolvers(QueryWithPersonResolver(), PersonFriendResolver(), PersonNameResolver()) + .build() + .makeExecutableSchema() + private val gql: GraphQL = GraphQL.newGraphQL(schema) + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() @Test fun `multiple resolvers for one data class should resolve methods with arguments`() { diff --git a/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt b/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt index dd319bb6..6d1caea8 100644 --- a/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt @@ -2,16 +2,12 @@ package graphql.kickstart.tools import graphql.GraphQL import graphql.execution.AsyncExecutionStrategy -import org.junit.Before +import graphql.schema.GraphQLSchema import org.junit.Test class ParameterizedGetterTest { - private lateinit var gql: GraphQL - - @Before - fun setup() { - val schema = SchemaParser.newParser().schemaString(""" + private val schema: GraphQLSchema = SchemaParser.newParser().schemaString(""" type Query { human: Human } @@ -25,13 +21,12 @@ class ParameterizedGetterTest { name: String! } """.trimIndent()) - .resolvers(QueryResolver(), HumanResolver()) - .build() - .makeExecutableSchema() - gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() - } + .resolvers(QueryResolver(), HumanResolver()) + .build() + .makeExecutableSchema() + private val gql: GraphQL = GraphQL.newGraphQL(schema) + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() @Test fun `parameterized query is resolved on data type instead of on its resolver`() { From f803d2b4877df25646ea981dd27bf909a8c64241 Mon Sep 17 00:00:00 2001 From: Oryan M Date: Wed, 24 Feb 2021 09:58:08 -0500 Subject: [PATCH 4/5] Unify test strings indentation --- .../graphql/kickstart/tools/BuiltInIdTest.kt | 21 +- .../graphql/kickstart/tools/DirectiveTest.kt | 129 +++-- .../graphql/kickstart/tools/EndToEndTest.kt | 121 ++-- .../kickstart/tools/EnumDefaultValueTest.kt | 49 +- .../kickstart/tools/EnumListParameterTest.kt | 32 +- .../tools/FieldResolverScannerTest.kt | 1 - .../kickstart/tools/GenericResolverTest.kt | 53 +- .../MethodFieldResolverDataFetcherTest.kt | 56 +- .../tools/MethodFieldResolverTest.kt | 173 +++--- .../kickstart/tools/MultiResolverTest.kt | 16 +- .../kickstart/tools/NestedInputTypesTest.kt | 131 ++--- .../tools/ParameterizedGetterTest.kt | 26 +- .../graphql/kickstart/tools/ReactiveTest.kt | 12 +- .../kickstart/tools/RelayConnectionTest.kt | 139 ++--- .../kickstart/tools/ResolverMethodsTest.java | 24 +- .../kickstart/tools/SchemaClassScannerTest.kt | 532 +++++++++--------- .../tools/SchemaParserBuilderTest.kt | 8 +- .../kickstart/tools/SchemaParserTest.kt | 523 ++++++++--------- .../kickstart/tools/SuperclassResolverTest.kt | 34 +- .../graphql/kickstart/tools/TestUtils.kt | 8 +- .../kickstart/tools/TypeClassMatcherTest.kt | 71 ++- 21 files changed, 1100 insertions(+), 1059 deletions(-) diff --git a/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt b/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt index 8a018537..255efa85 100644 --- a/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt @@ -8,7 +8,9 @@ import java.util.* class BuiltInIdTest { - private val schema: GraphQLSchema = SchemaParser.newParser().schemaString(""" + private val schema: GraphQLSchema = SchemaParser.newParser() + .schemaString( + """ type Query { itemByLongId(id: ID!): Item1! itemsByLongIds(ids: [ID!]!): [Item1!]! @@ -17,19 +19,19 @@ class BuiltInIdTest { } type Item1 { - id: ID! + id: ID! } type Item2 { - id: ID! + id: ID! } - """.trimIndent()) - .resolvers(QueryWithLongItemResolver()) - .build() - .makeExecutableSchema() + """) + .resolvers(QueryWithLongItemResolver()) + .build() + .makeExecutableSchema() private val gql: GraphQL = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() @Test fun `supports Long as ID as input`() { @@ -95,7 +97,6 @@ class BuiltInIdTest { assert(data["itemsByUuidIds"] != null) assert(((data["itemsByUuidIds"] as List<*>).size == 3)) assert(((data["itemsByUuidIds"] as List<*>)[0] as Map<*, *>)["id"] == "00000000-0000-0000-0000-000000000000") - } class QueryWithLongItemResolver : GraphQLQueryResolver { diff --git a/src/test/kotlin/graphql/kickstart/tools/DirectiveTest.kt b/src/test/kotlin/graphql/kickstart/tools/DirectiveTest.kt index 300f00c5..e23f4147 100644 --- a/src/test/kotlin/graphql/kickstart/tools/DirectiveTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/DirectiveTest.kt @@ -17,56 +17,59 @@ import java.util.function.BiFunction class DirectiveTest { @Test fun `should apply correctly the @uppercase directive`() { - val schema = SchemaParser.newParser().schemaString(""" - directive @uppercase on FIELD_DEFINITION - - type Query { - users: UserConnection - } - - type UserConnection { - edges: [UserEdge!]! - } - - type UserEdge { - node: User! - } - - type User { - id: ID! - name: String @uppercase - } - """) - .resolvers(UsersQueryResolver()) - .directive("uppercase", UppercaseDirective()) - .build() - .makeExecutableSchema() + val schema = SchemaParser.newParser() + .schemaString( + """ + directive @uppercase on FIELD_DEFINITION + + type Query { + users: UserConnection + } + + type UserConnection { + edges: [UserEdge!]! + } + + type UserEdge { + node: User! + } + + type User { + id: ID! + name: String @uppercase + } + """) + .resolvers(UsersQueryResolver()) + .directive("uppercase", UppercaseDirective()) + .build() + .makeExecutableSchema() val gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() - - val result = gql.execute(""" - query { - users { - edges { - node { - id - name + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() + + val result = gql.execute( + """ + query { + users { + edges { + node { + id + name + } + } } - } } - } - """) + """) val expected = mapOf( - "users" to mapOf( - "edges" to listOf( - mapOf("node" to - mapOf("id" to "1", "name" to "LUKE") - ) - ) + "users" to mapOf( + "edges" to listOf( + mapOf("node" to + mapOf("id" to "1", "name" to "LUKE") + ) ) + ) ) Assert.assertEquals(expected, result.getData>>()) @@ -75,23 +78,25 @@ class DirectiveTest { @Test @Ignore("Ignore until enums work in directives") fun `should compile schema with directive that has enum parameter`() { - val schema = SchemaParser.newParser().schemaString(""" - directive @allowed(state: [AllowedState!]) on FIELD_DEFINITION - - enum AllowedState { - ALLOWED - DISALLOWED - } - - type Book { - id: Int! - name: String! @allowed(state: [ALLOWED]) - } - - type Query { - books: [Book!] - } - """) + val schema = SchemaParser.newParser() + .schemaString( + """ + directive @allowed(state: [AllowedState!]) on FIELD_DEFINITION + + enum AllowedState { + ALLOWED + DISALLOWED + } + + type Book { + id: Int! + name: String! @allowed(state: [ALLOWED]) + } + + type Query { + books: [Book!] + } + """) .resolvers(QueryResolver()) .directive("allowed", AllowedDirective()) .build() @@ -151,8 +156,8 @@ class DirectiveTest { } private data class User( - val id: Long, - val name: String + val id: Long, + val name: String ) } } diff --git a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt index 267b8218..ec363f84 100644 --- a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt @@ -17,8 +17,8 @@ class EndToEndTest { private val schema: GraphQLSchema = createSchema() private val gql: GraphQL = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() @Test fun `schema comments are used as descriptions`() { @@ -33,9 +33,9 @@ class EndToEndTest { """ { items(itemsInput: {name: "item1"}) { - id - type - } + id + type + } } """ } @@ -46,12 +46,12 @@ class EndToEndTest { val data = assertNoGraphQlErrors(gql, mapOf("name" to "new1", "type" to Type.TYPE_2.toString())) { """ mutation addNewItem(${'$'}name: String!, ${'$'}type: Type!) { - addItem(newItem: {name: ${'$'}name, type: ${'$'}type}) { - id - name - type - } - } + addItem(newItem: {name: ${'$'}name, type: ${'$'}type}) { + id + name + type + } + } """ } @@ -74,9 +74,9 @@ class EndToEndTest { } val result = gql.execute(ExecutionInput.newExecutionInput() - .query(closure.invoke()) - .context(OnItemCreatedContext(newItem)) - .variables(mapOf())) + .query(closure.invoke()) + .context(OnItemCreatedContext(newItem)) + .variables(mapOf())) val data = result.getData() as Publisher val latch = CountDownLatch(1) @@ -119,13 +119,13 @@ class EndToEndTest { { allItems { ... on Item { - id - name - } + id + name + } ... on OtherItem { - name - type - } + name + type + } } } """ @@ -141,14 +141,14 @@ class EndToEndTest { { nestedUnionItems { ... on Item { - itemId: id - } + itemId: id + } ... on OtherItem { - otherItemId: id - } + otherItemId: id + } ... on ThirdItem { - thirdItemId: id - } + thirdItemId: id + } } } """ @@ -168,8 +168,8 @@ class EndToEndTest { """ { itemByUUID(uuid: "38f685f1-b460-4a54-a17f-7fd69e8cf3f8") { - uuid - } + uuid + } } """ } @@ -183,7 +183,7 @@ class EndToEndTest { val args = mapOf("fileParts" to fileParts) val data = assertNoGraphQlErrors(gql, args) { """ - mutation (${'$'}fileParts: [Upload!]!) { echoFiles(fileParts: ${'$'}fileParts)} + mutation (${'$'}fileParts: [Upload!]!) { echoFiles(fileParts: ${'$'}fileParts) } """ } @@ -220,8 +220,8 @@ class EndToEndTest { } assert(data["propertySortedMapItems"] == listOf( - mapOf("name" to "Arthur", "age" to 76), - mapOf("name" to "Jane", "age" to 28) + mapOf("name" to "Arthur", "age" to 76), + mapOf("name" to "Jane", "age" to 28) )) } @@ -295,12 +295,12 @@ class EndToEndTest { """ { missing: itemsWithOptionalInput { - id - } + id + } present: itemsWithOptionalInput(itemsInput: {name: "item1"}) { - id - } + id + } } """ } @@ -315,12 +315,12 @@ class EndToEndTest { """ { missing: itemsWithOptionalInputExplicit { - id - } + id + } present: itemsWithOptionalInputExplicit(itemsInput: {name: "item1"}) { - id - } + id + } } """ } @@ -335,12 +335,12 @@ class EndToEndTest { """ { missing: optionalItem(itemsInput: {name: "item?"}) { - id - } + id + } present: optionalItem(itemsInput: {name: "item1"}) { - id - } + id + } } """ } @@ -368,16 +368,16 @@ class EndToEndTest { """ { __type(name: "Query") { - name - fields { name - args { + fields { name - defaultValue + args { + name + defaultValue + } } } } - } """ } @@ -506,8 +506,8 @@ class EndToEndTest { """ { itemByBuiltInId(id: "38f685f1-b460-4a54-a17f-7fd69e8cf3f8") { - name - } + name + } } """ } @@ -544,8 +544,8 @@ class EndToEndTest { } assert(data["coroutineItems"] == listOf( - mapOf("id" to 0, "name" to "item1"), - mapOf("id" to 1, "name" to "item2") + mapOf("id" to 0, "name" to "item1"), + mapOf("id" to 1, "name" to "item2") )) } @@ -563,9 +563,9 @@ class EndToEndTest { } val result = gql.execute(ExecutionInput.newExecutionInput() - .query(closure.invoke()) - .context(OnItemCreatedContext(newItem)) - .variables(mapOf())) + .query(closure.invoke()) + .context(OnItemCreatedContext(newItem)) + .variables(mapOf())) val data = result.getData() as Publisher val subscriber = TestEnvironment().newManualSubscriber(data) @@ -591,9 +591,9 @@ class EndToEndTest { } val result = gql.execute(ExecutionInput.newExecutionInput() - .query(closure.invoke()) - .context(OnItemCreatedContext(newItem)) - .variables(mapOf())) + .query(closure.invoke()) + .context(OnItemCreatedContext(newItem)) + .variables(mapOf())) val data = result.getData() as Publisher val subscriber = TestEnvironment().newManualSubscriber(data) @@ -622,11 +622,12 @@ class EndToEndTest { @Test fun `generated schema should re-throw original runtime exception when executing a resolver method`() { - val result = gql.execute(ExecutionInput.newExecutionInput().query(""" + val result = gql.execute(ExecutionInput.newExecutionInput().query( + """ { throwsIllegalArgumentException } - """ + """ )) assert(result.errors.size == 1) diff --git a/src/test/kotlin/graphql/kickstart/tools/EnumDefaultValueTest.kt b/src/test/kotlin/graphql/kickstart/tools/EnumDefaultValueTest.kt index 11b62f3d..ca83b325 100644 --- a/src/test/kotlin/graphql/kickstart/tools/EnumDefaultValueTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/EnumDefaultValueTest.kt @@ -9,35 +9,36 @@ class EnumDefaultValueTest { @Test fun `enum value is not passed down to graphql-java`() { val schema = SchemaParser.newParser() - .schemaString(""" - type Query { - test(input: MySortSpecifier): SortBy - } - input MySortSpecifier { - sortBy: SortBy = createdOn - value: Int = 10 - } - enum SortBy { - createdOn - updatedOn - } - """.trimIndent()) - .resolvers(object : GraphQLQueryResolver { - fun test(input: MySortSpecifier): SortBy? = input.sortBy - }) - .build() - .makeExecutableSchema() + .schemaString( + """ + type Query { + test(input: MySortSpecifier): SortBy + } + input MySortSpecifier { + sortBy: SortBy = createdOn + value: Int = 10 + } + enum SortBy { + createdOn + updatedOn + } + """) + .resolvers(object : GraphQLQueryResolver { + fun test(input: MySortSpecifier): SortBy? = input.sortBy + }) + .build() + .makeExecutableSchema() val ggl = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() val data = assertNoGraphQlErrors(ggl, mapOf("input" to mapOf("value" to 1))) { """ - query test(${'$'}input: MySortSpecifier) { - test(input: ${'$'}input) - } - """.trimIndent() + query test(${'$'}input: MySortSpecifier) { + test(input: ${'$'}input) + } + """ } assert(data["test"] == "createdOn") diff --git a/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt b/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt index 2d96bec4..c14d8e00 100644 --- a/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt @@ -6,40 +6,42 @@ import graphql.schema.GraphQLSchema import org.junit.Test class EnumListParameterTest { - private val schema: GraphQLSchema = SchemaParser.newParser().schemaString(""" + private val schema: GraphQLSchema = SchemaParser.newParser() + .schemaString( + """ type Query { countries(regions: [Region!]!): [Country!]! } - + enum Region { EUROPE ASIA } - + type Country { code: String! name: String! regions: [Region!] } - """.trimIndent()) - .resolvers(QueryResolver()) - .build() - .makeExecutableSchema() + """) + .resolvers(QueryResolver()) + .build() + .makeExecutableSchema() private val gql: GraphQL = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() @Test fun `query with parameter type list of enums should resolve correctly`() { val data = assertNoGraphQlErrors(gql, mapOf("regions" to setOf("EUROPE", "ASIA"))) { """ - query getCountries(${'$'}regions: [Region!]!) { - countries(regions: ${'$'}regions){ - code - name - regions - } + query getCountries(${'$'}regions: [Region!]!) { + countries(regions: ${'$'}regions){ + code + name + regions } + } """ } diff --git a/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt b/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt index 1ad1f26f..299eca87 100644 --- a/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt @@ -12,7 +12,6 @@ import graphql.relay.DefaultConnection import graphql.relay.DefaultPageInfo import org.junit.Test - class FieldResolverScannerTest { private val options = defaultOptions() diff --git a/src/test/kotlin/graphql/kickstart/tools/GenericResolverTest.kt b/src/test/kotlin/graphql/kickstart/tools/GenericResolverTest.kt index 0d05bd16..cc8444df 100644 --- a/src/test/kotlin/graphql/kickstart/tools/GenericResolverTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/GenericResolverTest.kt @@ -6,19 +6,20 @@ class GenericResolverTest { @Test fun `methods from generic resolvers are resolved`() { - SchemaParser.newParser().schemaString(""" - type Query { - bar: Bar - } - - type Bar { - value: String - } - """) - .resolvers(QueryResolver1(), BarResolver()) - .build() - .makeExecutableSchema() - + SchemaParser.newParser() + .schemaString( + """ + type Query { + bar: Bar + } + + type Bar { + value: String + } + """) + .resolvers(QueryResolver1(), BarResolver()) + .build() + .makeExecutableSchema() } class QueryResolver1 : GraphQLQueryResolver { @@ -37,20 +38,21 @@ class GenericResolverTest { @Test fun `methods from generic inherited resolvers are resolved`() { - SchemaParser.newParser().schemaString(""" - type Query { - car: Car - } - type Car { - value: String - } - """) - .resolvers(QueryResolver2(), CarResolver()) - .build() - .makeExecutableSchema() + SchemaParser.newParser() + .schemaString( + """ + type Query { + car: Car + } + type Car { + value: String + } + """) + .resolvers(QueryResolver2(), CarResolver()) + .build() + .makeExecutableSchema() } - class QueryResolver2 : GraphQLQueryResolver { fun getCar(): Car = Car() } @@ -62,5 +64,4 @@ class GenericResolverTest { class Car class CarResolver : FooGraphQLResolver() - } diff --git a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt index 63824b04..c4b6d95e 100644 --- a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt @@ -46,13 +46,13 @@ class MethodFieldResolverDataFetcherTest { @ExperimentalCoroutinesApi val options = SchemaParserOptions.Builder() - .coroutineContext(dispatcher + job) - .build() + .coroutineContext(dispatcher + job) + .build() @Suppress("UNUSED_PARAMETER") suspend fun isActive(data: DataClass): Boolean { return coroutineContext[dispatcher.key] == dispatcher && - coroutineContext[Job] == job.children.first() + coroutineContext[Job] == job.children.first() } } @@ -203,7 +203,7 @@ class MethodFieldResolverDataFetcherTest { val name = "correct name" val resolver = createFetcher("active", listOf(InputValueDefinition("input", TypeName("InputClass"))), object : GraphQLQueryResolver { fun active(input: InputClass): Boolean = - input is InputClass && input.name == name + input is InputClass && input.name == name }) assert(resolver.get(createEnvironment(arguments = mapOf("input" to mapOf("name" to name)))) == true) @@ -214,7 +214,7 @@ class MethodFieldResolverDataFetcherTest { val name = "correct name" val resolver = createFetcher("active", listOf(InputValueDefinition("input", TypeName("Map"))), object : GraphQLQueryResolver { fun active(input: Map<*, *>): Boolean = - input is Map<*, *> && input["name"] == name + input is Map<*, *> && input["name"] == name }) assert(resolver.get(createEnvironment(arguments = mapOf("input" to mapOf("name" to name)))) == true) @@ -224,7 +224,7 @@ class MethodFieldResolverDataFetcherTest { fun `data fetcher returns null if nullable argument is passed null`() { val resolver = createFetcher("echo", listOf(InputValueDefinition("message", TypeName("String"))), object : GraphQLQueryResolver { fun echo(message: String?): String? = - message + message }) assert(resolver.get(createEnvironment()) == null) @@ -234,7 +234,7 @@ class MethodFieldResolverDataFetcherTest { fun `data fetcher throws exception if non-null argument is passed null`() { val resolver = createFetcher("echo", listOf(InputValueDefinition("message", NonNullType(TypeName("String")))), object : GraphQLQueryResolver { fun echo(message: String): String = - message + message }) resolver.get(createEnvironment()) @@ -255,24 +255,24 @@ class MethodFieldResolverDataFetcherTest { } private fun createFetcher( - methodName: String, - arguments: List = emptyList(), - resolver: GraphQLResolver<*> + methodName: String, + arguments: List = emptyList(), + resolver: GraphQLResolver<*> ): DataFetcher<*> { return createFetcher(methodName, resolver, arguments) } private fun createFetcher( - methodName: String, - resolver: GraphQLResolver<*>, - arguments: List = emptyList(), - options: SchemaParserOptions = SchemaParserOptions.defaultOptions() + methodName: String, + resolver: GraphQLResolver<*>, + arguments: List = emptyList(), + options: SchemaParserOptions = SchemaParserOptions.defaultOptions() ): DataFetcher<*> { val field = FieldDefinition.newFieldDefinition() - .name(methodName) - .type(TypeName("Boolean")) - .inputValueDefinitions(arguments) - .build() + .name(methodName) + .type(TypeName("Boolean")) + .inputValueDefinitions(arguments) + .build() val resolverInfo = if (resolver is GraphQLQueryResolver) { RootResolverInfo(listOf(resolver), options) @@ -284,10 +284,10 @@ class MethodFieldResolverDataFetcherTest { private fun createEnvironment(source: Any = Object(), arguments: Map = emptyMap(), context: Any? = null): DataFetchingEnvironment { return DataFetchingEnvironmentImpl.newDataFetchingEnvironment(buildExecutionContext()) - .source(source) - .arguments(arguments) - .context(context) - .build() + .source(source) + .arguments(arguments) + .context(context) + .build() } private fun buildExecutionContext(): ExecutionContext { @@ -298,12 +298,12 @@ class MethodFieldResolverDataFetcherTest { } val executionId = ExecutionId.from("executionId123") return ExecutionContextBuilder.newExecutionContextBuilder() - .instrumentation(SimpleInstrumentation.INSTANCE) - .executionId(executionId) - .queryStrategy(executionStrategy) - .mutationStrategy(executionStrategy) - .subscriptionStrategy(executionStrategy) - .build() + .instrumentation(SimpleInstrumentation.INSTANCE) + .executionId(executionId) + .queryStrategy(executionStrategy) + .mutationStrategy(executionStrategy) + .subscriptionStrategy(executionStrategy) + .build() } data class DataClass(val name: String = "TestName") diff --git a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt index d2d69bd7..7c94c29b 100644 --- a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt @@ -17,14 +17,14 @@ class MethodFieldResolverTest { @Test fun `should handle Optional type as method input argument`() { val schema = SchemaParser.newParser() - .schemaString(""" - type Query { - testValue(input: String): String - testOmitted(input: String): String - testNull(input: String): String - } - """ - ) + .schemaString( + """ + type Query { + testValue(input: String): String + testOmitted(input: String): String + testNull(input: String): String + } + """) .scalars(customScalarType) .resolvers(object : GraphQLQueryResolver { fun testValue(input: Optional) = input.toString() @@ -36,17 +36,16 @@ class MethodFieldResolverTest { val gql = GraphQL.newGraphQL(schema).build() - val result = gql - .execute(ExecutionInput.newExecutionInput() - .query(""" - query { - testValue(input: "test-value") - testOmitted - testNull(input: null) - } - """) - .context(Object()) - .root(Object())) + val result = gql.execute(ExecutionInput.newExecutionInput().query( + """ + query { + testValue(input: "test-value") + testOmitted + testNull(input: null) + } + """) + .context(Object()) + .root(Object())) val expected = mapOf( "testValue" to "Optional[test-value]", @@ -60,14 +59,14 @@ class MethodFieldResolverTest { @Test fun `should handle Optional type as method input argument with omission detection`() { val schema = SchemaParser.newParser() - .schemaString(""" - type Query { - testValue(input: String): String - testOmitted(input: String): String - testNull(input: String): String - } - """ - ) + .schemaString( + """ + type Query { + testValue(input: String): String + testOmitted(input: String): String + testNull(input: String): String + } + """) .scalars(customScalarType) .resolvers(object : GraphQLQueryResolver { fun testValue(input: Optional) = input.toString() @@ -82,17 +81,16 @@ class MethodFieldResolverTest { val gql = GraphQL.newGraphQL(schema).build() - val result = gql - .execute(ExecutionInput.newExecutionInput() - .query(""" - query { - testValue(input: "test-value") - testOmitted - testNull(input: null) - } - """) - .context(Object()) - .root(Object())) + val result = gql.execute(ExecutionInput.newExecutionInput().query( + """ + query { + testValue(input: "test-value") + testOmitted + testNull(input: null) + } + """) + .context(Object()) + .root(Object())) val expected = mapOf( "testValue" to "Optional[test-value]", @@ -106,13 +104,13 @@ class MethodFieldResolverTest { @Test fun `should handle scalar types as method input argument`() { val schema = SchemaParser.newParser() - .schemaString(""" - scalar CustomScalar - type Query { - test(input: CustomScalar): Int - } - """.trimIndent() - ) + .schemaString( + """ + scalar CustomScalar + type Query { + test(input: CustomScalar): Int + } + """) .scalars(customScalarType) .resolvers(object : GraphQLQueryResolver { fun test(scalar: CustomScalar) = scalar.value.length @@ -122,16 +120,15 @@ class MethodFieldResolverTest { val gql = GraphQL.newGraphQL(schema).build() - val result = gql - .execute(ExecutionInput.newExecutionInput() - .query(""" - query Test(${"$"}input: CustomScalar) { - test(input: ${"$"}input) - } - """.trimIndent()) - .variables(mapOf("input" to "FooBar")) - .context(Object()) - .root(Object())) + val result = gql.execute(ExecutionInput.newExecutionInput().query( + """ + query Test(${"$"}input: CustomScalar) { + test(input: ${"$"}input) + } + """) + .variables(mapOf("input" to "FooBar")) + .context(Object()) + .root(Object())) Assert.assertEquals(6, result.getData>()["test"]) } @@ -139,13 +136,13 @@ class MethodFieldResolverTest { @Test fun `should handle lists of scalar types`() { val schema = SchemaParser.newParser() - .schemaString(""" - scalar CustomScalar - type Query { - test(input: [CustomScalar]): Int - } - """.trimIndent() - ) + .schemaString( + """ + scalar CustomScalar + type Query { + test(input: [CustomScalar]): Int + } + """) .scalars(customScalarType) .resolvers(object : GraphQLQueryResolver { fun test(scalars: List) = scalars.map { it.value.length }.sum() @@ -155,16 +152,15 @@ class MethodFieldResolverTest { val gql = GraphQL.newGraphQL(schema).build() - val result = gql - .execute(ExecutionInput.newExecutionInput() - .query(""" - query Test(${"$"}input: [CustomScalar]) { - test(input: ${"$"}input) - } - """.trimIndent()) - .variables(mapOf("input" to listOf("Foo", "Bar"))) - .context(Object()) - .root(Object())) + val result = gql.execute(ExecutionInput.newExecutionInput().query( + """ + query Test(${"$"}input: [CustomScalar]) { + test(input: ${"$"}input) + } + """) + .variables(mapOf("input" to listOf("Foo", "Bar"))) + .context(Object()) + .root(Object())) Assert.assertEquals(6, result.getData>()["test"]) } @@ -190,13 +186,13 @@ class MethodFieldResolverTest { ) as GraphQLQueryResolver val schema = SchemaParser.newParser() - .schemaString(""" - scalar CustomScalar - type Query { - test(input: [CustomScalar]): Int - } - """.trimIndent() - ) + .schemaString( + """ + scalar CustomScalar + type Query { + test(input: [CustomScalar]): Int + } + """) .scalars(customScalarType) .resolvers(resolver) .build() @@ -204,16 +200,15 @@ class MethodFieldResolverTest { val gql = GraphQL.newGraphQL(schema).build() - val result = gql - .execute(ExecutionInput.newExecutionInput() - .query(""" - query Test(${"$"}input: [CustomScalar]) { - test(input: ${"$"}input) - } - """.trimIndent()) - .variables(mapOf("input" to listOf("Foo", "Bar"))) - .context(Object()) - .root(Object())) + val result = gql.execute(ExecutionInput.newExecutionInput().query( + """ + query Test(${"$"}input: [CustomScalar]) { + test(input: ${"$"}input) + } + """) + .variables(mapOf("input" to listOf("Foo", "Bar"))) + .context(Object()) + .root(Object())) Assert.assertEquals(6, result.getData>()["test"]) } diff --git a/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt b/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt index 189a5a7f..0450c90f 100644 --- a/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt @@ -7,7 +7,9 @@ import org.junit.Test class MultiResolverTest { - private val schema: GraphQLSchema = SchemaParser.newParser().schemaString(""" + private val schema: GraphQLSchema = SchemaParser.newParser() + .schemaString( + """ type Query { person: Person } @@ -20,13 +22,13 @@ class MultiResolverTest { type Friend { name: String! } - """.trimIndent()) - .resolvers(QueryWithPersonResolver(), PersonFriendResolver(), PersonNameResolver()) - .build() - .makeExecutableSchema() + """) + .resolvers(QueryWithPersonResolver(), PersonFriendResolver(), PersonNameResolver()) + .build() + .makeExecutableSchema() private val gql: GraphQL = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() @Test fun `multiple resolvers for one data class should resolve methods with arguments`() { diff --git a/src/test/kotlin/graphql/kickstart/tools/NestedInputTypesTest.kt b/src/test/kotlin/graphql/kickstart/tools/NestedInputTypesTest.kt index e7f7279f..feb4bd81 100644 --- a/src/test/kotlin/graphql/kickstart/tools/NestedInputTypesTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/NestedInputTypesTest.kt @@ -8,36 +8,38 @@ class NestedInputTypesTest { @Test fun `nested input types are parsed`() { - val schema = SchemaParser.newParser().schemaString(""" - type Query { - materials(filter: MaterialFilter): [Material!]! - } - - input MaterialFilter { - title: String - requestFilter: RequestFilter - } - - input RequestFilter { - and: [RequestFilter!] - or: [RequestFilter!] - discountTypeFilter: DiscountTypeFilter - } - - input DiscountTypeFilter { - name: String - } - - type Material { - id: ID! - } + val schema = SchemaParser.newParser() + .schemaString( + """ + type Query { + materials(filter: MaterialFilter): [Material!]! + } + + input MaterialFilter { + title: String + requestFilter: RequestFilter + } + + input RequestFilter { + and: [RequestFilter!] + or: [RequestFilter!] + discountTypeFilter: DiscountTypeFilter + } + + input DiscountTypeFilter { + name: String + } + + type Material { + id: ID! + } """) - .resolvers(QueryResolver()) - .build() - .makeExecutableSchema() + .resolvers(QueryResolver()) + .build() + .makeExecutableSchema() val gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() val data = assertNoGraphQlErrors(gql, mapOf("filter" to mapOf("title" to "title", "requestFilter" to mapOf("discountTypeFilter" to mapOf("name" to "discount"))))) { """ query materials(${'$'}filter: MaterialFilter!) { @@ -53,46 +55,48 @@ class NestedInputTypesTest { @Test fun `nested input in extensions are parsed`() { - val schema = SchemaParser.newParser().schemaString(""" - type Query { - materials(filter: MaterialFilter): [Material!]! - } - - input MaterialFilter { - title: String - } - - extend input MaterialFilter { - requestFilter: RequestFilter - } - - input RequestFilter { - and: [RequestFilter!] - or: [RequestFilter!] - discountTypeFilter: DiscountTypeFilter - } - - input DiscountTypeFilter { - name: String - } - - type Material { - id: ID! - } + val schema = SchemaParser.newParser() + .schemaString( + """ + type Query { + materials(filter: MaterialFilter): [Material!]! + } + + input MaterialFilter { + title: String + } + + extend input MaterialFilter { + requestFilter: RequestFilter + } + + input RequestFilter { + and: [RequestFilter!] + or: [RequestFilter!] + discountTypeFilter: DiscountTypeFilter + } + + input DiscountTypeFilter { + name: String + } + + type Material { + id: ID! + } """) - .resolvers(QueryResolver()) - .build() - .makeExecutableSchema() + .resolvers(QueryResolver()) + .build() + .makeExecutableSchema() val gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() val data = assertNoGraphQlErrors(gql, mapOf("filter" to mapOf("title" to "title", "requestFilter" to mapOf("discountTypeFilter" to mapOf("name" to "discount"))))) { """ - query materials(${'$'}filter: MaterialFilter!) { - materials(filter: ${'$'}filter) { - id - } + query materials(${'$'}filter: MaterialFilter!) { + materials(filter: ${'$'}filter) { + id } + } """ } @@ -121,5 +125,4 @@ class NestedInputTypesTest { class DiscountTypeFilter { var name: String? = null } - } diff --git a/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt b/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt index 6d1caea8..2e15d032 100644 --- a/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt @@ -7,7 +7,9 @@ import org.junit.Test class ParameterizedGetterTest { - private val schema: GraphQLSchema = SchemaParser.newParser().schemaString(""" + private val schema: GraphQLSchema = SchemaParser.newParser() + .schemaString( + """ type Query { human: Human } @@ -20,25 +22,25 @@ class ParameterizedGetterTest { type Character { name: String! } - """.trimIndent()) - .resolvers(QueryResolver(), HumanResolver()) - .build() - .makeExecutableSchema() + """) + .resolvers(QueryResolver(), HumanResolver()) + .build() + .makeExecutableSchema() private val gql: GraphQL = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() @Test fun `parameterized query is resolved on data type instead of on its resolver`() { val data = assertNoGraphQlErrors(gql, mapOf("limit" to 10)) { """ - query allFriends(${'$'}limit: Int!) { - human { - allFriends(limit: ${'$'}limit) { - name - } + query allFriends(${'$'}limit: Int!) { + human { + allFriends(limit: ${'$'}limit) { + name } } + } """ } diff --git a/src/test/kotlin/graphql/kickstart/tools/ReactiveTest.kt b/src/test/kotlin/graphql/kickstart/tools/ReactiveTest.kt index 35b53672..40c08d3f 100644 --- a/src/test/kotlin/graphql/kickstart/tools/ReactiveTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/ReactiveTest.kt @@ -23,14 +23,14 @@ class ReactiveTest { .build() val schema = newParser().file("Reactive.graphqls") - .resolvers(Query()) - .options(options) - .build() - .makeExecutableSchema() + .resolvers(Query()) + .options(options) + .build() + .makeExecutableSchema() val gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() assertNoGraphQlErrors(gql) { "query { organization(organizationId: 1) { user { id } } }" diff --git a/src/test/kotlin/graphql/kickstart/tools/RelayConnectionTest.kt b/src/test/kotlin/graphql/kickstart/tools/RelayConnectionTest.kt index 6f7d9a04..919a508d 100644 --- a/src/test/kotlin/graphql/kickstart/tools/RelayConnectionTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/RelayConnectionTest.kt @@ -12,42 +12,44 @@ class RelayConnectionTest { @Test fun `should compile relay schema when not using @connection directive`() { - val schema = SchemaParser.newParser().schemaString(""" - type Query { - users(first: Int, after: String): UserConnection - otherTypes: AnotherTypeConnection - } - - type UserConnection { - edges: [UserEdge!]! - pageInfo: PageInfo! - } - - type UserEdge { - node: User! - } - - type User { - id: ID! - name: String - } - - type PageInfo { - hasNextPage: Boolean - } - - type AnotherTypeConnection { - edges: [AnotherTypeEdge!]! - } - - type AnotherTypeEdge { - node: AnotherType! - } - - type AnotherType { - echo: String - } - """) + val schema = SchemaParser.newParser() + .schemaString( + """ + type Query { + users(first: Int, after: String): UserConnection + otherTypes: AnotherTypeConnection + } + + type UserConnection { + edges: [UserEdge!]! + pageInfo: PageInfo! + } + + type UserEdge { + node: User! + } + + type User { + id: ID! + name: String + } + + type PageInfo { + hasNextPage: Boolean + } + + type AnotherTypeConnection { + edges: [AnotherTypeEdge!]! + } + + type AnotherTypeEdge { + node: AnotherType! + } + + type AnotherType { + echo: String + } + """) .resolvers(QueryResolver()) .build() .makeExecutableSchema() @@ -56,25 +58,26 @@ class RelayConnectionTest { .queryExecutionStrategy(AsyncExecutionStrategy()) .build() - val result = gql.execute(""" - query { - users { - edges { - node { - id - name + val result = gql.execute( + """ + query { + users { + edges { + node { + id + name + } + } } - } - } - otherTypes { - edges { - node { - echo + otherTypes { + edges { + node { + echo + } + } } - } } - } - """) + """) val expected = mapOf( "users" to mapOf( @@ -99,35 +102,35 @@ class RelayConnectionTest { @Test fun `should compile relay schema when using @connection directive`() { val schema = SchemaParser.newParser() - .file("RelayConnection.graphqls") - .resolvers(QueryResolver()) - .dictionary(User::class.java) - .build() - .makeExecutableSchema() + .file("RelayConnection.graphqls") + .resolvers(QueryResolver()) + .dictionary(User::class.java) + .build() + .makeExecutableSchema() val gql = GraphQL.newGraphQL(schema) - .queryExecutionStrategy(AsyncExecutionStrategy()) - .build() + .queryExecutionStrategy(AsyncExecutionStrategy()) + .build() assertNoGraphQlErrors(gql) { """ - query { - users { - edges { + query { + users { + edges { cursor node { - id - name + id + name } - } - pageInfo { + } + pageInfo { hasPreviousPage hasNextPage startCursor endCursor - } } - } + } + } """ } } diff --git a/src/test/kotlin/graphql/kickstart/tools/ResolverMethodsTest.java b/src/test/kotlin/graphql/kickstart/tools/ResolverMethodsTest.java index 5153ee99..8cb3c712 100644 --- a/src/test/kotlin/graphql/kickstart/tools/ResolverMethodsTest.java +++ b/src/test/kotlin/graphql/kickstart/tools/ResolverMethodsTest.java @@ -12,6 +12,7 @@ import static org.junit.Assert.assertTrue; public class ResolverMethodsTest { + // Note: don't convert this code to Kotlin, since it's quite important that the // resolver method is defined with an argument of primitive type, like 'boolean', not 'Boolean': // String testOmittedBoolean(boolean value1, Boolean value2) @@ -20,30 +21,25 @@ public void testOmittedBooleanArgument() { // In this schema, the 'value1' argument is optional, but the Java resolver defines it as 'boolean' // Instead of failing with an error, we expect the argument to be set to the Java default (i.e. false for booleans) GraphQLSchema schema = SchemaParser.newParser() - .schemaString("" + - "type Query {" + - " testOmittedBoolean(value1: Boolean, value2: Boolean): String" + - "}") - .resolvers(new Resolver()) - .build() - .makeExecutableSchema(); + .schemaString("type Query { testOmittedBoolean(value1: Boolean, value2: Boolean): String }") + .resolvers(new Resolver()) + .build() + .makeExecutableSchema(); GraphQL gql = GraphQL.newGraphQL(schema).build(); ExecutionResult result = gql - .execute(ExecutionInput.newExecutionInput() - .query("" + - "query { " + - " testOmittedBoolean" + - "}") - .context(new Object()) - .root(new Object())); + .execute(ExecutionInput.newExecutionInput() + .query("query { testOmittedBoolean }") + .context(new Object()) + .root(new Object())); assertTrue(result.getErrors().isEmpty()); assertEquals("false,null", ((Map) result.getData()).get("testOmittedBoolean")); } static class Resolver implements GraphQLQueryResolver { + @SuppressWarnings("unused") public String testOmittedBoolean(boolean value1, Boolean value2) { return value1 + "," + value2; diff --git a/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt b/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt index 10ebb436..c41fb591 100644 --- a/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt @@ -9,19 +9,20 @@ class SchemaClassScannerTest { @Test fun `scanner handles futures and immediate return types`() { SchemaParser.newParser() - .resolvers(FutureImmediateQuery()) - .schemaString(""" - type Query { - future: Int! - immediate: Int! - } + .resolvers(FutureImmediateQuery()) + .schemaString( + """ + type Query { + future: Int! + immediate: Int! + } """) - .build() + .build() } private class FutureImmediateQuery : GraphQLQueryResolver { fun future(): CompletableFuture = - CompletableFuture.completedFuture(1) + CompletableFuture.completedFuture(1) fun immediate(): Int = 1 } @@ -29,14 +30,15 @@ class SchemaClassScannerTest { @Test fun `scanner handles primitive and boxed return types`() { SchemaParser.newParser() - .resolvers(PrimitiveBoxedQuery()) - .schemaString(""" - type Query { - primitive: Int! - boxed: Int! - } + .resolvers(PrimitiveBoxedQuery()) + .schemaString( + """ + type Query { + primitive: Int! + boxed: Int! + } """) - .build() + .build() } private class PrimitiveBoxedQuery : GraphQLQueryResolver { @@ -48,14 +50,15 @@ class SchemaClassScannerTest { @Test fun `scanner handles different scalars with same java class`() { SchemaParser.newParser() - .resolvers(ScalarDuplicateQuery()) - .schemaString(""" - type Query { - string: String! - id: ID! - } + .resolvers(ScalarDuplicateQuery()) + .schemaString( + """ + type Query { + string: String! + id: ID! + } """) - .build() + .build() } private class ScalarDuplicateQuery : GraphQLQueryResolver { @@ -66,18 +69,19 @@ class SchemaClassScannerTest { @Test fun `scanner handles interfaces referenced by objects that aren't explicitly used`() { val schema = SchemaParser.newParser() - .resolvers(InterfaceMissingQuery()) - .schemaString(""" - interface Interface { - id: ID! - } - - type Query implements Interface { - id: ID! - } + .resolvers(InterfaceMissingQuery()) + .schemaString( + """ + interface Interface { + id: ID! + } + + type Query implements Interface { + id: ID! + } """) - .build() - .makeExecutableSchema() + .build() + .makeExecutableSchema() assert(schema.additionalTypes.find { it is GraphQLInterfaceType } != null) } @@ -89,26 +93,27 @@ class SchemaClassScannerTest { @Test fun `scanner handles input types that reference other input types`() { val schema = SchemaParser.newParser() - .resolvers(MultipleInputTypeQuery()) - .schemaString(""" - input FirstInput { - id: String! - second: SecondInput! - third: ThirdInput! - } - input SecondInput { - id: String! - } - input ThirdInput { - id: String! - } - - type Query { - test(input: FirstInput): String! - } + .resolvers(MultipleInputTypeQuery()) + .schemaString( + """ + input FirstInput { + id: String! + second: SecondInput! + third: ThirdInput! + } + input SecondInput { + id: String! + } + input ThirdInput { + id: String! + } + + type Query { + test(input: FirstInput): String! + } """) - .build() - .makeExecutableSchema() + .build() + .makeExecutableSchema() assert(schema.additionalTypes.count { it is GraphQLInputType } == 3) } @@ -136,60 +141,62 @@ class SchemaClassScannerTest { @Test fun `scanner handles input types extensions`() { val schema = SchemaParser.newParser() - .schemaString(""" - type Query { test: Boolean } - - type Mutation { - save(input: UserInput!): Boolean - } - - input UserInput { - name: String - } - - extend input UserInput { - password: String - } + .schemaString( + """ + type Query { test: Boolean } + + type Mutation { + save(input: UserInput!): Boolean + } + + input UserInput { + name: String + } + + extend input UserInput { + password: String + } """) - .resolvers( - object : GraphQLMutationResolver { - fun save(map: Map<*, *>): Boolean = true - }, - object : GraphQLQueryResolver { - fun test(): Boolean = true - } - ) - .build() - .makeExecutableSchema() + .resolvers( + object : GraphQLMutationResolver { + fun save(map: Map<*, *>): Boolean = true + }, + object : GraphQLQueryResolver { + fun test(): Boolean = true + } + ) + .build() + .makeExecutableSchema() assert(schema.additionalTypes - .filterIsInstance() - .flatMap { it.extensionDefinitions } - .count() == 1 + .filterIsInstance() + .flatMap { it.extensionDefinitions } + .count() == 1 ) } @Test fun `scanner allows multiple return types for custom scalars`() { val schema = SchemaParser.newParser() - .resolvers(ScalarsWithMultipleTypes()) - .scalars(GraphQLScalarType.newScalar() - .name("UUID") - .description("Test scalars with duplicate types") - .coercing(object : Coercing { - override fun serialize(dataFetcherResult: Any?): Any? = null - override fun parseValue(input: Any?): Any? = null - override fun parseLiteral(input: Any?): Any? = null - }).build()) - .schemaString(""" - scalar UUID - - type Query { - first: UUID - second: UUID - } + .resolvers(ScalarsWithMultipleTypes()) + .scalars(GraphQLScalarType.newScalar() + .name("UUID") + .description("Test scalars with duplicate types") + .coercing(object : Coercing { + override fun serialize(dataFetcherResult: Any?): Any? = null + override fun parseValue(input: Any?): Any? = null + override fun parseLiteral(input: Any?): Any? = null + }).build()) + .schemaString( + """ + scalar UUID + + type Query { + first: UUID + second: UUID + } """) - .build() - .makeExecutableSchema() + .build() + .makeExecutableSchema() assert(schema.typeMap.containsKey("UUID")) } @@ -202,31 +209,32 @@ class SchemaClassScannerTest { @Test fun `scanner handles multiple interfaces that are not used as field types`() { val schema = SchemaParser.newParser() - .resolvers(MultipleInterfaces()) - .schemaString(""" - type Query { - query1: NamedResourceImpl - query2: VersionedResourceImpl - } - - interface NamedResource { - name: String! - } - - interface VersionedResource { - version: Int! - } - - type NamedResourceImpl implements NamedResource { - name: String! - } - - type VersionedResourceImpl implements VersionedResource { - version: Int! - } + .resolvers(MultipleInterfaces()) + .schemaString( + """ + type Query { + query1: NamedResourceImpl + query2: VersionedResourceImpl + } + + interface NamedResource { + name: String! + } + + interface VersionedResource { + version: Int! + } + + type NamedResourceImpl implements NamedResource { + name: String! + } + + type VersionedResourceImpl implements VersionedResource { + version: Int! + } """) - .build() - .makeExecutableSchema() + .build() + .makeExecutableSchema() assert(schema.additionalTypes.count { it is GraphQLInterfaceType } == 2) } @@ -255,24 +263,25 @@ class SchemaClassScannerTest { @Test fun `scanner handles interface implementation that is not used as field type`() { val schema = SchemaParser.newParser() - // uncommenting the line below makes the test succeed - .dictionary(InterfaceImplementation.NamedResourceImpl::class) - .resolvers(InterfaceImplementation()) - .schemaString(""" - type Query { - query1: NamedResource - } - - interface NamedResource { - name: String! - } - - type NamedResourceImpl implements NamedResource { - name: String! - } + // uncommenting the line below makes the test succeed + .dictionary(InterfaceImplementation.NamedResourceImpl::class) + .resolvers(InterfaceImplementation()) + .schemaString( + """ + type Query { + query1: NamedResource + } + + interface NamedResource { + name: String! + } + + type NamedResourceImpl implements NamedResource { + name: String! + } """) - .build() - .makeExecutableSchema() + .build() + .makeExecutableSchema() assert(schema.additionalTypes.count { it is GraphQLInterfaceType } == 1) } @@ -290,33 +299,34 @@ class SchemaClassScannerTest { @Test fun `scanner handles custom scalars when matching input types`() { val customMap = GraphQLScalarType.newScalar() - .name("customMap") - .coercing(object : Coercing, Map> { - override fun serialize(dataFetcherResult: Any?): Map = mapOf() - override fun parseValue(input: Any?): Map = mapOf() - override fun parseLiteral(input: Any?): Map = mapOf() - }).build() + .name("customMap") + .coercing(object : Coercing, Map> { + override fun serialize(dataFetcherResult: Any?): Map = mapOf() + override fun parseValue(input: Any?): Map = mapOf() + override fun parseLiteral(input: Any?): Map = mapOf() + }).build() val schema = SchemaParser.newParser() - .resolvers(object : GraphQLQueryResolver { - fun hasRawScalar(rawScalar: Map): Boolean = true - fun hasMapField(mapField: HasMapField): Boolean = true - }) - .scalars(customMap) - .schemaString(""" - type Query { - hasRawScalar(customMap: customMap): Boolean - hasMapField(mapField: HasMapField): Boolean - } - - input HasMapField { - map: customMap - } - - scalar customMap + .resolvers(object : GraphQLQueryResolver { + fun hasRawScalar(rawScalar: Map): Boolean = true + fun hasMapField(mapField: HasMapField): Boolean = true + }) + .scalars(customMap) + .schemaString( + """ + type Query { + hasRawScalar(customMap: customMap): Boolean + hasMapField(mapField: HasMapField): Boolean + } + + input HasMapField { + map: customMap + } + + scalar customMap """) - .build() - .makeExecutableSchema() + .build() + .makeExecutableSchema() assert(schema.typeMap.containsKey("customMap")) } @@ -328,24 +338,25 @@ class SchemaClassScannerTest { @Test fun `scanner allows class to be used for object type and input object type`() { val schema = SchemaParser.newParser() - .resolvers(object : GraphQLQueryResolver { - fun test(pojo: Pojo): Pojo = pojo - }) - .schemaString(""" - type Query { - test(inPojo: InPojo): OutPojo - } - - input InPojo { - name: String - } - - type OutPojo { - name: String - } + .resolvers(object : GraphQLQueryResolver { + fun test(pojo: Pojo): Pojo = pojo + }) + .schemaString( + """ + type Query { + test(inPojo: InPojo): OutPojo + } + + input InPojo { + name: String + } + + type OutPojo { + name: String + } """) - .build() - .makeExecutableSchema() + .build() + .makeExecutableSchema() assert(schema.additionalTypes.count() == 2) } @@ -357,31 +368,32 @@ class SchemaClassScannerTest { @Test fun `scanner should handle nested types in input types`() { val schema = SchemaParser.newParser() - .schemaString(""" - schema { - query: Query - } - - type Query { - animal: Animal - } - - interface Animal { - type: ComplexType - } - - type Dog implements Animal { - type: ComplexType - } - - type ComplexType { - id: String - } + .schemaString( + """ + schema { + query: Query + } + + type Query { + animal: Animal + } + + interface Animal { + type: ComplexType + } + + type Dog implements Animal { + type: ComplexType + } + + type ComplexType { + id: String + } """) - .resolvers(NestedInterfaceTypeQuery()) - .dictionary(NestedInterfaceTypeQuery.Dog::class) - .build() - .makeExecutableSchema() + .resolvers(NestedInterfaceTypeQuery()) + .dictionary(NestedInterfaceTypeQuery.Dog::class) + .build() + .makeExecutableSchema() assert(schema.additionalTypes.count() == 3) } @@ -401,36 +413,37 @@ class SchemaClassScannerTest { @Test fun `scanner should handle unused types when option is true`() { val schema = SchemaParser.newParser() - .schemaString(""" - # Let's say this is the Products service from Apollo Federation Introduction - - type Query { - allProducts: [Product] - } - - type Product { - name: String - } - - # these directives are defined in the Apollo Federation Specification: - # https://www.apollographql.com/docs/apollo-server/federation/federation-spec/ - type User @key(fields: "id") @extends { - id: ID! @external - recentPurchasedProducts: [Product] - address: Address - } - - type Address { - street: String - } + .schemaString( + """ + # Let's say this is the Products service from Apollo Federation Introduction + + type Query { + allProducts: [Product] + } + + type Product { + name: String + } + + # these directives are defined in the Apollo Federation Specification: + # https://www.apollographql.com/docs/apollo-server/federation/federation-spec/ + type User @key(fields: "id") @extends { + id: ID! @external + recentPurchasedProducts: [Product] + address: Address + } + + type Address { + street: String + } """) - .resolvers(object : GraphQLQueryResolver { - fun allProducts(): List? = null - }) - .options(SchemaParserOptions.newOptions().includeUnusedTypes(true).build()) - .dictionary(User::class) - .build() - .makeExecutableSchema() + .resolvers(object : GraphQLQueryResolver { + fun allProducts(): List? = null + }) + .options(SchemaParserOptions.newOptions().includeUnusedTypes(true).build()) + .dictionary(User::class) + .build() + .makeExecutableSchema() assert(schema.additionalTypes.filterIsInstance().find { it.name == "User" } != null) assert(schema.additionalTypes.filterIsInstance().find { it.name == "Address" } != null) @@ -453,34 +466,35 @@ class SchemaClassScannerTest { @Test fun `scanner should handle unused types with interfaces when option is true`() { val schema = SchemaParser.newParser() - .schemaString(""" - type Query { - whatever: Whatever - } - - type Whatever { - value: String - } - - type Unused { - someInterface: SomeInterface - } - - interface SomeInterface { - value: String - } - - type Implementation implements SomeInterface { - value: String - } + .schemaString( + """ + type Query { + whatever: Whatever + } + + type Whatever { + value: String + } + + type Unused { + someInterface: SomeInterface + } + + interface SomeInterface { + value: String + } + + type Implementation implements SomeInterface { + value: String + } """) - .resolvers(object : GraphQLQueryResolver { - fun whatever(): Whatever? = null - }) - .options(SchemaParserOptions.newOptions().includeUnusedTypes(true).build()) - .dictionary(Unused::class, Implementation::class) - .build() - .makeExecutableSchema() + .resolvers(object : GraphQLQueryResolver { + fun whatever(): Whatever? = null + }) + .options(SchemaParserOptions.newOptions().includeUnusedTypes(true).build()) + .dictionary(Unused::class, Implementation::class) + .build() + .makeExecutableSchema() assert(schema.additionalTypes.filterIsInstance().find { it.name == "Unused" } != null) assert(schema.additionalTypes.filterIsInstance().find { it.name == "SomeInterface" } != null) diff --git a/src/test/kotlin/graphql/kickstart/tools/SchemaParserBuilderTest.kt b/src/test/kotlin/graphql/kickstart/tools/SchemaParserBuilderTest.kt index f9b1a6c7..ded068e9 100644 --- a/src/test/kotlin/graphql/kickstart/tools/SchemaParserBuilderTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/SchemaParserBuilderTest.kt @@ -14,8 +14,8 @@ class SchemaParserBuilderTest(private val schema: String, private val error: Str fun `parser errors should be returned in full`() { try { newParser() - .schemaString(schema) - .build() + .schemaString(schema) + .build() } catch (e: InvalidSyntaxException) { Assert.assertTrue(e.toString().contains(error)) } @@ -26,8 +26,8 @@ class SchemaParserBuilderTest(private val schema: String, private val error: Str @JvmStatic fun data(): Collection> { return listOf( - arrayOf("invalid", "offending token 'invalid' at line 1 column 1"), - arrayOf("type Query {\ninvalid!\n}", "offending token '!' at line 2 column 8") + arrayOf("invalid", "offending token 'invalid' at line 1 column 1"), + arrayOf("type Query {\ninvalid!\n}", "offending token '!' at line 2 column 8") ) } } diff --git a/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt b/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt index 7569f399..2717faa9 100644 --- a/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt @@ -9,7 +9,6 @@ import org.springframework.aop.framework.ProxyFactory import java.io.FileNotFoundException import java.util.concurrent.Future - class SchemaParserTest { private lateinit var builder: SchemaParserBuilder @@ -20,10 +19,11 @@ class SchemaParserTest { @Before fun setup() { builder = SchemaParser.newParser() - .schemaString(""" - type Query { - get(int: Int!): Int! - } + .schemaString( + """ + type Query { + get(int: Int!): Int! + } """) } @@ -35,10 +35,10 @@ class SchemaParserTest { @Test fun `builder doesn't throw FileNotFound exception when file is present`() { SchemaParser.newParser().file("Test.graphqls") - .resolvers(object : GraphQLQueryResolver { - fun getId(): String = "1" - }) - .build() + .resolvers(object : GraphQLQueryResolver { + fun getId(): String = "1" + }) + .build() } @Test(expected = SchemaClassScannerError::class) @@ -49,137 +49,143 @@ class SchemaParserTest { @Test(expected = FieldResolverError::class) fun `parser throws ResolverError when Query resolver is given without correct method`() { SchemaParser.newParser() - .schemaString(""" - type Query { - get(int: Int!): Int! - } + .schemaString( + """ + type Query { + get(int: Int!): Int! + } """) - .resolvers(object : GraphQLQueryResolver {}) - .build() - .makeExecutableSchema() + .resolvers(object : GraphQLQueryResolver {}) + .build() + .makeExecutableSchema() } @Test fun `parser should parse correctly when Query resolver is given`() { SchemaParser.newParser() - .schemaString(""" - type Query { - get(int: Int!): Int! - } + .schemaString( + """ + type Query { + get(int: Int!): Int! + } """) - .resolvers(object : GraphQLQueryResolver { - fun get(i: Int): Int = i - }) - .build() - .makeExecutableSchema() + .resolvers(object : GraphQLQueryResolver { + fun get(i: Int): Int = i + }) + .build() + .makeExecutableSchema() } @Test fun `parser should parse correctly when multiple query resolvers are given`() { SchemaParser.newParser() - .schemaString(""" - type Obj { - name: String - } - - type AnotherObj { - key: String - } - - type Query { - obj: Obj - anotherObj: AnotherObj - } + .schemaString( + """ + type Obj { + name: String + } + + type AnotherObj { + key: String + } + + type Query { + obj: Obj + anotherObj: AnotherObj + } """) - .resolvers(object : GraphQLQueryResolver { - fun getObj(): Obj = Obj() - }, object : GraphQLQueryResolver { - fun getAnotherObj(): AnotherObj = AnotherObj() - }) - .build() - .makeExecutableSchema() + .resolvers(object : GraphQLQueryResolver { + fun getObj(): Obj = Obj() + }, object : GraphQLQueryResolver { + fun getAnotherObj(): AnotherObj = AnotherObj() + }) + .build() + .makeExecutableSchema() } @Test fun `parser should parse correctly when multiple resolvers for the same data type are given`() { SchemaParser.newParser() - .schemaString(""" - type RootObj { - obj: Obj - anotherObj: AnotherObj - } - - type Obj { - name: String - } - - type AnotherObj { - key: String - } - - type Query { - rootObj: RootObj - } + .schemaString( + """ + type RootObj { + obj: Obj + anotherObj: AnotherObj + } + + type Obj { + name: String + } + + type AnotherObj { + key: String + } + + type Query { + rootObj: RootObj + } """) - .resolvers(object : GraphQLQueryResolver { - fun getRootObj(): RootObj { - return RootObj() - } - }, object : GraphQLResolver { - fun getObj(rootObj: RootObj): Obj { - return Obj() - } - }, object : GraphQLResolver { - fun getAnotherObj(rootObj: RootObj): AnotherObj { - return AnotherObj() - } - }) - .build() - .makeExecutableSchema() + .resolvers(object : GraphQLQueryResolver { + fun getRootObj(): RootObj { + return RootObj() + } + }, object : GraphQLResolver { + fun getObj(rootObj: RootObj): Obj { + return Obj() + } + }, object : GraphQLResolver { + fun getAnotherObj(rootObj: RootObj): AnotherObj { + return AnotherObj() + } + }) + .build() + .makeExecutableSchema() } @Test fun `parser should allow setting custom generic wrappers`() { SchemaParser.newParser() - .schemaString(""" - type Query { - one: Object! - two: Object! - } - - type Object { - name: String! - } + .schemaString( + """ + type Query { + one: Object! + two: Object! + } + + type Object { + name: String! + } """) - .resolvers(object : GraphQLQueryResolver { - fun one(): CustomGenericWrapper? = null - fun two(): Obj? = null - }) - .options(SchemaParserOptions.newOptions().genericWrappers(SchemaParserOptions.GenericWrapper(CustomGenericWrapper::class, 1)).build()) - .build() - .makeExecutableSchema() + .resolvers(object : GraphQLQueryResolver { + fun one(): CustomGenericWrapper? = null + fun two(): Obj? = null + }) + .options(SchemaParserOptions.newOptions().genericWrappers(SchemaParserOptions.GenericWrapper(CustomGenericWrapper::class, 1)).build()) + .build() + .makeExecutableSchema() } @Test(expected = SchemaClassScannerError::class) fun `parser should allow turning off default generic wrappers`() { SchemaParser.newParser() - .schemaString(""" - type Query { - one: Object! - two: Object! - } - - type Object { - toString: String! - } + .schemaString( + """ + type Query { + one: Object! + two: Object! + } + + type Object { + toString: String! + } """) - .resolvers(object : GraphQLQueryResolver { - fun one(): Future? = null - fun two(): Obj? = null - }) - .options(SchemaParserOptions.newOptions().useDefaultGenericWrappers(false).build()) - .build() - .makeExecutableSchema() + .resolvers(object : GraphQLQueryResolver { + fun one(): Future? = null + fun two(): Obj? = null + }) + .options(SchemaParserOptions.newOptions().useDefaultGenericWrappers(false).build()) + .build() + .makeExecutableSchema() } @Test @@ -188,20 +194,21 @@ class SchemaParserTest { expectedEx.expectMessage("Was a type only permitted for object types incorrectly used as an input type, or vice-versa") SchemaParser.newParser() - .schemaString(""" - type Query { - name(filter: Filter): [String] - } - - type Filter { - filter: String - } + .schemaString( + """ + type Query { + name(filter: Filter): [String] + } + + type Filter { + filter: String + } """) - .resolvers(object : GraphQLQueryResolver { - fun name(filter: Filter): List? = null - }) - .build() - .makeExecutableSchema() + .resolvers(object : GraphQLQueryResolver { + fun name(filter: Filter): List? = null + }) + .build() + .makeExecutableSchema() throw AssertionError("should not be called") } @@ -211,49 +218,52 @@ class SchemaParserTest { val resolver = ProxyFactory(ProxiedResolver()).proxy as GraphQLQueryResolver SchemaParser.newParser() - .schemaString(""" - type Query { - test: [String] - } + .schemaString( + """ + type Query { + test: [String] + } """) - .resolvers(resolver) - .build() + .resolvers(resolver) + .build() } @Test fun `parser handles enums with overridden toString method`() { SchemaParser.newParser() - .schemaString(""" - enum CustomEnum { - FOO - } - - type Query { - customEnum: CustomEnum - } + .schemaString( + """ + enum CustomEnum { + FOO + } + + type Query { + customEnum: CustomEnum + } """) - .resolvers(object : GraphQLQueryResolver { - fun customEnum(): CustomEnum? = null - }) - .build() - .makeExecutableSchema() + .resolvers(object : GraphQLQueryResolver { + fun customEnum(): CustomEnum? = null + }) + .build() + .makeExecutableSchema() } @Test fun `parser should include source location for field definition`() { val schema = SchemaParser.newParser() - .schemaString(""" - |type Query { - | id: ID! - |} + .schemaString( + """ + |type Query { + | id: ID! + |} """.trimMargin()) - .resolvers(QueryWithIdResolver()) - .build() - .makeExecutableSchema() + .resolvers(QueryWithIdResolver()) + .build() + .makeExecutableSchema() val sourceLocation = schema.getObjectType("Query") - .getFieldDefinition("id") - .definition.sourceLocation + .getFieldDefinition("id") + .definition.sourceLocation assert(sourceLocation != null) assert(sourceLocation.line == 2) assert(sourceLocation.column == 5) @@ -263,14 +273,14 @@ class SchemaParserTest { @Test fun `parser should include source location for field definition when loaded from single classpath file`() { val schema = SchemaParser.newParser() - .file("Test.graphqls") - .resolvers(QueryWithIdResolver()) - .build() - .makeExecutableSchema() + .file("Test.graphqls") + .resolvers(QueryWithIdResolver()) + .build() + .makeExecutableSchema() val sourceLocation = schema.getObjectType("Query") - .getFieldDefinition("id") - .definition.sourceLocation + .getFieldDefinition("id") + .definition.sourceLocation assert(sourceLocation != null) assert(sourceLocation.line == 2) assert(sourceLocation.column == 3) @@ -279,112 +289,117 @@ class SchemaParserTest { @Test fun `support enum types if only used as input type`() { - SchemaParser.newParser().schemaString(""" - type Query { test: Boolean } - - type Mutation { - save(input: SaveInput!): Boolean - } - - input SaveInput { - type: EnumType! - } - - enum EnumType { - TEST - } - """.trimIndent()) - .resolvers(object : GraphQLMutationResolver { - fun save(input: SaveInput): Boolean = false - inner class SaveInput { - var type: EnumType? = null; - } - - }, object : GraphQLQueryResolver { - fun test(): Boolean = false - }) - .dictionary(EnumType::class) - .build() - .makeExecutableSchema() + SchemaParser.newParser() + .schemaString( + """ + type Query { test: Boolean } + + type Mutation { + save(input: SaveInput!): Boolean + } + + input SaveInput { + type: EnumType! + } + + enum EnumType { + TEST + } + """) + .resolvers(object : GraphQLMutationResolver { + fun save(input: SaveInput): Boolean = false + inner class SaveInput { + var type: EnumType? = null; + } + }, object : GraphQLQueryResolver { + fun test(): Boolean = false + }) + .dictionary(EnumType::class) + .build() + .makeExecutableSchema() } @Test fun `support enum types if only used in input Map`() { - SchemaParser.newParser().schemaString(""" - type Query { test: Boolean } - - type Mutation { - save(input: SaveInput!): Boolean - } - - input SaveInput { - age: Int - type: EnumType! - } - - enum EnumType { - TEST - } - """.trimIndent()) - .resolvers(object : GraphQLMutationResolver { - fun save(input: Map<*, *>): Boolean = false - }, object : GraphQLQueryResolver { - fun test(): Boolean = false - }) - .dictionary(EnumType::class) - .build() - .makeExecutableSchema() + SchemaParser.newParser() + .schemaString( + """ + type Query { test: Boolean } + + type Mutation { + save(input: SaveInput!): Boolean + } + + input SaveInput { + age: Int + type: EnumType! + } + + enum EnumType { + TEST + } + """) + .resolvers(object : GraphQLMutationResolver { + fun save(input: Map<*, *>): Boolean = false + }, object : GraphQLQueryResolver { + fun test(): Boolean = false + }) + .dictionary(EnumType::class) + .build() + .makeExecutableSchema() } @Test fun `allow circular relations in input objects`() { - SchemaParser.newParser().schemaString(""" - input A { - id: ID! - b: B - } - input B { - id: ID! - a: A - } - input C { - id: ID! - c: C - } - type Query { test: Boolean } - type Mutation { - test(input: A!): Boolean - testC(input: C!): Boolean - } - """.trimIndent()) - .resolvers(object : GraphQLMutationResolver { - inner class A { - var id: String? = null - var b: B? = null - } - - inner class B { - var id: String? = null - var a: A? = null - } - - inner class C { - var id: String? = null - var c: C? = null - } - - fun test(a: A): Boolean { - return true - } - - fun testC(c: C): Boolean { - return true - } - }, object : GraphQLQueryResolver { - fun test(): Boolean = false - }) - .build() - .makeExecutableSchema() + SchemaParser.newParser() + .schemaString( + """ + input A { + id: ID! + b: B + } + input B { + id: ID! + a: A + } + input C { + id: ID! + c: C + } + type Query { test: Boolean } + type Mutation { + test(input: A!): Boolean + testC(input: C!): Boolean + } + """) + .resolvers(object : GraphQLMutationResolver { + inner class A { + var id: String? = null + var b: B? = null + } + + inner class B { + var id: String? = null + var a: A? = null + } + + inner class C { + var id: String? = null + var c: C? = null + } + + fun test(a: A): Boolean { + return true + } + + fun testC(c: C): Boolean { + return true + } + }, object : GraphQLQueryResolver { + fun test(): Boolean = false + }) + .build() + .makeExecutableSchema() } enum class EnumType { diff --git a/src/test/kotlin/graphql/kickstart/tools/SuperclassResolverTest.kt b/src/test/kotlin/graphql/kickstart/tools/SuperclassResolverTest.kt index 64f238d7..c1f24ae9 100644 --- a/src/test/kotlin/graphql/kickstart/tools/SuperclassResolverTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/SuperclassResolverTest.kt @@ -6,23 +6,25 @@ class SuperclassResolverTest { @Test fun `methods from generic resolvers are resolved`() { - SchemaParser.newParser().schemaString(""" - type Query { - bar: Bar! - } - - type Bar implements Foo{ - value: String - getValueWithSeveralParameters(arg1: Boolean!, arg2: String): String! - } + SchemaParser.newParser() + .schemaString( + """ + type Query { + bar: Bar! + } - interface Foo { - getValueWithSeveralParameters(arg1: Boolean!, arg2: String): String! - } - """) - .resolvers(QueryResolver(), BarResolver()) - .build() - .makeExecutableSchema() + type Bar implements Foo{ + value: String + getValueWithSeveralParameters(arg1: Boolean!, arg2: String): String! + } + + interface Foo { + getValueWithSeveralParameters(arg1: Boolean!, arg2: String): String! + } + """) + .resolvers(QueryResolver(), BarResolver()) + .build() + .makeExecutableSchema() } class QueryResolver : GraphQLQueryResolver { diff --git a/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt b/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt index a6048770..00e3ce1e 100644 --- a/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt +++ b/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt @@ -8,10 +8,10 @@ private val mapper = ObjectMapper() fun assertNoGraphQlErrors(gql: GraphQL, args: Map = mapOf(), context: Any = Object(), closure: () -> String): Map { val result = gql.execute(ExecutionInput.newExecutionInput() - .query(closure.invoke()) - .context(context) - .root(context) - .variables(args)) + .query(closure.invoke()) + .context(context) + .root(context) + .variables(args)) if (result.errors.isNotEmpty()) { throw AssertionError("GraphQL result contained errors!\n${result.errors.map { mapper.writeValueAsString(it) }.joinToString { "\n" }}") diff --git a/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt b/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt index 461c3d45..78de4290 100644 --- a/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt @@ -17,10 +17,10 @@ import java.util.concurrent.Future @RunWith(Suite::class) @Suite.SuiteClasses( - TypeClassMatcherTest.Suit1::class, - TypeClassMatcherTest.Suit2::class, - TypeClassMatcherTest.Suit3::class, - TypeClassMatcherTest.Suit4::class + TypeClassMatcherTest.Suit1::class, + TypeClassMatcherTest.Suit2::class, + TypeClassMatcherTest.Suit3::class, + TypeClassMatcherTest.Suit4::class ) class TypeClassMatcherTest { @@ -31,21 +31,20 @@ class TypeClassMatcherTest { private val customDefinition: TypeDefinition<*> = ObjectTypeDefinition("CustomType") private val unwrappedCustomDefinition: TypeDefinition<*> = ObjectTypeDefinition("UnwrappedGenericCustomType") - private val matcher: TypeClassMatcher = TypeClassMatcher(mapOf( - "CustomType" to customDefinition, - "UnwrappedGenericCustomType" to unwrappedCustomDefinition + "CustomType" to customDefinition, + "UnwrappedGenericCustomType" to unwrappedCustomDefinition )) private val options: SchemaParserOptions = SchemaParserOptions.newOptions().genericWrappers( - GenericWrapper( - GenericCustomType::class.java, - 0 - ), - listCollectionWithTransformer( - GenericCustomListType::class.java, - 0 - ) { x -> x } + GenericWrapper( + GenericCustomType::class.java, + 0 + ), + listCollectionWithTransformer( + GenericCustomListType::class.java, + 0 + ) { x -> x } ).build() private val scanner: FieldResolverScanner = FieldResolverScanner(options) @@ -53,8 +52,8 @@ class TypeClassMatcherTest { private fun createPotentialMatch(methodName: String, graphQLType: Type<*>): TypeClassMatcher.PotentialMatch { return scanner.findFieldResolver(FieldDefinition(methodName, graphQLType), resolver) - .scanForMatches() - .find { it.location == TypeClassMatcher.Location.RETURN_TYPE }!! + .scanForMatches() + .find { it.location == TypeClassMatcher.Location.RETURN_TYPE }!! } private fun list(other: Type<*> = customType): Type<*> = ListType(other) @@ -77,20 +76,20 @@ class TypeClassMatcherTest { @JvmStatic fun data(): Collection> { return listOf( - arrayOf("type", customType), - arrayOf("futureType", customType), - arrayOf("listType", list()), - arrayOf("listListType", list(list())), - arrayOf("futureListType", list()), - arrayOf("listFutureType", list()), - arrayOf("listListFutureType", list(list())), - arrayOf("futureListListType", list(list())), - arrayOf("superType", customType), - arrayOf("superListFutureType", list(nonNull())), - arrayOf("nullableType", customType), - arrayOf("nullableListType", list(nonNull(customType))), - arrayOf("genericCustomType", customType), - arrayOf("genericListType", list()) + arrayOf("type", customType), + arrayOf("futureType", customType), + arrayOf("listType", list()), + arrayOf("listListType", list(list())), + arrayOf("futureListType", list()), + arrayOf("listFutureType", list()), + arrayOf("listListFutureType", list(list())), + arrayOf("futureListListType", list(list())), + arrayOf("superType", customType), + arrayOf("superListFutureType", list(nonNull())), + arrayOf("nullableType", customType), + arrayOf("nullableListType", list(nonNull(customType))), + arrayOf("genericCustomType", customType), + arrayOf("genericListType", list()) ) } } @@ -109,8 +108,8 @@ class TypeClassMatcherTest { @JvmStatic fun data(): Collection> { return listOf( - arrayOf("type", list()), - arrayOf("futureType", list()) + arrayOf("type", list()), + arrayOf("futureType", list()) ) } } @@ -129,9 +128,9 @@ class TypeClassMatcherTest { @JvmStatic fun data(): Collection> { return listOf( - arrayOf("nullableType", nonNull(customType)), - arrayOf("nullableNullableType", customType), - arrayOf("listNullableType", list(customType)) + arrayOf("nullableType", nonNull(customType)), + arrayOf("nullableNullableType", customType), + arrayOf("listNullableType", list(customType)) ) } } From d6f48115cacd48f4c5c2b2fc7ebd6a81b40c03d2 Mon Sep 17 00:00:00 2001 From: Oryan M Date: Wed, 24 Feb 2021 15:57:37 -0500 Subject: [PATCH 5/5] Update assert statements --- .../graphql/kickstart/tools/BuiltInIdTest.kt | 22 +++-- .../graphql/kickstart/tools/DirectiveTest.kt | 6 +- .../graphql/kickstart/tools/EndToEndTest.kt | 92 ++++++++++--------- .../kickstart/tools/EnumDefaultValueTest.kt | 2 +- .../kickstart/tools/EnumListParameterTest.kt | 2 +- .../tools/FieldResolverScannerTest.kt | 10 +- .../MethodFieldResolverDataFetcherTest.kt | 41 ++++----- .../tools/MethodFieldResolverTest.kt | 19 ++-- .../kickstart/tools/MultiResolverTest.kt | 2 +- .../kickstart/tools/NestedInputTypesTest.kt | 4 +- .../tools/ParameterizedGetterTest.kt | 2 +- .../kickstart/tools/RelayConnectionTest.kt | 3 +- .../kickstart/tools/SchemaClassScannerTest.kt | 40 +++++--- .../tools/SchemaParserBuilderTest.kt | 3 +- .../kickstart/tools/SchemaParserTest.kt | 22 ++--- .../graphql/kickstart/tools/TestUtils.kt | 15 +++ .../kickstart/tools/TypeClassMatcherTest.kt | 10 +- 17 files changed, 159 insertions(+), 136 deletions(-) diff --git a/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt b/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt index 255efa85..38f1d150 100644 --- a/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/BuiltInIdTest.kt @@ -45,8 +45,7 @@ class BuiltInIdTest { """ } - assert(data["itemByLongId"] != null) - assert((data["itemByLongId"] as Map<*, *>)["id"] == "1") + assertEquals(data["itemByLongId"], mapOf("id" to "1")) } @Test @@ -61,9 +60,11 @@ class BuiltInIdTest { """ } - assert(data["itemsByLongIds"] != null) - assert(((data["itemsByLongIds"] as List<*>).size == 3)) - assert(((data["itemsByLongIds"] as List<*>)[0] as Map<*, *>)["id"] == "1") + assertEquals(data["itemsByLongIds"], listOf( + mapOf("id" to "1"), + mapOf("id" to "2"), + mapOf("id" to "3") + )) } @Test @@ -78,8 +79,7 @@ class BuiltInIdTest { """ } - assert(data["itemByUuidId"] != null) - assert((data["itemByUuidId"] as Map<*, *>)["id"] == "00000000-0000-0000-0000-000000000000") + assertEquals(data["itemByUuidId"], mapOf("id" to "00000000-0000-0000-0000-000000000000")) } @Test @@ -94,9 +94,11 @@ class BuiltInIdTest { """ } - assert(data["itemsByUuidIds"] != null) - assert(((data["itemsByUuidIds"] as List<*>).size == 3)) - assert(((data["itemsByUuidIds"] as List<*>)[0] as Map<*, *>)["id"] == "00000000-0000-0000-0000-000000000000") + assertEquals(data["itemsByUuidIds"], listOf( + mapOf("id" to "00000000-0000-0000-0000-000000000000"), + mapOf("id" to "11111111-1111-1111-1111-111111111111"), + mapOf("id" to "22222222-2222-2222-2222-222222222222") + )) } class QueryWithLongItemResolver : GraphQLQueryResolver { diff --git a/src/test/kotlin/graphql/kickstart/tools/DirectiveTest.kt b/src/test/kotlin/graphql/kickstart/tools/DirectiveTest.kt index e23f4147..cb47e09d 100644 --- a/src/test/kotlin/graphql/kickstart/tools/DirectiveTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/DirectiveTest.kt @@ -9,10 +9,8 @@ import graphql.schema.DataFetchingEnvironment import graphql.schema.GraphQLFieldDefinition import graphql.schema.idl.SchemaDirectiveWiring import graphql.schema.idl.SchemaDirectiveWiringEnvironment -import org.junit.Assert import org.junit.Ignore import org.junit.Test -import java.util.function.BiFunction class DirectiveTest { @Test @@ -72,7 +70,7 @@ class DirectiveTest { ) ) - Assert.assertEquals(expected, result.getData>>()) + assertEquals(result.getData(), expected) } @Test @@ -140,7 +138,7 @@ class DirectiveTest { val parentType = environment.fieldsContainer val originalDataFetcher = environment.codeRegistry.getDataFetcher(parentType, field) - val wrappedDataFetcher = DataFetcherFactories.wrapDataFetcher(originalDataFetcher, BiFunction { env, value -> + val wrappedDataFetcher = DataFetcherFactories.wrapDataFetcher(originalDataFetcher, { _, value -> (value as? String)?.toUpperCase() }) diff --git a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt index ec363f84..b67d6973 100644 --- a/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/EndToEndTest.kt @@ -55,7 +55,7 @@ class EndToEndTest { """ } - assert(data["addItem"] != null) + assertNotNull(data["addItem"]) } @Test @@ -93,7 +93,7 @@ class EndToEndTest { latch.await(3, TimeUnit.SECONDS) assert(result.errors.isEmpty()) - assert(returnedItem?.get("onItemCreated")?.get("id") == 1) + assertEquals(returnedItem?.get("onItemCreated"), mapOf("id" to 1)) } @Test @@ -109,7 +109,7 @@ class EndToEndTest { """ } - assert(data["itemsByInterface"] != null) + assertNotNull(data["itemsByInterface"]) } @Test @@ -131,7 +131,7 @@ class EndToEndTest { """ } - assert(data["allItems"] != null) + assertNotNull(data["allItems"]) } @Test @@ -154,12 +154,13 @@ class EndToEndTest { """ } - val items = data["nestedUnionItems"] as List> - assert(items[0]["itemId"] == 0) - assert(items[1]["itemId"] == 1) - assert(items[2]["otherItemId"] == 0) - assert(items[3]["otherItemId"] == 1) - assert(items[4]["thirdItemId"] == 100) + assertEquals(data["nestedUnionItems"], listOf( + mapOf("itemId" to 0), + mapOf("itemId" to 1), + mapOf("otherItemId" to 0), + mapOf("otherItemId" to 1), + mapOf("thirdItemId" to 100) + )) } @Test @@ -174,7 +175,7 @@ class EndToEndTest { """ } - assert(data["itemByUUID"] != null) + assertNotNull(data["itemByUUID"]) } @Test @@ -187,7 +188,7 @@ class EndToEndTest { """ } - assert((data["echoFiles"] as ArrayList).joinToString(",") == "Hello,World") + assertEquals(data["echoFiles"], listOf("Hello", "World")) } @Test @@ -203,7 +204,7 @@ class EndToEndTest { """ } - assert(data["propertyHashMapItems"] == listOf(mapOf("name" to "bob", "age" to 55))) + assertEquals(data["propertyHashMapItems"], listOf(mapOf("name" to "bob", "age" to 55))) } @Test @@ -219,7 +220,7 @@ class EndToEndTest { """ } - assert(data["propertySortedMapItems"] == listOf( + assertEquals(data["propertySortedMapItems"], listOf( mapOf("name" to "Arthur", "age" to 76), mapOf("name" to "Jane", "age" to 28) )) @@ -245,7 +246,7 @@ class EndToEndTest { """ } - assert(data["propertyMapWithComplexItems"] == listOf(mapOf("nameId" to mapOf("id" to 150), "age" to 72))) + assertEquals(data["propertyMapWithComplexItems"], listOf(mapOf("nameId" to mapOf("id" to 150), "age" to 72))) } // This behavior is consistent with PropertyDataFetcher @@ -262,7 +263,7 @@ class EndToEndTest { """ } - assert(data["propertyMapMissingNamePropItems"] == listOf(mapOf("name" to null, "age" to 55))) + assertEquals(data["propertyMapMissingNamePropItems"], listOf(mapOf("name" to null, "age" to 55))) } // In this test a dictonary entry for the schema type NestedComplexMapItem is defined @@ -286,7 +287,7 @@ class EndToEndTest { """ } - assert(data["propertyMapWithNestedComplexItems"] == listOf(mapOf("nested" to mapOf("item" to mapOf("id" to 63)), "age" to 72))) + assertEquals(data["propertyMapWithNestedComplexItems"], listOf(mapOf("nested" to mapOf("item" to mapOf("id" to 63)), "age" to 72))) } @Test @@ -305,8 +306,8 @@ class EndToEndTest { """ } - assert((data["missing"] as List<*>).size > 1) - assert((data["present"] as List<*>).size == 1) + assertEquals(data["missing"], listOf(mapOf("id" to 0), mapOf("id" to 1))) + assertEquals(data["present"], listOf(mapOf("id" to 0))) } @Test @@ -325,8 +326,8 @@ class EndToEndTest { """ } - assert((data["missing"] as List<*>).size > 1) - assert((data["present"] as List<*>).size == 1) + assertEquals(data["missing"], listOf(mapOf("id" to 0), mapOf("id" to 1))) + assertEquals(data["present"], listOf(mapOf("id" to 0))) } @Test @@ -345,8 +346,8 @@ class EndToEndTest { """ } - assert(data["missing"] == null) - assert(data["present"] != null) + assertNull(data["missing"]) + assertNotNull(data["present"]) } @Test @@ -381,7 +382,7 @@ class EndToEndTest { """ } - assert(data["__type"] != null) + assertNotNull(data["__type"]) } @Test @@ -398,8 +399,8 @@ class EndToEndTest { """ } - assert((data as Map<*, *>).containsKey("complexNullableType")) - assert(data["complexNullableType"] == null) + assert(data.containsKey("complexNullableType")) + assertNull(data["complexNullableType"]) } @Test @@ -412,7 +413,7 @@ class EndToEndTest { """ } - assert(data["complexInputType"] != null) + assertNotNull(data["complexInputType"]) } @Test @@ -428,9 +429,10 @@ class EndToEndTest { """ } - assert(data["extendedType"] != null) - assert((data["extendedType"] as Map<*, *>)["first"] != null) - assert((data["extendedType"] as Map<*, *>)["second"] != null) + assertEquals(data["extendedType"], mapOf( + "first" to "test", + "second" to "test" + )) } @Test @@ -443,7 +445,7 @@ class EndToEndTest { """ } - assert(data["propertyField"] != null) + assertNotNull(data["propertyField"]) } @Test @@ -456,7 +458,7 @@ class EndToEndTest { """ } - assert(data["enumInputType"] == "TYPE_2") + assertEquals(data["enumInputType"], "TYPE_2") } @Test @@ -469,7 +471,7 @@ class EndToEndTest { """ } - assert(data["customScalarMapInputType"] == mapOf("test" to "me")) + assertEquals(data["customScalarMapInputType"], mapOf("test" to "me")) } @Test @@ -482,7 +484,7 @@ class EndToEndTest { """ } - assert(data["saveUser"] == "John/secret") + assertEquals(data["saveUser"], "John/secret") } @Test @@ -497,7 +499,7 @@ class EndToEndTest { """ } - assert(data["itemWithGenericProperties"] == mapOf("keys" to listOf("A", "B"))) + assertEquals(data["itemWithGenericProperties"], mapOf("keys" to listOf("A", "B"))) } @Test @@ -512,7 +514,7 @@ class EndToEndTest { """ } - assert(data["itemByBuiltInId"] != null) + assertNotNull(data["itemByBuiltInId"]) } @Test @@ -527,7 +529,7 @@ class EndToEndTest { """ } - assert((data["dataFetcherResult"] as Map<*, *>)["name"] == "item1") + assertEquals(data["dataFetcherResult"], mapOf("name" to "item1")) } @Test @@ -543,7 +545,7 @@ class EndToEndTest { """ } - assert(data["coroutineItems"] == listOf( + assertEquals(data["coroutineItems"], listOf( mapOf("id" to 0, "name" to "item1"), mapOf("id" to 1, "name" to "item2") )) @@ -573,7 +575,7 @@ class EndToEndTest { val subscriberResult = subscriber.requestNextElement() as ExecutionResultImpl val subscriberData = subscriberResult.getData() as Map>? assert(result.errors.isEmpty()) - assert(subscriberData?.get("onItemCreatedCoroutineChannel")?.get("id") == 1) + assertEquals(subscriberData?.get("onItemCreatedCoroutineChannel"), mapOf("id" to 1)) subscriber.expectCompletion() } @@ -601,7 +603,7 @@ class EndToEndTest { val subscriberResult = subscriber.requestNextElement() as ExecutionResultImpl val subscriberData = subscriberResult.getData() as Map>? assert(result.errors.isEmpty()) - assert(subscriberData?.get("onItemCreatedCoroutineChannelAndSuspendFunction")?.get("id") == 1) + assertEquals(subscriberData?.get("onItemCreatedCoroutineChannelAndSuspendFunction"), mapOf("id" to 1)) subscriber.expectCompletion() } @@ -617,7 +619,10 @@ class EndToEndTest { """ } - assert((data["arrayItems"] as List<*>).filterIsInstance>().map { it["name"] } == listOf("item1", "item2")) + assertEquals(data["arrayItems"], listOf( + mapOf("name" to "item1"), + mapOf("name" to "item2") + )) } @Test @@ -630,7 +635,8 @@ class EndToEndTest { """ )) - assert(result.errors.size == 1) - assert((result.errors[0] as ExceptionWhileDataFetching).exception is IllegalArgumentException) + assertEquals(result.errors.size, 1) + val exceptionWhileDataFetching = result.errors[0] as ExceptionWhileDataFetching + assert(exceptionWhileDataFetching.exception is IllegalArgumentException) } } diff --git a/src/test/kotlin/graphql/kickstart/tools/EnumDefaultValueTest.kt b/src/test/kotlin/graphql/kickstart/tools/EnumDefaultValueTest.kt index ca83b325..32b1781c 100644 --- a/src/test/kotlin/graphql/kickstart/tools/EnumDefaultValueTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/EnumDefaultValueTest.kt @@ -41,7 +41,7 @@ class EnumDefaultValueTest { """ } - assert(data["test"] == "createdOn") + assertEquals(data["test"], "createdOn") } class MySortSpecifier { diff --git a/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt b/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt index c14d8e00..d0b0c5cd 100644 --- a/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/EnumListParameterTest.kt @@ -45,7 +45,7 @@ class EnumListParameterTest { """ } - assert((data["countries"] as Collection<*>).isEmpty()) + assertEquals((data["countries"]), emptyList()) } class QueryResolver : GraphQLQueryResolver { diff --git a/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt b/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt index 299eca87..4646ddf4 100644 --- a/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/FieldResolverScannerTest.kt @@ -24,7 +24,7 @@ class FieldResolverScannerTest { val result1 = scanner.findFieldResolver(FieldDefinition("field1", TypeName("String")), resolver) val result2 = scanner.findFieldResolver(FieldDefinition("field2", TypeName("String")), resolver) - assert(result1.search.source != result2.search.source) + assertNotEquals(result1.search.source, result2.search.source) } @Test(expected = FieldResolverError::class) @@ -63,12 +63,12 @@ class FieldResolverScannerTest { @Test fun `scanner prefers concrete resolver`() { - val resolver = DataClassResolverInfo(Kayak::class.java); + val resolver = DataClassResolverInfo(Kayak::class.java) val meta = scanner.findFieldResolver(FieldDefinition("information", TypeName("VehicleInformation")), resolver) assert(meta is MethodFieldResolver) - assert((meta as MethodFieldResolver).method.returnType == BoatInformation::class.java) + assertEquals((meta as MethodFieldResolver).method.returnType, BoatInformation::class.java) } @Test @@ -78,7 +78,7 @@ class FieldResolverScannerTest { val meta = scanner.findFieldResolver(FieldDefinition("hull_type", TypeName("HullType")), resolver) assert(meta is MethodFieldResolver) - assert((meta as MethodFieldResolver).method.returnType == HullType::class.java) + assertEquals((meta as MethodFieldResolver).method.returnType, HullType::class.java) } class RootQuery1 : GraphQLQueryResolver { @@ -107,7 +107,7 @@ class FieldResolverScannerTest { private var name: String = "name" } - class User {} + class User class GenericQuery : GraphQLQueryResolver { fun getUsers(): Connection { diff --git a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt index c4b6d95e..564c7e3e 100644 --- a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt @@ -17,7 +17,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.ReceiveChannel -import org.junit.Assert import org.junit.Test import org.reactivestreams.Publisher import org.reactivestreams.tck.TestEnvironment @@ -37,12 +36,12 @@ class MethodFieldResolverDataFetcherTest { // expect @Suppress("UNCHECKED_CAST") val future = resolver.get(createEnvironment(DataClass())) as CompletableFuture - Assert.assertTrue(future.get()) + assert(future.get()) } class SuspendClass : GraphQLResolver { - val dispatcher = Dispatchers.IO - val job = Job() + private val dispatcher = Dispatchers.IO + private val job = Job() @ExperimentalCoroutinesApi val options = SchemaParserOptions.Builder() @@ -69,11 +68,11 @@ class MethodFieldResolverDataFetcherTest { val publisher = resolver.get(createEnvironment(DataClass())) as Publisher val subscriber = TestEnvironment().newManualSubscriber(publisher) - Assert.assertEquals("A", subscriber.requestNextElement()) + assertEquals(subscriber.requestNextElement(), "A") subscriber.cancel() Thread.sleep(100) - Assert.assertTrue(doubleChannel.channel.isClosedForReceive) + assert(doubleChannel.channel.isClosedForReceive) } class DoubleChannel : GraphQLResolver { @@ -100,7 +99,7 @@ class MethodFieldResolverDataFetcherTest { val publisher = resolver.get(createEnvironment(DataClass())) as Publisher val subscriber = TestEnvironment().newManualSubscriber(publisher) - Assert.assertEquals("A", subscriber.requestNextElement()) + assertEquals(subscriber.requestNextElement(), "A") subscriber.expectErrorWithMessage(IllegalStateException::class.java, "Channel error") } @@ -125,14 +124,14 @@ class MethodFieldResolverDataFetcherTest { fun getName(dataClass: DataClass): String = name }) - assert(resolver.get(createEnvironment(DataClass())) == name) + assertEquals(resolver.get(createEnvironment(DataClass())), name) } @Test fun `data fetcher uses data class methods if no resolver method is given`() { val resolver = createFetcher("name", object : GraphQLResolver {}) - assert(resolver.get(createEnvironment(DataClass())) == DataClass().name) + assertEquals(resolver.get(createEnvironment(DataClass())), DataClass().name) } @Test @@ -143,7 +142,7 @@ class MethodFieldResolverDataFetcherTest { fun name(dataClass: DataClass): String = name }) - assert(resolver.get(createEnvironment(DataClass())) == name) + assertEquals(resolver.get(createEnvironment(DataClass())), name) } @Test @@ -153,7 +152,7 @@ class MethodFieldResolverDataFetcherTest { fun getActive(dataClass: DataClass): Boolean = true }) - assert(resolver.get(createEnvironment(DataClass())) == true) + assertEquals(resolver.get(createEnvironment(DataClass())), true) } @Test @@ -163,7 +162,7 @@ class MethodFieldResolverDataFetcherTest { fun getActive(dataClass: DataClass): Boolean? = null }) - assert(resolver.get(createEnvironment(DataClass())) == null) + assertEquals(resolver.get(createEnvironment(DataClass())), null) } @Test @@ -172,7 +171,7 @@ class MethodFieldResolverDataFetcherTest { fun isActive(dataClass: DataClass, env: DataFetchingEnvironment): Boolean = env is DataFetchingEnvironment }) - assert(resolver.get(createEnvironment(DataClass())) == true) + assertEquals(resolver.get(createEnvironment(DataClass())), true) } @Test @@ -182,7 +181,7 @@ class MethodFieldResolverDataFetcherTest { fun isActive(dataClass: DataClass, env: DataFetchingEnvironment): Boolean = env is DataFetchingEnvironment }) - assert(resolver.get(createEnvironment(DataClass(), context = ContextClass())) == true) + assertEquals(resolver.get(createEnvironment(DataClass(), context = ContextClass())), true) } @Test @@ -195,7 +194,7 @@ class MethodFieldResolverDataFetcherTest { } }) - assert(resolver.get(createEnvironment(DataClass(), context = context)) == true) + assertEquals(resolver.get(createEnvironment(DataClass(), context = context)), true) } @Test @@ -203,10 +202,10 @@ class MethodFieldResolverDataFetcherTest { val name = "correct name" val resolver = createFetcher("active", listOf(InputValueDefinition("input", TypeName("InputClass"))), object : GraphQLQueryResolver { fun active(input: InputClass): Boolean = - input is InputClass && input.name == name + input.name == name }) - assert(resolver.get(createEnvironment(arguments = mapOf("input" to mapOf("name" to name)))) == true) + assertEquals(resolver.get(createEnvironment(arguments = mapOf("input" to mapOf("name" to name)))), true) } @Test @@ -214,10 +213,10 @@ class MethodFieldResolverDataFetcherTest { val name = "correct name" val resolver = createFetcher("active", listOf(InputValueDefinition("input", TypeName("Map"))), object : GraphQLQueryResolver { fun active(input: Map<*, *>): Boolean = - input is Map<*, *> && input["name"] == name + input["name"] == name }) - assert(resolver.get(createEnvironment(arguments = mapOf("input" to mapOf("name" to name)))) == true) + assertEquals(resolver.get(createEnvironment(arguments = mapOf("input" to mapOf("name" to name)))), true) } @Test @@ -227,7 +226,7 @@ class MethodFieldResolverDataFetcherTest { message }) - assert(resolver.get(createEnvironment()) == null) + assertEquals(resolver.get(createEnvironment()), null) } @Test(expected = ResolverError::class) @@ -241,7 +240,7 @@ class MethodFieldResolverDataFetcherTest { } class OnDataNameChanged : GraphQLResolver { - val channel = Channel(10) + private val channel = Channel(10) init { channel.offer("A") diff --git a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt index 7c94c29b..d6ed8549 100644 --- a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverTest.kt @@ -5,7 +5,6 @@ import graphql.GraphQL import graphql.language.StringValue import graphql.schema.Coercing import graphql.schema.GraphQLScalarType -import org.junit.Assert import org.junit.Test import java.lang.reflect.InvocationHandler import java.lang.reflect.Method @@ -47,13 +46,11 @@ class MethodFieldResolverTest { .context(Object()) .root(Object())) - val expected = mapOf( + assertEquals(result.getData(), mapOf( "testValue" to "Optional[test-value]", "testOmitted" to "Optional.empty", "testNull" to "Optional.empty" - ) - - Assert.assertEquals(expected, result.getData()) + )) } @Test @@ -92,13 +89,11 @@ class MethodFieldResolverTest { .context(Object()) .root(Object())) - val expected = mapOf( + assertEquals(result.getData(), mapOf( "testValue" to "Optional[test-value]", "testOmitted" to "null", "testNull" to "Optional.empty" - ) - - Assert.assertEquals(expected, result.getData()) + )) } @Test @@ -130,7 +125,7 @@ class MethodFieldResolverTest { .context(Object()) .root(Object())) - Assert.assertEquals(6, result.getData>()["test"]) + assertEquals(result.getData(), mapOf("test" to 6)) } @Test @@ -162,7 +157,7 @@ class MethodFieldResolverTest { .context(Object()) .root(Object())) - Assert.assertEquals(6, result.getData>()["test"]) + assertEquals(result.getData(), mapOf("test" to 6)) } @Test @@ -210,7 +205,7 @@ class MethodFieldResolverTest { .context(Object()) .root(Object())) - Assert.assertEquals(6, result.getData>()["test"]) + assertEquals(result.getData(), mapOf("test" to 6)) } /** diff --git a/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt b/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt index 0450c90f..dcb6f8c1 100644 --- a/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/MultiResolverTest.kt @@ -44,7 +44,7 @@ class MultiResolverTest { """ } - assert(data["person"] != null) + assertNotNull(data["person"]) } class QueryWithPersonResolver : GraphQLQueryResolver { diff --git a/src/test/kotlin/graphql/kickstart/tools/NestedInputTypesTest.kt b/src/test/kotlin/graphql/kickstart/tools/NestedInputTypesTest.kt index feb4bd81..2079cb91 100644 --- a/src/test/kotlin/graphql/kickstart/tools/NestedInputTypesTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/NestedInputTypesTest.kt @@ -50,7 +50,7 @@ class NestedInputTypesTest { """ } - assert((data["materials"] as Collection<*>).isEmpty()) + assertEquals((data["materials"]), emptyList()) } @Test @@ -100,7 +100,7 @@ class NestedInputTypesTest { """ } - assert((data["materials"] as Collection<*>).isEmpty()) + assertEquals((data["materials"]), emptyList()) } class QueryResolver : GraphQLQueryResolver { diff --git a/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt b/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt index 2e15d032..2f6cc754 100644 --- a/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/ParameterizedGetterTest.kt @@ -44,7 +44,7 @@ class ParameterizedGetterTest { """ } - assert(data["human"] != null) + assertNotNull(data["human"]) } class QueryResolver : GraphQLQueryResolver { diff --git a/src/test/kotlin/graphql/kickstart/tools/RelayConnectionTest.kt b/src/test/kotlin/graphql/kickstart/tools/RelayConnectionTest.kt index 919a508d..0906a90e 100644 --- a/src/test/kotlin/graphql/kickstart/tools/RelayConnectionTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/RelayConnectionTest.kt @@ -5,7 +5,6 @@ import graphql.execution.AsyncExecutionStrategy import graphql.relay.Connection import graphql.relay.SimpleListConnection import graphql.schema.DataFetchingEnvironment -import org.junit.Assert import org.junit.Test class RelayConnectionTest { @@ -96,7 +95,7 @@ class RelayConnectionTest { ) ) - Assert.assertEquals(expected, result.getData>>()) + assertEquals(result.getData(), expected) } @Test diff --git a/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt b/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt index c41fb591..16e533ac 100644 --- a/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/SchemaClassScannerTest.kt @@ -44,7 +44,7 @@ class SchemaClassScannerTest { private class PrimitiveBoxedQuery : GraphQLQueryResolver { fun primitive(): Int = 1 - fun boxed(): Int? = 1 + fun boxed(): Int? = null } @Test @@ -83,7 +83,8 @@ class SchemaClassScannerTest { .build() .makeExecutableSchema() - assert(schema.additionalTypes.find { it is GraphQLInterfaceType } != null) + val interfaceType = schema.additionalTypes.find { it is GraphQLInterfaceType } + assertNotNull(interfaceType) } private class InterfaceMissingQuery : GraphQLQueryResolver { @@ -115,7 +116,8 @@ class SchemaClassScannerTest { .build() .makeExecutableSchema() - assert(schema.additionalTypes.count { it is GraphQLInputType } == 3) + val inputTypeCount = schema.additionalTypes.count { it is GraphQLInputType } + assertEquals(inputTypeCount, 3) } private class MultipleInputTypeQuery : GraphQLQueryResolver { @@ -167,11 +169,12 @@ class SchemaClassScannerTest { ) .build() .makeExecutableSchema() - assert(schema.additionalTypes + + val inputTypeExtensionCount = schema.additionalTypes .filterIsInstance() .flatMap { it.extensionDefinitions } - .count() == 1 - ) + .count() + assertEquals(inputTypeExtensionCount, 1) } @Test @@ -236,7 +239,8 @@ class SchemaClassScannerTest { .build() .makeExecutableSchema() - assert(schema.additionalTypes.count { it is GraphQLInterfaceType } == 2) + val interfaceTypeCount = schema.additionalTypes.count { it is GraphQLInterfaceType } + assertEquals(interfaceTypeCount, 2) } class MultipleInterfaces : GraphQLQueryResolver { @@ -283,7 +287,8 @@ class SchemaClassScannerTest { .build() .makeExecutableSchema() - assert(schema.additionalTypes.count { it is GraphQLInterfaceType } == 1) + val interfaceTypeCount = schema.additionalTypes.count { it is GraphQLInterfaceType } + assertEquals(interfaceTypeCount, 1) } class InterfaceImplementation : GraphQLQueryResolver { @@ -358,7 +363,8 @@ class SchemaClassScannerTest { .build() .makeExecutableSchema() - assert(schema.additionalTypes.count() == 2) + val typeCount = schema.additionalTypes.count() + assertEquals(typeCount, 2) } class Pojo { @@ -395,7 +401,8 @@ class SchemaClassScannerTest { .build() .makeExecutableSchema() - assert(schema.additionalTypes.count() == 3) + val typeCount = schema.additionalTypes.count() + assertEquals(typeCount, 3) } class NestedInterfaceTypeQuery : GraphQLQueryResolver { @@ -445,8 +452,9 @@ class SchemaClassScannerTest { .build() .makeExecutableSchema() - assert(schema.additionalTypes.filterIsInstance().find { it.name == "User" } != null) - assert(schema.additionalTypes.filterIsInstance().find { it.name == "Address" } != null) + val objectTypes = schema.additionalTypes.filterIsInstance() + assert(objectTypes.any { it.name == "User" }) + assert(objectTypes.any { it.name == "Address" }) } class Product { @@ -496,9 +504,11 @@ class SchemaClassScannerTest { .build() .makeExecutableSchema() - assert(schema.additionalTypes.filterIsInstance().find { it.name == "Unused" } != null) - assert(schema.additionalTypes.filterIsInstance().find { it.name == "SomeInterface" } != null) - assert(schema.additionalTypes.filterIsInstance().find { it.name == "Implementation" } != null) + val objectTypes = schema.additionalTypes.filterIsInstance() + val interfaceTypes = schema.additionalTypes.filterIsInstance() + assert(objectTypes.any { it.name == "Unused" }) + assert(objectTypes.any { it.name == "Implementation" }) + assert(interfaceTypes.any { it.name == "SomeInterface" }) } class Whatever { diff --git a/src/test/kotlin/graphql/kickstart/tools/SchemaParserBuilderTest.kt b/src/test/kotlin/graphql/kickstart/tools/SchemaParserBuilderTest.kt index ded068e9..d9032e11 100644 --- a/src/test/kotlin/graphql/kickstart/tools/SchemaParserBuilderTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/SchemaParserBuilderTest.kt @@ -2,7 +2,6 @@ package graphql.kickstart.tools import graphql.kickstart.tools.SchemaParser.Companion.newParser import graphql.parser.InvalidSyntaxException -import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -17,7 +16,7 @@ class SchemaParserBuilderTest(private val schema: String, private val error: Str .schemaString(schema) .build() } catch (e: InvalidSyntaxException) { - Assert.assertTrue(e.toString().contains(error)) + assert(e.toString().contains(error)) } } diff --git a/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt b/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt index 2717faa9..06854b6d 100644 --- a/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt @@ -14,7 +14,7 @@ class SchemaParserTest { @Rule @JvmField - var expectedEx = ExpectedException.none() + var expectedEx: ExpectedException = ExpectedException.none() @Before fun setup() { @@ -157,7 +157,7 @@ class SchemaParserTest { } """) .resolvers(object : GraphQLQueryResolver { - fun one(): CustomGenericWrapper? = null + fun one(): CustomGenericWrapper? = null fun two(): Obj? = null }) .options(SchemaParserOptions.newOptions().genericWrappers(SchemaParserOptions.GenericWrapper(CustomGenericWrapper::class, 1)).build()) @@ -264,10 +264,10 @@ class SchemaParserTest { val sourceLocation = schema.getObjectType("Query") .getFieldDefinition("id") .definition.sourceLocation - assert(sourceLocation != null) - assert(sourceLocation.line == 2) - assert(sourceLocation.column == 5) - assert(sourceLocation.sourceName == null) + assertNotNull(sourceLocation) + assertEquals(sourceLocation.line, 2) + assertEquals(sourceLocation.column, 5) + assertNull(sourceLocation.sourceName) } @Test @@ -281,10 +281,10 @@ class SchemaParserTest { val sourceLocation = schema.getObjectType("Query") .getFieldDefinition("id") .definition.sourceLocation - assert(sourceLocation != null) - assert(sourceLocation.line == 2) - assert(sourceLocation.column == 3) - assert(sourceLocation.sourceName == "Test.graphqls") + assertNotNull(sourceLocation) + assertEquals(sourceLocation.line, 2) + assertEquals(sourceLocation.column, 3) + assertEquals(sourceLocation.sourceName, "Test.graphqls") } @Test @@ -309,7 +309,7 @@ class SchemaParserTest { .resolvers(object : GraphQLMutationResolver { fun save(input: SaveInput): Boolean = false inner class SaveInput { - var type: EnumType? = null; + var type: EnumType? = null } }, object : GraphQLQueryResolver { fun test(): Boolean = false diff --git a/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt b/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt index 00e3ce1e..955f7da5 100644 --- a/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt +++ b/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt @@ -20,3 +20,18 @@ fun assertNoGraphQlErrors(gql: GraphQL, args: Map = mapOf(), contex return result.getData() as Map } +fun assertEquals(actual: T, expected: T) { + assert(actual == expected) { "expected:<$expected> but was:<$actual>" } +} + +fun assertNotEquals(actual: T, unexpected: T) { + assert(actual != unexpected) { "Values should be different. Actual: $actual" } +} + +fun assertNull(actual: T) { + assertEquals(actual, null) +} + +fun assertNotNull(actual: T) { + assert(actual != null) +} diff --git a/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt b/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt index 78de4290..4fcb4b34 100644 --- a/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt +++ b/src/test/kotlin/graphql/kickstart/tools/TypeClassMatcherTest.kt @@ -67,8 +67,8 @@ class TypeClassMatcherTest { fun `matcher verifies that nested return type matches graphql definition for method`() { val match = matcher.match(createPotentialMatch(methodName, type)) match as TypeClassMatcher.ValidMatch - assert(match.type == customDefinition) - assert(match.javaType == CustomType::class.java) + assertEquals(match.type, customDefinition) + assertEquals(match.javaType, CustomType::class.java) } companion object { @@ -141,10 +141,10 @@ class TypeClassMatcherTest { fun `matcher allows unwrapped parameterized types as root types`() { val match = matcher.match(createPotentialMatch("genericCustomUnwrappedType", unwrappedCustomType)) match as TypeClassMatcher.ValidMatch - assert(match.type == unwrappedCustomDefinition) + assertEquals(match.type, unwrappedCustomDefinition) val javatype = match.javaType as ParameterizedTypeImpl - assert(javatype.rawType == UnwrappedGenericCustomType::class.java) - assert(javatype.actualTypeArguments.first() == CustomType::class.java) + assertEquals(javatype.rawType, UnwrappedGenericCustomType::class.java) + assertEquals(javatype.actualTypeArguments.first(), CustomType::class.java) } }