123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- /*
- * 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 (
- "bytes"
- "encoding/json"
- "flag"
- "fmt"
- "io/ioutil"
- "net/http"
- "os"
- "path/filepath"
- "strconv"
- "strings"
- "sync"
- "testing"
- "devt.de/krotik/common/errorutil"
- "devt.de/krotik/common/fileutil"
- "devt.de/krotik/common/httputil"
- "devt.de/krotik/eliasdb/api"
- "devt.de/krotik/eliasdb/config"
- "devt.de/krotik/eliasdb/ecal"
- "devt.de/krotik/eliasdb/eql"
- "devt.de/krotik/eliasdb/graph"
- "devt.de/krotik/eliasdb/graph/data"
- "devt.de/krotik/eliasdb/graph/graphstorage"
- )
- const TESTPORT = ":9090"
- var gmMSM *graphstorage.MemoryGraphStorage
- const testScriptDir = "testscripts"
- // Main function for all tests in this package
- func TestMain(m *testing.M) {
- flag.Parse()
- defer func() {
- if res, _ := fileutil.PathExists(testScriptDir); res {
- if err := os.RemoveAll(testScriptDir); err != nil {
- fmt.Print("Could not remove test directory:", err.Error())
- }
- }
- }()
- if res, _ := fileutil.PathExists(testScriptDir); res {
- if err := os.RemoveAll(testScriptDir); err != nil {
- fmt.Print("Could not remove test directory:", err.Error())
- }
- }
- ensurePath(testScriptDir)
- data := make(map[string]interface{})
- for k, v := range config.DefaultConfig {
- data[k] = v
- }
- config.Config = data
- config.Config[config.EnableECALScripts] = true
- config.Config[config.ECALScriptFolder] = testScriptDir
- config.Config[config.ECALLogFile] = filepath.Join(testScriptDir, "interpreter.log")
- gm, msm := filterGraph()
- api.GM = gm
- api.GS = msm
- gmMSM = msm
- resetSI()
- hs, wg := startServer()
- if hs == nil {
- return
- }
- // Register endpoints for version 1
- api.RegisterRestEndpoints(V1EndpointMap)
- api.RegisterRestEndpoints(V1PublicEndpointMap)
- // Run the tests
- m.Run()
- // Teardown
- stopServer(hs, wg)
- }
- func TestSwaggerDefs(t *testing.T) {
- // Test we can build swagger defs from the endpoint
- data := map[string]interface{}{
- "paths": map[string]interface{}{},
- "definitions": map[string]interface{}{},
- }
- for _, inst := range V1EndpointMap {
- inst().SwaggerDefs(data)
- }
- }
- /*
- Send a request to a HTTP test server
- */
- func sendTestRequest(url string, method string, content []byte) (string, http.Header, string) {
- var req *http.Request
- var err error
- if content != nil {
- req, err = http.NewRequest(method, url, bytes.NewBuffer(content))
- } else {
- req, err = http.NewRequest(method, url, nil)
- }
- if err != nil {
- panic(err)
- }
- req.Header.Set("Content-Type", "application/json")
- client := &http.Client{}
- resp, err := client.Do(req)
- if err != nil {
- panic(err)
- }
- defer resp.Body.Close()
- body, _ := ioutil.ReadAll(resp.Body)
- bodyStr := strings.Trim(string(body), " \n")
- // Try json decoding first
- out := bytes.Buffer{}
- err = json.Indent(&out, []byte(bodyStr), "", " ")
- if err == nil {
- return resp.Status, resp.Header, out.String()
- }
- // Just return the body
- return resp.Status, resp.Header, bodyStr
- }
- /*
- formatJSONString formats a given JSON string.
- */
- func formatJSONString(str string) string {
- out := bytes.Buffer{}
- errorutil.AssertOk(json.Indent(&out, []byte(str), "", " "))
- return out.String()
- }
- /*
- formatData returns a given datastructure as JSON string.
- */
- func formatData(data interface{}) string {
- actualResultBytes, _ := json.MarshalIndent(data, "", " ")
- return string(actualResultBytes)
- }
- /*
- Start a HTTP test server.
- */
- func startServer() (*httputil.HTTPServer, *sync.WaitGroup) {
- hs := &httputil.HTTPServer{}
- var wg sync.WaitGroup
- wg.Add(1)
- go hs.RunHTTPServer(TESTPORT, &wg)
- wg.Wait()
- // Server is started
- if hs.LastError != nil {
- panic(hs.LastError)
- }
- return hs, &wg
- }
- /*
- Stop a started HTTP test server.
- */
- func stopServer(hs *httputil.HTTPServer, wg *sync.WaitGroup) {
- if hs.Running == true {
- wg.Add(1)
- // Server is shut down
- hs.Shutdown()
- wg.Wait()
- } else {
- panic("Server was not running as expected")
- }
- }
- 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")
- node0.SetAttr("desc", "One of the most popular acoustic artists of the decade and one of its best-selling artists.")
- gm.StoreNode("main", node0)
- gm.StoreNode("test", node0) // Same node but different partition
- gm.StoreNode("_test", node0) // Same node but different (hidden) partition
- 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)
- // Create lots of spam nodes
- for i := 0; i < 21; i++ {
- nodespam := data.NewGraphNode()
- nodespam.SetAttr("key", "000"+strconv.Itoa(i))
- nodespam.SetAttr("kind", "Spam")
- nodespam.SetAttr("name", "Spam"+strconv.Itoa(i))
- gm.StoreNode("main", nodespam)
- }
- return gm, mgs.(*graphstorage.MemoryGraphStorage)
- }
- func songGraphGroups() (*graph.Manager, *graphstorage.MemoryGraphStorage) {
- gm, mgs := songGraph()
- node0 := data.NewGraphNode()
- node0.SetAttr("key", "Best")
- node0.SetAttr("kind", eql.GroupNodeKind)
- 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"))
- // Store additional groups
- node0 = data.NewGraphNode()
- node0.SetAttr("key", "foo")
- node0.SetAttr("kind", eql.GroupNodeKind)
- gm.StoreNode("main", node0)
- node0 = data.NewGraphNode()
- node0.SetAttr("key", "g1")
- node0.SetAttr("kind", eql.GroupNodeKind)
- gm.StoreNode("main", node0)
- node0 = data.NewGraphNode()
- node0.SetAttr("key", "g2")
- node0.SetAttr("kind", eql.GroupNodeKind)
- gm.StoreNode("main", node0)
- return gm, mgs
- }
- func filterGraph() (*graph.Manager, *graphstorage.MemoryGraphStorage) {
- gm, mgs := songGraphGroups()
- constructNode := func(key, val1, val2, val3 string) data.Node {
- node0 := data.NewGraphNode()
- node0.SetAttr("key", key)
- node0.SetAttr("kind", "filtertest")
- node0.SetAttr("val1", val1)
- node0.SetAttr("val2", val2)
- node0.SetAttr("val3", val3)
- return node0
- }
- gm.StoreNode("main", constructNode("1", "test", "Hans", "foo"))
- gm.StoreNode("main", constructNode("2", "test1", "Hans", "foo"))
- gm.StoreNode("main", constructNode("3", "test2", "Hans", "foo"))
- gm.StoreNode("main", constructNode("4", "test3", "Peter", "foo"))
- gm.StoreNode("main", constructNode("5", "test4", "Peter", "foo"))
- gm.StoreNode("main", constructNode("6", "test5", "Peter", "foo"))
- gm.StoreNode("main", constructNode("7", "test6", "Anna", "foo"))
- gm.StoreNode("main", constructNode("8", "test7", "Anna", "foo"))
- gm.StoreNode("main", constructNode("9", "test8", "Steve", "foo"))
- gm.StoreNode("main", constructNode("10", "test9", "Steve", "foo"))
- gm.StoreNode("main", constructNode("11", "test10", "Franz", "foo"))
- gm.StoreNode("main", constructNode("12", "test11", "Kevin", "foo"))
- gm.StoreNode("main", constructNode("13", "test12", "Kevin", "foo"))
- gm.StoreNode("main", constructNode("14", "test13", "Kevin", "foo"))
- gm.StoreNode("main", constructNode("15", "test14", "X1", "foo"))
- gm.StoreNode("main", constructNode("16", "test15", "X2", "foo"))
- gm.StoreNode("main", constructNode("17", "test16", "X3", "foo"))
- gm.StoreNode("main", constructNode("18", "test17", "X4", "foo"))
- gm.StoreNode("main", constructNode("19", "test18", "X5", "foo"))
- return gm, mgs
- }
- func ensurePath(path string) {
- if res, _ := fileutil.PathExists(path); !res {
- if err := os.Mkdir(path, 0770); err != nil {
- fmt.Print("Could not create directory:", err.Error())
- return
- }
- }
- }
- func resetSI() {
- api.SI = ecal.NewScriptingInterpreter(testScriptDir, api.GM)
- }
- func writeScript(content string) {
- filename := filepath.Join(testScriptDir, config.Str(config.ECALEntryScript))
- err := ioutil.WriteFile(
- filename,
- []byte(content), 0600)
- errorutil.AssertOk(err)
- os.Remove(config.Str(config.ECALLogFile))
- }
- func checkLog(expected string) error {
- var err error
- content, err := ioutil.ReadFile(config.Str(config.ECALLogFile))
- if err == nil {
- logtext := string(content)
- if logtext != expected {
- err = fmt.Errorf("Unexpected log text:\n%v", logtext)
- }
- }
- return err
- }
|