mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-08 00:22:14 +01:00
more tests + small refactoring (no more App struct)
This commit is contained in:
62
app/app.go
62
app/app.go
@@ -1,62 +0,0 @@
|
||||
// Package app is the entry point of the application.
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gulien/gotenberg/app/config"
|
||||
"github.com/gulien/gotenberg/app/handlers"
|
||||
"github.com/gulien/gotenberg/app/handlers/converter/process"
|
||||
"github.com/gulien/gotenberg/app/logger"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// App gathers all data required for the application.
|
||||
type App struct {
|
||||
// version is the application version as defined in the main package.
|
||||
version string
|
||||
// config is the application configuration.
|
||||
// It's populated thanks to the gotenberg.yml file.
|
||||
config *config.AppConfig
|
||||
// Server is the instance of http.Server used by the application.
|
||||
Server *http.Server
|
||||
}
|
||||
|
||||
// NewApp instantiates the application by loading the configuration from the
|
||||
// gotenberg.yml file.
|
||||
func NewApp(version string, configurationFilePath string) (*App, error) {
|
||||
c, err := config.NewAppConfig(configurationFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a := &App{}
|
||||
a.version = version
|
||||
a.config = c
|
||||
|
||||
// defines our application logging.
|
||||
logger.SetLevel(a.config.Logs.Level)
|
||||
logger.SetFormatter(a.config.Logs.Formatter)
|
||||
|
||||
// defines our application router.
|
||||
r := mux.NewRouter()
|
||||
r.Handle("/", handlers.GetHandlersChain())
|
||||
|
||||
a.Server = &http.Server{
|
||||
Addr: fmt.Sprintf(":%s", a.config.Port),
|
||||
Handler: r,
|
||||
}
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Run starts the server.
|
||||
func (a *App) Run() error {
|
||||
process.Load(a.config.CommandsConfig)
|
||||
logger.Infof("Starting Gotenberg version %s", a.version)
|
||||
logger.Infof("Listening on port %s", a.config.Port)
|
||||
|
||||
return a.Server.ListenAndServe()
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewApp(t *testing.T) {
|
||||
// case 1: uses an empty configuration file path.
|
||||
if _, err := NewApp("tests", ""); err == nil {
|
||||
t.Error("App should not have been instantiated!")
|
||||
}
|
||||
|
||||
// case 2: uses an correct configuration file.
|
||||
path, _ := filepath.Abs("../_tests/configurations/gotenberg.yml")
|
||||
if _, err := NewApp("tests", path); err != nil {
|
||||
t.Error("App should have been instantiated!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
path, _ := filepath.Abs("../_tests/configurations/gotenberg.yml")
|
||||
a, _ := NewApp("tests", path)
|
||||
|
||||
quit := make(chan bool, 1)
|
||||
go func() {
|
||||
a.Run()
|
||||
}()
|
||||
quit <- true
|
||||
}
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/gulien/gotenberg/app/handlers/converter"
|
||||
"github.com/gulien/gotenberg/app/converter"
|
||||
|
||||
ghttp "github.com/gulien/gotenberg/app/handlers/http"
|
||||
ghttp "github.com/gulien/gotenberg/app/http"
|
||||
)
|
||||
|
||||
type key uint32
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
gfile "github.com/gulien/gotenberg/app/handlers/converter/file"
|
||||
"github.com/gulien/gotenberg/app/handlers/converter/process"
|
||||
ghttp "github.com/gulien/gotenberg/app/handlers/http"
|
||||
gfile "github.com/gulien/gotenberg/app/converter/file"
|
||||
"github.com/gulien/gotenberg/app/converter/process"
|
||||
ghttp "github.com/gulien/gotenberg/app/http"
|
||||
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
ghttp "github.com/gulien/gotenberg/app/handlers/http"
|
||||
ghttp "github.com/gulien/gotenberg/app/http"
|
||||
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
52
app/converter/file/file_test.go
Normal file
52
app/converter/file/file_test.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewFile(t *testing.T) {
|
||||
workingDir := "test"
|
||||
os.Mkdir(workingDir, 0666)
|
||||
|
||||
// case 1: uses an empty reader.
|
||||
if _, err := NewFile(workingDir, new(bytes.Buffer)); err == nil {
|
||||
t.Error("File should not have been instantiated!")
|
||||
}
|
||||
|
||||
// case 2: uses a reader from a wrong file type.
|
||||
path, _ := filepath.Abs("../../../_tests/configurations/gotenberg.yml")
|
||||
r, _ := os.Open(path)
|
||||
defer r.Close()
|
||||
if _, err := NewFile(workingDir, r); err == nil {
|
||||
t.Error("File should not have been instantiated!")
|
||||
}
|
||||
|
||||
// case 3: uses a reader from a correct file type.
|
||||
path, _ = filepath.Abs("../../../_tests/file.pdf")
|
||||
r, _ = os.Open(path)
|
||||
defer r.Close()
|
||||
if _, err := NewFile(workingDir, r); err != nil {
|
||||
t.Error("File should have been instantiated!")
|
||||
}
|
||||
|
||||
os.RemoveAll(workingDir)
|
||||
}
|
||||
|
||||
func TestReworkFilePath(t *testing.T) {
|
||||
workingDir := "test"
|
||||
os.Mkdir(workingDir, 0666)
|
||||
|
||||
f := &File{
|
||||
Path: MakeFilePath(workingDir),
|
||||
Type: 999,
|
||||
}
|
||||
|
||||
if _, err := reworkFilePath(workingDir, f); err == nil {
|
||||
t.Error("It should not have been able to found the file extension!")
|
||||
}
|
||||
|
||||
os.RemoveAll(workingDir)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gulien/gotenberg/app/config"
|
||||
gfile "github.com/gulien/gotenberg/app/handlers/converter/file"
|
||||
gfile "github.com/gulien/gotenberg/app/converter/file"
|
||||
)
|
||||
|
||||
var commandsConfig *config.CommandsConfig
|
||||
@@ -1,5 +1,5 @@
|
||||
// Package handlers implements all functions on which a request will pass through.
|
||||
package handlers
|
||||
// Package app implements all functions on which a request will pass through.
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gulien/gotenberg/app/handlers/context"
|
||||
"github.com/gulien/gotenberg/app/handlers/converter"
|
||||
ghttp "github.com/gulien/gotenberg/app/handlers/http"
|
||||
"github.com/gulien/gotenberg/app/context"
|
||||
"github.com/gulien/gotenberg/app/converter"
|
||||
ghttp "github.com/gulien/gotenberg/app/http"
|
||||
"github.com/gulien/gotenberg/app/logger"
|
||||
|
||||
"github.com/justinas/alice"
|
||||
108
app/handlers_test.go
Normal file
108
app/handlers_test.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gulien/gotenberg/app/context"
|
||||
ghttp "github.com/gulien/gotenberg/app/http"
|
||||
|
||||
"github.com/justinas/alice"
|
||||
)
|
||||
|
||||
func fakeSuccessHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func makeBody(filesPaths ...string) io.Reader {
|
||||
if len(filesPaths) == 1 {
|
||||
file, _ := os.Open(filesPaths[0])
|
||||
defer file.Close()
|
||||
return file
|
||||
}
|
||||
|
||||
// TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestEnforceContentLengthHandler(t *testing.T) {
|
||||
h := alice.New(enforceContentLengthHandler).ThenFunc(fakeSuccessHandler)
|
||||
|
||||
// case 1: sends an empty request.
|
||||
req := httptest.NewRequest(http.MethodPost, "/", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
h.ServeHTTP(rr, req)
|
||||
|
||||
if status := rr.Code; status != http.StatusBadRequest {
|
||||
t.Errorf("Handler returned wrong status code: got %v want %v", status, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// case 2: sends a body.
|
||||
path, _ := filepath.Abs("../../_tests/file.docx")
|
||||
req = httptest.NewRequest(http.MethodPost, "/", makeBody(path))
|
||||
rr = httptest.NewRecorder()
|
||||
h.ServeHTTP(rr, req)
|
||||
|
||||
if status := rr.Code; status != http.StatusOK {
|
||||
t.Errorf("Handler returned wrong status code: got %v want %v", status, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnforceContentTypeHandler(t *testing.T) {
|
||||
h := alice.New(enforceContentTypeHandler).ThenFunc(fakeSuccessHandler)
|
||||
|
||||
// case 1: sends a wrong content type.
|
||||
req := httptest.NewRequest(http.MethodPost, "/", nil)
|
||||
req.Header.Set("Content-Type", string(ghttp.PDFContentType))
|
||||
rr := httptest.NewRecorder()
|
||||
h.ServeHTTP(rr, req)
|
||||
|
||||
if status := rr.Code; status != http.StatusUnsupportedMediaType {
|
||||
t.Errorf("Handler returned wrong status code: got %v want %v", status, http.StatusUnsupportedMediaType)
|
||||
}
|
||||
|
||||
// case 2: sends a good content type.
|
||||
req = httptest.NewRequest(http.MethodPost, "/", nil)
|
||||
req.Header.Set("Content-Type", string(ghttp.OctetStreamContentType))
|
||||
rr = httptest.NewRecorder()
|
||||
h.ServeHTTP(rr, req)
|
||||
|
||||
if status := rr.Code; status != http.StatusOK {
|
||||
t.Errorf("Handler returned wrong status code: got %v want %v", status, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertHandler(t *testing.T) {
|
||||
h := alice.New(convertHandler).ThenFunc(fakeSuccessHandler)
|
||||
|
||||
// case 1: sends a request without a content type in its context.
|
||||
req := httptest.NewRequest(http.MethodPost, "/", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
h.ServeHTTP(rr, req)
|
||||
|
||||
if status := rr.Code; status != http.StatusInternalServerError {
|
||||
t.Errorf("Handler returned wrong status code: got %v want %v", status, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// case 2: sends a request as without body.
|
||||
req = context.WithContentType(httptest.NewRequest(http.MethodPost, "/", nil), ghttp.OctetStreamContentType)
|
||||
rr = httptest.NewRecorder()
|
||||
h.ServeHTTP(rr, req)
|
||||
|
||||
if status := rr.Code; status != http.StatusInternalServerError {
|
||||
t.Errorf("Handler returned wrong status code: got %v want %v", status, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// case 3: sends a request as "multipart/form-data" without "files" key.
|
||||
/*req = context.WithContentType(httptest.NewRequest(http.MethodPost, "/", nil), ghttp.MultipartFormDataContentType)
|
||||
rr = httptest.NewRecorder()
|
||||
h.ServeHTTP(rr, req)
|
||||
|
||||
if status := rr.Code; status != http.StatusBadRequest {
|
||||
t.Errorf("Handler returned wrong status code: got %v want %v", status, http.StatusBadRequest)
|
||||
}*/
|
||||
}
|
||||
35
main.go
35
main.go
@@ -9,13 +9,18 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"github.com/gulien/gotenberg/app"
|
||||
"github.com/gulien/gotenberg/app/config"
|
||||
"github.com/gulien/gotenberg/app/converter/process"
|
||||
"github.com/gulien/gotenberg/app/logger"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -28,29 +33,47 @@ const defaultConfigurationFilePath = "gotenberg.yml"
|
||||
// main initializes the application, starts it, and handles
|
||||
// graceful shutdown.
|
||||
func main() {
|
||||
a, err := app.NewApp(version, defaultConfigurationFilePath)
|
||||
c, err := config.NewAppConfig(defaultConfigurationFilePath)
|
||||
if err != nil {
|
||||
logger.SetLevel(logrus.InfoLevel)
|
||||
logger.Fatal(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// defines our application logging.
|
||||
logger.SetLevel(c.Logs.Level)
|
||||
logger.SetFormatter(c.Logs.Formatter)
|
||||
|
||||
// defines our application router.
|
||||
r := mux.NewRouter()
|
||||
r.Handle("/", app.GetHandlersChain()).Methods(http.MethodPost)
|
||||
|
||||
// defines our server.
|
||||
s := &http.Server{
|
||||
Addr: fmt.Sprintf(":%s", c.Port),
|
||||
Handler: r,
|
||||
}
|
||||
|
||||
process.Load(c.CommandsConfig)
|
||||
logger.Infof("Starting Gotenberg version %s", version)
|
||||
logger.Infof("Listening on port %s", c.Port)
|
||||
|
||||
// runs our server in a goroutine so that it doesn't block.
|
||||
go func() {
|
||||
if err = a.Run(); err != nil {
|
||||
if err = s.ListenAndServe(); err != nil {
|
||||
logger.SetLevel(logrus.InfoLevel)
|
||||
logger.Panic(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
quit := make(chan os.Signal, 1)
|
||||
// we'll accept graceful shutdowns when quit via SIGINT (Ctrl+C)
|
||||
// SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught.
|
||||
signal.Notify(c, os.Interrupt)
|
||||
signal.Notify(quit, os.Interrupt)
|
||||
|
||||
// blocks until we receive our signal.
|
||||
<-c
|
||||
<-quit
|
||||
|
||||
// creates a deadline to wait for.
|
||||
var wait time.Duration
|
||||
@@ -59,7 +82,7 @@ func main() {
|
||||
|
||||
// doesn't block if no connections, but will otherwise wait
|
||||
// until the timeout deadline.
|
||||
a.Server.Shutdown(ctx)
|
||||
s.Shutdown(ctx)
|
||||
logger.SetLevel(logrus.InfoLevel)
|
||||
|
||||
logger.Info("Bye!")
|
||||
|
||||
Reference in New Issue
Block a user