pomme/internal/api/api_test.go

319 lines
6.4 KiB
Go

package api
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
"time"
"dns.froth.zone/pomme/internal"
"dns.froth.zone/pomme/internal/db"
"github.com/go-chi/chi/v5"
"github.com/go-chi/jwtauth/v5"
"github.com/stretchr/testify/assert"
"golang.org/x/crypto/bcrypt"
)
type response struct {
Username string `json:"username"`
Message string `json:"message"`
Status int `json:"status"`
}
type accountTest struct {
username string
password string
token string
url string
}
func makeTestToken(username string) (tokenString string, err error) {
claim := map[string]interface{}{"username": username, "admin": false}
jwtauth.SetExpiry(claim, time.Now().Add(time.Minute))
if _, tokenString, err = tokenAuth.Encode(claim); err == nil {
return
}
return "", fmt.Errorf("unable to generate JWT: %w", err)
}
func TestAPI(t *testing.T) {
config, err := internal.ReadConfig()
if err != nil {
panic(err)
}
pomme := chi.NewRouter()
pomme.Mount("/api", API())
s := &http.Server{
ReadTimeout: 3 * time.Second,
WriteTimeout: 15 * time.Second,
Addr: ":" + config.Port,
Handler: pomme,
}
ts := httptest.NewUnstartedServer(pomme)
ts.Config = s
ts.Start()
defer ts.Close()
tester := Init(ts.URL)
c, err := internal.ReadConfig()
if err != nil {
assert.NotNil(t, err)
}
db, err, ok := db.InitDb(c.TestDB)
if err != nil && !ok {
assert.NotNil(t, err)
}
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(tester.password), bcrypt.DefaultCost)
if err != nil {
assert.NotNil(t, err)
}
db.Create(&internal.User{Username: tester.username, HashedPassword: string(hashedPassword)})
tester.TestMakeAccount(t)
tester.TestLogin(t)
tester.TestLogout(t)
tester.TestUpload(t)
}
func Init(url string) accountTest {
user := autoUname()
token, err := makeTestToken(user)
if err != nil {
return accountTest{}
}
test := accountTest{
username: user,
password: "merde",
url: url,
token: token,
}
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")
req.Header.Add("User-Agent", "pomme-api-test-slave")
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")
req.Header.Add("User-Agent", "pomme-api-test-slave")
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")
req.Header.Add("User-Agent", "pomme-api-test-slave")
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) {
testCases := []struct {
name string
file string
contentType string
fileContents []byte
expected expectedValues
}{
{
name: "Should fail to upload an empty file",
file: "badzonefile",
contentType: "audio/aac",
expected: expectedValues{
response: http.StatusInternalServerError,
},
},
{
name: "Should upload a valid file",
file: "zonefile",
contentType: "multipart/form-data",
fileContents: []byte{},
expected: expectedValues{
response: http.StatusCreated,
},
},
}
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)
)
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)
}
if err = os.WriteFile(f.Name(), zonebytes, 0o600); err != nil {
assert.NotNil(t, err)
}
}
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)
}
b, err := os.ReadFile(f.Name())
if err != nil {
assert.NotNil(t, err)
}
_, err = part.Write(b)
if err != nil {
assert.NotNil(t, err)
}
err = w.Close()
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":
req.Header.Add("Content-Type", tc.contentType)
default:
req.Header.Add("Content-Type", w.FormDataContentType())
}
req.Header.Add("Authorization", `Bearer:`+a.token)
req.Header.Add("User-Agent", "pomme-api-test-slave")
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, tc.expected.response, resp.StatusCode)
}
})
}
}