github.com/cayleygraph/cayley@v0.7.7/query/graphql/graphql_test.go (about) 1 package graphql 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "reflect" 8 "strings" 9 "testing" 10 11 "github.com/stretchr/testify/require" 12 13 "github.com/cayleygraph/cayley/graph/graphtest/testutil" 14 "github.com/cayleygraph/cayley/graph/memstore" 15 "github.com/cayleygraph/quad" 16 "github.com/cayleygraph/quad/voc/rdf" 17 ) 18 19 func iris(arr ...string) (out []quad.Value) { 20 for _, s := range arr { 21 out = append(out, quad.IRI(s)) 22 } 23 return 24 } 25 26 var casesParse = []struct { 27 query string 28 expect []field 29 }{ 30 { 31 `{ 32 user(id: 3500401, http://iri: http://some_iri, follow: <bob>, n: _:bob, v: ["<bob>", "name", 3]) @rev(follow: "123"){ 33 id: ` + ValueKey + `, 34 type: ` + rdf.NS + "type" + `, 35 followed: follow @reverse @label(v: <fb>) { 36 name: <name> @label(v: <google>) 37 followed: ~follow 38 sname @label 39 } 40 isViewerFriend, 41 profilePicture(size: 50) @unnest { 42 uri, 43 width @opt, 44 height @rev 45 } 46 sub {*} 47 } 48 }`, 49 []field{{ 50 Via: "user", Alias: "user", 51 Has: []has{ 52 {"follow", true, []quad.Value{quad.String("123")}, nil}, 53 {"id", false, []quad.Value{quad.Int(3500401)}, nil}, 54 {"http://iri", false, iris("http://some_iri"), nil}, 55 {"follow", false, iris("bob"), nil}, 56 {"n", false, []quad.Value{quad.BNode("bob")}, nil}, 57 {"v", false, []quad.Value{quad.IRI("bob"), quad.String("name"), quad.Int(3)}, nil}, 58 }, 59 Fields: []field{ 60 {Via: quad.IRI(ValueKey), Alias: "id"}, 61 {Via: quad.IRI("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), Alias: "type"}, 62 { 63 Via: "follow", Alias: "followed", Rev: true, Labels: iris("fb"), 64 Fields: []field{ 65 {Via: "name", Alias: "name", Labels: iris("google")}, 66 {Via: "follow", Alias: "followed", Rev: true, Labels: iris("fb")}, 67 {Via: "sname", Alias: "sname"}, 68 }, 69 }, 70 {Via: "isViewerFriend", Alias: "isViewerFriend"}, 71 { 72 Via: "profilePicture", Alias: "profilePicture", 73 Has: []has{{"size", false, []quad.Value{quad.Int(50)}, nil}}, 74 Fields: []field{ 75 {Via: "uri", Alias: "uri"}, 76 {Via: "width", Alias: "width", Opt: true}, 77 {Via: "height", Alias: "height", Rev: true}, 78 }, 79 UnNest: true, 80 }, 81 {Via: "sub", Alias: "sub", AllFields: true}, 82 }, 83 }}, 84 }, 85 } 86 87 func TestParse(t *testing.T) { 88 for _, c := range casesParse { 89 q, err := Parse(strings.NewReader(c.query)) 90 if err != nil { 91 t.Fatal(err) 92 } else if !reflect.DeepEqual(q.fields, c.expect) { 93 t.Fatalf("\n%#v\nvs\n%#v", q.fields, c.expect) 94 } 95 } 96 } 97 98 type M = map[string]interface{} 99 100 var casesExecute = []struct { 101 name string 102 query string 103 result M 104 }{ 105 { 106 "cool people and friends", 107 `{ 108 me(status: "cool_person") { 109 id: ` + ValueKey + ` 110 follows { 111 ` + ValueKey + ` 112 status 113 } 114 followed: follows @rev { 115 ` + ValueKey + ` 116 } 117 } 118 }`, 119 M{ 120 "me": []M{ 121 { 122 "id": quad.IRI("bob"), 123 "follows": nil, 124 "followed": []M{ 125 {ValueKey: quad.IRI("alice")}, 126 {ValueKey: quad.IRI("dani")}, 127 {ValueKey: quad.IRI("charlie")}, 128 }, 129 }, 130 { 131 "id": quad.IRI("dani"), 132 "follows": []M{ 133 { 134 ValueKey: quad.IRI("bob"), 135 "status": quad.String("cool_person"), 136 }, 137 { 138 ValueKey: quad.IRI("greg"), 139 "status": []quad.Value{ 140 quad.String("cool_person"), 141 quad.String("smart_person"), 142 }, 143 }, 144 }, 145 "followed": M{ 146 ValueKey: quad.IRI("charlie"), 147 }, 148 }, 149 { 150 "id": quad.IRI("greg"), 151 "follows": nil, 152 "followed": []M{ 153 {ValueKey: quad.IRI("dani")}, 154 {ValueKey: quad.IRI("fred")}, 155 }, 156 }, 157 }, 158 }, 159 }, 160 { 161 "skip and limit", 162 `{ 163 me(status: "cool_person", ` + LimitKey + `: 1, ` + SkipKey + `: 1) { 164 id: ` + ValueKey + ` 165 follows(` + LimitKey + `: 1) @opt { 166 ` + ValueKey + ` 167 } 168 } 169 }`, 170 M{ 171 "me": M{ 172 "id": quad.IRI("dani"), 173 "follows": M{ 174 ValueKey: quad.IRI("bob"), 175 }, 176 }, 177 }, 178 }, 179 { 180 "labels", 181 `{ 182 me { 183 id: ` + ValueKey + ` 184 status @label(v: <smart_graph>) 185 } 186 }`, 187 M{ 188 "me": []M{ 189 { 190 "id": quad.IRI("emily"), 191 "status": quad.String("smart_person"), 192 }, 193 { 194 "id": quad.IRI("greg"), 195 "status": quad.String("smart_person"), 196 }, 197 }, 198 }, 199 }, 200 { 201 "expand all", 202 `{ 203 me { 204 id: ` + ValueKey + ` 205 status @label(v: <smart_graph>) 206 follows {*} 207 } 208 }`, 209 M{ 210 "me": []M{ 211 { 212 "id": quad.IRI("emily"), 213 "status": quad.String("smart_person"), 214 "follows": M{ 215 "id": quad.IRI("fred"), 216 "follows": quad.IRI("greg"), 217 }, 218 }, 219 { 220 "id": quad.IRI("greg"), 221 "status": quad.String("smart_person"), 222 "follows": nil, 223 }, 224 }, 225 }, 226 }, 227 { 228 "unnest object", 229 `{ 230 me(id: fred) { 231 id: ` + ValueKey + ` 232 follows @unnest { 233 friend: ` + ValueKey + ` 234 friend_status: status 235 followed: follows(` + LimitKey + `: 1) @rev @unnest { 236 fof: ` + ValueKey + ` 237 } 238 } 239 } 240 }`, 241 M{ 242 "me": M{ 243 "id": quad.IRI("fred"), 244 "fof": quad.IRI("dani"), 245 "friend": quad.IRI("greg"), 246 "friend_status": []quad.Value{ 247 quad.String("cool_person"), 248 quad.String("smart_person"), 249 }, 250 }, 251 }, 252 }, 253 { 254 "unnest object (non existent)", 255 `{ 256 me(id: fred) { 257 id: ` + ValueKey + ` 258 follows_missing @unnest { 259 friend: ` + ValueKey + ` 260 friend_status: status 261 } 262 } 263 }`, 264 M{ 265 "me": M{ 266 "id": quad.IRI("fred"), 267 }, 268 }, 269 }, 270 { 271 "all optional", 272 `{ 273 nodes { 274 id, 275 status @opt 276 } 277 }`, 278 M{ 279 "nodes": []M{ 280 {"id": quad.IRI("alice")}, 281 {"id": quad.IRI("follows")}, 282 {"id": quad.IRI("bob"), "status": quad.String("cool_person")}, 283 {"id": quad.IRI("fred")}, 284 {"id": quad.IRI("status")}, 285 {"id": quad.String("cool_person")}, 286 {"id": quad.IRI("dani"), "status": quad.String("cool_person")}, 287 {"id": quad.IRI("charlie")}, 288 {"id": quad.IRI("greg"), "status": []quad.Value{ 289 quad.String("cool_person"), 290 quad.String("smart_person"), 291 }}, 292 {"id": quad.IRI("emily"), "status": quad.String("smart_person")}, 293 {"id": quad.IRI("predicates")}, 294 {"id": quad.IRI("are")}, 295 {"id": quad.String("smart_person")}, 296 {"id": quad.IRI("smart_graph")}, 297 }, 298 }, 299 }, 300 } 301 302 func toJson(o interface{}) string { 303 buf := bytes.NewBuffer(nil) 304 json.NewEncoder(buf).Encode(o) 305 buf2 := bytes.NewBuffer(nil) 306 json.Indent(buf2, buf.Bytes(), "", " ") 307 return buf2.String() 308 } 309 310 func TestExecute(t *testing.T) { 311 qs := memstore.New() 312 qw := testutil.MakeWriter(t, qs, nil) 313 quads := testutil.LoadGraph(t, "../../data/testdata.nq") 314 err := qw.AddQuadSet(quads) 315 require.NoError(t, err) 316 317 for _, c := range casesExecute { 318 t.Run(c.name, func(t *testing.T) { 319 q, err := Parse(strings.NewReader(c.query)) 320 require.NoError(t, err) 321 out, err := q.Execute(context.Background(), qs) 322 require.NoError(t, err) 323 require.Equal(t, c.result, out, "results:\n%v\n\nvs\n\n%v", toJson(c.result), toJson(out)) 324 }) 325 } 326 }