gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/filesystem/siafile/persist_compat_test.go (about)

     1  package siafile
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"gitlab.com/NebulousLabs/errors"
     8  	"go.sia.tech/siad/crypto"
     9  	"go.sia.tech/siad/modules"
    10  	"go.sia.tech/siad/types"
    11  )
    12  
    13  // TestMetadataCompatCheck probes the metadataCompatCheck method
    14  func TestMetadataCompatCheck(t *testing.T) {
    15  	if testing.Short() {
    16  		t.SkipNow()
    17  	}
    18  	t.Parallel()
    19  
    20  	t.Run("NilMetadataToV1", testUpgradeNilMetadataToV1)
    21  	t.Run("V1MetadataToV2", testUpgradeV1MetadataToV2)
    22  	t.Run("V2MetadataToV3", testUpgradeV2MetadataToV3)
    23  }
    24  
    25  // testUpgradeNilMetadataToV1 tests the compat code related to upgrading from a
    26  // nilMetadataVerion to metadataVersion1
    27  func testUpgradeNilMetadataToV1(t *testing.T) {
    28  	t.Parallel()
    29  	t.Run("UniqueIDMissing", testUniqueIDMissing)
    30  	t.Run("ZeroByteFile", testZeroByteFileCompat)
    31  }
    32  
    33  // testUpgradeV1MetadataToV2 tests the compat code related to upgrading from a
    34  // metadataVersion1 to metadataVersion2
    35  func testUpgradeV1MetadataToV2(t *testing.T) {
    36  	t.Parallel()
    37  
    38  	// Create siafile
    39  	sf := newBlankTestFile()
    40  
    41  	// Add a piece to the file so that it shows uploaded bytes
    42  	err := sf.AddPiece(types.SiaPublicKey{}, uint64(0), 0, crypto.Hash{})
    43  	if err != nil {
    44  		t.Fatal(err)
    45  	}
    46  
    47  	// Assert test conditions
    48  	_, unique, err := sf.uploadedBytes()
    49  	if err != nil {
    50  		t.Fatal(err)
    51  	}
    52  	if unique != modules.SectorSize {
    53  		t.Fatal("unique not expected", unique)
    54  	}
    55  
    56  	// check checks the metadata
    57  	check := func(sf *SiaFile, finished, stuck bool, version [16]byte) error {
    58  		// Version check
    59  		if sf.staticMetadata.StaticVersion != version {
    60  			return fmt.Errorf("Wrong version, expected %v got %v", version, sf.staticMetadata.StaticVersion)
    61  		}
    62  
    63  		// Check Stuck
    64  		isStuck := sf.staticMetadata.NumStuckChunks > 0
    65  		if stuck != isStuck {
    66  			return fmt.Errorf("Expected stuck %v, got stuck %v", stuck, isStuck)
    67  		}
    68  
    69  		// Check finished
    70  		if finished != sf.finished() {
    71  			return fmt.Errorf("Expected finished %v, got finished %v", finished, sf.finished())
    72  		}
    73  		return nil
    74  	}
    75  
    76  	// reset resets the metadata
    77  	reset := func(sf *SiaFile, finished, stuck, localPath bool) {
    78  		// Reset version and finished flag
    79  		sf.staticMetadata.StaticVersion = metadataVersion1
    80  		sf.staticMetadata.Finished = false
    81  
    82  		// Set the expected stuck state
    83  		if stuck {
    84  			sf.staticMetadata.NumStuckChunks = 1
    85  		} else {
    86  			sf.staticMetadata.NumStuckChunks = 0
    87  		}
    88  
    89  		// Set the expected finished state
    90  		if finished {
    91  			sf.staticMetadata.FileSize = 1
    92  		} else {
    93  			sf.staticMetadata.FileSize = int64(modules.SectorSize) + 1
    94  		}
    95  
    96  		// Set the localPath
    97  		if localPath {
    98  			sf.staticMetadata.LocalPath = "localpath"
    99  		} else {
   100  			sf.staticMetadata.LocalPath = ""
   101  		}
   102  	}
   103  
   104  	// Define tests
   105  	var tests = []struct {
   106  		name string
   107  
   108  		finished bool
   109  		stuck    bool
   110  
   111  		localPath bool
   112  	}{
   113  		// With Local Path
   114  		//
   115  		// Test for finished stuck
   116  		{"Finished_Stuck", true, true, true},
   117  		// Test for unfinished stuck
   118  		{"Unfinished_Stuck", false, true, true},
   119  		// Test for finished unstuck
   120  		{"Finished_Unstuck", true, false, true},
   121  		// Test for unfinished unstuck
   122  		{"Unfinished_Unstuck", false, false, true},
   123  		// Without Local Path
   124  		//
   125  		// Test for finished stuck
   126  		{"Finished_Stuck", true, true, false},
   127  		// Test for unfinished stuck
   128  		{"Unfinished_Stuck", false, true, false},
   129  		// Test for finished unstuck
   130  		{"Finished_Unstuck", true, false, false},
   131  		// Test for unfinished unstuck
   132  		{"Unfinished_Unstuck", false, false, false},
   133  	}
   134  
   135  	// Run tests
   136  	for _, test := range tests {
   137  		// A file is considered finished if it finished or has a localpath
   138  		finished := test.finished || test.localPath
   139  
   140  		// At the end of the compat, the file should only be stuck if it
   141  		// was originally finished or has a localpath, and is stuck.
   142  		stuck := finished && test.stuck
   143  
   144  		// Reset the metadata
   145  		reset(sf, test.finished, test.stuck, test.localPath)
   146  
   147  		// test testUpgradeV1MetadataToV2
   148  		err = sf.upgradeMetadataFromV1ToV2()
   149  		if err != nil {
   150  			t.Fatal(test, err)
   151  		}
   152  
   153  		// Check the metadata
   154  		err = check(sf, finished, stuck, metadataVersion2)
   155  		if err != nil {
   156  			t.Fatal(test, err)
   157  		}
   158  
   159  		// Reset the metadata
   160  		reset(sf, test.finished, test.stuck, test.localPath)
   161  
   162  		// Save the metadata to disk
   163  		err = sf.SaveMetadata()
   164  		if err != nil {
   165  			t.Fatal(test, err)
   166  		}
   167  
   168  		// Reload the SiaFile from disk
   169  		sf, err = LoadSiaFile(sf.siaFilePath, sf.wal)
   170  		if err != nil {
   171  			t.Fatal(test, err)
   172  		}
   173  
   174  		// Check the metadata
   175  		//
   176  		// Update for MetadateVersion3, all cases should now be
   177  		// !finished and !stuck
   178  		err = check(sf, false, false, metadataVersion)
   179  		if err != nil {
   180  			t.Fatal(test, err)
   181  		}
   182  	}
   183  }
   184  
   185  // testUpgradeV2MetadataToV3 tests the compat code related to upgrading from a
   186  // metadataVersion2 to metadataVersion3
   187  func testUpgradeV2MetadataToV3(t *testing.T) {
   188  	t.Parallel()
   189  
   190  	// Create siafile
   191  	sf := newBlankTestFile()
   192  
   193  	// check checks the metadata
   194  	check := func(sf *SiaFile, version [16]byte) error {
   195  		// Version check
   196  		if sf.staticMetadata.StaticVersion != version {
   197  			return fmt.Errorf("Wrong version, expected %v got %v", version, sf.staticMetadata.StaticVersion)
   198  		}
   199  
   200  		// Check Stuck
   201  		if sf.staticMetadata.NumStuckChunks > 0 {
   202  			return fmt.Errorf("File should not be stuck %v", sf.staticMetadata.NumStuckChunks)
   203  		}
   204  
   205  		// Check finished
   206  		if sf.finished() {
   207  			return errors.New("file should not be finished")
   208  		}
   209  		return nil
   210  	}
   211  
   212  	// reset resets the metadata
   213  	reset := func(sf *SiaFile, finished, stuck bool) {
   214  		// Reset version and finished flag
   215  		sf.staticMetadata.StaticVersion = metadataVersion2
   216  		sf.staticMetadata.Finished = finished
   217  
   218  		// Set the expected stuck state
   219  		if stuck {
   220  			sf.staticMetadata.NumStuckChunks = 1
   221  		} else {
   222  			sf.staticMetadata.NumStuckChunks = 0
   223  		}
   224  	}
   225  
   226  	// Define tests
   227  	var tests = []struct {
   228  		name string
   229  
   230  		finished bool
   231  		stuck    bool
   232  	}{
   233  		// Test for finished stuck
   234  		{"Finished_Stuck", true, true},
   235  		// Test for unfinished stuck
   236  		{"Unfinished_Stuck", false, true},
   237  		// Test for finished unstuck
   238  		{"Finished_Unstuck", true, false},
   239  		// Test for unfinished unstuck
   240  		{"Unfinished_Unstuck", false, false},
   241  	}
   242  
   243  	// Run tests
   244  	for _, test := range tests {
   245  		// Reset the metadata
   246  		reset(sf, test.finished, test.stuck)
   247  
   248  		// test testUpgradeV2MetadataToV3
   249  		err := sf.upgradeMetadataFromV2ToV3()
   250  		if err != nil {
   251  			t.Fatal(test, err)
   252  		}
   253  
   254  		// Check the metadata
   255  		err = check(sf, metadataVersion3)
   256  		if err != nil {
   257  			t.Fatal(test, err)
   258  		}
   259  
   260  		// Reset the metadata
   261  		reset(sf, test.finished, test.stuck)
   262  
   263  		// Save the metadata to disk
   264  		err = sf.SaveMetadata()
   265  		if err != nil {
   266  			t.Fatal(test, err)
   267  		}
   268  
   269  		// Reload the SiaFile from disk
   270  		sf, err = LoadSiaFile(sf.siaFilePath, sf.wal)
   271  		if err != nil {
   272  			t.Fatal(test, err)
   273  		}
   274  
   275  		// Check the metadata
   276  		err = check(sf, metadataVersion)
   277  		if err != nil {
   278  			t.Fatal(test, err)
   279  		}
   280  	}
   281  }
   282  
   283  // testUniqueIDMissing makes sure that loading a siafile sets the unique id in
   284  // the metadata if it wasn't set before.
   285  func testUniqueIDMissing(t *testing.T) {
   286  	// Create a new file.
   287  	sf, wal, _ := newBlankTestFileAndWAL(1)
   288  	// It should have a UID.
   289  	if sf.staticMetadata.UniqueID == "" {
   290  		t.Fatal("unique ID wasn't set")
   291  	}
   292  
   293  	// check checks the metadata
   294  	check := func(sf *SiaFile, version [16]byte) error {
   295  		// It should have a UID now.
   296  		if sf.staticMetadata.UniqueID == "" {
   297  			return errors.New("unique ID still blank")
   298  		}
   299  
   300  		// Check Version
   301  		if sf.staticMetadata.StaticVersion != version {
   302  			return fmt.Errorf("Wrong version, expected %v got %v", version, sf.staticMetadata.StaticVersion)
   303  		}
   304  		return nil
   305  	}
   306  
   307  	// reset resets the metadata
   308  	reset := func(sf *SiaFile) {
   309  		// Set the UID to a blank string, and reset the metadata version
   310  		sf.staticMetadata.UniqueID = ""
   311  		sf.staticMetadata.StaticVersion = nilMetadataVesion
   312  	}
   313  
   314  	// Reset the metadata
   315  	reset(sf)
   316  
   317  	// Test upgradeMetadataFromNilToV1
   318  	sf.upgradeMetadataFromNilToV1()
   319  
   320  	// Check
   321  	err := check(sf, metadataVersion1)
   322  	if err != nil {
   323  		t.Fatal(err)
   324  	}
   325  
   326  	// Reset the metadata
   327  	reset(sf)
   328  
   329  	// Save the metadata to disk
   330  	err = sf.SaveMetadata()
   331  	if err != nil {
   332  		t.Fatal(err)
   333  	}
   334  
   335  	// Load the file again.
   336  	sf, err = LoadSiaFile(sf.siaFilePath, wal)
   337  	if err != nil {
   338  		t.Fatal(err)
   339  	}
   340  
   341  	// Check
   342  	err = check(sf, metadataVersion)
   343  	if err != nil {
   344  		t.Fatal(err)
   345  	}
   346  }
   347  
   348  // testZeroByteFileCompat checks that 0-byte siafiles that have been uploaded
   349  // before caching was introduced have the correct cached values after being
   350  // loaded.
   351  func testZeroByteFileCompat(t *testing.T) {
   352  	// Create the file.
   353  	siaFilePath, _, source, rc, sk, _, _, fileMode := newTestFileParams(1, true)
   354  	sf, wal, _ := customTestFileAndWAL(siaFilePath, source, rc, sk, 0, 0, fileMode)
   355  	// Check that the number of chunks in the file is correct.
   356  	if sf.numChunks != 0 {
   357  		panic(fmt.Sprintf("newTestFile didn't create the expected number of chunks: %v", sf.numChunks))
   358  	}
   359  
   360  	expectedRedundancy := float64(rc.NumPieces()) / float64(rc.MinPieces())
   361  	// check checks the metadata
   362  	check := func(sf *SiaFile, version [16]byte) error {
   363  		// Make sure the loaded file has the correct cached values.
   364  		if sf.staticMetadata.CachedHealth != 0 {
   365  			return fmt.Errorf("CachedHealth should be 0 but was %v", sf.staticMetadata.CachedHealth)
   366  		}
   367  		if sf.staticMetadata.CachedRedundancy != expectedRedundancy {
   368  			return fmt.Errorf("CachedRedundancy should be %v but was %v", expectedRedundancy, sf.staticMetadata.CachedRedundancy)
   369  		}
   370  		if sf.staticMetadata.CachedRepairBytes != 0 {
   371  			return fmt.Errorf("CachedRepairBytes should be 0 but was %v", sf.staticMetadata.CachedRepairBytes)
   372  		}
   373  		if sf.staticMetadata.CachedStuckBytes != 0 {
   374  			return fmt.Errorf("CachedStuckBytes should be 0 but was %v", sf.staticMetadata.CachedStuckBytes)
   375  		}
   376  		if sf.staticMetadata.CachedStuckHealth != 0 {
   377  			return fmt.Errorf("CachedStuckHealth should be 0 but was %v", sf.staticMetadata.CachedStuckHealth)
   378  		}
   379  		if sf.staticMetadata.CachedUserRedundancy != expectedRedundancy {
   380  			return fmt.Errorf("CachedRedundancy should be %v but was %v", expectedRedundancy, sf.staticMetadata.CachedUserRedundancy)
   381  		}
   382  		if sf.staticMetadata.CachedUploadProgress != 100 {
   383  			return fmt.Errorf("CachedUploadProgress should be 100 but was %v", sf.staticMetadata.CachedUploadProgress)
   384  		}
   385  		// Check Version
   386  		if sf.staticMetadata.StaticVersion != version {
   387  			return fmt.Errorf("Wrong version, expected %v got %v", version, sf.staticMetadata.StaticVersion)
   388  		}
   389  		return nil
   390  	}
   391  
   392  	// reset resets the metadata
   393  	reset := func(sf *SiaFile) {
   394  		// Set the cached fields and version to 0 like they would be if the file
   395  		// was already uploaded before caching was introduced.
   396  		sf.staticMetadata.StaticVersion = nilMetadataVesion
   397  		sf.staticMetadata.CachedHealth = 0
   398  		sf.staticMetadata.CachedRedundancy = 0
   399  		sf.staticMetadata.CachedRepairBytes = 0
   400  		sf.staticMetadata.CachedStuckBytes = 0
   401  		sf.staticMetadata.CachedStuckHealth = 0
   402  		sf.staticMetadata.CachedUserRedundancy = 0
   403  		sf.staticMetadata.CachedUploadProgress = 0
   404  	}
   405  
   406  	// Reset the metadata to pre compat state
   407  	reset(sf)
   408  
   409  	// test upgradeMetadataFromNilToV1
   410  	sf.upgradeMetadataFromNilToV1()
   411  
   412  	// Check the Metadata
   413  	err := check(sf, metadataVersion1)
   414  	if err != nil {
   415  		t.Fatal(err)
   416  	}
   417  
   418  	// Reset the metadata to pre compat state
   419  	reset(sf)
   420  
   421  	// Save the file and reload it.
   422  	if err := sf.SaveMetadata(); err != nil {
   423  		t.Fatal(err)
   424  	}
   425  	sf, err = loadSiaFile(siaFilePath, wal, modules.ProdDependencies)
   426  	if err != nil {
   427  		t.Fatal(err)
   428  	}
   429  
   430  	// Check the Metadata
   431  	err = check(sf, metadataVersion)
   432  	if err != nil {
   433  		t.Fatal(err)
   434  	}
   435  }