Files
addrkit/internal/domain/address/parse_test.go
T
Maksim Totmin 17ff5c591f refactor!: make addrkit a multi-city address normalizer
- remove the events/outages module, ArcGIS adapter, portal config and event statuses
- add a configurable city dictionary/parser and make City part of StreetKey
- seed: read city/city_name, namespace IDs by city, keep structures distinct
- fix reliability: schema v2 with fail-closed load, deterministic ordering,
  input limits, config validation, atomic DB publish, LLM response caps
- add read-only shadow mode comparing the old ES path with addrkit
- breaking: registry.db must be re-seeded; config has no portal section
2026-10-27 14:35:51 +07:00

180 lines
6.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package address
import "testing"
func TestParseHouseBlock(t *testing.T) {
tests := []struct {
name string
raw string
wantOrdinal string
wantName string
wantType string
wantHouse string
wantFrac string
wantLetter string
wantBuild string
wantBFrac string
wantBLetter string
wantStruct string
wantHas bool
}{
{
name: "ordinal, fraction and structure",
raw: "2-я Станционная улица 1/1 стр.",
wantOrdinal: "2", wantName: "станционная", wantType: "улица",
wantHouse: "1", wantFrac: "1", wantStruct: "", wantHas: true,
},
{
name: "building with fraction and structure",
raw: "Станционная улица 30а к9/1 стр.",
wantName: "станционная", wantType: "улица",
wantHouse: "30", wantLetter: "а", wantBuild: "9", wantBFrac: "1", wantHas: true,
},
{
name: "letter glued to house",
raw: "10-й Бронный переулок 33а",
wantOrdinal: "10", wantName: "бронный", wantType: "переулок",
wantHouse: "33", wantLetter: "а", wantHas: true,
},
{
name: "microdistrict, two-word name",
raw: "Зеленый бор микрорайон 13",
wantName: "бор зеленый", wantType: "микрорайон",
wantHouse: "13", wantHas: true,
},
{
name: "spelled-out ordinal",
raw: "вторая Станционная улица 1",
wantOrdinal: "2", wantName: "станционная", wantType: "улица",
wantHouse: "1", wantHas: true,
},
{
name: "en dash variant",
raw: "2–я Станционная улица 1",
wantOrdinal: "2", wantName: "станционная", wantType: "улица",
wantHouse: "1", wantHas: true,
},
{
name: "no house",
raw: "Станционная улица",
wantName: "станционная", wantType: "улица", wantHas: false,
},
{
name: "structure with number only",
raw: "Станционная улица 3 стр.",
wantName: "станционная", wantType: "улица",
wantHouse: "3", wantStruct: "", wantHas: true,
},
{
name: "letter before fraction",
raw: "Выборная улица 45а/4",
wantName: "выборная", wantType: "улица",
wantHouse: "45", wantLetter: "а", wantFrac: "4", wantHas: true,
},
{
name: "spoken fraction word",
raw: "Горская улица 18 дробь 1",
wantName: "горская", wantType: "улица",
wantHouse: "18", wantFrac: "1", wantHas: true,
},
{
name: "corpus letter without digit",
raw: "1-я Ельцовка улица 1 кД",
wantOrdinal: "1", wantName: "ельцовка", wantType: "улица",
wantHouse: "1", wantBLetter: "д", wantHas: true,
},
{
name: "two type words in a registry name",
raw: "Гусинобродский Тракт улица 21",
wantName: "гусинобродский", wantType: "тракт",
wantHouse: "21", wantHas: true,
},
{
name: "name coincides with its type word",
raw: "Набережная улица 5",
wantName: "набережная", wantType: "набережная",
wantHouse: "5", wantHas: true,
},
{
name: "abbreviations dom and flat",
raw: "ул. Кошурникова, д. 11/1 кв. 5",
wantName: "кошурникова", wantType: "улица",
wantHouse: "11", wantFrac: "1", wantHas: true,
},
{
name: "district suffix in parentheses",
raw: "Медкадры улица, 1 (Заельцовский район)",
wantName: "медкадры", wantType: "улица",
wantHouse: "1", wantHas: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Parse(tt.raw)
if got.Ordinal != tt.wantOrdinal {
t.Errorf("Ordinal = %q, want %q", got.Ordinal, tt.wantOrdinal)
}
if got.NameKey != tt.wantName {
t.Errorf("NameKey = %q, want %q", got.NameKey, tt.wantName)
}
if got.StreetType != tt.wantType {
t.Errorf("StreetType = %q, want %q", got.StreetType, tt.wantType)
}
if got.House != tt.wantHouse {
t.Errorf("House = %q, want %q", got.House, tt.wantHouse)
}
if got.HouseFrac != tt.wantFrac {
t.Errorf("HouseFrac = %q, want %q", got.HouseFrac, tt.wantFrac)
}
if got.HouseLetter != tt.wantLetter {
t.Errorf("HouseLetter = %q, want %q", got.HouseLetter, tt.wantLetter)
}
if got.Building != tt.wantBuild {
t.Errorf("Building = %q, want %q", got.Building, tt.wantBuild)
}
if got.BuildingFrac != tt.wantBFrac {
t.Errorf("BuildingFrac = %q, want %q", got.BuildingFrac, tt.wantBFrac)
}
if got.BuildingLetter != tt.wantBLetter {
t.Errorf("BuildingLetter = %q, want %q", got.BuildingLetter, tt.wantBLetter)
}
if got.Structure != tt.wantStruct {
t.Errorf("Structure = %q, want %q", got.Structure, tt.wantStruct)
}
if got.HasHouse != tt.wantHas {
t.Errorf("HasHouse = %v, want %v", got.HasHouse, tt.wantHas)
}
})
}
}
// TestParseDeterministic guards the core invariant: parsing the same input
// always yields the same structured result and the same street key.
func TestParseDeterministic(t *testing.T) {
input := "2-я Станционная улица 1/1 стр."
first := Parse(input)
for i := 0; i < 100; i++ {
if got := Parse(input); got.StreetKey != first.StreetKey || got.House != first.House {
t.Fatalf("non-deterministic parse: %+v vs %+v", got, first)
}
}
}
func TestNormalizeIdempotent(t *testing.T) {
inputs := []string{
" 2-Я Станционная\u00a0улица 1/1 стр. ",
"ЁЛКИ д. 5, кв. 3",
"СТАНИЦИОННАЯ — улица, 30а",
"Медкадры улица, 1 (Заельцовский район)",
"",
}
for _, in := range inputs {
once := NormalizeText(in)
twice := NormalizeText(once)
if once != twice {
t.Errorf("NormalizeText not idempotent for %q: %q != %q", in, once, twice)
}
}
}