github.com/cayleygraph/cayley@v0.7.7/query/gizmo/gizmo_test.go (about) 1 // Copyright 2017 The Cayley Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gizmo 16 17 import ( 18 "context" 19 "fmt" 20 "reflect" 21 "sort" 22 "testing" 23 24 "github.com/stretchr/testify/require" 25 26 "github.com/cayleygraph/cayley/graph" 27 "github.com/cayleygraph/cayley/graph/graphtest/testutil" 28 _ "github.com/cayleygraph/cayley/graph/memstore" 29 "github.com/cayleygraph/cayley/query" 30 _ "github.com/cayleygraph/cayley/writer" 31 "github.com/cayleygraph/quad" 32 33 // register global namespace for tests 34 _ "github.com/cayleygraph/quad/voc/rdf" 35 ) 36 37 // This is a simple test graph used for testing 38 // 39 // +-------+ +------+ 40 // | alice |----- ->| fred |<-- 41 // +-------+ \---->+-------+-/ +------+ \-+-------+ 42 // ----->| #bob# | | | emily | 43 // +---------+--/ --->+-------+ | +-------+ 44 // | charlie | / v 45 // +---------+ / +--------+ 46 // \--- +--------+ | #greg# | 47 // \-->| #dani# |------------>+--------+ 48 // +--------+ 49 // 50 51 func makeTestSession(data []quad.Quad) *Session { 52 qs, _ := graph.NewQuadStore("memstore", "", nil) 53 w, _ := graph.NewQuadWriter("single", qs, nil) 54 for _, t := range data { 55 w.AddQuad(t) 56 } 57 return NewSession(qs) 58 } 59 60 func intVal(v int) string { 61 return quad.Int(v).String() 62 } 63 64 const multiGraphTestFile = "../../data/testdata_multigraph.nq" 65 66 var testQueries = []struct { 67 message string 68 data []quad.Quad 69 query string 70 limit int 71 tag string 72 file string 73 expect []string 74 err bool // TODO(dennwc): define error types for Gizmo and handle them 75 }{ 76 // Simple query tests. 77 { 78 message: "get a single vertex", 79 query: ` 80 g.V("<alice>").all() 81 `, 82 expect: []string{"<alice>"}, 83 }, 84 { 85 message: "get a single vertex (legacy)", 86 query: ` 87 g.V("<alice>").All() 88 `, 89 expect: []string{"<alice>"}, 90 }, 91 { 92 message: "get a single vertex (legacy)", 93 query: ` 94 g.V("<alice>").All() 95 `, 96 expect: []string{"<alice>"}, 97 }, 98 { 99 message: "use .getLimit", 100 query: ` 101 g.V().getLimit(5) 102 `, 103 expect: []string{"<alice>", "<bob>", "<follows>", "<fred>", "<status>"}, 104 }, 105 { 106 message: "get a single vertex (IRI)", 107 query: ` 108 g.V(iri("alice")).all() 109 `, 110 expect: []string{"<alice>"}, 111 }, 112 { 113 message: "use .out()", 114 query: ` 115 g.V("<alice>").out("<follows>").all() 116 `, 117 expect: []string{"<bob>"}, 118 }, 119 { 120 message: "use .out() (IRI)", 121 query: ` 122 g.V(iri("alice")).out(iri("follows")).all() 123 `, 124 expect: []string{"<bob>"}, 125 }, 126 { 127 message: "use .out() (any)", 128 query: ` 129 g.V("<bob>").out().all() 130 `, 131 expect: []string{"<fred>", "cool_person"}, 132 }, 133 { 134 message: "use .in()", 135 query: ` 136 g.V("<bob>").in("<follows>").all() 137 `, 138 expect: []string{"<alice>", "<charlie>", "<dani>"}, 139 }, 140 { 141 message: "use .in() (any)", 142 query: ` 143 g.V("<bob>").in().all() 144 `, 145 expect: []string{"<alice>", "<charlie>", "<dani>"}, 146 }, 147 { 148 message: "use .in() with .filter()", 149 query: ` 150 g.V("<bob>").in("<follows>").filter(gt(iri("c")),lt(iri("d"))).all() 151 `, 152 expect: []string{"<charlie>"}, 153 }, 154 { 155 message: "use .in() with .filter(regex)", 156 query: ` 157 g.V("<bob>").in("<follows>").filter(regex("ar?li.*e")).all() 158 `, 159 expect: nil, 160 }, 161 { 162 message: "use .in() with .filter(prefix)", 163 query: ` 164 g.V("<bob>").in("<follows>").filter(like("al%")).all() 165 `, 166 expect: []string{"<alice>"}, 167 }, 168 { 169 message: "use .in() with .filter(wildcard)", 170 query: ` 171 g.V("<bob>").in("<follows>").filter(like("a?i%e")).all() 172 `, 173 expect: []string{"<alice>"}, 174 }, 175 { 176 message: "use .in() with .filter(regex with IRIs)", 177 query: ` 178 g.V("<bob>").in("<follows>").filter(regex("ar?li.*e", true)).all() 179 `, 180 expect: []string{"<alice>", "<charlie>"}, 181 }, 182 { 183 message: "use .in() with .filter(regex with IRIs)", 184 query: ` 185 g.V("<bob>").in("<follows>").filter(regex(iri("ar?li.*e"))).all() 186 `, 187 err: true, 188 }, 189 { 190 message: "use .in() with .filter(regex,gt)", 191 query: ` 192 g.V("<bob>").in("<follows>").filter(regex("ar?li.*e", true),gt(iri("c"))).all() 193 `, 194 expect: []string{"<charlie>"}, 195 }, 196 { 197 message: "filter with a wrong type", 198 query: ` 199 g.V().filter(/<alice>/).all() 200 `, 201 err: true, 202 }, 203 { 204 message: "use .both()", 205 query: ` 206 g.V("<fred>").both("<follows>").all() 207 `, 208 expect: []string{"<bob>", "<greg>", "<emily>"}, 209 }, 210 { 211 message: "use .both() with tag", 212 query: ` 213 g.V("<fred>").both(null, "pred").all() 214 `, 215 tag: "pred", 216 expect: []string{"<follows>", "<follows>", "<follows>"}, 217 }, 218 { 219 message: "use .tag()-.is()-.back()", 220 query: ` 221 g.V("<bob>").in("<follows>").tag("foo").out("<status>").is("cool_person").back("foo").all() 222 `, 223 expect: []string{"<dani>"}, 224 }, 225 { 226 message: "separate .tag()-.is()-.back()", 227 query: ` 228 x = g.V("<charlie>").out("<follows>").tag("foo").out("<status>").is("cool_person").back("foo") 229 x.in("<follows>").is("<dani>").back("foo").all() 230 `, 231 expect: []string{"<bob>"}, 232 }, 233 { 234 message: "do multiple .back()", 235 query: ` 236 g.V("<emily>").out("<follows>").as("f").out("<follows>").out("<status>").is("cool_person").back("f").in("<follows>").in("<follows>").as("acd").out("<status>").is("cool_person").back("f").all() 237 `, 238 tag: "acd", 239 expect: []string{"<dani>"}, 240 }, 241 { 242 message: "use Except to filter out a single vertex", 243 query: ` 244 g.V("<alice>", "<bob>").except(g.V("<alice>")).all() 245 `, 246 expect: []string{"<bob>"}, 247 }, 248 { 249 message: "use chained Except", 250 query: ` 251 g.V("<alice>", "<bob>", "<charlie>").except(g.V("<bob>")).except(g.V("<charlie>")).all() 252 `, 253 expect: []string{"<alice>"}, 254 }, 255 256 { 257 message: "use Unique", 258 query: ` 259 g.V("<alice>", "<bob>", "<charlie>").out("<follows>").unique().all() 260 `, 261 expect: []string{"<bob>", "<dani>", "<fred>"}, 262 }, 263 264 // Morphism tests. 265 { 266 message: "show simple morphism", 267 query: ` 268 grandfollows = g.M().out("<follows>").out("<follows>") 269 g.V("<charlie>").follow(grandfollows).all() 270 `, 271 expect: []string{"<greg>", "<fred>", "<bob>"}, 272 }, 273 { 274 message: "show reverse morphism", 275 query: ` 276 grandfollows = g.M().out("<follows>").out("<follows>") 277 g.V("<fred>").followR(grandfollows).all() 278 `, 279 expect: []string{"<alice>", "<charlie>", "<dani>"}, 280 }, 281 282 // Intersection tests. 283 { 284 message: "show simple intersection", 285 query: ` 286 function follows(x) { return g.V(x).out("<follows>") } 287 follows("<dani>").and(follows("<charlie>")).all() 288 `, 289 expect: []string{"<bob>"}, 290 }, 291 { 292 message: "show simple morphism intersection", 293 query: ` 294 grandfollows = g.M().out("<follows>").out("<follows>") 295 function gfollows(x) { return g.V(x).follow(grandfollows) } 296 gfollows("<alice>").and(gfollows("<charlie>")).all() 297 `, 298 expect: []string{"<fred>"}, 299 }, 300 { 301 message: "show double morphism intersection", 302 query: ` 303 grandfollows = g.M().out("<follows>").out("<follows>") 304 function gfollows(x) { return g.V(x).follow(grandfollows) } 305 gfollows("<emily>").and(gfollows("<charlie>")).and(gfollows("<bob>")).all() 306 `, 307 expect: []string{"<greg>"}, 308 }, 309 { 310 message: "show reverse intersection", 311 query: ` 312 grandfollows = g.M().out("<follows>").out("<follows>") 313 g.V("<greg>").followR(grandfollows).intersect(g.V("<fred>").followR(grandfollows)).all() 314 `, 315 expect: []string{"<charlie>"}, 316 }, 317 { 318 message: "show standard sort of morphism intersection, continue follow", 319 query: `gfollowers = g.M().in("<follows>").in("<follows>") 320 function cool(x) { return g.V(x).as("a").out("<status>").is("cool_person").back("a") } 321 cool("<greg>").follow(gfollowers).intersect(cool("<bob>").follow(gfollowers)).all() 322 `, 323 expect: []string{"<charlie>"}, 324 }, 325 { 326 message: "test Or()", 327 query: ` 328 g.V("<bob>").out("<follows>").or(g.V().has("<status>", "cool_person")).all() 329 `, 330 expect: []string{"<fred>", "<bob>", "<greg>", "<dani>"}, 331 }, 332 333 // Has tests. 334 { 335 message: "show a simple Has", 336 query: ` 337 g.V().has("<status>", "cool_person").all() 338 `, 339 expect: []string{"<greg>", "<dani>", "<bob>"}, 340 }, 341 { 342 message: "show a simple HasR", 343 query: ` 344 g.V().hasR("<status>", "<bob>").all() 345 `, 346 expect: []string{"cool_person"}, 347 }, 348 { 349 message: "show a double Has", 350 query: ` 351 g.V().has("<status>", "cool_person").has("<follows>", "<fred>").all() 352 `, 353 expect: []string{"<bob>"}, 354 }, 355 { 356 message: "show a Has with filter", 357 query: ` 358 g.V().has("<follows>", gt("<f>")).all() 359 `, 360 expect: []string{"<bob>", "<dani>", "<emily>", "<fred>"}, 361 }, 362 363 // Skip/Limit tests. 364 { 365 message: "use Limit", 366 query: ` 367 g.V().has("<status>", "cool_person").limit(2).all() 368 `, 369 expect: []string{"<bob>", "<dani>"}, 370 }, 371 { 372 message: "use Skip", 373 query: ` 374 g.V().has("<status>", "cool_person").skip(2).all() 375 `, 376 expect: []string{"<greg>"}, 377 }, 378 { 379 message: "use Skip and Limit", 380 query: ` 381 g.V().has("<status>", "cool_person").skip(1).limit(1).all() 382 `, 383 expect: []string{"<dani>"}, 384 }, 385 386 { 387 message: "show Count", 388 query: ` 389 g.V().has("<status>").count() 390 `, 391 expect: []string{"5"}, 392 }, 393 { 394 message: "use Count value", 395 query: ` 396 g.emit(g.V().has("<status>").count()+1) 397 `, 398 expect: []string{"6"}, 399 }, 400 401 // Tag tests. 402 { 403 message: "show a simple save", 404 query: ` 405 g.V().save("<status>", "somecool").all() 406 `, 407 tag: "somecool", 408 expect: []string{"cool_person", "cool_person", "cool_person", "smart_person", "smart_person"}, 409 }, 410 { 411 message: "show a simple save optional", 412 query: ` 413 g.V("<bob>","<charlie>").out("<follows>").saveOpt("<status>", "somecool").all() 414 `, 415 tag: "somecool", 416 expect: []string{"cool_person", "cool_person"}, 417 }, 418 { 419 message: "save iri no tag", 420 query: ` 421 g.V().save(g.IRI("status")).all() 422 `, 423 tag: "<status>", 424 expect: []string{"cool_person", "cool_person", "cool_person", "smart_person", "smart_person"}, 425 }, 426 { 427 message: "show a simple saveR", 428 query: ` 429 g.V("cool_person").saveR("<status>", "who").all() 430 `, 431 tag: "who", 432 expect: []string{"<greg>", "<dani>", "<bob>"}, 433 }, 434 { 435 message: "show an out save", 436 query: ` 437 g.V("<dani>").out(null, "pred").all() 438 `, 439 tag: "pred", 440 expect: []string{"<follows>", "<follows>", "<status>"}, 441 }, 442 { 443 message: "show a tag list", 444 query: ` 445 g.V("<dani>").out(null, ["pred", "foo", "bar"]).all() 446 `, 447 tag: "foo", 448 expect: []string{"<follows>", "<follows>", "<status>"}, 449 }, 450 { 451 message: "show a pred list", 452 query: ` 453 g.V("<dani>").out(["<follows>", "<status>"]).all() 454 `, 455 expect: []string{"<bob>", "<greg>", "cool_person"}, 456 }, 457 { 458 message: "show a predicate path", 459 query: ` 460 g.V("<dani>").out(g.V("<follows>"), "pred").all() 461 `, 462 expect: []string{"<bob>", "<greg>"}, 463 }, 464 { 465 message: "list all bob's incoming predicates", 466 query: ` 467 g.V("<bob>").inPredicates().all() 468 `, 469 expect: []string{"<follows>"}, 470 }, 471 { 472 message: "save all bob's incoming predicates", 473 query: ` 474 g.V("<bob>").saveInPredicates("pred").all() 475 `, 476 expect: []string{"<follows>", "<follows>", "<follows>"}, 477 tag: "pred", 478 }, 479 { 480 message: "list all labels", 481 query: ` 482 g.V().labels().all() 483 `, 484 expect: []string{"<smart_graph>"}, 485 }, 486 { 487 message: "list all in predicates", 488 query: ` 489 g.V().inPredicates().all() 490 `, 491 expect: []string{"<are>", "<follows>", "<status>"}, 492 }, 493 { 494 message: "list all out predicates", 495 query: ` 496 g.V().outPredicates().all() 497 `, 498 expect: []string{"<are>", "<follows>", "<status>"}, 499 }, 500 { 501 message: "traverse using LabelContext", 502 query: ` 503 g.V("<greg>").labelContext("<smart_graph>").out("<status>").all() 504 `, 505 expect: []string{"smart_person"}, 506 }, 507 { 508 message: "open and close a LabelContext", 509 query: ` 510 g.V().labelContext("<smart_graph>").in("<status>").labelContext(null).in("<follows>").all() 511 `, 512 expect: []string{"<dani>", "<fred>"}, 513 }, 514 { 515 message: "issue #254", 516 query: `g.V({"id":"<alice>"}).all()`, 517 expect: nil, err: true, 518 }, 519 { 520 message: "roundtrip values", 521 query: ` 522 v = g.V("<bob>").toValue() 523 s = g.V(v).out("<status>").toValue() 524 g.V(s).all() 525 `, 526 expect: []string{"cool_person"}, 527 }, 528 { 529 message: "roundtrip values (tag map)", 530 query: ` 531 v = g.V("<bob>").tagValue() 532 s = g.V(v.id).out("<status>").tagValue() 533 g.V(s.id).all() 534 `, 535 expect: []string{"cool_person"}, 536 }, 537 { 538 message: "show ToArray", 539 query: ` 540 arr = g.V("<bob>").in("<follows>").toArray() 541 for (i in arr) g.emit(arr[i]); 542 `, 543 expect: []string{"<alice>", "<charlie>", "<dani>"}, 544 }, 545 { 546 message: "show ToArray with limit", 547 query: ` 548 arr = g.V("<bob>").in("<follows>").toArray(2) 549 for (i in arr) g.emit(arr[i]); 550 `, 551 expect: []string{"<alice>", "<dani>"}, 552 }, 553 { 554 message: "show ForEach", 555 query: ` 556 g.V("<bob>").in("<follows>").forEach(function(o){g.emit(o.id)}); 557 `, 558 expect: []string{"<alice>", "<charlie>", "<dani>"}, 559 }, 560 { 561 message: "show ForEach with limit", 562 query: ` 563 g.V("<bob>").in("<follows>").forEach(2, function(o){g.emit(o.id)}); 564 `, 565 expect: []string{"<alice>", "<dani>"}, 566 }, 567 { 568 message: "clone paths", 569 query: ` 570 var alice = g.V('<alice>') 571 g.emit(alice.toValue()) 572 var out = alice.out('<follows>') 573 g.emit(out.toValue()) 574 g.emit(alice.toValue()) 575 `, 576 expect: []string{"<alice>", "<bob>", "<alice>"}, 577 }, 578 { 579 message: "default namespaces", 580 query: ` 581 g.addDefaultNamespaces() 582 g.emit(g.IRI('rdf:type')) 583 `, 584 expect: []string{"<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"}, 585 }, 586 { 587 message: "add namespace", 588 query: ` 589 g.addNamespace('ex','http://example.net/') 590 g.emit(g.IRI('ex:alice')) 591 `, 592 expect: []string{"<http://example.net/alice>"}, 593 }, 594 { 595 message: "recursive follow", 596 query: ` 597 g.V("<charlie>").followRecursive("<follows>").all(); 598 `, 599 expect: []string{"<bob>", "<dani>", "<fred>", "<greg>"}, 600 }, 601 { 602 message: "recursive follow tag", 603 query: ` 604 g.V("<charlie>").followRecursive("<follows>", "depth").all(); 605 `, 606 tag: "depth", 607 expect: []string{intVal(1), intVal(1), intVal(2), intVal(2)}, 608 }, 609 { 610 message: "recursive follow path", 611 query: ` 612 g.V("<charlie>").followRecursive(g.V().out("<follows>")).all(); 613 `, 614 expect: []string{"<bob>", "<dani>", "<fred>", "<greg>"}, 615 }, 616 { 617 message: "find non-existent", 618 query: ` 619 g.V('<not-existing>').forEach(function(d){ g.emit(d); }) 620 `, 621 expect: nil, 622 }, 623 { 624 message: "default limit All", 625 query: ` 626 g.V().all() 627 `, 628 limit: issue718Limit, 629 data: issue718Graph(), 630 expect: issue718Nodes(), 631 }, 632 { 633 message: "issue #758. Verify saveOpt respects label context", 634 query: ` 635 g.V("<greg>").labelContext("<smart_graph>").saveOpt("<status>", "statusTag").all() 636 `, 637 tag: "statusTag", 638 file: multiGraphTestFile, 639 expect: []string{"smart_person"}, 640 }, 641 { 642 message: "issue #758. Verify saveR respects label context.", 643 query: ` 644 g.V("smart_person").labelContext("<other_graph>").saveR("<status>", "who").all() 645 `, 646 tag: "who", 647 file: multiGraphTestFile, 648 expect: []string{"<fred>"}, 649 }, 650 { 651 message: "use order", 652 query: ` 653 g.V().order().all() 654 `, 655 expect: []string{ 656 "<alice>", 657 "<are>", 658 "<bob>", 659 "<charlie>", 660 "<dani>", 661 "<emily>", 662 "<follows>", 663 "<fred>", 664 "<greg>", 665 "<predicates>", 666 "<smart_graph>", 667 "<status>", 668 "cool_person", 669 "smart_person", 670 }, 671 }, 672 { 673 message: "use order tags", 674 query: ` 675 g.V().Tag("target").order().all() 676 `, 677 tag: "target", 678 expect: []string{ 679 "<alice>", 680 "<are>", 681 "<bob>", 682 "<charlie>", 683 "<dani>", 684 "<emily>", 685 "<follows>", 686 "<fred>", 687 "<greg>", 688 "<predicates>", 689 "<smart_graph>", 690 "<status>", 691 "cool_person", 692 "smart_person", 693 }, 694 }, 695 } 696 697 func runQueryGetTag(rec func(), g []quad.Quad, qu string, tag string, limit int) ([]string, error) { 698 js := makeTestSession(g) 699 ctx := context.TODO() 700 it, err := js.Execute(ctx, qu, query.Options{ 701 Collation: query.Raw, 702 Limit: limit, 703 }) 704 if err != nil { 705 return nil, err 706 } 707 defer it.Close() 708 defer rec() 709 710 var results []string 711 for it.Next(ctx) { 712 data := it.Result().(*Result) 713 if data.Val == nil { 714 if val := data.Tags[tag]; val != nil { 715 results = append(results, quadValueToString(js.qs.NameOf(val))) 716 } 717 } else { 718 switch v := data.Val.(type) { 719 case string: 720 results = append(results, v) 721 default: 722 results = append(results, fmt.Sprint(v)) 723 } 724 } 725 } 726 if err := it.Err(); err != nil { 727 return results, err 728 } 729 return results, nil 730 } 731 732 func TestGizmo(t *testing.T) { 733 734 simpleGraph := testutil.LoadGraph(t, "../../data/testdata.nq") 735 multiGraph := testutil.LoadGraph(t, multiGraphTestFile) 736 737 for _, test := range testQueries { 738 test := test 739 t.Run(test.message, func(t *testing.T) { 740 rec := func() { 741 if r := recover(); r != nil { 742 t.Errorf("Unexpected panic on %s: %v", test.message, r) 743 } 744 } 745 defer rec() 746 if test.tag == "" { 747 test.tag = TopResultTag 748 } 749 quads := simpleGraph 750 if test.file == multiGraphTestFile { 751 quads = multiGraph 752 } 753 754 if test.data != nil { 755 quads = test.data 756 } 757 limit := test.limit 758 if limit == 0 { 759 limit = -1 760 } 761 got, err := runQueryGetTag(rec, quads, test.query, test.tag, limit) 762 if err != nil { 763 if test.err { 764 return //expected 765 } 766 t.Error(err) 767 } 768 sort.Strings(got) 769 sort.Strings(test.expect) 770 if !reflect.DeepEqual(got, test.expect) { 771 t.Errorf("got: %v expected: %v", got, test.expect) 772 } 773 }) 774 } 775 } 776 777 var issue160TestGraph = []quad.Quad{ 778 quad.MakeRaw("alice", "follows", "bob", ""), 779 quad.MakeRaw("bob", "follows", "alice", ""), 780 quad.MakeRaw("charlie", "follows", "bob", ""), 781 quad.MakeRaw("dani", "follows", "charlie", ""), 782 quad.MakeRaw("dani", "follows", "alice", ""), 783 quad.MakeRaw("alice", "is", "cool", ""), 784 quad.MakeRaw("bob", "is", "not cool", ""), 785 quad.MakeRaw("charlie", "is", "cool", ""), 786 quad.MakeRaw("danie", "is", "not cool", ""), 787 } 788 789 func TestIssue160(t *testing.T) { 790 qu := `g.V().tag('query').out(raw('follows')).out(raw('follows')).forEach(function (item) { 791 if (item.id !== item.query) g.emit({ id: item.id }); 792 })` 793 expect := []string{ 794 "****\nid : alice\n", 795 "****\nid : bob\n", 796 "****\nid : bob\n", 797 } 798 799 ses := makeTestSession(issue160TestGraph) 800 ctx := context.TODO() 801 it, err := ses.Execute(ctx, qu, query.Options{ 802 Collation: query.REPL, 803 Limit: 100, 804 }) 805 if err != nil { 806 t.Fatal(err) 807 } 808 defer it.Close() 809 var got []string 810 for it.Next(ctx) { 811 func() { 812 defer func() { 813 if r := recover(); r != nil { 814 t.Errorf("Unexpected panic: %v", r) 815 } 816 }() 817 got = append(got, it.Result().(string)) 818 }() 819 } 820 sort.Strings(got) 821 if !reflect.DeepEqual(got, expect) { 822 t.Errorf("Unexpected result, got: %q expected: %q", got, expect) 823 } 824 } 825 826 func TestShapeOf(t *testing.T) { 827 ses := makeTestSession(nil) 828 const query = `g.V().forEach(function(x){ 829 g.emit({id: x.id}) 830 })` 831 _, err := ses.ShapeOf(query) 832 require.NoError(t, err) 833 } 834 835 const issue718Limit = 5 836 837 func issue718Graph() []quad.Quad { 838 var quads []quad.Quad 839 for i := 0; i < issue718Limit; i++ { 840 n := fmt.Sprintf("n%d", i+1) 841 quads = append(quads, quad.MakeIRI("a", "b", n, "")) 842 } 843 return quads 844 } 845 846 func issue718Nodes() []string { 847 var nodes []string 848 nodes = append(nodes, "<a>", "<b>") 849 for i := 0; i < issue718Limit-2; i++ { 850 n := fmt.Sprintf("<n%d>", i+1) 851 nodes = append(nodes, n) 852 } 853 return nodes 854 }