explorer_test.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. // Copyright 2019 The go-ethereum Authors
  2. // This file is part of go-ethereum.
  3. //
  4. // go-ethereum is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // go-ethereum is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
  16. package main
  17. import (
  18. "encoding/json"
  19. "fmt"
  20. "net/http"
  21. "sort"
  22. "testing"
  23. "github.com/ethereum/go-ethereum/common"
  24. "github.com/ethereum/go-ethereum/swarm/storage/mock/explorer"
  25. mockRPC "github.com/ethereum/go-ethereum/swarm/storage/mock/rpc"
  26. )
  27. // TestExplorer validates basic chunk explorer functionality by storing
  28. // a small set of chunk and making http requests on exposed endpoint.
  29. // Full chunk explorer validation is done in mock/explorer package.
  30. func TestExplorer(t *testing.T) {
  31. addr := findFreeTCPAddress(t)
  32. explorerAddr := findFreeTCPAddress(t)
  33. testCmd := runGlobalStore(t, "ws", "--addr", addr, "--explorer-address", explorerAddr)
  34. defer testCmd.Kill()
  35. client := websocketClient(t, addr)
  36. store := mockRPC.NewGlobalStore(client)
  37. defer store.Close()
  38. nodeKeys := map[string][]string{
  39. "a1": {"b1", "b2", "b3"},
  40. "a2": {"b3", "b4", "b5"},
  41. }
  42. keyNodes := make(map[string][]string)
  43. for addr, keys := range nodeKeys {
  44. for _, key := range keys {
  45. keyNodes[key] = append(keyNodes[key], addr)
  46. }
  47. }
  48. invalidAddr := "c1"
  49. invalidKey := "d1"
  50. for addr, keys := range nodeKeys {
  51. for _, key := range keys {
  52. err := store.Put(common.HexToAddress(addr), common.Hex2Bytes(key), []byte("data"))
  53. if err != nil {
  54. t.Fatal(err)
  55. }
  56. }
  57. }
  58. endpoint := "http://" + explorerAddr
  59. t.Run("has key", func(t *testing.T) {
  60. for addr, keys := range nodeKeys {
  61. for _, key := range keys {
  62. testStatusResponse(t, endpoint+"/api/has-key/"+addr+"/"+key, http.StatusOK)
  63. testStatusResponse(t, endpoint+"/api/has-key/"+invalidAddr+"/"+key, http.StatusNotFound)
  64. }
  65. testStatusResponse(t, endpoint+"/api/has-key/"+addr+"/"+invalidKey, http.StatusNotFound)
  66. }
  67. testStatusResponse(t, endpoint+"/api/has-key/"+invalidAddr+"/"+invalidKey, http.StatusNotFound)
  68. })
  69. t.Run("keys", func(t *testing.T) {
  70. var keys []string
  71. for key := range keyNodes {
  72. keys = append(keys, key)
  73. }
  74. sort.Strings(keys)
  75. testKeysResponse(t, endpoint+"/api/keys", explorer.KeysResponse{
  76. Keys: keys,
  77. })
  78. })
  79. t.Run("nodes", func(t *testing.T) {
  80. var nodes []string
  81. for addr := range nodeKeys {
  82. nodes = append(nodes, common.HexToAddress(addr).Hex())
  83. }
  84. sort.Strings(nodes)
  85. testNodesResponse(t, endpoint+"/api/nodes", explorer.NodesResponse{
  86. Nodes: nodes,
  87. })
  88. })
  89. t.Run("node keys", func(t *testing.T) {
  90. for addr, keys := range nodeKeys {
  91. testKeysResponse(t, endpoint+"/api/keys?node="+addr, explorer.KeysResponse{
  92. Keys: keys,
  93. })
  94. }
  95. testKeysResponse(t, endpoint+"/api/keys?node="+invalidAddr, explorer.KeysResponse{})
  96. })
  97. t.Run("key nodes", func(t *testing.T) {
  98. for key, addrs := range keyNodes {
  99. var nodes []string
  100. for _, addr := range addrs {
  101. nodes = append(nodes, common.HexToAddress(addr).Hex())
  102. }
  103. sort.Strings(nodes)
  104. testNodesResponse(t, endpoint+"/api/nodes?key="+key, explorer.NodesResponse{
  105. Nodes: nodes,
  106. })
  107. }
  108. testNodesResponse(t, endpoint+"/api/nodes?key="+invalidKey, explorer.NodesResponse{})
  109. })
  110. }
  111. // TestExplorer_CORSOrigin validates if chunk explorer returns
  112. // correct CORS origin header in GET and OPTIONS requests.
  113. func TestExplorer_CORSOrigin(t *testing.T) {
  114. origin := "http://localhost/"
  115. addr := findFreeTCPAddress(t)
  116. explorerAddr := findFreeTCPAddress(t)
  117. testCmd := runGlobalStore(t, "ws",
  118. "--addr", addr,
  119. "--explorer-address", explorerAddr,
  120. "--explorer-cors-origin", origin,
  121. )
  122. defer testCmd.Kill()
  123. // wait until the server is started
  124. waitHTTPEndpoint(t, explorerAddr)
  125. url := "http://" + explorerAddr + "/api/keys"
  126. t.Run("get", func(t *testing.T) {
  127. req, err := http.NewRequest(http.MethodGet, url, nil)
  128. if err != nil {
  129. t.Fatal(err)
  130. }
  131. req.Header.Set("Origin", origin)
  132. resp, err := http.DefaultClient.Do(req)
  133. if err != nil {
  134. t.Fatal(err)
  135. }
  136. header := resp.Header.Get("Access-Control-Allow-Origin")
  137. if header != origin {
  138. t.Errorf("got Access-Control-Allow-Origin header %q, want %q", header, origin)
  139. }
  140. })
  141. t.Run("preflight", func(t *testing.T) {
  142. req, err := http.NewRequest(http.MethodOptions, url, nil)
  143. if err != nil {
  144. t.Fatal(err)
  145. }
  146. req.Header.Set("Origin", origin)
  147. req.Header.Set("Access-Control-Request-Method", "GET")
  148. resp, err := http.DefaultClient.Do(req)
  149. if err != nil {
  150. t.Fatal(err)
  151. }
  152. header := resp.Header.Get("Access-Control-Allow-Origin")
  153. if header != origin {
  154. t.Errorf("got Access-Control-Allow-Origin header %q, want %q", header, origin)
  155. }
  156. })
  157. }
  158. // testStatusResponse makes an http request to provided url
  159. // and validates if response is explorer.StatusResponse for
  160. // the expected status code.
  161. func testStatusResponse(t *testing.T, url string, code int) {
  162. t.Helper()
  163. resp, err := http.Get(url)
  164. if err != nil {
  165. t.Fatal(err)
  166. }
  167. if resp.StatusCode != code {
  168. t.Errorf("got status code %v, want %v", resp.StatusCode, code)
  169. }
  170. var r explorer.StatusResponse
  171. if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
  172. t.Fatal(err)
  173. }
  174. if r.Code != code {
  175. t.Errorf("got response code %v, want %v", r.Code, code)
  176. }
  177. if r.Message != http.StatusText(code) {
  178. t.Errorf("got response message %q, want %q", r.Message, http.StatusText(code))
  179. }
  180. }
  181. // testKeysResponse makes an http request to provided url
  182. // and validates if response machhes expected explorer.KeysResponse.
  183. func testKeysResponse(t *testing.T, url string, want explorer.KeysResponse) {
  184. t.Helper()
  185. resp, err := http.Get(url)
  186. if err != nil {
  187. t.Fatal(err)
  188. }
  189. if resp.StatusCode != http.StatusOK {
  190. t.Errorf("got status code %v, want %v", resp.StatusCode, http.StatusOK)
  191. }
  192. var r explorer.KeysResponse
  193. if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
  194. t.Fatal(err)
  195. }
  196. if fmt.Sprint(r.Keys) != fmt.Sprint(want.Keys) {
  197. t.Errorf("got keys %v, want %v", r.Keys, want.Keys)
  198. }
  199. if r.Next != want.Next {
  200. t.Errorf("got next %s, want %s", r.Next, want.Next)
  201. }
  202. }
  203. // testNodeResponse makes an http request to provided url
  204. // and validates if response machhes expected explorer.NodeResponse.
  205. func testNodesResponse(t *testing.T, url string, want explorer.NodesResponse) {
  206. t.Helper()
  207. resp, err := http.Get(url)
  208. if err != nil {
  209. t.Fatal(err)
  210. }
  211. if resp.StatusCode != http.StatusOK {
  212. t.Errorf("got status code %v, want %v", resp.StatusCode, http.StatusOK)
  213. }
  214. var r explorer.NodesResponse
  215. if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
  216. t.Fatal(err)
  217. }
  218. if fmt.Sprint(r.Nodes) != fmt.Sprint(want.Nodes) {
  219. t.Errorf("got nodes %v, want %v", r.Nodes, want.Nodes)
  220. }
  221. if r.Next != want.Next {
  222. t.Errorf("got next %s, want %s", r.Next, want.Next)
  223. }
  224. }