Files
sm-bm-plugin/console/materials_reindex_test.go
Jakub Zych ffd19342b2 feat: keep the materials index in sync and add bm:materials-reindex
- the materials admin syncs after create and update and removes after
  delete, once the save transaction commits (UseMaterialIndex, set at Boot)
- POST and PUT /api/v1/bm/materials sync after the transaction, DELETE
  removes the document (its statement has no primary key, so no callback)
- bm:materials-reindex [--fresh] [--if-configured] ensures the index and
  syncs every material; it refuses without an engine and runs nightly
  at 04:15 with --if-configured
- TestMaterialSearchAgainstDatabase (Postgres) is written for the final
  verification phase
2026-11-08 19:50:50 +01:00

90 lines
3.0 KiB
Go

package console_test
import (
"bytes"
"context"
"net/http"
"slices"
"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())
}
}