Skip to content

Commit

Permalink
Merge pull request #50 from bmf-san/feature/improve-test-cases
Browse files Browse the repository at this point in the history
Improve test cases
  • Loading branch information
bmf-san authored Jun 25, 2024
2 parents 23effd8 + c442bdd commit dccb07b
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 54 deletions.
46 changes: 29 additions & 17 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"errors"
"flag"
"log/slog"
"os"
Expand All @@ -17,40 +18,51 @@ func init() {

}

func main() {
defer func() {
if x := recover(); x != nil {
slog.Error(string(debug.Stack()))
}
os.Exit(1)
}()

// parseFlags parses the command line flags.
func parseFlags() error {
flag.Parse()

if len(os.Args) == 1 {
flag.Usage()
os.Exit(1)
return errors.New("config file is not specified")
}
return nil
}

// setConfig returns the config file.
func setConfig(cfgFile string) (*os.File, error) {
if cfgFile == "" {
slog.Error("config file is not specified")
os.Exit(1)
return nil, errors.New("config file is not specified")
}

cfg, err := os.Open(filepath.Clean(cfgFile))
if err != nil {
slog.Error(err.Error())
os.Exit(1)
return nil, err
}

return cfg, nil
}

func main() {
defer func() {
err = cfg.Close()
if err != nil {
slog.Error(err.Error())
os.Exit(1)
if x := recover(); x != nil {
slog.Error(string(debug.Stack()))
}
os.Exit(1)
}()

err := parseFlags()
if err != nil {
slog.Error(err.Error())
os.Exit(1)
}

cfg, err := setConfig(cfgFile)
if err != nil {
slog.Error(err.Error())
os.Exit(1)
}

gondola, err := gondola.NewGondola(cfg)
if err != nil {
slog.Error(err.Error())
Expand Down
44 changes: 44 additions & 0 deletions cmd/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package main

import (
"flag"
"os"
"testing"
)

func TestParseFlags(t *testing.T) {
os.Args = []string{"config", "../testdata/config/config.yml"}
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)

err := parseFlags()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
}

func TestParseFlagsError(t *testing.T) {
os.Args = []string{"invalid"}
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)

err := parseFlags()
if err == nil {
t.Error("expected error")
}
}

func TestSetConfig(t *testing.T) {
_, err := setConfig("")
if err == nil {
t.Errorf("unexpected error: %v", err)
}

_, err = setConfig("invalid_file")
if err == nil {
t.Errorf("unexpected error: %v", err)
}

_, err = setConfig("../testdata/config/config.yml")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
}
50 changes: 14 additions & 36 deletions gondola.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package gondola

import (
"context"
"fmt"
"io"
"log/slog"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)

// Runner is an interface that defines the Run method.
type Runner interface {
Run() error
}

// NewGondola returns a new Gondola.
func NewGondola(r io.Reader) (*Gondola, error) {
cfg := &Config{}
Expand Down Expand Up @@ -39,37 +38,16 @@ func (g *Gondola) Run() error {

// TODO: do health check for upstreams.

ch := make(chan error, 1)
go func() {
if g.config.Proxy.IsEnableTLS() {
slog.Info(fmt.Sprintf("Running server on port %s with TLS...", g.config.Proxy.Port))
if err := g.server.ListenAndServeTLS(g.config.Proxy.TLSCertPath, g.config.Proxy.TLSKeyPath); err != http.ErrServerClosed {
ch <- err
}
} else {
slog.Info("Running server on port " + g.config.Proxy.Port + "...")
if err := g.server.ListenAndServe(); err != http.ErrServerClosed {
ch <- err
}
if g.config.Proxy.IsEnableTLS() {
slog.Info(fmt.Sprintf("Running server on port %s with TLS...", g.config.Proxy.Port))
if err := g.server.ListenAndServeTLS(g.config.Proxy.TLSCertPath, g.config.Proxy.TLSKeyPath); err != nil {
slog.Error("Error running server with TLS: " + err.Error())
}
} else {
slog.Info("Running server on port " + g.config.Proxy.Port + "...")
if err := g.server.ListenAndServe(); err != nil {
slog.Error("Error running server: " + err.Error())
}
}()
e := <-ch
if e != nil {
return e
}

q := make(chan os.Signal, 1)
signal.Notify(q, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
defer signal.Stop(q)
<-q

ctx, cancel := context.WithTimeout(context.Background(), time.Duration(g.config.Proxy.ShutdownTimeout)*time.Millisecond)
defer cancel()
if err := g.server.Shutdown(ctx); err != nil {
slog.Error(fmt.Sprintf("Shutdown failed: %v", err))
return err
}

slog.Info("Server stopped gracefully")
return nil
}
107 changes: 106 additions & 1 deletion gondola_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gondola

import (
"crypto/tls"
"errors"
"io"
"net/http"
Expand All @@ -13,7 +14,7 @@ import (
func TestNewGondola(t *testing.T) {
data := `
proxy:
port: 8080
port: 443
read_header_timeout: 2000
shutdown_timeout: 3000
tls_cert_path: /path/to/cert
Expand Down Expand Up @@ -177,3 +178,107 @@ log_level: -4
}
gondola.server.Close()
}

func TestRunWithTLS(t *testing.T) {
backend1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("backend1"))
}))
defer backend1.Close()

backend2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("backend2"))
}))
defer backend2.Close()

backend1URL, err := url.Parse(backend1.URL)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}

backend2URL, err := url.Parse(backend2.URL)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}

// with TLS config
data := `
proxy:
port: 5443
read_header_timeout: 2000
shutdown_timeout: 3000
tls_cert_path: testdata/certificates/cert.pem
tls_key_path: testdata/certificates/key.pem
static_files:
- path: /public/
dir: testdata/public
upstreams:
- host_name: backend1.local
target: ` + backend1URL.String() + `
- host_name: backend2.local
target: ` + backend2URL.String() + `
log_level: -4
`
gondola, err := NewGondola(strings.NewReader(data))
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}

go func() {
gondola.Run()
}()

for _, test := range []struct {
name string
reqPath string
path string
body string
code int
}{
{
name: "request to backend1",
reqPath: "https://backend1.local:5443",
body: "backend1",
code: http.StatusOK,
},
{
name: "request to backend2",
reqPath: "https://backend2.local:5443",
body: "backend2",
code: http.StatusOK,
},
{
name: "request to static file",
reqPath: "https://localhost:5443/public/index.html",
body: "test",
code: http.StatusOK,
},
} {
t.Run(test.name, func(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, test.reqPath, nil)
if err != nil {
t.Fatal(err)
}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
res, err := client.Do(req)
if err != nil {
t.Fatal(err)
}
defer res.Body.Close()
b, err := io.ReadAll(res.Body)
if err != nil {
t.Fatal(err)
}
if res.StatusCode != test.code {
t.Errorf("Expected status code %d, got %d", test.code, res.StatusCode)
}
if string(b) != test.body {
t.Errorf("Expected body %s, got %s", test.body, string(b))
}
})
}
gondola.server.Close()
}
18 changes: 18 additions & 0 deletions testdata/certificates/cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC+jCCAeKgAwIBAgIRAMIgKU8yn8Rc3SqzTl+7UBIwDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0yNDA2MjUxMTQ4MTVaFw0yNTA2MjUxMTQ4
MTVaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQC/qVxv4cynNrnWPvLbbiDrwivDNjca8LBlUSeruYqJH6l2bqyFz3U4
lKnN3d6U+4VYXBu/beYjnTOk+K28tbEy27sFMnFNiWp0XyImvUOZ9hWo3mZVuwwf
LyBJxYAdj1EL/KIl/j54N6/ZBuCbvpa076tXQdKrPy8PHFoGRJw0feZC5xh3cSCQ
2sWIBt0noK7RppuAnklwLpK2IxpmgqEP0VbjqqZ0LU8qvctw8gRwiYhTcfqllazp
tVN9DK+CozBLIPw9jyKkHBwYLORHa4M9x++TlFy98nNOM8eBS4VPbbPjtlqeJrd3
J7r5FwmF5zzcItpPJfBA7ui3Mp0uZAorAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAqm+Fk8hEqvvTfGtQWg+WYGaG
AjJM2/lmtij1n6Z4eEjNuRqutSzUfV0YOg4g+hXAfCyy7egSnuwPOZSHwM2A/St1
+Gh93djv+egbVDjcLwF8syeZIqqqBVdqwDrUaHQv+LPTgFZBO4qS6Rhe3EqT6S+L
ClcSAOnRocQdg+JzniV2tsHhhIReUU0HF6n8tO6OuqJxOnObtYPUjtVPpu+KMMEp
ugDnG1y/f75iNXF17wxEtT6qZc1hgLIn/jd6qPMxMYlVGr3/l6238r9KaxqPBXiZ
tN4XcCOk1yANAUyBCeq3zmduyih0gFRpXG8A6TxPKxKqiLF+YV0KI0XQGLHmNA==
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions testdata/certificates/key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC/qVxv4cynNrnW
PvLbbiDrwivDNjca8LBlUSeruYqJH6l2bqyFz3U4lKnN3d6U+4VYXBu/beYjnTOk
+K28tbEy27sFMnFNiWp0XyImvUOZ9hWo3mZVuwwfLyBJxYAdj1EL/KIl/j54N6/Z
BuCbvpa076tXQdKrPy8PHFoGRJw0feZC5xh3cSCQ2sWIBt0noK7RppuAnklwLpK2
IxpmgqEP0VbjqqZ0LU8qvctw8gRwiYhTcfqllazptVN9DK+CozBLIPw9jyKkHBwY
LORHa4M9x++TlFy98nNOM8eBS4VPbbPjtlqeJrd3J7r5FwmF5zzcItpPJfBA7ui3
Mp0uZAorAgMBAAECggEASbSkwrXwEQH/t3+fv3j7mg6i68CGsoqLG3t5KRYCS7ds
YQowuIjOiILLRksjlOGhJxjM5vIphdqniLLFhYPN8EQGCPqcDfhhgv0u0f0I6UV0
kOtuKVv0Tm2+GgCkwzlhLp3p8Xhzp6Gqs2hsqhhCNrTQjI7YqFh1DXeWu0BZXem9
rmesH9ahUjc62X8DhjJgMvrwooSXKF7Iiub2OBiIgTZCFfZV9MqawZbskFg0DfKn
c2kj/CdDWUaLV8sm+BG/DkDnhH+9BgPSgmjHakL0+burODKNdhiroekhx1pg1NlE
O8t2ADxF6pSE92GnYcimwgFs076xfbml5dwtk8u0uQKBgQDzJDp5niD+BIMJzaYz
LmJRM7ElyWue16dTfnUMDY8vERVxtrLExRmroMgFgMXGl7D/fKeFk61HFxQZ1Yn4
tYCDNnS2XiG2x+BDJHBXJudpFzf/eULaWrwc9PmxuWoujUdxTMN31DCGnohS99fB
P/1jlfNeY2DACR82NFIzn7U2BwKBgQDJzCvbv4klwnI7zpeqrrgJ4o0YX54TR5Vo
TDaaNK5T0dR5MFYX6eIQhvJO1A8lekWpsqzDSj/B6mEQZBxEiX/Zikwk/SAbbOYV
Cqi8WVLBJ1FJa1OyWELYDd7iRF6EsDsICoOU9HU9UjQq8vMDC47kjXBKbosGJCzo
E6vaVinhvQKBgBdLhkq2qsSLnB5pVJ4cuJC9GiDbrnL2iHKIcCBqgChrQ5m0fmY5
2B7IdcGJN+myCORBhi9XGcfw3x0mh2SLxzdLFV2PoT1I4ySrrhO7h3pbHiNc89nF
zbYchnvPnuu833Zse2HUs52CPN9uHX7HabiL2lhVvyRG+3WPB70odQ0rAoGBAJ0M
ZOW0fswANF8PPUH8kAZBZoWdhKyEfWaaSbRhY3qgRre6QoaB4wnxvApVRyRnsGeN
Qp8J3ezQt5HGF6nPSMzIsNHonWA6tCWUEhXjfmAMdVqqSIz3263BvcBibRze+lIb
vKPkTInYu/jg8vdfu2lsGgQTW+b/qGNFS+20OQwRAoGBAIe7MdYzlJf6dJ30fgl2
lcyhpYZfTV1LkLMn4SMUGUPhFSRzj7N/tYRER9GWVRh+QGxvwWJwJWXE+wZw+V1q
7sTXHvPrgs+EHqYKLUfwbNWLftMsQ4lYnWyJBflQh65xxwPIlibTtGvU/fRiBnS7
MsUunajRiMaeNiRMiYqoDO7A
-----END PRIVATE KEY-----
15 changes: 15 additions & 0 deletions testdata/config/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
proxy:
port: 443
read_header_timeout: 2000
shutdown_timeout: 3000
tls_cert_path: ../testdata/certificates/cert.pem
tls_key_path: ../testdata/certificates/key.pem
static_files:
- path: /public/
dir: ./public
upstreams:
- host_name: backend1.local
target: http://backend1:8081 # backend1 is the name of the container
- host_name: backend2.local
target: http://backend2:8082 # backend2 is the name of the container
log_level: 0 # Debug:-4 Info:0 Warn:4 Error:8

0 comments on commit dccb07b

Please sign in to comment.