1
0

tgbot_declined_callback_test.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. package tgbot
  2. import (
  3. "testing"
  4. "time"
  5. "github.com/mhsanaei/3x-ui/v3/internal/web/global"
  6. "github.com/mymmrac/telego"
  7. )
  8. func tapCallback(t *testing.T, tb *Tgbot, isAdmin bool, data string) {
  9. t.Helper()
  10. tb.answerCallback(&telego.CallbackQuery{
  11. ID: "q1",
  12. From: telego.User{ID: 1},
  13. Data: data,
  14. Message: &telego.Message{Chat: telego.Chat{ID: 1}},
  15. }, isAdmin)
  16. }
  17. // decliningServer answers both methods a declined callback can reach, and
  18. // leaves isRunning set so a notice would go out if the code decided to send one.
  19. func decliningServer(t *testing.T) func(string) int {
  20. t.Helper()
  21. mock, calls := staleButtonServer(t, map[string]any{
  22. "answerCallbackQuery": map[string]any{"ok": true, "result": true},
  23. "sendMessage": map[string]any{"ok": true, "result": map[string]any{
  24. "message_id": 1,
  25. "date": 0,
  26. "chat": map[string]any{"id": 1, "type": "private"},
  27. }},
  28. })
  29. swapTestBot(t, mock.URL)
  30. t.Cleanup(mock.Close)
  31. origRunning := isRunning
  32. t.Cleanup(func() { isRunning = origRunning })
  33. isRunning = true
  34. return calls
  35. }
  36. // Regression test: a payload the bot could not route was dropped without an
  37. // answer, so Telegram kept the button spinning until the callback timed out.
  38. func TestUnroutableCallbackIsAnswered(t *testing.T) {
  39. for _, data := range []string{"no_such_callback", "get_backup_legacy", "add_client_legacy_step 5"} {
  40. t.Run(data, func(t *testing.T) {
  41. calls := decliningServer(t)
  42. tapCallback(t, &Tgbot{}, true, data)
  43. if n := calls("answerCallbackQuery"); n != 1 {
  44. t.Errorf("answerCallbackQuery calls = %d, want 1: an unroutable tap must be answered", n)
  45. }
  46. if n := calls("sendMessage"); n != 0 {
  47. t.Errorf("sendMessage calls = %d, want 0: an answer is not a posted message", n)
  48. }
  49. })
  50. }
  51. }
  52. // Regression test: a button whose hash aged out was answered with nothing at all;
  53. // the answer clears it and the chat notice survives a failed send.
  54. func TestExpiredCallbackHashIsAnsweredAndReported(t *testing.T) {
  55. calls := decliningServer(t)
  56. origHash := hashStorage
  57. hashStorage = global.NewHashStorage(time.Minute)
  58. t.Cleanup(func() { hashStorage = origHash })
  59. // 32 hex characters — the shape decodeQuery looks up, and never stored.
  60. tapCallback(t, &Tgbot{}, true, "0123456789abcdef0123456789abcdef")
  61. if n := calls("answerCallbackQuery"); n != 1 {
  62. t.Errorf("answerCallbackQuery calls = %d, want 1: an expired button must be answered", n)
  63. }
  64. if n := calls("sendMessage"); n != 1 {
  65. t.Errorf("sendMessage calls = %d, want 1: the notice is the durable half of the reply", n)
  66. }
  67. }