| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- package service
- import (
- "testing"
- "github.com/mhsanaei/3x-ui/v3/internal/database"
- "github.com/mhsanaei/3x-ui/v3/internal/database/model"
- )
- // TestAddInbound_ImportConvertsExternalProxyToHosts reproduces the panel report:
- // an inbound exported from a build that predated the hosts table carries its
- // external proxies inline in streamSettings.externalProxy. The one-time startup
- // migration that converts those to host rows is gated off after first run, so a
- // freshly imported inbound used to land with zero hosts (its external proxies
- // silently lost). AddInbound must convert them on import.
- func TestAddInbound_ImportConvertsExternalProxyToHosts(t *testing.T) {
- setupConflictDB(t)
- svc := &InboundService{}
- stream := `{
- "network":"ws",
- "wsSettings":{"path":"/req3","host":"astr.khafanha.ir"},
- "security":"none",
- "externalProxy":[
- {"forceTls":"same","dest":"snapp.ir","port":8080,"remark":"","sni":"","alpn":[],"pinnedPeerCertSha256":[],"echConfigList":""},
- {"forceTls":"tls","dest":"cdn.example.com","port":8443,"remark":"front","sni":"sni.example.com","fingerprint":"chrome","alpn":["h2","h3"],"pinnedPeerCertSha256":["AAAA"],"echConfigList":"ECHV"}
- ]
- }`
- settings := `{"clients":[{"id":"6df5616b-ebfd-4186-86d5-4bce29fe8805","email":"imp_user","subId":"s-imp","enable":true}],"decryption":"none","encryption":"none"}`
- in := &model.Inbound{
- UserId: 1,
- Tag: "in-8080-tcp",
- Enable: true,
- Listen: "",
- Port: 8080,
- Protocol: model.VLESS,
- StreamSettings: stream,
- Settings: settings,
- }
- created, _, err := svc.AddInbound(in)
- if err != nil {
- t.Fatalf("import inbound: %v", err)
- }
- var hosts []model.Host
- if err := database.GetDB().Where("inbound_id = ?", created.Id).Order("sort_order asc").Find(&hosts).Error; err != nil {
- t.Fatalf("load hosts: %v", err)
- }
- if len(hosts) != 2 {
- t.Fatalf("hosts = %d, want 2 (one per externalProxy entry)", len(hosts))
- }
- a := hosts[0]
- if a.SortOrder != 0 || a.Security != "same" || a.Address != "snapp.ir" || a.Port != 8080 {
- t.Fatalf("host A mapping wrong: %+v", a)
- }
- if a.Remark == "" {
- t.Fatalf("host A remark must be backfilled for a blank externalProxy remark, got empty")
- }
- b := hosts[1]
- if b.SortOrder != 1 || b.Security != "tls" || b.Address != "cdn.example.com" || b.Port != 8443 ||
- b.Remark != "front" || b.Sni != "sni.example.com" || b.Fingerprint != "chrome" || b.EchConfigList != "ECHV" {
- t.Fatalf("host B mapping wrong: %+v", b)
- }
- if len(b.Alpn) != 2 || b.Alpn[0] != "h2" || b.Alpn[1] != "h3" {
- t.Fatalf("host B alpn = %v, want [h2 h3]", b.Alpn)
- }
- if len(b.PinnedPeerCertSha256) != 1 || b.PinnedPeerCertSha256[0] != "AAAA" {
- t.Fatalf("host B pins = %v, want [AAAA]", b.PinnedPeerCertSha256)
- }
- }
- // TestAddInbound_NoExternalProxyCreatesNoHosts guards the no-op path: an inbound
- // built by the current UI (no externalProxy) must not gain phantom host rows.
- func TestAddInbound_NoExternalProxyCreatesNoHosts(t *testing.T) {
- setupConflictDB(t)
- svc := &InboundService{}
- in := &model.Inbound{
- UserId: 1,
- Tag: "in-9201-tcp",
- Enable: true,
- Listen: "0.0.0.0",
- Port: 9201,
- Protocol: model.VLESS,
- StreamSettings: `{"network":"tcp","security":"none"}`,
- Settings: `{"clients":[{"id":"77777777-7777-7777-7777-777777777777","email":"plain","subId":"s-plain","enable":true}],"decryption":"none","encryption":"none"}`,
- }
- created, _, err := svc.AddInbound(in)
- if err != nil {
- t.Fatalf("add inbound: %v", err)
- }
- var count int64
- if err := database.GetDB().Model(&model.Host{}).Where("inbound_id = ?", created.Id).Count(&count).Error; err != nil {
- t.Fatalf("count hosts: %v", err)
- }
- if count != 0 {
- t.Fatalf("host count = %d, want 0", count)
- }
- }
|