gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/skynetportals/skynetportals_test.go (about) 1 package skynetportals 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "path/filepath" 8 "testing" 9 10 "gitlab.com/NebulousLabs/errors" 11 "gitlab.com/NebulousLabs/fastrand" 12 "gitlab.com/SkynetLabs/skyd/build" 13 "gitlab.com/SkynetLabs/skyd/skymodules" 14 "go.sia.tech/siad/modules" 15 "go.sia.tech/siad/persist" 16 ) 17 18 // testDir is a helper function for creating the testing directory 19 func testDir(name string) string { 20 return build.TempDir("skynetportals", name) 21 } 22 23 // checkNumPersistedPortals checks that the expected number of portals has been 24 // persisted on disk by checking the size of the persistence file. 25 func checkNumPersistedPortals(portalsPath string, numPortals int) error { 26 expectedSize := numPortals*int(persistSize) + int(persist.MetadataPageSize) 27 if fi, err := os.Stat(portalsPath); err != nil { 28 return errors.AddContext(err, "failed to get portal list filesize") 29 } else if fi.Size() != int64(expectedSize) { 30 return fmt.Errorf("expected %v portals to have a filesize of %v but was %v", numPortals, expectedSize, fi.Size()) 31 } 32 return nil 33 } 34 35 // TestPersist tests the persistence of the Skynet portals list. 36 func TestPersist(t *testing.T) { 37 if testing.Short() { 38 t.SkipNow() 39 } 40 t.Parallel() 41 42 // Create a new SkynetPortals 43 testdir := testDir(t.Name()) 44 pl, err := New(testdir) 45 if err != nil { 46 t.Fatal(err) 47 } 48 49 filename := filepath.Join(testdir, persistFile) 50 if filename != pl.staticAop.FilePath() { 51 t.Fatalf("Expected filepath %v, was %v", filename, pl.staticAop.FilePath()) 52 } 53 54 // There should be no portals in the list 55 if len(pl.portals) != 0 { 56 t.Fatal("Expected portals list to be empty but found:", len(pl.portals)) 57 } 58 59 // Update portals list 60 portal := skymodules.SkynetPortal{ 61 Address: "localhost:9980", 62 Public: true, 63 } 64 add := []skymodules.SkynetPortal{portal} 65 remove := []modules.NetAddress{portal.Address} 66 err = pl.UpdatePortals(add, remove) 67 if err != nil { 68 t.Fatal(err) 69 } 70 71 // Portals list should be empty because we added and then removed the same 72 // portal 73 if len(pl.portals) != 0 { 74 t.Fatal("Expected portals list to be empty but found:", len(pl.portals)) 75 } 76 77 // Verify that the correct number of portals were persisted to verify no 78 // portals are being truncated 79 if err := checkNumPersistedPortals(filename, 2); err != nil { 80 t.Errorf("error verifying correct number of portals: %v", err) 81 } 82 83 // Add the portal again 84 err = pl.UpdatePortals(add, []modules.NetAddress{}) 85 if err != nil { 86 t.Fatal(err) 87 } 88 89 // There should be 1 element in the portals list now 90 if len(pl.portals) != 1 { 91 t.Fatal("Expected 1 element in the portals list but found:", len(pl.portals)) 92 } 93 public, ok := pl.portals[portal.Address] 94 if public != portal.Public { 95 t.Fatalf("Expected publicness of portal listed in portals list to be %v but was %v", portal.Public, public) 96 } 97 if !ok { 98 t.Fatalf("Expected address %v to be listed in portals list", portal.Address) 99 } 100 101 // Load a new Skynet Portals List to verify the contents from disk get loaded 102 // properly 103 pl2, err := New(testdir) 104 if err != nil { 105 t.Fatal(err) 106 } 107 108 // Verify that the correct number of portals were persisted to verify no 109 // portals are being truncated 110 if err := checkNumPersistedPortals(filename, 3); err != nil { 111 t.Fatalf("error verifying correct number of portals: %v", err) 112 } 113 114 // There should be 1 element in the portals list 115 if len(pl2.portals) != 1 { 116 t.Fatal("Expected 1 element in the portals list but found:", len(pl2.portals)) 117 } 118 public, ok = pl2.portals[portal.Address] 119 if public != portal.Public { 120 t.Fatalf("Expected publicness of portal listed in portals list to be %v but was %v", portal.Public, public) 121 } 122 if !ok { 123 t.Fatalf("Expected address %v to be listed in portals list", portal.Address) 124 } 125 126 // Add the portal again 127 err = pl2.UpdatePortals(add, []modules.NetAddress{}) 128 if err != nil { 129 t.Fatal(err) 130 } 131 132 // There should still only be 1 element in the portal list 133 if len(pl2.portals) != 1 { 134 t.Fatal("Expected 1 element in the portal list but found:", len(pl2.portals)) 135 } 136 public, ok = pl2.portals[portal.Address] 137 if public != portal.Public { 138 t.Fatalf("Expected publicness of portal listed in portals list to be %v but was %v", portal.Public, public) 139 } 140 if !ok { 141 t.Fatalf("Expected address %v to be listed in portals list", portal.Address) 142 } 143 144 // Load another new Skynet Portals List to verify the contents from disk get 145 // loaded properly 146 pl3, err := New(testdir) 147 if err != nil { 148 t.Fatal(err) 149 } 150 151 // Verify that the correct number of portals were persisted to verify no 152 // portals are being truncated 153 if err := checkNumPersistedPortals(filename, 4); err != nil { 154 t.Fatalf("error verifying correct number of portals: %v", err) 155 } 156 157 // There should be 1 element in the portals list 158 if len(pl3.portals) != 1 { 159 t.Fatal("Expected 1 element in the portals list but found:", len(pl3.portals)) 160 } 161 public, ok = pl3.portals[portal.Address] 162 if !ok { 163 t.Fatalf("Expected address %v to be listed in portals list", portal.Address) 164 } 165 } 166 167 // TestPersistCorruption tests the persistence of the Skynet portal list when 168 // corruption occurs. 169 func TestPersistCorruption(t *testing.T) { 170 if testing.Short() { 171 t.SkipNow() 172 } 173 t.Parallel() 174 175 // Create a new SkynetPortalList 176 testdir := testDir(t.Name()) 177 pl, err := New(testdir) 178 if err != nil { 179 t.Fatal(err) 180 } 181 182 filename := filepath.Join(testdir, persistFile) 183 if filename != pl.staticAop.FilePath() { 184 t.Fatalf("Expected filepath %v, was %v", filename, pl.staticAop.FilePath()) 185 } 186 187 // There should be no portals in the list 188 if len(pl.portals) != 0 { 189 t.Fatal("Expected portals list to be empty but found:", len(pl.portals)) 190 } 191 192 // Append a bunch of random data to the end of the portals list file to test 193 // corruption 194 f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, skymodules.DefaultFilePerm) 195 if err != nil { 196 t.Fatal(err) 197 } 198 minNumBytes := int(2 * persist.MetadataPageSize) 199 _, err = f.Write(fastrand.Bytes(minNumBytes + fastrand.Intn(minNumBytes))) 200 if err != nil { 201 t.Fatal(err) 202 } 203 err = f.Close() 204 if err != nil { 205 t.Fatal(err) 206 } 207 208 // The filesize with corruption should be greater than the persist length. 209 fi, err := os.Stat(filename) 210 if err != nil { 211 t.Fatal(err) 212 } 213 filesize := fi.Size() 214 if uint64(filesize) <= pl.staticAop.PersistLength() { 215 t.Fatalf("Expected file size greater than %v, got %v", pl.staticAop.PersistLength(), filesize) 216 } 217 218 // Update portals list 219 portal := skymodules.SkynetPortal{ 220 Address: "localhost:9980", 221 Public: true, 222 } 223 add := []skymodules.SkynetPortal{portal} 224 remove := []modules.NetAddress{portal.Address} 225 err = pl.UpdatePortals(add, remove) 226 if err != nil { 227 t.Fatal(err) 228 } 229 230 // The filesize should be equal to the persist length now due to the 231 // truncate when updating. 232 fi, err = os.Stat(filename) 233 if err != nil { 234 t.Fatal(err) 235 } 236 filesize = fi.Size() 237 if uint64(filesize) != pl.staticAop.PersistLength() { 238 t.Fatalf("Expected file size %v, got %v", pl.staticAop.PersistLength(), filesize) 239 } 240 241 // Portals list should be empty because we added and then removed the same 242 // portal 243 if len(pl.portals) != 0 { 244 t.Fatal("Expected portals list to be empty but found:", len(pl.portals)) 245 } 246 247 // Add the portal again 248 err = pl.UpdatePortals(add, []modules.NetAddress{}) 249 if err != nil { 250 t.Fatal(err) 251 } 252 253 // There should be 1 element in the portals list now 254 if len(pl.portals) != 1 { 255 t.Fatal("Expected 1 element in the portals list but found:", len(pl.portals)) 256 } 257 public, ok := pl.portals[portal.Address] 258 if public != portal.Public { 259 t.Fatalf("Expected publicness of portal listed in portals list to be %v but was %v", portal.Public, public) 260 } 261 if !ok { 262 t.Fatalf("Expected address %v to be listed in portals list", portal.Address) 263 } 264 265 // Load a new Skynet Portals List to verify the contents from disk get loaded 266 // properly 267 pl2, err := New(testdir) 268 if err != nil { 269 t.Fatal(err) 270 } 271 272 // There should be 1 element in the portals list 273 if len(pl2.portals) != 1 { 274 t.Fatal("Expected 1 element in the portals list but found:", len(pl2.portals)) 275 } 276 public, ok = pl2.portals[portal.Address] 277 if public != portal.Public { 278 t.Fatalf("Expected publicness of portal listed in portals list to be %v but was %v", portal.Public, public) 279 } 280 if !ok { 281 t.Fatalf("Expected address %v to be listed in portals list", portal.Address) 282 } 283 284 // Add the portal again 285 err = pl2.UpdatePortals(add, []modules.NetAddress{}) 286 if err != nil { 287 t.Fatal(err) 288 } 289 290 // There should still only be 1 element in the portal list 291 if len(pl2.portals) != 1 { 292 t.Fatal("Expected 1 element in the portal list but found:", len(pl2.portals)) 293 } 294 public, ok = pl2.portals[portal.Address] 295 if public != portal.Public { 296 t.Fatalf("Expected publicness of portal listed in portals list to be %v but was %v", portal.Public, public) 297 } 298 if !ok { 299 t.Fatalf("Expected address %v to be listed in portals list", portal.Address) 300 } 301 302 // Load another new Skynet Portals List to verify the contents from disk get 303 // loaded properly 304 pl3, err := New(testdir) 305 if err != nil { 306 t.Fatal(err) 307 } 308 309 // There should be 1 element in the portals list 310 if len(pl3.portals) != 1 { 311 t.Fatal("Expected 1 element in the portals list but found:", len(pl3.portals)) 312 } 313 public, ok = pl3.portals[portal.Address] 314 if !ok { 315 t.Fatalf("Expected address %v to be listed in portals list", portal.Address) 316 } 317 318 // The final filesize should be equal to the persist length. 319 fi, err = os.Stat(filename) 320 if err != nil { 321 t.Fatal(err) 322 } 323 filesize = fi.Size() 324 if uint64(filesize) != pl3.staticAop.PersistLength() { 325 t.Fatalf("Expected file size %v, got %v", pl3.staticAop.PersistLength(), filesize) 326 } 327 328 // Verify that the correct number of portals were persisted to verify no 329 // portals are being truncated 330 if err := checkNumPersistedPortals(filename, 4); err != nil { 331 t.Fatalf("error verifying correct number of portals: %v", err) 332 } 333 } 334 335 // TestMarshalSia probes the marshalSia and unmarshalSia methods 336 func TestMarshalSia(t *testing.T) { 337 // Test MarshalSia 338 portal := skymodules.SkynetPortal{ 339 Address: modules.NetAddress("localhost:9980"), 340 Public: true, 341 } 342 var buf bytes.Buffer 343 address := portal.Address 344 listed := false 345 public := portal.Public 346 pe := persistEntry{address, public, listed} 347 err := pe.MarshalSia(&buf) 348 if err != nil { 349 t.Fatal(err) 350 } 351 pe.listed = true 352 err = pe.MarshalSia(&buf) 353 if err != nil { 354 t.Fatal(err) 355 } 356 357 // Test UnmarshalSia, portals should unmarshal in the order they were 358 // marshalled. 359 r := bytes.NewBuffer(buf.Bytes()) 360 err = pe.UnmarshalSia(r) 361 if err != nil { 362 t.Fatal(err) 363 } 364 if address != pe.address { 365 t.Fatalf("Addresses don't match, expected %v, got %v", address, pe.address) 366 } 367 if public != pe.public { 368 t.Fatalf("Publicness doesn't match, expected %v, got %v", public, pe.public) 369 } 370 if pe.listed { 371 t.Fatal("expected persisted portal to not be listed") 372 } 373 err = pe.UnmarshalSia(r) 374 if err != nil { 375 t.Fatal(err) 376 } 377 if public != pe.public { 378 t.Fatalf("Publicness doesn't match, expected %v, got %v", public, pe.public) 379 } 380 if address != pe.address { 381 t.Fatalf("Addresses don't match, expected %v, got %v", address, pe.address) 382 } 383 if !pe.listed { 384 t.Fatal("expected persisted portal to be listed") 385 } 386 387 // Test unmarshalPersistPortals 388 portals, err := unmarshalObjects(&buf) 389 if err != nil { 390 t.Fatal(err) 391 } 392 393 // Since the address is the same the portals list should only have a length 394 // of 1 since the non listed address was added first. 395 if len(portals) != 1 { 396 t.Fatalf("Incorrect number of listed addresses, expected %v, got %v", 1, len(portals)) 397 } 398 _, ok := portals[address] 399 if !ok { 400 t.Fatal("address not found in portals list") 401 } 402 }