- inline and signed uploads queue the text job in their transaction - the admin form queues it in the save transaction - detach resyncs the material (its text row goes by cascade) - bm:materials-reindex backfills missing PDF texts before each sync
172 lines
6.0 KiB
Go
172 lines
6.0 KiB
Go
package console_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/bonfire"
|
|
"git.golem15.com/golem15/summercms/modules/lagoon"
|
|
"git.golem15.com/jakub/sm-bm-plugin/classes"
|
|
"git.golem15.com/jakub/sm-bm-plugin/console"
|
|
"git.golem15.com/jakub/sm-bm-plugin/internal/searchtest"
|
|
"gorm.io/driver/postgres"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func TestMaterialsReindexRefuses(t *testing.T) {
|
|
asked := false
|
|
deps := console.MaterialsReindexDeps{
|
|
DB: func(context.Context) (*gorm.DB, func(), error) {
|
|
asked = true
|
|
return nil, func() {}, nil
|
|
},
|
|
Index: classes.NewMaterialIndexer(searchtest.App(t)),
|
|
}
|
|
cmds := []bonfire.Command{console.MaterialsReindexCommand(deps)}
|
|
var out bytes.Buffer
|
|
err := bonfire.Call(context.Background(), cmds, console.MaterialsReindexName, nil, &out)
|
|
if err == nil || !strings.Contains(err.Error(), "search.driver") || !strings.Contains(err.Error(), "search.typesense.api_key") {
|
|
t.Errorf("unconfigured engine: err = %v, want the configuration hint", err)
|
|
}
|
|
out.Reset()
|
|
if err := bonfire.Call(context.Background(), cmds, console.MaterialsReindexName, []string{"--if-configured"}, &out); err != nil {
|
|
t.Errorf("--if-configured: %v", err)
|
|
}
|
|
if !strings.Contains(out.String(), "not configured") {
|
|
t.Errorf("--if-configured output %q, want the notice", out.String())
|
|
}
|
|
if asked {
|
|
t.Error("the command opened the database without an engine")
|
|
}
|
|
}
|
|
|
|
func TestMaterialsReindexFresh(t *testing.T) {
|
|
stub := &searchtest.Stub{}
|
|
app := searchtest.App(t, searchtest.Start(t, stub)...)
|
|
gdb, err := gorm.Open(postgres.New(postgres.Config{DSN: "host=127.0.0.1"}), &gorm.Config{DryRun: true, DisableAutomaticPing: true})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sqlDB, err := gdb.DB()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := lagoon.Publish(app, sqlDB, gdb); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
deps := console.MaterialsReindexDeps{
|
|
DB: func(context.Context) (*gorm.DB, func(), error) { return gdb, func() {}, nil },
|
|
Index: classes.NewMaterialIndexer(app),
|
|
}
|
|
cmds := []bonfire.Command{console.MaterialsReindexCommand(deps)}
|
|
coll := "/collections/" + searchtest.Index
|
|
var out bytes.Buffer
|
|
for run := 1; run <= 2; run++ {
|
|
before := len(stub.Requests())
|
|
out.Reset()
|
|
if err := bonfire.Call(context.Background(), cmds, console.MaterialsReindexName, []string{"--fresh"}, &out); err != nil {
|
|
t.Fatalf("run %d: %v\n%s", run, err, out.String())
|
|
}
|
|
var sent []string
|
|
for _, r := range stub.Requests()[before:] {
|
|
sent = append(sent, r.Method+" "+r.Path)
|
|
}
|
|
if want := []string{http.MethodDelete + " " + coll, http.MethodGet + " " + coll, http.MethodPost + " /collections"}; !slices.Equal(sent, want) {
|
|
t.Errorf("run %d sent %v, want %v", run, sent, want)
|
|
}
|
|
if !strings.Contains(out.String(), "Reindexed: 0 synced, 0 failed") {
|
|
t.Errorf("run %d output %q", run, out.String())
|
|
}
|
|
}
|
|
// The second run dropped the collection the first one created.
|
|
if !strings.Contains(out.String(), "Dropped "+searchtest.Index) {
|
|
t.Errorf("second run output %q, want Dropped", out.String())
|
|
}
|
|
}
|
|
|
|
func TestMaterialsReindexBackfill(t *testing.T) {
|
|
ctx := context.Background()
|
|
var calls []string
|
|
var synced []uint
|
|
sync := func(id uint) error {
|
|
calls = append(calls, "sync "+strconv.FormatUint(uint64(id), 10))
|
|
synced = append(synced, id)
|
|
return nil
|
|
}
|
|
|
|
// pdftotext missing on the first material: one warning, no more
|
|
// backfill calls, and every material is still synced.
|
|
deps := console.MaterialsReindexDeps{Backfill: func(_ context.Context, _ *gorm.DB, id uint) error {
|
|
calls = append(calls, "backfill "+strconv.FormatUint(uint64(id), 10))
|
|
return classes.ErrPDFToolMissing
|
|
}}
|
|
var stdout, stderr bytes.Buffer
|
|
run := console.NewReindexRun(deps, sync, bonfire.NewOutput(strings.NewReader(""), &stdout, &stderr))
|
|
for _, id := range []uint{1, 2, 3} {
|
|
run.Material(ctx, nil, id)
|
|
}
|
|
if want := []string{"backfill 1", "sync 1", "sync 2", "sync 3"}; !slices.Equal(calls, want) {
|
|
t.Errorf("calls = %v, want %v", calls, want)
|
|
}
|
|
if !slices.Equal(synced, []uint{1, 2, 3}) {
|
|
t.Errorf("synced = %v, want all three", synced)
|
|
}
|
|
all := stdout.String() + stderr.String()
|
|
if n := strings.Count(all, "pdftotext not found"); n != 1 {
|
|
t.Errorf("the tool warning was printed %d times, want once:\n%s", n, all)
|
|
}
|
|
if !strings.Contains(all, "install poppler-utils") {
|
|
t.Errorf("output %q, want the poppler-utils hint", all)
|
|
}
|
|
if s, f, noTool := run.Counts(); s != 3 || f != 0 || !noTool {
|
|
t.Errorf("counts = %d synced %d failed noTool %v, want 3 0 true", s, f, noTool)
|
|
}
|
|
|
|
// Another backfill error warns for that material only; it is still
|
|
// synced and the next material is backfilled. A failed sync counts.
|
|
calls, synced = nil, nil
|
|
stdout.Reset()
|
|
stderr.Reset()
|
|
deps.Backfill = func(_ context.Context, _ *gorm.DB, id uint) error {
|
|
calls = append(calls, "backfill "+strconv.FormatUint(uint64(id), 10))
|
|
if id == 1 {
|
|
return errors.New("blob gone")
|
|
}
|
|
return nil
|
|
}
|
|
failing := func(id uint) error {
|
|
if err := sync(id); err != nil || id == 3 {
|
|
return errors.New("engine down")
|
|
}
|
|
return nil
|
|
}
|
|
run = console.NewReindexRun(deps, failing, bonfire.NewOutput(strings.NewReader(""), &stdout, &stderr))
|
|
for _, id := range []uint{1, 2, 3} {
|
|
run.Material(ctx, nil, id)
|
|
}
|
|
if want := []string{"backfill 1", "sync 1", "backfill 2", "sync 2", "backfill 3", "sync 3"}; !slices.Equal(calls, want) {
|
|
t.Errorf("calls = %v, want %v", calls, want)
|
|
}
|
|
all = stdout.String() + stderr.String()
|
|
if !strings.Contains(all, "material 1: file text: blob gone") || strings.Contains(all, "pdftotext not found") {
|
|
t.Errorf("output %q, want the material 1 file text warning only", all)
|
|
}
|
|
if s, f, noTool := run.Counts(); s != 2 || f != 1 || noTool {
|
|
t.Errorf("counts = %d synced %d failed noTool %v, want 2 1 false", s, f, noTool)
|
|
}
|
|
|
|
// No Backfill: only syncs.
|
|
calls = nil
|
|
run = console.NewReindexRun(console.MaterialsReindexDeps{}, sync, bonfire.NewOutput(strings.NewReader(""), &stdout, &stderr))
|
|
run.Material(ctx, nil, 9)
|
|
if !slices.Equal(calls, []string{"sync 9"}) {
|
|
t.Errorf("calls without Backfill = %v", calls)
|
|
}
|
|
}
|