mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 03:05:02 +03:00
a9191cb468
UpdateInboundClient carried the stored forwardedPorts forward whenever the incoming value was empty, and Client.ForwardedPorts was a plain omitempty string, so clearing the field in the client editor, as its hint says to, reported success while the settings kept the old ports and the host kept relaying them to the client's device. The field is now a pointer, as KeepAlive is: nil (not sent) keeps the stored spec and "" withdraws it. The client record follows the live spec on every update, and a one-time seeder fills records that still hold their creation value from the inbound settings, so the editor no longer posts an empty field that would drop forwards added later.
113 lines
3.7 KiB
Go
113 lines
3.7 KiB
Go
package model
|
|
|
|
import (
|
|
"encoding/json"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestClientToRecordRoundTripWireGuard(t *testing.T) {
|
|
c := &Client{
|
|
Email: "alice@example.test",
|
|
Enable: true,
|
|
PrivateKey: "cGVlci1wcml2YXRlLWtleS1iYXNlNjQtMzJieXRlcw==",
|
|
PublicKey: "cGVlci1wdWJsaWMta2V5LWJhc2U2NC0zMmJ5dGVzISE=",
|
|
AllowedIPs: []string{"10.0.0.2/32", "fd00::2/128"},
|
|
PreSharedKey: "cHNrLWJhc2U2NC0zMmJ5dGVzLXBsYWNlaG9sZGVyISE=",
|
|
KeepAlive: KeepAlivePtr(25),
|
|
|
|
ForwardedPorts: ForwardedPortsPtr("80,8000-8100"),
|
|
}
|
|
|
|
rec := c.ToRecord()
|
|
if rec.AllowedIPs != "10.0.0.2/32,fd00::2/128" {
|
|
t.Fatalf("AllowedIPs CSV = %q, want %q", rec.AllowedIPs, "10.0.0.2/32,fd00::2/128")
|
|
}
|
|
|
|
got := rec.ToClient()
|
|
for _, f := range []struct {
|
|
name string
|
|
a, b any
|
|
}{
|
|
{"PrivateKey", c.PrivateKey, got.PrivateKey},
|
|
{"PublicKey", c.PublicKey, got.PublicKey},
|
|
{"PreSharedKey", c.PreSharedKey, got.PreSharedKey},
|
|
{"KeepAlive", c.KeepAliveSeconds(), got.KeepAliveSeconds()},
|
|
{"ForwardedPorts", c.ForwardedPortsSpec(), got.ForwardedPortsSpec()},
|
|
} {
|
|
if f.a != f.b {
|
|
t.Errorf("%s round-trip = %v, want %v", f.name, f.b, f.a)
|
|
}
|
|
}
|
|
if !reflect.DeepEqual(got.AllowedIPs, c.AllowedIPs) {
|
|
t.Errorf("AllowedIPs round-trip = %v, want %v", got.AllowedIPs, c.AllowedIPs)
|
|
}
|
|
}
|
|
|
|
// ToClient feeds the settings JSON of every protocol, not just the tunnels, and
|
|
// that JSON reaches xray-core verbatim through GenXrayInboundConfig.
|
|
func TestClientToClientOmitsUnsetKeepAlive(t *testing.T) {
|
|
rec := &ClientRecord{Email: "vless@example.test", UUID: "11111111-2222-3333-4444-555555555555", Enable: true}
|
|
|
|
if got := rec.ToClient().KeepAlive; got != nil {
|
|
t.Fatalf("KeepAlive for a record that never set one = %s, want nil", *got)
|
|
}
|
|
blob, err := json.Marshal(map[string][]Client{"clients": {*rec.ToClient()}})
|
|
if err != nil {
|
|
t.Fatalf("marshal settings payload: %v", err)
|
|
}
|
|
if strings.Contains(string(blob), "keepAlive") {
|
|
t.Fatalf("settings payload carries keepAlive for a non-tunnel client: %s", blob)
|
|
}
|
|
}
|
|
|
|
// An explicit "" withdraws the forwards, which a re-saved record must never ask for.
|
|
func TestClientToClientOmitsUnsetForwardedPorts(t *testing.T) {
|
|
rec := &ClientRecord{Email: "awg@example.test", Enable: true}
|
|
|
|
if got := rec.ToClient().ForwardedPorts; got != nil {
|
|
t.Fatalf("ForwardedPorts for a record that never set any = %q, want nil", *got)
|
|
}
|
|
}
|
|
|
|
func TestClientRecordEmptyAllowedIPs(t *testing.T) {
|
|
rec := &ClientRecord{Email: "bob@example.test", AllowedIPs: ""}
|
|
if got := rec.ToClient().AllowedIPs; got != nil {
|
|
t.Fatalf("empty CSV → AllowedIPs = %v, want nil", got)
|
|
}
|
|
|
|
rec.AllowedIPs = " 10.0.0.5/32 , ,"
|
|
if got := rec.ToClient().AllowedIPs; !reflect.DeepEqual(got, []string{"10.0.0.5/32"}) {
|
|
t.Fatalf("trimmed CSV → AllowedIPs = %v, want [10.0.0.5/32]", got)
|
|
}
|
|
}
|
|
|
|
func TestMergeClientRecordWireGuardKeysPreserved(t *testing.T) {
|
|
existing := &ClientRecord{
|
|
Email: "carol@example.test",
|
|
PrivateKey: "existing-private",
|
|
PublicKey: "existing-public",
|
|
AllowedIPs: "10.0.0.7/32",
|
|
UpdatedAt: 100,
|
|
}
|
|
incomingEmpty := &ClientRecord{Email: "carol@example.test", UpdatedAt: 200}
|
|
MergeClientRecord(existing, incomingEmpty)
|
|
if existing.PrivateKey != "existing-private" || existing.PublicKey != "existing-public" {
|
|
t.Fatalf("empty incoming wiped keys: priv=%q pub=%q", existing.PrivateKey, existing.PublicKey)
|
|
}
|
|
if existing.AllowedIPs != "10.0.0.7/32" {
|
|
t.Fatalf("empty incoming wiped allowedIPs: %q", existing.AllowedIPs)
|
|
}
|
|
|
|
incomingNewer := &ClientRecord{
|
|
Email: "carol@example.test",
|
|
AllowedIPs: "10.0.0.8/32",
|
|
UpdatedAt: 300,
|
|
}
|
|
MergeClientRecord(existing, incomingNewer)
|
|
if existing.AllowedIPs != "10.0.0.8/32" {
|
|
t.Fatalf("newer allowedIPs not applied: %q", existing.AllowedIPs)
|
|
}
|
|
}
|