mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-17 20:52:14 +01:00
feat: add more PDF formats, drastically improve LibreOffice long-running instance management
This commit is contained in:
@@ -1,68 +1,14 @@
|
||||
package libreoffice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/gotenberg/gotenberg/v7/pkg/gotenberg"
|
||||
"github.com/gotenberg/gotenberg/v7/pkg/modules/libreoffice/unoconv"
|
||||
"go.uber.org/zap"
|
||||
"github.com/gotenberg/gotenberg/v7/pkg/modules/libreoffice/uno"
|
||||
)
|
||||
|
||||
type ProtoModule struct {
|
||||
descriptor func() gotenberg.ModuleDescriptor
|
||||
}
|
||||
|
||||
func (mod ProtoModule) Descriptor() gotenberg.ModuleDescriptor {
|
||||
return mod.descriptor()
|
||||
}
|
||||
|
||||
type ProtoUnoconvProvider struct {
|
||||
ProtoModule
|
||||
unoconv func() (unoconv.API, error)
|
||||
}
|
||||
|
||||
func (mod ProtoUnoconvProvider) Unoconv() (unoconv.API, error) {
|
||||
return mod.unoconv()
|
||||
}
|
||||
|
||||
type ProtoUnoconvAPI struct {
|
||||
pdf func(_ context.Context, _ *zap.Logger, _, _ string, _ unoconv.Options) error
|
||||
extensions func() []string
|
||||
}
|
||||
|
||||
func (mod ProtoUnoconvAPI) PDF(ctx context.Context, logger *zap.Logger, inputPath, outputPath string, options unoconv.Options) error {
|
||||
return mod.pdf(ctx, logger, inputPath, outputPath, options)
|
||||
}
|
||||
|
||||
func (mod ProtoUnoconvAPI) Extensions() []string {
|
||||
return mod.extensions()
|
||||
}
|
||||
|
||||
type ProtoPDFEngineProvider struct {
|
||||
ProtoModule
|
||||
pdfEngine func() (gotenberg.PDFEngine, error)
|
||||
}
|
||||
|
||||
func (mod ProtoPDFEngineProvider) PDFEngine() (gotenberg.PDFEngine, error) {
|
||||
return mod.pdfEngine()
|
||||
}
|
||||
|
||||
type ProtoPDFEngine struct {
|
||||
merge func(_ context.Context, _ *zap.Logger, _ []string, _ string) error
|
||||
convert func(_ context.Context, _ *zap.Logger, _, _, _ string) error
|
||||
}
|
||||
|
||||
func (mod ProtoPDFEngine) Merge(ctx context.Context, logger *zap.Logger, inputPaths []string, outputPath string) error {
|
||||
return mod.merge(ctx, logger, inputPaths, outputPath)
|
||||
}
|
||||
|
||||
func (mod ProtoPDFEngine) Convert(ctx context.Context, logger *zap.Logger, format, inputPath, outputPath string) error {
|
||||
return mod.convert(ctx, logger, format, inputPath, outputPath)
|
||||
}
|
||||
|
||||
func TestLibreOffice_Descriptor(t *testing.T) {
|
||||
descriptor := LibreOffice{}.Descriptor()
|
||||
|
||||
@@ -75,15 +21,38 @@ func TestLibreOffice_Descriptor(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLibreOffice_Provision(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
ctx *gotenberg.Context
|
||||
expectErr bool
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx *gotenberg.Context
|
||||
expectProvisionErr bool
|
||||
}{
|
||||
{
|
||||
name: "nominal behavior",
|
||||
ctx: func() *gotenberg.Context {
|
||||
mod := struct{ ProtoModule }{}
|
||||
mod.descriptor = func() gotenberg.ModuleDescriptor {
|
||||
return gotenberg.ModuleDescriptor{ID: "foo", New: func() gotenberg.Module { return mod }}
|
||||
provider1 := struct {
|
||||
gotenberg.ModuleMock
|
||||
uno.ProviderMock
|
||||
}{}
|
||||
provider1.DescriptorMock = func() gotenberg.ModuleDescriptor {
|
||||
return gotenberg.ModuleDescriptor{ID: "foo", New: func() gotenberg.Module {
|
||||
return provider1
|
||||
}}
|
||||
}
|
||||
provider1.UNOMock = func() (uno.API, error) {
|
||||
return uno.APIMock{}, nil
|
||||
}
|
||||
|
||||
provider2 := struct {
|
||||
gotenberg.ModuleMock
|
||||
gotenberg.PDFEngineProviderMock
|
||||
}{}
|
||||
provider2.DescriptorMock = func() gotenberg.ModuleDescriptor {
|
||||
return gotenberg.ModuleDescriptor{ID: "bar", New: func() gotenberg.Module {
|
||||
return provider2
|
||||
}}
|
||||
}
|
||||
provider2.PDFEngineMock = func() (gotenberg.PDFEngine, error) {
|
||||
return gotenberg.PDFEngineMock{}, nil
|
||||
}
|
||||
|
||||
return gotenberg.NewContext(
|
||||
@@ -91,41 +60,36 @@ func TestLibreOffice_Provision(t *testing.T) {
|
||||
FlagSet: new(LibreOffice).Descriptor().FlagSet,
|
||||
},
|
||||
[]gotenberg.ModuleDescriptor{
|
||||
mod.Descriptor(),
|
||||
provider1.Descriptor(),
|
||||
provider2.Descriptor(),
|
||||
},
|
||||
)
|
||||
}(),
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
ctx: func() *gotenberg.Context {
|
||||
mod := struct{ ProtoUnoconvProvider }{}
|
||||
mod.descriptor = func() gotenberg.ModuleDescriptor {
|
||||
return gotenberg.ModuleDescriptor{ID: "foo", New: func() gotenberg.Module { return mod }}
|
||||
}
|
||||
mod.unoconv = func() (unoconv.API, error) {
|
||||
return nil, errors.New("foo")
|
||||
}
|
||||
|
||||
return gotenberg.NewContext(
|
||||
gotenberg.ParsedFlags{
|
||||
FlagSet: new(LibreOffice).Descriptor().FlagSet,
|
||||
},
|
||||
[]gotenberg.ModuleDescriptor{
|
||||
mod.Descriptor(),
|
||||
},
|
||||
)
|
||||
}(),
|
||||
expectErr: true,
|
||||
name: "no UNO API provider",
|
||||
ctx: gotenberg.NewContext(
|
||||
gotenberg.ParsedFlags{
|
||||
FlagSet: new(LibreOffice).Descriptor().FlagSet,
|
||||
},
|
||||
[]gotenberg.ModuleDescriptor{},
|
||||
),
|
||||
expectProvisionErr: true,
|
||||
},
|
||||
{
|
||||
name: "no API from UNO API provider",
|
||||
ctx: func() *gotenberg.Context {
|
||||
mod := struct{ ProtoUnoconvProvider }{}
|
||||
mod.descriptor = func() gotenberg.ModuleDescriptor {
|
||||
return gotenberg.ModuleDescriptor{ID: "foo", New: func() gotenberg.Module { return mod }}
|
||||
provider := struct {
|
||||
gotenberg.ModuleMock
|
||||
uno.ProviderMock
|
||||
}{}
|
||||
provider.DescriptorMock = func() gotenberg.ModuleDescriptor {
|
||||
return gotenberg.ModuleDescriptor{ID: "foo", New: func() gotenberg.Module {
|
||||
return provider
|
||||
}}
|
||||
}
|
||||
mod.unoconv = func() (unoconv.API, error) {
|
||||
return struct{ ProtoUnoconvAPI }{}, nil
|
||||
provider.UNOMock = func() (uno.API, error) {
|
||||
return uno.APIMock{}, errors.New("foo")
|
||||
}
|
||||
|
||||
return gotenberg.NewContext(
|
||||
@@ -133,28 +97,26 @@ func TestLibreOffice_Provision(t *testing.T) {
|
||||
FlagSet: new(LibreOffice).Descriptor().FlagSet,
|
||||
},
|
||||
[]gotenberg.ModuleDescriptor{
|
||||
mod.Descriptor(),
|
||||
provider.Descriptor(),
|
||||
},
|
||||
)
|
||||
}(),
|
||||
expectErr: true,
|
||||
expectProvisionErr: true,
|
||||
},
|
||||
{
|
||||
name: "no PDF engine provider",
|
||||
ctx: func() *gotenberg.Context {
|
||||
mod1 := struct{ ProtoUnoconvProvider }{}
|
||||
mod1.descriptor = func() gotenberg.ModuleDescriptor {
|
||||
return gotenberg.ModuleDescriptor{ID: "foo", New: func() gotenberg.Module { return mod1 }}
|
||||
provider := struct {
|
||||
gotenberg.ModuleMock
|
||||
uno.ProviderMock
|
||||
}{}
|
||||
provider.DescriptorMock = func() gotenberg.ModuleDescriptor {
|
||||
return gotenberg.ModuleDescriptor{ID: "foo", New: func() gotenberg.Module {
|
||||
return provider
|
||||
}}
|
||||
}
|
||||
mod1.unoconv = func() (unoconv.API, error) {
|
||||
return struct{ ProtoUnoconvAPI }{}, nil
|
||||
}
|
||||
|
||||
mod2 := struct{ ProtoPDFEngineProvider }{}
|
||||
mod2.descriptor = func() gotenberg.ModuleDescriptor {
|
||||
return gotenberg.ModuleDescriptor{ID: "bar", New: func() gotenberg.Module { return mod2 }}
|
||||
}
|
||||
mod2.pdfEngine = func() (gotenberg.PDFEngine, error) {
|
||||
return nil, errors.New("foo")
|
||||
provider.UNOMock = func() (uno.API, error) {
|
||||
return uno.APIMock{}, nil
|
||||
}
|
||||
|
||||
return gotenberg.NewContext(
|
||||
@@ -162,29 +124,39 @@ func TestLibreOffice_Provision(t *testing.T) {
|
||||
FlagSet: new(LibreOffice).Descriptor().FlagSet,
|
||||
},
|
||||
[]gotenberg.ModuleDescriptor{
|
||||
mod1.Descriptor(),
|
||||
mod2.Descriptor(),
|
||||
provider.Descriptor(),
|
||||
},
|
||||
)
|
||||
}(),
|
||||
expectErr: true,
|
||||
expectProvisionErr: true,
|
||||
},
|
||||
{
|
||||
name: "no PDF engine from PDF engine provider",
|
||||
ctx: func() *gotenberg.Context {
|
||||
mod1 := struct{ ProtoUnoconvProvider }{}
|
||||
mod1.descriptor = func() gotenberg.ModuleDescriptor {
|
||||
return gotenberg.ModuleDescriptor{ID: "foo", New: func() gotenberg.Module { return mod1 }}
|
||||
provider1 := struct {
|
||||
gotenberg.ModuleMock
|
||||
uno.ProviderMock
|
||||
}{}
|
||||
provider1.DescriptorMock = func() gotenberg.ModuleDescriptor {
|
||||
return gotenberg.ModuleDescriptor{ID: "foo", New: func() gotenberg.Module {
|
||||
return provider1
|
||||
}}
|
||||
}
|
||||
mod1.unoconv = func() (unoconv.API, error) {
|
||||
return struct{ ProtoUnoconvAPI }{}, nil
|
||||
provider1.UNOMock = func() (uno.API, error) {
|
||||
return uno.APIMock{}, nil
|
||||
}
|
||||
|
||||
mod2 := struct{ ProtoPDFEngineProvider }{}
|
||||
mod2.descriptor = func() gotenberg.ModuleDescriptor {
|
||||
return gotenberg.ModuleDescriptor{ID: "bar", New: func() gotenberg.Module { return mod2 }}
|
||||
provider2 := struct {
|
||||
gotenberg.ModuleMock
|
||||
gotenberg.PDFEngineProviderMock
|
||||
}{}
|
||||
provider2.DescriptorMock = func() gotenberg.ModuleDescriptor {
|
||||
return gotenberg.ModuleDescriptor{ID: "bar", New: func() gotenberg.Module {
|
||||
return provider2
|
||||
}}
|
||||
}
|
||||
mod2.pdfEngine = func() (gotenberg.PDFEngine, error) {
|
||||
return struct{ ProtoPDFEngine }{}, nil
|
||||
provider2.PDFEngineMock = func() (gotenberg.PDFEngine, error) {
|
||||
return gotenberg.PDFEngineMock{}, errors.New("foo")
|
||||
}
|
||||
|
||||
return gotenberg.NewContext(
|
||||
@@ -192,59 +164,60 @@ func TestLibreOffice_Provision(t *testing.T) {
|
||||
FlagSet: new(LibreOffice).Descriptor().FlagSet,
|
||||
},
|
||||
[]gotenberg.ModuleDescriptor{
|
||||
mod1.Descriptor(),
|
||||
mod2.Descriptor(),
|
||||
provider1.Descriptor(),
|
||||
provider2.Descriptor(),
|
||||
},
|
||||
)
|
||||
}(),
|
||||
expectProvisionErr: true,
|
||||
},
|
||||
} {
|
||||
mod := new(LibreOffice)
|
||||
err := mod.Provision(tc.ctx)
|
||||
}
|
||||
|
||||
if tc.expectErr && err == nil {
|
||||
t.Errorf("test %d: expected error but got: %v", i, err)
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mod := new(LibreOffice)
|
||||
err := mod.Provision(tc.ctx)
|
||||
|
||||
if !tc.expectErr && err != nil {
|
||||
t.Errorf("test %d: expected no error but got: %v", i, err)
|
||||
}
|
||||
if tc.expectProvisionErr && err == nil {
|
||||
t.Error("expected mod.Provision() error, but got none")
|
||||
}
|
||||
|
||||
if !tc.expectProvisionErr && err != nil {
|
||||
t.Errorf("expected no error from mod.Provision(), but got: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLibreOffice_Routes(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
expectRoutes int
|
||||
disableRoutes bool
|
||||
tests := []struct {
|
||||
name string
|
||||
mod LibreOffice
|
||||
expectRoutesCount int
|
||||
}{
|
||||
{
|
||||
expectRoutes: 1,
|
||||
name: "route not disabled",
|
||||
mod: LibreOffice{},
|
||||
expectRoutesCount: 1,
|
||||
},
|
||||
{
|
||||
disableRoutes: true,
|
||||
name: "route disabled",
|
||||
mod: LibreOffice{
|
||||
disableRoutes: true,
|
||||
},
|
||||
},
|
||||
} {
|
||||
mod := new(LibreOffice)
|
||||
mod.disableRoutes = tc.disableRoutes
|
||||
}
|
||||
|
||||
routes, err := mod.Routes()
|
||||
if err != nil {
|
||||
t.Fatalf("test %d: expected no error but got: %v", i, err)
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
routes, err := tc.mod.Routes()
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error from mod.Routes(), but got: %v", err)
|
||||
}
|
||||
|
||||
if tc.expectRoutes != len(routes) {
|
||||
t.Errorf("test %d: expected %d routes but got %d", i, tc.expectRoutes, len(routes))
|
||||
}
|
||||
if tc.expectRoutesCount != len(routes) {
|
||||
t.Errorf("expected %d routes from mod.Routes(), but got %d", tc.expectRoutesCount, len(routes))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Interface guards.
|
||||
var (
|
||||
_ gotenberg.Module = (*ProtoModule)(nil)
|
||||
_ unoconv.Provider = (*ProtoUnoconvProvider)(nil)
|
||||
_ gotenberg.Module = (*ProtoUnoconvProvider)(nil)
|
||||
_ unoconv.API = (*ProtoUnoconvAPI)(nil)
|
||||
_ gotenberg.PDFEngineProvider = (*ProtoPDFEngineProvider)(nil)
|
||||
_ gotenberg.Module = (*ProtoPDFEngineProvider)(nil)
|
||||
_ gotenberg.PDFEngine = (*ProtoPDFEngine)(nil)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user