2026-05-22 20:26:11 -04:00
|
|
|
package embeddings
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"github.com/Silo-Server/silo-server/internal/models"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestBuildEmbeddingTextSortsCrewDeterministically(t *testing.T) {
|
|
|
|
|
item := &models.MediaItem{
|
|
|
|
|
Title: "Example",
|
|
|
|
|
Type: "movie",
|
|
|
|
|
People: []models.ItemPerson{
|
|
|
|
|
{Person: models.Person{ID: 3, Name: "Charlie"}, Kind: models.PersonKindWriter, SortOrder: 0},
|
|
|
|
|
{Person: models.Person{ID: 1, Name: "Alice"}, Kind: models.PersonKindWriter, SortOrder: 0},
|
|
|
|
|
{Person: models.Person{ID: 2, Name: "Bob"}, Kind: models.PersonKindWriter, SortOrder: 0},
|
|
|
|
|
{Person: models.Person{ID: 5, Name: "Zed"}, Kind: models.PersonKindDirector, SortOrder: 0},
|
|
|
|
|
{Person: models.Person{ID: 4, Name: "Ann"}, Kind: models.PersonKindDirector, SortOrder: 0},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
text := BuildEmbeddingText(item)
|
|
|
|
|
|
|
|
|
|
if !strings.Contains(text, "Directed by Ann, Zed") {
|
|
|
|
|
t.Fatalf("director order was not deterministic by name: %q", text)
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(text, "Written by Alice, Bob, Charlie") {
|
|
|
|
|
t.Fatalf("writer order was not deterministic by name: %q", text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-07 15:57:05 -04:00
|
|
|
func TestBuildEmbeddingTextAudiobookUsesAuthorAndNarrator(t *testing.T) {
|
|
|
|
|
item := &models.MediaItem{
|
|
|
|
|
Title: "The Ice Angels",
|
|
|
|
|
Type: "audiobook",
|
|
|
|
|
Year: 2026,
|
|
|
|
|
Genres: []string{"Thriller", "Mystery"},
|
|
|
|
|
Overview: "Detective Elea Baker hunts a child killer in Lincoln.",
|
|
|
|
|
People: []models.ItemPerson{
|
|
|
|
|
{Person: models.Person{ID: 1, Name: "Caroline Mitchell"}, Kind: models.PersonKindAuthor},
|
|
|
|
|
{Person: models.Person{ID: 2, Name: "Aryana Ramkhalawon"}, Kind: models.PersonKindNarrator},
|
|
|
|
|
// Cast/director/writer rows must be ignored for audiobooks.
|
|
|
|
|
{Person: models.Person{ID: 3, Name: "Bystander"}, Kind: models.PersonKindActor, Character: "Detective"},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
text := BuildEmbeddingText(item)
|
|
|
|
|
|
|
|
|
|
if !strings.Contains(text, "Thriller, Mystery audiobook about Detective Elea Baker") {
|
|
|
|
|
t.Fatalf("audiobook lede missing or wrong: %q", text)
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(text, "Written by Caroline Mitchell") {
|
|
|
|
|
t.Fatalf("author missing: %q", text)
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(text, "Narrated by Aryana Ramkhalawon") {
|
|
|
|
|
t.Fatalf("narrator missing: %q", text)
|
|
|
|
|
}
|
|
|
|
|
if strings.Contains(text, "Cast:") || strings.Contains(text, "Directed by") {
|
|
|
|
|
t.Fatalf("audiobook text should not contain cast/director credits: %q", text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-10 08:18:35 -04:00
|
|
|
func TestBuildEmbeddingTextEbookUsesAuthorWithoutNarratorOrCast(t *testing.T) {
|
|
|
|
|
item := &models.MediaItem{
|
|
|
|
|
Title: "A Wizard of Earthsea",
|
|
|
|
|
Type: "ebook",
|
|
|
|
|
Year: 1968,
|
|
|
|
|
Genres: []string{"Fantasy"},
|
|
|
|
|
Overview: "Ged learns the cost of power.",
|
|
|
|
|
People: []models.ItemPerson{
|
|
|
|
|
{Person: models.Person{ID: 1, Name: "Ursula K. Le Guin"}, Kind: models.PersonKindAuthor},
|
|
|
|
|
{Person: models.Person{ID: 2, Name: "Narrator Should Not Apply"}, Kind: models.PersonKindNarrator},
|
|
|
|
|
{Person: models.Person{ID: 3, Name: "Actor Should Not Apply"}, Kind: models.PersonKindActor, Character: "Ged"},
|
|
|
|
|
{Person: models.Person{ID: 4, Name: "Writer Should Not Apply"}, Kind: models.PersonKindWriter},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
text := BuildEmbeddingText(item)
|
|
|
|
|
|
|
|
|
|
if !strings.Contains(text, "Fantasy ebook about Ged learns the cost of power.") {
|
|
|
|
|
t.Fatalf("ebook lede missing or wrong: %q", text)
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(text, "Written by Ursula K. Le Guin") {
|
|
|
|
|
t.Fatalf("ebook author missing: %q", text)
|
|
|
|
|
}
|
|
|
|
|
for _, unwanted := range []string{
|
|
|
|
|
"Narrated by",
|
|
|
|
|
"Narrator Should Not Apply",
|
|
|
|
|
"Cast:",
|
|
|
|
|
"Actor Should Not Apply",
|
|
|
|
|
"Writer Should Not Apply",
|
|
|
|
|
} {
|
|
|
|
|
if strings.Contains(text, unwanted) {
|
|
|
|
|
t.Fatalf("ebook text should not contain %q: %q", unwanted, text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-22 20:26:11 -04:00
|
|
|
func TestBuildEmbeddingTextSortsActorsBeforeTopFive(t *testing.T) {
|
|
|
|
|
item := &models.MediaItem{
|
|
|
|
|
Title: "Example",
|
|
|
|
|
Type: "movie",
|
|
|
|
|
People: []models.ItemPerson{
|
|
|
|
|
{Person: models.Person{ID: 6, Name: "F"}, Kind: models.PersonKindActor, Character: "Six", SortOrder: 6},
|
|
|
|
|
{Person: models.Person{ID: 2, Name: "B"}, Kind: models.PersonKindActor, Character: "Two", SortOrder: 2},
|
|
|
|
|
{Person: models.Person{ID: 1, Name: "A"}, Kind: models.PersonKindActor, Character: "One", SortOrder: 1},
|
|
|
|
|
{Person: models.Person{ID: 5, Name: "E"}, Kind: models.PersonKindActor, Character: "Five", SortOrder: 5},
|
|
|
|
|
{Person: models.Person{ID: 4, Name: "D"}, Kind: models.PersonKindActor, Character: "Four", SortOrder: 4},
|
|
|
|
|
{Person: models.Person{ID: 3, Name: "C"}, Kind: models.PersonKindActor, Character: "Three", SortOrder: 3},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
text := BuildEmbeddingText(item)
|
|
|
|
|
|
|
|
|
|
if strings.Contains(text, "F as Six") {
|
|
|
|
|
t.Fatalf("sixth actor should not be included after sorting: %q", text)
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(text, "Cast: A as One, B as Two, C as Three, D as Four, E as Five") {
|
|
|
|
|
t.Fatalf("actor order was not deterministic by sort order: %q", text)
|
|
|
|
|
}
|
|
|
|
|
}
|