Procházet zdrojové kódy

test(tgbot): detect open-coded keypad transitions (#6214)

Co-authored-by: n0ctal <[email protected]>
n0ctal před 6 hodinami
rodič
revize
aecbad3ab1

+ 2 - 3
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:

+ 47 - 4
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
 }