github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/api/server_helpers_test.go (about)

     1  package api
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/url"
     9  	"path/filepath"
    10  	"time"
    11  
    12  	"github.com/NebulousLabs/Sia/build"
    13  	"github.com/NebulousLabs/Sia/crypto"
    14  	"github.com/NebulousLabs/Sia/modules"
    15  	"github.com/NebulousLabs/Sia/modules/consensus"
    16  	"github.com/NebulousLabs/Sia/modules/explorer"
    17  	"github.com/NebulousLabs/Sia/modules/gateway"
    18  	"github.com/NebulousLabs/Sia/modules/host"
    19  	"github.com/NebulousLabs/Sia/modules/miner"
    20  	"github.com/NebulousLabs/Sia/modules/renter"
    21  	"github.com/NebulousLabs/Sia/modules/transactionpool"
    22  	"github.com/NebulousLabs/Sia/modules/wallet"
    23  	"github.com/NebulousLabs/Sia/persist"
    24  	"github.com/NebulousLabs/Sia/types"
    25  )
    26  
    27  // serverTester contains a server and a set of channels for keeping all of the
    28  // modules synchronized during testing.
    29  type serverTester struct {
    30  	cs        modules.ConsensusSet
    31  	gateway   modules.Gateway
    32  	host      modules.Host
    33  	miner     modules.TestMiner
    34  	renter    modules.Renter
    35  	tpool     modules.TransactionPool
    36  	explorer  modules.Explorer
    37  	wallet    modules.Wallet
    38  	walletKey crypto.TwofishKey
    39  
    40  	server *Server
    41  
    42  	dir string
    43  }
    44  
    45  // assembleServerTester creates a bunch of modules and assembles them into a
    46  // server tester, without creating any directories or mining any blocks.
    47  func assembleServerTester(key crypto.TwofishKey, testdir string) (*serverTester, error) {
    48  	// Create the modules.
    49  	g, err := gateway.New("localhost:0", filepath.Join(testdir, modules.GatewayDir))
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir))
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir))
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir))
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	if !w.Encrypted() {
    66  		_, err = w.Encrypt(key)
    67  		if err != nil {
    68  			return nil, err
    69  		}
    70  	}
    71  	err = w.Unlock(key)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir))
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	h, err := host.New(cs, tp, w, "localhost:0", filepath.Join(testdir, modules.HostDir))
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	r, err := renter.New(cs, w, tp, filepath.Join(testdir, modules.RenterDir))
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	e, err := explorer.New(cs, filepath.Join(testdir, modules.ExplorerDir))
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	srv, err := NewServer("localhost:0", "Sia-Agent", cs, e, g, h, m, r, tp, w)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	// Assemble the serverTester.
    97  	st := &serverTester{
    98  		cs:        cs,
    99  		gateway:   g,
   100  		host:      h,
   101  		miner:     m,
   102  		renter:    r,
   103  		tpool:     tp,
   104  		explorer:  e,
   105  		wallet:    w,
   106  		walletKey: key,
   107  
   108  		server: srv,
   109  
   110  		dir: testdir,
   111  	}
   112  
   113  	// TODO: A more reasonable way of listening for server errors.
   114  	go func() {
   115  		listenErr := srv.Serve()
   116  		if listenErr != nil {
   117  			panic(listenErr)
   118  		}
   119  	}()
   120  	return st, nil
   121  }
   122  
   123  // assembleExplorerServerTester creates all the explorer dependencies and
   124  // explorer module without creating any directories. The user agent requirement
   125  // is disabled.
   126  func assembleExplorerServerTester(testdir string) (*serverTester, error) {
   127  	// Create the modules.
   128  	g, err := gateway.New("localhost:0", filepath.Join(testdir, modules.GatewayDir))
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir))
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	e, err := explorer.New(cs, filepath.Join(testdir, modules.ExplorerDir))
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	srv, err := NewServer("localhost:0", "", cs, e, g, nil, nil, nil, nil, nil)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	// Assemble the serverTester.
   146  	st := &serverTester{
   147  		cs:       cs,
   148  		explorer: e,
   149  		gateway:  g,
   150  
   151  		server: srv,
   152  
   153  		dir: testdir,
   154  	}
   155  
   156  	// TODO: A more reasonable way of listening for server errors.
   157  	go func() {
   158  		listenErr := srv.Serve()
   159  		if listenErr != nil {
   160  			panic(listenErr)
   161  		}
   162  	}()
   163  	return st, nil
   164  }
   165  
   166  // createServerTester creates a server tester object that is ready for testing,
   167  // including money in the wallet and all modules initialized.
   168  func createServerTester(name string) (*serverTester, error) {
   169  	// Create the testing directory.
   170  	testdir := build.TempDir("api", name)
   171  
   172  	key, err := crypto.GenerateTwofishKey()
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  	st, err := assembleServerTester(key, testdir)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  
   181  	// Mine blocks until the wallet has confirmed money.
   182  	for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ {
   183  		_, err := st.miner.AddBlock()
   184  		if err != nil {
   185  			return nil, err
   186  		}
   187  	}
   188  
   189  	return st, nil
   190  }
   191  
   192  // createExplorerServerTester creates a server tester object containing only
   193  // the explorer and some presets that match standard explorer setups.
   194  func createExplorerServerTester(name string) (*serverTester, error) {
   195  	testdir := build.TempDir("api", name)
   196  	st, err := assembleExplorerServerTester(testdir)
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  	return st, nil
   201  }
   202  
   203  // reloadedServerTester creates a server tester where all of the persistent
   204  // data has been copied to a new folder and all of the modules re-initialized
   205  // on the new folder. This gives an opportunity to see how modules will behave
   206  // when they are relying on their persistent structures.
   207  func (st *serverTester) reloadedServerTester() (*serverTester, error) {
   208  	// Copy the testing directory.
   209  	copiedDir := st.dir + " - " + persist.RandomSuffix()
   210  	err := build.CopyDir(st.dir, copiedDir)
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  	copyST, err := assembleServerTester(st.walletKey, copiedDir)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  	return copyST, nil
   219  }
   220  
   221  // netAddress returns the NetAddress of the caller.
   222  func (st *serverTester) netAddress() modules.NetAddress {
   223  	return st.server.gateway.Address()
   224  }
   225  
   226  // coinAddress returns a coin address that the caller is able to spend from.
   227  func (st *serverTester) coinAddress() string {
   228  	var addr struct {
   229  		Address string
   230  	}
   231  	st.getAPI("/wallet/address", &addr)
   232  	return addr.Address
   233  }
   234  
   235  // acceptContracts instructs the host to begin accepting contracts.
   236  func (st *serverTester) acceptContracts() error {
   237  	settingsValues := url.Values{}
   238  	settingsValues.Set("acceptingcontracts", "true")
   239  	return st.stdPostAPI("/host", settingsValues)
   240  }
   241  
   242  // setHostStorage adds a 1 GB folder to the host.
   243  func (st *serverTester) setHostStorage() error {
   244  	values := url.Values{}
   245  	values.Set("path", st.dir)
   246  	values.Set("size", "1048576")
   247  	return st.stdPostAPI("/storage/folders/add", values)
   248  }
   249  
   250  // announceHost announces the host, mines a block, and waits for the
   251  // announcement to register.
   252  func (st *serverTester) announceHost() error {
   253  	announceValues := url.Values{}
   254  	announceValues.Set("address", string(st.host.ExternalSettings().NetAddress))
   255  	err := st.stdPostAPI("/host/announce", announceValues)
   256  	if err != nil {
   257  		return err
   258  	}
   259  	// mine block
   260  	_, err = st.miner.AddBlock()
   261  	if err != nil {
   262  		return err
   263  	}
   264  	// wait for announcement
   265  	var hosts ActiveHosts
   266  	err = st.getAPI("/renter/hosts/active", &hosts)
   267  	if err != nil {
   268  		return err
   269  	}
   270  	for i := 0; i < 20 && len(hosts.Hosts) == 0; i++ {
   271  		time.Sleep(100 * time.Millisecond)
   272  		err = st.getAPI("/renter/hosts/active", &hosts)
   273  		if err != nil {
   274  			return err
   275  		}
   276  	}
   277  	if len(hosts.Hosts) == 0 {
   278  		return errors.New("host announcement not seen")
   279  	}
   280  	return nil
   281  }
   282  
   283  // getAPI makes an API call and decodes the response.
   284  func (st *serverTester) getAPI(call string, obj interface{}) error {
   285  	resp, err := HttpGET("http://" + st.server.listener.Addr().String() + call)
   286  	if err != nil {
   287  		return err
   288  	}
   289  	defer resp.Body.Close()
   290  
   291  	// Check for a call error.
   292  	if resp.StatusCode != http.StatusOK {
   293  		respErr, err := ioutil.ReadAll(resp.Body)
   294  		if err != nil {
   295  			return err
   296  		}
   297  		return errors.New(string(respErr))
   298  	}
   299  
   300  	// Decode the response into 'obj'.
   301  	err = json.NewDecoder(resp.Body).Decode(obj)
   302  	if err != nil {
   303  		return err
   304  	}
   305  	return nil
   306  }
   307  
   308  // postAPI makes an API call and decodes the response.
   309  func (st *serverTester) postAPI(call string, values url.Values, obj interface{}) error {
   310  	resp, err := HttpPOST("http://"+st.server.listener.Addr().String()+call, values.Encode())
   311  	if err != nil {
   312  		return err
   313  	}
   314  	defer resp.Body.Close()
   315  
   316  	// Check for a call error.
   317  	if resp.StatusCode != http.StatusOK {
   318  		respErr, err := ioutil.ReadAll(resp.Body)
   319  		if err != nil {
   320  			return err
   321  		}
   322  		return errors.New(string(respErr))
   323  	}
   324  
   325  	// Decode the response into 'obj'.
   326  	err = json.NewDecoder(resp.Body).Decode(obj)
   327  	if err != nil {
   328  		return err
   329  	}
   330  	return nil
   331  }
   332  
   333  // stdGetAPI makes an API call and discards the response.
   334  func (st *serverTester) stdGetAPI(call string) error {
   335  	resp, err := HttpGET("http://" + st.server.listener.Addr().String() + call)
   336  	if err != nil {
   337  		return err
   338  	}
   339  	defer resp.Body.Close()
   340  
   341  	// Check for a call error.
   342  	if resp.StatusCode != http.StatusOK {
   343  		respErr, err := ioutil.ReadAll(resp.Body)
   344  		if err != nil {
   345  			return err
   346  		}
   347  		return errors.New(string(respErr))
   348  	}
   349  	return nil
   350  }
   351  
   352  // stdGetAPIUA makes an API call with a custom user agent.
   353  func (st *serverTester) stdGetAPIUA(call string, userAgent string) error {
   354  	req, err := http.NewRequest("GET", "http://"+st.server.listener.Addr().String()+call, nil)
   355  	if err != nil {
   356  		return err
   357  	}
   358  	req.Header.Add("User-Agent", userAgent)
   359  	resp, err := new(http.Client).Do(req)
   360  	if err != nil {
   361  		return err
   362  	}
   363  	defer resp.Body.Close()
   364  
   365  	// Check for a call error.
   366  	if resp.StatusCode != http.StatusOK {
   367  		respErr, err := ioutil.ReadAll(resp.Body)
   368  		if err != nil {
   369  			return err
   370  		}
   371  		return errors.New(string(respErr))
   372  	}
   373  	return nil
   374  }
   375  
   376  // stdPostAPI makes an API call and discards the response.
   377  func (st *serverTester) stdPostAPI(call string, values url.Values) error {
   378  	resp, err := HttpPOST("http://"+st.server.listener.Addr().String()+call, values.Encode())
   379  	if err != nil {
   380  		return err
   381  	}
   382  	defer resp.Body.Close()
   383  
   384  	// Check for a call error.
   385  	if resp.StatusCode != http.StatusOK {
   386  		respErr, err := ioutil.ReadAll(resp.Body)
   387  		if err != nil {
   388  			return err
   389  		}
   390  		return errors.New(string(respErr))
   391  	}
   392  	return nil
   393  }