1package graphql_test 2 3import ( 4 "fmt" 5 "reflect" 6 "testing" 7 8 "github.com/graphql-go/graphql" 9 "github.com/graphql-go/graphql/testutil" 10) 11 12// TODO: have a separate package for other tests for eg `parser` 13// maybe for: 14// - tests that supposed to be black-boxed (no reason to access private identifiers) 15// - tests that create internal tests structs, we might not want to pollute the package with too many test structs 16 17type testPic struct { 18 Url string `json:"url"` 19 Width string `json:"width"` 20 Height string `json:"height"` 21} 22 23type testPicFn func(width, height string) *testPic 24 25type testAuthor struct { 26 Id int `json:"id"` 27 Name string `json:"name"` 28 Pic testPicFn `json:"pic"` 29 RecentArticle *testArticle `json:"recentArticle"` 30} 31type testArticle struct { 32 Id string `json:"id"` 33 IsPublished string `json:"isPublished"` 34 Author *testAuthor `json:"author"` 35 Title string `json:"title"` 36 Body string `json:"body"` 37 Hidden string `json:"hidden"` 38 Keywords []interface{} `json:"keywords"` 39} 40 41func getPic(id int, width, height string) *testPic { 42 return &testPic{ 43 Url: fmt.Sprintf("cdn://%v", id), 44 Width: width, 45 Height: height, 46 } 47} 48 49var johnSmith *testAuthor 50 51func article(id interface{}) *testArticle { 52 return &testArticle{ 53 Id: fmt.Sprintf("%v", id), 54 IsPublished: "true", 55 Author: johnSmith, 56 Title: fmt.Sprintf("My Article %v", id), 57 Body: "This is a post", 58 Hidden: "This data is not exposed in the schema", 59 Keywords: []interface{}{ 60 "foo", "bar", 1, true, nil, 61 }, 62 } 63} 64 65func TestExecutesUsingAComplexSchema(t *testing.T) { 66 67 johnSmith = &testAuthor{ 68 Id: 123, 69 Name: "John Smith", 70 Pic: func(width string, height string) *testPic { 71 return getPic(123, width, height) 72 }, 73 RecentArticle: article("1"), 74 } 75 76 blogImage := graphql.NewObject(graphql.ObjectConfig{ 77 Name: "Image", 78 Fields: graphql.Fields{ 79 "url": &graphql.Field{ 80 Type: graphql.String, 81 }, 82 "width": &graphql.Field{ 83 Type: graphql.Int, 84 }, 85 "height": &graphql.Field{ 86 Type: graphql.Int, 87 }, 88 }, 89 }) 90 blogAuthor := graphql.NewObject(graphql.ObjectConfig{ 91 Name: "Author", 92 Fields: graphql.Fields{ 93 "id": &graphql.Field{ 94 Type: graphql.String, 95 }, 96 "name": &graphql.Field{ 97 Type: graphql.String, 98 }, 99 "pic": &graphql.Field{ 100 Type: blogImage, 101 Args: graphql.FieldConfigArgument{ 102 "width": &graphql.ArgumentConfig{ 103 Type: graphql.Int, 104 }, 105 "height": &graphql.ArgumentConfig{ 106 Type: graphql.Int, 107 }, 108 }, 109 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 110 if author, ok := p.Source.(*testAuthor); ok { 111 width := fmt.Sprintf("%v", p.Args["width"]) 112 height := fmt.Sprintf("%v", p.Args["height"]) 113 return author.Pic(width, height), nil 114 } 115 return nil, nil 116 }, 117 }, 118 "recentArticle": &graphql.Field{}, 119 }, 120 }) 121 blogArticle := graphql.NewObject(graphql.ObjectConfig{ 122 Name: "Article", 123 Fields: graphql.Fields{ 124 "id": &graphql.Field{ 125 Type: graphql.NewNonNull(graphql.String), 126 }, 127 "isPublished": &graphql.Field{ 128 Type: graphql.Boolean, 129 }, 130 "author": &graphql.Field{ 131 Type: blogAuthor, 132 }, 133 "title": &graphql.Field{ 134 Type: graphql.String, 135 }, 136 "body": &graphql.Field{ 137 Type: graphql.String, 138 }, 139 "keywords": &graphql.Field{ 140 Type: graphql.NewList(graphql.String), 141 }, 142 }, 143 }) 144 145 blogAuthor.AddFieldConfig("recentArticle", &graphql.Field{ 146 Type: blogArticle, 147 }) 148 149 blogQuery := graphql.NewObject(graphql.ObjectConfig{ 150 Name: "Query", 151 Fields: graphql.Fields{ 152 "article": &graphql.Field{ 153 Type: blogArticle, 154 Args: graphql.FieldConfigArgument{ 155 "id": &graphql.ArgumentConfig{ 156 Type: graphql.ID, 157 }, 158 }, 159 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 160 id := p.Args["id"] 161 return article(id), nil 162 }, 163 }, 164 "feed": &graphql.Field{ 165 Type: graphql.NewList(blogArticle), 166 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 167 return []*testArticle{ 168 article(1), 169 article(2), 170 article(3), 171 article(4), 172 article(5), 173 article(6), 174 article(7), 175 article(8), 176 article(9), 177 article(10), 178 }, nil 179 }, 180 }, 181 }, 182 }) 183 184 blogSchema, err := graphql.NewSchema(graphql.SchemaConfig{ 185 Query: blogQuery, 186 }) 187 if err != nil { 188 t.Fatalf("Error in schema %v", err.Error()) 189 } 190 191 request := ` 192 { 193 feed { 194 id, 195 title 196 }, 197 article(id: "1") { 198 ...articleFields, 199 author { 200 id, 201 name, 202 pic(width: 640, height: 480) { 203 url, 204 width, 205 height 206 }, 207 recentArticle { 208 ...articleFields, 209 keywords 210 } 211 } 212 } 213 } 214 215 fragment articleFields on Article { 216 id, 217 isPublished, 218 title, 219 body, 220 hidden, 221 notdefined 222 } 223 ` 224 225 expected := &graphql.Result{ 226 Data: map[string]interface{}{ 227 "article": map[string]interface{}{ 228 "title": "My Article 1", 229 "body": "This is a post", 230 "author": map[string]interface{}{ 231 "id": "123", 232 "name": "John Smith", 233 "pic": map[string]interface{}{ 234 "url": "cdn://123", 235 "width": 640, 236 "height": 480, 237 }, 238 "recentArticle": map[string]interface{}{ 239 "id": "1", 240 "isPublished": bool(true), 241 "title": "My Article 1", 242 "body": "This is a post", 243 "keywords": []interface{}{ 244 "foo", 245 "bar", 246 "1", 247 "true", 248 nil, 249 }, 250 }, 251 }, 252 "id": "1", 253 "isPublished": bool(true), 254 }, 255 "feed": []interface{}{ 256 map[string]interface{}{ 257 "id": "1", 258 "title": "My Article 1", 259 }, 260 map[string]interface{}{ 261 "id": "2", 262 "title": "My Article 2", 263 }, 264 map[string]interface{}{ 265 "id": "3", 266 "title": "My Article 3", 267 }, 268 map[string]interface{}{ 269 "id": "4", 270 "title": "My Article 4", 271 }, 272 map[string]interface{}{ 273 "id": "5", 274 "title": "My Article 5", 275 }, 276 map[string]interface{}{ 277 "id": "6", 278 "title": "My Article 6", 279 }, 280 map[string]interface{}{ 281 "id": "7", 282 "title": "My Article 7", 283 }, 284 map[string]interface{}{ 285 "id": "8", 286 "title": "My Article 8", 287 }, 288 map[string]interface{}{ 289 "id": "9", 290 "title": "My Article 9", 291 }, 292 map[string]interface{}{ 293 "id": "10", 294 "title": "My Article 10", 295 }, 296 }, 297 }, 298 } 299 300 // parse query 301 ast := testutil.TestParse(t, request) 302 303 // execute 304 ep := graphql.ExecuteParams{ 305 Schema: blogSchema, 306 AST: ast, 307 } 308 result := testutil.TestExecute(t, ep) 309 if len(result.Errors) > 0 { 310 t.Fatalf("wrong result, unexpected errors: %v", result.Errors) 311 } 312 if !reflect.DeepEqual(expected, result) { 313 t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expected, result)) 314 } 315} 316