github.com/System-Glitch/goyave/v2@v2.10.3-0.20200819142921-51011e75d504/goyave_test.go (about) 1 package goyave 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "net" 8 "net/http" 9 "os" 10 "runtime" 11 "syscall" 12 "testing" 13 "time" 14 15 "github.com/System-Glitch/goyave/v2/config" 16 "github.com/System-Glitch/goyave/v2/helper/filesystem" 17 18 _ "github.com/jinzhu/gorm/dialects/mysql" 19 ) 20 21 type GoyaveTestSuite struct { 22 TestSuite 23 } 24 25 func helloHandler(response *Response, request *Request) { 26 response.String(http.StatusOK, "Hi!") 27 } 28 29 func (suite *GoyaveTestSuite) SetupSuite() { 30 os.Setenv("GOYAVE_ENV", "test") 31 suite.SetTimeout(5 * time.Second) 32 } 33 34 func (suite *GoyaveTestSuite) loadConfig() { 35 if err := config.Load(); err != nil { 36 suite.FailNow(err.Error()) 37 } 38 config.Set("server.tls.key", "resources/server.key") 39 config.Set("server.tls.cert", "resources/server.crt") 40 } 41 42 func (suite *GoyaveTestSuite) TestGetHost() { 43 suite.loadConfig() 44 suite.Equal("127.0.0.1:1235", getHost("http")) 45 suite.Equal("127.0.0.1:1236", getHost("https")) 46 } 47 48 func (suite *GoyaveTestSuite) TestGetAddress() { 49 suite.loadConfig() 50 suite.Equal("http://127.0.0.1:1235", getAddress("http")) 51 suite.Equal("https://127.0.0.1:1236", getAddress("https")) 52 53 config.Set("server.domain", "test.system-glitch.me") 54 suite.Equal("http://test.system-glitch.me:1235", getAddress("http")) 55 suite.Equal("https://test.system-glitch.me:1236", getAddress("https")) 56 57 config.Set("server.port", 80.0) 58 config.Set("server.httpsPort", 443.0) 59 suite.Equal("http://test.system-glitch.me", getAddress("http")) 60 suite.Equal("https://test.system-glitch.me", getAddress("https")) 61 } 62 63 func (suite *GoyaveTestSuite) TestStartStopServer() { 64 config.Clear() 65 proc, err := os.FindProcess(os.Getpid()) 66 if err == nil { 67 c := make(chan bool, 1) 68 c2 := make(chan bool, 1) 69 ctx, cancel := context.WithTimeout(context.Background(), suite.Timeout()) 70 defer cancel() 71 72 RegisterStartupHook(func() { 73 suite.True(IsReady()) 74 if runtime.GOOS == "windows" { 75 fmt.Println("Testing on a windows machine. Cannot test proc signals") 76 Stop() 77 } else { 78 if err := proc.Signal(syscall.SIGTERM); err != nil { 79 suite.Fail(err.Error()) 80 } 81 time.Sleep(10 * time.Millisecond) 82 for IsReady() { 83 time.Sleep(10 * time.Millisecond) 84 if err := proc.Signal(syscall.SIGTERM); err != nil { 85 suite.Fail(err.Error()) 86 } 87 } 88 } 89 c <- true 90 }) 91 go func() { 92 if err := Start(func(router *Router) {}); err != nil { 93 suite.Fail(err.Error()) 94 } 95 c2 <- true 96 }() 97 98 select { 99 case <-ctx.Done(): 100 suite.Fail("Timeout exceeded in server start/stop test") 101 case <-c: 102 suite.False(IsReady()) 103 suite.Nil(server) 104 suite.Nil(hookChannel) 105 ClearStartupHooks() 106 } 107 <-c2 108 } else { 109 fmt.Println("WARNING: Couldn't get process PID, skipping SIGINT test") 110 } 111 } 112 113 func (suite *GoyaveTestSuite) TestTLSServer() { 114 suite.loadConfig() 115 config.Set("server.protocol", "https") 116 suite.RunServer(func(router *Router) { 117 router.Route("GET", "/hello", helloHandler) 118 }, func() { 119 netClient := suite.getHTTPClient() 120 resp, err := netClient.Get("http://127.0.0.1:1235/hello") 121 suite.Nil(err) 122 if err != nil { 123 fmt.Println(err) 124 } 125 126 suite.NotNil(resp) 127 if resp != nil { 128 suite.Equal(308, resp.StatusCode) 129 130 body, err := ioutil.ReadAll(resp.Body) 131 resp.Body.Close() 132 suite.Nil(err) 133 suite.Equal("<a href=\"https://127.0.0.1:1236/hello\">Permanent Redirect</a>.\n\n", string(body)) 134 } 135 136 resp, err = netClient.Get("http://127.0.0.1:1235/hello?param=1") 137 suite.Nil(err) 138 if err != nil { 139 fmt.Println(err) 140 } 141 142 suite.NotNil(resp) 143 if resp != nil { 144 suite.Equal(308, resp.StatusCode) 145 146 body, err := ioutil.ReadAll(resp.Body) 147 resp.Body.Close() 148 suite.Nil(err) 149 suite.Equal("<a href=\"https://127.0.0.1:1236/hello?param=1\">Permanent Redirect</a>.\n\n", string(body)) 150 } 151 152 resp, err = netClient.Get("https://127.0.0.1:1236/hello") 153 suite.Nil(err) 154 if err != nil { 155 fmt.Println(err) 156 } 157 158 suite.NotNil(resp) 159 if resp != nil { 160 suite.Equal(200, resp.StatusCode) 161 162 body, err := ioutil.ReadAll(resp.Body) 163 resp.Body.Close() 164 suite.Nil(err) 165 suite.Equal("Hi!", string(body)) 166 } 167 }) 168 169 config.Set("server.protocol", "http") 170 } 171 172 func (suite *GoyaveTestSuite) TestTLSRedirectServerError() { 173 suite.loadConfig() 174 c := make(chan bool) 175 c2 := make(chan bool) 176 ctx, cancel := context.WithTimeout(context.Background(), suite.Timeout()) 177 defer cancel() 178 179 go func() { 180 go func() { 181 // Run a server using the same port. 182 ln, err := net.Listen("tcp", getHost("http")) 183 if err != nil { 184 suite.Fail(err.Error()) 185 return 186 } 187 c2 <- true 188 <-c2 189 ln.Close() 190 c2 <- true 191 }() 192 <-c2 193 config.Set("server.protocol", "https") 194 suite.RunServer(func(router *Router) {}, func() {}) 195 config.Set("server.protocol", "http") 196 c2 <- true 197 <-c2 198 c <- true 199 }() 200 201 select { 202 case <-ctx.Done(): 203 suite.Fail("Timeout exceeded in redirect server error test") 204 case <-c: 205 suite.False(IsReady()) 206 suite.Nil(redirectServer) 207 suite.Nil(stopChannel) 208 } 209 } 210 211 func (suite *GoyaveTestSuite) TestStaticServing() { 212 suite.RunServer(func(router *Router) { 213 router.Static("/resources", "resources", true) 214 }, func() { 215 netClient := suite.getHTTPClient() 216 resp, err := netClient.Get("http://127.0.0.1:1235/resources/nothing") 217 suite.Nil(err) 218 if err != nil { 219 fmt.Println(err) 220 } 221 suite.NotNil(resp) 222 if resp != nil { 223 suite.Equal(404, resp.StatusCode) 224 } 225 226 err = ioutil.WriteFile("resources/template/test-static-serve.txt", []byte("test-content"), 0644) 227 if err != nil { 228 panic(err) 229 } 230 defer filesystem.Delete("resources/template/test-static-serve.txt") 231 resp, err = netClient.Get("http://127.0.0.1:1235/resources/template/test-static-serve.txt") 232 suite.Nil(err) 233 if err != nil { 234 fmt.Println(err) 235 } 236 suite.NotNil(resp) 237 if resp != nil { 238 suite.Equal(200, resp.StatusCode) 239 240 body, err := ioutil.ReadAll(resp.Body) 241 resp.Body.Close() 242 suite.Nil(err) 243 suite.Equal("test-content", string(body)) 244 } 245 }) 246 } 247 248 func (suite *GoyaveTestSuite) TestServerError() { 249 suite.loadConfig() 250 suite.testServerError("http") 251 suite.testServerError("https") 252 } 253 254 func (suite *GoyaveTestSuite) testServerError(protocol string) { 255 c := make(chan error) 256 c2 := make(chan bool) 257 ctx, cancel := context.WithTimeout(context.Background(), suite.Timeout()) 258 defer cancel() 259 260 var ln net.Listener 261 262 go func() { 263 go func() { 264 265 // Run a server using the same port as Goyave, so Goyave fails to bind. 266 if protocol != "https" { 267 var err error 268 ln, err = net.Listen("tcp", getHost(protocol)) 269 if err != nil { 270 suite.Fail(err.Error()) 271 } 272 c2 <- true 273 } else { 274 c2 <- true 275 } 276 c2 <- true 277 }() 278 <-c2 279 config.Set("server.protocol", protocol) 280 if protocol == "https" { 281 // Invalid certificates 282 config.Set("server.tls.key", "doesntexist") 283 config.Set("server.tls.cert", "doesntexist") 284 } 285 286 fmt.Println("test server error " + protocol) 287 err := Start(func(router *Router) {}) 288 config.Set("server.protocol", "http") 289 c <- err 290 }() 291 292 select { 293 case <-ctx.Done(): 294 suite.Fail("Timeout exceeded in server error test") 295 case err := <-c: 296 suite.False(IsReady()) 297 suite.Nil(server) 298 suite.NotNil(err) 299 if protocol == "https" { 300 suite.Equal(ExitHTTPError, err.(*Error).ExitCode) 301 } else { 302 suite.Equal(ExitNetworkError, err.(*Error).ExitCode) 303 } 304 } 305 306 if protocol != "https" { 307 ln.Close() 308 } 309 <-c2 310 } 311 312 func (suite *GoyaveTestSuite) TestServerAlreadyRunning() { 313 suite.loadConfig() 314 suite.RunServer(func(router *Router) {}, func() { 315 suite.Panics(func() { 316 if err := Start(func(router *Router) {}); err != nil { 317 suite.Fail(err.Error()) 318 } 319 }) 320 }) 321 } 322 323 func (suite *GoyaveTestSuite) TestMaintenanceMode() { 324 suite.loadConfig() 325 suite.RunServer(func(router *Router) { 326 router.Route("GET", "/hello", helloHandler) 327 }, func() { 328 EnableMaintenance() 329 suite.True(IsMaintenanceEnabled()) 330 331 netClient := suite.getHTTPClient() 332 resp, err := netClient.Get("http://127.0.0.1:1235/hello") 333 suite.Nil(err) 334 if err != nil { 335 fmt.Println(err) 336 } 337 338 suite.NotNil(resp) 339 if resp != nil { 340 suite.Equal(503, resp.StatusCode) 341 } 342 343 DisableMaintenance() 344 suite.False(IsMaintenanceEnabled()) 345 346 resp, err = netClient.Get("http://127.0.0.1:1235/hello") 347 suite.Nil(err) 348 if err != nil { 349 fmt.Println(err) 350 } 351 352 suite.NotNil(resp) 353 if resp != nil { 354 suite.Equal(200, resp.StatusCode) 355 356 body, err := ioutil.ReadAll(resp.Body) 357 resp.Body.Close() 358 suite.Nil(err) 359 suite.Equal("Hi!", string(body)) 360 } 361 }) 362 363 config.Set("server.maintenance", true) 364 suite.RunServer(func(router *Router) { 365 router.Route("GET", "/hello", helloHandler) 366 }, func() { 367 suite.True(IsMaintenanceEnabled()) 368 369 netClient := suite.getHTTPClient() 370 resp, err := netClient.Get("http://127.0.0.1:1235/hello") 371 suite.Nil(err) 372 if err != nil { 373 fmt.Println(err) 374 } 375 376 suite.NotNil(resp) 377 if resp != nil { 378 suite.Equal(503, resp.StatusCode) 379 } 380 381 DisableMaintenance() 382 383 suite.False(IsMaintenanceEnabled()) 384 385 resp, err = netClient.Get("http://127.0.0.1:1235/hello") 386 suite.Nil(err) 387 if err != nil { 388 fmt.Println(err) 389 } 390 391 suite.NotNil(resp) 392 if resp != nil { 393 suite.Equal(200, resp.StatusCode) 394 395 body, err := ioutil.ReadAll(resp.Body) 396 resp.Body.Close() 397 suite.Nil(err) 398 suite.Equal("Hi!", string(body)) 399 } 400 }) 401 config.Set("server.maintenance", false) 402 } 403 404 func (suite *GoyaveTestSuite) TestAutoMigrate() { 405 suite.loadConfig() 406 config.Set("database.connection", "mysql") 407 config.Set("database.autoMigrate", true) 408 suite.RunServer(func(router *Router) {}, func() {}) 409 config.Set("database.autoMigrate", false) 410 config.Set("database.Connection", "none") 411 } 412 413 func (suite *GoyaveTestSuite) TestError() { 414 err := &Error{ExitHTTPError, fmt.Errorf("test error")} 415 suite.Equal("test error", err.Error()) 416 } 417 418 func (suite *GoyaveTestSuite) TestConfigError() { 419 config.Clear() 420 if err := os.Chdir("config"); err != nil { 421 panic(err) 422 } 423 defer os.Chdir("..") 424 425 os.Setenv("GOYAVE_ENV", "test_invalid") 426 defer os.Setenv("GOYAVE_ENV", "test") 427 428 c := make(chan error, 1) 429 ctx, cancel := context.WithTimeout(context.Background(), suite.Timeout()) 430 defer cancel() 431 432 go func() { 433 c <- Start(func(r *Router) {}) 434 }() 435 436 select { 437 case <-ctx.Done(): 438 suite.Fail("Timeout exceeded in Goyave test suite TestConfigError") 439 case err := <-c: 440 suite.NotNil(err) 441 if err != nil { 442 e := err.(*Error) 443 suite.Equal(ExitInvalidConfig, e.ExitCode) 444 suite.Equal("Invalid config:\n\t- \"app.environment\" type must be string", e.Error()) 445 } 446 } 447 } 448 449 func TestGoyaveTestSuite(t *testing.T) { 450 RunTest(t, new(GoyaveTestSuite)) 451 }