github.com/dgraph-io/dgraph@v1.2.8/graphql/schema/wrappers_test.go (about) 1 /* 2 * Copyright 2019 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package schema 18 19 import ( 20 "testing" 21 22 "github.com/google/go-cmp/cmp" 23 "github.com/pkg/errors" 24 "github.com/stretchr/testify/require" 25 "github.com/vektah/gqlparser/ast" 26 ) 27 28 func TestDgraphMapping_WithoutDirectives(t *testing.T) { 29 schemaStr := ` 30 type Author { 31 id: ID! 32 33 name: String! @search(by: [hash, trigram]) 34 dob: DateTime @search 35 reputation: Float @search 36 posts: [Post!] @hasInverse(field: author) 37 } 38 39 type Post { 40 postID: ID! 41 postType: PostType @search 42 author: Author! @hasInverse(field: posts) 43 } 44 45 enum PostType { 46 Fact 47 Question 48 Opinion 49 } 50 51 interface Employee { 52 ename: String! 53 } 54 55 interface Character { 56 id: ID! 57 name: String! @search(by: [exact]) 58 appearsIn: [Episode!] @search 59 } 60 61 type Human implements Character & Employee { 62 starships: [Starship] 63 totalCredits: Float 64 } 65 66 type Droid implements Character { 67 primaryFunction: String 68 } 69 70 enum Episode { 71 NEWHOPE 72 EMPIRE 73 JEDI 74 } 75 76 type Starship { 77 id: ID! 78 name: String! @search(by: [term]) 79 length: Float 80 }` 81 82 schHandler, errs := NewHandler(schemaStr) 83 require.NoError(t, errs) 84 sch, err := FromString(schHandler.GQLSchema()) 85 require.NoError(t, err) 86 87 s, ok := sch.(*schema) 88 require.True(t, ok, "expected to be able to convert sch to internal schema type") 89 90 author := map[string]string{ 91 "name": "Author.name", 92 "dob": "Author.dob", 93 "reputation": "Author.reputation", 94 "posts": "Author.posts", 95 } 96 post := map[string]string{ 97 "postType": "Post.postType", 98 "author": "Post.author", 99 } 100 character := map[string]string{ 101 "name": "Character.name", 102 "appearsIn": "Character.appearsIn", 103 } 104 human := map[string]string{ 105 "ename": "Employee.ename", 106 "name": "Character.name", 107 "appearsIn": "Character.appearsIn", 108 "starships": "Human.starships", 109 "totalCredits": "Human.totalCredits", 110 } 111 droid := map[string]string{ 112 "name": "Character.name", 113 "appearsIn": "Character.appearsIn", 114 "primaryFunction": "Droid.primaryFunction", 115 } 116 starship := map[string]string{ 117 "name": "Starship.name", 118 "length": "Starship.length", 119 } 120 121 expected := map[string]map[string]string{ 122 "Author": author, 123 "UpdateAuthorPayload": author, 124 "DeleteAuthorPayload": author, 125 "Post": post, 126 "UpdatePostPayload": post, 127 "DeletePostPayload": post, 128 "Employee": map[string]string{ 129 "ename": "Employee.ename", 130 }, 131 "Character": character, 132 "UpdateCharacterPayload": character, 133 "DeleteCharacterPayload": character, 134 "Human": human, 135 "UpdateHumanPayload": human, 136 "DeleteHumanPayload": human, 137 "Droid": droid, 138 "UpdateDroidPayload": droid, 139 "DeleteDroidPayload": droid, 140 "Starship": starship, 141 "UpdateStarshipPayload": starship, 142 "DeleteStarshipPayload": starship, 143 } 144 145 if diff := cmp.Diff(expected, s.dgraphPredicate); diff != "" { 146 t.Errorf("dgraph predicate map mismatch (-want +got):\n%s", diff) 147 } 148 } 149 150 func TestDgraphMapping_WithDirectives(t *testing.T) { 151 schemaStr := ` 152 type Author @dgraph(type: "dgraph.author") { 153 id: ID! 154 155 name: String! @search(by: [hash, trigram]) 156 dob: DateTime @search 157 reputation: Float @search 158 posts: [Post!] @hasInverse(field: author) 159 } 160 161 type Post @dgraph(type: "dgraph.Post") { 162 postID: ID! 163 postType: PostType @search @dgraph(pred: "dgraph.post_type") 164 author: Author! @hasInverse(field: posts) @dgraph(pred: "dgraph.post_author") 165 } 166 167 enum PostType { 168 Fact 169 Question 170 Opinion 171 } 172 173 interface Employee @dgraph(type: "dgraph.employee.en") { 174 ename: String! 175 } 176 177 interface Character @dgraph(type: "performance.character") { 178 id: ID! 179 name: String! @search(by: [exact]) 180 appearsIn: [Episode!] @search @dgraph(pred: "appears_in") 181 } 182 183 type Human implements Character & Employee { 184 starships: [Starship] 185 totalCredits: Float @dgraph(pred: "credits") 186 } 187 188 type Droid implements Character @dgraph(type: "roboDroid") { 189 primaryFunction: String 190 } 191 192 enum Episode { 193 NEWHOPE 194 EMPIRE 195 JEDI 196 } 197 198 type Starship @dgraph(type: "star.ship") { 199 id: ID! 200 name: String! @search(by: [term]) @dgraph(pred: "star.ship.name") 201 length: Float 202 }` 203 204 schHandler, errs := NewHandler(schemaStr) 205 require.NoError(t, errs) 206 sch, err := FromString(schHandler.GQLSchema()) 207 require.NoError(t, err) 208 209 s, ok := sch.(*schema) 210 require.True(t, ok, "expected to be able to convert sch to internal schema type") 211 212 author := map[string]string{ 213 "name": "dgraph.author.name", 214 "dob": "dgraph.author.dob", 215 "reputation": "dgraph.author.reputation", 216 "posts": "dgraph.author.posts", 217 } 218 post := map[string]string{ 219 "postType": "dgraph.post_type", 220 "author": "dgraph.post_author", 221 } 222 character := map[string]string{ 223 "name": "performance.character.name", 224 "appearsIn": "appears_in", 225 } 226 human := map[string]string{ 227 "ename": "dgraph.employee.en.ename", 228 "name": "performance.character.name", 229 "appearsIn": "appears_in", 230 "starships": "Human.starships", 231 "totalCredits": "credits", 232 } 233 droid := map[string]string{ 234 "name": "performance.character.name", 235 "appearsIn": "appears_in", 236 "primaryFunction": "roboDroid.primaryFunction", 237 } 238 starship := map[string]string{ 239 "name": "star.ship.name", 240 "length": "star.ship.length", 241 } 242 243 expected := map[string]map[string]string{ 244 "Author": author, 245 "UpdateAuthorPayload": author, 246 "DeleteAuthorPayload": author, 247 "Post": post, 248 "UpdatePostPayload": post, 249 "DeletePostPayload": post, 250 "Employee": map[string]string{ 251 "ename": "dgraph.employee.en.ename", 252 }, 253 "Character": character, 254 "UpdateCharacterPayload": character, 255 "DeleteCharacterPayload": character, 256 "Human": human, 257 "UpdateHumanPayload": human, 258 "DeleteHumanPayload": human, 259 "Droid": droid, 260 "UpdateDroidPayload": droid, 261 "DeleteDroidPayload": droid, 262 "Starship": starship, 263 "UpdateStarshipPayload": starship, 264 "DeleteStarshipPayload": starship, 265 } 266 267 if diff := cmp.Diff(expected, s.dgraphPredicate); diff != "" { 268 t.Errorf("dgraph predicate map mismatch (-want +got):\n%s", diff) 269 } 270 } 271 272 func TestCheckNonNulls(t *testing.T) { 273 274 gqlSchema, err := FromString(` 275 type T { 276 req: String! 277 notReq: String 278 alsoReq: String! 279 }`) 280 require.NoError(t, err) 281 282 tcases := map[string]struct { 283 obj map[string]interface{} 284 exc string 285 err error 286 }{ 287 "all present": { 288 obj: map[string]interface{}{"req": "here", "notReq": "here", "alsoReq": "here"}, 289 err: nil, 290 }, 291 "only non-null": { 292 obj: map[string]interface{}{"req": "here", "alsoReq": "here"}, 293 err: nil, 294 }, 295 "missing non-null": { 296 obj: map[string]interface{}{"req": "here", "notReq": "here"}, 297 err: errors.Errorf("type T requires a value for field alsoReq, but no value present"), 298 }, 299 "missing all non-null": { 300 obj: map[string]interface{}{"notReq": "here"}, 301 err: errors.Errorf("type T requires a value for field req, but no value present"), 302 }, 303 "with exclusion": { 304 obj: map[string]interface{}{"req": "here", "notReq": "here"}, 305 exc: "alsoReq", 306 err: nil, 307 }, 308 } 309 310 typ := &astType{ 311 typ: &ast.Type{NamedType: "T"}, 312 inSchema: (gqlSchema.(*schema)).schema, 313 } 314 315 for name, test := range tcases { 316 t.Run(name, func(t *testing.T) { 317 err := typ.EnsureNonNulls(test.obj, test.exc) 318 if test.err == nil { 319 require.NoError(t, err) 320 } else { 321 require.EqualError(t, err, test.err.Error()) 322 } 323 }) 324 } 325 }