router: add NormalizeMatchText tests and property invariants (slice 9b)

17 tests covering: whitespace collapse, case folding, NFKC normalization,
punctuation preservation, mixed-script identifiers, ё passthrough, and
property invariants (idempotent, deterministic, never removes punctuation/
wake words/numbers, never mutates original).

Test-only foldYo helper proves ё→е is lossy (всё → все) without
exposing an unused production function.

Vikunja: #725
This commit is contained in:
2026-09-06 22:33:27 +04:00
parent 431e052e6f
commit e9049ad27e
2 changed files with 321 additions and 3 deletions
+6 -3
View File
@@ -7,19 +7,22 @@ import (
)
// TestNormalizedInputIsMinimalValueObject — the typed ingress boundary carries
// text and source and nothing else. This test pins the shape so a future slice
// text, match text and source. This test pins the shape so a future slice
// cannot add fields without updating every construction site.
func TestNormalizedInputIsMinimalValueObject(t *testing.T) {
input := NormalizedInput{Text: "привет", Source: InputSourceVoice}
input := NormalizedInput{Text: "привет", MatchText: "привет", Source: InputSourceVoice}
if input.Text != "привет" {
t.Errorf("Text = %q, want %q", input.Text, "привет")
}
if input.MatchText != "привет" {
t.Errorf("MatchText = %q, want %q", input.MatchText, "привет")
}
if input.Source != InputSourceVoice {
t.Errorf("Source = %q, want %q", input.Source, InputSourceVoice)
}
// Empty zero value is usable.
var zero NormalizedInput
if zero.Text != "" || zero.Source != "" {
if zero.Text != "" || zero.MatchText != "" || zero.Source != "" {
t.Errorf("zero value is not empty: %+v", zero)
}
}