|
|
@@ -0,0 +1,113 @@
|
|
|
+package xray
|
|
|
+
|
|
|
+import (
|
|
|
+ "testing"
|
|
|
+
|
|
|
+ "github.com/mhsanaei/3x-ui/v3/internal/util/json_util"
|
|
|
+)
|
|
|
+
|
|
|
+func hotConfigWithClients(clients string) *Config {
|
|
|
+ cfg := makeHotConfig()
|
|
|
+ for i := range cfg.InboundConfigs {
|
|
|
+ if cfg.InboundConfigs[i].Tag == "inbound-1080" {
|
|
|
+ cfg.InboundConfigs[i].Settings = json_util.RawMessage(`{"clients":` + clients + `}`)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return cfg
|
|
|
+}
|
|
|
+
|
|
|
+// diffInboundUsers refuses shadowsocks and hysteria, so their dropped clients
|
|
|
+// reach the guard through the inbound instead of through a per-user op.
|
|
|
+func TestHotDiffDropsUsersOnProtocolsItCannotDiff(t *testing.T) {
|
|
|
+ for _, protocol := range []string{"shadowsocks", "hysteria"} {
|
|
|
+ t.Run(protocol, func(t *testing.T) {
|
|
|
+ withClients := func(clients string) *Config {
|
|
|
+ cfg := makeHotConfig()
|
|
|
+ ib := &cfg.InboundConfigs[1]
|
|
|
+ ib.Protocol = protocol
|
|
|
+ ib.Settings = json_util.RawMessage(`{"clients":` + clients + `}`)
|
|
|
+ return cfg
|
|
|
+ }
|
|
|
+ both := `[{"email":"a@x","password":"pa"},{"email":"b@x","password":"pb"}]`
|
|
|
+ onlyA := `[{"email":"a@x","password":"pa"}]`
|
|
|
+
|
|
|
+ diff, ok := ComputeHotDiff(withClients(both), withClients(onlyA))
|
|
|
+ if !ok {
|
|
|
+ t.Fatal("a dropped client must stay API-applicable")
|
|
|
+ }
|
|
|
+ if !diff.DropsUsers() {
|
|
|
+ t.Fatalf("DropsUsers = false for a dropped %s client (removed=%+v added=%+v dropped=%+v)",
|
|
|
+ protocol, diff.RemovedUsers, diff.AddedUsers, diff.DroppedClients)
|
|
|
+ }
|
|
|
+
|
|
|
+ edited, ok := ComputeHotDiff(withClients(both), withClients(`[{"email":"a@x","password":"pa"},{"email":"b@x","password":"pb","level":1}]`))
|
|
|
+ if !ok {
|
|
|
+ t.Fatal("a client edit must stay API-applicable")
|
|
|
+ }
|
|
|
+ if edited.DropsUsers() {
|
|
|
+ t.Fatal("an edited client is still served")
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// A disable or a delete takes the client out of the generated config; an edit
|
|
|
+// keeps the email and re-adds it. Only the first leaves sessions running.
|
|
|
+func TestHotDiffDropsUsers(t *testing.T) {
|
|
|
+ cases := []struct {
|
|
|
+ name string
|
|
|
+ old string
|
|
|
+ new string
|
|
|
+ want bool
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ "client taken out of the config",
|
|
|
+ `[{"email":"a@x","id":"11111111-1111-1111-1111-111111111111","enable":true}]`,
|
|
|
+ `[]`,
|
|
|
+ true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "edited in place",
|
|
|
+ `[{"email":"a@x","id":"11111111-1111-1111-1111-111111111111","enable":true}]`,
|
|
|
+ `[{"email":"a@x","id":"11111111-1111-1111-1111-111111111111","limitIp":5,"enable":true}]`,
|
|
|
+ false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "added",
|
|
|
+ `[]`,
|
|
|
+ `[{"email":"a@x","id":"11111111-1111-1111-1111-111111111111","enable":true}]`,
|
|
|
+ false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "renamed",
|
|
|
+ `[{"email":"a@x","id":"11111111-1111-1111-1111-111111111111","enable":true}]`,
|
|
|
+ `[{"email":"b@x","id":"11111111-1111-1111-1111-111111111111","enable":true}]`,
|
|
|
+ true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "one dropped, one edited",
|
|
|
+ `[{"email":"a@x","id":"11111111-1111-1111-1111-111111111111","enable":true},{"email":"b@x","id":"22222222-2222-2222-2222-222222222222","enable":true}]`,
|
|
|
+ `[{"email":"b@x","id":"22222222-2222-2222-2222-222222222222","limitIp":5,"enable":true}]`,
|
|
|
+ true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "unchanged",
|
|
|
+ `[{"email":"a@x","id":"11111111-1111-1111-1111-111111111111","enable":true}]`,
|
|
|
+ `[{"email":"a@x","id":"11111111-1111-1111-1111-111111111111","enable":true}]`,
|
|
|
+ false,
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, tc := range cases {
|
|
|
+ t.Run(tc.name, func(t *testing.T) {
|
|
|
+ diff, ok := ComputeHotDiff(hotConfigWithClients(tc.old), hotConfigWithClients(tc.new))
|
|
|
+ if !ok {
|
|
|
+ t.Fatalf("diff of %s -> %s must be API-applicable", tc.old, tc.new)
|
|
|
+ }
|
|
|
+ if got := diff.DropsUsers(); got != tc.want {
|
|
|
+ t.Fatalf("DropsUsers = %v, want %v (removed=%+v added=%+v)",
|
|
|
+ got, tc.want, diff.RemovedUsers, diff.AddedUsers)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|