| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- package sub
- import (
- "fmt"
- "strings"
- "testing"
- "time"
- "github.com/mhsanaei/3x-ui/v3/internal/database"
- "github.com/mhsanaei/3x-ui/v3/internal/database/model"
- "github.com/mhsanaei/3x-ui/v3/internal/web/service"
- "github.com/mhsanaei/3x-ui/v3/internal/xray"
- )
- func TestSubClash_InfoNode_Active(t *testing.T) {
- setupInfoNodeTestDB(t)
- db := database.GetDB()
- ib := &model.Inbound{
- Id: 1,
- UserId: 1,
- Remark: "Germany-VLESS",
- Enable: true,
- Port: 443,
- Protocol: model.VLESS,
- Settings: `{"clients":[{"id":"c1-uuid","email":"[email protected]","subId":"sub-clash","enable":true,"totalGB":10737418240}]}`,
- StreamSettings: `{"network":"tcp","security":"none"}`,
- }
- if err := db.Create(ib).Error; err != nil {
- t.Fatal(err)
- }
- rec := &model.ClientRecord{
- Id: 1,
- Email: "[email protected]",
- SubID: "sub-clash",
- UUID: "c1-uuid",
- Enable: true,
- TotalGB: 10737418240,
- }
- if err := db.Create(rec).Error; err != nil {
- t.Fatal(err)
- }
- if err := db.Create(&model.ClientInbound{InboundId: 1, ClientId: 1}).Error; err != nil {
- t.Fatal(err)
- }
- if err := db.Create(&xray.ClientTraffic{
- InboundId: 1,
- Email: "[email protected]",
- Up: 1073741824,
- Down: 1073741824,
- Total: 10737418240,
- Enable: true,
- }).Error; err != nil {
- t.Fatal(err)
- }
- sub := NewSubService("{{EMAIL}}|📊{{TRAFFIC_LEFT}}")
- sub.subInfoNodeEnable = true
- clash := NewSubClashService(false, "", sub)
- out, _, err := clash.GetClash("sub-clash", "sub.example.com")
- if err != nil {
- t.Fatalf("GetClash: %v", err)
- }
- if !strings.Contains(out, "type: socks5") || !strings.Contains(out, "server: 127.0.0.1") {
- t.Fatalf("expected socks5 dummy node in clash YAML, got:\n%s", out)
- }
- if !strings.Contains(out, "[email protected]|📊8.00GB") {
- t.Fatalf("expected expanded remark on dummy proxy, got:\n%s", out)
- }
- }
- func TestSubClash_InfoNode_Expired(t *testing.T) {
- setupInfoNodeTestDB(t)
- db := database.GetDB()
- expiredTime := time.Now().Add(-24 * time.Hour).UnixMilli()
- ib := &model.Inbound{
- Id: 1,
- UserId: 1,
- Remark: "Germany-VLESS",
- Enable: true,
- Port: 443,
- Protocol: model.VLESS,
- Settings: fmt.Sprintf(`{"clients":[{"id":"c1-uuid","email":"[email protected]","subId":"sub-clash-exp","enable":true,"expiryTime":%d}]}`, expiredTime),
- StreamSettings: `{"network":"tcp","security":"none"}`,
- }
- if err := db.Create(ib).Error; err != nil {
- t.Fatal(err)
- }
- rec := &model.ClientRecord{
- Id: 1,
- Email: "[email protected]",
- SubID: "sub-clash-exp",
- UUID: "c1-uuid",
- Enable: true,
- ExpiryTime: expiredTime,
- }
- if err := db.Create(rec).Error; err != nil {
- t.Fatal(err)
- }
- if err := db.Create(&model.ClientInbound{InboundId: 1, ClientId: 1}).Error; err != nil {
- t.Fatal(err)
- }
- if err := db.Create(&xray.ClientTraffic{
- InboundId: 1,
- Email: "[email protected]",
- ExpiryTime: expiredTime,
- Enable: true,
- }).Error; err != nil {
- t.Fatal(err)
- }
- sub := NewSubService("{{INBOUND}}")
- sub.subInfoNodeEnable = true
- sub.subExpiredTemplate = service.DefaultSubExpiredTemplate
- clash := NewSubClashService(false, "", sub)
- out, _, err := clash.GetClash("sub-clash-exp", "sub.example.com")
- if err != nil {
- t.Fatalf("GetClash: %v", err)
- }
- if !strings.Contains(out, "type: socks5") || !strings.Contains(out, "server: 127.0.0.1") {
- t.Fatalf("expected socks5 dummy node in clash YAML, got:\n%s", out)
- }
- if !strings.Contains(out, "Expired") || !strings.Contains(out, "[email protected]") {
- t.Fatalf("expected expired remark in clash YAML, got:\n%s", out)
- }
- if strings.Contains(out, "Germany-VLESS") {
- t.Fatalf("expired subscription must NOT contain working inbound, got:\n%s", out)
- }
- }
- func TestSubClash_InfoNode_Depleted(t *testing.T) {
- setupInfoNodeTestDB(t)
- db := database.GetDB()
- ib := &model.Inbound{
- Id: 1,
- UserId: 1,
- Remark: "Germany-VLESS",
- Enable: true,
- Port: 443,
- Protocol: model.VLESS,
- Settings: `{"clients":[{"id":"c1-uuid","email":"[email protected]","subId":"sub-clash-dep","enable":true,"totalGB":5368709120}]}`,
- StreamSettings: `{"network":"tcp","security":"none"}`,
- }
- if err := db.Create(ib).Error; err != nil {
- t.Fatal(err)
- }
- rec := &model.ClientRecord{
- Id: 1,
- Email: "[email protected]",
- SubID: "sub-clash-dep",
- UUID: "c1-uuid",
- Enable: true,
- TotalGB: 5368709120,
- }
- if err := db.Create(rec).Error; err != nil {
- t.Fatal(err)
- }
- if err := db.Create(&model.ClientInbound{InboundId: 1, ClientId: 1}).Error; err != nil {
- t.Fatal(err)
- }
- if err := db.Create(&xray.ClientTraffic{
- InboundId: 1,
- Email: "[email protected]",
- Up: 3221225472,
- Down: 2147483648,
- Total: 5368709120,
- Enable: true,
- }).Error; err != nil {
- t.Fatal(err)
- }
- sub := NewSubService("{{INBOUND}}")
- sub.subInfoNodeEnable = true
- sub.subTrafficDepletedTemplate = service.DefaultSubTrafficDepletedTemplate
- clash := NewSubClashService(false, "", sub)
- out, _, err := clash.GetClash("sub-clash-dep", "sub.example.com")
- if err != nil {
- t.Fatalf("GetClash: %v", err)
- }
- if !strings.Contains(out, "type: socks5") || !strings.Contains(out, "server: 127.0.0.1") {
- t.Fatalf("expected socks5 dummy node in clash YAML, got:\n%s", out)
- }
- if !strings.Contains(out, "Traffic Depleted") || !strings.Contains(out, "[email protected]") {
- t.Fatalf("expected depleted remark in clash YAML, got:\n%s", out)
- }
- if strings.Contains(out, "Germany-VLESS") {
- t.Fatalf("depleted subscription must NOT contain working inbound, got:\n%s", out)
- }
- }
- func TestSubClash_InfoNode_ProxyGroupOrder_DoesNotDefaultToDummy(t *testing.T) {
- setupInfoNodeTestDB(t)
- db := database.GetDB()
- ib := &model.Inbound{
- Id: 1,
- UserId: 1,
- Remark: "Germany-VLESS",
- Enable: true,
- Port: 443,
- Protocol: model.VLESS,
- Settings: `{"clients":[{"id":"c1-uuid","email":"[email protected]","subId":"sub-clash","enable":true,"totalGB":10737418240}]}`,
- StreamSettings: `{"network":"tcp","security":"none"}`,
- }
- if err := db.Create(ib).Error; err != nil {
- t.Fatal(err)
- }
- if err := db.Create(&model.ClientRecord{Id: 1, Email: "[email protected]", SubID: "sub-clash", UUID: "c1-uuid", Enable: true}).Error; err != nil {
- t.Fatal(err)
- }
- if err := db.Create(&model.ClientInbound{InboundId: 1, ClientId: 1}).Error; err != nil {
- t.Fatal(err)
- }
- if err := db.Create(&xray.ClientTraffic{InboundId: 1, Email: "[email protected]", Enable: true}).Error; err != nil {
- t.Fatal(err)
- }
- sub := NewSubService("{{EMAIL}}|📊{{TRAFFIC_LEFT}}|{{STATUS}}")
- sub.subInfoNodeEnable = true
- clash := NewSubClashService(false, "", sub)
- out, _, err := clash.GetClash("sub-clash", "sub.example.com")
- if err != nil {
- t.Fatalf("GetClash: %v", err)
- }
- // Status token must be rendered as active
- if !strings.Contains(out, "active") {
- t.Fatalf("expected active status in clash dummy remark, got:\n%s", out)
- }
- // In proxy-groups, PROXY select group must NOT have the dummy node as first member
- // PROXY group proxies should begin with real proxy
- if strings.Contains(out, "[email protected]|active") && strings.Contains(out, "proxies:\n - [email protected]|active") {
- t.Fatalf("PROXY group must NOT contain dummy info node as member, got:\n%s", out)
- }
- if !strings.Contains(out, "proxies:\n - Germany-VLESS\n - DIRECT") {
- t.Fatalf("expected real proxy Germany-VLESS in PROXY group, got:\n%s", out)
- }
- }
|