graph_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  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 v1
  11. import (
  12. "encoding/json"
  13. "fmt"
  14. "testing"
  15. "devt.de/krotik/common/datautil"
  16. "devt.de/krotik/eliasdb/api"
  17. "devt.de/krotik/eliasdb/graph"
  18. "devt.de/krotik/eliasdb/graph/data"
  19. "devt.de/krotik/eliasdb/hash"
  20. "devt.de/krotik/eliasdb/storage"
  21. )
  22. func TestNestedStorage(t *testing.T) {
  23. queryURL := "http://localhost" + TESTPORT + EndpointGraph
  24. // Store a nested node
  25. st, _, res := sendTestRequest(queryURL+"main/n", "POST", []byte(`
  26. [{
  27. "key":"nestedtest",
  28. "kind":"Test",
  29. "int":42,
  30. "float":3.1415926,
  31. "str":"foo bar",
  32. "nested":{
  33. "nested_int":12,
  34. "nested_float":1.234,
  35. "nested_str":"time flies like an arrow",
  36. "more nesting": {
  37. "atom" : "value42"
  38. }
  39. }
  40. }]
  41. `[1:]))
  42. if st != "200 OK" {
  43. t.Error("Unexpected response:", st, res)
  44. return
  45. }
  46. n, err := api.GM.FetchNode("main", "nestedtest", "Test")
  47. if err != nil {
  48. t.Error(err)
  49. return
  50. }
  51. // Check that the node was stored correctly
  52. nested := n.Attr("nested")
  53. if nt := fmt.Sprintf("%T", nested); nt != "map[string]interface {}" {
  54. t.Error("Unexpected type:", nt)
  55. return
  56. }
  57. nf, err := datautil.GetNestedValue(nested.(map[string]interface{}), []string{"nested_float"})
  58. if nft := fmt.Sprintf("%T %v", nf, nf); nft != "float64 1.234" || err != nil {
  59. t.Error("Unexpected type:", nft, err)
  60. return
  61. }
  62. ns, err := datautil.GetNestedValue(nested.(map[string]interface{}), []string{"more nesting", "atom"})
  63. if nst := fmt.Sprintf("%T %v", ns, ns); nst != "string value42" || err != nil {
  64. t.Error("Unexpected type:", nst, err)
  65. return
  66. }
  67. // Now try to retrieve the value
  68. st, _, res = sendTestRequest(queryURL+"/main/n/Test/nestedtest", "GET", nil)
  69. if st != "200 OK" || res != `
  70. {
  71. "float": 3.1415926,
  72. "int": 42,
  73. "key": "nestedtest",
  74. "kind": "Test",
  75. "nested": {
  76. "more nesting": {
  77. "atom": "value42"
  78. },
  79. "nested_float": 1.234,
  80. "nested_int": 12,
  81. "nested_str": "time flies like an arrow"
  82. },
  83. "str": "foo bar"
  84. }`[1:] {
  85. t.Error("Unexpected response:", st, res)
  86. return
  87. }
  88. }
  89. func TestGraphQuery(t *testing.T) {
  90. queryURL := "http://localhost" + TESTPORT + EndpointGraph
  91. // Test error message
  92. _, _, res := sendTestRequest(queryURL, "GET", nil)
  93. if res != "Need a partition, entity type (n or e) and a kind; optional key and traversal spec" {
  94. t.Error("Unexpected response:", res)
  95. return
  96. }
  97. _, _, res = sendTestRequest(queryURL+"/main/t/Song", "GET", nil)
  98. if res != "Entity type must be n (nodes) or e (edges)" {
  99. t.Error("Unexpected response:", res)
  100. return
  101. }
  102. _, _, res = sendTestRequest(queryURL+"/main/e/Song", "GET", nil)
  103. if res != "Entity type must be n (nodes) when requesting all items" {
  104. t.Error("Unexpected response:", res)
  105. return
  106. }
  107. _, _, res = sendTestRequest(queryURL+"/main/n/SSong", "GET", nil)
  108. if res != "Unknown partition or node kind" {
  109. t.Error("Unexpected response:", res)
  110. return
  111. }
  112. _, _, res = sendTestRequest(queryURL+"/xmain/n/Song", "GET", nil)
  113. if res != "Unknown partition or node kind" {
  114. t.Error("Unexpected response:", res)
  115. return
  116. }
  117. st, h, res := sendTestRequest(queryURL+"/main/n/Song", "GET", nil)
  118. if tc := h.Get(HTTPHeaderTotalCount); tc != "9" {
  119. t.Error("Unexpected total count header:", tc)
  120. return
  121. }
  122. if st != "200 OK" || res != `
  123. [
  124. {
  125. "key": "StrangeSong1",
  126. "kind": "Song",
  127. "name": "StrangeSong1",
  128. "ranking": 5
  129. },
  130. {
  131. "key": "FightSong4",
  132. "kind": "Song",
  133. "name": "FightSong4",
  134. "ranking": 3
  135. },
  136. {
  137. "key": "DeadSong2",
  138. "kind": "Song",
  139. "name": "DeadSong2",
  140. "ranking": 6
  141. },
  142. {
  143. "key": "LoveSong3",
  144. "kind": "Song",
  145. "name": "LoveSong3",
  146. "ranking": 1
  147. },
  148. {
  149. "key": "MyOnlySong3",
  150. "kind": "Song",
  151. "name": "MyOnlySong3",
  152. "ranking": 19
  153. },
  154. {
  155. "key": "Aria1",
  156. "kind": "Song",
  157. "name": "Aria1",
  158. "ranking": 8
  159. },
  160. {
  161. "key": "Aria2",
  162. "kind": "Song",
  163. "name": "Aria2",
  164. "ranking": 2
  165. },
  166. {
  167. "key": "Aria3",
  168. "kind": "Song",
  169. "name": "Aria3",
  170. "ranking": 4
  171. },
  172. {
  173. "key": "Aria4",
  174. "kind": "Song",
  175. "name": "Aria4",
  176. "ranking": 18
  177. }
  178. ]`[1:] {
  179. t.Error("Unexpected response:", st, res)
  180. return
  181. }
  182. // Test offset and limit
  183. st, _, res = sendTestRequest(queryURL+"/main/n/Song?offset=3&limit=2", "GET", nil)
  184. if st != "200 OK" || res != `
  185. [
  186. {
  187. "key": "LoveSong3",
  188. "kind": "Song",
  189. "name": "LoveSong3",
  190. "ranking": 1
  191. },
  192. {
  193. "key": "MyOnlySong3",
  194. "kind": "Song",
  195. "name": "MyOnlySong3",
  196. "ranking": 19
  197. }
  198. ]`[1:] {
  199. t.Error("Unexpected response:", st, res)
  200. return
  201. }
  202. st, _, res = sendTestRequest(queryURL+"/main/n/Song?offset=7&limit=200", "GET", nil)
  203. if st != "200 OK" || res != `
  204. [
  205. {
  206. "key": "Aria3",
  207. "kind": "Song",
  208. "name": "Aria3",
  209. "ranking": 4
  210. },
  211. {
  212. "key": "Aria4",
  213. "kind": "Song",
  214. "name": "Aria4",
  215. "ranking": 18
  216. }
  217. ]`[1:] {
  218. t.Error("Unexpected response:", st, res)
  219. return
  220. }
  221. st, _, res = sendTestRequest(queryURL+"/main/n/Song?offset=p&limit=2", "GET", nil)
  222. if st != "400 Bad Request" || res != "Invalid parameter value: offset should be a positive integer number" {
  223. t.Error("Unexpected response:", st, res)
  224. return
  225. }
  226. st, _, res = sendTestRequest(queryURL+"/main/n/Song?offset=2&limit=p", "GET", nil)
  227. if st != "400 Bad Request" || res != "Invalid parameter value: limit should be a positive integer number" {
  228. t.Error("Unexpected response:", st, res)
  229. return
  230. }
  231. st, _, res = sendTestRequest(queryURL+"/main/n/Song?offset=700&limit=2", "GET", nil)
  232. if st != "500 Internal Server Error" || res != "Offset exceeds available nodes" {
  233. t.Error("Unexpected response:", st, res)
  234. return
  235. }
  236. // Test error cases
  237. msm := gmMSM.StorageManager("main"+"Song"+graph.StorageSuffixNodes,
  238. true).(*storage.MemoryStorageManager)
  239. msm.AccessMap[2] = storage.AccessCacheAndFetchError
  240. st, _, res = sendTestRequest(queryURL+"/main/n/Song", "GET", nil)
  241. if st != "500 Internal Server Error" ||
  242. res != "GraphError: Failed to access graph storage component (Slot not found (mystorage/mainSong.nodes - Location:2))" {
  243. t.Error("Unexpected response:", res)
  244. return
  245. }
  246. delete(msm.AccessMap, 2)
  247. msm.AccessMap[4] = storage.AccessCacheAndFetchError
  248. st, _, res = sendTestRequest(queryURL+"/main/n/Song", "GET", nil)
  249. if st != "500 Internal Server Error" ||
  250. res != "GraphError: Could not read graph information (Slot not found (mystorage/mainSong.nodes - Location:4))" {
  251. t.Error("Unexpected response:", res)
  252. return
  253. }
  254. delete(msm.AccessMap, 4)
  255. msm = gmMSM.StorageManager("main"+"Spam"+graph.StorageSuffixNodes,
  256. true).(*storage.MemoryStorageManager)
  257. loc := msm.Root(graph.RootIDNodeHTree)
  258. htree, _ := hash.LoadHTree(msm, loc)
  259. _, kloc, _ := htree.GetValueAndLocation([]byte(graph.PrefixNSAttrs + "00019"))
  260. msm.AccessMap[kloc] = storage.AccessCacheAndFetchSeriousError
  261. st, _, res = sendTestRequest(queryURL+"/main/n/Spam?offset=19&limit=1", "GET", nil)
  262. if st != "500 Internal Server Error" ||
  263. res != "GraphError: Could not read graph information (Record is already in-use (<memory> - ))" {
  264. t.Error("Unexpected response:", res)
  265. return
  266. }
  267. st, _, res = sendTestRequest(queryURL+"/main/n/Spam", "GET", nil)
  268. if st != "500 Internal Server Error" ||
  269. res != "GraphError: Could not read graph information (Record is already in-use (<memory> - ))" {
  270. t.Error("Unexpected response:", res)
  271. return
  272. }
  273. delete(msm.AccessMap, kloc)
  274. }
  275. func TestGraphQuerySingleItem(t *testing.T) {
  276. queryURL := "http://localhost" + TESTPORT + EndpointGraph
  277. st, _, res := sendTestRequest(queryURL+"/main/n/Author/123", "GET", nil)
  278. if st != "200 OK" || res != `
  279. {
  280. "key": "123",
  281. "kind": "Author",
  282. "name": "Mike"
  283. }`[1:] {
  284. t.Error("Unexpected response:", st, res)
  285. return
  286. }
  287. st, _, res = sendTestRequest(queryURL+"/main/e/Wrote/LoveSong3", "GET", nil)
  288. if st != "200 OK" || res != `
  289. {
  290. "end1cascading": true,
  291. "end1key": "123",
  292. "end1kind": "Author",
  293. "end1role": "Author",
  294. "end2cascading": false,
  295. "end2key": "LoveSong3",
  296. "end2kind": "Song",
  297. "end2role": "Song",
  298. "key": "LoveSong3",
  299. "kind": "Wrote",
  300. "number": 3
  301. }`[1:] {
  302. t.Error("Unexpected response:", st, res)
  303. return
  304. }
  305. // Test error cases
  306. st, _, res = sendTestRequest(queryURL+"/main/n/Spam/x0005", "GET", nil)
  307. if st != "400 Bad Request" ||
  308. res != "Unknown partition or node kind" {
  309. t.Error("Unexpected response:", st, res)
  310. return
  311. }
  312. st, _, res = sendTestRequest(queryURL+"/main/e/xSpam/0005", "GET", nil)
  313. if st != "400 Bad Request" ||
  314. res != "Unknown partition or edge kind" {
  315. t.Error("Unexpected response:", st, res)
  316. return
  317. }
  318. msm := gmMSM.StorageManager("main"+"Spam"+graph.StorageSuffixNodes,
  319. true).(*storage.MemoryStorageManager)
  320. msm.AccessMap[2] = storage.AccessCacheAndFetchError
  321. st, _, res = sendTestRequest(queryURL+"/main/n/Spam/0005", "GET", nil)
  322. if st != "500 Internal Server Error" ||
  323. res != "GraphError: Failed to access graph storage component (Slot not found (mystorage/mainSpam.nodes - Location:2))" {
  324. t.Error("Unexpected response:", res)
  325. return
  326. }
  327. delete(msm.AccessMap, 2)
  328. msm = gmMSM.StorageManager("main"+"Wrote"+graph.StorageSuffixEdges,
  329. true).(*storage.MemoryStorageManager)
  330. msm.AccessMap[1] = storage.AccessCacheAndFetchError
  331. st, _, res = sendTestRequest(queryURL+"/main/e/Wrote/LoveSong3", "GET", nil)
  332. if st != "500 Internal Server Error" ||
  333. res != "GraphError: Failed to access graph storage component (Slot not found (mystorage/mainWrote.edges - Location:1))" {
  334. t.Error("Unexpected response:", res)
  335. return
  336. }
  337. delete(msm.AccessMap, 1)
  338. }
  339. func TestGraphQueryTraversal(t *testing.T) {
  340. queryURL := "http://localhost" + TESTPORT + EndpointGraph
  341. _, _, res := sendTestRequest(queryURL+"/main/n/Author/123/:::/aaa", "GET", nil)
  342. if res != "Invalid resource specification: n/Author/123/:::/aaa" {
  343. t.Error("Unexpected response:", res)
  344. return
  345. }
  346. _, _, res = sendTestRequest(queryURL+"/main/e/Author/123/:::", "GET", nil)
  347. if res != "Entity type must be n (nodes) when requesting traversal results" {
  348. t.Error("Unexpected response:", res)
  349. return
  350. }
  351. st, _, res := sendTestRequest(queryURL+"/main/n/Author/123/:::", "GET", nil)
  352. if st != "200 OK" || res != `
  353. [
  354. [
  355. {
  356. "key": "DeadSong2",
  357. "kind": "Song",
  358. "name": "DeadSong2",
  359. "ranking": 6
  360. },
  361. {
  362. "key": "FightSong4",
  363. "kind": "Song",
  364. "name": "FightSong4",
  365. "ranking": 3
  366. },
  367. {
  368. "key": "LoveSong3",
  369. "kind": "Song",
  370. "name": "LoveSong3",
  371. "ranking": 1
  372. },
  373. {
  374. "key": "StrangeSong1",
  375. "kind": "Song",
  376. "name": "StrangeSong1",
  377. "ranking": 5
  378. }
  379. ],
  380. [
  381. {
  382. "end1cascading": true,
  383. "end1key": "123",
  384. "end1kind": "Author",
  385. "end1role": "Author",
  386. "end2cascading": false,
  387. "end2key": "DeadSong2",
  388. "end2kind": "Song",
  389. "end2role": "Song",
  390. "key": "DeadSong2",
  391. "kind": "Wrote",
  392. "number": 2
  393. },
  394. {
  395. "end1cascading": true,
  396. "end1key": "123",
  397. "end1kind": "Author",
  398. "end1role": "Author",
  399. "end2cascading": false,
  400. "end2key": "FightSong4",
  401. "end2kind": "Song",
  402. "end2role": "Song",
  403. "key": "FightSong4",
  404. "kind": "Wrote",
  405. "number": 4
  406. },
  407. {
  408. "end1cascading": true,
  409. "end1key": "123",
  410. "end1kind": "Author",
  411. "end1role": "Author",
  412. "end2cascading": false,
  413. "end2key": "LoveSong3",
  414. "end2kind": "Song",
  415. "end2role": "Song",
  416. "key": "LoveSong3",
  417. "kind": "Wrote",
  418. "number": 3
  419. },
  420. {
  421. "end1cascading": true,
  422. "end1key": "123",
  423. "end1kind": "Author",
  424. "end1role": "Author",
  425. "end2cascading": false,
  426. "end2key": "StrangeSong1",
  427. "end2kind": "Song",
  428. "end2role": "Song",
  429. "key": "StrangeSong1",
  430. "kind": "Wrote",
  431. "number": 1
  432. }
  433. ]
  434. ]`[1:] {
  435. t.Error("Unexpected response:", st, res)
  436. return
  437. }
  438. st, _, res = sendTestRequest(queryURL+"/main/n/Spam/0005/:::", "GET", nil)
  439. if st != "200 OK" || res != `
  440. [
  441. [],
  442. []
  443. ]`[1:] {
  444. t.Error("Unexpected response:", st, res)
  445. return
  446. }
  447. // Test error cases
  448. st, _, res = sendTestRequest(queryURL+"/main/n/Spam/x0005/:::", "GET", nil)
  449. if st != "400 Bad Request" ||
  450. res != "Unknown partition or node kind" {
  451. t.Error("Unexpected response:", st, res)
  452. return
  453. }
  454. msm := gmMSM.StorageManager("main"+"Song"+graph.StorageSuffixNodes,
  455. true).(*storage.MemoryStorageManager)
  456. msm.AccessMap[2] = storage.AccessCacheAndFetchError
  457. st, _, res = sendTestRequest(queryURL+"/main/n/Author/123/:::", "GET", nil)
  458. if st != "500 Internal Server Error" ||
  459. res != "GraphError: Failed to access graph storage component (Slot not found (mystorage/mainSong.nodes - Location:2))" {
  460. t.Error("Unexpected response:", res)
  461. return
  462. }
  463. delete(msm.AccessMap, 2)
  464. msm = gmMSM.StorageManager("main"+"Spam"+graph.StorageSuffixNodes,
  465. true).(*storage.MemoryStorageManager)
  466. msm.AccessMap[2] = storage.AccessCacheAndFetchError
  467. st, _, res = sendTestRequest(queryURL+"/main/n/Spam/0005/:::", "GET", nil)
  468. if st != "500 Internal Server Error" ||
  469. res != "GraphError: Failed to access graph storage component (Slot not found (mystorage/mainSpam.nodes - Location:2))" {
  470. t.Error("Unexpected response:", res)
  471. return
  472. }
  473. delete(msm.AccessMap, 2)
  474. }
  475. func TestGraphOperation(t *testing.T) {
  476. queryURL := "http://localhost" + TESTPORT + EndpointGraph
  477. // Test error message
  478. _, _, res := sendTestRequest(queryURL, "POST", nil)
  479. if res != "Need a partition; optional entity type (n or e)" {
  480. t.Error("Unexpected response:", res)
  481. return
  482. }
  483. // Test node creation
  484. node := data.NewGraphNode()
  485. node.SetAttr("key", "111")
  486. node.SetAttr("name", "node1")
  487. node2 := data.NewGraphNode()
  488. node2.SetAttr("key", "112")
  489. node2.SetAttr("name", "node2")
  490. jsonString, err := json.Marshal([]map[string]interface{}{node.Data(), node2.Data()})
  491. if err != nil {
  492. t.Error(err)
  493. return
  494. }
  495. st, _, res := sendTestRequest(queryURL+"main/n", "POST", []byte(jsonString[5:]))
  496. if st != "400 Bad Request" ||
  497. res != "Could not decode request body as list of nodes: invalid character 'y' looking for beginning of value" {
  498. t.Error("Unexpected response:", st, res)
  499. return
  500. }
  501. st, _, res = sendTestRequest(queryURL+"main/n", "POST", []byte(jsonString))
  502. if st != "400 Bad Request" ||
  503. res != "GraphError: Invalid data (Node is missing a kind value)" {
  504. t.Error("Unexpected response:", st, res)
  505. return
  506. }
  507. node.SetAttr("kind", "graphtest")
  508. node2.SetAttr("kind", "graphtest")
  509. jsonString, err = json.Marshal([]map[string]interface{}{node.Data(), node2.Data()})
  510. if err != nil {
  511. t.Error(err)
  512. return
  513. }
  514. st, _, res = sendTestRequest(queryURL+"main/n", "POST", []byte(jsonString))
  515. if st != "200 OK" {
  516. t.Error("Unexpected response:", st, res)
  517. return
  518. }
  519. // Check that the node was stored
  520. n, err := api.GM.FetchNode("main", "111", "graphtest")
  521. if err != nil {
  522. t.Error(err)
  523. return
  524. }
  525. if !data.NodeCompare(n, node, nil) {
  526. t.Error("Stored node does not match given node")
  527. return
  528. }
  529. n, err = api.GM.FetchNode("main", "112", "graphtest")
  530. if err != nil {
  531. t.Error(err)
  532. return
  533. }
  534. if !data.NodeCompare(n, node2, nil) {
  535. t.Error("Stored node does not match given node")
  536. return
  537. }
  538. // Try to store a relationship
  539. edge := data.NewGraphEdge()
  540. edge.SetAttr("key", "123")
  541. edge.SetAttr("kind", "testrel")
  542. edge.SetAttr(data.EdgeEnd1Kind, node.Kind())
  543. edge.SetAttr(data.EdgeEnd1Role, "node1")
  544. edge.SetAttr(data.EdgeEnd1Cascading, false)
  545. edge.SetAttr(data.EdgeEnd2Key, node2.Key())
  546. edge.SetAttr(data.EdgeEnd2Kind, node2.Kind())
  547. edge.SetAttr(data.EdgeEnd2Role, "node2")
  548. edge.SetAttr(data.EdgeEnd2Cascading, false)
  549. jsonString, err = json.Marshal([]map[string]interface{}{edge.Data()})
  550. if err != nil {
  551. t.Error(err)
  552. return
  553. }
  554. st, _, res = sendTestRequest(queryURL+"main/e", "POST", []byte(jsonString))
  555. if st != "400 Bad Request" ||
  556. res != "GraphError: Invalid data (Edge is missing a key value for end1)" {
  557. t.Error("Unexpected response:", st, res)
  558. return
  559. }
  560. edge.SetAttr(data.EdgeEnd1Key, "foo")
  561. jsonString, err = json.Marshal([]map[string]interface{}{edge.Data()})
  562. if err != nil {
  563. t.Error(err)
  564. return
  565. }
  566. st, _, res = sendTestRequest(queryURL+"main/e", "POST", []byte(jsonString))
  567. if st != "500 Internal Server Error" ||
  568. res != "GraphError: Invalid data (Can't find edge endpoint: foo (graphtest))" {
  569. t.Error("Unexpected response:", st, res)
  570. return
  571. }
  572. edge.SetAttr(data.EdgeEnd1Key, node.Key())
  573. jsonString, err = json.Marshal([]map[string]interface{}{edge.Data()})
  574. if err != nil {
  575. t.Error(err)
  576. return
  577. }
  578. st, _, res = sendTestRequest(queryURL+"main/e", "POST", []byte(jsonString[5:]))
  579. if st != "400 Bad Request" ||
  580. res != "Could not decode request body as list of edges: invalid character 'd' looking for beginning of value" {
  581. t.Error("Unexpected response:", st, res)
  582. return
  583. }
  584. st, _, res = sendTestRequest(queryURL+"main/e", "POST", []byte(jsonString))
  585. if st != "200 OK" {
  586. t.Error("Unexpected response:", st, res)
  587. return
  588. }
  589. // Check that the edge was stored
  590. tn, te, err := api.GM.TraverseMulti("main", node.Key(), node.Kind(), ":::", true)
  591. if err != nil {
  592. t.Error(err)
  593. return
  594. }
  595. if !data.NodeCompare(tn[0], node2, nil) {
  596. t.Error("Stored node does not match given node")
  597. return
  598. }
  599. if !data.NodeCompare(te[0], edge, nil) {
  600. t.Error("Stored edge does not match given node")
  601. return
  602. }
  603. // Update nodes
  604. st, _, res = sendTestRequest(queryURL+"main", "PUT", []byte(jsonString))
  605. if st != "400 Bad Request" ||
  606. res != "Could not decode request body as object with list of nodes and/or edges: json: cannot unmarshal array into Go value of type map[string][]map[string]interface {}" {
  607. t.Error("Unexpected response:", st, res)
  608. return
  609. }
  610. node.SetAttr("name", "updatenode1")
  611. node2.SetAttr("name", "updatenode2")
  612. edge.SetAttr("name", "updateedge")
  613. jsonString, err = json.Marshal(map[string][]map[string]interface{}{
  614. "nodes": {node.Data(), node2.Data()},
  615. "edges": {edge.Data()},
  616. })
  617. if err != nil {
  618. t.Error(err)
  619. return
  620. }
  621. st, _, res = sendTestRequest(queryURL+"main", "PUT", []byte(jsonString))
  622. if st != "200 OK" {
  623. t.Error("Unexpected response:", st, res)
  624. return
  625. }
  626. // Check that the nodes and the edge were updated
  627. tn, te, err = api.GM.TraverseMulti("main", node.Key(), node.Kind(), ":::", true)
  628. if err != nil {
  629. t.Error(err)
  630. return
  631. }
  632. if !data.NodeCompare(tn[0], node2, nil) {
  633. t.Error("Stored node does not match given node")
  634. return
  635. }
  636. if !data.NodeCompare(te[0], edge, nil) {
  637. t.Error("Stored edge does not match given node")
  638. return
  639. }
  640. // Delete edge
  641. jsonString, _ = json.Marshal(map[string][]map[string]interface{}{
  642. "nodes": {},
  643. "edges": {
  644. {
  645. "key": edge.Key(),
  646. "kind": edge.Kind(),
  647. },
  648. },
  649. })
  650. st, _, res = sendTestRequest(queryURL+"main", "DELETE", []byte(jsonString))
  651. if st != "200 OK" {
  652. t.Error("Unexpected response:", st, res)
  653. return
  654. }
  655. // Check that the edge no longer exists
  656. tn, _, err = api.GM.TraverseMulti("main", node.Key(), node.Kind(), ":::", true)
  657. if err != nil {
  658. t.Error(err)
  659. return
  660. }
  661. if len(tn) != 0 {
  662. t.Error("Unexpected traversal result: ", tn)
  663. return
  664. }
  665. // Delete node
  666. jsonString, _ = json.Marshal(map[string][]map[string]interface{}{
  667. "edges": {},
  668. "nodes": {
  669. {
  670. "key": node.Key(),
  671. "kind": node.Kind(),
  672. },
  673. },
  674. })
  675. st, _, res = sendTestRequest(queryURL+"main", "DELETE", []byte(jsonString))
  676. if st != "200 OK" {
  677. t.Error("Unexpected response:", st, res)
  678. return
  679. }
  680. // Check that the node was deleted
  681. nres, err := api.GM.FetchNode("main", node.Key(), node.Kind())
  682. if err != nil {
  683. t.Error(err)
  684. return
  685. }
  686. if nres != nil {
  687. t.Error("Unexpected node query result:", nres)
  688. return
  689. }
  690. }