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 }