| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 | /* * EliasDB * * Copyright 2016 Matthias Ladkau. All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */package interpreterimport (	"encoding/json"	"fmt"	"testing"	"devt.de/krotik/common/lang/graphql/parser"	"devt.de/krotik/eliasdb/graph"	"devt.de/krotik/eliasdb/graph/data"	"devt.de/krotik/eliasdb/graph/graphstorage")func TestNamedQueries(t *testing.T) {	gm, _ := songGraphGroups()	query := map[string]interface{}{		"operationName": "bar",		"query": `query foo {  Song(key : "StrangeSong1") {	name  }}mutation bar {  Song(key : "StrangeSong1"){	key  }}subscription foobar {  Song(key : "StrangeSong1"){	key	name  }}`,		"variables": nil,	}	if rerr := checkResult(`{  "data": {    "Song": [      {        "key": "StrangeSong1"      }    ]  }}`[1:], query, gm); rerr != nil {		t.Error(rerr)		return	}	query["operationName"] = "foobar"	if rerr := checkResult(`{  "data": {    "Song": [      {        "key": "StrangeSong1",        "name": "StrangeSong1"      }    ]  }}`[1:], query, gm); rerr != nil {		t.Error(rerr)		return	}}func TestInvalidRuntime(t *testing.T) {	ast, _ := parser.ParseWithRuntime("", fmt.Sprint("{name}"), nil)	rtp := NewGraphQLRuntimeProvider("test", "", nil, "", nil, nil, true)	rt := &invalidRuntime{rtp, ast}	if err := rt.Validate(); err.Error() != "Fatal GraphQL operation error in test: Invalid construct (Document) (Line:1 Pos:1)" {		t.Error("Unexpected result:", err)		return	}	if _, err := rt.Eval(); err.Error() != "Fatal GraphQL operation error in test: Invalid construct (Document) (Line:1 Pos:1)" {		t.Error("Unexpected result:", err)		return	}}func TestDirectives(t *testing.T) {	gm, _ := songGraphGroups()	query := map[string]interface{}{		"operationName": nil,		"query": `query ($foo : String = "bar") {  Song(key : $foo) {    song_key : key    ...kindField @include(if : false)	foo : bar(traverse : ":::") @skip(if : true) {		key		kind		Name : name	}  }}fragment kindField on Song {	kind}`,		"variables": map[string]interface{}{			"foo": "StrangeSong1",		},	}	if rerr := checkResult(`{  "data": {    "Song": [      {        "song_key": "StrangeSong1"      }    ]  }}`[1:], query, gm); rerr != nil {		t.Error(rerr)		return	}	query = map[string]interface{}{		"operationName": nil,		"query": `query ($foo : String = "bar") {  Song(key : $foo) {	foo : bar(traverse : ":::") @skip() {		key	}  }}`,		"variables": map[string]interface{}{			"foo": "StrangeSong1",		},	}	if rerr := checkResult(`{  "data": {    "Song": [      {        "foo": [          {            "key": "123"          },          {            "key": "Best"          }        ]      }    ]  },  "errors": [    {      "locations": [        {          "column": 31,          "line": 4        }      ],      "message": "Directive skip is missing the 'if' argument",      "path": [        "Song"      ]    }  ]}`, query, gm); rerr != nil {		t.Error(rerr)		return	}}func TestVariables(t *testing.T) {	gm, _ := songGraphGroups()	query := map[string]interface{}{		"operationName": nil,		"query": `query ($foo : String = "bar", $traverse : String = ":::") {  Song(key : $foo) {    song_key : key	foo : bar(traverse : $traverse, x : $y) {		key		kind		Name : name	}  }}`,		"variables": map[string]interface{}{			"foo": "StrangeSong1",		},	}	if rerr := checkResult(`{  "data": {    "Song": [      {        "foo": [          {            "Name": "Mike",            "key": "123",            "kind": "Author"          },          {            "Name": null,            "key": "Best",            "kind": "group"          }        ],        "song_key": "StrangeSong1"      }    ]  },  "errors": [    {      "locations": [        {          "column": 40,          "line": 5        }      ],      "message": "Variable y was used but not declared",      "path": []    },    {      "locations": [        {          "column": 43,          "line": 5        }      ],      "message": "Unknown argument: x",      "path": [        "Song",        ":::"      ]    }  ]}`[1:], query, gm); rerr != nil {		t.Error(rerr)		return	}}func TestRuntimeObjects(t *testing.T) {	rtp := NewGraphQLRuntimeProvider("test", "", nil, "", nil, nil, true)	ast, err := parser.ParseWithRuntime("test", `{	     Song(matches : { name : {	       IntValue : 1,	       FloatValue : 2.2,	       StringValue : "abc",	       BooleanValue : true,	       NullValue : null,	       EnumValue : TEST,	       ListValueConst : [1,2,3],	       ObjectValueConst : { foo : "bar", foo1 : "bar1",  },	   }}) {	       song_key : key	     }	   }`, rtp)	objectValue := ast.Children[0].Children[0].Children[0].Children[0].Children[1].Children[0].Children[1]	rt := objectValue.Runtime.(*valueRuntime)	actualResultBytes, _ := json.MarshalIndent(rt.Value(), "", "  ")	actualResult := string(actualResultBytes)	if err != nil || actualResult != `{  "name": {    "BooleanValue": true,    "EnumValue": "TEST",    "FloatValue": 2.2,    "IntValue": 1,    "ListValueConst": [      1,      2,      3    ],    "NullValue": null,    "ObjectValueConst": {      "foo": "bar",      "foo1": "bar1"    },    "StringValue": "abc"  }}` {		t.Error("Unexpected result:", actualResult, err)		return	}}func runQuery(name string, part string, query map[string]interface{},	gm *graph.Manager, callbackHandler SubscriptionCallbackHandler,	readOnly bool) (map[string]interface{}, error) {	var ok bool	var vars map[string]interface{}	// Nil pointer become empty strings	if query["operationName"] == nil {		query["operationName"] = ""	}	if query["query"] == nil {		query["query"] = ""	}	if vars, ok = query["variables"].(map[string]interface{}); !ok {		vars = make(map[string]interface{})	}	// Create runtime provider	rtp := NewGraphQLRuntimeProvider(name, part, gm,		fmt.Sprint(query["operationName"]), vars, callbackHandler, readOnly)	// Parse the query and annotate the AST with runtime components	ast, err := parser.ParseWithRuntime(name, fmt.Sprint(query["query"]), rtp)	if err == nil {		// Purposefully skipping Validate() here to ensure it is called by Eval()		// Evaluate the query		return ast.Runtime.Eval()	}	return nil, err}func formatData(data interface{}) string {	actualResultBytes, _ := json.MarshalIndent(data, "", "  ")	return string(actualResultBytes)}func checkResult(expectedResult string, query map[string]interface{}, gm *graph.Manager) error {	actualResultObject, err := runQuery("test", "main", query, gm, nil, false)	if err == nil {		var actualResultBytes []byte		actualResultBytes, err = json.MarshalIndent(actualResultObject, "", "  ")		actualResult := string(actualResultBytes)		if err == nil {			if expectedResult != actualResult {				err = fmt.Errorf("Unexpected result:\nExpected:\n%s\nActual:\n%s",					expectedResult, actualResult)			}		}	}	if expectedResult != "" && err != nil {		ast, _ := parser.ParseWithRuntime("test", fmt.Sprint(query["query"]), nil)		fmt.Println(ast)	}	return err}func songGraph() (*graph.Manager, *graphstorage.MemoryGraphStorage) {	mgs := graphstorage.NewMemoryGraphStorage("mystorage")	gm := graph.NewGraphManager(mgs)	constructEdge := func(key string, node1 data.Node, node2 data.Node, number int) data.Edge {		edge := data.NewGraphEdge()		edge.SetAttr("key", key)		edge.SetAttr("kind", "Wrote")		edge.SetAttr(data.EdgeEnd1Key, node1.Key())		edge.SetAttr(data.EdgeEnd1Kind, node1.Kind())		edge.SetAttr(data.EdgeEnd1Role, "Author")		edge.SetAttr(data.EdgeEnd1Cascading, true)		edge.SetAttr(data.EdgeEnd2Key, node2.Key())		edge.SetAttr(data.EdgeEnd2Kind, node2.Kind())		edge.SetAttr(data.EdgeEnd2Role, "Song")		edge.SetAttr(data.EdgeEnd2Cascading, false)		edge.SetAttr("number", number)		return edge	}	storeSong := func(node data.Node, name string, ranking int, number int) {		node3 := data.NewGraphNode()		node3.SetAttr("key", name)		node3.SetAttr("kind", "Song")		node3.SetAttr("name", name)		node3.SetAttr("ranking", ranking)		gm.StoreNode("main", node3)		gm.StoreEdge("main", constructEdge(name, node, node3, number))	}	node0 := data.NewGraphNode()	node0.SetAttr("key", "000")	node0.SetAttr("kind", "Author")	node0.SetAttr("name", "John")	gm.StoreNode("main", node0)	storeSong(node0, "Aria1", 8, 1)	storeSong(node0, "Aria2", 2, 2)	storeSong(node0, "Aria3", 4, 3)	storeSong(node0, "Aria4", 18, 4)	node1 := data.NewGraphNode()	node1.SetAttr("key", "123")	node1.SetAttr("kind", "Author")	node1.SetAttr("name", "Mike")	gm.StoreNode("main", node1)	storeSong(node1, "LoveSong3", 1, 3)	storeSong(node1, "FightSong4", 3, 4)	storeSong(node1, "DeadSong2", 6, 2)	storeSong(node1, "StrangeSong1", 5, 1)	node2 := data.NewGraphNode()	node2.SetAttr("key", "456")	node2.SetAttr("kind", "Author")	node2.SetAttr("name", "Hans")	gm.StoreNode("main", node2)	storeSong(node2, "MyOnlySong3", 19, 3)	return gm, mgs.(*graphstorage.MemoryGraphStorage)}func songGraphGroups() (*graph.Manager, *graphstorage.MemoryGraphStorage) {	gm, mgs := songGraph()	node0 := data.NewGraphNode()	node0.SetAttr("key", "Best")	node0.SetAttr("kind", "group")	node0.SetAttr("owner", "noowner")	gm.StoreNode("main", node0)	constructEdge := func(songkey string) data.Edge {		edge := data.NewGraphEdge()		edge.SetAttr("key", songkey)		edge.SetAttr("kind", "Contains")		edge.SetAttr(data.EdgeEnd1Key, node0.Key())		edge.SetAttr(data.EdgeEnd1Kind, node0.Kind())		edge.SetAttr(data.EdgeEnd1Role, "group")		edge.SetAttr(data.EdgeEnd1Cascading, false)		edge.SetAttr(data.EdgeEnd2Key, songkey)		edge.SetAttr(data.EdgeEnd2Kind, "Song")		edge.SetAttr(data.EdgeEnd2Role, "Song")		edge.SetAttr(data.EdgeEnd2Cascading, false)		return edge	}	gm.StoreEdge("main", constructEdge("LoveSong3"))	gm.StoreEdge("main", constructEdge("Aria3"))	gm.StoreEdge("main", constructEdge("MyOnlySong3"))	gm.StoreEdge("main", constructEdge("StrangeSong1"))	return gm, mgs}
 |