95 lines
3 KiB
Go
95 lines
3 KiB
Go
package store
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func rr(book, base, reading string, first, occ int) RubyReading {
|
|
return RubyReading{BookID: book, Base: base, Reading: reading, FirstChapter: first, Occurrences: occ}
|
|
}
|
|
|
|
func TestRubyReadingsReplaceAndRead(t *testing.T) {
|
|
s, _ := openTemp(t)
|
|
if err := s.ReplaceRubyReadings("b", []RubyReading{
|
|
rr("b", "漢字", "かんじ", 2, 5),
|
|
rr("b", "東京", "とうきょう", 1, 3),
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.ReplaceRubyReadings("other", []RubyReading{rr("other", "京", "きょう", 1, 1)}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := s.RubyReadingsForBook("b")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Ordered by (first_chapter, base, reading): 東京(ch1) before 漢字(ch2).
|
|
want := []RubyReading{
|
|
rr("b", "東京", "とうきょう", 1, 3),
|
|
rr("b", "漢字", "かんじ", 2, 5),
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("read = %#v, want %#v (other book must not leak)", got, want)
|
|
}
|
|
}
|
|
|
|
func TestRubyReadingsReplaceIsFullAndIdempotent(t *testing.T) {
|
|
s, _ := openTemp(t)
|
|
first := []RubyReading{rr("b", "漢字", "かんじ", 3, 4), rr("b", "海", "うみ", 1, 2)}
|
|
if err := s.ReplaceRubyReadings("b", first); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Re-ingesting the identical aggregate is a no-op (idempotent).
|
|
if err := s.ReplaceRubyReadings("b", first); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, _ := s.RubyReadingsForBook("b")
|
|
if len(got) != 2 {
|
|
t.Fatalf("identical re-replace must not add rows, got %#v", got)
|
|
}
|
|
// A source edit re-ingest: 海 removed entirely, 漢字 moved to ch 1 with a new count.
|
|
// The full replace must DROP the phantom 海 and CONVERGE 漢字 — a prior upsert-only
|
|
// path would have left 海 forever (external-review #6).
|
|
if err := s.ReplaceRubyReadings("b", []RubyReading{rr("b", "漢字", "かんじ", 1, 2)}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, _ = s.RubyReadingsForBook("b")
|
|
want := []RubyReading{rr("b", "漢字", "かんじ", 1, 2)}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("full replace must drop the removed pair and converge, got %#v", got)
|
|
}
|
|
}
|
|
|
|
func TestRubyReadingsVariantReadingsAreDistinctRows(t *testing.T) {
|
|
// The same base with two readings (variant yomi) are two rows, not a collision.
|
|
s, _ := openTemp(t)
|
|
if err := s.ReplaceRubyReadings("b", []RubyReading{
|
|
rr("b", "海", "うみ", 1, 2),
|
|
rr("b", "海", "かい", 4, 1),
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := s.RubyReadingsForBook("b")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(got) != 2 {
|
|
t.Fatalf("variant readings must be distinct rows, got %#v", got)
|
|
}
|
|
}
|
|
|
|
func TestRubyReadingsReplaceEmptyClears(t *testing.T) {
|
|
// A source that dropped all its furigana must clear the book's rows.
|
|
s, _ := openTemp(t)
|
|
if err := s.ReplaceRubyReadings("b", []RubyReading{rr("b", "漢字", "かんじ", 1, 1)}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.ReplaceRubyReadings("b", nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, _ := s.RubyReadingsForBook("b")
|
|
if len(got) != 0 {
|
|
t.Fatalf("empty replace must clear the book, got %#v", got)
|
|
}
|
|
}
|