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 }