github.com/NebulousLabs/Sia@v1.3.7/modules/renter/renter_test.go (about)

     1  package renter
     2  
     3  import (
     4  	"path/filepath"
     5  	"reflect"
     6  	"testing"
     7  
     8  	"github.com/NebulousLabs/Sia/build"
     9  	"github.com/NebulousLabs/Sia/crypto"
    10  	"github.com/NebulousLabs/Sia/modules"
    11  	"github.com/NebulousLabs/Sia/modules/consensus"
    12  	"github.com/NebulousLabs/Sia/modules/gateway"
    13  	"github.com/NebulousLabs/Sia/modules/miner"
    14  	"github.com/NebulousLabs/Sia/modules/renter/contractor"
    15  	"github.com/NebulousLabs/Sia/modules/transactionpool"
    16  	"github.com/NebulousLabs/Sia/modules/wallet"
    17  	"github.com/NebulousLabs/Sia/types"
    18  )
    19  
    20  // renterTester contains all of the modules that are used while testing the renter.
    21  type renterTester struct {
    22  	cs        modules.ConsensusSet
    23  	gateway   modules.Gateway
    24  	miner     modules.TestMiner
    25  	tpool     modules.TransactionPool
    26  	wallet    modules.Wallet
    27  	walletKey crypto.TwofishKey
    28  
    29  	renter *Renter
    30  	dir    string
    31  }
    32  
    33  // Close shuts down the renter tester.
    34  func (rt *renterTester) Close() error {
    35  	rt.wallet.Lock()
    36  	rt.cs.Close()
    37  	rt.gateway.Close()
    38  	return nil
    39  }
    40  
    41  // newRenterTester creates a ready-to-use renter tester with money in the
    42  // wallet.
    43  func newRenterTester(name string) (*renterTester, error) {
    44  	// Create the modules.
    45  	testdir := build.TempDir("renter", name)
    46  	g, err := gateway.New("localhost:0", false, filepath.Join(testdir, modules.GatewayDir))
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	cs, err := consensus.New(g, false, filepath.Join(testdir, modules.ConsensusDir))
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir))
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir))
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	key := crypto.GenerateTwofishKey()
    63  	_, err = w.Encrypt(key)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  	err = w.Unlock(key)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	r, err := New(g, cs, w, tp, filepath.Join(testdir, modules.RenterDir))
    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  
    80  	// Assemble all pieces into a renter tester.
    81  	rt := &renterTester{
    82  		cs:      cs,
    83  		gateway: g,
    84  		miner:   m,
    85  		tpool:   tp,
    86  		wallet:  w,
    87  
    88  		renter: r,
    89  		dir:    testdir,
    90  	}
    91  
    92  	// Mine blocks until there is money in the wallet.
    93  	for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ {
    94  		_, err := rt.miner.AddBlock()
    95  		if err != nil {
    96  			return nil, err
    97  		}
    98  	}
    99  	return rt, nil
   100  }
   101  
   102  // stubHostDB is the minimal implementation of the hostDB interface. It can be
   103  // embedded in other mock hostDB types, removing the need to reimplement all
   104  // of the hostDB's methods on every mock.
   105  type stubHostDB struct{}
   106  
   107  func (stubHostDB) ActiveHosts() []modules.HostDBEntry   { return nil }
   108  func (stubHostDB) AllHosts() []modules.HostDBEntry      { return nil }
   109  func (stubHostDB) AverageContractPrice() types.Currency { return types.Currency{} }
   110  func (stubHostDB) Close() error                         { return nil }
   111  func (stubHostDB) IsOffline(modules.NetAddress) bool    { return true }
   112  func (stubHostDB) RandomHosts(int, []types.SiaPublicKey) ([]modules.HostDBEntry, error) {
   113  	return []modules.HostDBEntry{}, nil
   114  }
   115  func (stubHostDB) EstimateHostScore(modules.HostDBEntry) modules.HostScoreBreakdown {
   116  	return modules.HostScoreBreakdown{}
   117  }
   118  func (stubHostDB) Host(types.SiaPublicKey) (modules.HostDBEntry, bool) {
   119  	return modules.HostDBEntry{}, false
   120  }
   121  func (stubHostDB) ScoreBreakdown(modules.HostDBEntry) modules.HostScoreBreakdown {
   122  	return modules.HostScoreBreakdown{}
   123  }
   124  
   125  // stubContractor is the minimal implementation of the hostContractor
   126  // interface.
   127  type stubContractor struct{}
   128  
   129  func (stubContractor) SetAllowance(modules.Allowance) error { return nil }
   130  func (stubContractor) Allowance() modules.Allowance         { return modules.Allowance{} }
   131  func (stubContractor) Contract(modules.NetAddress) (modules.RenterContract, bool) {
   132  	return modules.RenterContract{}, false
   133  }
   134  func (stubContractor) Contracts() []modules.RenterContract                    { return nil }
   135  func (stubContractor) CurrentPeriod() types.BlockHeight                       { return 0 }
   136  func (stubContractor) IsOffline(modules.NetAddress) bool                      { return false }
   137  func (stubContractor) Editor(types.FileContractID) (contractor.Editor, error) { return nil, nil }
   138  func (stubContractor) Downloader(types.FileContractID) (contractor.Downloader, error) {
   139  	return nil, nil
   140  }
   141  
   142  type pricesStub struct {
   143  	stubHostDB
   144  
   145  	dbEntries []modules.HostDBEntry
   146  }
   147  
   148  func (pricesStub) InitialScanComplete() (bool, error) { return true, nil }
   149  
   150  func (ps pricesStub) RandomHosts(n int, exclude []types.SiaPublicKey) ([]modules.HostDBEntry, error) {
   151  	return ps.dbEntries, nil
   152  }
   153  
   154  // TestRenterPricesVolatility verifies that the renter caches its price
   155  // estimation, and subsequent calls result in non-volatile results.
   156  func TestRenterPricesVolatility(t *testing.T) {
   157  	if testing.Short() {
   158  		t.SkipNow()
   159  	}
   160  	rt, err := newRenterTester(t.Name())
   161  	if err != nil {
   162  		t.Fatal(err)
   163  	}
   164  	defer rt.Close()
   165  
   166  	// create a stubbed hostdb, query it with one contract, add another, verify
   167  	// the price estimation remains constant until the timeout has passed.
   168  	hdb := &pricesStub{}
   169  	id := rt.renter.mu.Lock()
   170  	rt.renter.hostDB = hdb
   171  	rt.renter.mu.Unlock(id)
   172  	dbe := modules.HostDBEntry{}
   173  	dbe.ContractPrice = types.SiacoinPrecision
   174  	dbe.DownloadBandwidthPrice = types.SiacoinPrecision
   175  	dbe.StoragePrice = types.SiacoinPrecision
   176  	dbe.UploadBandwidthPrice = types.SiacoinPrecision
   177  	hdb.dbEntries = append(hdb.dbEntries, dbe)
   178  	initial := rt.renter.PriceEstimation()
   179  	dbe.ContractPrice = dbe.ContractPrice.Mul64(2)
   180  	hdb.dbEntries = append(hdb.dbEntries, dbe)
   181  	after := rt.renter.PriceEstimation()
   182  	if !reflect.DeepEqual(initial, after) {
   183  		t.Log(initial)
   184  		t.Log(after)
   185  		t.Fatal("expected renter price estimation to be constant")
   186  	}
   187  	_, err = rt.miner.AddBlock()
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  	after = rt.renter.PriceEstimation()
   192  	if reflect.DeepEqual(initial, after) {
   193  		t.Fatal("expected renter price estimation to change after mining a block")
   194  	}
   195  }
   196  
   197  // TestRenterSiapathValidate verifies that the validateSiapath function correctly validates SiaPaths.
   198  func TestRenterSiapathValidate(t *testing.T) {
   199  	var pathtests = []struct {
   200  		in    string
   201  		valid bool
   202  	}{
   203  		{"valid/siapath", true},
   204  		{"../../../directory/traversal", false},
   205  		{"testpath", true},
   206  		{"valid/siapath/../with/directory/traversal", false},
   207  		{"validpath/test", true},
   208  		{"..validpath/..test", true},
   209  		{"./invalid/path", false},
   210  		{".../path", true},
   211  		{"valid./path", true},
   212  		{"valid../path", true},
   213  		{"valid/path./test", true},
   214  		{"valid/path../test", true},
   215  		{"test/path", true},
   216  		{"/leading/slash", false},
   217  		{"foo/./bar", false},
   218  		{"", false},
   219  	}
   220  	for _, pathtest := range pathtests {
   221  		err := validateSiapath(pathtest.in)
   222  		if err != nil && pathtest.valid {
   223  			t.Fatal("validateSiapath failed on valid path: ", pathtest.in)
   224  		}
   225  		if err == nil && !pathtest.valid {
   226  			t.Fatal("validateSiapath succeeded on invalid path: ", pathtest.in)
   227  		}
   228  	}
   229  }