fix(outages): do not space-pad ordinals in the portal LIKE pattern

likePattern padded every token containing a digit, including ordinals like
"3-я". When the ordinal starts the address, " 3-я " never matches, so
"3-я Механическая улица 24а" returned no outages. Pad only pure-numeric
house tokens; precision is enforced by the local house filter regardless.
This commit is contained in:
Maksim Totmin
2026-10-09 22:06:31 +07:00
parent 50f180ea10
commit 5120bf5de5
2 changed files with 29 additions and 8 deletions
+15 -10
View File
@@ -220,24 +220,29 @@ func actionFor(system string) string {
} }
} }
// likePattern builds the substring pattern the portal supports. Numeric tokens // likePattern builds the substring pattern the portal supports. Only pure
// are padded with spaces, matching the historical integration, so that "1" does // numeric tokens (house numbers) are padded with spaces, so "1" does not
// not incidentally match "10". // incidentally match "10". Ordinals ("3-я") and houses with a letter ("24а")
// are left as-is: padding an ordinal breaks the match when it starts the
// string, and precision is guaranteed by the local house filter anyway.
func likePattern(canonical string) string { func likePattern(canonical string) string {
parts := strings.Fields(canonical) parts := strings.Fields(canonical)
for i, part := range parts { for i, part := range parts {
if containsDigit(part) { if isPureDigits(part) {
parts[i] = " " + part + " " parts[i] = " " + part + " "
} }
} }
return "%" + strings.Join(parts, "%") + "%" return "%" + strings.Join(parts, "%") + "%"
} }
func containsDigit(s string) bool { func isPureDigits(s string) bool {
for _, r := range s { if s == "" {
if r >= '0' && r <= '9' {
return true
}
}
return false return false
}
for _, r := range s {
if r < '0' || r > '9' {
return false
}
}
return true
} }
+16
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"strings"
"testing" "testing"
"time" "time"
) )
@@ -92,3 +93,18 @@ func TestQueryStripsDistrictSuffix(t *testing.T) {
t.Errorf("house not parsed at the boundary: %+v", rows[0].Unit) t.Errorf("house not parsed at the boundary: %+v", rows[0].Unit)
} }
} }
// TestLikePattern guards the ordinal/house padding rule: padding an ordinal
// that starts the string breaks the portal match.
func TestLikePattern(t *testing.T) {
got := likePattern("3-я Механическая улица 24а")
if strings.Contains(got, " 3-я ") {
t.Errorf("ordinal must not be space-padded: %q", got)
}
if strings.Contains(got, " 24а ") {
t.Errorf("house with a letter must not be space-padded: %q", got)
}
if !strings.Contains(likePattern("Ленина улица 1"), "% 1 %") {
t.Errorf("pure numeric house must be space-padded: %q", likePattern("Ленина улица 1"))
}
}