github.com/decred/dcrlnd@v0.7.6/chanbackup/backupfile_test.go (about)

     1  package chanbackup
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"math/rand"
     8  	"os"
     9  	"path/filepath"
    10  	"testing"
    11  )
    12  
    13  func makeFakePackedMulti() (PackedMulti, error) {
    14  	newPackedMulti := make([]byte, 50)
    15  	if _, err := rand.Read(newPackedMulti[:]); err != nil {
    16  		return nil, fmt.Errorf("unable to make test backup: %v", err)
    17  	}
    18  
    19  	return PackedMulti(newPackedMulti), nil
    20  }
    21  
    22  func assertBackupMatches(t *testing.T, filePath string,
    23  	currentBackup PackedMulti) {
    24  
    25  	t.Helper()
    26  
    27  	packedBackup, err := ioutil.ReadFile(filePath)
    28  	if err != nil {
    29  		t.Fatalf("unable to test file: %v", err)
    30  	}
    31  
    32  	if !bytes.Equal(packedBackup, currentBackup) {
    33  		t.Fatalf("backups don't match after first swap: "+
    34  			"expected %x got %x", packedBackup[:],
    35  			currentBackup)
    36  	}
    37  }
    38  
    39  func assertFileDeleted(t *testing.T, filePath string) {
    40  	t.Helper()
    41  
    42  	_, err := os.Stat(filePath)
    43  	if err == nil {
    44  		t.Fatalf("file %v still exists: ", filePath)
    45  	}
    46  }
    47  
    48  // TestUpdateAndSwap test that we're able to properly swap out old backups on
    49  // disk with new ones. Additionally, after a swap operation succeeds, then each
    50  // time we should only have the main backup file on disk, as the temporary file
    51  // has been removed.
    52  func TestUpdateAndSwap(t *testing.T) {
    53  	t.Parallel()
    54  
    55  	tempTestDir, err := ioutil.TempDir("", "")
    56  	if err != nil {
    57  		t.Fatalf("unable to make temp dir: %v", err)
    58  	}
    59  	defer os.Remove(tempTestDir)
    60  
    61  	testCases := []struct {
    62  		fileName     string
    63  		tempFileName string
    64  
    65  		oldTempExists bool
    66  
    67  		valid bool
    68  	}{
    69  		// Main file name is blank, should fail.
    70  		{
    71  			fileName: "",
    72  			valid:    false,
    73  		},
    74  
    75  		// Old temporary file still exists, should be removed. Only one
    76  		// file should remain.
    77  		{
    78  			fileName: filepath.Join(
    79  				tempTestDir, DefaultBackupFileName,
    80  			),
    81  			tempFileName: filepath.Join(
    82  				tempTestDir, DefaultTempBackupFileName,
    83  			),
    84  			oldTempExists: true,
    85  			valid:         true,
    86  		},
    87  
    88  		// Old temp doesn't exist, should swap out file, only a single
    89  		// file remains.
    90  		{
    91  			fileName: filepath.Join(
    92  				tempTestDir, DefaultBackupFileName,
    93  			),
    94  			tempFileName: filepath.Join(
    95  				tempTestDir, DefaultTempBackupFileName,
    96  			),
    97  			valid: true,
    98  		},
    99  	}
   100  	for i, testCase := range testCases {
   101  		// Ensure that all created files are removed at the end of the
   102  		// test case.
   103  		defer os.Remove(testCase.fileName)
   104  		defer os.Remove(testCase.tempFileName)
   105  
   106  		backupFile := NewMultiFile(testCase.fileName)
   107  
   108  		// To start with, we'll make a random byte slice that'll pose
   109  		// as our packed multi backup.
   110  		newPackedMulti, err := makeFakePackedMulti()
   111  		if err != nil {
   112  			t.Fatalf("unable to make test backup: %v", err)
   113  		}
   114  
   115  		// If the old temporary file is meant to exist, then we'll
   116  		// create it now as an empty file.
   117  		if testCase.oldTempExists {
   118  			_, err := os.Create(testCase.tempFileName)
   119  			if err != nil {
   120  				t.Fatalf("unable to create temp file: %v", err)
   121  			}
   122  
   123  			// TODO(roasbeef): mock out fs calls?
   124  		}
   125  
   126  		// With our backup created, we'll now attempt to swap out this
   127  		// backup, for the old one.
   128  		err = backupFile.UpdateAndSwap(newPackedMulti)
   129  		switch {
   130  		// If this is a valid test case, and we failed, then we'll
   131  		// return an error.
   132  		case err != nil && testCase.valid:
   133  			t.Fatalf("#%v, unable to swap file: %v", i, err)
   134  
   135  		// If this is an invalid test case, and we passed it, then
   136  		// we'll return an error.
   137  		case err == nil && !testCase.valid:
   138  			t.Fatalf("#%v file swap should have failed: %v", i, err)
   139  		}
   140  
   141  		if !testCase.valid {
   142  			continue
   143  		}
   144  
   145  		// If we read out the file on disk, then it should match
   146  		// exactly what we wrote. The temp backup file should also be
   147  		// gone.
   148  		assertBackupMatches(t, testCase.fileName, newPackedMulti)
   149  		assertFileDeleted(t, testCase.tempFileName)
   150  
   151  		// Now that we know this is a valid test case, we'll make a new
   152  		// packed multi to swap out this current one.
   153  		newPackedMulti2, err := makeFakePackedMulti()
   154  		if err != nil {
   155  			t.Fatalf("unable to make test backup: %v", err)
   156  		}
   157  
   158  		// We'll then attempt to swap the old version for this new one.
   159  		err = backupFile.UpdateAndSwap(newPackedMulti2)
   160  		if err != nil {
   161  			t.Fatalf("unable to swap file: %v", err)
   162  		}
   163  
   164  		// Once again, the file written on disk should have been
   165  		// properly swapped out with the new instance.
   166  		assertBackupMatches(t, testCase.fileName, newPackedMulti2)
   167  
   168  		// Additionally, we shouldn't be able to find the temp backup
   169  		// file on disk, as it should be deleted each time.
   170  		assertFileDeleted(t, testCase.tempFileName)
   171  	}
   172  }
   173  
   174  func assertMultiEqual(t *testing.T, a, b *Multi) {
   175  
   176  	if len(a.StaticBackups) != len(b.StaticBackups) {
   177  		t.Fatalf("expected %v backups, got %v", len(a.StaticBackups),
   178  			len(b.StaticBackups))
   179  	}
   180  
   181  	for i := 0; i < len(a.StaticBackups); i++ {
   182  		assertSingleEqual(t, a.StaticBackups[i], b.StaticBackups[i])
   183  	}
   184  }
   185  
   186  // TestExtractMulti tests that given a valid packed multi file on disk, we're
   187  // able to read it multiple times repeatedly.
   188  func TestExtractMulti(t *testing.T) {
   189  	t.Parallel()
   190  
   191  	keyRing := &mockKeyRing{}
   192  
   193  	// First, as prep, we'll create a single chan backup, then pack that
   194  	// fully into a multi backup.
   195  	channel, err := genRandomOpenChannelShell()
   196  	if err != nil {
   197  		t.Fatalf("unable to gen chan: %v", err)
   198  	}
   199  
   200  	singleBackup := NewSingle(channel, nil)
   201  
   202  	var b bytes.Buffer
   203  	unpackedMulti := Multi{
   204  		StaticBackups: []Single{singleBackup},
   205  	}
   206  	err = unpackedMulti.PackToWriter(&b, keyRing)
   207  	if err != nil {
   208  		t.Fatalf("unable to pack to writer: %v", err)
   209  	}
   210  
   211  	packedMulti := PackedMulti(b.Bytes())
   212  
   213  	// Finally, we'll make a new temporary file, then write out the packed
   214  	// multi directly to to it.
   215  	tempFile, err := ioutil.TempFile("", "")
   216  	if err != nil {
   217  		t.Fatalf("unable to create temp file: %v", err)
   218  	}
   219  	defer os.Remove(tempFile.Name())
   220  
   221  	_, err = tempFile.Write(packedMulti)
   222  	if err != nil {
   223  		t.Fatalf("unable to write temp file: %v", err)
   224  	}
   225  	if err := tempFile.Sync(); err != nil {
   226  		t.Fatalf("unable to sync temp file: %v", err)
   227  	}
   228  
   229  	testCases := []struct {
   230  		fileName string
   231  		pass     bool
   232  	}{
   233  		// Main file not read, file name not present.
   234  		{
   235  			fileName: "",
   236  			pass:     false,
   237  		},
   238  
   239  		// Main file not read, file name is there, but file doesn't
   240  		// exist.
   241  		{
   242  			fileName: "kek",
   243  			pass:     false,
   244  		},
   245  
   246  		// Main file not read, should be able to read multiple times.
   247  		{
   248  			fileName: tempFile.Name(),
   249  			pass:     true,
   250  		},
   251  	}
   252  	for i, testCase := range testCases {
   253  		// First, we'll make our backup file with the specified name.
   254  		backupFile := NewMultiFile(testCase.fileName)
   255  
   256  		// With our file made, we'll now attempt to read out the
   257  		// multi-file.
   258  		freshUnpackedMulti, err := backupFile.ExtractMulti(keyRing)
   259  		switch {
   260  		// If this is a valid test case, and we failed, then we'll
   261  		// return an error.
   262  		case err != nil && testCase.pass:
   263  			t.Fatalf("#%v, unable to extract file: %v", i, err)
   264  
   265  			// If this is an invalid test case, and we passed it, then
   266  			// we'll return an error.
   267  		case err == nil && !testCase.pass:
   268  			t.Fatalf("#%v file extraction should have "+
   269  				"failed: %v", i, err)
   270  		}
   271  
   272  		if !testCase.pass {
   273  			continue
   274  		}
   275  
   276  		// We'll now ensure that the unpacked multi we read is
   277  		// identical to the one we wrote out above.
   278  		assertMultiEqual(t, &unpackedMulti, freshUnpackedMulti)
   279  
   280  		// We should also be able to read the file again, as we have an
   281  		// existing handle to it.
   282  		freshUnpackedMulti, err = backupFile.ExtractMulti(keyRing)
   283  		if err != nil {
   284  			t.Fatalf("unable to unpack multi: %v", err)
   285  		}
   286  
   287  		assertMultiEqual(t, &unpackedMulti, freshUnpackedMulti)
   288  	}
   289  }