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  }