Skip to content

Increase code coverage #331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.expediagroup.graphql.federation.execution

import com.expediagroup.graphql.federation.exception.FederatedRequestFailure
import com.expediagroup.graphql.federation.exception.InvalidFederatedRequest
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
Expand All @@ -25,4 +28,62 @@ internal class ResolversKtTest {
coVerify(exactly = 1) { mockResolver.resolve(any()) }
}
}

@Test
fun `resolver still works when registry returns null`() {
val indexedValue = mapOf<String, Any>()
val indexedRequests: List<IndexedValue<Map<String, Any>>> = listOf(IndexedValue(7, indexedValue))
val mockResolver: FederatedTypeResolver<*> = mockk()
coEvery { mockResolver.resolve(any()) } returns listOf("foo")
val registry: FederatedTypeRegistry = mockk()
every { registry.getFederatedResolver(any()) } returns null

runBlocking {
val result = resolveType("MyType", indexedRequests, registry)
assertTrue(result.isNotEmpty())
val mappedValue = result.first()
val response = mappedValue.second
assertTrue(response is InvalidFederatedRequest)
coVerify(exactly = 0) { mockResolver.resolve(any()) }
}
}

@Test
fun `resolver maps the value to a failure when the federated resolver throws an exception`() {
val indexedValue = mapOf<String, Any>()
val indexedRequests: List<IndexedValue<Map<String, Any>>> = listOf(IndexedValue(7, indexedValue))
val mockResolver: FederatedTypeResolver<*> = mockk()
coEvery { mockResolver.resolve(any()) } throws Exception("custom exception")
val registry = FederatedTypeRegistry(mapOf("MyType" to mockResolver))

runBlocking {
val result = resolveType("MyType", indexedRequests, registry)
assertTrue(result.isNotEmpty())
val mappedValue = result.first()
val response = mappedValue.second
assertTrue(response is FederatedRequestFailure)
coVerify(exactly = 1) { mockResolver.resolve(any()) }
}
}

@Test
fun `maps to failure if the result size does not match request size`() {
val indexedValue = mapOf<String, Any>()
val indexedRequests: List<IndexedValue<Map<String, Any>>> = listOf(
IndexedValue(7, indexedValue),
IndexedValue(5, indexedValue)
)
val mockResolver: FederatedTypeResolver<*> = mockk()
coEvery { mockResolver.resolve(any()) } returns listOf("foo")
val registry = FederatedTypeRegistry(mapOf("MyType" to mockResolver))

runBlocking {
val result = resolveType("MyType", indexedRequests, registry)
assertEquals(expected = 2, actual = result.size)
val mappedValue = result.first()
val response = mappedValue.second
assertTrue(response is FederatedRequestFailure)
coVerify(exactly = 1) { mockResolver.resolve(any()) }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.expediagroup.graphql.directives

import graphql.schema.DataFetcher
import graphql.schema.FieldCoordinates
import graphql.schema.GraphQLCodeRegistry
import graphql.schema.GraphQLDirective
import graphql.schema.GraphQLFieldDefinition

/**
* KotlinFieldDirectiveEnvironment holds wiring information for applying directives on GraphQL fields.
*/
class KotlinFieldDirectiveEnvironment(
field: GraphQLFieldDefinition,
fieldDirective: GraphQLDirective,
private val coordinates: FieldCoordinates,
private val codeRegistry: GraphQLCodeRegistry.Builder
) : KotlinSchemaDirectiveEnvironment<GraphQLFieldDefinition>(element = field, directive = fieldDirective) {

/**
* Retrieve current data fetcher associated with the target element.
*/
fun getDataFetcher(): DataFetcher<Any> = codeRegistry.getDataFetcher(coordinates, element)

/**
* Update target element data fetcher.
*/
fun setDataFetcher(newDataFetcher: DataFetcher<Any>) {
codeRegistry.dataFetcher(coordinates, newDataFetcher)
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package com.expediagroup.graphql.directives

import graphql.introspection.Introspection
import graphql.schema.DataFetcher
import graphql.schema.FieldCoordinates
import graphql.schema.GraphQLArgument
import graphql.schema.GraphQLCodeRegistry
import graphql.schema.GraphQLDirective
import graphql.schema.GraphQLDirectiveContainer
import graphql.schema.GraphQLEnumType
Expand Down Expand Up @@ -43,26 +40,3 @@ open class KotlinSchemaDirectiveEnvironment<out T : GraphQLDirectiveContainer>(
else -> false
}
}

/**
* KotlinFieldDirectiveEnvironment holds wiring information for applying directives on GraphQL fields.
*/
class KotlinFieldDirectiveEnvironment(
field: GraphQLFieldDefinition,
fieldDirective: GraphQLDirective,
private val coordinates: FieldCoordinates,
private val codeRegistry: GraphQLCodeRegistry.Builder
) : KotlinSchemaDirectiveEnvironment<GraphQLFieldDefinition>(element = field, directive = fieldDirective) {

/**
* Retrieve current data fetcher associated with the target element.
*/
fun getDataFetcher(): DataFetcher<Any> = codeRegistry.getDataFetcher(coordinates, element)

/**
* Update target element data fetcher.
*/
fun setDataFetcher(newDataFetcher: DataFetcher<Any>) {
codeRegistry.dataFetcher(coordinates, newDataFetcher)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ open class SchemaGenerator(val config: SchemaGeneratorConfig) {
open fun enumType(kClass: KClass<out Enum<*>>) =
enumTypeBuilder.enumType(kClass)

open fun scalarType(type: KType, annotatedAsID: Boolean = false) =
open fun scalarType(type: KType, annotatedAsID: Boolean) =
scalarTypeBuilder.scalarType(type, annotatedAsID)

open fun directives(element: KAnnotatedElement, parentClass: KClass<*>? = null): List<GraphQLDirective> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import kotlin.reflect.full.isSubclassOf

internal class FunctionBuilder(generator: SchemaGenerator) : TypeBuilder(generator) {

internal fun function(fn: KFunction<*>, parentName: String, target: Any? = null, abstract: Boolean = false): GraphQLFieldDefinition {
internal fun function(fn: KFunction<*>, parentName: String, target: Any?, abstract: Boolean): GraphQLFieldDefinition {
val builder = GraphQLFieldDefinition.newFieldDefinition()
val functionName = fn.getFunctionName()
builder.name(functionName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import kotlin.reflect.KType

internal class ScalarBuilder(generator: SchemaGenerator) : TypeBuilder(generator) {

internal fun scalarType(type: KType, annotatedAsID: Boolean = false): GraphQLScalarType? {
internal fun scalarType(type: KType, annotatedAsID: Boolean): GraphQLScalarType? {
val kClass = type.getKClass()

val scalar = when {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.expediagroup.graphql.directives

import graphql.schema.DataFetcher
import graphql.schema.FieldCoordinates
import graphql.schema.GraphQLCodeRegistry
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.Test

internal class KotlinFieldDirectiveEnvironmentTest {

@Test
fun `getDataFetcher calls the code registry with the coordinates and element`() {
val coordinates: FieldCoordinates = mockk()
val codeRegistry: GraphQLCodeRegistry.Builder = mockk()
every { codeRegistry.getDataFetcher(any<FieldCoordinates>(), any()) } returns mockk()
val environment = KotlinFieldDirectiveEnvironment(mockk(), mockk(), coordinates, codeRegistry)

environment.getDataFetcher()

verify(exactly = 1) { codeRegistry.getDataFetcher(eq(coordinates), any()) }
}

@Test
fun `setDataFetcher sets the new dataFetcher at the coordinates`() {
val coordinates: FieldCoordinates = mockk()
val codeRegistry: GraphQLCodeRegistry.Builder = mockk()
every { codeRegistry.dataFetcher(any(), any<DataFetcher<Any>>()) } returns mockk()
val environment = KotlinFieldDirectiveEnvironment(mockk(), mockk(), coordinates, codeRegistry)

val newDataFetcher: DataFetcher<Any> = mockk()
environment.setDataFetcher(newDataFetcher)

verify(exactly = 1) { codeRegistry.dataFetcher(eq(coordinates), eq(newDataFetcher)) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.expediagroup.graphql.directives

import graphql.introspection.Introspection
import graphql.schema.GraphQLArgument
import graphql.schema.GraphQLDirectiveContainer
import graphql.schema.GraphQLEnumType
import graphql.schema.GraphQLEnumValueDefinition
import graphql.schema.GraphQLFieldDefinition
import graphql.schema.GraphQLInputObjectField
import graphql.schema.GraphQLInputObjectType
import graphql.schema.GraphQLInterfaceType
import graphql.schema.GraphQLObjectType
import graphql.schema.GraphQLScalarType
import graphql.schema.GraphQLUnionType
import io.mockk.mockk
import org.junit.jupiter.api.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue

internal class KotlinSchemaDirectiveEnvironmentTest {

@Test
fun `isValid checks the directive locations`() {
val directive = graphql.schema.GraphQLDirective.newDirective()
.name("customDirective")
.validLocations(
Introspection.DirectiveLocation.ARGUMENT_DEFINITION,
Introspection.DirectiveLocation.ENUM,
Introspection.DirectiveLocation.ENUM_VALUE,
Introspection.DirectiveLocation.FIELD_DEFINITION,
Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION,
Introspection.DirectiveLocation.INPUT_OBJECT,
Introspection.DirectiveLocation.INTERFACE,
Introspection.DirectiveLocation.OBJECT,
Introspection.DirectiveLocation.SCALAR,
Introspection.DirectiveLocation.UNION
)
.build()

assertTrue(KotlinSchemaDirectiveEnvironment(mockk<GraphQLArgument>(), directive).isValid())
assertTrue(KotlinSchemaDirectiveEnvironment(mockk<GraphQLEnumType>(), directive).isValid())
assertTrue(KotlinSchemaDirectiveEnvironment(mockk<GraphQLEnumValueDefinition>(), directive).isValid())
assertTrue(KotlinSchemaDirectiveEnvironment(mockk<GraphQLFieldDefinition>(), directive).isValid())
assertTrue(KotlinSchemaDirectiveEnvironment(mockk<GraphQLInputObjectField>(), directive).isValid())
assertTrue(KotlinSchemaDirectiveEnvironment(mockk<GraphQLInputObjectType>(), directive).isValid())
assertTrue(KotlinSchemaDirectiveEnvironment(mockk<GraphQLInterfaceType>(), directive).isValid())
assertTrue(KotlinSchemaDirectiveEnvironment(mockk<GraphQLObjectType>(), directive).isValid())
assertTrue(KotlinSchemaDirectiveEnvironment(mockk<GraphQLScalarType>(), directive).isValid())
assertTrue(KotlinSchemaDirectiveEnvironment(mockk<GraphQLUnionType>(), directive).isValid())
}

@Test
fun `isValid returns false in an invalid directive location`() {
val enumDirective = graphql.schema.GraphQLDirective.newDirective()
.name("enumDirective")
.validLocations(Introspection.DirectiveLocation.ENUM)
.build()
assertFalse(KotlinSchemaDirectiveEnvironment(mockk<GraphQLArgument>(), enumDirective).isValid())
}

@Test
fun `isValid returns false on non valid type`() {
assertFalse(KotlinSchemaDirectiveEnvironment(mockk<GraphQLDirectiveContainer>(), mockk()).isValid())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import com.expediagroup.graphql.testSchemaConfig
import com.expediagroup.graphql.toSchema
import graphql.GraphQL
import graphql.Scalars
import graphql.schema.GraphQLFieldDefinition
import graphql.schema.GraphQLNonNull
import graphql.schema.GraphQLObjectType
import graphql.schema.GraphQLSchema
import org.junit.jupiter.api.Test
import java.net.CookieManager
import java.util.UUID
Expand Down Expand Up @@ -44,6 +46,53 @@ class SchemaGeneratorTest {
assertEquals(1, geo?.get("query")?.get("id"))
}

@Test
fun `SchemaGenerator generates a simple GraphQL schema with default builder`() {
val schemaGenerator = SchemaGenerator(testSchemaConfig)
val schema = schemaGenerator.generate(
queries = listOf(TopLevelObject(QueryObject())),
mutations = listOf(TopLevelObject(MutationObject())),
subscriptions = emptyList()
)

val graphQL = GraphQL.newGraphQL(schema).build()

val result = graphQL.execute(" { query(value: 1) { id } }")
val geo: Map<String, Map<String, Any>>? = result.getData()

assertEquals(1, geo?.get("query")?.get("id"))
}

@Test
fun `SchemaGenerator generates a simple GraphQL schema with predefined builder`() {
val schemaGenerator = SchemaGenerator(testSchemaConfig)
val field = GraphQLFieldDefinition.newFieldDefinition()
.name("foo")
.type(Scalars.GraphQLInt)
.build()
val customObject = GraphQLObjectType.Builder()
.field(field)
.name("MyObject")
.build()
val builder = GraphQLSchema.newSchema()
.additionalType(customObject)

val schema = schemaGenerator.generate(
queries = listOf(TopLevelObject(QueryObject())),
mutations = listOf(TopLevelObject(MutationObject())),
subscriptions = emptyList(),
builder = builder
)

val graphQL = GraphQL.newGraphQL(schema).build()

val result = graphQL.execute(" { query(value: 1) { id } }")
val geo: Map<String, Map<String, Any>>? = result.getData()

assertEquals(1, geo?.get("query")?.get("id"))
assertNotNull(schema.getType("MyObject"))
}

@Test
fun `Schema generator exposes arrays of primitive types as function arguments`() {
val schema = toSchema(queries = listOf(TopLevelObject(QueryWithArray())), config = testSchemaConfig)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,15 @@ internal class GraphQLExtensionsTest {
assertEquals(expected = 3, actual = container.getAllDirectives().size)
}

@Test
fun `safeCast with GraphQLType passes`() {
val type: GraphQLType = mockk()
every { type.name } returns "foo"

val castedType = type.safeCast<GraphQLType>()
assertEquals("foo", castedType.name)
}

@Test
fun `safeCast valid type passes`() {
val type: GraphQLType = GraphQLInterfaceType.newInterface().name("name").description("description").build()
Expand Down
Loading