Files
silo-server/internal/literaryworks/matcher_test.go
e084cdd1d6 Add unified literary works for ebooks and audiobooks (#107)
* docs: add literary works design and plan

* feat(literary): add work link schema

* feat(literary): add work domain primitives

* feat(literary): persist work links

* feat(literary): score work matches

* feat(catalog): include literary work summary on item detail

* feat(literary): expose work detail API

* feat(literary): assemble work detail

* feat(literary): add admin work linking primitives

* feat(catalog): group literary items by work

* feat(literary): auto-link works during book scans

* fix(literary): narrow work match candidates

* fix(literary): address work merge blockers

---------

Co-authored-by: rxwatcher <rxwatcher@users.noreply.github.com>
Co-authored-by: Quick <31828688+Quick104@users.noreply.github.com>
2026-06-16 18:17:08 -04:00

53 lines
2.8 KiB
Go

package literaryworks
import "testing"
func TestMatcherSameTitleAuthorLinks(t *testing.T) {
ebook := MatchItem{ContentID: "e1", Type: FormatEbook, Title: "Project Hail Mary", Authors: []string{"Andy Weir"}}
audio := MatchItem{ContentID: "a1", Type: FormatAudiobook, Title: "Project Hail Mary", Authors: []string{"Andy Weir"}, Narrators: []string{"Ray Porter"}}
candidate := ScoreCandidate(ebook, audio)
if candidate.Score < AutoLinkThreshold || candidate.LinkSource != LinkMetadataMatch {
t.Fatalf("candidate = %#v, want metadata auto-link", candidate)
}
}
func TestMatcherSameTitleDifferentAuthorDoesNotLink(t *testing.T) {
ebook := MatchItem{ContentID: "e1", Type: FormatEbook, Title: "The Last Adventure", Authors: []string{"A. Lee"}}
audio := MatchItem{ContentID: "a1", Type: FormatAudiobook, Title: "The Last Adventure", Authors: []string{"B. Lee"}}
candidate := ScoreCandidate(ebook, audio)
if candidate.Score >= AutoLinkThreshold {
t.Fatalf("score = %v, want below threshold", candidate.Score)
}
}
func TestMatcherSeriesIndexLinksSubtitleVariants(t *testing.T) {
ebook := MatchItem{ContentID: "e1", Type: FormatEbook, Title: "The Last Adventure", Authors: []string{"A. Lee"}, SeriesName: "Constance Verity", SeriesIndex: floatPtr(1)}
audio := MatchItem{ContentID: "a1", Type: FormatAudiobook, Title: "Constance Verity 1 - The Last Adventure of Constance Verity", Authors: []string{"A. Lee"}, SeriesName: "Constance Verity", SeriesIndex: floatPtr(1)}
candidate := ScoreCandidate(ebook, audio)
if candidate.Score < AutoLinkThreshold || candidate.LinkSource != LinkSeriesMatch {
t.Fatalf("candidate = %#v, want series auto-link", candidate)
}
}
func TestMatcherSharedExternalIDWins(t *testing.T) {
ebook := MatchItem{ContentID: "e1", Type: FormatEbook, Title: "Different Ebook Title", Authors: []string{"A. Lee"}, ExternalIDs: map[string]string{"isbn": "978123"}}
audio := MatchItem{ContentID: "a1", Type: FormatAudiobook, Title: "Different Audio Title", Authors: []string{"B. Lee"}, ExternalIDs: map[string]string{"isbn": "978123"}}
candidate := ScoreCandidate(ebook, audio)
if candidate.Score < AutoLinkThreshold || candidate.LinkSource != LinkExternalID {
t.Fatalf("candidate = %#v, want external id auto-link", candidate)
}
}
func TestMatcherIgnoresASINAsWorkIdentity(t *testing.T) {
ebook := MatchItem{ContentID: "e1", Type: FormatEbook, Title: "Different Ebook Title", Authors: []string{"A. Lee"}, ExternalIDs: map[string]string{"asin": "B123"}}
audio := MatchItem{ContentID: "a1", Type: FormatAudiobook, Title: "Different Audio Title", Authors: []string{"B. Lee"}, ExternalIDs: map[string]string{"asin": "B123"}}
candidate := ScoreCandidate(ebook, audio)
if candidate.Score >= AutoLinkThreshold {
t.Fatalf("candidate = %#v, want ASIN below auto-link threshold", candidate)
}
}
func floatPtr(v float64) *float64 {
return &v
}