Files
sm-bm-plugin/controllers/materials_hooks_test.go
Jakub Zych f841e8ed71 feat: extract file text on every attach path and backfill on reindex
- 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
2026-11-08 20:16:14 +01:00

89 lines
2.6 KiB
Go

package controllers
import (
"context"
"slices"
"strconv"
"testing"
"git.golem15.com/jakub/sm-bm-plugin/models"
"gorm.io/gorm"
)
// recordingIndex records the after-commit calls of the materials admin.
type recordingIndex struct {
calls []string
txs []*gorm.DB
}
func (r *recordingIndex) SyncAfterCommit(_ context.Context, tx *gorm.DB, id uint) {
r.calls = append(r.calls, "sync "+itoa(id))
r.txs = append(r.txs, tx)
}
func (r *recordingIndex) RemoveAfterCommit(_ context.Context, tx *gorm.DB, id uint) {
r.calls = append(r.calls, "remove "+itoa(id))
r.txs = append(r.txs, tx)
}
func (r *recordingIndex) QueueExtract(_ context.Context, tx *gorm.DB, id uint) {
r.calls = append(r.calls, "queue "+itoa(id))
r.txs = append(r.txs, tx)
}
func itoa(id uint) string { return strconv.FormatUint(uint64(id), 10) }
func TestMaterialsAdminHooks(t *testing.T) {
t.Cleanup(func() { UseMaterialIndex(nil) })
var c materialsAdmin
ctx := context.Background()
m := &models.Material{ID: 7}
// No index installed: nothing to do, and no error.
UseMaterialIndex(nil)
for name, fn := range map[string]func(context.Context, any) error{"create": c.FormAfterCreate, "update": c.FormAfterUpdate, "delete": c.FormAfterDelete} {
if err := fn(ctx, m); err != nil {
t.Errorf("%s without an index: %v", name, err)
}
}
rec := &recordingIndex{}
UseMaterialIndex(rec)
if err := c.FormAfterCreate(ctx, m); err != nil {
t.Fatal(err)
}
if err := c.FormAfterUpdate(ctx, m); err != nil {
t.Fatal(err)
}
if err := c.FormAfterDelete(ctx, m); err != nil {
t.Fatal(err)
}
// Create and update queue the file text job (in the save transaction)
// before the after-commit sync; delete queues nothing.
if want := []string{"queue 7", "sync 7", "queue 7", "sync 7", "remove 7"}; !slices.Equal(rec.calls, want) {
t.Errorf("calls = %v, want %v", rec.calls, want)
}
// Outside a cabana write there is no transaction: the index then runs
// at once (lagoon.AfterCommit with a nil handle).
for i, tx := range rec.txs {
if tx != nil {
t.Errorf("call %d got a transaction outside a write", i)
}
}
// A model of another type is an error and schedules nothing.
rec.calls = nil
for name, fn := range map[string]func(context.Context, any) error{"create": c.FormAfterCreate, "update": c.FormAfterUpdate, "delete": c.FormAfterDelete} {
if err := fn(ctx, &models.Course{ID: 7}); err == nil {
t.Errorf("%s with a course: no error", name)
}
}
var nilMaterial *models.Material
if err := c.FormAfterUpdate(ctx, nilMaterial); err == nil {
t.Error("update with a nil material: no error")
}
if len(rec.calls) != 0 {
t.Errorf("calls for a wrong model = %v", rec.calls)
}
}