agent_nodes gains probe_only; getConfig always serves the flag and withholds the sing-box payload (null config, empty hash) for flagged nodes while probe tasks, fast interval, stat iface and update intents ride as usual. The agent gates both core branches when the local agent.json preset or the master flag is on, retires a running core once (best-effort tail flush, switchover bookkeeping untouched so turning the flag off self-heals through the existing hash-current-but-not-running path), and makes the traffic cycle an explicit no-op. Panel: node create/update accept probeOnly, the node view overlays it from the binding, the node form gains a switch (carried into the generated install command), the list shows a probe-only tag in the core-version cell, and install-agent.sh --probe-only writes the preset, skips the core download/self-check and inherits the flag on no-arg reruns.
81 lines
2.1 KiB
Go
81 lines
2.1 KiB
Go
package service
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
)
|
|
|
|
// V2.1.0: the node form's probeOnly switch persists onto the bound
|
|
// agent rows via UpdateFromRequest (nil leaves the flag untouched),
|
|
// and the list view overlays it from the binding.
|
|
func TestNodeProbeOnlyUpdateAndView(t *testing.T) {
|
|
setupConflictDB(t)
|
|
db := database.GetDB()
|
|
|
|
node := &model.Node{Id: 7601, Name: "probe-svc", Enable: true, Status: "online"}
|
|
if err := db.Create(node).Error; err != nil {
|
|
t.Fatalf("create node: %v", err)
|
|
}
|
|
ag := &model.AgentNode{TokenHash: "hash-probe-svc", Name: "probe-svc", NodeID: &node.Id}
|
|
if err := db.Create(ag).Error; err != nil {
|
|
t.Fatalf("create agent: %v", err)
|
|
}
|
|
|
|
svc := &NodeService{}
|
|
truth := true
|
|
lies := false
|
|
base := func() *NodeMutationRequest {
|
|
return &NodeMutationRequest{
|
|
Name: "probe-svc", NodeType: "agent", Enable: true,
|
|
Scheme: "https", BasePath: "/",
|
|
}
|
|
}
|
|
|
|
// Nil: flag untouched (still false).
|
|
if err := svc.UpdateFromRequest(node.Id, base()); err != nil {
|
|
t.Fatalf("update (nil): %v", err)
|
|
}
|
|
var row model.AgentNode
|
|
if err := db.First(&row, ag.Id).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if row.ProbeOnly {
|
|
t.Fatal("nil probeOnly must not set the flag")
|
|
}
|
|
|
|
// True: flag set on the agent row, and the annotated view shows it.
|
|
req := base()
|
|
req.ProbeOnly = &truth
|
|
if err := svc.UpdateFromRequest(node.Id, req); err != nil {
|
|
t.Fatalf("update (true): %v", err)
|
|
}
|
|
if err := db.First(&row, ag.Id).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !row.ProbeOnly {
|
|
t.Fatal("probeOnly=true not persisted on the agent row")
|
|
}
|
|
view, err := svc.GetViewById(node.Id)
|
|
if err != nil {
|
|
t.Fatalf("GetViewById: %v", err)
|
|
}
|
|
if !view.ProbeOnly {
|
|
t.Fatal("view probeOnly = false, want true from the binding")
|
|
}
|
|
|
|
// False: explicit off wins over the stored value.
|
|
req = base()
|
|
req.ProbeOnly = &lies
|
|
if err := svc.UpdateFromRequest(node.Id, req); err != nil {
|
|
t.Fatalf("update (false): %v", err)
|
|
}
|
|
if err := db.First(&row, ag.Id).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if row.ProbeOnly {
|
|
t.Fatal("probeOnly=false not persisted on the agent row")
|
|
}
|
|
}
|