Add backend

This commit is contained in:
jimmy 2022-09-09 18:10:34 +12:00
parent 312a8fb017
commit 9ddd929a0b
13 changed files with 805 additions and 0 deletions

189
api/controllers/user.go Normal file
View File

@ -0,0 +1,189 @@
package controllers
import (
"encoding/json"
"magmise/models"
"net/http"
"github.com/go-chi/chi/v5"
)
type User struct {
}
// "/user/:username"
func (u User) Get(w http.ResponseWriter, r *http.Request) {
username := chi.URLParam(r, "username")
if username == "" {
http.Error(w, "Missing user id", http.StatusNotFound)
return
}
user := models.User{Username: username}
if user.Read() != nil {
http.Error(w, "Not found", http.StatusNotFound)
return
}
if err := json.NewEncoder(w).Encode(user); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Add("Content-Type", "application/json")
}
// "/user/{userid}"
func (t User) Create(w http.ResponseWriter, r *http.Request) {
username := chi.URLParam(r, "username")
if username == "" {
http.Error(w, "Missing username", http.StatusNotFound)
}
user := models.User{Username: username, Password: "a"}
if user.Create() != nil {
http.Error(w, "failed to create user", http.StatusInternalServerError)
return
}
}
// // /users/application/:status/
// func (t User) Getusers(w http.ResponseWriter, r *http.Request) {
// status := vestigo.Param(r, "status")
// offset, err := strconv.ParseInt(vestigo.Param(r, "offset"), 0, 32)
// if err != nil {
// offset = 0
// }
// limit, err := strconv.ParseInt(vestigo.Param(r, "limit"), 0, 32)
// if err != nil {
// limit = 100
// }
// var user models.User
// switch status {
// case "open", "closed":
// user = models.User{Status: status}
// case "all":
// user = models.User{}
// default:
// http.Error(w, "Missing status", http.StatusNotFound)
// return
// }
// users, err := user.ReadAll(int(offset), int(limit))
// if err != nil {
// http.Error(w, "Not found", http.StatusNotFound)
// return
// }
// if err := json.NewEncoder(w).Encode(users); err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
// w.Header().Add("Content-Type", "application/json")
// }
// // "/user/application/:userid"
// func (t User) Updateuser(w http.ResponseWriter, r *http.Request) {
// userid := vestigo.Param(r, "userid")
// if userid == "" {
// http.Error(w, "Missing user or discord id", http.StatusNotFound)
// return
// }
// log.Println(r.Body)
// var user models.User
// if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
// log.Println(user)
// if err := user.Update(); err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
// }
// // "/user/application/:userid"
// func (t User) Deleteuser(w http.ResponseWriter, r *http.Request) {
// userid := vestigo.Param(r, "userid")
// if userid == "" {
// http.Error(w, "Missing user or discord id", http.StatusNotFound)
// }
// user := models.User{userId: userid}
// if user.Delete() != nil {
// http.Error(w, "failed to delete user", http.StatusInternalServerError)
// return
// }
// }
// // /user/:discordid/users/application/:status/
// func (t User) GetUserusers(w http.ResponseWriter, r *http.Request) {
// discordid, _ := strconv.ParseInt(vestigo.Param(r, "discordid"), 0, 64)
// status := vestigo.Param(r, "status")
// offset, err := strconv.ParseInt(vestigo.Param(r, "offset"), 0, 32)
// if err != nil {
// offset = 0
// }
// limit, err := strconv.ParseInt(vestigo.Param(r, "limit"), 0, 32)
// if err != nil {
// limit = 100
// }
// log.Println(discordid, status)
// var user models.User
// switch status {
// case "open", "closed":
// user = models.User{DiscordId: discordid, Status: status}
// case "all":
// user = models.User{DiscordId: discordid}
// default:
// http.Error(w, "Missing status", http.StatusNotFound)
// return
// }
// users, err := user.ReadAll(int(offset), int(limit))
// if err != nil {
// http.Error(w, "Not found", http.StatusNotFound)
// return
// }
// if err := json.NewEncoder(w).Encode(users); err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
// w.Header().Add("Content-Type", "application/json")
// }
// // "/answer/application/:userid/:modalid"
// func (t User) AddAnswer(w http.ResponseWriter, r *http.Request) {
// userid := vestigo.Param(r, "userid")
// modalid := vestigo.Param(r, "modalid")
// if userid == "" {
// http.Error(w, "Missing user or discord id", http.StatusNotFound)
// return
// }
// b, err := ioutil.ReadAll(r.Body)
// if err != nil {
// http.Error(w, "Missing body", http.StatusNotFound)
// }
// user := models.User{userId: userid}
// if user.Read() != nil {
// http.Error(w, "Failed to read user", http.StatusInternalServerError)
// }
// for _, answer := range user.Answers {
// if answer.ModalId == modalid {
// answer.Response = string(b)
// if answer.Update() != nil {
// http.Error(w, "Failed to update answer", http.StatusInternalServerError)
// return
// }
// return
// }
// }
// answer := models.Answer{ModalId: modalid, Response: string(b)}
// user.Answers = append(user.Answers, answer)
// if user.Update() != nil {
// http.Error(w, "Failed to update user", http.StatusInternalServerError)
// }
// }

23
api/go.mod Normal file
View File

@ -0,0 +1,23 @@
module magmise
go 1.19
require (
github.com/go-chi/chi/v5 v5.0.7
github.com/go-redis/redis/v8 v8.11.5
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/uuid v1.3.0
github.com/husobee/vestigo v1.1.1
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
gorm.io/driver/sqlite v1.3.6
gorm.io/gorm v1.23.8
)
require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/mattn/go-sqlite3 v1.14.12 // indirect
github.com/stretchr/testify v1.8.0 // indirect
)

51
api/go.sum Normal file
View File

@ -0,0 +1,51 @@
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/husobee/vestigo v1.1.1 h1:bsReVP78YhmHUn/nQ4AxIEfObmWMSLGLGXP1OwgFa9s=
github.com/husobee/vestigo v1.1.1/go.mod h1:JigD7C8lzUfpo1uzqYgefpyZLswrtJbAQxMw7ds7YCE=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.3.6 h1:Fi8xNYCUplOqWiPa3/GuCeowRNBRGTf62DEmhMDHeQQ=
gorm.io/driver/sqlite v1.3.6/go.mod h1:Sg1/pvnKtbQ7jLXxfZa+jSHvoX8hoZA8cn4xllOMTgE=
gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=

21
api/main.go Normal file
View File

@ -0,0 +1,21 @@
package main
import (
"magmise/controllers"
"magmise/models"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
models.Init()
var user controllers.User
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Handle("/*", http.FileServer(http.Dir("/api/www")))
r.Get("/user/{username}", user.Get)
r.Post("/user/{username}", user.Create)
http.ListenAndServe(":8080", r)
}

58
api/middleware/auth.go Normal file
View File

@ -0,0 +1,58 @@
package middleware
import (
"fmt"
"log"
"net/http"
"os"
"strings"
"github.com/golang-jwt/jwt"
)
var secret = []byte(os.Getenv("SECRET"))
func Auth(f http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// get token
authheader, ok := r.Header["Authorization"]
if !ok {
http.Error(w, "Missing token", http.StatusBadRequest)
return
}
tokenString := strings.Split(authheader[0], " ")[1]
//parse token
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return secret, nil
})
if err != nil {
http.Error(w, "Bad token", http.StatusBadRequest)
return
}
// check if path is allowed
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid && r.URL.Path == claims["path"] {
log.Println(r.URL.Path)
f(w, r)
} else {
log.Println(err)
http.Error(w, "Forbidden", http.StatusUnauthorized)
}
// it's all good
}
}
func GenerateToken(path string) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"path": path,
})
tokenString, err := token.SignedString(secret)
if err != nil {
panic(err)
}
fmt.Println(tokenString)
}

View File

@ -0,0 +1,33 @@
package middleware
import (
"net/http"
"os"
"golang.org/x/crypto/bcrypt"
)
func BasicAuth(f http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth()
hash, err := HashPassword(os.Getenv("PASSWORD"))
if err == nil && ok {
if username == os.Getenv("USERNAME") && CheckPasswordHash(password, hash) {
f(w, r)
return
}
}
w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`)
http.Error(w, "Unauthorized", http.StatusUnauthorized)
})
}
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
return string(bytes), err
}
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}

59
api/models/db.go Normal file
View File

@ -0,0 +1,59 @@
package models
import (
"errors"
"log"
"os"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
var db *gorm.DB
func Init() {
if _, err := os.Stat("/data/database.db"); errors.Is(err, os.ErrNotExist) {
log.Printf("Creating sqlite database")
_, err := os.Create("/data/database.db")
if err != nil {
log.Fatal(err)
}
}
var err error
db, err = gorm.Open(sqlite.Open("/data/database.db"), &gorm.Config{})
if err != nil {
log.Println("Failed to connect to db")
}
migrate()
}
func DB() *gorm.DB {
if db == nil {
Init()
}
return db
}
func create(d interface{}) error {
return DB().Create(&d).Error
}
func read(d *interface{}) error {
return DB().First(d, d).Error
}
func readAll(d interface{}, offset, limit int) ([]interface{}, error) {
var data []interface{}
err := DB().Offset(offset).Limit(limit).Where(&d).Find(&data).Error
return data, err
}
func update(d interface{}, where string, f interface{}) error {
return DB().Model(d).Where(where+" = ?", f).Updates(d).Error
}
func delete(d interface{}) error {
return DB().Delete(d).Error
}

9
api/models/migrations.go Normal file
View File

@ -0,0 +1,9 @@
package models
import "fmt"
func migrate() {
DB().AutoMigrate(&User{})
fmt.Println("Migrated")
}

34
api/models/user.go Normal file
View File

@ -0,0 +1,34 @@
package models
import (
"gorm.io/gorm"
)
type User struct {
gorm.Model
Username string `json:"username" gorm:"uniqueIndex, column: username"`
Password string
}
func (u User) Create() error {
return DB().Create(&u).Error
}
func (u *User) Read() error {
return DB().First(u, u).Error
}
func (u User) ReadAll(offset, limit int) ([]User, error) {
var Users []User
err := DB().Offset(offset).Limit(limit).Where(&u).Find(&Users).Error
return Users, err
}
func (u *User) Update() error {
return DB().Model(u).Where("username = ?", u.Username).Updates(u).Error
}
func (u *User) Delete() error {
return DB().Where("username = ?", u.Username).Delete(u).Error
}

View File

@ -0,0 +1,188 @@
package controllers_test
import (
"encoding/json"
"magmise/controllers"
"magmise/models"
"magmise/test"
"testing"
)
// "/user/{username}"
func TestGetUser(t *testing.T) {
setup := test.Setup(t, "get user")
defer setup()
var user controllers.User
w := test.Request(t, user.Get, "GET", "/user/{username}", `{}`, map[string]string{"username": "fred"})
var usermodel models.User
if json.Unmarshal(w.Body.Bytes(), &usermodel) != nil {
t.Fail()
}
if usermodel.Username != "fred" {
t.Fatal(usermodel)
}
t.Log(usermodel)
}
// // "/user/application/:userid/:discordid"
// func TestCreateuser(t *testing.T) {
// setup := test.Setup(t)
// defer setup()
// var user controllers.User
// handler := http.HandlerFunc(user.Createuser)
// r, err := http.NewRequest("POST", "/user/application/:userid/:discordid", nil)
// if err != nil {
// t.Fatal(err)
// }
// q := r.URL.Query()
// q.Add(":userid", "6")
// q.Add(":discordid", "6")
// r.URL.RawQuery = q.Encode()
// w := httptest.NewRecorder()
// handler(w, r)
// if w.Code != 200 {
// t.Fail()
// }
// }
// // PATCH "/user/application/:userid"
// func TestUpdateuser(t *testing.T) {
// setup := test.Setup(t)
// defer setup()
// t.Log("Test Updateuser")
// usermodel := models.User{userId: "1", Status: "closed"}
// userjson, err := json.Marshal(usermodel)
// if err != nil {
// t.Fatal(err)
// }
// var user controllers.Userco
// handler := http.HandlerFunc(user.Updateuser)
// r, err := http.NewRequest("PATCH", "/user/application/:userid", bytes.NewBuffer(userjson))
// r.Header.Set("Content-Type", "application/json; charset=UTF-8")
// if err != nil {
// t.Fatal(err)
// }
// q := r.URL.Query()
// q.Add(":userid", "1")
// r.URL.RawQuery = q.Encode()
// w := httptest.NewRecorder()
// handler(w, r)
// if w.Code != 200 {
// t.Log(w.Body)
// t.Fatal(w.Code)
// }
// usermodel.Read()
// if usermodel.Status != "closed" {
// t.Fail()
// }
// }
// // "/user/application/:userid"
// func TestDeleteuser(t *testing.T) {
// setup := test.Setup(t)
// defer setup()
// var user controllers.User
// handler := http.HandlerFunc(user.Deleteuser)
// r, err := http.NewRequest("POST", "/user/application/:userid", nil)
// if err != nil {
// t.Fatal(err)
// }
// q := r.URL.Query()
// q.Add(":userid", "1")
// r.URL.RawQuery = q.Encode()
// w := httptest.NewRecorder()
// handler(w, r)
// if w.Code != 200 {
// t.Fail()
// }
// }
// // /user/:discordid/users/application/:status/
// func TestGetUserusers(t *testing.T) {
// setup := test.Setup(t)
// defer setup()
// handler := http.HandlerFunc(controllers.User{}.GetUserusers)
// r, err := http.NewRequest("GET", "/user/:discordid/users/application/:status/", nil)
// if err != nil {
// t.Fatal(err)
// }
// q := r.URL.Query()
// q.Add(":discordid", "1")
// q.Add(":status", "all")
// q.Add(":limit", "100")
// r.URL.RawQuery = q.Encode()
// w := httptest.NewRecorder()
// handler(w, r)
// if w.Code != 200 {
// t.Fail()
// }
// var users []models.User
// if json.Unmarshal(w.Body.Bytes(), &users) != nil {
// t.Fatal("Failed unmarshal")
// }
// for _, user := range users {
// t.Log(user)
// t.Log("\n")
// }
// }
// // /users/application/:status/
// func TestGetusers(t *testing.T) {
// setup := test.Setup(t)
// defer setup()
// handler := http.HandlerFunc(controllers.User{}.GetUserusers)
// r, err := http.NewRequest("GET", "/users/application/:status/", nil)
// if err != nil {
// t.Fatal(err)
// }
// q := r.URL.Query()
// q.Add(":status", "all")
// q.Add(":limit", "100")
// r.URL.RawQuery = q.Encode()
// w := httptest.NewRecorder()
// handler(w, r)
// if w.Code != 200 {
// t.Fail()
// }
// var users []models.User
// if json.Unmarshal(w.Body.Bytes(), &users) != nil {
// t.Fatal("Failed unmarshal")
// }
// for _, user := range users {
// t.Log(user)
// t.Log("\n")
// }
// }
// // "/answer/application/:userid/"
// func TestAddAnswer(t *testing.T) {
// setup := test.Setup(t)
// defer setup()
// var user controllers.User
// handler := http.HandlerFunc(user.AddAnswer)
// r, err := http.NewRequest("POST", "/answer/application/:userid/", bytes.NewBufferString("Hello world"))
// if err != nil {
// t.Fatal(err)
// }
// q := r.URL.Query()
// q.Add(":userid", "1")
// r.URL.RawQuery = q.Encode()
// w := httptest.NewRecorder()
// handler(w, r)
// if w.Code != 200 {
// t.Fatal("Failed to add answer", w.Code)
// }
// }

View File

@ -0,0 +1,18 @@
package middleware_test
import (
"magmise/middleware"
"testing"
)
func TestCheckPasswordHash(t *testing.T) {
password := "password"
hash, _ := middleware.HashPassword(password)
if middleware.CheckPasswordHash(password, hash) == false {
t.Fail()
}
}
func TestBasicAuth(t *testing.T) {
}

View File

@ -0,0 +1,73 @@
package models_test
import (
"magmise/models"
"magmise/test"
"testing"
)
// func TestUserCreate(t *testing.T) {
// cleanup := test.Setup(t, "user create")
// defer cleanup()
// user := models.User{Username: "Bob", Password: "a"}
// if user.Create() != nil {
// t.Fail()
// }
// }
func TestUserRead(t *testing.T) {
cleanup := test.Setup(t, "user read")
defer cleanup()
user := models.User{Username: "fred"}
if user.Read() != nil {
t.Fatal("Faied to read application user")
}
if user.Password != "a" {
t.Log(user)
}
}
func TestUserReadAll(t *testing.T) {
cleanup := test.Setup(t, "user readall")
defer cleanup()
user := models.User{}
users, err := user.ReadAll(0, 10)
if err != nil {
t.Fatal(err)
}
if users[0].Username != "fred" {
t.Log(users)
t.Fail()
}
}
func TestUserUpdate(t *testing.T) {
cleanup := test.Setup(t, "user update")
defer cleanup()
user := models.User{Username: "fred"}
user.Read()
user.Password = "b"
if user.Update() != nil {
t.Fail()
}
user.Read()
if user.Password != "b" {
t.Fail()
}
}
func TestUserDelete(t *testing.T) {
cleanup := test.Setup(t, "user delete")
defer cleanup()
user := models.User{Username: "fred"}
if err := user.Delete(); err != nil {
t.Fatal(err)
}
if user.Read() == nil {
t.Fatal(user)
}
}

49
api/test/test.go Normal file
View File

@ -0,0 +1,49 @@
package test
import (
"bytes"
"context"
"magmise/models"
"net/http"
"net/http/httptest"
"testing"
"github.com/go-chi/chi/v5"
)
func Setup(t *testing.T, name string) func() {
t.Log("Setup " + name)
models.Init()
//models.DB().Exec("DROP TABLE ticketapplication_answers")
models.DB().Migrator().DropTable(models.User{})
models.DB().AutoMigrate(models.User{})
models.User{Username: "fred", Password: "a"}.Create()
return func() {
t.Log("Cleanup " + name)
models.DB().Migrator().DropTable(models.User{})
}
}
func Request(t *testing.T, model http.HandlerFunc, method, path, _json string, urlparams map[string]string) *httptest.ResponseRecorder {
handler := http.HandlerFunc(model)
r, err := http.NewRequest("GET", "/user/{username}", bytes.NewBufferString(_json))
if err != nil {
t.Fatal(err)
}
rctx := chi.NewRouteContext()
for key, value := range urlparams {
rctx.URLParams.Add(key, value)
}
r = r.WithContext(context.WithValue(r.Context(), chi.RouteCtxKey, rctx))
w := httptest.NewRecorder()
handler(w, r)
if w.Code != 200 {
t.Fail()
}
return w
}