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