tgbot_individual_links_test.go 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. package tgbot
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "net/http/httptest"
  6. "path/filepath"
  7. "strings"
  8. "sync"
  9. "testing"
  10. "github.com/mhsanaei/3x-ui/v3/internal/database"
  11. "github.com/mhsanaei/3x-ui/v3/internal/database/dbtest"
  12. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  13. "github.com/mhsanaei/3x-ui/v3/internal/sub"
  14. "github.com/mhsanaei/3x-ui/v3/internal/web/service"
  15. "github.com/mhsanaei/3x-ui/v3/internal/xray"
  16. )
  17. // recordingBotServer answers sendMessage and records every text it was asked to send.
  18. func recordingBotServer(t *testing.T) func() []string {
  19. t.Helper()
  20. var mu sync.Mutex
  21. var texts []string
  22. srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  23. if r.URL.Path != "/bot"+testBotToken+"/sendMessage" {
  24. w.WriteHeader(http.StatusNotFound)
  25. return
  26. }
  27. var body struct {
  28. Text string `json:"text"`
  29. }
  30. _ = json.NewDecoder(r.Body).Decode(&body)
  31. mu.Lock()
  32. texts = append(texts, body.Text)
  33. mu.Unlock()
  34. w.Header().Set("Content-Type", "application/json")
  35. _ = json.NewEncoder(w).Encode(map[string]any{"ok": true, "result": map[string]any{
  36. "message_id": 1, "date": 0, "chat": map[string]any{"id": ownerTgID, "type": "private"},
  37. }})
  38. }))
  39. t.Cleanup(srv.Close)
  40. swapTestBot(t, srv.URL)
  41. return func() []string {
  42. mu.Lock()
  43. defer mu.Unlock()
  44. return append([]string(nil), texts...)
  45. }
  46. }
  47. // With no sub or web domain set, the only host the bot knows is the machine
  48. // name, which need not resolve; the links must not depend on reaching it.
  49. func TestIndividualLinksDoNotNeedAResolvableHost(t *testing.T) {
  50. sent := recordingBotServer(t)
  51. dbtest.InitDB(t, filepath.Join(t.TempDir(), "x-ui.db"))
  52. service.RegisterSubLinkProvider(sub.NewLinkProvider())
  53. const uuid = "11111111-2222-4333-8444-555555555555"
  54. db := database.GetDB()
  55. ib := &model.Inbound{
  56. UserId: 1, Tag: "in-443", Enable: true, Listen: "203.0.113.5", Port: 443,
  57. Protocol: model.VLESS, Remark: "in",
  58. Settings: `{"clients":[{"id":"` + uuid + `","email":"` + ownerMail + `","tgId":4242,"subId":"sub-owned","enable":true}],"decryption":"none"}`,
  59. }
  60. if err := db.Create(ib).Error; err != nil {
  61. t.Fatalf("seed inbound: %v", err)
  62. }
  63. rec := &model.ClientRecord{Email: ownerMail, SubID: "sub-owned", UUID: uuid, TgID: ownerTgID, Enable: true}
  64. if err := db.Create(rec).Error; err != nil {
  65. t.Fatalf("seed client: %v", err)
  66. }
  67. if err := db.Create(&model.ClientInbound{ClientId: rec.Id, InboundId: ib.Id}).Error; err != nil {
  68. t.Fatalf("seed client_inbound: %v", err)
  69. }
  70. if err := db.Create(&xray.ClientTraffic{InboundId: ib.Id, Email: ownerMail, Enable: true}).Error; err != nil {
  71. t.Fatalf("seed traffic: %v", err)
  72. }
  73. origHost, origRunning := hostname, isRunning
  74. t.Cleanup(func() { hostname, isRunning = origHost, origRunning })
  75. hostname, isRunning = "unresolvable-panel-host.invalid", true
  76. tapClientLinks(t, &Tgbot{}, ownerTgID, "client_individual_links "+ownerMail)
  77. got := strings.Join(sent(), "\n")
  78. if !strings.Contains(got, "vless://"+uuid+"@203.0.113.5:443") {
  79. t.Fatalf("bot sent %q, want the client's vless link", got)
  80. }
  81. }