| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- package job
- import (
- "net/http"
- "net/http/httptest"
- "path/filepath"
- "strconv"
- "strings"
- "testing"
- "github.com/op/go-logging"
- "github.com/mhsanaei/3x-ui/v3/internal/database"
- "github.com/mhsanaei/3x-ui/v3/internal/database/dbtest"
- "github.com/mhsanaei/3x-ui/v3/internal/database/model"
- xuilogger "github.com/mhsanaei/3x-ui/v3/internal/logger"
- "github.com/mhsanaei/3x-ui/v3/internal/web/runtime"
- "github.com/mhsanaei/3x-ui/v3/internal/web/service"
- )
- func transitiveGuids(t *testing.T) []string {
- t.Helper()
- tree, err := (&service.NodeService{}).GetNodeTree()
- if err != nil {
- t.Fatalf("GetNodeTree: %v", err)
- }
- var out []string
- for _, n := range tree {
- if n.Transitive {
- out = append(out, n.Guid)
- }
- }
- return out
- }
- // The heartbeat skips a disabled node and never sees a deleted one, so the
- // sub-nodes it had learned from them stayed on the Nodes page for good.
- func TestHeartbeatDropsSubNodesOfNodesItNoLongerProbes(t *testing.T) {
- cases := []struct {
- name string
- retire func(t *testing.T, nodeID int)
- }{
- {"disabled", func(t *testing.T, nodeID int) {
- if err := (&service.NodeService{}).SetEnable(nodeID, false); err != nil {
- t.Fatalf("SetEnable: %v", err)
- }
- }},
- {"deleted", func(t *testing.T, nodeID int) {
- if err := (&service.NodeService{}).Delete(nodeID); err != nil {
- t.Fatalf("Delete: %v", err)
- }
- }},
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- xuilogger.InitLogger(logging.ERROR)
- dbtest.InitDB(t, filepath.Join(t.TempDir(), "x-ui.db"))
- runtime.SetManager(runtime.NewManager(runtime.LocalDeps{APIPort: func() int { return 0 }, SetNeedRestart: func() {}}))
- t.Cleanup(func() { runtime.SetManager(nil) })
- srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
- switch {
- case strings.HasSuffix(r.URL.Path, "server/status"):
- _, _ = w.Write([]byte(`{"success":true,"obj":{"panelGuid":"direct-guid","xray":{"state":"running"}}}`))
- case strings.HasSuffix(r.URL.Path, "server/descendants"):
- _, _ = w.Write([]byte(`{"success":true,"obj":[{"guid":"sub-guid","parentGuid":"direct-guid","name":"sub","status":"online"}]}`))
- default:
- _, _ = w.Write([]byte(`{"success":true}`))
- }
- }))
- t.Cleanup(srv.Close)
- host, port, _ := strings.Cut(strings.TrimPrefix(srv.URL, "http://"), ":")
- portNum, _ := strconv.Atoi(port)
- node := &model.Node{
- Name: "direct", Scheme: "http", Address: host, Port: portNum, BasePath: "/", ApiToken: "tok",
- Enable: true, Status: "unknown", AllowPrivateAddress: true, TlsVerifyMode: "verify",
- }
- if err := database.GetDB().Create(node).Error; err != nil {
- t.Fatalf("create node: %v", err)
- }
- hb := NewNodeHeartbeatJob()
- hb.Run()
- if got := transitiveGuids(t); len(got) != 1 || got[0] != "sub-guid" {
- t.Fatalf("sub-nodes after first heartbeat = %v, want [sub-guid]", got)
- }
- tc.retire(t, node.Id)
- hb.Run()
- if got := transitiveGuids(t); len(got) != 0 {
- t.Fatalf("sub-nodes after the node was %s = %v, want none", tc.name, got)
- }
- })
- }
- }
|