gitlab.com/jokerrs1/Sia@v1.3.2/node/api/host_test.go (about)

     1  package api
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"net/url"
     8  	"os"
     9  	"path/filepath"
    10  	"reflect"
    11  	"strconv"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/NebulousLabs/Sia/build"
    16  	"github.com/NebulousLabs/Sia/crypto"
    17  	"github.com/NebulousLabs/Sia/modules"
    18  	"github.com/NebulousLabs/Sia/modules/host/contractmanager"
    19  	"github.com/NebulousLabs/Sia/types"
    20  )
    21  
    22  var (
    23  	// Various folder sizes for testing host storage folder resizing.
    24  	// Must be provided as strings to the API call.
    25  	minFolderSizeString    = strconv.FormatUint(modules.SectorSize*contractmanager.MinimumSectorsPerStorageFolder, 10)
    26  	maxFolderSizeString    = strconv.FormatUint(modules.SectorSize*contractmanager.MaximumSectorsPerStorageFolder, 10)
    27  	tooSmallFolderString   = strconv.FormatUint(modules.SectorSize*(contractmanager.MinimumSectorsPerStorageFolder-1), 10)
    28  	tooLargeFolderString   = strconv.FormatUint(modules.SectorSize*(contractmanager.MaximumSectorsPerStorageFolder+1), 10)
    29  	mediumSizeFolderString = strconv.FormatUint(modules.SectorSize*contractmanager.MinimumSectorsPerStorageFolder*3, 10)
    30  
    31  	// Test cases for resizing a host's storage folder.
    32  	// Running all the invalid cases before the valid ones simplifies some
    33  	// logic in the tests that use resizeTests.
    34  	resizeTests = []struct {
    35  		sizeString string
    36  		size       uint64
    37  		err        error
    38  	}{
    39  		// invalid sizes
    40  		{"", 0, io.EOF},
    41  		{"0", 0, contractmanager.ErrSmallStorageFolder},
    42  		{tooSmallFolderString, modules.SectorSize * (contractmanager.MinimumSectorsPerStorageFolder - 1), contractmanager.ErrSmallStorageFolder},
    43  		{tooLargeFolderString, modules.SectorSize * (contractmanager.MaximumSectorsPerStorageFolder + 1), contractmanager.ErrLargeStorageFolder},
    44  
    45  		// valid sizes
    46  		//
    47  		// TODO: Re-enable these when the host can support resizing into the
    48  		// same folder.
    49  		//
    50  		// {minFolderSizeString, contractmanager.MinimumSectorsPerStorageFolder * modules.SectorSize, nil},
    51  		// {maxFolderSizeString, contractmanager.MaximumSectorsPerStorageFolder * modules.SectorSize, nil},
    52  		// {mediumSizeFolderString, 3 * contractmanager.MinimumSectorsPerStorageFolder * modules.SectorSize, nil},
    53  	}
    54  )
    55  
    56  // TestEstimateWeight tests that /host/estimatescore works correctly.
    57  func TestEstimateWeight(t *testing.T) {
    58  	if testing.Short() {
    59  		t.SkipNow()
    60  	}
    61  	t.Parallel()
    62  
    63  	st, err := createServerTester(t.Name())
    64  	if err != nil {
    65  		t.Fatal(err)
    66  	}
    67  	defer st.server.panicClose()
    68  
    69  	// announce a host, create an allowance, upload some data.
    70  	if err := st.announceHost(); err != nil {
    71  		t.Fatal(err)
    72  	}
    73  	if err := st.acceptContracts(); err != nil {
    74  		t.Fatal(err)
    75  	}
    76  	if err := st.setHostStorage(); err != nil {
    77  		t.Fatal(err)
    78  	}
    79  
    80  	var eg HostEstimateScoreGET
    81  	if err := st.getAPI("/host/estimatescore", &eg); err != nil {
    82  		t.Fatal(err)
    83  	}
    84  	originalEstimate := eg.EstimatedScore
    85  
    86  	// verify that the estimate is being correctly updated by setting a massively
    87  	// increased min contract price and verifying that the score decreases.
    88  	is := st.host.InternalSettings()
    89  	is.MinContractPrice = is.MinContractPrice.Add(types.SiacoinPrecision.Mul64(9999999999))
    90  	if err := st.host.SetInternalSettings(is); err != nil {
    91  		t.Fatal(err)
    92  	}
    93  	if err := st.getAPI("/host/estimatescore", &eg); err != nil {
    94  		t.Fatal(err)
    95  	}
    96  	if eg.EstimatedScore.Cmp(originalEstimate) != -1 {
    97  		t.Fatal("score estimate did not decrease after incrementing mincontractprice")
    98  	}
    99  
   100  	// add a few hosts to the hostdb and verify that the conversion rate is
   101  	// reflected correctly
   102  	st2, err := blankServerTester(t.Name() + "-st2")
   103  	if err != nil {
   104  		t.Fatal(err)
   105  	}
   106  	defer st2.panicClose()
   107  	st3, err := blankServerTester(t.Name() + "-st3")
   108  	if err != nil {
   109  		t.Fatal(err)
   110  	}
   111  	defer st3.panicClose()
   112  	st4, err := blankServerTester(t.Name() + "-st4")
   113  	if err != nil {
   114  		t.Fatal(err)
   115  	}
   116  	defer st4.panicClose()
   117  	sts := []*serverTester{st, st2, st3, st4}
   118  	err = fullyConnectNodes(sts)
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  	err = fundAllNodes(sts)
   123  	if err != nil {
   124  		t.Fatal(err)
   125  	}
   126  	for i, tester := range sts {
   127  		is = tester.host.InternalSettings()
   128  		is.MinContractPrice = types.SiacoinPrecision.Mul64(1000 + (1000 * uint64(i)))
   129  		err = tester.host.SetInternalSettings(is)
   130  		if err != nil {
   131  			t.Fatal(err)
   132  		}
   133  	}
   134  	err = announceAllHosts(sts)
   135  	if err != nil {
   136  		t.Fatal(err)
   137  	}
   138  
   139  	tests := []struct {
   140  		price             types.Currency
   141  		minConversionRate float64
   142  	}{
   143  		{types.SiacoinPrecision, 100},
   144  		{types.SiacoinPrecision.Mul64(50), 98},
   145  		{types.SiacoinPrecision.Mul64(2500), 50},
   146  		{types.SiacoinPrecision.Mul64(3000), 10},
   147  		{types.SiacoinPrecision.Mul64(30000), 0.00001},
   148  	}
   149  	for i, test := range tests {
   150  		err = st.getAPI(fmt.Sprintf("/host/estimatescore?mincontractprice=%v", test.price.String()), &eg)
   151  		if err != nil {
   152  			t.Fatal("test", i, "failed:", err)
   153  		}
   154  		if eg.ConversionRate < test.minConversionRate {
   155  			t.Fatalf("test %v: incorrect conversion rate: got %v wanted %v\n", i, eg.ConversionRate, test.minConversionRate)
   156  		}
   157  	}
   158  }
   159  
   160  // TestHostSettingsHandlerParsing verifies that providing invalid host settings
   161  // doesn't reset the host's settings.
   162  func TestHostSettingsHandlerParsing(t *testing.T) {
   163  	if testing.Short() {
   164  		t.SkipNow()
   165  	}
   166  	t.Parallel()
   167  
   168  	st, err := createServerTester(t.Name())
   169  	if err != nil {
   170  		t.Fatal(err)
   171  	}
   172  	defer st.server.panicClose()
   173  
   174  	settings := st.host.InternalSettings()
   175  	settingsValues := url.Values{}
   176  	settingsValues.Set("maxdownloadbatchsize", "foo")
   177  	st.stdPostAPI("/host", settingsValues)
   178  	newSettings := st.host.InternalSettings()
   179  	if !reflect.DeepEqual(newSettings, settings) {
   180  		t.Fatal("invalid acceptingcontracts value changed host settings! got", newSettings, "wanted", settings)
   181  	}
   182  }
   183  
   184  // TestWorkingStatus tests that the host's WorkingStatus field is set
   185  // correctly.
   186  func TestWorkingStatus(t *testing.T) {
   187  	if testing.Short() {
   188  		t.SkipNow()
   189  	}
   190  	t.Parallel()
   191  
   192  	st, err := createServerTester(t.Name())
   193  	if err != nil {
   194  		t.Fatal(err)
   195  	}
   196  	defer st.server.panicClose()
   197  
   198  	// announce a host, create an allowance, upload some data.
   199  	if err := st.announceHost(); err != nil {
   200  		t.Fatal(err)
   201  	}
   202  	if err := st.acceptContracts(); err != nil {
   203  		t.Fatal(err)
   204  	}
   205  	if err := st.setHostStorage(); err != nil {
   206  		t.Fatal(err)
   207  	}
   208  
   209  	// Set an allowance for the renter, allowing a contract to be formed.
   210  	allowanceValues := url.Values{}
   211  	allowanceValues.Set("funds", testFunds)
   212  	allowanceValues.Set("period", testPeriod)
   213  	allowanceValues.Set("renewwindow", testRenewWindow)
   214  	allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts))
   215  	if err = st.stdPostAPI("/renter", allowanceValues); err != nil {
   216  		t.Fatal(err)
   217  	}
   218  
   219  	// Block until the allowance has finished forming contracts.
   220  	err = build.Retry(50, time.Millisecond*250, func() error {
   221  		var rc RenterContracts
   222  		err = st.getAPI("/renter/contracts", &rc)
   223  		if err != nil {
   224  			return errors.New("couldn't get renter stats")
   225  		}
   226  		if len(rc.Contracts) != 1 {
   227  			return errors.New("no contracts")
   228  		}
   229  		return nil
   230  	})
   231  	if err != nil {
   232  		t.Fatal("allowance setting failed")
   233  	}
   234  
   235  	// Create a file.
   236  	path := filepath.Join(st.dir, "test.dat")
   237  	fileBytes := 1024
   238  	if err := createRandFile(path, fileBytes); err != nil {
   239  		t.Fatal(err)
   240  	}
   241  
   242  	// Upload to host.
   243  	uploadValues := url.Values{}
   244  	uploadValues.Set("source", path)
   245  	if err := st.stdPostAPI("/renter/upload/test", uploadValues); err != nil {
   246  		t.Fatal(err)
   247  	}
   248  
   249  	// Only one piece will be uploaded (10% at current redundancy)
   250  	var rf RenterFiles
   251  	for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ {
   252  		st.getAPI("/renter/files", &rf)
   253  		time.Sleep(50 * time.Millisecond)
   254  	}
   255  	if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 {
   256  		t.Error(rf.Files[0].UploadProgress)
   257  		t.Fatal("uploading has failed")
   258  	}
   259  
   260  	err = retry(30, time.Second, func() error {
   261  		var hg HostGET
   262  		st.getAPI("/host", &hg)
   263  
   264  		if hg.WorkingStatus != modules.HostWorkingStatusWorking {
   265  			return errors.New("expected host to be working")
   266  		}
   267  		return nil
   268  	})
   269  	if err != nil {
   270  		t.Fatal(err)
   271  	}
   272  }
   273  
   274  // TestConnectabilityStatus tests that the host's ConnectabilityStatus field is
   275  // set correctly.
   276  func TestConnectabilityStatus(t *testing.T) {
   277  	if testing.Short() {
   278  		t.SkipNow()
   279  	}
   280  	t.Parallel()
   281  
   282  	// create and announce a host
   283  	st, err := createServerTester(t.Name())
   284  	if err != nil {
   285  		t.Fatal(err)
   286  	}
   287  	defer st.server.panicClose()
   288  
   289  	if err := st.announceHost(); err != nil {
   290  		t.Fatal(err)
   291  	}
   292  
   293  	err = retry(30, time.Second, func() error {
   294  		var hg HostGET
   295  		st.getAPI("/host", &hg)
   296  
   297  		if hg.ConnectabilityStatus != modules.HostConnectabilityStatusConnectable {
   298  			return errors.New("expected host to be connectable")
   299  		}
   300  		return nil
   301  	})
   302  	if err != nil {
   303  		t.Fatal(err)
   304  	}
   305  }
   306  
   307  // TestStorageHandler tests that host storage is being reported correctly.
   308  func TestStorageHandler(t *testing.T) {
   309  	if testing.Short() {
   310  		t.SkipNow()
   311  	}
   312  	t.Parallel()
   313  	st, err := createServerTester(t.Name())
   314  	if err != nil {
   315  		t.Fatal(err)
   316  	}
   317  	defer st.server.panicClose()
   318  
   319  	// Announce the host and start accepting contracts.
   320  	if err := st.announceHost(); err != nil {
   321  		t.Fatal(err)
   322  	}
   323  	if err := st.acceptContracts(); err != nil {
   324  		t.Fatal(err)
   325  	}
   326  	if err := st.setHostStorage(); err != nil {
   327  		t.Fatal(err)
   328  	}
   329  
   330  	// Set an allowance for the renter, allowing a contract to be formed.
   331  	allowanceValues := url.Values{}
   332  	allowanceValues.Set("funds", testFunds)
   333  	allowanceValues.Set("period", testPeriod)
   334  	allowanceValues.Set("renewwindow", testRenewWindow)
   335  	allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts))
   336  	if err = st.stdPostAPI("/renter", allowanceValues); err != nil {
   337  		t.Fatal(err)
   338  	}
   339  
   340  	// Block until the allowance has finished forming contracts.
   341  	err = build.Retry(50, time.Millisecond*250, func() error {
   342  		var rc RenterContracts
   343  		err = st.getAPI("/renter/contracts", &rc)
   344  		if err != nil {
   345  			return errors.New("couldn't get renter stats")
   346  		}
   347  		if len(rc.Contracts) != 1 {
   348  			return errors.New("no contracts")
   349  		}
   350  		return nil
   351  	})
   352  	if err != nil {
   353  		t.Fatal("allowance setting failed")
   354  	}
   355  
   356  	// Create a file.
   357  	path := filepath.Join(st.dir, "test.dat")
   358  	fileBytes := 1024
   359  	if err := createRandFile(path, fileBytes); err != nil {
   360  		t.Fatal(err)
   361  	}
   362  
   363  	// Upload to host.
   364  	uploadValues := url.Values{}
   365  	uploadValues.Set("source", path)
   366  	if err := st.stdPostAPI("/renter/upload/test", uploadValues); err != nil {
   367  		t.Fatal(err)
   368  	}
   369  
   370  	// Only one piece will be uploaded (10% at current redundancy)
   371  	var rf RenterFiles
   372  	for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ {
   373  		st.getAPI("/renter/files", &rf)
   374  		time.Sleep(50 * time.Millisecond)
   375  	}
   376  	if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 {
   377  		t.Error(rf.Files[0].UploadProgress)
   378  		t.Fatal("uploading has failed")
   379  	}
   380  
   381  	var sg StorageGET
   382  	if err := st.getAPI("/host/storage", &sg); err != nil {
   383  		t.Fatal(err)
   384  	}
   385  
   386  	// Uploading succeeded, so /host/storage should be reporting a successful
   387  	// write.
   388  	if sg.Folders[0].SuccessfulWrites != 1 {
   389  		t.Fatalf("expected 1 successful write, got %v", sg.Folders[0].SuccessfulWrites)
   390  	}
   391  	if used := sg.Folders[0].Capacity - sg.Folders[0].CapacityRemaining; used != modules.SectorSize {
   392  		t.Fatalf("expected used capacity to be the size of one sector (%v bytes), got %v bytes", modules.SectorSize, used)
   393  	}
   394  }
   395  
   396  // TestAddFolderNoPath tests that an API call to add a storage folder fails if
   397  // no path was provided.
   398  func TestAddFolderNoPath(t *testing.T) {
   399  	if testing.Short() {
   400  		t.SkipNow()
   401  	}
   402  	t.Parallel()
   403  	st, err := createServerTester(t.Name())
   404  	if err != nil {
   405  		t.Fatal(err)
   406  	}
   407  	defer st.server.panicClose()
   408  
   409  	// Try adding a storage folder without setting "path" in the API call.
   410  	addValues := url.Values{}
   411  	addValues.Set("size", mediumSizeFolderString)
   412  	err = st.stdPostAPI("/host/storage/folders/add", addValues)
   413  	if err == nil {
   414  		t.Fatal(err)
   415  	}
   416  
   417  	// Setting the path to an empty string should trigger the same error.
   418  	addValues.Set("path", "")
   419  	err = st.stdPostAPI("/host/storage/folders/add", addValues)
   420  	if err == nil {
   421  		t.Fatal(err)
   422  	}
   423  }
   424  
   425  // TestAddFolderNoSize tests that an API call to add a storage folder fails if
   426  // no path was provided.
   427  func TestAddFolderNoSize(t *testing.T) {
   428  	if testing.Short() {
   429  		t.SkipNow()
   430  	}
   431  	t.Parallel()
   432  	st, err := createServerTester(t.Name())
   433  	if err != nil {
   434  		t.Fatal(err)
   435  	}
   436  	defer st.server.panicClose()
   437  
   438  	// Try adding a storage folder without setting "size" in the API call.
   439  	addValues := url.Values{}
   440  	addValues.Set("path", st.dir)
   441  	err = st.stdPostAPI("/host/storage/folders/add", addValues)
   442  	if err == nil || err.Error() != io.EOF.Error() {
   443  		t.Fatalf("expected error to be %v, got %v", io.EOF, err)
   444  	}
   445  }
   446  
   447  // TestAddSameFolderTwice tests that an API call that attempts to add a
   448  // host storage folder that's already been added is handled gracefully.
   449  func TestAddSameFolderTwice(t *testing.T) {
   450  	if testing.Short() {
   451  		t.SkipNow()
   452  	}
   453  	t.Parallel()
   454  	st, err := createServerTester(t.Name())
   455  	if err != nil {
   456  		t.Fatal(err)
   457  	}
   458  	defer st.server.panicClose()
   459  
   460  	// Make the call to add a storage folder twice.
   461  	addValues := url.Values{}
   462  	addValues.Set("path", st.dir)
   463  	addValues.Set("size", mediumSizeFolderString)
   464  	err = st.stdPostAPI("/host/storage/folders/add", addValues)
   465  	if err != nil {
   466  		t.Fatal(err)
   467  	}
   468  	err = st.stdPostAPI("/host/storage/folders/add", addValues)
   469  	if err == nil || err.Error() != contractmanager.ErrRepeatFolder.Error() {
   470  		t.Fatalf("expected err to be %v, got %v", err, contractmanager.ErrRepeatFolder)
   471  	}
   472  }
   473  
   474  // TestResizeEmptyStorageFolder tests that invalid and valid calls to resize
   475  // an empty storage folder are properly handled.
   476  func TestResizeEmptyStorageFolder(t *testing.T) {
   477  	if testing.Short() {
   478  		t.SkipNow()
   479  	}
   480  	t.Parallel()
   481  	st, err := createServerTester(t.Name())
   482  	if err != nil {
   483  		t.Fatal(err)
   484  	}
   485  	defer st.server.panicClose()
   486  
   487  	// Announce the host and start accepting contracts.
   488  	if err := st.announceHost(); err != nil {
   489  		t.Fatal(err)
   490  	}
   491  	if err := st.acceptContracts(); err != nil {
   492  		t.Fatal(err)
   493  	}
   494  	if err := st.setHostStorage(); err != nil {
   495  		t.Fatal(err)
   496  	}
   497  
   498  	// Find out how large the host's initial storage folder is.
   499  	var sg StorageGET
   500  	if err := st.getAPI("/host/storage", &sg); err != nil {
   501  		t.Fatal(err)
   502  	}
   503  	defaultSize := sg.Folders[0].Capacity
   504  	// Convert defaultSize (uint64) to a string for the API call.
   505  	defaultSizeString := strconv.FormatUint(defaultSize, 10)
   506  
   507  	resizeValues := url.Values{}
   508  	resizeValues.Set("path", st.dir)
   509  	resizeValues.Set("newsize", defaultSizeString)
   510  
   511  	// Attempting to resize to the same size should return an error.
   512  	err = st.stdPostAPI("/host/storage/folders/resize", resizeValues)
   513  	if err == nil || err.Error() != contractmanager.ErrNoResize.Error() {
   514  		t.Fatalf("expected error %v, got %v", contractmanager.ErrNoResize, err)
   515  	}
   516  
   517  	// Try resizing to a bunch of sizes (invalid ones first, valid ones second).
   518  	// This ordering simplifies logic within the for loop.
   519  	for i, test := range resizeTests {
   520  		// Attempt to resize the host's storage folder.
   521  		resizeValues.Set("newsize", test.sizeString)
   522  		err = st.stdPostAPI("/host/storage/folders/resize", resizeValues)
   523  		if (err == nil && test.err != nil) || (err != nil && err.Error() != test.err.Error()) {
   524  			t.Fatalf("test %v: expected error to be %v, got %v", i, test.err, err)
   525  		}
   526  
   527  		// Find out if the resize call worked as expected.
   528  		if err := st.getAPI("/host/storage", &sg); err != nil {
   529  			t.Fatal(err)
   530  		}
   531  		// If the test size is valid, check that the folder has been resized
   532  		// properly.
   533  		if test.err == nil {
   534  			// Check that the folder's total capacity has been updated.
   535  			if got := sg.Folders[0].Capacity; got != test.size {
   536  				t.Fatalf("test %v: expected folder to be resized to %v; got %v instead", i, test.size, got)
   537  			}
   538  			// Check that the folder's remaining capacity has been updated.
   539  			if got := sg.Folders[0].CapacityRemaining; got != test.size {
   540  				t.Fatalf("folder should be empty, but capacity remaining (%v) != total capacity (%v)", got, test.size)
   541  			}
   542  		} else {
   543  			// If the test size is invalid, the folder should not have been
   544  			// resized. The invalid test cases are all run before the valid ones,
   545  			// so the folder size should still be defaultSize.
   546  			if got := sg.Folders[0].Capacity; got != defaultSize {
   547  				t.Fatalf("folder was resized to an invalid size (%v) in a test case that should have failed: %v", got, test)
   548  			}
   549  		}
   550  	}
   551  }
   552  
   553  // TestResizeNonemptyStorageFolder tests that invalid and valid calls to resize
   554  // a storage folder with one sector filled are properly handled.
   555  // Ideally, we would also test a very full storage folder (including the case
   556  // where the host tries to resize to a size smaller than the amount of data
   557  // in the folder), but that would be a very expensive test.
   558  func TestResizeNonemptyStorageFolder(t *testing.T) {
   559  	if testing.Short() {
   560  		t.SkipNow()
   561  	}
   562  	t.Parallel()
   563  	st, err := createServerTester(t.Name())
   564  	if err != nil {
   565  		t.Fatal(err)
   566  	}
   567  	defer st.server.panicClose()
   568  
   569  	// Announce the host and start accepting contracts.
   570  	if err := st.announceHost(); err != nil {
   571  		t.Fatal(err)
   572  	}
   573  	if err := st.acceptContracts(); err != nil {
   574  		t.Fatal(err)
   575  	}
   576  	if err := st.setHostStorage(); err != nil {
   577  		t.Fatal(err)
   578  	}
   579  
   580  	// Set an allowance for the renter, allowing a contract to be formed.
   581  	allowanceValues := url.Values{}
   582  	allowanceValues.Set("funds", testFunds)
   583  	allowanceValues.Set("period", testPeriod)
   584  	allowanceValues.Set("renewwindow", testRenewWindow)
   585  	allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts))
   586  	if err = st.stdPostAPI("/renter", allowanceValues); err != nil {
   587  		t.Fatal(err)
   588  	}
   589  
   590  	// Block until the allowance has finished forming contracts.
   591  	err = build.Retry(50, time.Millisecond*250, func() error {
   592  		var rc RenterContracts
   593  		err = st.getAPI("/renter/contracts", &rc)
   594  		if err != nil {
   595  			return errors.New("couldn't get renter stats")
   596  		}
   597  		if len(rc.Contracts) != 1 {
   598  			return errors.New("no contracts")
   599  		}
   600  		return nil
   601  	})
   602  	if err != nil {
   603  		t.Fatal("allowance setting failed")
   604  	}
   605  
   606  	// Create a file.
   607  	path := filepath.Join(st.dir, "test.dat")
   608  	fileBytes := 1024
   609  	if err := createRandFile(path, fileBytes); err != nil {
   610  		t.Fatal(err)
   611  	}
   612  
   613  	// Upload to host.
   614  	uploadValues := url.Values{}
   615  	uploadValues.Set("source", path)
   616  	if err := st.stdPostAPI("/renter/upload/test", uploadValues); err != nil {
   617  		t.Fatal(err)
   618  	}
   619  
   620  	// Only one piece will be uploaded (10% at current redundancy)
   621  	var rf RenterFiles
   622  	for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ {
   623  		st.getAPI("/renter/files", &rf)
   624  		time.Sleep(50 * time.Millisecond)
   625  	}
   626  	if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 {
   627  		t.Error(rf.Files[0].UploadProgress)
   628  		t.Fatal("uploading has failed")
   629  	}
   630  
   631  	// Find out how large the host's initial storage folder is.
   632  	var sg StorageGET
   633  	if err := st.getAPI("/host/storage", &sg); err != nil {
   634  		t.Fatal(err)
   635  	}
   636  	defaultSize := sg.Folders[0].Capacity
   637  	// Convert defaultSize (uint64) to a string for the API call.
   638  	defaultSizeString := strconv.FormatUint(defaultSize, 10)
   639  
   640  	resizeValues := url.Values{}
   641  	resizeValues.Set("path", st.dir)
   642  	resizeValues.Set("newsize", defaultSizeString)
   643  
   644  	// Attempting to resize to the same size should return an error.
   645  	err = st.stdPostAPI("/host/storage/folders/resize", resizeValues)
   646  	if err == nil || err.Error() != contractmanager.ErrNoResize.Error() {
   647  		t.Fatalf("expected error %v, got %v", contractmanager.ErrNoResize, err)
   648  	}
   649  
   650  	// Try resizing to a bunch of sizes (invalid ones first, valid ones second).
   651  	// This ordering simplifies logic within the for loop.
   652  	for _, test := range resizeTests {
   653  		// Attempt to resize the host's storage folder.
   654  		resizeValues.Set("newsize", test.sizeString)
   655  		err = st.stdPostAPI("/host/storage/folders/resize", resizeValues)
   656  		if (err == nil && test.err != nil) || (err != nil && test.err == nil) || (err != nil && err.Error() != test.err.Error()) {
   657  			t.Fatalf("expected error to be %v, got %v", test.err, err)
   658  		}
   659  
   660  		// Find out if the resize call worked as expected.
   661  		if err := st.getAPI("/host/storage", &sg); err != nil {
   662  			t.Fatal(err)
   663  		}
   664  		// If the test size is valid, check that the folder has been resized
   665  		// properly.
   666  		if test.err == nil {
   667  			// Check that the folder's total capacity has been updated.
   668  			if sg.Folders[0].Capacity != test.size {
   669  				t.Fatalf("expected folder to be resized to %v; got %v instead", test.size, sg.Folders[0].Capacity)
   670  			}
   671  			// Since one sector has been uploaded, the available capacity
   672  			// should be one sector size smaller than the total capacity.
   673  			if used := test.size - sg.Folders[0].CapacityRemaining; used != modules.SectorSize {
   674  				t.Fatalf("used capacity (%v) != the size of 1 sector (%v)", used, modules.SectorSize)
   675  			}
   676  		} else {
   677  			// If the test size is invalid, the folder should not have been
   678  			// resized. The invalid test cases are all run before the valid
   679  			// ones, so the folder size should still be defaultSize.
   680  			if got := sg.Folders[0].Capacity; got != defaultSize {
   681  				t.Fatalf("folder was resized to an invalid size (%v) in a test case that should have failed: %v", got, test)
   682  			}
   683  		}
   684  	}
   685  }
   686  
   687  // TestResizeNonexistentFolder checks that an API call to resize a nonexistent
   688  // folder triggers the appropriate error.
   689  func TestResizeNonexistentFolder(t *testing.T) {
   690  	if testing.Short() {
   691  		t.SkipNow()
   692  	}
   693  	t.Parallel()
   694  	st, err := createServerTester(t.Name())
   695  	if err != nil {
   696  		t.Fatal(err)
   697  	}
   698  	defer st.server.panicClose()
   699  
   700  	// No folder has been created yet at st.dir, so using it as the path for
   701  	// the resize call should trigger an error.
   702  	resizeValues := url.Values{}
   703  	resizeValues.Set("path", st.dir)
   704  	resizeValues.Set("newsize", mediumSizeFolderString)
   705  	err = st.stdPostAPI("/host/storage/folders/resize", resizeValues)
   706  	if err == nil || err.Error() != errStorageFolderNotFound.Error() {
   707  		t.Fatalf("expected error to be %v, got %v", errStorageFolderNotFound, err)
   708  	}
   709  }
   710  
   711  // TestStorageFolderUnavailable simulates the situation where a storage folder
   712  // is not available to the host when the host starts, verifying that it sets
   713  // FailedWrites and FailedReads correctly and eventually finds the storage
   714  // folder when it is made available to the host again.
   715  func TestStorageFolderUnavailable(t *testing.T) {
   716  	if testing.Short() || !build.VLONG {
   717  		t.SkipNow()
   718  	}
   719  	t.Parallel()
   720  
   721  	st, err := createServerTester(t.Name())
   722  	if err != nil {
   723  		t.Fatal(err)
   724  	}
   725  	defer st.server.Close()
   726  
   727  	// add a storage folder
   728  	sfPath := build.TempDir(t.Name(), "storagefolder")
   729  	err = os.MkdirAll(sfPath, 0755)
   730  	if err != nil {
   731  		t.Fatal(err)
   732  	}
   733  	sfValues := url.Values{}
   734  	sfValues.Set("path", sfPath)
   735  	sfValues.Set("size", "1048576")
   736  	err = st.stdPostAPI("/host/storage/folders/add", sfValues)
   737  	if err != nil {
   738  		t.Fatal(err)
   739  	}
   740  
   741  	var sfs StorageGET
   742  	err = st.getAPI("/host/storage", &sfs)
   743  	if err != nil {
   744  		t.Fatal(err)
   745  	}
   746  
   747  	if sfs.Folders[0].FailedReads != 0 || sfs.Folders[0].FailedWrites != 0 {
   748  		t.Fatal("newly added folder has failed reads or writes")
   749  	}
   750  
   751  	// remove the folder on disk
   752  	st.server.Close()
   753  	sfPath2 := build.TempDir(t.Name(), "storagefolder-old")
   754  	err = os.Rename(sfPath, sfPath2)
   755  	if err != nil {
   756  		t.Fatal(err)
   757  	}
   758  
   759  	// reload the host
   760  	st, err = st.reloadedServerTester()
   761  	if err != nil {
   762  		t.Fatal(err)
   763  	}
   764  	defer st.server.Close()
   765  
   766  	err = st.getAPI("/host/storage", &sfs)
   767  	if err != nil {
   768  		t.Fatal(err)
   769  	}
   770  	if sfs.Folders[0].FailedWrites < 999 {
   771  		t.Fatal("storage folder should have lots of failed writes after being moved on disk")
   772  	}
   773  	if sfs.Folders[0].FailedReads < 999 {
   774  		t.Fatal("storage folder should have lots of failed reads after being moved on disk")
   775  	}
   776  
   777  	// try some actions on the dead storage folder
   778  	// resize
   779  	sfValues.Set("size", "2097152")
   780  	err = st.stdPostAPI("/host/storage/folders/resize", sfValues)
   781  	if err == nil {
   782  		t.Fatal("expected resize on unavailable storage folder to fail")
   783  	}
   784  	// remove
   785  	err = st.stdPostAPI("/host/storage/folders/remove", sfValues)
   786  	if err == nil {
   787  		t.Fatal("expected remove on unavailable storage folder to fail")
   788  	}
   789  
   790  	// move the folder back
   791  	err = os.Rename(sfPath2, sfPath)
   792  	if err != nil {
   793  		t.Fatal(err)
   794  	}
   795  
   796  	// wait for the contract manager to recheck the storage folder
   797  	// NOTE: this is a hard-coded constant based on the contractmanager's maxFolderRecheckInterval constant.
   798  	time.Sleep(time.Second * 10)
   799  
   800  	// verify the storage folder is reset to normal
   801  	err = st.getAPI("/host/storage", &sfs)
   802  	if err != nil {
   803  		t.Fatal(err)
   804  	}
   805  	if sfs.Folders[0].FailedWrites > 0 {
   806  		t.Fatal("storage folder should have no failed writes after being moved back")
   807  	}
   808  	if sfs.Folders[0].FailedReads > 0 {
   809  		t.Fatal("storage folder should have no failed reads after being moved back")
   810  	}
   811  
   812  	// reload the host and verify the storage folder is still good
   813  	st.server.Close()
   814  	st, err = st.reloadedServerTester()
   815  	if err != nil {
   816  		t.Fatal(err)
   817  	}
   818  	defer st.server.Close()
   819  
   820  	// storage folder should still be good
   821  	err = st.getAPI("/host/storage", &sfs)
   822  	if err != nil {
   823  		t.Fatal(err)
   824  	}
   825  	if sfs.Folders[0].FailedWrites > 0 {
   826  		t.Fatal("storage folder should have no failed writes after being moved back")
   827  	}
   828  	if sfs.Folders[0].FailedReads > 0 {
   829  		t.Fatal("storage folder should have no failed reads after being moved back")
   830  	}
   831  }
   832  
   833  // TestResizeFolderNoPath checks that an API call to resize a storage folder fails
   834  // if no path was provided.
   835  func TestResizeFolderNoPath(t *testing.T) {
   836  	if testing.Short() {
   837  		t.SkipNow()
   838  	}
   839  	t.Parallel()
   840  	st, err := createServerTester(t.Name())
   841  	if err != nil {
   842  		t.Fatal(err)
   843  	}
   844  	defer st.server.panicClose()
   845  
   846  	// The call to resize should fail if no path has been provided.
   847  	resizeValues := url.Values{}
   848  	resizeValues.Set("newsize", mediumSizeFolderString)
   849  	err = st.stdPostAPI("/host/storage/folders/resize", resizeValues)
   850  	if err == nil || err.Error() != errNoPath.Error() {
   851  		t.Fatalf("expected error to be %v; got %v", errNoPath, err)
   852  	}
   853  }
   854  
   855  // TestRemoveEmptyStorageFolder checks that removing an empty storage folder
   856  // succeeds -- even if the host is left with zero storage folders.
   857  func TestRemoveEmptyStorageFolder(t *testing.T) {
   858  	if testing.Short() {
   859  		t.SkipNow()
   860  	}
   861  	t.Parallel()
   862  	st, err := createServerTester(t.Name())
   863  	if err != nil {
   864  		t.Fatal(err)
   865  	}
   866  	defer st.server.panicClose()
   867  
   868  	// Set up a storage folder for the host.
   869  	if err := st.setHostStorage(); err != nil {
   870  		t.Fatal(err)
   871  	}
   872  
   873  	// Try to delete the host's empty storage folder.
   874  	removeValues := url.Values{}
   875  	removeValues.Set("path", st.dir)
   876  	if err = st.stdPostAPI("/host/storage/folders/remove", removeValues); err != nil {
   877  		t.Fatal(err)
   878  	}
   879  }
   880  
   881  // TestRemoveStorageFolderError checks that invalid calls to
   882  // /host/storage/folders/remove fail with the appropriate error.
   883  func TestRemoveStorageFolderError(t *testing.T) {
   884  	if testing.Short() {
   885  		t.SkipNow()
   886  	}
   887  	t.Parallel()
   888  	st, err := createServerTester(t.Name())
   889  	if err != nil {
   890  		t.Fatal(err)
   891  	}
   892  	defer st.server.panicClose()
   893  
   894  	// Set up a storage folder for the host.
   895  	if err := st.setHostStorage(); err != nil {
   896  		t.Fatal(err)
   897  	}
   898  
   899  	// Try removing a nonexistent folder.
   900  	removeValues := url.Values{}
   901  	removeValues.Set("path", "/foo/bar")
   902  	err = st.stdPostAPI("/host/storage/folders/remove", removeValues)
   903  	if err == nil || err.Error() != errStorageFolderNotFound.Error() {
   904  		t.Fatalf("expected error %v, got %v", errStorageFolderNotFound, err)
   905  	}
   906  
   907  	// The folder path can't be an empty string.
   908  	removeValues.Set("path", "")
   909  	err = st.stdPostAPI("/host/storage/folders/remove", removeValues)
   910  	if err == nil || err.Error() != errNoPath.Error() {
   911  		t.Fatalf("expected error to be %v; got %v", errNoPath, err)
   912  	}
   913  }
   914  
   915  // TestRemoveStorageFolderForced checks that if a call to remove a storage
   916  // folder will result in data loss, that call succeeds if and only if "force"
   917  // has been set to "true".
   918  func TestRemoveStorageFolderForced(t *testing.T) {
   919  	if testing.Short() {
   920  		t.SkipNow()
   921  	}
   922  	t.Parallel()
   923  	st, err := createServerTester(t.Name())
   924  	if err != nil {
   925  		t.Fatal(err)
   926  	}
   927  	defer st.server.panicClose()
   928  
   929  	// Announce the host.
   930  	if err := st.announceHost(); err != nil {
   931  		t.Fatal(err)
   932  	}
   933  	if err := st.acceptContracts(); err != nil {
   934  		t.Fatal(err)
   935  	}
   936  	if err := st.setHostStorage(); err != nil {
   937  		t.Fatal(err)
   938  	}
   939  
   940  	// Set an allowance for the renter, allowing a contract to be formed.
   941  	allowanceValues := url.Values{}
   942  	allowanceValues.Set("funds", testFunds)
   943  	allowanceValues.Set("period", testPeriod)
   944  	allowanceValues.Set("renewwindow", testRenewWindow)
   945  	allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts))
   946  	if err = st.stdPostAPI("/renter", allowanceValues); err != nil {
   947  		t.Fatal(err)
   948  	}
   949  
   950  	// Block until the allowance has finished forming contracts.
   951  	err = build.Retry(50, time.Millisecond*250, func() error {
   952  		var rc RenterContracts
   953  		err = st.getAPI("/renter/contracts", &rc)
   954  		if err != nil {
   955  			return errors.New("couldn't get renter stats")
   956  		}
   957  		if len(rc.Contracts) != 1 {
   958  			return errors.New("no contracts")
   959  		}
   960  		return nil
   961  	})
   962  	if err != nil {
   963  		t.Fatal("allowance setting failed")
   964  	}
   965  
   966  	// Create a file for upload.
   967  	path := filepath.Join(st.dir, "test.dat")
   968  	if err := createRandFile(path, 512); err != nil {
   969  		t.Fatal(err)
   970  	}
   971  	// Upload to host.
   972  	uploadValues := url.Values{}
   973  	uploadValues.Set("source", path)
   974  	if err := st.stdPostAPI("/renter/upload/test", uploadValues); err != nil {
   975  		t.Fatal(err)
   976  	}
   977  
   978  	// Only one piece will be uploaded (10%  at current redundancy)
   979  	var rf RenterFiles
   980  	for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ {
   981  		st.getAPI("/renter/files", &rf)
   982  		time.Sleep(50 * time.Millisecond)
   983  	}
   984  	if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 {
   985  		t.Error(rf.Files[0].UploadProgress)
   986  		t.Fatal("uploading has failed")
   987  	}
   988  
   989  	// The host should not be able to remove its only folder without setting
   990  	// "force" to "true", since this will result in data loss (there are no
   991  	// other folders for the data to be redistributed to).
   992  	removeValues := url.Values{}
   993  	removeValues.Set("path", st.dir)
   994  	err = st.stdPostAPI("/host/storage/folders/remove", removeValues)
   995  	if err == nil || err.Error() != contractmanager.ErrPartialRelocation.Error() {
   996  		t.Fatalf("expected err to be %v; got %v", contractmanager.ErrPartialRelocation, err)
   997  	}
   998  	// Forced removal of the folder should succeed, though.
   999  	removeValues.Set("force", "true")
  1000  	err = st.stdPostAPI("/host/storage/folders/remove", removeValues)
  1001  	if err != nil {
  1002  		t.Fatal(err)
  1003  	}
  1004  }
  1005  
  1006  // TestDeleteSector tests the call to delete a storage sector from the host.
  1007  func TestDeleteSector(t *testing.T) {
  1008  	t.Skip("broken because Merkle roots are no longer exposed")
  1009  	if testing.Short() {
  1010  		t.SkipNow()
  1011  	}
  1012  	t.Parallel()
  1013  	st, err := createServerTester(t.Name())
  1014  	if err != nil {
  1015  		t.Fatal(err)
  1016  	}
  1017  	defer st.server.panicClose()
  1018  
  1019  	// Set up the host for forming contracts.
  1020  	if err := st.announceHost(); err != nil {
  1021  		t.Fatal(err)
  1022  	}
  1023  	if err := st.acceptContracts(); err != nil {
  1024  		t.Fatal(err)
  1025  	}
  1026  	if err := st.setHostStorage(); err != nil {
  1027  		t.Fatal(err)
  1028  	}
  1029  
  1030  	// Set an allowance for the renter, allowing contracts to formed.
  1031  	allowanceValues := url.Values{}
  1032  	allowanceValues.Set("funds", testFunds)
  1033  	allowanceValues.Set("period", testPeriod)
  1034  	if err = st.stdPostAPI("/renter", allowanceValues); err != nil {
  1035  		t.Fatal(err)
  1036  	}
  1037  
  1038  	// Block until the allowance has finished forming contracts.
  1039  	err = build.Retry(50, time.Millisecond*250, func() error {
  1040  		var rc RenterContracts
  1041  		err = st.getAPI("/renter/contracts", &rc)
  1042  		if err != nil {
  1043  			return errors.New("couldn't get renter stats")
  1044  		}
  1045  		if len(rc.Contracts) != 1 {
  1046  			return errors.New("no contracts")
  1047  		}
  1048  		return nil
  1049  	})
  1050  	if err != nil {
  1051  		t.Fatal("allowance setting failed")
  1052  	}
  1053  
  1054  	// Create a file.
  1055  	path := filepath.Join(st.dir, "test.dat")
  1056  	if err := createRandFile(path, 1024); err != nil {
  1057  		t.Fatal(err)
  1058  	}
  1059  
  1060  	// Upload to host.
  1061  	uploadValues := url.Values{}
  1062  	uploadValues.Set("source", path)
  1063  	if err = st.stdPostAPI("/renter/upload/test", uploadValues); err != nil {
  1064  		t.Fatal(err)
  1065  	}
  1066  
  1067  	// Only one piece will be uploaded (10%  at current redundancy)
  1068  	var rf RenterFiles
  1069  	for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ {
  1070  		st.getAPI("/renter/files", &rf)
  1071  		time.Sleep(50 * time.Millisecond)
  1072  	}
  1073  	if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 {
  1074  		t.Error(rf.Files[0].UploadProgress)
  1075  		t.Fatal("uploading has failed")
  1076  	}
  1077  
  1078  	// Get the Merkle root of the piece that was uploaded.
  1079  	contracts := st.renter.Contracts()
  1080  	if len(contracts) != 1 {
  1081  		t.Fatalf("expected exactly 1 contract to have been formed; got %v instead", len(contracts))
  1082  	}
  1083  	// if len(contracts[0].MerkleRoots) < 1 {
  1084  	// 	t.Fatal("expected at least one merkle root")
  1085  	// }
  1086  	// sectorRoot := contracts[0].MerkleRoots[0].String()
  1087  
  1088  	// if err = st.stdPostAPI("/host/storage/sectors/delete/"+sectorRoot, url.Values{}); err != nil {
  1089  	// 	t.Fatal(err)
  1090  	// }
  1091  }
  1092  
  1093  // TestDeleteNonexistentSector checks that attempting to delete a storage
  1094  // sector that doesn't exist will fail with the appropriate error.
  1095  func TestDeleteNonexistentSector(t *testing.T) {
  1096  	if testing.Short() {
  1097  		t.SkipNow()
  1098  	}
  1099  	t.Parallel()
  1100  	st, err := createServerTester(t.Name())
  1101  	if err != nil {
  1102  		t.Fatal(err)
  1103  	}
  1104  	defer st.server.panicClose()
  1105  
  1106  	// These calls to delete imaginary sectors should fail for a few reasons:
  1107  	// - the given sector root strings are invalid
  1108  	// - the renter hasn't uploaded anything
  1109  	// - the host has no storage folders yet
  1110  	// Right now, the calls fail for the first reason. This test will report if that behavior changes.
  1111  	badHash := crypto.HashObject("fake object").String()
  1112  	err = st.stdPostAPI("/host/storage/sectors/delete/"+badHash, url.Values{})
  1113  	if err == nil || err.Error() != contractmanager.ErrSectorNotFound.Error() {
  1114  		t.Fatalf("expected error to be %v; got %v", contractmanager.ErrSectorNotFound, err)
  1115  	}
  1116  	wrongSize := "wrong size string"
  1117  	err = st.stdPostAPI("/host/storage/sectors/delete/"+wrongSize, url.Values{})
  1118  	if err == nil || err.Error() != crypto.ErrHashWrongLen.Error() {
  1119  		t.Fatalf("expected error to be %v; got %v", crypto.ErrHashWrongLen, err)
  1120  	}
  1121  }