| 
									
										
										
										
											2019-02-07 15:46:58 +01:00
										 |  |  | // Copyright 2019 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of go-ethereum. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // go-ethereum is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // go-ethereum is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  | // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package main | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"net" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/rpc" | 
					
						
							|  |  |  | 	mockRPC "github.com/ethereum/go-ethereum/swarm/storage/mock/rpc" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TestHTTP_InMemory tests in-memory global store that exposes | 
					
						
							|  |  |  | // HTTP server. | 
					
						
							|  |  |  | func TestHTTP_InMemory(t *testing.T) { | 
					
						
							|  |  |  | 	testHTTP(t, true) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TestHTTP_Database tests global store with persisted database | 
					
						
							|  |  |  | // that exposes HTTP server. | 
					
						
							|  |  |  | func TestHTTP_Database(t *testing.T) { | 
					
						
							|  |  |  | 	dir, err := ioutil.TempDir("", "swarm-global-store-") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer os.RemoveAll(dir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// create a fresh global store | 
					
						
							|  |  |  | 	testHTTP(t, true, "--dir", dir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// check if data saved by the previous global store instance | 
					
						
							|  |  |  | 	testHTTP(t, false, "--dir", dir) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // testWebsocket starts global store binary with HTTP server | 
					
						
							|  |  |  | // and validates that it can store and retrieve data. | 
					
						
							|  |  |  | // If put is false, no data will be stored, only retrieved, | 
					
						
							|  |  |  | // giving the possibility to check if data is present in the | 
					
						
							|  |  |  | // storage directory. | 
					
						
							|  |  |  | func testHTTP(t *testing.T, put bool, args ...string) { | 
					
						
							|  |  |  | 	addr := findFreeTCPAddress(t) | 
					
						
							|  |  |  | 	testCmd := runGlobalStore(t, append([]string{"http", "--addr", addr}, args...)...) | 
					
						
							| 
									
										
										
										
											2019-02-20 22:58:25 +01:00
										 |  |  | 	defer testCmd.Kill() | 
					
						
							| 
									
										
										
										
											2019-02-07 15:46:58 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	client, err := rpc.DialHTTP("http://" + addr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// wait until global store process is started as | 
					
						
							|  |  |  | 	// rpc.DialHTTP is actually not connecting | 
					
						
							| 
									
										
										
										
											2019-02-23 10:47:33 +01:00
										 |  |  | 	waitHTTPEndpoint(t, addr) | 
					
						
							| 
									
										
										
										
											2019-02-07 15:46:58 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	store := mockRPC.NewGlobalStore(client) | 
					
						
							|  |  |  | 	defer store.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	node := store.NewNodeStore(common.HexToAddress("123abc")) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wantKey := "key" | 
					
						
							|  |  |  | 	wantValue := "value" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if put { | 
					
						
							|  |  |  | 		err = node.Put([]byte(wantKey), []byte(wantValue)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatal(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	gotValue, err := node.Get([]byte(wantKey)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if string(gotValue) != wantValue { | 
					
						
							|  |  |  | 		t.Errorf("got value %s for key %s, want %s", string(gotValue), wantKey, wantValue) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TestWebsocket_InMemory tests in-memory global store that exposes | 
					
						
							|  |  |  | // WebSocket server. | 
					
						
							|  |  |  | func TestWebsocket_InMemory(t *testing.T) { | 
					
						
							|  |  |  | 	testWebsocket(t, true) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TestWebsocket_Database tests global store with persisted database | 
					
						
							|  |  |  | // that exposes HTTP server. | 
					
						
							|  |  |  | func TestWebsocket_Database(t *testing.T) { | 
					
						
							|  |  |  | 	dir, err := ioutil.TempDir("", "swarm-global-store-") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer os.RemoveAll(dir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// create a fresh global store | 
					
						
							|  |  |  | 	testWebsocket(t, true, "--dir", dir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// check if data saved by the previous global store instance | 
					
						
							|  |  |  | 	testWebsocket(t, false, "--dir", dir) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // testWebsocket starts global store binary with WebSocket server | 
					
						
							|  |  |  | // and validates that it can store and retrieve data. | 
					
						
							|  |  |  | // If put is false, no data will be stored, only retrieved, | 
					
						
							|  |  |  | // giving the possibility to check if data is present in the | 
					
						
							|  |  |  | // storage directory. | 
					
						
							|  |  |  | func testWebsocket(t *testing.T, put bool, args ...string) { | 
					
						
							|  |  |  | 	addr := findFreeTCPAddress(t) | 
					
						
							|  |  |  | 	testCmd := runGlobalStore(t, append([]string{"ws", "--addr", addr}, args...)...) | 
					
						
							| 
									
										
										
										
											2019-02-20 22:58:25 +01:00
										 |  |  | 	defer testCmd.Kill() | 
					
						
							| 
									
										
										
										
											2019-02-07 15:46:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-23 10:47:33 +01:00
										 |  |  | 	client := websocketClient(t, addr) | 
					
						
							| 
									
										
										
										
											2019-02-07 15:46:58 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	store := mockRPC.NewGlobalStore(client) | 
					
						
							|  |  |  | 	defer store.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	node := store.NewNodeStore(common.HexToAddress("123abc")) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wantKey := "key" | 
					
						
							|  |  |  | 	wantValue := "value" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if put { | 
					
						
							| 
									
										
										
										
											2019-02-23 10:47:33 +01:00
										 |  |  | 		err := node.Put([]byte(wantKey), []byte(wantValue)) | 
					
						
							| 
									
										
										
										
											2019-02-07 15:46:58 +01:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatal(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	gotValue, err := node.Get([]byte(wantKey)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if string(gotValue) != wantValue { | 
					
						
							|  |  |  | 		t.Errorf("got value %s for key %s, want %s", string(gotValue), wantKey, wantValue) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // findFreeTCPAddress returns a local address (IP:Port) to which | 
					
						
							|  |  |  | // global store can listen on. | 
					
						
							|  |  |  | func findFreeTCPAddress(t *testing.T) (addr string) { | 
					
						
							|  |  |  | 	t.Helper() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	listener, err := net.Listen("tcp", "") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer listener.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return listener.Addr().String() | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-02-23 10:47:33 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | // websocketClient waits until global store process is started | 
					
						
							|  |  |  | // and returns rpc client. | 
					
						
							|  |  |  | func websocketClient(t *testing.T, addr string) (client *rpc.Client) { | 
					
						
							|  |  |  | 	t.Helper() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	for i := 0; i < 1000; i++ { | 
					
						
							|  |  |  | 		client, err = rpc.DialWebsocket(context.Background(), "ws://"+addr, "") | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		time.Sleep(10 * time.Millisecond) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return client | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // waitHTTPEndpoint retries http requests to a provided | 
					
						
							|  |  |  | // address until the connection is established. | 
					
						
							|  |  |  | func waitHTTPEndpoint(t *testing.T, addr string) { | 
					
						
							|  |  |  | 	t.Helper() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	for i := 0; i < 1000; i++ { | 
					
						
							|  |  |  | 		_, err = http.Get("http://" + addr) | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		time.Sleep(10 * time.Millisecond) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |