github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/api/server_helpers_test.go (about) 1 package api 2 3 import ( 4 "encoding/json" 5 "errors" 6 "io/ioutil" 7 "net/http" 8 "net/url" 9 "path/filepath" 10 "time" 11 12 "github.com/NebulousLabs/Sia/build" 13 "github.com/NebulousLabs/Sia/crypto" 14 "github.com/NebulousLabs/Sia/modules" 15 "github.com/NebulousLabs/Sia/modules/consensus" 16 "github.com/NebulousLabs/Sia/modules/explorer" 17 "github.com/NebulousLabs/Sia/modules/gateway" 18 "github.com/NebulousLabs/Sia/modules/host" 19 "github.com/NebulousLabs/Sia/modules/miner" 20 "github.com/NebulousLabs/Sia/modules/renter" 21 "github.com/NebulousLabs/Sia/modules/transactionpool" 22 "github.com/NebulousLabs/Sia/modules/wallet" 23 "github.com/NebulousLabs/Sia/persist" 24 "github.com/NebulousLabs/Sia/types" 25 ) 26 27 // serverTester contains a server and a set of channels for keeping all of the 28 // modules synchronized during testing. 29 type serverTester struct { 30 cs modules.ConsensusSet 31 gateway modules.Gateway 32 host modules.Host 33 miner modules.TestMiner 34 renter modules.Renter 35 tpool modules.TransactionPool 36 explorer modules.Explorer 37 wallet modules.Wallet 38 walletKey crypto.TwofishKey 39 40 server *Server 41 42 dir string 43 } 44 45 // assembleServerTester creates a bunch of modules and assembles them into a 46 // server tester, without creating any directories or mining any blocks. 47 func assembleServerTester(key crypto.TwofishKey, testdir string) (*serverTester, error) { 48 // Create the modules. 49 g, err := gateway.New("localhost:0", filepath.Join(testdir, modules.GatewayDir)) 50 if err != nil { 51 return nil, err 52 } 53 cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir)) 54 if err != nil { 55 return nil, err 56 } 57 tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir)) 58 if err != nil { 59 return nil, err 60 } 61 w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) 62 if err != nil { 63 return nil, err 64 } 65 if !w.Encrypted() { 66 _, err = w.Encrypt(key) 67 if err != nil { 68 return nil, err 69 } 70 } 71 err = w.Unlock(key) 72 if err != nil { 73 return nil, err 74 } 75 m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) 76 if err != nil { 77 return nil, err 78 } 79 h, err := host.New(cs, tp, w, "localhost:0", filepath.Join(testdir, modules.HostDir)) 80 if err != nil { 81 return nil, err 82 } 83 r, err := renter.New(cs, w, tp, filepath.Join(testdir, modules.RenterDir)) 84 if err != nil { 85 return nil, err 86 } 87 e, err := explorer.New(cs, filepath.Join(testdir, modules.ExplorerDir)) 88 if err != nil { 89 return nil, err 90 } 91 srv, err := NewServer("localhost:0", "Sia-Agent", cs, e, g, h, m, r, tp, w) 92 if err != nil { 93 return nil, err 94 } 95 96 // Assemble the serverTester. 97 st := &serverTester{ 98 cs: cs, 99 gateway: g, 100 host: h, 101 miner: m, 102 renter: r, 103 tpool: tp, 104 explorer: e, 105 wallet: w, 106 walletKey: key, 107 108 server: srv, 109 110 dir: testdir, 111 } 112 113 // TODO: A more reasonable way of listening for server errors. 114 go func() { 115 listenErr := srv.Serve() 116 if listenErr != nil { 117 panic(listenErr) 118 } 119 }() 120 return st, nil 121 } 122 123 // assembleExplorerServerTester creates all the explorer dependencies and 124 // explorer module without creating any directories. The user agent requirement 125 // is disabled. 126 func assembleExplorerServerTester(testdir string) (*serverTester, error) { 127 // Create the modules. 128 g, err := gateway.New("localhost:0", filepath.Join(testdir, modules.GatewayDir)) 129 if err != nil { 130 return nil, err 131 } 132 cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir)) 133 if err != nil { 134 return nil, err 135 } 136 e, err := explorer.New(cs, filepath.Join(testdir, modules.ExplorerDir)) 137 if err != nil { 138 return nil, err 139 } 140 srv, err := NewServer("localhost:0", "", cs, e, g, nil, nil, nil, nil, nil) 141 if err != nil { 142 return nil, err 143 } 144 145 // Assemble the serverTester. 146 st := &serverTester{ 147 cs: cs, 148 explorer: e, 149 gateway: g, 150 151 server: srv, 152 153 dir: testdir, 154 } 155 156 // TODO: A more reasonable way of listening for server errors. 157 go func() { 158 listenErr := srv.Serve() 159 if listenErr != nil { 160 panic(listenErr) 161 } 162 }() 163 return st, nil 164 } 165 166 // createServerTester creates a server tester object that is ready for testing, 167 // including money in the wallet and all modules initialized. 168 func createServerTester(name string) (*serverTester, error) { 169 // Create the testing directory. 170 testdir := build.TempDir("api", name) 171 172 key, err := crypto.GenerateTwofishKey() 173 if err != nil { 174 return nil, err 175 } 176 st, err := assembleServerTester(key, testdir) 177 if err != nil { 178 return nil, err 179 } 180 181 // Mine blocks until the wallet has confirmed money. 182 for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { 183 _, err := st.miner.AddBlock() 184 if err != nil { 185 return nil, err 186 } 187 } 188 189 return st, nil 190 } 191 192 // createExplorerServerTester creates a server tester object containing only 193 // the explorer and some presets that match standard explorer setups. 194 func createExplorerServerTester(name string) (*serverTester, error) { 195 testdir := build.TempDir("api", name) 196 st, err := assembleExplorerServerTester(testdir) 197 if err != nil { 198 return nil, err 199 } 200 return st, nil 201 } 202 203 // reloadedServerTester creates a server tester where all of the persistent 204 // data has been copied to a new folder and all of the modules re-initialized 205 // on the new folder. This gives an opportunity to see how modules will behave 206 // when they are relying on their persistent structures. 207 func (st *serverTester) reloadedServerTester() (*serverTester, error) { 208 // Copy the testing directory. 209 copiedDir := st.dir + " - " + persist.RandomSuffix() 210 err := build.CopyDir(st.dir, copiedDir) 211 if err != nil { 212 return nil, err 213 } 214 copyST, err := assembleServerTester(st.walletKey, copiedDir) 215 if err != nil { 216 return nil, err 217 } 218 return copyST, nil 219 } 220 221 // netAddress returns the NetAddress of the caller. 222 func (st *serverTester) netAddress() modules.NetAddress { 223 return st.server.gateway.Address() 224 } 225 226 // coinAddress returns a coin address that the caller is able to spend from. 227 func (st *serverTester) coinAddress() string { 228 var addr struct { 229 Address string 230 } 231 st.getAPI("/wallet/address", &addr) 232 return addr.Address 233 } 234 235 // acceptContracts instructs the host to begin accepting contracts. 236 func (st *serverTester) acceptContracts() error { 237 settingsValues := url.Values{} 238 settingsValues.Set("acceptingcontracts", "true") 239 return st.stdPostAPI("/host", settingsValues) 240 } 241 242 // setHostStorage adds a 1 GB folder to the host. 243 func (st *serverTester) setHostStorage() error { 244 values := url.Values{} 245 values.Set("path", st.dir) 246 values.Set("size", "1048576") 247 return st.stdPostAPI("/storage/folders/add", values) 248 } 249 250 // announceHost announces the host, mines a block, and waits for the 251 // announcement to register. 252 func (st *serverTester) announceHost() error { 253 announceValues := url.Values{} 254 announceValues.Set("address", string(st.host.ExternalSettings().NetAddress)) 255 err := st.stdPostAPI("/host/announce", announceValues) 256 if err != nil { 257 return err 258 } 259 // mine block 260 _, err = st.miner.AddBlock() 261 if err != nil { 262 return err 263 } 264 // wait for announcement 265 var hosts ActiveHosts 266 err = st.getAPI("/renter/hosts/active", &hosts) 267 if err != nil { 268 return err 269 } 270 for i := 0; i < 20 && len(hosts.Hosts) == 0; i++ { 271 time.Sleep(100 * time.Millisecond) 272 err = st.getAPI("/renter/hosts/active", &hosts) 273 if err != nil { 274 return err 275 } 276 } 277 if len(hosts.Hosts) == 0 { 278 return errors.New("host announcement not seen") 279 } 280 return nil 281 } 282 283 // getAPI makes an API call and decodes the response. 284 func (st *serverTester) getAPI(call string, obj interface{}) error { 285 resp, err := HttpGET("http://" + st.server.listener.Addr().String() + call) 286 if err != nil { 287 return err 288 } 289 defer resp.Body.Close() 290 291 // Check for a call error. 292 if resp.StatusCode != http.StatusOK { 293 respErr, err := ioutil.ReadAll(resp.Body) 294 if err != nil { 295 return err 296 } 297 return errors.New(string(respErr)) 298 } 299 300 // Decode the response into 'obj'. 301 err = json.NewDecoder(resp.Body).Decode(obj) 302 if err != nil { 303 return err 304 } 305 return nil 306 } 307 308 // postAPI makes an API call and decodes the response. 309 func (st *serverTester) postAPI(call string, values url.Values, obj interface{}) error { 310 resp, err := HttpPOST("http://"+st.server.listener.Addr().String()+call, values.Encode()) 311 if err != nil { 312 return err 313 } 314 defer resp.Body.Close() 315 316 // Check for a call error. 317 if resp.StatusCode != http.StatusOK { 318 respErr, err := ioutil.ReadAll(resp.Body) 319 if err != nil { 320 return err 321 } 322 return errors.New(string(respErr)) 323 } 324 325 // Decode the response into 'obj'. 326 err = json.NewDecoder(resp.Body).Decode(obj) 327 if err != nil { 328 return err 329 } 330 return nil 331 } 332 333 // stdGetAPI makes an API call and discards the response. 334 func (st *serverTester) stdGetAPI(call string) error { 335 resp, err := HttpGET("http://" + st.server.listener.Addr().String() + call) 336 if err != nil { 337 return err 338 } 339 defer resp.Body.Close() 340 341 // Check for a call error. 342 if resp.StatusCode != http.StatusOK { 343 respErr, err := ioutil.ReadAll(resp.Body) 344 if err != nil { 345 return err 346 } 347 return errors.New(string(respErr)) 348 } 349 return nil 350 } 351 352 // stdGetAPIUA makes an API call with a custom user agent. 353 func (st *serverTester) stdGetAPIUA(call string, userAgent string) error { 354 req, err := http.NewRequest("GET", "http://"+st.server.listener.Addr().String()+call, nil) 355 if err != nil { 356 return err 357 } 358 req.Header.Add("User-Agent", userAgent) 359 resp, err := new(http.Client).Do(req) 360 if err != nil { 361 return err 362 } 363 defer resp.Body.Close() 364 365 // Check for a call error. 366 if resp.StatusCode != http.StatusOK { 367 respErr, err := ioutil.ReadAll(resp.Body) 368 if err != nil { 369 return err 370 } 371 return errors.New(string(respErr)) 372 } 373 return nil 374 } 375 376 // stdPostAPI makes an API call and discards the response. 377 func (st *serverTester) stdPostAPI(call string, values url.Values) error { 378 resp, err := HttpPOST("http://"+st.server.listener.Addr().String()+call, values.Encode()) 379 if err != nil { 380 return err 381 } 382 defer resp.Body.Close() 383 384 // Check for a call error. 385 if resp.StatusCode != http.StatusOK { 386 respErr, err := ioutil.ReadAll(resp.Body) 387 if err != nil { 388 return err 389 } 390 return errors.New(string(respErr)) 391 } 392 return nil 393 }