server_test.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  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 server
  11. import (
  12. "errors"
  13. "flag"
  14. "fmt"
  15. "io/ioutil"
  16. "log"
  17. "net/http"
  18. "os"
  19. "runtime"
  20. "strings"
  21. "testing"
  22. "time"
  23. "devt.de/krotik/common/fileutil"
  24. "devt.de/krotik/common/httputil"
  25. "devt.de/krotik/eliasdb/api"
  26. "devt.de/krotik/eliasdb/api/ac"
  27. "devt.de/krotik/eliasdb/cluster"
  28. "devt.de/krotik/eliasdb/cluster/manager"
  29. "devt.de/krotik/eliasdb/config"
  30. "devt.de/krotik/eliasdb/graph"
  31. "devt.de/krotik/eliasdb/graph/graphstorage"
  32. )
  33. /*
  34. Flag to enable / disable long running tests.
  35. (Only used for test development - should never be false)
  36. */
  37. const RunLongRunningTests = true
  38. const testdb = "testdb"
  39. const invalidFileName = "**" + "\x00"
  40. var printLog = []string{}
  41. var errorLog = []string{}
  42. var printLogging = false
  43. func TestMain(m *testing.M) {
  44. flag.Parse()
  45. basepath = testdb + "/"
  46. // Log all print and error messages
  47. print = func(v ...interface{}) {
  48. if printLogging {
  49. fmt.Println(v...)
  50. }
  51. printLog = append(printLog, fmt.Sprint(v...))
  52. }
  53. fatal = func(v ...interface{}) {
  54. if printLogging {
  55. fmt.Println(v...)
  56. }
  57. errorLog = append(errorLog, fmt.Sprint(v...))
  58. }
  59. defer func() {
  60. fatal = log.Fatal
  61. basepath = ""
  62. }()
  63. if res, _ := fileutil.PathExists(testdb); res {
  64. if err := os.RemoveAll(testdb); err != nil {
  65. fmt.Print("Could not remove test directory:", err.Error())
  66. }
  67. }
  68. ensurePath(testdb)
  69. // Run the tests
  70. res := m.Run()
  71. if res, _ := fileutil.PathExists(testdb); res {
  72. if err := os.RemoveAll(testdb); err != nil {
  73. fmt.Print("Could not remove test directory:", err.Error())
  74. }
  75. }
  76. os.Exit(res)
  77. }
  78. func TestMainNormalCase(t *testing.T) {
  79. if !RunLongRunningTests {
  80. return
  81. }
  82. // Make sure to reset the DefaultServeMux
  83. defer func() { http.DefaultServeMux = http.NewServeMux() }()
  84. // Make sure to remove any files
  85. defer func() {
  86. if err := os.RemoveAll(testdb); err != nil {
  87. fmt.Print("Could not remove test directory:", err.Error())
  88. }
  89. time.Sleep(time.Duration(100) * time.Millisecond)
  90. ensurePath(testdb)
  91. }()
  92. // Reset logs
  93. printLog = []string{}
  94. errorLog = []string{}
  95. errorChan := make(chan error)
  96. // Load default configuration
  97. config.LoadDefaultConfig()
  98. // Start cluster by default
  99. config.Config[config.EnableCluster] = true
  100. config.Config[config.EnableClusterTerminal] = true
  101. // Enable access control
  102. config.Config[config.EnableAccessControl] = true
  103. // Kick off main function
  104. go func() {
  105. out, _ := runServer()
  106. config.Config[config.EnableCluster] = false
  107. config.Config[config.EnableClusterTerminal] = false
  108. config.Config[config.EnableAccessControl] = false
  109. ac.ACL.Close()
  110. os.Remove("access.db")
  111. lines := strings.Split(strings.TrimSpace(out), "\n")
  112. errorChan <- nil
  113. // stderr should contain one line from the rpc code
  114. if len(lines) != 1 || !strings.Contains(lines[0], "rpc.Serve: accept") {
  115. t.Error("Unexpected stderr:", out)
  116. return
  117. }
  118. }()
  119. // To exit the main function the lock watcher thread
  120. // has to recognise that the lockfile was modified
  121. shutdown := false
  122. go func() {
  123. filename := basepath + config.Str(config.LockFile)
  124. for !shutdown {
  125. // Do a normal shutdown with a log file - don't check for errors
  126. shutdownWithLogFile(filename)
  127. time.Sleep(time.Duration(200) * time.Millisecond)
  128. }
  129. }()
  130. // Wait for the main function to end
  131. if err := <-errorChan; err != nil || len(errorLog) != 0 {
  132. t.Error("Unexpected ending of main thread:", err, errorLog)
  133. return
  134. }
  135. shutdown = true
  136. // Check the print log
  137. logString := strings.Join(printLog, "\n")
  138. if runtime.GOOS == "windows" {
  139. // Very primitive but good enough
  140. logString = strings.Replace(logString, "\\", "/", -1)
  141. }
  142. if logString != `
  143. EliasDB `[1:]+config.ProductVersion+`
  144. Starting datastore in testdb/db
  145. Reading cluster config
  146. Opening cluster state info
  147. Starting cluster (log history: 100)
  148. [Cluster] member1: Starting member manager member1 rpc server on: 127.0.0.1:9030
  149. Creating GraphManager instance
  150. Creating key (key.pem) and certificate (cert.pem) in: ssl
  151. Ensuring web folder: testdb/web
  152. Ensuring login page: testdb/web/login.html
  153. Ensuring web terminal: testdb/web/db/term.html
  154. Ensuring cluster terminal: testdb/web/db/cluster.html
  155. Starting server on: 127.0.0.1:9090
  156. Writing fingerprint file: testdb/web/fingerprint.json
  157. Waiting for shutdown
  158. Lockfile was modified
  159. Shutting down
  160. [Cluster] member1: Housekeeping stopped
  161. [Cluster] member1: Shutdown rpc server on: 127.0.0.1:9030
  162. [Cluster] member1: Connection closed: 127.0.0.1:9030
  163. Closing datastore` {
  164. t.Error("Unexpected log:", logString)
  165. return
  166. }
  167. }
  168. func TestMainErrorCases(t *testing.T) {
  169. if !RunLongRunningTests {
  170. return
  171. }
  172. // Make sure to reset the DefaultServeMux
  173. defer func() { http.DefaultServeMux = http.NewServeMux() }()
  174. // Make sure to remove any files
  175. defer func() {
  176. if err := os.RemoveAll(testdb); err != nil {
  177. fmt.Print("Could not remove test directory:", err.Error())
  178. }
  179. time.Sleep(time.Duration(100) * time.Millisecond)
  180. ensurePath(testdb)
  181. }()
  182. // Setup config and logs
  183. data := make(map[string]interface{})
  184. for k, v := range config.DefaultConfig {
  185. data[k] = v
  186. }
  187. config.Config = data
  188. printLog = []string{}
  189. errorLog = []string{}
  190. // Test db access error
  191. config.Config[config.LocationDatastore] = invalidFileName
  192. config.Config[config.EnableReadOnly] = true
  193. runServer()
  194. // Check that an error happened
  195. if len(errorLog) != 2 ||
  196. !strings.Contains(errorLog[0], "Could not create directory") ||
  197. !strings.Contains(errorLog[1], "Failed to open graph storage") {
  198. t.Error("Unexpected error:", errorLog)
  199. return
  200. }
  201. // Set back logs
  202. printLog = []string{}
  203. errorLog = []string{}
  204. // Use memory only storage and the ignored readonly flag
  205. config.Config[config.MemoryOnlyStorage] = true
  206. config.Config[config.EnableReadOnly] = true
  207. // Test failed ssl key generation
  208. config.Config[config.HTTPSKey] = invalidFileName
  209. runServer()
  210. // Check that an error happened
  211. if len(errorLog) != 1 ||
  212. !strings.Contains(errorLog[0], "Failed to generate ssl key and certificate") {
  213. t.Error("Unexpected error:", errorLog)
  214. return
  215. }
  216. config.Config[config.HTTPSKey] = config.DefaultConfig[config.HTTPSKey]
  217. // Set back logs
  218. printLog = []string{}
  219. errorLog = []string{}
  220. // Special error when closing the store
  221. graphstorage.MgsRetClose = errors.New("Testerror")
  222. // Use 9090
  223. config.Config[config.HTTPSPort] = "9090"
  224. ths := httputil.HTTPServer{}
  225. go ths.RunHTTPServer(":9090", nil)
  226. time.Sleep(time.Duration(1) * time.Second)
  227. runServer()
  228. ths.Shutdown()
  229. time.Sleep(time.Duration(1) * time.Second)
  230. if ths.Running {
  231. t.Error("Server should not be running")
  232. return
  233. }
  234. if len(errorLog) != 2 || (errorLog[0] != "listen tcp :9090"+
  235. ": bind: address already in use" && errorLog[0] != "listen tcp :9090"+
  236. ": bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.") ||
  237. errorLog[1] != "Testerror" {
  238. t.Error("Unexpected error:", errorLog)
  239. return
  240. }
  241. // Set back logs
  242. printLog = []string{}
  243. errorLog = []string{}
  244. config.Config[config.EnableCluster] = true
  245. cluster.DSRetNew = errors.New("testerror")
  246. defer func() {
  247. cluster.DSRetNew = nil
  248. }()
  249. runServer()
  250. if len(errorLog) != 1 ||
  251. !strings.Contains(errorLog[0], "testerror") {
  252. t.Error("Unexpected error:", errorLog)
  253. return
  254. }
  255. // Set back logs
  256. printLog = []string{}
  257. errorLog = []string{}
  258. config.Config[config.EnableCluster] = true
  259. config.Config[config.ClusterStateInfoFile] = invalidFileName
  260. runServer()
  261. if len(errorLog) != 1 ||
  262. !strings.Contains(errorLog[0], "Failed to load cluster state info") {
  263. t.Error("Unexpected error:", errorLog)
  264. return
  265. }
  266. // Set back logs
  267. printLog = []string{}
  268. errorLog = []string{}
  269. config.Config[config.ClusterConfigFile] = invalidFileName
  270. runServer()
  271. if len(errorLog) != 1 ||
  272. !strings.Contains(errorLog[0], "Failed to load cluster config") {
  273. t.Error("Unexpected error:", errorLog)
  274. return
  275. }
  276. // Call to debug log function
  277. manager.LogDebug("test debug text")
  278. if logout := api.DDLog.String(); !strings.Contains(logout, "test debug text") {
  279. t.Error("Unexpected error:", logout)
  280. return
  281. }
  282. config.Config = nil
  283. SOPExecuted := false
  284. // Test single operation
  285. StartServerWithSingleOp(func(gm *graph.Manager) bool {
  286. SOPExecuted = true
  287. return true
  288. })
  289. if !SOPExecuted {
  290. t.Error("Single operation function was not executed")
  291. return
  292. }
  293. config.Config = nil
  294. }
  295. func shutdownWithLogFile(filename string) error {
  296. file, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0660)
  297. defer file.Close()
  298. if err != nil {
  299. fmt.Println(errorLog)
  300. return err
  301. }
  302. _, err = file.Write([]byte("a"))
  303. if err != nil {
  304. return err
  305. }
  306. return nil
  307. }
  308. /*
  309. Run the server and capture the output.
  310. */
  311. func runServer() (string, error) {
  312. defer func() {
  313. if r := recover(); r != nil {
  314. fmt.Println("Server execution caused a panic.")
  315. out, err := ioutil.ReadFile("out.txt")
  316. if err != nil {
  317. fmt.Println(err)
  318. }
  319. fmt.Println(out)
  320. }
  321. }()
  322. // Exchange stderr to a file
  323. origStdErr := os.Stderr
  324. outFile, err := os.Create("out.txt")
  325. if err != nil {
  326. return "", err
  327. }
  328. defer func() {
  329. outFile.Close()
  330. os.RemoveAll("out.txt")
  331. // Put Stderr back
  332. os.Stderr = origStdErr
  333. log.SetOutput(os.Stderr)
  334. }()
  335. os.Stderr = outFile
  336. log.SetOutput(outFile)
  337. StartServer()
  338. // Reset flags
  339. outFile.Sync()
  340. out, err := ioutil.ReadFile("out.txt")
  341. if err != nil {
  342. return "", err
  343. }
  344. return string(out), nil
  345. }