194 lines
8.5 KiB
Go
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)
|
|
}
|
|
}
|