Skip to content

Commit

Permalink
test: add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lewislbr committed Oct 2, 2021
1 parent e49d72b commit 124db91
Show file tree
Hide file tree
Showing 31 changed files with 5,957 additions and 5,560 deletions.
26 changes: 10 additions & 16 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
name: Publish Docker image
name: Test code

on:
push:
tags:
- "*"
branches: [main]
workflow_dispatch:

env:
IMAGE_NAME: lewislbr/gss

jobs:
build-push:
name: Build and push the Docker image to Docker Hub
test:
name: Run unit tests
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@master
with:
fetch-depth: 0
- name: Get git tag
run: echo "GIT_TAG=`echo $(git describe --tags --abbrev=0)`" >> $GITHUB_ENV
- name: Build image
run: docker build -t "$IMAGE_NAME":"$GIT_TAG" -t "$IMAGE_NAME":latest .
- name: Log in
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
- name: Push image
run: docker push "$IMAGE_NAME":"$GIT_TAG" && docker push "$IMAGE_NAME":latest
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: Test
run: go test -v ./...
28 changes: 28 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Publish Docker image

on:
push:
tags:
- "*"
workflow_dispatch:

env:
IMAGE_NAME: lewislbr/gss

jobs:
build-release:
name: Build and push the Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@master
with:
fetch-depth: 0
- name: Get git tag
run: echo "GIT_TAG=`echo $(git describe --tags --abbrev=0)`" >> $GITHUB_ENV
- name: Build image
run: docker build -t "$IMAGE_NAME":"$GIT_TAG" -t "$IMAGE_NAME":latest .
- name: Log in
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
- name: Push image
run: docker push "$IMAGE_NAME":"$GIT_TAG" && docker push "$IMAGE_NAME":latest
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ run-default: build

run-yaml: build
@docker run --rm -p 8080:8081 -v $$PWD/test/gss.yaml:/gss.yaml -v $$PWD/test/web/dist:/public lewislbr/gss:test

run-yaml-cli: build
@docker run --rm -p 8080:8082 -v $$PWD/test/gss.yaml:/gss.yaml -v $$PWD/test/web/dist:/public lewislbr/gss:test -d public -p 8082
9 changes: 8 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
module gss

go 1.16
go 1.17

require (
github.com/rs/zerolog v1.25.0
github.com/stretchr/testify v1.7.0
gopkg.in/yaml.v2 v2.4.0
)

require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.25.0 h1:Rj7XygbUHKUlDPcVdoLyR91fJBsduXj5fRxyqIQj/II=
github.com/rs/zerolog v1.25.0/go.mod h1:7KHcEGe0QZPOm2IE4Kpb5rTh6n1h2hIgS5OOnu1rUaI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
Expand Down Expand Up @@ -31,3 +38,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
148 changes: 70 additions & 78 deletions gss.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,15 @@ import (
"gopkg.in/yaml.v2"
)

var (
dir = "dist"
headers = map[string]string{}
port = "80"
)

type configYAML struct {
Dir string `yaml:"directory,omitempty"`
Headers map[string]string `yaml:"headers,omitempty"`
Port string `yaml:"port,omitempty"`
}

func main() {
setUpLogger()

err := setUpYAML()
config, err := NewConfig().GetYAML().GetCLI().Validate()
if err != nil {
log.Fatal().Msgf("Error retrieving YAML config: %v", err)
log.Fatal().Msgf("Error validating config: %v", err)
}

setUpCLI()

// Check if the directory to serve exists
if _, err := os.Stat(dir); os.IsNotExist(err) {
log.Fatal().Msgf("Directory %q not found", dir)
}

err = startServer()
err = NewApp(config).Init().ListenAndServe()
if err != nil {
log.Fatal().Msgf("Error starting server: %v", err)
}
Expand All @@ -56,83 +37,98 @@ func setUpLogger() {
log.Logger = log.With().Str("app", "GSS").Logger()
}

// Enable configuration via YAML file.
func setUpYAML() error {
type Config struct {
Dir string `yaml:"directory,omitempty"`
Headers map[string]string `yaml:"headers,omitempty"`
Port string `yaml:"port,omitempty"`
}

func NewConfig() *Config {
return &Config{}
}

func (c *Config) GetYAML() *Config {
configFile := "gss.yaml"

// Check if there is a config file
if _, err := os.Stat(configFile); os.IsNotExist(err) {
return nil
return c
}

// Read the file
content, err := os.ReadFile(configFile)
if err != nil {
return fmt.Errorf("reading YAML file: %w", err)
log.Fatal().Msgf("Error reading YAML config: %v", err)
}

config := configYAML{}

// Serialize the YAML content
err = yaml.Unmarshal([]byte(content), &config)
err = yaml.Unmarshal([]byte(content), &c)
if err != nil {
return fmt.Errorf("unmarshaling YAML file: %w", err)
}

// Assign non-empty values
if config.Dir != "" {
dir = config.Dir
}
if len(config.Headers) != 0 {
headers = config.Headers
}
if config.Port != "" {
port = config.Port
log.Fatal().Msgf("Error unmarshalling YAML data: %v", err)
}

return nil
return c
}

// Enable configuration via CLI flags.
func setUpCLI() {
d := flag.String("d", dir, "Path to the directory to serve.")
p := flag.String("p", port, "Port where to run the server.")
func (c *Config) GetCLI() *Config {
dir := flag.String("d", c.Dir, "Path to the directory to serve.")
port := flag.String("p", c.Port, "Port where to run the server.")

flag.Parse()

// Assign non-empty values
if *d != "" {
dir = *d
if *dir != "" {
c.Dir = *dir
}
if *p != "" {
port = *p
if *port != "" {
c.Port = *port
}

return c
}

// Initialize the server.
func startServer() error {
s := setUpServer()
func (c *Config) Validate() (*Config, error) {
if c.Dir == "" {
c.Dir = "dist"
}
if c.Port == "" {
c.Port = "80"
}
if _, err := os.Stat(c.Dir); os.IsNotExist(err) {
return nil, fmt.Errorf("directory %q not found", c.Dir)
}

log.Info().Msgf("Serving directory %q on port %v", dir, port)
return c, nil
}

return s.ListenAndServe()
type App struct {
Config Config
Server *http.Server
}

// Configure a basic HTTP server.
func setUpServer() *http.Server {
return &http.Server{
Addr: ":" + port,
Handler: addHeaders(serveSPA(dir)),
IdleTimeout: 120 * time.Second,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
func NewApp(config *Config) *App {
return &App{
Config: *config,
Server: &http.Server{
Addr: ":" + config.Port,
IdleTimeout: 120 * time.Second,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
},
}
}

// Serve static files from a directory.
func serveSPA(dir string) http.HandlerFunc {
func (a *App) Init() *App {
a.Server.Handler = a.AddHeaders((a.ServeSPA()))

return a
}

func (a *App) ListenAndServe() error {
log.Info().Msgf("Serving directory %q on port %v", a.Config.Dir, a.Config.Port)

return a.Server.ListenAndServe()
}

func (a *App) ServeSPA() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
reqFile := filepath.Join(dir, filepath.Clean(r.URL.Path))
reqFile := filepath.Join(a.Config.Dir, filepath.Clean(r.URL.Path))

// Send the index if the root path is requested.
if filepath.Clean(r.URL.Path) == "/" {
Expand All @@ -148,10 +144,9 @@ func serveSPA(dir string) http.HandlerFunc {
return
}

reqFile = filepath.Join(dir, "index.html")
reqFile = filepath.Join(a.Config.Dir, "index.html")
}

// Serve pre-compressed file with appropriate headers and extension.
serveCompressedFile := func(encoding, extension string) {
serve := func(mimeType string) {
w.Header().Add("Content-Encoding", encoding)
Expand All @@ -175,15 +170,14 @@ func serveSPA(dir string) http.HandlerFunc {
}

acceptedEncodings := r.Header.Get("Accept-Encoding")
files, err := filepath.Glob(dir + "/*")
files, err := filepath.Glob(a.Config.Dir + "/*")
if err != nil {
log.Error().Msgf("Error getting files to serve: %v", err)
}

brotli := "br"
brotliExt := ".br"

// If the request accepts brotli, and the directory contains brotli files, serve them.
if strings.Contains(acceptedEncodings, brotli) {
for _, f := range files {
if f == reqFile+brotliExt {
Expand All @@ -197,7 +191,6 @@ func serveSPA(dir string) http.HandlerFunc {
gzip := "gzip"
gzipExt := ".gz"

// If the request accepts gzip, and the directory contains gzip files, serve them.
if strings.Contains(acceptedEncodings, gzip) {
for _, f := range files {
if f == reqFile+gzipExt {
Expand All @@ -214,12 +207,11 @@ func serveSPA(dir string) http.HandlerFunc {
}
}

// Add custom headers to the response.
func addHeaders(h http.Handler) http.HandlerFunc {
func (a *App) AddHeaders(h http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Server", "GSS")

for k, v := range headers {
for k, v := range a.Config.Headers {
w.Header().Add(k, v)
}

Expand Down
Loading

0 comments on commit 124db91

Please sign in to comment.