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  }