file handler tests
This commit is contained in:
parent
24a1f2555b
commit
cde4f47f3e
|
@ -15,43 +15,44 @@ import (
|
|||
"git.hoogi.eu/go-blog/models"
|
||||
)
|
||||
|
||||
type FileHandler struct {
|
||||
Context *middleware.AppContext
|
||||
}
|
||||
|
||||
//FileGetHandler serves the file based on the url filename
|
||||
func FileGetHandler(ctx *middleware.AppContext) http.Handler {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
rv := getVar(r, "filename")
|
||||
func (fh FileHandler) FileGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||
rv := getVar(r, "filename")
|
||||
|
||||
f, err := ctx.FileService.GetByName(rv, nil)
|
||||
f, err := fh.Context.FileService.GetByName(rv, nil)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, "the file was not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
loc := filepath.Join(ctx.ConfigService.Location, f.FullFilename)
|
||||
|
||||
w.Header().Set("Content-Type", f.ContentType)
|
||||
w.Header().Set("Content-Disposition", "inline")
|
||||
|
||||
rf, err := os.Open(loc)
|
||||
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
logger.Log.Errorf("the file %s was not found - %v", loc, err)
|
||||
http.Error(w, "404 page not found", http.StatusNotFound)
|
||||
}
|
||||
if os.IsPermission(err) {
|
||||
logger.Log.Errorf("not permitted to read file %s - %v", loc, err)
|
||||
http.Error(w, "404 page not found", http.StatusForbidden)
|
||||
}
|
||||
logger.Log.Errorf("an internal error while reading file %s - %v", loc, err)
|
||||
http.Error(w, "500 Internal Server Error", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
defer rf.Close()
|
||||
|
||||
http.ServeContent(w, r, loc, f.LastModified, rf)
|
||||
if err != nil {
|
||||
http.Error(w, "the file was not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
|
||||
loc := filepath.Join(fh.Context.ConfigService.Location, f.FullFilename)
|
||||
|
||||
w.Header().Set("Content-Type", f.ContentType)
|
||||
w.Header().Set("Content-Disposition", "inline")
|
||||
|
||||
rf, err := os.Open(loc)
|
||||
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
logger.Log.Errorf("the file %s was not found - %v", loc, err)
|
||||
http.Error(w, "404 page not found", http.StatusNotFound)
|
||||
}
|
||||
if os.IsPermission(err) {
|
||||
logger.Log.Errorf("not permitted to read file %s - %v", loc, err)
|
||||
http.Error(w, "404 page not found", http.StatusForbidden)
|
||||
}
|
||||
logger.Log.Errorf("an internal error while reading file %s - %v", loc, err)
|
||||
http.Error(w, "500 Internal Server Error", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
defer rf.Close()
|
||||
|
||||
http.ServeContent(w, r, loc, f.LastModified, rf)
|
||||
}
|
||||
|
||||
//AdminListFilesHandler returns the template which lists alle uploaded files belonging to a user, admins will see all files
|
||||
|
|
|
@ -1,13 +1,149 @@
|
|||
package controllers_test
|
||||
|
||||
func doAdminListFilesRequest(user reqUser) {
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"git.hoogi.eu/go-blog/controllers"
|
||||
"git.hoogi.eu/go-blog/models"
|
||||
)
|
||||
|
||||
func TestFileWorkflow(t *testing.T) {
|
||||
err := doAdminUploadFileRequest(rAdminUser, "testdata/color.png")
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
files, err := doAdminListFilesRequest(rAdminUser)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(files) != 1 {
|
||||
t.Errorf("one file is uploaded; but list files returns %d file(s)", len(files))
|
||||
}
|
||||
|
||||
rr, err := doAdminGetFileRequest(rGuest, files[0].FullFilename)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if rr.Result().ContentLength != 1610 {
|
||||
t.Errorf("expected 1610 bytes, but got %d", rr.Result().ContentLength)
|
||||
}
|
||||
|
||||
if rr.Result().Header.Get("Content-Type") != "image/png" {
|
||||
t.Errorf("expected image/png content type, but got %s", rr.Result().Header.Get("Content-Type"))
|
||||
}
|
||||
|
||||
err = doAdminFileDeleteRequest(rAdminUser, files[0].ID)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = doAdminGetFileRequest(rGuest, files[0].FullFilename)
|
||||
|
||||
if err == nil {
|
||||
t.Errorf("file should be removed, but file is there %s", files[0].FullFilename)
|
||||
}
|
||||
}
|
||||
|
||||
func doAdminUploadFileRequest(user reqUser) {
|
||||
func doAdminListFilesRequest(user reqUser) ([]models.File, error) {
|
||||
r := request{
|
||||
url: "/admin/files",
|
||||
user: user,
|
||||
method: "GET",
|
||||
}
|
||||
|
||||
rw := httptest.NewRecorder()
|
||||
tpl := controllers.AdminListFilesHandler(ctx, rw, r.buildRequest())
|
||||
|
||||
if tpl.Err != nil {
|
||||
return nil, tpl.Err
|
||||
}
|
||||
|
||||
return tpl.Data["files"].([]models.File), nil
|
||||
}
|
||||
|
||||
func doAdminUploadDeleteRequest(user reqUser) {
|
||||
func doAdminGetFileRequest(user reqUser, filename string) (*httptest.ResponseRecorder, error) {
|
||||
r := request{
|
||||
url: "/file/" + filename,
|
||||
user: user,
|
||||
method: "GET",
|
||||
pathVar: []pathVar{
|
||||
pathVar{
|
||||
key: "filename",
|
||||
value: filename,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rw := httptest.NewRecorder()
|
||||
|
||||
fh := controllers.FileHandler{
|
||||
Context: ctx,
|
||||
}
|
||||
|
||||
fh.FileGetHandler(rw, r.buildRequest())
|
||||
|
||||
if rw.Result().StatusCode != http.StatusOK {
|
||||
return rw, fmt.Errorf("got an invalid status code during file request /file/%s , code: %d, message %s", filename, rw.Result().StatusCode, rw.Result().Status)
|
||||
}
|
||||
|
||||
return rw, nil
|
||||
}
|
||||
|
||||
func doAdminUploadFileRequest(user reqUser, file string) error {
|
||||
mp := []multipartRequest{
|
||||
multipartRequest{
|
||||
key: "file",
|
||||
file: file,
|
||||
},
|
||||
}
|
||||
|
||||
r := request{
|
||||
url: "/admin/file/upload",
|
||||
user: user,
|
||||
method: "POST",
|
||||
multipartReq: mp,
|
||||
}
|
||||
|
||||
rw := httptest.NewRecorder()
|
||||
tpl := controllers.AdminUploadFilePostHandler(ctx, rw, r.buildRequest())
|
||||
|
||||
if tpl.Err != nil {
|
||||
return tpl.Err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func doAdminFileDeleteRequest(user reqUser, fileID int) error {
|
||||
r := request{
|
||||
url: "/admin/file/delete/" + strconv.Itoa(fileID),
|
||||
user: user,
|
||||
method: "GET",
|
||||
pathVar: []pathVar{
|
||||
pathVar{
|
||||
key: "fileID",
|
||||
value: strconv.Itoa(fileID),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rw := httptest.NewRecorder()
|
||||
tpl := controllers.AdminUploadDeletePostHandler(ctx, rw, r.buildRequest())
|
||||
|
||||
if tpl.Err != nil {
|
||||
return tpl.Err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
|
@ -9,10 +9,15 @@ import (
|
|||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"git.hoogi.eu/go-blog/components/database"
|
||||
|
@ -57,6 +62,8 @@ func setup(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cfg.File.Location = os.TempDir()
|
||||
|
||||
userService := models.UserService{
|
||||
Datasource: models.SQLiteUserDatasource{
|
||||
SQLConn: db,
|
||||
|
@ -187,10 +194,6 @@ func setHeader(r *http.Request, key, value string) {
|
|||
r.Header.Set("X-Unit-Testing-Value-"+key, value)
|
||||
}
|
||||
|
||||
func addValue(m url.Values, key, value string) {
|
||||
m.Add(key, value)
|
||||
}
|
||||
|
||||
type MockSMTP struct{}
|
||||
|
||||
func (sm MockSMTP) Send(m mail.Mail) error {
|
||||
|
@ -201,6 +204,10 @@ func (sm MockSMTP) SendAsync(m mail.Mail) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func addValue(m url.Values, key, value string) {
|
||||
m.Add(key, value)
|
||||
}
|
||||
|
||||
func addCheckboxValue(m url.Values, key string, value bool) {
|
||||
if value {
|
||||
m.Add(key, "on")
|
||||
|
@ -208,20 +215,69 @@ func addCheckboxValue(m url.Values, key string, value bool) {
|
|||
m.Add(key, "off")
|
||||
}
|
||||
|
||||
func postMultipart(path string, mp []multipartRequest) (*http.Request, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
mw := multipart.NewWriter(buf)
|
||||
|
||||
defer mw.Close()
|
||||
|
||||
for _, v := range mp {
|
||||
fh, err := os.Open(v.file)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer fh.Close()
|
||||
|
||||
fw, err := mw.CreateFormFile(v.key, filepath.Base(fh.Name()))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = io.Copy(fw, fh)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", path, buf)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", mw.FormDataContentType())
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func post(path string, values url.Values) (*http.Request, error) {
|
||||
var b bytes.Buffer
|
||||
b.WriteString(values.Encode())
|
||||
req, err := http.NewRequest("POST", path, &b)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
return req, err
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func get(path string, values url.Values) (*http.Request, error) {
|
||||
var b bytes.Buffer
|
||||
b.WriteString(values.Encode())
|
||||
req, err := http.NewRequest("GET", path, &b)
|
||||
return req, err
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
//reqUser the user which should be added to the context
|
||||
|
@ -239,11 +295,17 @@ const (
|
|||
//url will not really considered as the requests are not send, the *http.Request is just passed directly to the controllers
|
||||
//pathvar is an array of key/value pairs used as dynamic query parameters such as /article/{id}
|
||||
type request struct {
|
||||
url string
|
||||
user reqUser
|
||||
method string
|
||||
values url.Values
|
||||
pathVar []pathVar
|
||||
url string
|
||||
user reqUser
|
||||
method string
|
||||
values url.Values
|
||||
pathVar []pathVar
|
||||
multipartReq []multipartRequest
|
||||
}
|
||||
|
||||
type multipartRequest struct {
|
||||
key string
|
||||
file string
|
||||
}
|
||||
|
||||
type pathVar struct {
|
||||
|
@ -253,11 +315,18 @@ type pathVar struct {
|
|||
|
||||
func (r request) buildRequest() *http.Request {
|
||||
var req *http.Request
|
||||
var err error
|
||||
|
||||
if r.method == http.MethodPost {
|
||||
req, _ = post(r.url, r.values)
|
||||
if len(r.multipartReq) > 0 {
|
||||
req, err = postMultipart(r.url, r.multipartReq)
|
||||
} else if r.method == http.MethodPost {
|
||||
req, err = post(r.url, r.values)
|
||||
} else {
|
||||
req, _ = get(r.url, r.values)
|
||||
req, err = get(r.url, r.values)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
if r.pathVar != nil {
|
||||
|
|
|
@ -147,6 +147,10 @@ func restrictedRoutes(ctx *m.AppContext, router *mux.Router, chain alice.Chain)
|
|||
}
|
||||
|
||||
func publicRoutes(ctx *m.AppContext, router *mux.Router, chain alice.Chain) {
|
||||
fh := c.FileHandler{
|
||||
Context: ctx,
|
||||
}
|
||||
|
||||
router.Handle("/", chain.Then(useTemplateHandler(ctx, c.ListArticlesHandler))).Methods("GET")
|
||||
router.Handle("/articles/category/{categorySlug}", chain.Then(useTemplateHandler(ctx, c.ListArticlesCategoryHandler))).Methods("GET")
|
||||
router.Handle("/articles/category/{categorySlug}/{page}", chain.Then(useTemplateHandler(ctx, c.ListArticlesCategoryHandler))).Methods("GET")
|
||||
|
@ -161,7 +165,8 @@ func publicRoutes(ctx *m.AppContext, router *mux.Router, chain alice.Chain) {
|
|||
|
||||
router.Handle("/site/{site}", chain.Then(useTemplateHandler(ctx, c.GetSiteHandler))).Methods("GET")
|
||||
|
||||
router.Handle("/file/{filename}", chain.Then(c.FileGetHandler(ctx))).Methods("GET")
|
||||
router.Handle("/file/{filename}", chain.ThenFunc(fh.FileGetHandler)).Methods("GET")
|
||||
|
||||
router.Handle("/admin", chain.Then(useTemplateHandler(ctx, c.LoginHandler))).Methods("GET")
|
||||
router.Handle("/admin", chain.Then(useTemplateHandler(ctx, c.LoginPostHandler))).Methods("POST")
|
||||
|
||||
|
|
Loading…
Reference in New Issue