Skip to content
This repository was archived by the owner on Apr 1, 2024. It is now read-only.

add introspection tests #17

Merged
merged 5 commits into from
Apr 6, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
added introspection tests
  • Loading branch information
Code-Hex committed Apr 5, 2021
commit 51cf489919476e3ee841dbdcc5280ded7291cba8
90 changes: 90 additions & 0 deletions internal/gqlgen/gqlgen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package gqlgen_test
import (
"context"
"encoding/json"
"io/ioutil"
"path/filepath"
"testing"

Expand All @@ -11,6 +12,9 @@ import (
"github.com/Code-Hex/gqldoc/internal/gqlgen"
"github.com/Code-Hex/gqldoc/internal/introspection"
"github.com/Code-Hex/gqldoc/loader"
gqlparser "github.com/Code-Hex/gqlparser/v2"
"github.com/Code-Hex/gqlparser/v2/ast"
"github.com/goccy/go-yaml"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/pkg/errors"
Expand Down Expand Up @@ -70,3 +74,89 @@ func LoadSchema(filenames ...string) (*introspection.Root, error) {
}
return &res, nil
}

func TestExec(t *testing.T) {
RunSpec(t, "gqlgen_test.yml", func(t *testing.T, schema, query string) ([]byte, error) {
source := &ast.Source{
Name: "test.gql",
Input: schema,
}
AST, gerr := gqlparser.LoadSchema(source)
if gerr != nil {
return nil, gerr
}

oc, err := gqlgen.CreateOperationContext(
gqlgen.Params{
Schema: AST,
Query: query,
Variables: map[string]interface{}{},
},
)
if err != nil {
return nil, err
}

es := &gqlgen.ExecutableSchema{
ParsedSchema: AST,
}

resp := es.Exec(oc)

return resp.Bytes(), nil
})
}

type Features struct {
Tests map[string][]Spec
}

type Spec struct {
Name string
Schema string
Query string
Error error
JSON string
}

func RunSpec(t *testing.T, filename string, f func(t *testing.T, schema, query string) ([]byte, error)) {
b, err := ioutil.ReadFile(filename)
if err != nil {
panic(err)
}
var features Features
err = yaml.Unmarshal(b, &features)
if err != nil {
t.Errorf("unable to load %s: %s", filename, err.Error())
return
}
for name, specs := range features.Tests {
t.Run(name, func(t *testing.T) {
for _, spec := range specs {
t.Run(spec.Name, func(t *testing.T) {
gotJSON, err := f(t, spec.Schema, spec.Query)
if spec.Error != nil {
if diff := cmp.Diff(spec.Error, err); diff != "" {
t.Fatalf("error (-want, +got)\n%s", diff)
}
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if spec.JSON != "" {
var got, want interface{}
if err := json.Unmarshal(gotJSON, &got); err != nil {
t.Fatal(err)
}
if err := json.Unmarshal([]byte(spec.JSON), &want); err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(want, got); diff != "" {
t.Fatalf("json (-want, +got)\n%s", diff)
}
}
})
}
})
}
}
226 changes: 226 additions & 0 deletions internal/gqlgen/gqlgen_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
starwarsSchema: &starwarsSchema |
"""Starwars schema"""
schema {
query: Query
}

"One of the films in the Star Wars Trilogy"
enum Episode {
"Released in 1977."
NEW_HOPE
"Released in 1980."
EMPIRE
"Released in 1983."
JEDI
}

"A character in the Star Wars Trilogy"
interface Character {
"The id of the character."
id: String!
"The name of the character."
name: String
"The friends of the character, or an empty list if they have none."
friends: [Character]
"Which movies they appear in."
appearsIn: [Episode]
"All secrets about their past."
secretBackstory: String
}

"A humanoid creature in the Star Wars universe."
type Human implements Character {
"The id of the human."
id: String!
"The name of the human."
name: String
"The friends of the human, or an empty list if they have none."
friends: [Character]
"Which movies they appear in."
appearsIn: [Episode]
"The home planet of the human, or null if unknown."
homePlanet: String
"Where are they from and how they came to be who they are."
secretBackstory: String
}

"A mechanical creature in the Star Wars universe."
type Droid implements Character {
"The id of the droid."
id: String!
"The name of the droid."
name: String
"The friends of the droid, or an empty list if they have none."
friends: [Character]
"Which movies they appear in."
appearsIn: [Episode]
"Construction date and the name of the designer."
secretBackstory: String
"The primary function of the droid."
primaryFunction: String
}
type Query {
hero(
"""
If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode.
"""
episode: Episode
): Character

human(
"""
id of the human
"""
id: String!
): Human

droid(
"""
id of the droid
"""
id: String!
): Droid
}

tests:
Basic Introspection:
- name: querying the schema for types
schema: *starwarsSchema
query: |
{
__schema {
types {
name
}
}
}
json: |
{"__schema":{"types":[{"name":"Boolean"},{"name":"Character"},{"name":"Droid"},{"name":"Episode"},{"name":"Float"},{"name":"Human"},{"name":"ID"},{"name":"Int"},{"name":"Query"},{"name":"String"},{"name":"__Directive"},{"name":"__DirectiveLocation"},{"name":"__EnumValue"},{"name":"__Field"},{"name":"__InputValue"},{"name":"__Schema"},{"name":"__Type"},{"name":"__TypeKind"}]}}

- name: querying the schema for query type
schema: *starwarsSchema
query: |
{
__schema {
queryType {
name
}
}
}
json: |
{"__schema":{"queryType":{"name":"Query"}}}

- name: querying the schema for a specific type
schema: *starwarsSchema
query: |
{
__type(name: "Droid") {
name
}
}
json: |
{"__type":{"name":"Droid"}}

- name: querying the schema for an object kind
schema: *starwarsSchema
query: |
{
__type(name: "Droid") {
name
kind
}
}
json: |
{"__type":{"name":"Droid","kind":"OBJECT"}}

- name: querying the schema for an interface kind
schema: *starwarsSchema
query: |
{
__type(name: "Character") {
name
kind
}
}
json: |
{"__type":{"name":"Character","kind":"INTERFACE"}}

- name: querying the schema for object fields
schema: *starwarsSchema
query: |
{
__type(name: "Droid") {
name
fields {
name
type {
name
kind
}
}
}
}
json: |
{"__type":{"name":"Droid","fields":[{"name":"id","type":{"name":null,"kind":"NON_NULL"}},{"name":"name","type":{"name":"String","kind":"SCALAR"}},{"name":"friends","type":{"name":null,"kind":"LIST"}},{"name":"appearsIn","type":{"name":null,"kind":"LIST"}},{"name":"secretBackstory","type":{"name":"String","kind":"SCALAR"}},{"name":"primaryFunction","type":{"name":"String","kind":"SCALAR"}}]}}

- name: querying the schema for nested object fields
schema: *starwarsSchema
query: |
{
__type(name: "Droid") {
name
fields {
name
type {
name
kind
ofType {
name
kind
}
}
}
}
}
json: |
{"__type":{"name":"Droid","fields":[{"name":"id","type":{"name":null,"kind":"NON_NULL","ofType":{"name":"String","kind":"SCALAR"}}},{"name":"name","type":{"name":"String","kind":"SCALAR","ofType":null}},{"name":"friends","type":{"name":null,"kind":"LIST","ofType":{"name":"Character","kind":"INTERFACE"}}},{"name":"appearsIn","type":{"name":null,"kind":"LIST","ofType":{"name":"Episode","kind":"ENUM"}}},{"name":"secretBackstory","type":{"name":"String","kind":"SCALAR","ofType":null}},{"name":"primaryFunction","type":{"name":"String","kind":"SCALAR","ofType":null}}]}}

- name: querying the schema for field args
schema: *starwarsSchema
query: |
{
__schema {
queryType {
fields {
name
args {
name
description
type {
name
kind
ofType {
name
kind
}
}
defaultValue
}
}
}
}
}
json: |
{"__schema":{"queryType":{"fields":[{"name":"hero","args":[{"defaultValue":null,"description":"If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode.","name":"episode","type":{"kind":"ENUM","name":"Episode","ofType":null}}]},{"name":"human","args":[{"name":"id","description":"id of the human","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String"}},"defaultValue":null}]},{"name":"droid","args":[{"name":"id","description":"id of the droid","type":{"kind":"NON_NULL","name":null,"ofType":{"kind":"SCALAR","name":"String"}},"defaultValue":null}]}]}}}

- name: querying the schema for documentation
schema: *starwarsSchema
query: |
{
__type(name: "Droid") {
name
description
}
}
json: |
{"__type":{"name":"Droid","description":"A mechanical creature in the Star Wars universe."}}
9 changes: 6 additions & 3 deletions internal/wrapper/schema.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package wrapper

import (
"sort"
"strings"

"github.com/Code-Hex/gqlparser/v2/ast"
Expand All @@ -13,11 +14,13 @@ type Schema struct {
func (s *Schema) Types() []Type {
types := make([]Type, 0, len(s.schema.Types))
for _, typ := range s.schema.Types {
if strings.HasPrefix(typ.Name, "__") {
continue
}
types = append(types, *WrapTypeFromDef(s.schema, typ))
}
sort.Slice(types, func(i, j int) bool {
x := *types[i].Name()
y := *types[j].Name()
return strings.Compare(x, y) < 0
})
return types
}

Expand Down
7 changes: 1 addition & 6 deletions internal/wrapper/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ func WrapTypeFromType(s *ast.Schema, typ *ast.Type) *Type {
if typ == nil {
return nil
}

if !typ.NonNull && typ.NamedType != "" {
return &Type{schema: s, def: s.Types[typ.NamedType]}
}
Expand All @@ -35,15 +34,11 @@ func (t *Type) Kind() string {
if t.typ.NonNull {
return "NON_NULL"
}

if t.typ.Elem != nil {
return "LIST"
}
} else {
return string(t.def.Kind)
}

panic("UNKNOWN")
return string(t.def.Kind)
}

func (t *Type) Name() *string {
Expand Down