Skip to content

Commit

Permalink
test: Provide tests for client introspection query (sourcenetwork#1492)
Browse files Browse the repository at this point in the history
## Relevant issue(s)

Resolves sourcenetwork#1099

## Description

Provide test that is somewhat isomorphic to the way that clients
validate the resulting introspection schema.

I've found that the client introspection query is the same accross
Altair, GraphiQL, and Postman clients.

## How has this been tested?

Linux & CI
  • Loading branch information
orpheuslummis authored May 12, 2023
1 parent 4c51cc3 commit a211bee
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types {
...FullType
}
directives {
name
description
locations
args {
...InputValue
}
}
}
}

fragment FullType on __Type {
kind
name
description
fields(includeDeprecated: true) {
name
description
args {
...InputValue
}
type {
...TypeRef
}
isDeprecated
deprecationReason
}
inputFields {
...InputValue
}
interfaces {
...TypeRef
}
enumValues(includeDeprecated: true) {
name
description
isDeprecated
deprecationReason
}
possibleTypes {
...TypeRef
}
}

fragment InputValue on __InputValue {
name
description
type { ...TypeRef }
defaultValue
}

fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}
47 changes: 47 additions & 0 deletions tests/integration/schema/client_introspection/one_many_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2023 Democratized Data Foundation
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package client_introspection

import (
_ "embed"
"testing"

testUtils "github.com/sourcenetwork/defradb/tests/integration"
)

func TestClientIntrospectionWithOneToManySchema(t *testing.T) {
test := testUtils.TestCase{
Actions: []any{
testUtils.SchemaUpdate{
Schema: `
type Book {
name: String
author: Author
}
type Author {
name: String
published: [Book]
}
`,
},
testUtils.ClientIntrospectionRequest{
Request: clientIntrospectionQuery,
// TODO: this should pass without error.
// https://github.com/sourcenetwork/defradb/issues/1502
ExpectedError: "Unknown kind of type: ",
// TODO: this should pass without error.
// https://github.com/sourcenetwork/defradb/issues/1463
// ExpectedError: "InputFields are missing",
},
},
}
testUtils.ExecuteTestCase(t, []string{"Book", "Author"}, test)
}
32 changes: 32 additions & 0 deletions tests/integration/schema/client_introspection/simple_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2023 Democratized Data Foundation
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package client_introspection

import (
_ "embed"
"testing"

testUtils "github.com/sourcenetwork/defradb/tests/integration"
)

//go:embed altair_graphiql_postman_2023.gql
var clientIntrospectionQuery string

func TestClientIntrospectionBasic(t *testing.T) {
test := testUtils.TestCase{
Actions: []any{
testUtils.ClientIntrospectionRequest{
Request: clientIntrospectionQuery,
},
},
}
testUtils.ExecuteTestCase(t, []string{}, test)
}
14 changes: 14 additions & 0 deletions tests/integration/test_case.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,3 +251,17 @@ type IntrospectionRequest struct {
// contains this string.
ExpectedError string
}

// ClientIntrospectionRequest represents a GraphQL client introspection request.
// The GraphQL clients usually use this to fetch the schema state with a default introspection
// query they provide.
type ClientIntrospectionRequest struct {
// The introspection request to use when fetching schema state.
Request string

// Any error expected from the action. Optional.
//
// String can be a partial, and the test will pass if an error is returned that
// contains this string.
ExpectedError string
}
52 changes: 52 additions & 0 deletions tests/integration/utils2.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,9 @@ func executeTestCase(
case IntrospectionRequest:
assertIntrospectionResults(ctx, t, testCase.Description, db, action)

case ClientIntrospectionRequest:
assertClientIntrospectionResults(ctx, t, testCase.Description, db, action)

case WaitForSync:
waitForSync(t, testCase, action, syncChans)

Expand Down Expand Up @@ -1179,6 +1182,55 @@ func assertIntrospectionResults(
return false
}

// Asserts that the client introspection results conform to our expectations.
func assertClientIntrospectionResults(
ctx context.Context,
t *testing.T,
description string,
db client.DB,
action ClientIntrospectionRequest,
) bool {
result := db.ExecRequest(ctx, action.Request)

if AssertErrors(t, description, result.GQL.Errors, action.ExpectedError) {
return true
}
resultantData := result.GQL.Data.(map[string]any)

if len(resultantData) == 0 {
return false
}

// Iterate through all types, validating each type definition.
// Inspired from buildClientSchema.ts from graphql-js,
// which is one way that clients do validate the schema.
types := resultantData["__schema"].(map[string]any)["types"].([]any)

for _, typeData := range types {
typeDef := typeData.(map[string]any)
kind := typeDef["kind"].(string)

switch kind {
case "SCALAR", "INTERFACE", "UNION", "ENUM":
// No validation for these types in this test
case "OBJECT":
fields := typeDef["fields"]
if fields == nil {
t.Errorf("Fields are missing for OBJECT type %v", typeDef["name"])
}
case "INPUT_OBJECT":
inputFields := typeDef["inputFields"]
if inputFields == nil {
t.Errorf("InputFields are missing for INPUT_OBJECT type %v", typeDef["name"])
}
default:
// t.Errorf("Unknown type kind: %v", kind)
}
}

return true
}

// Asserts that the `actual` contains the given `contains` value according to the logic
// described on the [RequestTestCase.ContainsData] property.
func assertContains(t *testing.T, contains map[string]any, actual map[string]any) {
Expand Down

0 comments on commit a211bee

Please sign in to comment.