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  }