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

Commit 51cf489

Browse files
committed
added introspection tests
1 parent 1036ba4 commit 51cf489

File tree

4 files changed

+323
-9
lines changed

4 files changed

+323
-9
lines changed

internal/gqlgen/gqlgen_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package gqlgen_test
33
import (
44
"context"
55
"encoding/json"
6+
"io/ioutil"
67
"path/filepath"
78
"testing"
89

@@ -11,6 +12,9 @@ import (
1112
"github.com/Code-Hex/gqldoc/internal/gqlgen"
1213
"github.com/Code-Hex/gqldoc/internal/introspection"
1314
"github.com/Code-Hex/gqldoc/loader"
15+
gqlparser "github.com/Code-Hex/gqlparser/v2"
16+
"github.com/Code-Hex/gqlparser/v2/ast"
17+
"github.com/goccy/go-yaml"
1418
"github.com/google/go-cmp/cmp"
1519
"github.com/google/go-cmp/cmp/cmpopts"
1620
"github.com/pkg/errors"
@@ -70,3 +74,89 @@ func LoadSchema(filenames ...string) (*introspection.Root, error) {
7074
}
7175
return &res, nil
7276
}
77+
78+
func TestExec(t *testing.T) {
79+
RunSpec(t, "gqlgen_test.yml", func(t *testing.T, schema, query string) ([]byte, error) {
80+
source := &ast.Source{
81+
Name: "test.gql",
82+
Input: schema,
83+
}
84+
AST, gerr := gqlparser.LoadSchema(source)
85+
if gerr != nil {
86+
return nil, gerr
87+
}
88+
89+
oc, err := gqlgen.CreateOperationContext(
90+
gqlgen.Params{
91+
Schema: AST,
92+
Query: query,
93+
Variables: map[string]interface{}{},
94+
},
95+
)
96+
if err != nil {
97+
return nil, err
98+
}
99+
100+
es := &gqlgen.ExecutableSchema{
101+
ParsedSchema: AST,
102+
}
103+
104+
resp := es.Exec(oc)
105+
106+
return resp.Bytes(), nil
107+
})
108+
}
109+
110+
type Features struct {
111+
Tests map[string][]Spec
112+
}
113+
114+
type Spec struct {
115+
Name string
116+
Schema string
117+
Query string
118+
Error error
119+
JSON string
120+
}
121+
122+
func RunSpec(t *testing.T, filename string, f func(t *testing.T, schema, query string) ([]byte, error)) {
123+
b, err := ioutil.ReadFile(filename)
124+
if err != nil {
125+
panic(err)
126+
}
127+
var features Features
128+
err = yaml.Unmarshal(b, &features)
129+
if err != nil {
130+
t.Errorf("unable to load %s: %s", filename, err.Error())
131+
return
132+
}
133+
for name, specs := range features.Tests {
134+
t.Run(name, func(t *testing.T) {
135+
for _, spec := range specs {
136+
t.Run(spec.Name, func(t *testing.T) {
137+
gotJSON, err := f(t, spec.Schema, spec.Query)
138+
if spec.Error != nil {
139+
if diff := cmp.Diff(spec.Error, err); diff != "" {
140+
t.Fatalf("error (-want, +got)\n%s", diff)
141+
}
142+
}
143+
if err != nil {
144+
t.Fatalf("unexpected error: %v", err)
145+
}
146+
if spec.JSON != "" {
147+
var got, want interface{}
148+
if err := json.Unmarshal(gotJSON, &got); err != nil {
149+
t.Fatal(err)
150+
}
151+
if err := json.Unmarshal([]byte(spec.JSON), &want); err != nil {
152+
t.Fatal(err)
153+
}
154+
if diff := cmp.Diff(want, got); diff != "" {
155+
t.Fatalf("json (-want, +got)\n%s", diff)
156+
}
157+
}
158+
})
159+
}
160+
})
161+
}
162+
}

internal/gqlgen/gqlgen_test.yml

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
starwarsSchema: &starwarsSchema |
2+
"""Starwars schema"""
3+
schema {
4+
query: Query
5+
}
6+
7+
"One of the films in the Star Wars Trilogy"
8+
enum Episode {
9+
"Released in 1977."
10+
NEW_HOPE
11+
"Released in 1980."
12+
EMPIRE
13+
"Released in 1983."
14+
JEDI
15+
}
16+
17+
"A character in the Star Wars Trilogy"
18+
interface Character {
19+
"The id of the character."
20+
id: String!
21+
"The name of the character."
22+
name: String
23+
"The friends of the character, or an empty list if they have none."
24+
friends: [Character]
25+
"Which movies they appear in."
26+
appearsIn: [Episode]
27+
"All secrets about their past."
28+
secretBackstory: String
29+
}
30+
31+
"A humanoid creature in the Star Wars universe."
32+
type Human implements Character {
33+
"The id of the human."
34+
id: String!
35+
"The name of the human."
36+
name: String
37+
"The friends of the human, or an empty list if they have none."
38+
friends: [Character]
39+
"Which movies they appear in."
40+
appearsIn: [Episode]
41+
"The home planet of the human, or null if unknown."
42+
homePlanet: String
43+
"Where are they from and how they came to be who they are."
44+
secretBackstory: String
45+
}
46+
47+
"A mechanical creature in the Star Wars universe."
48+
type Droid implements Character {
49+
"The id of the droid."
50+
id: String!
51+
"The name of the droid."
52+
name: String
53+
"The friends of the droid, or an empty list if they have none."
54+
friends: [Character]
55+
"Which movies they appear in."
56+
appearsIn: [Episode]
57+
"Construction date and the name of the designer."
58+
secretBackstory: String
59+
"The primary function of the droid."
60+
primaryFunction: String
61+
}
62+
type Query {
63+
hero(
64+
"""
65+
If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode.
66+
"""
67+
episode: Episode
68+
): Character
69+
70+
human(
71+
"""
72+
id of the human
73+
"""
74+
id: String!
75+
): Human
76+
77+
droid(
78+
"""
79+
id of the droid
80+
"""
81+
id: String!
82+
): Droid
83+
}
84+
85+
tests:
86+
Basic Introspection:
87+
- name: querying the schema for types
88+
schema: *starwarsSchema
89+
query: |
90+
{
91+
__schema {
92+
types {
93+
name
94+
}
95+
}
96+
}
97+
json: |
98+
{"__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"}]}}
99+
100+
- name: querying the schema for query type
101+
schema: *starwarsSchema
102+
query: |
103+
{
104+
__schema {
105+
queryType {
106+
name
107+
}
108+
}
109+
}
110+
json: |
111+
{"__schema":{"queryType":{"name":"Query"}}}
112+
113+
- name: querying the schema for a specific type
114+
schema: *starwarsSchema
115+
query: |
116+
{
117+
__type(name: "Droid") {
118+
name
119+
}
120+
}
121+
json: |
122+
{"__type":{"name":"Droid"}}
123+
124+
- name: querying the schema for an object kind
125+
schema: *starwarsSchema
126+
query: |
127+
{
128+
__type(name: "Droid") {
129+
name
130+
kind
131+
}
132+
}
133+
json: |
134+
{"__type":{"name":"Droid","kind":"OBJECT"}}
135+
136+
- name: querying the schema for an interface kind
137+
schema: *starwarsSchema
138+
query: |
139+
{
140+
__type(name: "Character") {
141+
name
142+
kind
143+
}
144+
}
145+
json: |
146+
{"__type":{"name":"Character","kind":"INTERFACE"}}
147+
148+
- name: querying the schema for object fields
149+
schema: *starwarsSchema
150+
query: |
151+
{
152+
__type(name: "Droid") {
153+
name
154+
fields {
155+
name
156+
type {
157+
name
158+
kind
159+
}
160+
}
161+
}
162+
}
163+
json: |
164+
{"__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"}}]}}
165+
166+
- name: querying the schema for nested object fields
167+
schema: *starwarsSchema
168+
query: |
169+
{
170+
__type(name: "Droid") {
171+
name
172+
fields {
173+
name
174+
type {
175+
name
176+
kind
177+
ofType {
178+
name
179+
kind
180+
}
181+
}
182+
}
183+
}
184+
}
185+
json: |
186+
{"__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}}]}}
187+
188+
- name: querying the schema for field args
189+
schema: *starwarsSchema
190+
query: |
191+
{
192+
__schema {
193+
queryType {
194+
fields {
195+
name
196+
args {
197+
name
198+
description
199+
type {
200+
name
201+
kind
202+
ofType {
203+
name
204+
kind
205+
}
206+
}
207+
defaultValue
208+
}
209+
}
210+
}
211+
}
212+
}
213+
json: |
214+
{"__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}]}]}}}
215+
216+
- name: querying the schema for documentation
217+
schema: *starwarsSchema
218+
query: |
219+
{
220+
__type(name: "Droid") {
221+
name
222+
description
223+
}
224+
}
225+
json: |
226+
{"__type":{"name":"Droid","description":"A mechanical creature in the Star Wars universe."}}

internal/wrapper/schema.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package wrapper
22

33
import (
4+
"sort"
45
"strings"
56

67
"github.com/Code-Hex/gqlparser/v2/ast"
@@ -13,11 +14,13 @@ type Schema struct {
1314
func (s *Schema) Types() []Type {
1415
types := make([]Type, 0, len(s.schema.Types))
1516
for _, typ := range s.schema.Types {
16-
if strings.HasPrefix(typ.Name, "__") {
17-
continue
18-
}
1917
types = append(types, *WrapTypeFromDef(s.schema, typ))
2018
}
19+
sort.Slice(types, func(i, j int) bool {
20+
x := *types[i].Name()
21+
y := *types[j].Name()
22+
return strings.Compare(x, y) < 0
23+
})
2124
return types
2225
}
2326

internal/wrapper/type.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ func WrapTypeFromType(s *ast.Schema, typ *ast.Type) *Type {
2323
if typ == nil {
2424
return nil
2525
}
26-
2726
if !typ.NonNull && typ.NamedType != "" {
2827
return &Type{schema: s, def: s.Types[typ.NamedType]}
2928
}
@@ -35,15 +34,11 @@ func (t *Type) Kind() string {
3534
if t.typ.NonNull {
3635
return "NON_NULL"
3736
}
38-
3937
if t.typ.Elem != nil {
4038
return "LIST"
4139
}
42-
} else {
43-
return string(t.def.Kind)
4440
}
45-
46-
panic("UNKNOWN")
41+
return string(t.def.Kind)
4742
}
4843

4944
func (t *Type) Name() *string {

0 commit comments

Comments
 (0)