mirror of
https://gitcode.com/JianFeeeee/webui4frpc.git
synced 2026-09-21 17:37:56 +00:00
feat(M6): localOnly forward option (loopback→LAN rewrite for cluster, local direct), canvas diff-based create/revoke commands, revoke task semantics, topology derived canvas on sync nodes, LAN addr helper
This commit is contained in:
64
internal/cluster/ring_revoke_test.go
Normal file
64
internal/cluster/ring_revoke_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"webui4frpc/internal/store"
|
||||
)
|
||||
|
||||
// TestRevokeTaskRemovesTopology: a revoke task published to the ring removes
|
||||
// the forward from topology, calls Handler.Revoke, and logs forward.remove.
|
||||
func TestRevokeTaskRemovesTopology(t *testing.T) {
|
||||
revoked := false
|
||||
eng := NewEngine("n1", "n1:7500", "u", "p", "0.71.0", nil,
|
||||
&fakeHandler{load: Load{MemPct: 5, NetPct: 5},
|
||||
revoke: func(ctx context.Context, tk *Task) error { revoked = true; return nil }},
|
||||
func(ctx context.Context, next string, tk *Token) error { return nil },
|
||||
"n1:7500", true)
|
||||
|
||||
// establish a forward
|
||||
eng.state.AddPending(store.Local{Name: "web"}, store.Remote{Name: "frps1"}, store.Link{RemotePort: 18081})
|
||||
// lowest-load is n1 (only node): it claims + builds topology
|
||||
if err := eng.phase2(context.Background(), &Token{Cycle: 1, Phase: PhaseSync, State: eng.state}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(eng.state.TopologyList()) != 1 {
|
||||
t.Fatalf("topology after claim = %+v", eng.state.TopologyList())
|
||||
}
|
||||
|
||||
// publish a revoke task pointing at the same forward
|
||||
eng.state.AddRevoke(store.Local{Name: "web"}, store.Remote{Name: "frps1"}, store.Link{RemotePort: 18081})
|
||||
if err := eng.phase2(context.Background(), &Token{Cycle: 2, Phase: PhaseSync, State: eng.state}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(eng.state.TopologyList()) != 0 {
|
||||
t.Fatalf("topology after revoke = %+v", eng.state.TopologyList())
|
||||
}
|
||||
if !revoked {
|
||||
t.Fatal("Handler.Revoke was not called")
|
||||
}
|
||||
// log should contain forward.remove
|
||||
var sawRemove bool
|
||||
for _, e := range eng.Log.Snapshot() {
|
||||
if e.Kind == LogForwardRemove {
|
||||
sawRemove = true
|
||||
}
|
||||
}
|
||||
if !sawRemove {
|
||||
t.Fatalf("log missing forward.remove: %+v", eng.Log.Snapshot())
|
||||
}
|
||||
}
|
||||
|
||||
// TestRevokeIdempotent: revoking an already-missing forward does not error.
|
||||
func TestRevokeIdempotent(t *testing.T) {
|
||||
eng := newTestEngine("n1", true)
|
||||
eng.state.AddRevoke(store.Local{Name: "ghost"}, store.Remote{Name: "frps1"}, store.Link{RemotePort: 1})
|
||||
if err := eng.phase2(context.Background(), &Token{Cycle: 1, Phase: PhaseSync, State: eng.state}); err != nil {
|
||||
t.Fatalf("revoke missing: %v", err)
|
||||
}
|
||||
// no topology entry, no panic
|
||||
if len(eng.state.TopologyList()) != 0 {
|
||||
t.Fatal("should be empty")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user