765ed36340
The owner's wording, taken from the PR 111 review, with one correction from the
PR 113 review folded in: {temp} {word} rather than {temp}°, because the degree
sign reads as nothing through piper.
What the wording changes: query_unknown drops "не знаю.", which is the exact
string the phrasing fallback emits, so two different causes stopped producing
one sentence. weather_nolocation stops reading voice.weather.default_location
out loud and just asks which city. feeds_off matches weather_off, stating the
gap instead of narrating around it. The passive doubles and the near-identical
pairs go.
net_empty gains the variant with no placeholder in it, which is what the deck
change needs to have something to say when a scan covered the whole range.
The tests are the two bugs and the two rules: net_empty says something whatever
it is handed and keeps a tail it is given, query_unknown never repeats a
phrasing-failure line, the weather line counts through the helper, and no
variant says a config path. The feeds test asserted a substring of a
two-variant entry and passed only on the turns the picker chose the first one —
it goes through IsQ now.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XGTGCWX33aX8SMBSRz9VmS
108 lines
3.3 KiB
Go
108 lines
3.3 KiB
Go
package phraser
|
|
|
|
import (
|
|
"math/rand"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestQueriesLoad(t *testing.T) {
|
|
q, err := LoadQueries(rand.NewSource(1))
|
|
if err != nil {
|
|
t.Fatalf("LoadQueries: %v", err)
|
|
}
|
|
for _, k := range queryKeys {
|
|
if got := q.Say(k, nil); got == "" {
|
|
t.Errorf("%s says nothing", k)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The bug: net_empty carried {tail} in every variant, and a scan that finished
|
|
// the whole range has no caveat to put there. Whatever the file says, an answer
|
|
// he can hear has to come out — never braces, never nothing.
|
|
func TestNetEmptySaysSomethingWithNoTail(t *testing.T) {
|
|
q, err := LoadQueries(rand.NewSource(1))
|
|
if err != nil {
|
|
t.Fatalf("LoadQueries: %v", err)
|
|
}
|
|
for _, vars := range []map[string]string{nil, {"tail": ""}} {
|
|
for i := 0; i < 20; i++ {
|
|
got := q.Say(QueryNetEmpty, vars)
|
|
if got == "" || strings.ContainsAny(got, "{}") {
|
|
t.Fatalf("net_empty with vars %v said %q", vars, got)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// The other half: a caveat he was given is not dropped for a shorter wording.
|
|
func TestNetEmptyKeepsTheTailItIsGiven(t *testing.T) {
|
|
q, err := LoadQueries(rand.NewSource(1))
|
|
if err != nil {
|
|
t.Fatalf("LoadQueries: %v", err)
|
|
}
|
|
const tail = ", но успела посмотреть не всю сеть"
|
|
for i := 0; i < 20; i++ {
|
|
if got := q.Say(QueryNetEmpty, map[string]string{"tail": tail}); !strings.Contains(got, tail) {
|
|
t.Fatalf("net_empty dropped the tail: %q", got)
|
|
}
|
|
}
|
|
}
|
|
|
|
// query_unknown means she looked and found nothing. The phraser's fallback
|
|
// means she failed to phrase an answer she had. Two causes, two sentences, or
|
|
// the distinction the two files exist for is unobservable from the outside.
|
|
func TestQueryUnknownNeverRepeatsAPhrasingFallback(t *testing.T) {
|
|
q, err := LoadQueries(rand.NewSource(1))
|
|
if err != nil {
|
|
t.Fatalf("LoadQueries: %v", err)
|
|
}
|
|
f, err := LoadFallbacks(rand.NewSource(1))
|
|
if err != nil {
|
|
t.Fatalf("LoadFallbacks: %v", err)
|
|
}
|
|
failures := map[string]bool{}
|
|
for _, v := range f.Variants() {
|
|
failures[v] = true
|
|
}
|
|
for _, v := range q.d.file.Entries[QueryUnknown].Variants {
|
|
if failures[v] {
|
|
t.Errorf("query_unknown variant %q is also a phrasing failure line", v)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The weather line splits the count into a number and a noun, so a variant that
|
|
// says the temperature without {word} is the hardcoded "градусов" coming back.
|
|
func TestWeatherLineCountsWithTheHelper(t *testing.T) {
|
|
q, err := LoadQueries(rand.NewSource(1))
|
|
if err != nil {
|
|
t.Fatalf("LoadQueries: %v", err)
|
|
}
|
|
for _, v := range q.d.file.Entries[QueryWeatherNow].Variants {
|
|
if strings.Contains(v, "градус") {
|
|
t.Errorf("weather_now variant %q spells the noun out instead of using {word}", v)
|
|
}
|
|
}
|
|
got := q.Say(QueryWeatherNow, map[string]string{
|
|
"location": "Москва", "temp": "1", "word": Degrees(1), "condition": "ясно",
|
|
})
|
|
if !strings.Contains(got, "1 градус,") {
|
|
t.Errorf("weather_now said %q, want the singular noun", got)
|
|
}
|
|
}
|
|
|
|
// No line spoken to him names a config key. She asks instead.
|
|
func TestNoQueryLineRecitesAConfigPath(t *testing.T) {
|
|
q, err := LoadQueries(rand.NewSource(1))
|
|
if err != nil {
|
|
t.Fatalf("LoadQueries: %v", err)
|
|
}
|
|
for _, v := range q.Variants() {
|
|
if strings.Contains(v, "voice.") || strings.Contains(v, "_location") {
|
|
t.Errorf("variant %q says a config path out loud", v)
|
|
}
|
|
}
|
|
}
|