From aecbad3ab10eac70b957106c9ec0c8e92a69b1a4 Mon Sep 17 00:00:00 2001 From: n0ctal <4c866w5fn9@privaterelay.appleid.com> Date: Sat, 15 Aug 2026 18:27:02 +0500 Subject: [PATCH] test(tgbot): detect open-coded keypad transitions (#6214) Co-authored-by: n0ctal <293235942+n0ctal@users.noreply.github.com> --- internal/web/service/tgbot/numeric_input.go | 5 +- .../web/service/tgbot/numeric_input_test.go | 51 +++++++++++++++++-- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/internal/web/service/tgbot/numeric_input.go b/internal/web/service/tgbot/numeric_input.go index e9269af8a..37e90a97f 100644 --- a/internal/web/service/tgbot/numeric_input.go +++ b/internal/web/service/tgbot/numeric_input.go @@ -1,8 +1,7 @@ package tgbot -// updateNumericInput applies one key from the shared inline number pad. -// Key -2 clears the value, -1 removes the last decimal digit, and 0..9 append -// a digit. Callers retain their own validation and keyboard labels. +// updateNumericInput applies one number-pad key: -2 clears, -1 backspaces, and 0..9 append. +// Callers retain their own validation and keyboard labels. func updateNumericInput(value, key int) int { switch key { case -2: diff --git a/internal/web/service/tgbot/numeric_input_test.go b/internal/web/service/tgbot/numeric_input_test.go index 9f744b585..958e295e7 100644 --- a/internal/web/service/tgbot/numeric_input_test.go +++ b/internal/web/service/tgbot/numeric_input_test.go @@ -1,7 +1,11 @@ package tgbot import ( + "go/ast" + "go/parser" + "go/token" "os" + "path/filepath" "strings" "testing" ) @@ -28,11 +32,50 @@ func TestUpdateNumericInput(t *testing.T) { } func TestNumericInputTransitionIsUsedByEveryKeypad(t *testing.T) { - source, err := os.ReadFile("tgbot_router.go") + entries, err := os.ReadDir(".") if err != nil { - t.Fatalf("read tgbot_router.go: %v", err) + t.Fatalf("read tgbot package: %v", err) } - if got := strings.Count(string(source), "updateNumericInput("); got != 6 { - t.Fatalf("numeric keypad transition call sites = %d, want 6", got) + fset := token.NewFileSet() + for _, entry := range entries { + name := entry.Name() + if entry.IsDir() || filepath.Ext(name) != ".go" || name == "numeric_input.go" || filepath.Ext(strings.TrimSuffix(name, "_test.go")) != ".go" { + continue + } + parsed, err := parser.ParseFile(fset, name, nil, 0) + if err != nil { + t.Fatalf("parse %s: %v", name, err) + } + ast.Inspect(parsed, func(node ast.Node) bool { + switchStmt, ok := node.(*ast.SwitchStmt) + if !ok { + return true + } + hasClear, hasBackspace, hasDefault := false, false, false + for _, stmt := range switchStmt.Body.List { + clause := stmt.(*ast.CaseClause) + if clause.List == nil { + hasDefault = true + } + for _, expr := range clause.List { + hasClear = hasClear || numericKeyLiteral(expr, "2") + hasBackspace = hasBackspace || numericKeyLiteral(expr, "1") + } + } + if hasClear && hasBackspace && hasDefault { + position := fset.Position(switchStmt.Pos()) + t.Errorf("open-coded numeric keypad transition at %s; use updateNumericInput", position) + } + return true + }) } } + +func numericKeyLiteral(expr ast.Expr, magnitude string) bool { + unary, ok := expr.(*ast.UnaryExpr) + if !ok || unary.Op != token.SUB { + return false + } + literal, ok := unary.X.(*ast.BasicLit) + return ok && literal.Kind == token.INT && literal.Value == magnitude +}