/* * 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 v1 import ( "fmt" "testing" "devt.de/krotik/eliasdb/api" "devt.de/krotik/eliasdb/eql/interpreter" "devt.de/krotik/eliasdb/graph/data" ) func TestResultGroupingWithState(t *testing.T) { queryURL := "http://localhost" + TESTPORT + EndpointQuery st, header, res := sendTestRequest(queryURL+"/main?q=get+Song+with+ordering(ascending+key)", "GET", nil) if st != "200 OK" { t.Error("Unexpected result: ", st, res) return } id := header.Get(HTTPHeaderCacheID) sr, _ := ResultCache.Get(id) ssr := sr.(*APISearchResult) ssr.SetSelection(2, true) ssr.SetSelection(4, true) ssr.SetSelection(5, true) ssr.SetSelection(6, true) if res := fmt.Sprint(ssr.Selections()); res != "[false false true false true true true false false]" { t.Error("Unexpected result: ", res) return } queryURL2 := "http://localhost" + TESTPORT + EndpointQueryResult // Put nodes into a groups st, _, res = sendTestRequest(queryURL2+id+"/groupselected/g1", "PUT", nil) if st != "200 OK" || res == "" { t.Error("Unexpected result: ", st, res) return } ssr.NoneSelection() ssr.SetSelection(2, true) ssr.SetSelection(3, true) st, _, res = sendTestRequest(queryURL2+id+"/groupselected/g2", "PUT", nil) if st != "200 OK" || res == "" { t.Error("Unexpected result: ", st, res) return } ssr.SetSelection(4, true) ssr.SetSelection(5, true) ssr.SetSelection(6, true) if res := fmt.Sprint(ssr.Rows()); res != "[[Aria1 Aria1 8] "+ "[Aria2 Aria2 2] "+ "[Aria3 Aria3 4] "+ // Selected - group Best g1 g2 "[Aria4 Aria4 18] "+ // Selected - group g2 "[DeadSong2 DeadSong2 6] "+ // Selected - group g1 "[FightSong4 FightSong4 3] "+ // Selected - group g1 "[LoveSong3 LoveSong3 1] "+ // Selected - group Best g1 "[MyOnlySong3 MyOnlySong3 19] "+ "[StrangeSong1 StrangeSong1 5]]" { t.Error("Unexpected result: ", res) return } if res := fmt.Sprint(ssr.Selections()); res != "[false false true true true true true false false]" { t.Error("Unexpected result: ", res) return } // Get information about groups st, _, gsres := sendTestRequest(queryURL2+id+"/groupselected", "GET", nil) if st != "200 OK" || gsres != ` { "groups": [ "Best", "g1", "g2" ], "keys": [ [ "Aria3", "LoveSong3" ], [ "Aria3", "DeadSong2", "FightSong4", "LoveSong3" ], [ "Aria3", "Aria4" ] ], "kinds": [ [ "Song", "Song" ], [ "Song", "Song", "Song", "Song" ], [ "Song", "Song" ] ] }`[1:] { t.Error("Unexpected result: ", st, gsres) return } ssr.NoneSelection() ssr.SetSelection(2, true) ssr.SetSelection(3, true) st, _, res = sendTestRequest(queryURL2+id+"/groupselected/g2", "DELETE", nil) if st != "200 OK" || res == "" { t.Error("Unexpected result: ", st, res) return } ssr.NoneSelection() ssr.SetSelection(2, true) ssr.SetSelection(4, true) ssr.SetSelection(5, true) ssr.SetSelection(6, true) st, _, res = sendTestRequest(queryURL2+id+"/groupselected/g1", "DELETE", nil) if st != "200 OK" || res == "" { t.Error("Unexpected result: ", st, res) return } st, _, res = sendTestRequest(queryURL2+id+"/groupselected", "PUT", nil) if st != "400 Bad Request" || res != "Groupselected can only handle GET and POST requests" { t.Error("Unexpected result: ", st, res) return } ssr.Header().(*interpreter.SearchHeader).ResPartition = "foo bar" st, _, res = sendTestRequest(queryURL2+id+"/groupselected", "GET", nil) if st != "500 Internal Server Error" || res != "GraphError: Invalid data (Partition name foo bar is not alphanumeric - can only contain [a-zA-Z0-9_])" { t.Error("Unexpected result: ", st, res) return } ssr.Header().(*interpreter.SearchHeader).ResPartition = "main" st, _, res = sendTestRequest(queryURL2+id+"/groupselected", "GET", nil) if st != "200 OK" || res != ` { "groups": [ "Best" ], "keys": [ [ "Aria3", "LoveSong3" ] ], "kinds": [ [ "Song", "Song" ] ] }`[1:] { t.Error("Unexpected result: ", st, res) return } n, _, err := api.GM.TraverseMulti("main", "Aria3", "Song", ":::group", true) data.NodeSort(n) if err != nil || fmt.Sprint(n) != `[GraphNode: key : Best kind : group ]` { t.Error(n, err) return } st, _, res = sendTestRequest(queryURL2+id+"/groupselected", "POST", []byte(gsres)) if st != "200 OK" || res == "" { t.Error("Unexpected result: ", st, res) return } n, _, err = api.GM.TraverseMulti("main", "Aria3", "Song", ":::group", true) data.NodeSort(n) if err != nil || fmt.Sprint(n) != ` [GraphNode: key : Best kind : group GraphNode: key : g1 kind : group GraphNode: key : g2 kind : group ]`[1:] { t.Error(n, err) return } ssr.SetSelection(3, true) // Make sure also item 3 is selected st, _, gsres = sendTestRequest(queryURL2+id+"/groupselected", "GET", nil) if st != "200 OK" || gsres != ` { "groups": [ "Best", "g1", "g2" ], "keys": [ [ "Aria3", "LoveSong3" ], [ "Aria3", "DeadSong2", "FightSong4", "LoveSong3" ], [ "Aria3", "Aria4" ] ], "kinds": [ [ "Song", "Song" ], [ "Song", "Song", "Song", "Song" ], [ "Song", "Song" ] ] }`[1:] { t.Error("Unexpected result: ", st, gsres) return } st, _, res = sendTestRequest(queryURL2+id+"/groupselected", "POST", []byte("{:")) if st != "400 Bad Request" || res != "Could not decode request body as object with lists of groups, keys and kinds: invalid character ':' looking for beginning of object key string" { t.Error("Unexpected result: ", st, res) return } st, _, res = sendTestRequest(queryURL2+id+"/groupselected", "POST", []byte(`{"foo":"bar"}`)) if st != "400 Bad Request" || res != "Wrong data structures in request body - expecting an object with lists of groups, keys and kinds." { t.Error("Unexpected result: ", st, res) return } sstate := ` { "groups": [ "Best" ], "keys": [ [ "Aria3", "LoveSong3" ] ], "kinds": [ [ "Song", "Song" ] ] }`[1:] st, _, res = sendTestRequest(queryURL2+id+"/groupselected", "POST", []byte(sstate)) if st != "200 OK" || res != sstate { // Response should be the same state which we have put in! t.Error("Unexpected result: ", st, res) return } ssr.Header().(*interpreter.SearchHeader).ResPartition = "foo bar" st, _, res = sendTestRequest(queryURL2+id+"/groupselected", "POST", []byte(sstate)) if st != "500 Internal Server Error" || res != "GraphError: Invalid data (Partition name foo bar is not alphanumeric - can only contain [a-zA-Z0-9_])" { t.Error("Unexpected result: ", st, res) return } ssr.Header().(*interpreter.SearchHeader).ResPartition = "main" // Check the state has been restored st, _, gsres = sendTestRequest(queryURL2+id+"/groupselected", "GET", nil) if st != "200 OK" || gsres != ` { "groups": [ "Best" ], "keys": [ [ "Aria3", "LoveSong3" ] ], "kinds": [ [ "Song", "Song" ] ] }`[1:] { t.Error("Unexpected result: ", st, gsres) return } } func TestResultGroupingSpecifiedGroup(t *testing.T) { queryURL := "http://localhost" + TESTPORT + EndpointQuery st, header, res := sendTestRequest(queryURL+"/main?q=get+Song+with+ordering(ascending+key)", "GET", nil) if st != "200 OK" { t.Error("Unexpected result: ", st, res) return } id := header.Get(HTTPHeaderCacheID) sr, _ := ResultCache.Get(id) ssr := sr.(*APISearchResult) ssr.SetSelection(2, true) if res := fmt.Sprint(ssr.Selections()); res != "[false false true false false false false false false]" { t.Error("Unexpected result: ", res) return } if fmt.Sprint(ssr.RowSources()[2]) != "[n:Song:Aria3 n:Song:Aria3 n:Song:Aria3]" { t.Error("Unexpected row source:", ssr.RowSources()[2]) return } n, _, err := api.GM.TraverseMulti("main", "Aria3", "Song", ":::group", true) data.NodeSort(n) if err != nil || fmt.Sprint(n) != `[GraphNode: key : Best kind : group ]` { t.Error(n, err) return } queryURL2 := "http://localhost" + TESTPORT + EndpointQueryResult st, _, res = sendTestRequest(queryURL2+id+"/groupselected/foo", "GET", nil) if st != "400 Bad Request" || res != "Groupselected for a specific group can only handle PUT and DELETE requests" { t.Error("Unexpected result: ", st, res) return } st, _, res = sendTestRequest(queryURL2+id+"/groupselected/foo", "PUT", nil) if st != "200 OK" || res == "" { t.Error("Unexpected result: ", st, res) return } n, _, err = api.GM.TraverseMulti("main", "Aria3", "Song", ":::group", true) data.NodeSort(n) if err != nil || fmt.Sprint(n) != `[GraphNode: key : Best kind : group GraphNode: key : foo kind : group ]` { t.Error(n, err) return } st, _, res = sendTestRequest(queryURL2+id+"/groupselected/foo", "DELETE", nil) if st != "200 OK" || res == "" { t.Error("Unexpected result: ", st, res) return } n, _, err = api.GM.TraverseMulti("main", "Aria3", "Song", ":::group", true) data.NodeSort(n) if err != nil || fmt.Sprint(n) != `[GraphNode: key : Best kind : group ]` { t.Error(n, err) return } ssr.SearchResult.Header().(*interpreter.SearchHeader).ResPrimaryKind = "foo" st, _, res = sendTestRequest(queryURL2+id+"/groupselected/foo", "DELETE", nil) if st != "400 Bad Request" || res != "Could not determine key of primary node - query needs a primary expression" { t.Error("Unexpected result: ", st, res) return } st, _, res = sendTestRequest(queryURL2+id+"/select", "GET", nil) if st != "400 Bad Request" || res != "Could not determine key of primary node - query needs a primary expression" { t.Error("Unexpected result: ", st, res) return } } func TestResultSelection(t *testing.T) { queryURL := "http://localhost" + TESTPORT + EndpointQuery st, header, res := sendTestRequest(queryURL+"/main?q=get+Song+with+ordering(ascending+key)&offset=2&limit=3", "GET", nil) id := header.Get(HTTPHeaderCacheID) if st != "200 OK" || res != ` { "header": { "data": [ "1:n:key", "1:n:name", "1:n:ranking" ], "format": [ "auto", "auto", "auto" ], "labels": [ "Song Key", "Song Name", "Ranking" ], "primary_kind": "Song" }, "rows": [ [ "Aria3", "Aria3", 4 ], [ "Aria4", "Aria4", 18 ], [ "DeadSong2", "DeadSong2", 6 ] ], "selections": [ false, false, false ], "sources": [ [ "n:Song:Aria3", "n:Song:Aria3", "n:Song:Aria3" ], [ "n:Song:Aria4", "n:Song:Aria4", "n:Song:Aria4" ], [ "n:Song:DeadSong2", "n:Song:DeadSong2", "n:Song:DeadSong2" ] ], "total_selections": 0 }`[1:] { t.Error("Unexpected response:", st, res) return } queryURL2 := "http://localhost" + TESTPORT + EndpointQueryResult st, _, res = sendTestRequest(queryURL2+id+"/select/", "PUT", []byte("")) if st != "400 Bad Request" || res != "Need a selection ('all', 'none', 'invert' or row number)" { t.Error("Unexpected result: ", st, res) return } // Test CSV output st, _, res = sendTestRequest(queryURL2+id+"/csv", "PUT", nil) if st != "400 Bad Request" || res != "Csv can only handle GET requests" { t.Error("Unexpected result: ", st, res) return } st, _, res = sendTestRequest(queryURL2+id+"/csv", "GET", nil) if st != "200 OK" || res != ` Song Key,Song Name,Ranking Aria1,Aria1,8 Aria2,Aria2,2 Aria3,Aria3,4 Aria4,Aria4,18 DeadSong2,DeadSong2,6 FightSong4,FightSong4,3 LoveSong3,LoveSong3,1 MyOnlySong3,MyOnlySong3,19 StrangeSong1,StrangeSong1,5`[1:] { t.Error("Unexpected result: ", st, res) return } // Test selection st, _, res = sendTestRequest(queryURL2+id+"/select/all", "PUT", nil) if st != "200 OK" || res != `{ "total_selections": 9 }` { t.Error("Unexpected result: ", st, res) return } sr, _ := ResultCache.Get(id) ssr := sr.(*APISearchResult) if res := fmt.Sprint(ssr.Selections()); res != "[true true true true true true true true true]" { t.Error("Unexpected result: ", res) return } st, _, res = sendTestRequest(queryURL2+id+"/select/5", "PUT", nil) if st != "200 OK" || res != `{ "total_selections": 8 }` { t.Error("Unexpected result: ", st, res) return } st, _, res = sendTestRequest(queryURL2+id+"/select/0", "PUT", nil) if st != "200 OK" || res != `{ "total_selections": 7 }` { t.Error("Unexpected result: ", st, res) return } sr, _ = ResultCache.Get(id) ssr = sr.(*APISearchResult) if res := fmt.Sprint(ssr.Selections()); res != "[false true true true true false true true true]" { t.Error("Unexpected result: ", res) return } st, _, res = sendTestRequest(queryURL2+id+"/select/0", "PUT", nil) if st != "200 OK" || res != `{ "total_selections": 8 }` { t.Error("Unexpected result: ", st, res) return } sr, _ = ResultCache.Get(id) ssr = sr.(*APISearchResult) if res := fmt.Sprint(ssr.Selections()); res != "[true true true true true false true true true]" { t.Error("Unexpected result: ", res) return } st, _, res = sendTestRequest(queryURL2+id+"/select/invert", "PUT", nil) if st != "200 OK" || res != `{ "total_selections": 1 }` { t.Error("Unexpected result: ", st, res) return } sr, _ = ResultCache.Get(id) ssr = sr.(*APISearchResult) if res := fmt.Sprint(ssr.Selections()); res != "[false false false false false true false false false]" { t.Error("Unexpected result: ", res) return } // Test an error in between st, _, res = sendTestRequest(queryURL2+id+"/select/invert2", "PUT", nil) if st != "400 Bad Request" || res != "Invalid selection row number" { t.Error("Unexpected result: ", st, res) return } st, _, res = sendTestRequest(queryURL2+id+"/select/none", "PUT", nil) if st != "200 OK" || res != `{ "total_selections": 0 }` { t.Error("Unexpected result: ", st, res) return } sr, _ = ResultCache.Get(id) ssr = sr.(*APISearchResult) if res := fmt.Sprint(ssr.Selections()); res != "[false false false false false false false false false]" { t.Error("Unexpected result: ", res) return } // Blow away the selection aray and simulate a previous selection ssr.selections = []bool{true, false} st, _, res = sendTestRequest(queryURL2+id+"/select/2", "PUT", nil) if st != "200 OK" || res != `{ "total_selections": 2 }` { t.Error("Unexpected result: ", st, res) return } // Produce an error st, _, res = sendTestRequest(queryURL2+id+"/select/2", "DELETE", nil) if st != "400 Bad Request" || res != "Select can only handle GET and PUT requests" { t.Error("Unexpected result: ", st, res) return } sr, _ = ResultCache.Get(id) ssr = sr.(*APISearchResult) if res := fmt.Sprint(ssr.Selections()); res != "[true false true false false false false false false]" { t.Error("Unexpected result: ", res) return } st, _, res = sendTestRequest(queryURL2+id+"/select", "GET", nil) if st != "200 OK" || res != ` { "keys": [ "Aria1", "Aria3" ], "kinds": [ "Song", "Song" ] }`[1:] { t.Error("Unexpected result: ", st, res) return } st, _, res = sendTestRequest(queryURL+"/main?rid="+id+"&offset=2&limit=3", "GET", nil) if st != "200 OK" || res != ` { "header": { "data": [ "1:n:key", "1:n:name", "1:n:ranking" ], "format": [ "auto", "auto", "auto" ], "labels": [ "Song Key", "Song Name", "Ranking" ], "primary_kind": "Song" }, "rows": [ [ "Aria3", "Aria3", 4 ], [ "Aria4", "Aria4", 18 ], [ "DeadSong2", "DeadSong2", 6 ] ], "selections": [ true, false, false ], "sources": [ [ "n:Song:Aria3", "n:Song:Aria3", "n:Song:Aria3" ], [ "n:Song:Aria4", "n:Song:Aria4", "n:Song:Aria4" ], [ "n:Song:DeadSong2", "n:Song:DeadSong2", "n:Song:DeadSong2" ] ], "total_selections": 2 }`[1:] { t.Error("Unexpected response:", st, res) return } } func TestResultFiltering(t *testing.T) { queryURL := "http://localhost" + TESTPORT + EndpointQuery st, header, res := sendTestRequest(queryURL+"/main?q=get+filtertest+with+ordering(ascending+key)", "GET", nil) if st != "200 OK" || res != ` { "header": { "data": [ "1:n:key", "1:n:val1", "1:n:val2", "1:n:val3" ], "format": [ "auto", "auto", "auto", "auto" ], "labels": [ "Filtertest Key", "Val1", "Val2", "Val3" ], "primary_kind": "filtertest" }, "rows": [ [ "1", "test", "Hans", "foo" ], [ "2", "test1", "Hans", "foo" ], [ "3", "test2", "Hans", "foo" ], [ "4", "test3", "Peter", "foo" ], [ "5", "test4", "Peter", "foo" ], [ "6", "test5", "Peter", "foo" ], [ "7", "test6", "Anna", "foo" ], [ "8", "test7", "Anna", "foo" ], [ "9", "test8", "Steve", "foo" ], [ "10", "test9", "Steve", "foo" ], [ "11", "test10", "Franz", "foo" ], [ "12", "test11", "Kevin", "foo" ], [ "13", "test12", "Kevin", "foo" ], [ "14", "test13", "Kevin", "foo" ], [ "15", "test14", "X1", "foo" ], [ "16", "test15", "X2", "foo" ], [ "17", "test16", "X3", "foo" ], [ "18", "test17", "X4", "foo" ], [ "19", "test18", "X5", "foo" ] ], "selections": [ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false ], "sources": [ [ "n:filtertest:1", "n:filtertest:1", "n:filtertest:1", "n:filtertest:1" ], [ "n:filtertest:2", "n:filtertest:2", "n:filtertest:2", "n:filtertest:2" ], [ "n:filtertest:3", "n:filtertest:3", "n:filtertest:3", "n:filtertest:3" ], [ "n:filtertest:4", "n:filtertest:4", "n:filtertest:4", "n:filtertest:4" ], [ "n:filtertest:5", "n:filtertest:5", "n:filtertest:5", "n:filtertest:5" ], [ "n:filtertest:6", "n:filtertest:6", "n:filtertest:6", "n:filtertest:6" ], [ "n:filtertest:7", "n:filtertest:7", "n:filtertest:7", "n:filtertest:7" ], [ "n:filtertest:8", "n:filtertest:8", "n:filtertest:8", "n:filtertest:8" ], [ "n:filtertest:9", "n:filtertest:9", "n:filtertest:9", "n:filtertest:9" ], [ "n:filtertest:10", "n:filtertest:10", "n:filtertest:10", "n:filtertest:10" ], [ "n:filtertest:11", "n:filtertest:11", "n:filtertest:11", "n:filtertest:11" ], [ "n:filtertest:12", "n:filtertest:12", "n:filtertest:12", "n:filtertest:12" ], [ "n:filtertest:13", "n:filtertest:13", "n:filtertest:13", "n:filtertest:13" ], [ "n:filtertest:14", "n:filtertest:14", "n:filtertest:14", "n:filtertest:14" ], [ "n:filtertest:15", "n:filtertest:15", "n:filtertest:15", "n:filtertest:15" ], [ "n:filtertest:16", "n:filtertest:16", "n:filtertest:16", "n:filtertest:16" ], [ "n:filtertest:17", "n:filtertest:17", "n:filtertest:17", "n:filtertest:17" ], [ "n:filtertest:18", "n:filtertest:18", "n:filtertest:18", "n:filtertest:18" ], [ "n:filtertest:19", "n:filtertest:19", "n:filtertest:19", "n:filtertest:19" ] ], "total_selections": 0 }`[1:] { t.Error("Unexpected response:", st, res) return } queryURL = "http://localhost" + TESTPORT + EndpointQueryResult id := header.Get(HTTPHeaderCacheID) st, _, res = sendTestRequest(queryURL+id+"/quickfilter/2?limit=10", "PUT", nil) if st != "400 Bad Request" || res != "Quickfilter can only handle GET requests" { t.Error("Unexpected response:", st, res) return } _, _, res = sendTestRequest(queryURL+id+"/quickfilter/2?limit=10", "GET", nil) // The normal case - some values are the same if res != ` { "frequencies": [ 3, 3, 3, 2, 2, 1, 1, 1, 1, 1 ], "values": [ "Hans", "Kevin", "Peter", "Anna", "Steve", "Franz", "X1", "X2", "X3", "X4" ] }`[1:] { t.Error("Unexpected result: ", res) return } _, _, res = sendTestRequest(queryURL+"/"+id+"/quickfilter/2", "GET", nil) if res != ` { "frequencies": [ 3, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1 ], "values": [ "Hans", "Kevin", "Peter", "Anna", "Steve", "Franz", "X1", "X2", "X3", "X4", "X5" ] }`[1:] { t.Error("Unexpected result: ", res) return } // All values are unique _, _, res = sendTestRequest(queryURL+"/"+id+"/quickfilter/1?limit=10", "GET", nil) if res != ` { "frequencies": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], "values": [ "test", "test1", "test10", "test11", "test12", "test13", "test14", "test15", "test16", "test17" ] }`[1:] { t.Error("Unexpected result: ", res) return } // All values are the same _, _, res = sendTestRequest(queryURL+"/"+id+"/quickfilter/3", "GET", nil) if res != ` { "frequencies": [ 19 ], "values": [ "foo" ] }`[1:] { t.Error("Unexpected result: ", res) return } // Test error cases _, _, res = sendTestRequest(queryURL+"/"+id+"/quickfilter/4", "GET", nil) if res != "Invalid query result column" { t.Error("Unexpected result: ", res) return } _, _, res = sendTestRequest(queryURL+"/"+id+"/quickfilter/1?limit=-5", "GET", nil) if res != "Invalid parameter value: limit should be a positive integer number" { t.Error("Unexpected result: ", res) return } _, _, res = sendTestRequest(queryURL+"/"+id+"/quickfilter/", "GET", nil) if res != "Need a query result column to filter" { t.Error("Unexpected result: ", res) return } _, _, res = sendTestRequest(queryURL+"/"+id+"xx/quickfilter/2", "GET", nil) if res != "Unknown query result" { t.Error("Unexpected result: ", res) return } _, _, res = sendTestRequest(queryURL, "GET", nil) if res != "Need a result ID and an operation" { t.Error("Unexpected result: ", res) return } _, _, res = sendTestRequest(queryURL+"/"+id+"/foo/", "GET", nil) if res != "Unknown operation: foo" { t.Error("Unexpected result: ", res) return } }