package claim import ( "reflect" "testing" ) func TestTokensStripsPunctuationAndCase(t *testing.T) { got := Tokens("Какая сейчас погода в Риме?") want := []string{"какая", "сейчас", "погода", "в", "риме"} if !reflect.DeepEqual(got, want) { t.Errorf("Tokens = %q, want %q", got, want) } } // The dash forms matter: an STT transcript routinely carries "а, да, прости - // на 9", and a stray dash counted as a token would dilute every coverage score // in the sentence. func TestTokensDropsBareDashes(t *testing.T) { got := Tokens("а, да, прости — на 9") want := []string{"а", "да", "прости", "на", "9"} if !reflect.DeepEqual(got, want) { t.Errorf("Tokens = %q, want %q", got, want) } } func TestSplitPartitionsInOrder(t *testing.T) { consumed, unexplained := Split("напомни позвонить маме", "позвонить маме") if want := []string{"позвонить", "маме"}; !reflect.DeepEqual(consumed, want) { t.Errorf("consumed = %q, want %q", consumed, want) } if want := []string{"напомни"}; !reflect.DeepEqual(unexplained, want) { t.Errorf("unexplained = %q, want %q", unexplained, want) } } func TestCoverageIsZeroWithoutTokens(t *testing.T) { if got := (Claim{}).Coverage(); got != 0 { t.Errorf("Coverage of an empty claim = %v, want 0", got) } } func TestCoverageFraction(t *testing.T) { c := Claim{Consumed: []string{"a", "b", "c"}, Unexplained: []string{"d"}} if got := c.Coverage(); got != 0.75 { t.Errorf("Coverage = %v, want 0.75", got) } } // The band order is load-bearing, so it is asserted rather than assumed from // the iota. BandUnknown must sit at the bottom: a claim whose builder forgot to // set a band is a bug and must not outrank a measured one. func TestBandOrder(t *testing.T) { ordered := []Band{BandUnknown, BandVetoed, BandNearest, BandStructural, BandAnchored} for i := 1; i < len(ordered); i++ { if !(ordered[i-1] < ordered[i]) { t.Errorf("%v is not below %v", ordered[i-1], ordered[i]) } } for _, b := range ordered { if b.String() == "" { t.Errorf("band %d has no name", b) } } if BandUnknown.String() != "unknown" { t.Errorf("BandUnknown.String() = %q", BandUnknown.String()) } } // The failure V-558 opened with, as an ordering test. A pending reminder eats // "какая сейчас погода в Риме?" as a time answer and explains none of it; the // weather source explains all of it. Coverage decides, and the band never gets // consulted, which is the point: the pending claimant is structural and the // weather claim is anchored, but even if the bands were equal the weather claim // wins. func TestCoverageBeatsBand(t *testing.T) { utterance := "какая сейчас погода в Риме?" pendingConsumed, pendingRest := Split(utterance, "сейчас") pending := Claim{ Claimant: "reminder-followup", Intent: "reminder", Consumed: pendingConsumed, Unexplained: pendingRest, Band: BandStructural, } weatherConsumed, weatherRest := Split(utterance, utterance) weather := Claim{ Claimant: "weather", Intent: "query", Consumed: weatherConsumed, Unexplained: weatherRest, Band: BandNearest, } if !weather.MoreSpecificThan(pending) { t.Errorf("weather (%v cover) did not beat pending (%v cover)", weather.Coverage(), pending.Coverage()) } if pending.MoreSpecificThan(weather) { t.Error("pending beat weather, so the ordering is not asymmetric") } } // Where coverage ties, the band decides. Two grammars claiming the same // utterance is the stage-0 contention case (ru-query-019 on the fixture), and // today list order settles it with nothing recorded. func TestBandBreaksACoverageTie(t *testing.T) { anchored := Claim{Claimant: "calendar-query", Consumed: []string{"a"}, Band: BandAnchored} nearest := Claim{Claimant: "classifier", Consumed: []string{"a"}, Band: BandNearest} if !anchored.MoreSpecificThan(nearest) { t.Error("anchored did not beat nearest on equal coverage") } if nearest.MoreSpecificThan(anchored) { t.Error("nearest beat anchored on equal coverage") } } func TestVetoed(t *testing.T) { if (Claim{}).Vetoed() { t.Error("a claim with no veto reports itself vetoed") } if !(Claim{Veto: "fact with no key"}).Vetoed() { t.Error("a claim with a veto reason does not report itself vetoed") } }