Files

194 lines
8.5 KiB
Go

package region
import (
"testing"
"ai-operator/internal/dialogue/state"
)
func TestCatalogCountsAndNames(t *testing.T) {
r := NewDefaultResolver()
if got := len(r.ListEnabled()); got != 20 {
t.Fatalf("enabled=%d", got)
}
if got := len(r.ListDisabled()); got != 1 {
t.Fatalf("disabled=%d", got)
}
city, oblast := 0, 0
seen := map[string]bool{}
for _, reg := range r.ListEnabled() {
if reg.Code == "" || seen[reg.Code] {
t.Fatalf("bad code %q", reg.Code)
}
seen[reg.Code] = true
if reg.DisplayNameRU == "" || reg.DisplayNameKK == "" {
t.Fatalf("missing display names for %s", reg.Code)
}
switch reg.Type {
case RegionTypeRepublicCity:
city++
case RegionTypeOblast:
oblast++
}
}
if city != 3 || oblast != 17 {
t.Fatalf("city=%d oblast=%d", city, oblast)
}
}
func TestNormalizer(t *testing.T) {
cases := map[string]string{
" Г. Алматы! ": "город алматы",
"Алматинская обл.": "алматинская обл",
"Нұр-Сұлтан": "нұр сұлтан",
"Восточно-Казахстанская": "восточно казахстанская",
"Қазақша, Астана": "қазақша астана",
}
for input, want := range cases {
if got := Normalize(input); got != want {
t.Fatalf("Normalize(%q)=%q want %q", input, got, want)
}
}
}
func TestRepublicCityDetection(t *testing.T) {
r := NewDefaultResolver()
cases := map[string]string{
"астана": "astana_city",
"город астана": "astana_city",
"астана қаласы": "astana_city",
"astana": "astana_city",
"нурсултан": "astana_city",
"нұр-сұлтан": "astana_city",
"город алматы": "almaty_city",
"алматы қаласы": "almaty_city",
"шымкент": "shymkent_city",
"шимкент": "shymkent_city",
"shymkent": "shymkent_city",
}
for input, want := range cases {
got := r.Resolve(input, SourceCLI)
if got.RegionCode != want || got.Confidence < MediumConfidence {
t.Fatalf("%q => %+v want %s", input, got, want)
}
}
}
func TestOblastDetection(t *testing.T) {
r := NewDefaultResolver()
cases := map[string]string{
"Акмолинская область": "akmola_region",
"Ақмола облысы": "akmola_region",
"Актюбинская область": "aktobe_region",
"Ақтөбе облысы": "aktobe_region",
"Алматинская область": "almaty_region",
"Алматы облысы": "almaty_region",
"Атырауская область": "atyrau_region",
"Атырау облысы": "atyrau_region",
"Восточно-Казахстанская область": "east_kazakhstan_region",
"Шығыс Қазақстан облысы": "east_kazakhstan_region",
"Жамбылская область": "zhambyl_region",
"Жамбыл облысы": "zhambyl_region",
"Западно-Казахстанская область": "west_kazakhstan_region",
"Батыс Қазақстан облысы": "west_kazakhstan_region",
"Карагандинская область": "karaganda_region",
"Қарағанды облысы": "karaganda_region",
"Костанайская область": "kostanay_region",
"Қостанай облысы": "kostanay_region",
"Кызылординская область": "kyzylorda_region",
"Қызылорда облысы": "kyzylorda_region",
"Мангистауская область": "mangystau_region",
"Маңғыстау облысы": "mangystau_region",
"Павлодарская область": "pavlodar_region",
"Павлодар облысы": "pavlodar_region",
"Северо-Казахстанская область": "north_kazakhstan_region",
"Солтүстік Қазақстан облысы": "north_kazakhstan_region",
"Туркестанская область": "turkistan_region",
"Түркістан облысы": "turkistan_region",
"область Абай": "abai_region",
"Абай облысы": "abai_region",
"область Жетісу": "jetisu_region",
"Жетісу облысы": "jetisu_region",
"область Ұлытау": "ulytau_region",
"Ұлытау облысы": "ulytau_region",
}
for input, want := range cases {
got := r.Resolve(input, SourceCLI)
if got.RegionCode != want || got.Confidence < MediumConfidence {
t.Fatalf("%q => %+v want %s", input, got, want)
}
}
}
func TestAbbreviations(t *testing.T) {
r := NewDefaultResolver()
cases := map[string]string{"ВКО": "east_kazakhstan_region", "ЗКО": "west_kazakhstan_region", "СКО": "north_kazakhstan_region", "vko": "east_kazakhstan_region", "zko": "west_kazakhstan_region", "sko": "north_kazakhstan_region"}
for input, want := range cases {
if got := r.Resolve(input, SourceCLI); got.RegionCode != want || got.ReasonCode != "abbreviation" {
t.Fatalf("%q => %+v want %s", input, got, want)
}
}
}
func TestAlmatyAmbiguityAndClarification(t *testing.T) {
r := NewDefaultResolver()
for _, input := range []string{"Алматы", "almaty", "Алматы тарифы"} {
got := r.Resolve(input, SourceCLI)
if !got.NeedsClarification || got.ReasonCode != "ambiguous_almaty" || len(got.Candidates) != 2 {
t.Fatalf("%q => %+v", input, got)
}
}
pending := []string{"almaty_city", "almaty_region"}
if got := r.ResolvePending(pending, "город", SourceCLI); got.RegionCode != "almaty_city" {
t.Fatalf("city clarification: %+v", got)
}
if got := r.ResolvePending(pending, "қала", SourceCLI); got.RegionCode != "almaty_city" {
t.Fatalf("kk city clarification: %+v", got)
}
if got := r.ResolvePending(pending, "область", SourceCLI); got.RegionCode != "almaty_region" {
t.Fatalf("region clarification: %+v", got)
}
if got := r.ResolvePending(pending, "облыс", SourceCLI); got.RegionCode != "almaty_region" {
t.Fatalf("kk region clarification: %+v", got)
}
}
func TestFalsePositivesAndDisabled(t *testing.T) {
r := NewDefaultResolver()
for _, input := range []string{"Казахтелеком", "Казахстан", "область", "город", "мой тариф", "астана балет", "карагандинский уголь", "у меня вопрос по шымкентскому номеру", "алматинский район"} {
got := r.Resolve(input, SourceCLI)
if got.RegionCode != "" {
t.Fatalf("false positive %q => %+v", input, got)
}
}
got := r.Resolve("Байконур", SourceCLI)
if got.Intent != IntentUnsupported || got.ReasonCode != "disabled_region" {
t.Fatalf("disabled: %+v", got)
}
}
func TestSelectionPolicy(t *testing.T) {
r := NewDefaultResolver()
astana := r.Resolve("Астана", SourceCLI)
if d := ShouldApplyRegionResolution(SelectionContext{CurrentState: state.StateLanguageSelection, CurrentLanguage: state.LanguageUnknown}, astana); d.Apply || d.ReasonCode != "language_required" {
t.Fatalf("language selection applied region: %+v", d)
}
if d := ShouldApplyRegionResolution(SelectionContext{CurrentState: state.StateRegionSelection, CurrentLanguage: state.LanguageRU}, astana); !d.Apply || d.RegionCode != "astana_city" {
t.Fatalf("region selection did not apply: %+v", d)
}
amb := r.Resolve("Алматы", SourceCLI)
if d := ShouldApplyRegionResolution(SelectionContext{CurrentState: state.StateRegionSelection, CurrentLanguage: state.LanguageRU}, amb); !d.NeedsClarification {
t.Fatalf("ambiguous not clarified: %+v", d)
}
if d := ShouldApplyRegionResolution(SelectionContext{CurrentState: state.StateReadyToHelp, CurrentLanguage: state.LanguageRU, CurrentRegionCode: "astana_city", AllowChange: true}, astana); d.Apply {
t.Fatalf("random mention switched: %+v", d)
}
astana.Intent = IntentRegionChange
if d := ShouldApplyRegionResolution(SelectionContext{CurrentState: state.StateReadyToHelp, CurrentLanguage: state.LanguageRU, CurrentRegionCode: "shymkent_city", AllowChange: true}, astana); !d.Apply {
t.Fatalf("explicit change not applied: %+v", d)
}
if d := ShouldApplyRegionResolution(SelectionContext{CurrentState: state.StateEnded, CurrentLanguage: state.LanguageRU}, astana); d.Apply {
t.Fatalf("ended applied: %+v", d)
}
}