mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-24 11:57:15 +00:00
a255ab7c65
* fix(node): don't stamp InboundsAdoptedAt when the sync adopted nothing
Onboarding a node in selected mode with an empty tag list empties the
traffic snapshot via FilterNodeSnapshot before the merge sees it, so
the first clean sync adopts nothing — yet syncOne stamped
InboundsAdoptedAt regardless. The flag is documented as the first
clean sync that imported the node's pre-existing inbounds; stamping it
in this state arms the reconcile sweep (gated on the flag since
200ea091, the fix for #5898) to delete the node's pre-existing
inbounds on their next real sync: registering first and choosing tags
afterwards destroyed the node's inbounds.
Gate the stamp on the sync actually being able to adopt: in selected
mode, at least one selected tag or adopted alias must exist for the
snapshot filter to keep anything.
Fixes #6283
* restore atomicBool tests; trim comment to repo 2-line cap
The new test file unintentionally replaced the existing
node_traffic_sync_job_test.go, dropping its four atomicBool tests;
restore them and keep only an additive diff. Trim the syncCanAdopt
doc comment to the repository's 2-line comment cap.
* trim syncCanAdopt comment to the 2-line cap
113 lines
2.5 KiB
Go
113 lines
2.5 KiB
Go
package job
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
)
|
|
|
|
func TestAtomicBool_DefaultIsFalse(t *testing.T) {
|
|
var a atomicBool
|
|
if a.takeAndReset() {
|
|
t.Fatal("default atomicBool should report false")
|
|
}
|
|
}
|
|
|
|
func TestAtomicBool_SetThenTakeReturnsTrueOnce(t *testing.T) {
|
|
var a atomicBool
|
|
a.set()
|
|
if !a.takeAndReset() {
|
|
t.Fatal("takeAndReset after set should return true")
|
|
}
|
|
if a.takeAndReset() {
|
|
t.Fatal("second takeAndReset should return false (state was reset)")
|
|
}
|
|
}
|
|
|
|
func TestAtomicBool_SetIsIdempotent(t *testing.T) {
|
|
var a atomicBool
|
|
a.set()
|
|
a.set()
|
|
a.set()
|
|
if !a.takeAndReset() {
|
|
t.Fatal("repeated set should still leave the flag true")
|
|
}
|
|
if a.takeAndReset() {
|
|
t.Fatal("flag should be cleared after the first take")
|
|
}
|
|
}
|
|
|
|
func TestAtomicBool_ConcurrentSettersExactlyOneTakeWins(t *testing.T) {
|
|
var a atomicBool
|
|
const setters = 100
|
|
const readers = 20
|
|
|
|
var wg sync.WaitGroup
|
|
for range setters {
|
|
wg.Go(func() {
|
|
a.set()
|
|
})
|
|
}
|
|
wg.Wait()
|
|
|
|
trueCount := 0
|
|
var rwg sync.WaitGroup
|
|
var mu sync.Mutex
|
|
for range readers {
|
|
rwg.Go(func() {
|
|
if a.takeAndReset() {
|
|
mu.Lock()
|
|
trueCount++
|
|
mu.Unlock()
|
|
}
|
|
})
|
|
}
|
|
rwg.Wait()
|
|
|
|
if trueCount != 1 {
|
|
t.Fatalf("expected exactly one reader to observe true, got %d", trueCount)
|
|
}
|
|
}
|
|
|
|
// Regression (#6283): a node onboarded in selected mode with an empty tag
|
|
// list empties its snapshot via FilterNodeSnapshot before the merge sees it,
|
|
// so that sync adopts nothing and must not stamp InboundsAdoptedAt.
|
|
func TestSyncCanAdoptInbounds(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
node *model.Node
|
|
aliases []string
|
|
expected bool
|
|
}{
|
|
{"all mode always adopts", &model.Node{InboundSyncMode: "all"}, nil, true},
|
|
{
|
|
"selected with tags adopts",
|
|
&model.Node{InboundSyncMode: "selected", InboundTags: []string{"in-443-tcp"}},
|
|
nil,
|
|
true,
|
|
},
|
|
{
|
|
"selected empty with adopted alias adopts",
|
|
&model.Node{InboundSyncMode: "selected"},
|
|
[]string{"in-443-tcp"},
|
|
true,
|
|
},
|
|
{
|
|
// The reported bug: registering in selected mode and choosing
|
|
// tags afterwards stamped adoption while adopting nothing.
|
|
"selected empty with no aliases adopts nothing",
|
|
&model.Node{InboundSyncMode: "selected"},
|
|
nil,
|
|
false,
|
|
},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
if got := syncCanAdoptInbounds(c.node, c.aliases); got != c.expected {
|
|
t.Fatalf("syncCanAdoptInbounds(%+v, %v) = %v, want %v", c.node, c.aliases, got, c.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|