gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/gateway/gateway_test.go (about) 1 package gateway 2 3 import ( 4 "io/ioutil" 5 "net" 6 "os" 7 "path/filepath" 8 "strconv" 9 "sync" 10 "testing" 11 "time" 12 13 "gitlab.com/SiaPrime/SiaPrime/build" 14 "gitlab.com/SiaPrime/SiaPrime/modules" 15 siasync "gitlab.com/SiaPrime/SiaPrime/sync" 16 ) 17 18 // newTestingGateway returns a gateway ready to use in a testing environment. 19 func newTestingGateway(t *testing.T) *Gateway { 20 if testing.Short() { 21 panic("newTestingGateway called during short test") 22 } 23 24 g, err := New("localhost:0", false, build.TempDir("gateway", t.Name())) 25 if err != nil { 26 panic(err) 27 } 28 return g 29 } 30 31 // newNamedTestingGateway returns a gateway ready to use in a testing 32 // environment. The gateway's persist folder will have the specified suffix. 33 func newNamedTestingGateway(t *testing.T, suffix string) *Gateway { 34 if testing.Short() { 35 panic("newTestingGateway called during short test") 36 } 37 38 g, err := New("localhost:0", false, build.TempDir("gateway", t.Name()+suffix)) 39 if err != nil { 40 panic(err) 41 } 42 return g 43 } 44 45 // NDF safe connection helpers 46 func connectToNode(g1 *Gateway, g2 *Gateway, manual bool) error { 47 return build.Retry(100, 10*time.Millisecond, func() error { 48 if manual { 49 return g1.ConnectManual(g2.Address()) 50 } 51 return g1.Connect(g2.Address()) 52 }) 53 } 54 func disconnectFromNode(g1 *Gateway, g2 *Gateway, manual bool) error { 55 return build.Retry(100, 10*time.Millisecond, func() error { 56 if manual { 57 return g1.DisconnectManual(g2.Address()) 58 } 59 return g1.Disconnect(g2.Address()) 60 }) 61 } 62 63 // TestExportedMethodsErrAfterClose tests that exported methods like Close and 64 // Connect error with siasync.ErrStopped after the gateway has been closed. 65 func TestExportedMethodsErrAfterClose(t *testing.T) { 66 if testing.Short() { 67 t.SkipNow() 68 } 69 t.Parallel() 70 g := newTestingGateway(t) 71 72 if err := g.Close(); err != nil { 73 t.Fatal(err) 74 } 75 if err := g.Close(); err != siasync.ErrStopped { 76 t.Fatalf("expected %q, got %q", siasync.ErrStopped, err) 77 } 78 if err := g.Connect("localhost:1234"); err != siasync.ErrStopped { 79 t.Fatalf("expected %q, got %q", siasync.ErrStopped, err) 80 } 81 } 82 83 // TestAddress tests that Gateway.Address returns the address of its listener. 84 // Also tests that the address is not unspecified and is a loopback address. 85 // The address must be a loopback address for testing. 86 func TestAddress(t *testing.T) { 87 if testing.Short() { 88 t.SkipNow() 89 } 90 t.Parallel() 91 g := newTestingGateway(t) 92 defer g.Close() 93 94 if g.Address() != g.myAddr { 95 t.Fatal("Address does not return g.myAddr") 96 } 97 if g.Address() != modules.NetAddress(g.listener.Addr().String()) { 98 t.Fatalf("wrong address: expected %v, got %v", g.listener.Addr(), g.Address()) 99 } 100 host := modules.NetAddress(g.listener.Addr().String()).Host() 101 ip := net.ParseIP(host) 102 if ip == nil { 103 t.Fatal("address is not an IP address") 104 } 105 if ip.IsUnspecified() { 106 t.Fatal("expected a non-unspecified address") 107 } 108 if !ip.IsLoopback() { 109 t.Fatal("expected a loopback address") 110 } 111 } 112 113 // TestPeers checks that two gateways are able to connect to each other. 114 func TestPeers(t *testing.T) { 115 if testing.Short() { 116 t.SkipNow() 117 } 118 t.Parallel() 119 g1 := newNamedTestingGateway(t, "1") 120 defer g1.Close() 121 g2 := newNamedTestingGateway(t, "2") 122 defer g2.Close() 123 124 err := g1.Connect(g2.Address()) 125 if err != nil { 126 t.Fatal("failed to connect:", err) 127 } 128 peers := g1.Peers() 129 if len(peers) != 1 || peers[0].NetAddress != g2.Address() { 130 t.Fatal("g1 has bad peer list:", peers) 131 } 132 err = g1.Disconnect(g2.Address()) 133 if err != nil { 134 t.Fatal("failed to disconnect:", err) 135 } 136 peers = g1.Peers() 137 if len(peers) != 0 { 138 t.Fatal("g1 has peers after disconnect:", peers) 139 } 140 } 141 142 // TestNew checks that a call to New is effective. 143 func TestNew(t *testing.T) { 144 if testing.Short() { 145 t.SkipNow() 146 } 147 t.Parallel() 148 149 if _, err := New("", false, ""); err == nil { 150 t.Fatal("expecting persistDir error, got nil") 151 } 152 if _, err := New("localhost:0", false, ""); err == nil { 153 t.Fatal("expecting persistDir error, got nil") 154 } 155 if g, err := New("foo", false, build.TempDir("gateway", t.Name()+"1")); err == nil { 156 t.Fatal("expecting listener error, got nil", g.myAddr) 157 } 158 // create corrupted nodes.json 159 dir := build.TempDir("gateway", t.Name()+"2") 160 os.MkdirAll(dir, 0700) 161 err := ioutil.WriteFile(filepath.Join(dir, "nodes.json"), []byte{1, 2, 3}, 0660) 162 if err != nil { 163 t.Fatal("couldn't create corrupted file:", err) 164 } 165 if _, err := New("localhost:0", false, dir); err == nil { 166 t.Fatal("expected load error, got nil") 167 } 168 } 169 170 // TestClose creates and closes a gateway. 171 func TestClose(t *testing.T) { 172 if testing.Short() { 173 t.SkipNow() 174 } 175 t.Parallel() 176 177 g := newTestingGateway(t) 178 err := g.Close() 179 if err != nil { 180 t.Fatal(err) 181 } 182 } 183 184 // TestParallelClose spins up 3 gateways, connects them all, and then closes 185 // them in parallel. The goal of this test is to make it more vulnerable to any 186 // potential nondeterministic failures. 187 func TestParallelClose(t *testing.T) { 188 if testing.Short() { 189 t.SkipNow() 190 } 191 t.Parallel() 192 193 // Spin up three gateways in parallel. 194 var gs [3]*Gateway 195 var wg sync.WaitGroup 196 wg.Add(3) 197 for i := range gs { 198 go func(i int) { 199 gs[i] = newNamedTestingGateway(t, strconv.Itoa(i)) 200 wg.Done() 201 }(i) 202 } 203 wg.Wait() 204 205 // Connect g1 to g2, g2 to g3. They may connect to eachother further. 206 wg.Add(2) 207 for i := range gs[:2] { 208 go func(i int) { 209 err := gs[i].Connect(gs[i+1].myAddr) 210 if err != nil { 211 panic(err) 212 } 213 wg.Done() 214 }(i) 215 } 216 wg.Wait() 217 218 // Close all three gateways in parallel. 219 wg.Add(3) 220 for i := range gs { 221 go func(i int) { 222 err := gs[i].Close() 223 if err != nil { 224 panic(err) 225 } 226 wg.Done() 227 }(i) 228 } 229 wg.Wait() 230 } 231 232 // TestManualConnectDisconnect checks if a user initiated connect and 233 // disconnect works as expected. 234 func TestManualConnectDisconnect(t *testing.T) { 235 if testing.Short() { 236 t.SkipNow() 237 } 238 t.Parallel() 239 g1 := newNamedTestingGateway(t, "1") 240 defer g1.Close() 241 g2 := newNamedTestingGateway(t, "2") 242 defer g2.Close() 243 244 // g1 should be able to connect to g2 245 if err := connectToNode(g1, g2, false); err != nil { 246 t.Fatal("failed to connect:", err) 247 } 248 // g2 manually disconnects from g1 and therefore blacklists it 249 if err := disconnectFromNode(g2, g1, true); err != nil { 250 t.Fatal("failed to disconnect:", err) 251 } 252 // Neither g1 nor g2 can connect after g1 being blacklisted 253 if err := connectToNode(g1, g2, false); err == nil { 254 t.Fatal("shouldn't be able to connect") 255 } 256 if err := connectToNode(g1, g2, true); err == nil { 257 t.Fatal("shouldn't be able to connect") 258 } 259 if err := connectToNode(g2, g1, false); err == nil { 260 t.Fatal("shouldn't be able to connect") 261 } 262 263 // g2 manually connects and therefore removes g1 from the blacklist again 264 if err := connectToNode(g2, g1, true); err != nil { 265 t.Fatal("failed to connect:", err) 266 } 267 268 // g2 disconnects and lets g1 connect which should also be possible now 269 if err := disconnectFromNode(g2, g1, false); err != nil { 270 t.Fatal("failed to disconnect:", err) 271 } 272 if err := connectToNode(g1, g2, false); err != nil { 273 t.Fatal("failed to connect:", err) 274 } 275 276 // same thing again but the other way round 277 if err := disconnectFromNode(g2, g1, false); err != nil { 278 t.Fatal("failed to disconnect:", err) 279 } 280 if err := connectToNode(g2, g1, false); err != nil { 281 t.Fatal("failed to connect:", err) 282 } 283 } 284 285 // TestManualConnectDisconnectPersist checks if the blacklist is persistet on 286 // disk 287 func TestManualConnectDisconnectPersist(t *testing.T) { 288 if testing.Short() { 289 t.SkipNow() 290 } 291 t.Parallel() 292 g1 := newNamedTestingGateway(t, "1") 293 defer g1.Close() 294 g2 := newNamedTestingGateway(t, "2") 295 296 // g1 should be able to connect to g2 297 if err := connectToNode(g1, g2, false); err != nil { 298 t.Fatal("failed to connect:", err) 299 } 300 301 // g2 manually disconnects from g1 and therefore blacklists it 302 if err := disconnectFromNode(g2, g1, true); err != nil { 303 t.Fatal("failed to disconnect:", err) 304 } 305 306 // Neither g1 nor g2 can connect after g1 being blacklisted 307 if err := connectToNode(g1, g2, false); err == nil { 308 t.Fatal("shouldn't be able to connect") 309 } 310 if err := connectToNode(g1, g2, true); err == nil { 311 t.Fatal("shouldn't be able to connect") 312 } 313 if err := connectToNode(g2, g1, false); err == nil { 314 t.Fatal("shouldn't be able to connect") 315 } 316 317 // Restart g2 without deleting the tmp dir 318 g2.Close() 319 g2, err := New("localhost:0", false, g2.persistDir) 320 if err != nil { 321 t.Fatal(err) 322 } 323 defer g2.Close() 324 325 // Neither g1 nor g2 can connect after g1 being blacklisted 326 if err := connectToNode(g1, g2, false); err == nil { 327 t.Fatal("shouldn't be able to connect") 328 } 329 if err := connectToNode(g1, g2, true); err == nil { 330 t.Fatal("shouldn't be able to connect") 331 } 332 if err := connectToNode(g2, g1, false); err == nil { 333 t.Fatal("shouldn't be able to connect") 334 } 335 }