Merge pull request 'chore: test improvements' (#68) from test-improvements into master

Reviewed-on: https://git.freecumextremist.com/grumbulon/pomme/pulls/68
This commit is contained in:
grumbulon 2023-03-25 15:04:44 +00:00
commit 6157ee2f7a
6 changed files with 267 additions and 158 deletions

View file

@ -48,4 +48,4 @@ test-frontend:
pnpm -F frontend "test:unit"
test-backend:
go test -v -cover ./internal/api
go test -v -cover ./internal/...

View file

@ -2,9 +2,7 @@ package api
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/http/httptest"
@ -24,12 +22,6 @@ import (
"gorm.io/gorm"
)
type response struct {
Username string `json:"username"`
Message string `json:"message"`
Status int `json:"status"`
}
type accountTest struct {
username string
password string
@ -52,7 +44,7 @@ func makeTestToken(username string) (tokenString string, err error) {
func TestAPI(t *testing.T) {
config, err := internal.ReadConfig()
if err != nil {
panic(err)
assert.NotNil(t, err)
}
pomme := chi.NewRouter()
@ -75,23 +67,17 @@ func TestAPI(t *testing.T) {
tester := Init(ts.URL)
// test mode
mode = "test"
db, err, ok := db.InitDb(config.DB, mode)
if err != nil && !ok {
assert.NotNil(t, err)
}
db, err, _ := db.InitDb(config.DB, mode)
assert.Nil(t, err)
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(tester.password), bcrypt.DefaultCost)
if err != nil {
assert.NotNil(t, err)
}
assert.Nil(t, err)
db.Create(&internal.User{Username: tester.username, HashedPassword: string(hashedPassword)})
tester.TestMakeAccount(t)
tester.TestLogin(t)
tester.TestLogout(t)
tester.TestUpload(t)
tester.TestOpenRoutes(t)
tester.TestProtectedRoutes(t)
tester.TestRateLimit(t)
tester.CleanUp(db)
}
@ -115,119 +101,186 @@ func Init(url string) accountTest {
return test
}
func (a *accountTest) TestMakeAccount(t *testing.T) {
var target response
client := http.Client{}
form := url.Values{}
form.Add("username", a.username)
form.Add("password", a.password)
if req, err := http.NewRequest(http.MethodPost, a.url+`api/create`, strings.NewReader(form.Encode())); err == nil {
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
assert.NotNil(t, err)
}
respBody, _ := io.ReadAll(resp.Body)
err = json.Unmarshal(respBody, &target)
if err != nil {
assert.NotNil(t, err)
}
assert.Equal(t, http.StatusCreated, target.Status)
}
}
func (a *accountTest) TestLogin(t *testing.T) {
var target response
client := http.Client{}
form := url.Values{}
form.Add("username", a.username)
form.Add("password", a.password)
if req, err := http.NewRequest(http.MethodPost, a.url+`/api/login`, strings.NewReader(form.Encode())); err == nil {
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
assert.NotNil(t, err)
}
respBody, _ := io.ReadAll(resp.Body)
err = json.Unmarshal(respBody, &target)
if err != nil {
assert.NotNil(t, err)
}
assert.Equal(t, http.StatusOK, resp.StatusCode)
}
}
func (a *accountTest) TestLogout(t *testing.T) {
var target response
client := http.Client{}
form := url.Values{}
form.Add("username", a.username)
if req, err := http.NewRequest(http.MethodPost, a.url+`/api/logout`, strings.NewReader(form.Encode())); err == nil {
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
assert.NotNil(t, err)
}
respBody, _ := io.ReadAll(resp.Body)
err = json.Unmarshal(respBody, &target)
if err != nil {
assert.NotNil(t, err)
}
assert.Equal(t, http.StatusOK, resp.StatusCode)
}
}
type expectedValues struct {
response int
}
func (a *accountTest) TestUpload(t *testing.T) {
func (a *accountTest) TestOpenRoutes(t *testing.T) {
testCases := []struct {
name string
route string
token string
password string
username string
expected expectedValues
}{
{
name: "Should fail to login with bad username",
route: "login",
username: "a.username",
password: a.password,
expected: expectedValues{
response: http.StatusUnauthorized,
},
},
{
name: "Should fail to login with bad password",
route: "login",
username: a.username,
password: "a.password",
expected: expectedValues{
response: http.StatusUnauthorized,
},
},
{
name: "Should fail to login with bad password or username",
route: "login",
username: "apple",
password: "a.password",
expected: expectedValues{
response: http.StatusUnauthorized,
},
},
{
name: "Should fail to login with empty form",
route: "login",
expected: expectedValues{
response: http.StatusInternalServerError,
},
},
{
name: "Should login successfully",
route: "login",
username: a.username,
password: a.password,
expected: expectedValues{
response: http.StatusOK,
},
},
{
name: "Should fail to create account with empty form",
route: "create",
expected: expectedValues{
response: http.StatusInternalServerError,
},
},
{
name: "Should create account with empty username",
route: "create",
password: "asdf",
expected: expectedValues{
response: http.StatusCreated,
},
},
{
name: "Should fail to create account with empty password",
route: "create",
expected: expectedValues{
response: http.StatusInternalServerError,
},
},
{
name: "Should log out successfully",
route: "logout",
username: a.username,
expected: expectedValues{
response: http.StatusOK,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
client := http.Client{}
form := url.Values{}
form.Add("username", tc.username)
form.Add("password", tc.password)
if req, err := http.NewRequest(http.MethodPost, a.url+`/api/`+tc.route, strings.NewReader(form.Encode())); err == nil {
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
assert.NotNil(t, err)
}
assert.Equal(t, tc.expected.response, resp.StatusCode)
}
})
}
}
func (a *accountTest) TestRateLimit(t *testing.T) {
testCases := []struct {
name string
route string
password string
username string
expected expectedValues
}{
{
name: "Should rate limit open routes",
route: "login",
expected: expectedValues{
response: http.StatusTooManyRequests,
},
},
{
name: "Should rate limit private routes",
route: "upload",
expected: expectedValues{
response: http.StatusTooManyRequests,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
client := http.Client{}
form := url.Values{}
if req, err := http.NewRequest(http.MethodPost, a.url+`/api/`+tc.route, strings.NewReader(form.Encode())); err == nil {
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
for i := 0; i < 100; i++ {
resp, err := client.Do(req)
if err != nil {
assert.NotNil(t, err)
}
if resp.StatusCode == tc.expected.response {
assert.Equal(t, tc.expected.response, resp.StatusCode)
}
}
}
})
}
}
func (a *accountTest) TestProtectedRoutes(t *testing.T) {
testCases := []struct {
name string
file string
contentType string
route string
fileContents []byte
expected expectedValues
}{
{
name: "Should fail to upload an empty file",
file: "badzonefile",
contentType: "audio/aac",
route: "upload",
expected: expectedValues{
response: http.StatusInternalServerError,
},
},
{
name: "Should upload a valid file",
file: "zonefile",
contentType: "multipart/form-data",
fileContents: []byte{},
route: "upload",
expected: expectedValues{
response: http.StatusCreated,
},
@ -237,25 +290,18 @@ func (a *accountTest) TestUpload(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var (
f *os.File
target response
err error
buf = new(bytes.Buffer)
w = multipart.NewWriter(buf)
f *os.File
err error
buf = new(bytes.Buffer)
w = multipart.NewWriter(buf)
)
switch tc.name {
case "Should fail to upload an empty file":
f, err = os.CreateTemp(".", tc.file)
if err != nil {
assert.NotNil(t, err)
}
default:
f, err = os.CreateTemp(".", "zonefile")
if err != nil {
assert.NotNil(t, err)
}
f, err = os.CreateTemp(".", "zonefile")
if err != nil {
assert.NotNil(t, err)
}
if tc.name == "Should upload a valid file" {
if err = os.WriteFile(f.Name(), zonebytes, 0o600); err != nil {
assert.NotNil(t, err)
}
@ -263,8 +309,6 @@ func (a *accountTest) TestUpload(t *testing.T) {
defer os.Remove(f.Name()) //nolint: errcheck
client := http.Client{}
part, err := w.CreateFormFile("file", filepath.Base(f.Name()))
if err != nil {
assert.NotNil(t, err)
@ -284,12 +328,11 @@ func (a *accountTest) TestUpload(t *testing.T) {
if err != nil {
assert.NotNil(t, err)
}
if req, err := http.NewRequest(http.MethodPost, a.url+`/api/upload`, buf); err == nil {
switch tc.name {
case "Should fail to upload an empty file":
client := http.Client{}
if req, err := http.NewRequest(http.MethodPost, a.url+`/api/`+tc.route, buf); err == nil {
if tc.name == "Should fail to upload an empty file" {
req.Header.Add("Content-Type", tc.contentType)
default:
} else {
req.Header.Add("Content-Type", w.FormDataContentType())
}
@ -299,14 +342,6 @@ func (a *accountTest) TestUpload(t *testing.T) {
if err != nil {
assert.NotNil(t, err)
}
respBody, _ := io.ReadAll(resp.Body)
err = json.Unmarshal(respBody, &target)
if err != nil {
assert.NotNil(t, err)
}
assert.Equal(t, tc.expected.response, resp.StatusCode)
}
})
@ -314,15 +349,6 @@ func (a *accountTest) TestUpload(t *testing.T) {
}
func (a *accountTest) CleanUp(db *gorm.DB) {
var (
user internal.User
req internal.ZoneRequest
)
db.Where("username = ?", a.username).Delete(&user)
db.Where("user = ?", a.username).Delete(&req)
if err := os.Remove("pomme-test.sqlite"); err != nil {
l := newResponder(Response[any]{
Message: "unable to clean up test DB",

View file

@ -22,7 +22,7 @@ func ReadConfig() (*Config, error) {
if data, err = os.ReadFile(filepath.Clean(defaultConfigPath)); err == nil {
if err = yaml.Unmarshal(data, &config); err != nil {
return &Config{}, fmt.Errorf("unable to unmarshal config file: %w", err)
return nil, fmt.Errorf("unable to unmarshal config file: %w", err)
}
return &config, nil
@ -30,11 +30,11 @@ func ReadConfig() (*Config, error) {
if data, err = os.ReadFile(filepath.Clean("./config.yaml")); err == nil {
if err = yaml.Unmarshal(data, &config); err != nil {
return &Config{}, fmt.Errorf("unable to unmarshal config file: %w", err)
return nil, fmt.Errorf("unable to unmarshal config file: %w", err)
}
return &config, nil
}
return &Config{}, fmt.Errorf("unable to read config file: %w", err)
return nil, fmt.Errorf("unable to read config file: %w", err)
}

View file

@ -0,0 +1,50 @@
package internal
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestConfig(t *testing.T) {
testCases := []struct {
name string
fileContents string
}{
{
name: "Should fail to read config",
},
{
name: "Should read config file successfully",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if tc.name == "Should read config file successfully" {
f, err := os.Create("config.yaml")
if err != nil {
assert.NotNil(t, err)
}
defer os.Remove(f.Name()) //nolint: errcheck
} else {
err := os.Remove("config.yaml")
if err != nil {
assert.NotNil(t, err)
}
}
config, err := ReadConfig()
if err != nil {
assert.NotNil(t, err)
}
switch tc.name {
case "Should read config file successfully":
assert.NotNil(t, config)
default:
assert.Nil(t, config)
}
})
}
}

34
internal/db/db_test.go Normal file
View file

@ -0,0 +1,34 @@
package db
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestDB(t *testing.T) {
testCases := []struct {
name string
dbMode string
dbName string
}{
{
name: "Should fail to open db",
dbMode: "nothing",
dbName: "",
},
{
name: "Should open test db",
dbMode: "test",
dbName: "",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
_, err, ok := InitDb(tc.dbName, tc.dbMode)
if err != nil && !ok {
assert.NotNil(t, err)
}
})
}
}

View file

@ -1 +0,0 @@
package logger