import_db_restart_test.go 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. package controller
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "mime/multipart"
  6. "net/http"
  7. "net/http/httptest"
  8. "os"
  9. "path/filepath"
  10. "runtime"
  11. "testing"
  12. "time"
  13. "github.com/mhsanaei/3x-ui/v3/internal/database"
  14. "github.com/mhsanaei/3x-ui/v3/internal/web/global"
  15. "github.com/mhsanaei/3x-ui/v3/internal/xray"
  16. "github.com/gin-gonic/gin"
  17. )
  18. // A successful import must schedule the panel restart itself: the browser's
  19. // restartPanel follow-up can 401 once the imported users table lands (#6446).
  20. func TestImportDBSchedulesPanelRestart(t *testing.T) {
  21. if runtime.GOOS == "windows" {
  22. t.Skip("the stub xray binary is a shell script")
  23. }
  24. uploadPath := filepath.Join(t.TempDir(), "x-ui.db")
  25. if err := database.InitDB(uploadPath); err != nil {
  26. t.Fatalf("InitDB(upload): %v", err)
  27. }
  28. if err := database.CloseDB(); err != nil {
  29. t.Fatalf("CloseDB(upload): %v", err)
  30. }
  31. upload, err := os.ReadFile(uploadPath)
  32. if err != nil {
  33. t.Fatalf("read upload: %v", err)
  34. }
  35. newHostTestDB(t)
  36. binDir := t.TempDir()
  37. t.Setenv("XUI_BIN_FOLDER", binDir)
  38. t.Setenv("XUI_LOG_FOLDER", t.TempDir())
  39. if err := os.WriteFile(filepath.Join(binDir, xray.GetBinaryName()), []byte("#!/bin/sh\nexit 0\n"), 0o755); err != nil {
  40. t.Fatalf("write stub xray: %v", err)
  41. }
  42. restarts := make(chan struct{}, 1)
  43. global.SetRestartHook(func() {
  44. select {
  45. case restarts <- struct{}{}:
  46. default:
  47. }
  48. })
  49. t.Cleanup(func() { global.SetRestartHook(func() {}) })
  50. var body bytes.Buffer
  51. mw := multipart.NewWriter(&body)
  52. part, err := mw.CreateFormFile("db", "x-ui.db")
  53. if err != nil {
  54. t.Fatalf("CreateFormFile: %v", err)
  55. }
  56. if _, err := part.Write(upload); err != nil {
  57. t.Fatalf("write part: %v", err)
  58. }
  59. if err := mw.Close(); err != nil {
  60. t.Fatalf("close multipart: %v", err)
  61. }
  62. a := &ServerController{}
  63. engine := gin.New()
  64. engine.POST("/panel/api/server/importDB", a.importDB)
  65. req := httptest.NewRequest(http.MethodPost, "/panel/api/server/importDB", &body)
  66. req.Header.Set("Content-Type", mw.FormDataContentType())
  67. w := httptest.NewRecorder()
  68. engine.ServeHTTP(w, req)
  69. var env hostEnvelope
  70. if err := json.Unmarshal(w.Body.Bytes(), &env); err != nil {
  71. t.Fatalf("decode envelope: %v body=%s", err, w.Body.String())
  72. }
  73. if !env.Success {
  74. t.Fatalf("importDB failed: %s", env.Msg)
  75. }
  76. select {
  77. case <-restarts:
  78. case <-time.After(6 * time.Second):
  79. t.Fatal("importDB succeeded but no panel restart was scheduled within 6s")
  80. }
  81. }