runtime_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. /*
  2. * EliasDB
  3. *
  4. * Copyright 2016 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. */
  10. package interpreter
  11. import (
  12. "encoding/json"
  13. "fmt"
  14. "testing"
  15. "devt.de/krotik/common/lang/graphql/parser"
  16. "devt.de/krotik/eliasdb/graph"
  17. "devt.de/krotik/eliasdb/graph/data"
  18. "devt.de/krotik/eliasdb/graph/graphstorage"
  19. )
  20. func TestNamedQueries(t *testing.T) {
  21. gm, _ := songGraphGroups()
  22. query := map[string]interface{}{
  23. "operationName": "bar",
  24. "query": `
  25. query foo {
  26. Song(key : "StrangeSong1") {
  27. name
  28. }
  29. }
  30. mutation bar {
  31. Song(key : "StrangeSong1"){
  32. key
  33. }
  34. }
  35. subscription foobar {
  36. Song(key : "StrangeSong1"){
  37. key
  38. name
  39. }
  40. }
  41. `,
  42. "variables": nil,
  43. }
  44. if rerr := checkResult(`
  45. {
  46. "data": {
  47. "Song": [
  48. {
  49. "key": "StrangeSong1"
  50. }
  51. ]
  52. }
  53. }`[1:], query, gm); rerr != nil {
  54. t.Error(rerr)
  55. return
  56. }
  57. query["operationName"] = "foobar"
  58. if rerr := checkResult(`
  59. {
  60. "data": {
  61. "Song": [
  62. {
  63. "key": "StrangeSong1",
  64. "name": "StrangeSong1"
  65. }
  66. ]
  67. }
  68. }`[1:], query, gm); rerr != nil {
  69. t.Error(rerr)
  70. return
  71. }
  72. }
  73. func TestInvalidRuntime(t *testing.T) {
  74. ast, _ := parser.ParseWithRuntime("", fmt.Sprint("{name}"), nil)
  75. rtp := NewGraphQLRuntimeProvider("test", "", nil, "", nil, nil, true)
  76. rt := &invalidRuntime{rtp, ast}
  77. if err := rt.Validate(); err.Error() != "Fatal GraphQL operation error in test: Invalid construct (Document) (Line:1 Pos:1)" {
  78. t.Error("Unexpected result:", err)
  79. return
  80. }
  81. if _, err := rt.Eval(); err.Error() != "Fatal GraphQL operation error in test: Invalid construct (Document) (Line:1 Pos:1)" {
  82. t.Error("Unexpected result:", err)
  83. return
  84. }
  85. }
  86. func TestDirectives(t *testing.T) {
  87. gm, _ := songGraphGroups()
  88. query := map[string]interface{}{
  89. "operationName": nil,
  90. "query": `
  91. query ($foo : String = "bar") {
  92. Song(key : $foo) {
  93. song_key : key
  94. ...kindField @include(if : false)
  95. foo : bar(traverse : ":::") @skip(if : true) {
  96. key
  97. kind
  98. Name : name
  99. }
  100. }
  101. }
  102. fragment kindField on Song {
  103. kind
  104. }
  105. `,
  106. "variables": map[string]interface{}{
  107. "foo": "StrangeSong1",
  108. },
  109. }
  110. if rerr := checkResult(`
  111. {
  112. "data": {
  113. "Song": [
  114. {
  115. "song_key": "StrangeSong1"
  116. }
  117. ]
  118. }
  119. }`[1:], query, gm); rerr != nil {
  120. t.Error(rerr)
  121. return
  122. }
  123. query = map[string]interface{}{
  124. "operationName": nil,
  125. "query": `
  126. query ($foo : String = "bar") {
  127. Song(key : $foo) {
  128. foo : bar(traverse : ":::") @skip() {
  129. key
  130. }
  131. }
  132. }
  133. `,
  134. "variables": map[string]interface{}{
  135. "foo": "StrangeSong1",
  136. },
  137. }
  138. if rerr := checkResult(`{
  139. "data": {
  140. "Song": [
  141. {
  142. "foo": [
  143. {
  144. "key": "123"
  145. },
  146. {
  147. "key": "Best"
  148. }
  149. ]
  150. }
  151. ]
  152. },
  153. "errors": [
  154. {
  155. "locations": [
  156. {
  157. "column": 31,
  158. "line": 4
  159. }
  160. ],
  161. "message": "Directive skip is missing the 'if' argument",
  162. "path": [
  163. "Song"
  164. ]
  165. }
  166. ]
  167. }`, query, gm); rerr != nil {
  168. t.Error(rerr)
  169. return
  170. }
  171. }
  172. func TestVariables(t *testing.T) {
  173. gm, _ := songGraphGroups()
  174. query := map[string]interface{}{
  175. "operationName": nil,
  176. "query": `
  177. query ($foo : String = "bar", $traverse : String = ":::") {
  178. Song(key : $foo) {
  179. song_key : key
  180. foo : bar(traverse : $traverse, x : $y) {
  181. key
  182. kind
  183. Name : name
  184. }
  185. }
  186. }
  187. `,
  188. "variables": map[string]interface{}{
  189. "foo": "StrangeSong1",
  190. },
  191. }
  192. if rerr := checkResult(`
  193. {
  194. "data": {
  195. "Song": [
  196. {
  197. "foo": [
  198. {
  199. "Name": "Mike",
  200. "key": "123",
  201. "kind": "Author"
  202. },
  203. {
  204. "Name": null,
  205. "key": "Best",
  206. "kind": "group"
  207. }
  208. ],
  209. "song_key": "StrangeSong1"
  210. }
  211. ]
  212. },
  213. "errors": [
  214. {
  215. "locations": [
  216. {
  217. "column": 40,
  218. "line": 5
  219. }
  220. ],
  221. "message": "Variable y was used but not declared",
  222. "path": []
  223. },
  224. {
  225. "locations": [
  226. {
  227. "column": 43,
  228. "line": 5
  229. }
  230. ],
  231. "message": "Unknown argument: x",
  232. "path": [
  233. "Song",
  234. ":::"
  235. ]
  236. }
  237. ]
  238. }`[1:], query, gm); rerr != nil {
  239. t.Error(rerr)
  240. return
  241. }
  242. }
  243. func TestRuntimeObjects(t *testing.T) {
  244. rtp := NewGraphQLRuntimeProvider("test", "", nil, "", nil, nil, true)
  245. ast, err := parser.ParseWithRuntime("test", `{
  246. Song(matches : { name : {
  247. IntValue : 1,
  248. FloatValue : 2.2,
  249. StringValue : "abc",
  250. BooleanValue : true,
  251. NullValue : null,
  252. EnumValue : TEST,
  253. ListValueConst : [1,2,3],
  254. ObjectValueConst : { foo : "bar", foo1 : "bar1", },
  255. }}) {
  256. song_key : key
  257. }
  258. }`, rtp)
  259. objectValue := ast.Children[0].Children[0].Children[0].Children[0].Children[1].Children[0].Children[1]
  260. rt := objectValue.Runtime.(*valueRuntime)
  261. actualResultBytes, _ := json.MarshalIndent(rt.Value(), "", " ")
  262. actualResult := string(actualResultBytes)
  263. if err != nil || actualResult != `{
  264. "name": {
  265. "BooleanValue": true,
  266. "EnumValue": "TEST",
  267. "FloatValue": 2.2,
  268. "IntValue": 1,
  269. "ListValueConst": [
  270. 1,
  271. 2,
  272. 3
  273. ],
  274. "NullValue": null,
  275. "ObjectValueConst": {
  276. "foo": "bar",
  277. "foo1": "bar1"
  278. },
  279. "StringValue": "abc"
  280. }
  281. }` {
  282. t.Error("Unexpected result:", actualResult, err)
  283. return
  284. }
  285. }
  286. func runQuery(name string, part string, query map[string]interface{},
  287. gm *graph.Manager, callbackHandler SubscriptionCallbackHandler,
  288. readOnly bool) (map[string]interface{}, error) {
  289. var ok bool
  290. var vars map[string]interface{}
  291. // Nil pointer become empty strings
  292. if query["operationName"] == nil {
  293. query["operationName"] = ""
  294. }
  295. if query["query"] == nil {
  296. query["query"] = ""
  297. }
  298. if vars, ok = query["variables"].(map[string]interface{}); !ok {
  299. vars = make(map[string]interface{})
  300. }
  301. // Create runtime provider
  302. rtp := NewGraphQLRuntimeProvider(name, part, gm,
  303. fmt.Sprint(query["operationName"]), vars, callbackHandler, readOnly)
  304. // Parse the query and annotate the AST with runtime components
  305. ast, err := parser.ParseWithRuntime(name, fmt.Sprint(query["query"]), rtp)
  306. if err == nil {
  307. // Purposefully skipping Validate() here to ensure it is called by Eval()
  308. // Evaluate the query
  309. return ast.Runtime.Eval()
  310. }
  311. return nil, err
  312. }
  313. func formatData(data interface{}) string {
  314. actualResultBytes, _ := json.MarshalIndent(data, "", " ")
  315. return string(actualResultBytes)
  316. }
  317. func checkResult(expectedResult string, query map[string]interface{}, gm *graph.Manager) error {
  318. actualResultObject, err := runQuery("test", "main", query, gm, nil, false)
  319. if err == nil {
  320. var actualResultBytes []byte
  321. actualResultBytes, err = json.MarshalIndent(actualResultObject, "", " ")
  322. actualResult := string(actualResultBytes)
  323. if err == nil {
  324. if expectedResult != actualResult {
  325. err = fmt.Errorf("Unexpected result:\nExpected:\n%s\nActual:\n%s",
  326. expectedResult, actualResult)
  327. }
  328. }
  329. }
  330. if expectedResult != "" && err != nil {
  331. ast, _ := parser.ParseWithRuntime("test", fmt.Sprint(query["query"]), nil)
  332. fmt.Println(ast)
  333. }
  334. return err
  335. }
  336. func songGraph() (*graph.Manager, *graphstorage.MemoryGraphStorage) {
  337. mgs := graphstorage.NewMemoryGraphStorage("mystorage")
  338. gm := graph.NewGraphManager(mgs)
  339. constructEdge := func(key string, node1 data.Node, node2 data.Node, number int) data.Edge {
  340. edge := data.NewGraphEdge()
  341. edge.SetAttr("key", key)
  342. edge.SetAttr("kind", "Wrote")
  343. edge.SetAttr(data.EdgeEnd1Key, node1.Key())
  344. edge.SetAttr(data.EdgeEnd1Kind, node1.Kind())
  345. edge.SetAttr(data.EdgeEnd1Role, "Author")
  346. edge.SetAttr(data.EdgeEnd1Cascading, true)
  347. edge.SetAttr(data.EdgeEnd2Key, node2.Key())
  348. edge.SetAttr(data.EdgeEnd2Kind, node2.Kind())
  349. edge.SetAttr(data.EdgeEnd2Role, "Song")
  350. edge.SetAttr(data.EdgeEnd2Cascading, false)
  351. edge.SetAttr("number", number)
  352. return edge
  353. }
  354. storeSong := func(node data.Node, name string, ranking int, number int) {
  355. node3 := data.NewGraphNode()
  356. node3.SetAttr("key", name)
  357. node3.SetAttr("kind", "Song")
  358. node3.SetAttr("name", name)
  359. node3.SetAttr("ranking", ranking)
  360. gm.StoreNode("main", node3)
  361. gm.StoreEdge("main", constructEdge(name, node, node3, number))
  362. }
  363. node0 := data.NewGraphNode()
  364. node0.SetAttr("key", "000")
  365. node0.SetAttr("kind", "Author")
  366. node0.SetAttr("name", "John")
  367. gm.StoreNode("main", node0)
  368. storeSong(node0, "Aria1", 8, 1)
  369. storeSong(node0, "Aria2", 2, 2)
  370. storeSong(node0, "Aria3", 4, 3)
  371. storeSong(node0, "Aria4", 18, 4)
  372. node1 := data.NewGraphNode()
  373. node1.SetAttr("key", "123")
  374. node1.SetAttr("kind", "Author")
  375. node1.SetAttr("name", "Mike")
  376. gm.StoreNode("main", node1)
  377. storeSong(node1, "LoveSong3", 1, 3)
  378. storeSong(node1, "FightSong4", 3, 4)
  379. storeSong(node1, "DeadSong2", 6, 2)
  380. storeSong(node1, "StrangeSong1", 5, 1)
  381. node2 := data.NewGraphNode()
  382. node2.SetAttr("key", "456")
  383. node2.SetAttr("kind", "Author")
  384. node2.SetAttr("name", "Hans")
  385. gm.StoreNode("main", node2)
  386. storeSong(node2, "MyOnlySong3", 19, 3)
  387. return gm, mgs.(*graphstorage.MemoryGraphStorage)
  388. }
  389. func songGraphGroups() (*graph.Manager, *graphstorage.MemoryGraphStorage) {
  390. gm, mgs := songGraph()
  391. node0 := data.NewGraphNode()
  392. node0.SetAttr("key", "Best")
  393. node0.SetAttr("kind", "group")
  394. node0.SetAttr("owner", "noowner")
  395. gm.StoreNode("main", node0)
  396. constructEdge := func(songkey string) data.Edge {
  397. edge := data.NewGraphEdge()
  398. edge.SetAttr("key", songkey)
  399. edge.SetAttr("kind", "Contains")
  400. edge.SetAttr(data.EdgeEnd1Key, node0.Key())
  401. edge.SetAttr(data.EdgeEnd1Kind, node0.Kind())
  402. edge.SetAttr(data.EdgeEnd1Role, "group")
  403. edge.SetAttr(data.EdgeEnd1Cascading, false)
  404. edge.SetAttr(data.EdgeEnd2Key, songkey)
  405. edge.SetAttr(data.EdgeEnd2Kind, "Song")
  406. edge.SetAttr(data.EdgeEnd2Role, "Song")
  407. edge.SetAttr(data.EdgeEnd2Cascading, false)
  408. return edge
  409. }
  410. gm.StoreEdge("main", constructEdge("LoveSong3"))
  411. gm.StoreEdge("main", constructEdge("Aria3"))
  412. gm.StoreEdge("main", constructEdge("MyOnlySong3"))
  413. gm.StoreEdge("main", constructEdge("StrangeSong1"))
  414. return gm, mgs
  415. }