package address import "testing" func testDictionary() *Dictionary { return NewDictionary([]City{ {Name: "Новосибирск", Aliases: []string{"нск", "новосиб", "новосибирская область", "нсо"}}, {Name: "Бердск"}, }) } func TestParseCityFromAliases(t *testing.T) { parser := NewParser(testDictionary(), "") tests := []struct { raw string city string nameKey string }{ {"нск, 2-я Станционная улица 1", "Новосибирск", "станционная"}, {"НСК Ленина 5", "Новосибирск", "ленина"}, {"Новосибирская область, Станционная 1", "Новосибирск", "станционная"}, {"Бердск, Ленина 5", "Бердск", "ленина"}, {"г. Новосибирск, ул. Ленина 5", "Новосибирск", "ленина"}, } for _, tt := range tests { got := parser.Parse(tt.raw) if got.City != tt.city { t.Errorf("%q: City = %q, want %q", tt.raw, got.City, tt.city) } if got.NameKey != tt.nameKey { t.Errorf("%q: NameKey = %q, want %q", tt.raw, got.NameKey, tt.nameKey) } } } func TestParseCityIsPartOfStreetKey(t *testing.T) { parser := NewParser(testDictionary(), "") nsk := parser.Parse("Новосибирск, Ленина 5") berdsk := parser.Parse("Бердск, Ленина 5") if nsk.StreetKey == berdsk.StreetKey { t.Fatalf("same street key for different cities: %q", nsk.StreetKey) } if nsk.CityKey != "новосибирск" || berdsk.CityKey != "бердск" { t.Fatalf("city keys = %q / %q", nsk.CityKey, berdsk.CityKey) } } func TestParseDefaultCity(t *testing.T) { parser := NewParser(testDictionary(), "Новосибирск") got := parser.Parse("Ленина 5") if got.City != "Новосибирск" || got.CityKey != "новосибирск" { t.Fatalf("City = %q / %q", got.City, got.CityKey) } // The default city is also recognised when the user spells it out, even // if the dictionary contains only aliases. explicit := NewParser(DefaultDictionary(), "Новосибирск").Parse("г. Новосибирск, Ленина 5") if explicit.City != "Новосибирск" || explicit.NameKey != "ленина" { t.Fatalf("explicit default city: %+v", explicit) } } func TestParseInCityOverridesDetectedCity(t *testing.T) { parser := NewParser(testDictionary(), "") got := parser.ParseInCity("Бердск, Ленина 5", "Новосибирск") if got.City != "Новосибирск" { t.Fatalf("City = %q, want forced Новосибирск", got.City) } if got.NameKey != "ленина" { t.Fatalf("NameKey = %q", got.NameKey) } } func TestParseWithoutCityKeepsCityKeyEmpty(t *testing.T) { parser := NewParser(testDictionary(), "") got := parser.Parse("Ленина 5") if got.City != "" || got.CityKey != "" { t.Fatalf("City = %q / %q, want empty", got.City, got.CityKey) } } func TestNormalizeTextStillLeavesUnknownCityTokens(t *testing.T) { // A city that is not in the dictionary must not be silently dropped: the // resolver should fail rather than guess another city. parser := NewParser(testDictionary(), "") got := parser.Parse("Томск, Ленина 5") if got.City != "" { t.Fatalf("City = %q, want empty for unknown city", got.City) } if got.NameKey == "ленина" { t.Fatalf("unknown city token disappeared from the street name") } } func TestMatchCity(t *testing.T) { dict := testDictionary() cases := []struct { query string want string ok bool }{ {"нск", "Новосибирск", true}, {"г. Новосибирск", "Новосибирск", true}, {"Новосибирская область", "Новосибирск", true}, {"Бердск", "Бердск", true}, {"Бердск, Ленина 5", "", false}, {"Томск", "", false}, } for _, tt := range cases { got, ok := dict.MatchCity(tt.query) if ok != tt.ok || got.Name != tt.want { t.Errorf("MatchCity(%q) = %+v, %v; want %q, %v", tt.query, got, ok, tt.want, tt.ok) } } } func FuzzParse(f *testing.F) { for _, seed := range []string{ "", "2-я Станционная улица 1/1 стр.", "нск, Ленина 5", "Бердск, Ленина 5 к2", "г. Новосибирск, ул. Кошурникова, д. 11/1 кв. 5", "\x00\xff", } { f.Add(seed) } parser := NewParser(testDictionary(), "") f.Fuzz(func(t *testing.T, raw string) { first := parser.Parse(raw) second := parser.Parse(raw) if first.StreetKey != second.StreetKey { t.Fatalf("non-deterministic street key for %q: %q vs %q", raw, first.StreetKey, second.StreetKey) } if NormalizeText(raw) != NormalizeText(NormalizeText(raw)) { t.Fatalf("NormalizeText is not idempotent for %q", raw) } }) }