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  }