gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/renter/siadir/persist_test.go (about)

     1  package siadir
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"testing"
    10  
    11  	"gitlab.com/NebulousLabs/fastrand"
    12  	"gitlab.com/SiaPrime/SiaPrime/modules"
    13  	"gitlab.com/SiaPrime/writeaheadlog"
    14  )
    15  
    16  // equalMetadatas is a helper that compares two siaDirMetadatas. If using this
    17  // function to check persistence the time fields should be checked in the test
    18  // itself as well and reset due to how time is persisted
    19  func equalMetadatas(md, md2 Metadata) error {
    20  	// Check Aggregate Fields
    21  	if md.AggregateHealth != md2.AggregateHealth {
    22  		return fmt.Errorf("AggregateHealths not equal, %v and %v", md.AggregateHealth, md2.AggregateHealth)
    23  	}
    24  	if md.AggregateLastHealthCheckTime != md2.AggregateLastHealthCheckTime {
    25  		return fmt.Errorf("AggregateLastHealthCheckTimes not equal, %v and %v", md.AggregateLastHealthCheckTime, md2.AggregateLastHealthCheckTime)
    26  	}
    27  	if md.AggregateMinRedundancy != md2.AggregateMinRedundancy {
    28  		return fmt.Errorf("AggregateMinRedundancy not equal, %v and %v", md.AggregateMinRedundancy, md2.AggregateMinRedundancy)
    29  	}
    30  	if md.AggregateModTime != md2.AggregateModTime {
    31  		return fmt.Errorf("AggregateModTimes not equal, %v and %v", md.AggregateModTime, md2.AggregateModTime)
    32  	}
    33  	if md.AggregateNumFiles != md2.AggregateNumFiles {
    34  		return fmt.Errorf("AggregateNumFiles not equal, %v and %v", md.AggregateNumFiles, md2.AggregateNumFiles)
    35  	}
    36  	if md.AggregateNumStuckChunks != md2.AggregateNumStuckChunks {
    37  		return fmt.Errorf("AggregateNumStuckChunks not equal, %v and %v", md.AggregateNumStuckChunks, md2.AggregateNumStuckChunks)
    38  	}
    39  	if md.AggregateNumSubDirs != md2.AggregateNumSubDirs {
    40  		return fmt.Errorf("AggregateNumSubDirs not equal, %v and %v", md.AggregateNumSubDirs, md2.AggregateNumSubDirs)
    41  	}
    42  	if md.AggregateSize != md2.AggregateSize {
    43  		return fmt.Errorf("AggregateSizes not equal, %v and %v", md.AggregateSize, md2.AggregateSize)
    44  	}
    45  	if md.AggregateStuckHealth != md2.AggregateStuckHealth {
    46  		return fmt.Errorf("AggregateStuckHealths not equal, %v and %v", md.AggregateStuckHealth, md2.AggregateStuckHealth)
    47  	}
    48  
    49  	// Check SiaDir Fields
    50  	if md.Health != md2.Health {
    51  		return fmt.Errorf("Healths not equal, %v and %v", md.Health, md2.Health)
    52  	}
    53  	if md.LastHealthCheckTime != md2.LastHealthCheckTime {
    54  		return fmt.Errorf("lasthealthchecktimes not equal, %v and %v", md.LastHealthCheckTime, md2.LastHealthCheckTime)
    55  	}
    56  	if md.MinRedundancy != md2.MinRedundancy {
    57  		return fmt.Errorf("MinRedundancy not equal, %v and %v", md.MinRedundancy, md2.MinRedundancy)
    58  	}
    59  	if md.ModTime != md2.ModTime {
    60  		return fmt.Errorf("ModTimes not equal, %v and %v", md.ModTime, md2.ModTime)
    61  	}
    62  	if md.NumFiles != md2.NumFiles {
    63  		return fmt.Errorf("NumFiles not equal, %v and %v", md.NumFiles, md2.NumFiles)
    64  	}
    65  	if md.NumStuckChunks != md2.NumStuckChunks {
    66  		return fmt.Errorf("NumStuckChunks not equal, %v and %v", md.NumStuckChunks, md2.NumStuckChunks)
    67  	}
    68  	if md.NumSubDirs != md2.NumSubDirs {
    69  		return fmt.Errorf("NumSubDirs not equal, %v and %v", md.NumSubDirs, md2.NumSubDirs)
    70  	}
    71  	if md.Size != md2.Size {
    72  		return fmt.Errorf("Sizes not equal, %v and %v", md.Size, md2.Size)
    73  	}
    74  	if md.StuckHealth != md2.StuckHealth {
    75  		return fmt.Errorf("StuckHealths not equal, %v and %v", md.StuckHealth, md2.StuckHealth)
    76  	}
    77  	return nil
    78  }
    79  
    80  // newTestDir creates a new SiaDir for testing, the test Name should be passed
    81  // in as the rootDir
    82  func newTestDir(rootDir string) (*SiaDir, error) {
    83  	rootPath := filepath.Join(os.TempDir(), "siadirs", rootDir)
    84  	if err := os.RemoveAll(rootPath); err != nil {
    85  		return nil, err
    86  	}
    87  	wal, _ := newTestWAL()
    88  	return New(modules.RandomSiaPath(), rootPath, wal)
    89  }
    90  
    91  // newTestWal is a helper method to create a WAL for testing.
    92  func newTestWAL() (*writeaheadlog.WAL, string) {
    93  	// Create the wal.
    94  	walsDir := filepath.Join(os.TempDir(), "wals")
    95  	if err := os.MkdirAll(walsDir, 0700); err != nil {
    96  		panic(err)
    97  	}
    98  	walFilePath := filepath.Join(walsDir, hex.EncodeToString(fastrand.Bytes(8)))
    99  	_, wal, err := writeaheadlog.New(walFilePath)
   100  	if err != nil {
   101  		panic(err)
   102  	}
   103  	return wal, walFilePath
   104  }
   105  
   106  // TestCreateReadMetadataUpdate tests if an update can be created using createMetadataUpdate
   107  // and if the created update can be read using readMetadataUpdate.
   108  func TestCreateReadMetadataUpdate(t *testing.T) {
   109  	if testing.Short() {
   110  		t.SkipNow()
   111  	}
   112  	t.Parallel()
   113  
   114  	sd, err := newTestDir(t.Name())
   115  	if err != nil {
   116  		t.Fatal(err)
   117  	}
   118  	// Create metadata update
   119  	path := sd.siaPath.SiaDirMetadataSysPath(sd.rootDir)
   120  	update, err := createMetadataUpdate(path, sd.metadata)
   121  	if err != nil {
   122  		t.Fatal(err)
   123  	}
   124  
   125  	// Read metadata update
   126  	data, path, err := readMetadataUpdate(update)
   127  	if err != nil {
   128  		t.Fatal("Failed to read update", err)
   129  	}
   130  
   131  	// Check path
   132  	path2 := sd.siaPath.SiaDirMetadataSysPath(sd.rootDir)
   133  	if path != path2 {
   134  		t.Fatalf("Path not correct: expected %v got %v", path2, path)
   135  	}
   136  
   137  	// Check data
   138  	var metadata Metadata
   139  	err = json.Unmarshal(data, &metadata)
   140  	if err != nil {
   141  		t.Fatal(err)
   142  	}
   143  	// Check Time separately due to how the time is persisted
   144  	if !metadata.AggregateLastHealthCheckTime.Equal(sd.metadata.AggregateLastHealthCheckTime) {
   145  		t.Fatalf("AggregateLastHealthCheckTimes not equal, got %v expected %v", metadata.AggregateLastHealthCheckTime, sd.metadata.AggregateLastHealthCheckTime)
   146  	}
   147  	sd.metadata.AggregateLastHealthCheckTime = metadata.AggregateLastHealthCheckTime
   148  	if !metadata.LastHealthCheckTime.Equal(sd.metadata.LastHealthCheckTime) {
   149  		t.Fatalf("LastHealthCheckTimes not equal, got %v expected %v", metadata.LastHealthCheckTime, sd.metadata.LastHealthCheckTime)
   150  	}
   151  	sd.metadata.LastHealthCheckTime = metadata.LastHealthCheckTime
   152  	if !metadata.AggregateModTime.Equal(sd.metadata.AggregateModTime) {
   153  		t.Fatalf("AggregateModTimes not equal, got %v expected %v", metadata.AggregateModTime, sd.metadata.AggregateModTime)
   154  	}
   155  	sd.metadata.AggregateModTime = metadata.AggregateModTime
   156  	if !metadata.ModTime.Equal(sd.metadata.ModTime) {
   157  		t.Fatalf("ModTimes not equal, got %v expected %v", metadata.ModTime, sd.metadata.ModTime)
   158  	}
   159  	sd.metadata.ModTime = metadata.ModTime
   160  	if err := equalMetadatas(metadata, sd.metadata); err != nil {
   161  		t.Fatal(err)
   162  	}
   163  }
   164  
   165  // TestCreateReadDeleteUpdate tests if an update can be created using
   166  // createDeleteUpdate and if the created update can be read using
   167  // readDeleteUpdate.
   168  func TestCreateReadDeleteUpdate(t *testing.T) {
   169  	if testing.Short() {
   170  		t.SkipNow()
   171  	}
   172  	t.Parallel()
   173  
   174  	sd, err := newTestDir(t.Name())
   175  	if err != nil {
   176  		t.Fatal(err)
   177  	}
   178  	update := sd.createDeleteUpdate()
   179  	// Read update
   180  	path := readDeleteUpdate(update)
   181  	// Compare values
   182  	siaDirPath := sd.siaPath.SiaDirSysPath(sd.rootDir)
   183  	if path != siaDirPath {
   184  		t.Error("paths don't match")
   185  	}
   186  }
   187  
   188  // TestApplyUpdates tests a variety of functions that are used to apply
   189  // updates.
   190  func TestApplyUpdates(t *testing.T) {
   191  	if testing.Short() {
   192  		t.SkipNow()
   193  	}
   194  	t.Parallel()
   195  
   196  	t.Run("TestApplyUpdates", func(t *testing.T) {
   197  		siadir, err := newTestDir(t.Name())
   198  		if err != nil {
   199  			t.Fatal(err)
   200  		}
   201  		testApply(t, siadir, ApplyUpdates)
   202  	})
   203  	t.Run("TestSiaDirApplyUpdates", func(t *testing.T) {
   204  		siadir, err := newTestDir(t.Name())
   205  		if err != nil {
   206  			t.Fatal(err)
   207  		}
   208  		testApply(t, siadir, siadir.applyUpdates)
   209  	})
   210  	t.Run("TestCreateAndApplyTransaction", func(t *testing.T) {
   211  		siadir, err := newTestDir(t.Name())
   212  		if err != nil {
   213  			t.Fatal(err)
   214  		}
   215  		testApply(t, siadir, siadir.createAndApplyTransaction)
   216  	})
   217  }
   218  
   219  // testApply tests if a given method applies a set of updates correctly.
   220  func testApply(t *testing.T, siadir *SiaDir, apply func(...writeaheadlog.Update) error) {
   221  	// Create an update to the metadata
   222  	metadata := siadir.metadata
   223  	metadata.Health = 1.0
   224  	path := siadir.siaPath.SiaDirMetadataSysPath(siadir.rootDir)
   225  	update, err := createMetadataUpdate(path, metadata)
   226  	if err != nil {
   227  		t.Fatal(err)
   228  	}
   229  
   230  	// Apply update.
   231  	if err := apply(update); err != nil {
   232  		t.Fatal("Failed to apply update", err)
   233  	}
   234  	// Open file.
   235  	sd, err := LoadSiaDir(siadir.rootDir, siadir.siaPath, modules.ProdDependencies, siadir.wal)
   236  	if err != nil {
   237  		t.Fatal("Failed to load siadir", err)
   238  	}
   239  	// Check Time separately due to how the time is persisted
   240  	if !metadata.AggregateLastHealthCheckTime.Equal(sd.metadata.AggregateLastHealthCheckTime) {
   241  		t.Fatalf("AggregateLastHealthCheckTimes not equal, got %v expected %v", metadata.AggregateLastHealthCheckTime, sd.metadata.AggregateLastHealthCheckTime)
   242  	}
   243  	sd.metadata.AggregateLastHealthCheckTime = metadata.AggregateLastHealthCheckTime
   244  	if !metadata.LastHealthCheckTime.Equal(sd.metadata.LastHealthCheckTime) {
   245  		t.Fatalf("LastHealthCheckTimes not equal, got %v expected %v", metadata.LastHealthCheckTime, sd.metadata.LastHealthCheckTime)
   246  	}
   247  	sd.metadata.LastHealthCheckTime = metadata.LastHealthCheckTime
   248  	if !metadata.AggregateModTime.Equal(sd.metadata.AggregateModTime) {
   249  		t.Fatalf("AggregateModTimes not equal, got %v expected %v", metadata.AggregateModTime, sd.metadata.AggregateModTime)
   250  	}
   251  	sd.metadata.AggregateModTime = metadata.AggregateModTime
   252  	if !metadata.ModTime.Equal(sd.metadata.ModTime) {
   253  		t.Fatalf("ModTimes not equal, got %v expected %v", metadata.ModTime, sd.metadata.ModTime)
   254  	}
   255  	sd.metadata.ModTime = metadata.ModTime
   256  	// Check if correct data was written.
   257  	if err := equalMetadatas(metadata, sd.metadata); err != nil {
   258  		t.Fatal(err)
   259  	}
   260  }
   261  
   262  // TestManagedCreateAndApplyTransactions tests if
   263  // managedCreateAndApplyTransactions applies a set of updates correctly.
   264  func TestManagedCreateAndApplyTransactions(t *testing.T) {
   265  	if testing.Short() {
   266  		t.SkipNow()
   267  	}
   268  	t.Parallel()
   269  
   270  	siadir, err := newTestDir(t.Name())
   271  	if err != nil {
   272  		t.Fatal(err)
   273  	}
   274  	// Create an update to the metadata
   275  	metadata := siadir.metadata
   276  	metadata.Health = 1.0
   277  	path := siadir.siaPath.SiaDirMetadataSysPath(siadir.rootDir)
   278  	update, err := createMetadataUpdate(path, metadata)
   279  	if err != nil {
   280  		t.Fatal(err)
   281  	}
   282  
   283  	// Apply update.
   284  	if err := managedCreateAndApplyTransaction(siadir.wal, update); err != nil {
   285  		t.Fatal("Failed to apply update", err)
   286  	}
   287  	// Open file.
   288  	sd, err := LoadSiaDir(siadir.rootDir, siadir.siaPath, modules.ProdDependencies, siadir.wal)
   289  	if err != nil {
   290  		t.Fatal("Failed to load siadir", err)
   291  	}
   292  	// Check Time separately due to how the time is persisted
   293  	if !metadata.AggregateLastHealthCheckTime.Equal(sd.metadata.AggregateLastHealthCheckTime) {
   294  		t.Fatalf("AggregateLastHealthCheckTimes not equal, got %v expected %v", metadata.AggregateLastHealthCheckTime, sd.metadata.AggregateLastHealthCheckTime)
   295  	}
   296  	sd.metadata.AggregateLastHealthCheckTime = metadata.AggregateLastHealthCheckTime
   297  	if !metadata.LastHealthCheckTime.Equal(sd.metadata.LastHealthCheckTime) {
   298  		t.Fatalf("LastHealthCheckTimes not equal, got %v expected %v", metadata.LastHealthCheckTime, sd.metadata.LastHealthCheckTime)
   299  	}
   300  	sd.metadata.LastHealthCheckTime = metadata.LastHealthCheckTime
   301  	if !metadata.AggregateModTime.Equal(sd.metadata.AggregateModTime) {
   302  		t.Fatalf("AggregateModTimes not equal, got %v expected %v", metadata.AggregateModTime, sd.metadata.AggregateModTime)
   303  	}
   304  	sd.metadata.AggregateModTime = metadata.AggregateModTime
   305  	if !metadata.ModTime.Equal(sd.metadata.ModTime) {
   306  		t.Fatalf("ModTimes not equal, got %v expected %v", metadata.ModTime, sd.metadata.ModTime)
   307  	}
   308  	sd.metadata.ModTime = metadata.ModTime
   309  	// Check if correct data was written.
   310  	if err := equalMetadatas(metadata, sd.metadata); err != nil {
   311  		t.Fatal(err)
   312  	}
   313  }