Release 3X-UI for Docker / build (push) Canceled after 0s
Release 3X-UI / build (386) (push) Canceled after 0s
Release 3X-UI / build (amd64) (push) Canceled after 0s
Release 3X-UI / build (arm64) (push) Canceled after 0s
Release 3X-UI / build (armv5) (push) Canceled after 0s
Release 3X-UI / build (armv6) (push) Canceled after 0s
Release 3X-UI / build (armv7) (push) Canceled after 0s
Release 3X-UI / build (s390x) (push) Canceled after 0s
Release 3X-UI / Build for Windows (amd64) (push) Canceled after 0s
Release 3X-UI / Publish rolling dev release (push) Canceled after 0s
89 lines
2.8 KiB
Go
89 lines
2.8 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/util/json_util"
|
|
)
|
|
|
|
// rulesOf unmarshals a router config and returns its rules for assertions.
|
|
func rulesOf(t *testing.T, raw json_util.RawMessage) []map[string]any {
|
|
t.Helper()
|
|
var parsed struct {
|
|
Rules []map[string]any `json:"rules"`
|
|
}
|
|
if err := json.Unmarshal(raw, &parsed); err != nil {
|
|
t.Fatalf("unmarshal result: %v", err)
|
|
}
|
|
return parsed.Rules
|
|
}
|
|
|
|
func TestStripDisabledRules(t *testing.T) {
|
|
t.Run("empty config is returned untouched", func(t *testing.T) {
|
|
if got := stripDisabledRules(nil); got != nil {
|
|
t.Fatalf("expected nil passthrough, got %s", got)
|
|
}
|
|
})
|
|
|
|
t.Run("missing or empty rules is a no-op", func(t *testing.T) {
|
|
in := json_util.RawMessage(`{"domainStrategy":"AsIs"}`)
|
|
if got := stripDisabledRules(in); string(got) != string(in) {
|
|
t.Fatalf("config without rules was modified: %s", got)
|
|
}
|
|
})
|
|
|
|
t.Run("drops disabled rules and strips the enabled key from the rest", func(t *testing.T) {
|
|
in := json_util.RawMessage(`{"rules":[
|
|
{"outboundTag":"direct","domain":["a.com"],"enabled":true},
|
|
{"outboundTag":"block","domain":["b.com"],"enabled":false},
|
|
{"outboundTag":"proxy","domain":["c.com"]}
|
|
]}`)
|
|
rules := rulesOf(t, stripDisabledRules(in))
|
|
if len(rules) != 2 {
|
|
t.Fatalf("expected 2 active rules, got %d: %v", len(rules), rules)
|
|
}
|
|
for _, r := range rules {
|
|
if _, ok := r["enabled"]; ok {
|
|
t.Fatalf("enabled key must not survive into the runtime config: %v", r)
|
|
}
|
|
}
|
|
if rules[0]["outboundTag"] != "direct" || rules[1]["outboundTag"] != "proxy" {
|
|
t.Fatalf("kept rules or their order are wrong: %v", rules)
|
|
}
|
|
})
|
|
|
|
t.Run("never drops the api rule even when marked disabled", func(t *testing.T) {
|
|
in := json_util.RawMessage(`{"rules":[
|
|
{"inboundTag":["api"],"outboundTag":"api","enabled":false},
|
|
{"outboundTag":"block","domain":["b.com"],"enabled":false}
|
|
]}`)
|
|
rules := rulesOf(t, stripDisabledRules(in))
|
|
if len(rules) != 1 {
|
|
t.Fatalf("expected only the api rule to survive, got %d: %v", len(rules), rules)
|
|
}
|
|
if rules[0]["outboundTag"] != "api" {
|
|
t.Fatalf("api rule was dropped: %v", rules)
|
|
}
|
|
if _, ok := rules[0]["enabled"]; ok {
|
|
t.Fatalf("enabled key must be stripped from the api rule too: %v", rules[0])
|
|
}
|
|
})
|
|
|
|
t.Run("non-object rules pass through, disabled object is dropped", func(t *testing.T) {
|
|
in := json_util.RawMessage(`{"rules":["weird",{"outboundTag":"block","enabled":false}]}`)
|
|
var parsed struct {
|
|
Rules []any `json:"rules"`
|
|
}
|
|
if err := json.Unmarshal(stripDisabledRules(in), &parsed); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(parsed.Rules) != 1 {
|
|
t.Fatalf("expected 1 surviving rule (the string), got %v", parsed.Rules)
|
|
}
|
|
if s, _ := parsed.Rules[0].(string); s != "weird" {
|
|
t.Fatalf("non-object rule should be preserved, got %v", parsed.Rules[0])
|
|
}
|
|
})
|
|
}
|