- jz_bm_material_file_texts caches the pdftotext output per file - the River job jz.bm.extract_material_text extracts and resyncs - the management multipart attach queues it inside its transaction - file_texts in the document and a file snippet with its name
135 lines
5.0 KiB
Go
135 lines
5.0 KiB
Go
package models_test
|
|
|
|
import (
|
|
"slices"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.golem15.com/jakub/sm-bm-plugin/models"
|
|
)
|
|
|
|
func TestMaterialSearchSchema(t *testing.T) {
|
|
m := models.Material{}
|
|
if got := m.SearchableAs(); got != "jz_bm_materials" {
|
|
t.Errorf("SearchableAs = %q, want jz_bm_materials", got)
|
|
}
|
|
fields, ok := m.SearchIndexSchema()["fields"].([]map[string]any)
|
|
if !ok || len(fields) == 0 {
|
|
t.Fatalf("schema fields = %#v", m.SearchIndexSchema()["fields"])
|
|
}
|
|
byName := map[string]map[string]any{}
|
|
for _, f := range fields {
|
|
name, _ := f["name"].(string)
|
|
typ, _ := f["type"].(string)
|
|
if name == "" || typ == "" {
|
|
t.Errorf("field without a name or type: %v", f)
|
|
}
|
|
if _, ok := f["locale"]; ok {
|
|
t.Errorf("field %s sets a locale; the default one folds Polish diacritics", name)
|
|
}
|
|
if store, ok := f["store"]; ok && store == false {
|
|
t.Errorf("field %s turns storage off", name)
|
|
}
|
|
byName[name] = f
|
|
}
|
|
for _, name := range []string{"body", "file_texts"} {
|
|
f, ok := byName[name]
|
|
if !ok {
|
|
t.Errorf("schema has no %s field", name)
|
|
continue
|
|
}
|
|
if f["optional"] != true {
|
|
t.Errorf("%s is not optional", name)
|
|
}
|
|
}
|
|
if byName["body"]["type"] != "string" || byName["file_texts"]["type"] != "string[]" {
|
|
t.Errorf("body %v, file_texts %v: want string and string[]", byName["body"]["type"], byName["file_texts"]["type"])
|
|
}
|
|
for _, name := range []string{"id", "title", "source", "url", "tags", "tag_slugs", "course", "course_id", "meeting_id", "meeting_number", "type", "published_at"} {
|
|
if _, ok := byName[name]; !ok {
|
|
t.Errorf("schema has no %s field", name)
|
|
}
|
|
}
|
|
if !(&m).ShouldBeSearchable() {
|
|
t.Error("a material is not searchable")
|
|
}
|
|
var nilMaterial *models.Material
|
|
if nilMaterial.ShouldBeSearchable() {
|
|
t.Error("a nil material is searchable")
|
|
}
|
|
}
|
|
|
|
func TestMaterialDocument(t *testing.T) {
|
|
plain := models.MaterialPlainBody("# Nagłówek\n\nTekst **pogrubienie** i _kursywa_ oraz [tekst](https://x) i .\n\n> cytat\n> dalej\n\n- punkt `kod` snake_case")
|
|
want := "Nagłówek Tekst pogrubienie i kursywa oraz tekst i obraz. cytat dalej punkt kod snake_case"
|
|
if plain != want {
|
|
t.Errorf("MaterialPlainBody =\n%q\nwant\n%q", plain, want)
|
|
}
|
|
|
|
code := "BM-12"
|
|
course := &models.Course{ID: 4, Name: "Bezpieczeństwo", Code: &code}
|
|
courseID, meetingID := uint(4), uint(9)
|
|
at := time.Date(2026, 10, 6, 10, 0, 0, 0, time.UTC)
|
|
body := "## Wstęp\n\n**Globalizacja** w praktyce"
|
|
source := "Giddens"
|
|
m := models.Material{ID: 12, Title: "Globalizacja", Type: "notatka", CourseID: &courseID, MeetingID: &meetingID, PublishedAt: &at, Body: &body, Source: &source}
|
|
tags := []models.Tag{{Name: "Globalizacja", Slug: "globalizacja"}, {Name: "Polityka", Slug: "polityka"}}
|
|
doc := models.MaterialDocument(m, course, 3, tags, []string{"tekst pliku"})
|
|
checks := map[string]any{
|
|
"id": "12",
|
|
"title": "Globalizacja",
|
|
"type": "notatka",
|
|
"source": "Giddens",
|
|
"course": "Bezpieczeństwo BM-12",
|
|
"course_id": int64(4),
|
|
"meeting_id": int64(9),
|
|
"meeting_number": 3,
|
|
"published_at": at.Unix(),
|
|
"body": "Wstęp Globalizacja w praktyce",
|
|
}
|
|
for k, v := range checks {
|
|
if doc[k] != v {
|
|
t.Errorf("doc[%s] = %#v, want %#v", k, doc[k], v)
|
|
}
|
|
}
|
|
if got, _ := doc["tags"].([]string); !slices.Equal(got, []string{"Globalizacja", "Polityka"}) {
|
|
t.Errorf("tags = %v", doc["tags"])
|
|
}
|
|
if got, _ := doc["tag_slugs"].([]string); !slices.Equal(got, []string{"globalizacja", "polityka"}) {
|
|
t.Errorf("tag_slugs = %v", doc["tag_slugs"])
|
|
}
|
|
if got, _ := doc["file_texts"].([]string); !slices.Equal(got, []string{"tekst pliku"}) {
|
|
t.Errorf("file_texts = %v", doc["file_texts"])
|
|
}
|
|
|
|
// File texts keep their order (the IndexedFiles order); an empty list
|
|
// leaves the key out.
|
|
ordered := models.MaterialDocument(m, course, 3, tags, []string{"pierwszy plik", "drugi plik", "trzeci plik"})
|
|
if got, _ := ordered["file_texts"].([]string); !slices.Equal(got, []string{"pierwszy plik", "drugi plik", "trzeci plik"}) {
|
|
t.Errorf("file_texts = %v, want the three texts in order", ordered["file_texts"])
|
|
}
|
|
if _, ok := models.MaterialDocument(m, course, 3, tags, []string{})["file_texts"]; ok {
|
|
t.Error("an empty file text list carries file_texts")
|
|
}
|
|
|
|
// No code, no course id, no meeting, a draft, no files.
|
|
bare := models.MaterialDocument(models.Material{ID: 5, Title: "Szkic", Type: "inne"}, &models.Course{Name: "Teoria"}, 0, nil, nil)
|
|
if bare["course"] != "Teoria" {
|
|
t.Errorf("course without code = %v", bare["course"])
|
|
}
|
|
for _, k := range []string{"course_id", "meeting_id", "published_at"} {
|
|
if bare[k] != int64(0) {
|
|
t.Errorf("bare %s = %#v, want 0", k, bare[k])
|
|
}
|
|
}
|
|
for _, k := range []string{"meeting_number", "file_texts", "body", "source", "url"} {
|
|
if _, ok := bare[k]; ok {
|
|
t.Errorf("bare document carries %s", k)
|
|
}
|
|
}
|
|
none := models.MaterialDocument(models.Material{ID: 6, Title: "X", Type: "link"}, nil, 0, nil, nil)
|
|
if _, ok := none["course"]; ok {
|
|
t.Error("a material without a course carries course")
|
|
}
|
|
}
|