Added unit test

This commit is contained in:
Marc Ole Bulling
2021-04-13 20:54:25 +02:00
parent 7486d3df51
commit ec17c9f732
7 changed files with 162 additions and 29 deletions

View File

@@ -1,4 +1,4 @@
name: Test Compilation
name: Compile and run unit tests
on: [push, pull_request]
@@ -9,11 +9,7 @@ jobs:
steps:
- name: checkout code
uses: actions/checkout@v2
- name: install buildx
id: buildx
uses: crazy-max/ghaction-docker-buildx@v1
with:
version: latest
- name: build the image
run: |
docker buildx build --tag f0rc3/gokapi:test --platform linux/386,linux/amd64,linux/arm/v6,linux/arm/v7 .
- uses: actions/setup-go@v2
with:
go-version: '^1.16.0'
- run: go test

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@ config/
data/
.idea/
Gokapi
test/

10
Main.go
View File

@@ -31,7 +31,7 @@ func main() {
configuration.Load()
checkArguments()
go storage.CleanUp(true)
webserver.Start(&staticFolderEmbedded, &templateFolderEmbedded)
webserver.Start(&StaticFolderEmbedded, &TemplateFolderEmbedded, true)
}
// Checks for command line arguments that have to be parsed before loading the configuration
@@ -59,15 +59,15 @@ func checkArguments() {
}
}
// Embedded version of the "static" folder
// StaticFolderEmbedded is the embedded version of the "static" folder
// This contains JS files, CSS, images etc
//go:embed web/static
var staticFolderEmbedded embed.FS
var StaticFolderEmbedded embed.FS
// Embedded version of the "templates" folder
// TemplateFolderEmbedded is the embedded version of the "templates" folder
// This contains templates that Gokapi uses for creating the HTML output
//go:embed web/templates
var templateFolderEmbedded embed.FS
var TemplateFolderEmbedded embed.FS
// ASCII art logo
const logo = `

133
Main_test.go Normal file
View File

@@ -0,0 +1,133 @@
package main
/**
Unit testing for whole project. At the moment coverage is low.
*/
import (
"Gokapi/internal/configuration"
"Gokapi/internal/environment"
"Gokapi/internal/webserver"
"bytes"
"html/template"
"io/fs"
"io/ioutil"
"net/http"
"os"
"strings"
"testing"
"time"
)
func loadConfig() {
os.Setenv("GOKAPI_CONFIG_DIR", "test")
os.Mkdir("test", 0777)
os.WriteFile("test/config.json", configTestFile, 0777)
configuration.Load()
}
func isEqualString(t *testing.T, got, want string) {
if got != want {
t.Errorf("Assertion failed, got: %s, want: %s.", got, want)
}
}
func isEqualBool(t *testing.T, got, want bool) {
if got != want {
t.Errorf("Assertion failed, got: %t, want: %t.", got, want)
}
}
func isEqualInt(t *testing.T, got, want int) {
if got != want {
t.Errorf("Assertion failed, got: %d, want: %d.", got, want)
}
}
func TestConfigLoad(t *testing.T) {
loadConfig()
isEqualString(t, configuration.Environment.ConfigDir, "test")
isEqualString(t, configuration.ServerSettings.Port, "127.0.0.1:53843")
isEqualString(t, configuration.ServerSettings.AdminName, "test")
isEqualString(t, configuration.ServerSettings.AdminPassword, "10340aece68aa4fb14507ae45b05506026f276cf")
isEqualString(t, configuration.ServerSettings.ServerUrl, "http://127.0.0.1:53843/")
isEqualString(t, configuration.ServerSettings.AdminPassword, "10340aece68aa4fb14507ae45b05506026f276cf")
isEqualString(t, configuration.HashPassword("testtest", false), "10340aece68aa4fb14507ae45b05506026f276cf")
}
func testHttpPage(t *testing.T, testUrl, requiredContent string, isHtml bool, authCookie string) {
client := &http.Client{}
req, err := http.NewRequest("GET", testUrl, nil)
if err != nil {
t.Fatal(err)
}
if authCookie != "" {
req.Header.Set("Cookie", "session_token="+authCookie)
}
resp, err := client.Do(req)
if resp.StatusCode != 200 {
t.Errorf("Status %d != 200", resp.StatusCode)
}
bs, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
if isHtml && !bytes.Contains(bs, []byte("</html>")) {
t.Error(testUrl + ": Incorrect response")
}
if !bytes.Contains(bs, []byte(requiredContent)) {
t.Error(testUrl + ": Incorrect response. Got:\n" + string(bs))
}
resp.Body.Close()
}
func TestWebserver(t *testing.T) {
loadConfig()
go webserver.Start(&StaticFolderEmbedded, &TemplateFolderEmbedded, false)
time.Sleep(1 * time.Second)
// Index redirect
testHttpPage(t, "http://localhost:53843/", "<html><head><meta http-equiv=\"Refresh\" content=\"0; URL=./index\"></head></html>", true, "")
// CSS file
testHttpPage(t, "http://localhost:53843/css/cover.css", ".btn-secondary:hover", false, "")
// Login page
testHttpPage(t, "http://localhost:53843/login", "id=\"uname_hidden\"", true, "")
// Admin without auth
testHttpPage(t, "http://localhost:53843/admin", "URL=./login\"", true, "")
// Admin with auth
testHttpPage(t, "http://localhost:53843/admin", "Downloads remaining", true, "GubBwU9KVuuRmOTjUvlKSIl9MyBLumuql9NAHFps8hc0UdumD8lD7mdPRuK01ouU9rZ5a4JiWUeB5aJ")
// Admin with invalid auth
testHttpPage(t, "http://localhost:53843/admin", "URL=./login\"", true, "invalid")
}
func TestEmbedFs(t *testing.T) {
templates, err := template.ParseFS(TemplateFolderEmbedded, "web/templates/*.tmpl")
if err != nil {
t.Error("Unable to read templates")
}
if !strings.Contains(templates.DefinedTemplates(), "app_name") {
t.Error("Unable to parse templates")
}
_, err = fs.Stat(StaticFolderEmbedded, "web/static/expired.png")
if err != nil {
t.Error("Static webdir incomplete")
}
}
func TestEnvLoad(t *testing.T) {
os.Setenv("GOKAPI_CONFIG_DIR", "test")
os.Setenv("GOKAPI_CONFIG_FILE", "test2")
os.Setenv("GOKAPI_LOCALHOST", "yes")
os.Setenv("GOKAPI_LENGTH_ID", "7")
env := environment.New()
isEqualString(t, env.ConfigPath, "test/test2")
isEqualString(t, env.WebserverLocalhost, environment.IsTrue)
isEqualInt(t, env.LengthId, 7)
os.Setenv("GOKAPI_LENGTH_ID", "3")
env = environment.New()
isEqualInt(t, env.LengthId, 5)
}
var configTestFile = []byte(`{"Port":"127.0.0.1:53843","AdminName":"test","AdminPassword":"10340aece68aa4fb14507ae45b05506026f276cf","ServerUrl":"http://127.0.0.1:53843/","DefaultDownloads":3,"DefaultExpiry":20,"DefaultPassword":"123","RedirectUrl":"https://test.com/","Sessions":{"GubBwU9KVuuRmOTjUvlKSIl9MyBLumuql9NAHFps8hc0UdumD8lD7mdPRuK01ouU9rZ5a4JiWUeB5aJ":{"RenewAt":2147483646,"ValidUntil":2147483646}},"Files":{},"Hotlinks":{},"ConfigVersion":4,"SaltAdmin":"LW6fW4Pjv8GtdWVLSZD66gYEev6NAaXxOVBw7C","SaltFiles":"lL5wMTtnVCn5TPbpRaSe4vAQodWW0hgk00WCZE","LengthId":0,"DataDir":"test/data"}`)

21
go.sum
View File

@@ -1,15 +1,15 @@
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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=
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=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -17,6 +17,7 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 h1:EBZoQjiKKPaLbPrbpssUfuHtwM6KV/vb4U85g/cigFY=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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=

View File

@@ -32,7 +32,7 @@ var imageExpiredPicture []byte
const expiredFile = "static/expired.png"
// Start the webserver on the port set in the config
func Start(staticFolderEmbedded, templateFolderEmbedded *embed.FS) {
func Start(staticFolderEmbedded, templateFolderEmbedded *embed.FS, verbose bool) {
initTemplates(*templateFolderEmbedded)
webserverDir, _ := fs.Sub(*staticFolderEmbedded, "web/static")
var err error
@@ -57,8 +57,10 @@ func Start(staticFolderEmbedded, templateFolderEmbedded *embed.FS) {
http.HandleFunc("/delete", deleteFile)
http.HandleFunc("/downloadFile", downloadFile)
http.HandleFunc("/forgotpw", forgotPassword)
fmt.Println("Binding webserver to " + configuration.ServerSettings.Port)
fmt.Println("Webserver can be accessed at " + configuration.ServerSettings.ServerUrl + "admin")
if verbose {
fmt.Println("Binding webserver to " + configuration.ServerSettings.Port)
fmt.Println("Webserver can be accessed at " + configuration.ServerSettings.ServerUrl + "admin")
}
srv := &http.Server{
Addr: configuration.ServerSettings.Port,
ReadTimeout: timeOutWebserver,
@@ -84,7 +86,7 @@ func initTemplates(templateFolderEmbedded embed.FS) {
// Sends a redirect HTTP output to the client. Variable url is used to redirect to ./url
func redirect(w http.ResponseWriter, url string) {
_, _ = fmt.Fprint(w, "<head><meta http-equiv=\"Refresh\" content=\"0; URL=./"+url+"\"></head>")
_, _ = fmt.Fprint(w, "<html><head><meta http-equiv=\"Refresh\" content=\"0; URL=./"+url+"\"></head></html>")
}
// Handling of /logout

View File

@@ -1 +1 @@
<head><meta http-equiv="Refresh" content="0; URL=./index"></head>
<html><head><meta http-equiv="Refresh" content="0; URL=./index"></head></html>