mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-07-02 08:27:41 +08:00
170 lines
4.8 KiB
Go
170 lines
4.8 KiB
Go
package pdfengines
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"go.opentelemetry.io/otel"
|
|
"go.opentelemetry.io/otel/codes"
|
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
|
"go.opentelemetry.io/otel/sdk/trace/tracetest"
|
|
|
|
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
|
|
)
|
|
|
|
func newFallbackRecorder(t *testing.T) *tracetest.SpanRecorder {
|
|
t.Helper()
|
|
recorder := tracetest.NewSpanRecorder()
|
|
provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(recorder))
|
|
previous := otel.GetTracerProvider()
|
|
otel.SetTracerProvider(provider)
|
|
t.Cleanup(func() { otel.SetTracerProvider(previous) })
|
|
return recorder
|
|
}
|
|
|
|
func findFallbackSpan(recorder *tracetest.SpanRecorder, name string) sdktrace.ReadOnlySpan {
|
|
for _, s := range recorder.Ended() {
|
|
if s.Name() == name {
|
|
return s
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func wrapTest(err error) error { return fmt.Errorf("test op with multi PDF engines: %w", err) }
|
|
|
|
func TestRunWithFallback_FirstSucceeds(t *testing.T) {
|
|
recorder := newFallbackRecorder(t)
|
|
engines := []gotenberg.PdfEngine{&gotenberg.PdfEngineMock{}, &gotenberg.PdfEngineMock{}}
|
|
|
|
calls := 0
|
|
got, err := runWithFallback(context.Background(), "pdfengines.Test", engines,
|
|
func(_ context.Context, _ gotenberg.PdfEngine) (string, error) {
|
|
calls++
|
|
return "ok", nil
|
|
}, wrapTest)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got != "ok" {
|
|
t.Errorf("got %q, want ok", got)
|
|
}
|
|
if calls != 1 {
|
|
t.Errorf("expected 1 engine call, got %d", calls)
|
|
}
|
|
|
|
span := findFallbackSpan(recorder, "pdfengines.Test")
|
|
if span.Status().Code != codes.Ok {
|
|
t.Errorf("status = %v, want Ok", span.Status().Code)
|
|
}
|
|
attrs := map[string]string{}
|
|
for _, kv := range span.Attributes() {
|
|
attrs[string(kv.Key)] = kv.Value.Emit()
|
|
}
|
|
if attrs["gotenberg.pdf_engine.attempts"] != "1" {
|
|
t.Errorf("attempts = %q, want 1", attrs["gotenberg.pdf_engine.attempts"])
|
|
}
|
|
if attrs["gotenberg.pdf_engine.selected"] == "" {
|
|
t.Error("expected a selected engine attribute")
|
|
}
|
|
}
|
|
|
|
func TestRunWithFallback_SecondSucceeds(t *testing.T) {
|
|
recorder := newFallbackRecorder(t)
|
|
engines := []gotenberg.PdfEngine{&gotenberg.PdfEngineMock{}, &gotenberg.PdfEngineMock{}}
|
|
|
|
calls := 0
|
|
got, err := runWithFallback(context.Background(), "pdfengines.Test", engines,
|
|
func(_ context.Context, _ gotenberg.PdfEngine) (string, error) {
|
|
calls++
|
|
if calls == 1 {
|
|
return "", errors.New("first engine failed")
|
|
}
|
|
return "ok", nil
|
|
}, wrapTest)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got != "ok" || calls != 2 {
|
|
t.Errorf("got %q after %d calls, want ok after 2", got, calls)
|
|
}
|
|
|
|
span := findFallbackSpan(recorder, "pdfengines.Test")
|
|
var failedEvents int
|
|
for _, e := range span.Events() {
|
|
if e.Name == "pdf_engine.attempt_failed" {
|
|
failedEvents++
|
|
}
|
|
}
|
|
if failedEvents != 1 {
|
|
t.Errorf("expected 1 attempt_failed event, got %d", failedEvents)
|
|
}
|
|
for _, kv := range span.Attributes() {
|
|
if string(kv.Key) == "gotenberg.pdf_engine.attempts" && kv.Value.Emit() != "2" {
|
|
t.Errorf("attempts = %q, want 2", kv.Value.Emit())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRunWithFallback_AllFail(t *testing.T) {
|
|
recorder := newFallbackRecorder(t)
|
|
engines := []gotenberg.PdfEngine{&gotenberg.PdfEngineMock{}, &gotenberg.PdfEngineMock{}}
|
|
|
|
sentinel := errors.New("engine failed")
|
|
_, err := runWithFallback(context.Background(), "pdfengines.Test", engines,
|
|
func(_ context.Context, _ gotenberg.PdfEngine) (string, error) {
|
|
return "", sentinel
|
|
}, wrapTest)
|
|
|
|
if err == nil {
|
|
t.Fatal("expected an error when all engines fail")
|
|
}
|
|
if !errors.Is(err, sentinel) {
|
|
t.Errorf("expected the joined engine error to be wrapped, got %v", err)
|
|
}
|
|
|
|
span := findFallbackSpan(recorder, "pdfengines.Test")
|
|
if span.Status().Code != codes.Error {
|
|
t.Errorf("status = %v, want Error", span.Status().Code)
|
|
}
|
|
}
|
|
|
|
func TestRunWithFallback_ZeroEngines(t *testing.T) {
|
|
newFallbackRecorder(t)
|
|
_, err := runWithFallback(context.Background(), "pdfengines.Test", nil,
|
|
func(_ context.Context, _ gotenberg.PdfEngine) (string, error) {
|
|
return "ok", nil
|
|
}, wrapTest)
|
|
if err == nil {
|
|
t.Error("expected an error with no engines")
|
|
}
|
|
}
|
|
|
|
func TestRunWithFallback_ContextDone(t *testing.T) {
|
|
recorder := newFallbackRecorder(t)
|
|
engines := []gotenberg.PdfEngine{&gotenberg.PdfEngineMock{}}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
|
|
release := make(chan struct{})
|
|
t.Cleanup(func() { close(release) })
|
|
|
|
_, err := runWithFallback(ctx, "pdfengines.Test", engines,
|
|
func(_ context.Context, _ gotenberg.PdfEngine) (string, error) {
|
|
<-release // never returns during the call, forcing the ctx.Done branch
|
|
return "", nil
|
|
}, wrapTest)
|
|
|
|
if !errors.Is(err, context.Canceled) {
|
|
t.Errorf("expected context.Canceled, got %v", err)
|
|
}
|
|
|
|
span := findFallbackSpan(recorder, "pdfengines.Test")
|
|
if span.Status().Code != codes.Error {
|
|
t.Errorf("status = %v, want Error (the cancellation must mark the span)", span.Status().Code)
|
|
}
|
|
}
|