精简P2: geodata 测试夹具改 protowire 手工编码(geodat.proto 字段号+proto3 零值省略语义原样复刻), 不再依赖 xray-core 生成类型写 .dat 夹具
This commit is contained in:
@@ -13,8 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/op/go-logging"
|
"github.com/op/go-logging"
|
||||||
xraygeodata "github.com/xtls/xray-core/common/geodata"
|
"google.golang.org/protobuf/encoding/protowire"
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
|
|
||||||
xuilogger "github.com/mhsanaei/3x-ui/v3/internal/logger"
|
xuilogger "github.com/mhsanaei/3x-ui/v3/internal/logger"
|
||||||
"github.com/mhsanaei/3x-ui/v3/internal/web/service"
|
"github.com/mhsanaei/3x-ui/v3/internal/web/service"
|
||||||
@@ -36,20 +35,47 @@ func newGeodataEngine(t *testing.T) *gin.Engine {
|
|||||||
return engine
|
return engine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The fixture databases are encoded with protowire: the wire layout
|
||||||
|
// (geodat.proto field numbers) is the contract under test, and spelling it
|
||||||
|
// out keeps the test independent of xray-core's generated types.
|
||||||
|
|
||||||
|
func geoWireBytes(num protowire.Number, payload []byte) []byte {
|
||||||
|
out := protowire.AppendTag(nil, num, protowire.BytesType)
|
||||||
|
return protowire.AppendBytes(out, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func geoWireString(num protowire.Number, value string) []byte {
|
||||||
|
out := protowire.AppendTag(nil, num, protowire.BytesType)
|
||||||
|
return protowire.AppendString(out, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func geoWireVarint(num protowire.Number, value uint64) []byte {
|
||||||
|
out := protowire.AppendTag(nil, num, protowire.VarintType)
|
||||||
|
return protowire.AppendVarint(out, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func geoFixtureDomain(typ uint64, value string, attrs ...string) []byte {
|
||||||
|
var msg []byte
|
||||||
|
if typ != 0 {
|
||||||
|
msg = append(msg, geoWireVarint(1, typ)...)
|
||||||
|
}
|
||||||
|
msg = append(msg, geoWireString(2, value)...)
|
||||||
|
for _, attr := range attrs {
|
||||||
|
attrMsg := geoWireString(1, attr)
|
||||||
|
attrMsg = append(attrMsg, geoWireVarint(2, 1)...) // bool_value = true
|
||||||
|
msg = append(msg, geoWireBytes(3, attrMsg)...)
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
func writeGeositeDB(t *testing.T, dir string) {
|
func writeGeositeDB(t *testing.T, dir string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
data, err := proto.Marshal(&xraygeodata.GeoSiteList{Entry: []*xraygeodata.GeoSite{
|
google := geoWireString(1, "google")
|
||||||
{Code: "google", Domain: []*xraygeodata.Domain{
|
google = append(google, geoWireBytes(2, geoFixtureDomain(2, "google.com"))...)
|
||||||
{Type: xraygeodata.Domain_Domain, Value: "google.com"},
|
google = append(google, geoWireBytes(2, geoFixtureDomain(3, "ads.google.com", "ads"))...)
|
||||||
{Type: xraygeodata.Domain_Full, Value: "ads.google.com", Attribute: []*xraygeodata.Domain_Attribute{
|
cn := geoWireString(1, "cn")
|
||||||
{Key: "ads", TypedValue: &xraygeodata.Domain_Attribute_BoolValue{BoolValue: true}},
|
cn = append(cn, geoWireBytes(2, geoFixtureDomain(2, "baidu.com"))...)
|
||||||
}},
|
data := append(geoWireBytes(1, google), geoWireBytes(1, cn)...)
|
||||||
}},
|
|
||||||
{Code: "cn", Domain: []*xraygeodata.Domain{{Type: xraygeodata.Domain_Domain, Value: "baidu.com"}}},
|
|
||||||
}})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("marshal geosite: %v", err)
|
|
||||||
}
|
|
||||||
if err := os.WriteFile(filepath.Join(dir, "geosite.dat"), data, 0o644); err != nil {
|
if err := os.WriteFile(filepath.Join(dir, "geosite.dat"), data, 0o644); err != nil {
|
||||||
t.Fatalf("write geosite.dat: %v", err)
|
t.Fatalf("write geosite.dat: %v", err)
|
||||||
}
|
}
|
||||||
@@ -58,12 +84,11 @@ func writeGeositeDB(t *testing.T, dir string) {
|
|||||||
func writeGeoipDB(t *testing.T, dir string) {
|
func writeGeoipDB(t *testing.T, dir string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
prefix := netip.MustParsePrefix("10.0.0.0/8")
|
prefix := netip.MustParsePrefix("10.0.0.0/8")
|
||||||
data, err := proto.Marshal(&xraygeodata.GeoIPList{Entry: []*xraygeodata.GeoIP{
|
cidr := geoWireBytes(1, prefix.Addr().AsSlice())
|
||||||
{Code: "private", Cidr: []*xraygeodata.CIDR{{Ip: prefix.Addr().AsSlice(), Prefix: uint32(prefix.Bits())}}},
|
cidr = append(cidr, geoWireVarint(2, uint64(prefix.Bits()))...)
|
||||||
}})
|
entry := geoWireString(1, "private")
|
||||||
if err != nil {
|
entry = append(entry, geoWireBytes(2, cidr)...)
|
||||||
t.Fatalf("marshal geoip: %v", err)
|
data := geoWireBytes(1, entry)
|
||||||
}
|
|
||||||
if err := os.WriteFile(filepath.Join(dir, "geoip.dat"), data, 0o644); err != nil {
|
if err := os.WriteFile(filepath.Join(dir, "geoip.dat"), data, 0o644); err != nil {
|
||||||
t.Fatalf("write geoip.dat: %v", err)
|
t.Fatalf("write geoip.dat: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,24 +12,97 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
xraygeodata "github.com/xtls/xray-core/common/geodata"
|
"google.golang.org/protobuf/encoding/protowire"
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeSiteDB(t *testing.T, dir, name string, sites ...*xraygeodata.GeoSite) string {
|
// The fixtures are encoded with protowire directly: the databases' wire
|
||||||
|
// layout is the contract this package parses, and spelling it out keeps the
|
||||||
|
// tests independent of xray-core's generated types. Field numbers follow
|
||||||
|
// geodat.proto — list entry 1; entry code 1, payload 2; domain type 1,
|
||||||
|
// value 2, attribute 3; attribute key 1, bool value 2; cidr ip 1, prefix 2.
|
||||||
|
// proto3 zero-value omission is reproduced exactly (a domain of type 0 and
|
||||||
|
// a /0 prefix emit no field), because the reader's behavior depends on it.
|
||||||
|
|
||||||
|
// Domain match types, mirroring xray's Domain_Type enum.
|
||||||
|
const (
|
||||||
|
DomainSubstr = 0
|
||||||
|
DomainRegex = 1
|
||||||
|
DomainDomain = 2
|
||||||
|
DomainFull = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
type fixtureDomain struct {
|
||||||
|
typ uint64
|
||||||
|
value string
|
||||||
|
attrs []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type fixtureSite struct {
|
||||||
|
code string
|
||||||
|
domains []fixtureDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
type fixtureIP struct {
|
||||||
|
code string
|
||||||
|
prefixes []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func wireBytes(num protowire.Number, payload []byte) []byte {
|
||||||
|
out := protowire.AppendTag(nil, num, protowire.BytesType)
|
||||||
|
return protowire.AppendBytes(out, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func wireString(num protowire.Number, value string) []byte {
|
||||||
|
out := protowire.AppendTag(nil, num, protowire.BytesType)
|
||||||
|
return protowire.AppendString(out, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func wireVarint(num protowire.Number, value uint64) []byte {
|
||||||
|
out := protowire.AppendTag(nil, num, protowire.VarintType)
|
||||||
|
return protowire.AppendVarint(out, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeFixtureDomain(d fixtureDomain) []byte {
|
||||||
|
var msg []byte
|
||||||
|
if d.typ != 0 {
|
||||||
|
msg = append(msg, wireVarint(1, d.typ)...)
|
||||||
|
}
|
||||||
|
msg = append(msg, wireString(2, d.value)...)
|
||||||
|
for _, attr := range d.attrs {
|
||||||
|
attrMsg := wireString(1, attr)
|
||||||
|
attrMsg = append(attrMsg, wireVarint(2, 1)...) // bool_value = true
|
||||||
|
msg = append(msg, wireBytes(3, attrMsg)...)
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeSiteDB(t *testing.T, dir, name string, sites ...fixtureSite) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
data, err := proto.Marshal(&xraygeodata.GeoSiteList{Entry: sites})
|
var data []byte
|
||||||
if err != nil {
|
for _, s := range sites {
|
||||||
t.Fatalf("marshal geosite list: %v", err)
|
entry := wireString(1, s.code)
|
||||||
|
for _, d := range s.domains {
|
||||||
|
entry = append(entry, wireBytes(2, encodeFixtureDomain(d))...)
|
||||||
|
}
|
||||||
|
data = append(data, wireBytes(1, entry)...)
|
||||||
}
|
}
|
||||||
return writeFile(t, dir, name, data)
|
return writeFile(t, dir, name, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeIPDB(t *testing.T, dir, name string, geoips ...*xraygeodata.GeoIP) string {
|
func writeIPDB(t *testing.T, dir, name string, geoips ...fixtureIP) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
data, err := proto.Marshal(&xraygeodata.GeoIPList{Entry: geoips})
|
var data []byte
|
||||||
if err != nil {
|
for _, g := range geoips {
|
||||||
t.Fatalf("marshal geoip list: %v", err)
|
entry := wireString(1, g.code)
|
||||||
|
for _, raw := range g.prefixes {
|
||||||
|
prefix := netip.MustParsePrefix(raw)
|
||||||
|
cidr := wireBytes(1, prefix.Addr().AsSlice())
|
||||||
|
if prefix.Bits() != 0 {
|
||||||
|
cidr = append(cidr, wireVarint(2, uint64(prefix.Bits()))...)
|
||||||
|
}
|
||||||
|
entry = append(entry, wireBytes(2, cidr)...)
|
||||||
|
}
|
||||||
|
data = append(data, wireBytes(1, entry)...)
|
||||||
}
|
}
|
||||||
return writeFile(t, dir, name, data)
|
return writeFile(t, dir, name, data)
|
||||||
}
|
}
|
||||||
@@ -43,45 +116,30 @@ func writeFile(t *testing.T, dir, name string, data []byte) string {
|
|||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
func site(code string, domains ...*xraygeodata.Domain) *xraygeodata.GeoSite {
|
func site(code string, domains ...fixtureDomain) fixtureSite {
|
||||||
return &xraygeodata.GeoSite{Code: code, Domain: domains}
|
return fixtureSite{code: code, domains: domains}
|
||||||
}
|
}
|
||||||
|
|
||||||
func domain(domainType xraygeodata.Domain_Type, value string, attributes ...string) *xraygeodata.Domain {
|
func domain(domainType uint64, value string, attributes ...string) fixtureDomain {
|
||||||
d := &xraygeodata.Domain{Type: domainType, Value: value}
|
return fixtureDomain{typ: domainType, value: value, attrs: attributes}
|
||||||
for _, attribute := range attributes {
|
|
||||||
d.Attribute = append(d.Attribute, &xraygeodata.Domain_Attribute{
|
|
||||||
Key: attribute,
|
|
||||||
TypedValue: &xraygeodata.Domain_Attribute_BoolValue{BoolValue: true},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return d
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func geoip(code string, prefixes ...string) *xraygeodata.GeoIP {
|
func geoip(code string, prefixes ...string) fixtureIP {
|
||||||
entry := &xraygeodata.GeoIP{Code: code}
|
return fixtureIP{code: code, prefixes: prefixes}
|
||||||
for _, raw := range prefixes {
|
|
||||||
prefix := netip.MustParsePrefix(raw)
|
|
||||||
entry.Cidr = append(entry.Cidr, &xraygeodata.CIDR{
|
|
||||||
Ip: prefix.Addr().AsSlice(),
|
|
||||||
Prefix: uint32(prefix.Bits()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return entry
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func sampleSiteDB(t *testing.T, dir string) {
|
func sampleSiteDB(t *testing.T, dir string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
writeSiteDB(t, dir, "geosite.dat",
|
writeSiteDB(t, dir, "geosite.dat",
|
||||||
site("google",
|
site("google",
|
||||||
domain(xraygeodata.Domain_Domain, "google.com"),
|
domain(DomainDomain, "google.com"),
|
||||||
domain(xraygeodata.Domain_Full, "ads.google.com", "ads"),
|
domain(DomainFull, "ads.google.com", "ads"),
|
||||||
domain(xraygeodata.Domain_Substr, "googlevideo", "cn"),
|
domain(DomainSubstr, "googlevideo", "cn"),
|
||||||
domain(xraygeodata.Domain_Regex, `^g.*\.cn$`),
|
domain(DomainRegex, `^g.*\.cn$`),
|
||||||
),
|
),
|
||||||
site("CN",
|
site("CN",
|
||||||
domain(xraygeodata.Domain_Domain, "baidu.com"),
|
domain(DomainDomain, "baidu.com"),
|
||||||
domain(xraygeodata.Domain_Domain, "qq.com"),
|
domain(DomainDomain, "qq.com"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -126,7 +184,7 @@ func TestListFilesReportsKindAndCategories(t *testing.T) {
|
|||||||
|
|
||||||
func TestKindDetectedFromContentsNotName(t *testing.T) {
|
func TestKindDetectedFromContentsNotName(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
writeSiteDB(t, dir, "my_ip_rules.dat", site("corp", domain(xraygeodata.Domain_Domain, "intranet.corp.local")))
|
writeSiteDB(t, dir, "my_ip_rules.dat", site("corp", domain(DomainDomain, "intranet.corp.local")))
|
||||||
writeIPDB(t, dir, "custom_sites.dat", geoip("office", "192.168.7.0/24"))
|
writeIPDB(t, dir, "custom_sites.dat", geoip("office", "192.168.7.0/24"))
|
||||||
|
|
||||||
store := NewStore(dir)
|
store := NewStore(dir)
|
||||||
@@ -241,9 +299,9 @@ func TestSearchFilters(t *testing.T) {
|
|||||||
|
|
||||||
func TestPagination(t *testing.T) {
|
func TestPagination(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
domains := make([]*xraygeodata.Domain, 0, 250)
|
domains := make([]fixtureDomain, 0, 250)
|
||||||
for i := range 250 {
|
for i := range 250 {
|
||||||
domains = append(domains, domain(xraygeodata.Domain_Domain, "host"+strconv.Itoa(i)+".example.com"))
|
domains = append(domains, domain(DomainDomain, "host"+strconv.Itoa(i)+".example.com"))
|
||||||
}
|
}
|
||||||
writeSiteDB(t, dir, "geosite.dat", site("bulk", domains...))
|
writeSiteDB(t, dir, "geosite.dat", site("bulk", domains...))
|
||||||
store := NewStore(dir)
|
store := NewStore(dir)
|
||||||
@@ -284,9 +342,9 @@ func TestPagination(t *testing.T) {
|
|||||||
|
|
||||||
func TestCategoriesReturnEverythingWithoutLimit(t *testing.T) {
|
func TestCategoriesReturnEverythingWithoutLimit(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
sites := make([]*xraygeodata.GeoSite, 0, MaxPageSize+20)
|
sites := make([]fixtureSite, 0, MaxPageSize+20)
|
||||||
for i := range MaxPageSize + 20 {
|
for i := range MaxPageSize + 20 {
|
||||||
sites = append(sites, site("cat"+strconv.Itoa(i), domain(xraygeodata.Domain_Domain, "example.com")))
|
sites = append(sites, site("cat"+strconv.Itoa(i), domain(DomainDomain, "example.com")))
|
||||||
}
|
}
|
||||||
writeSiteDB(t, dir, "geosite.dat", sites...)
|
writeSiteDB(t, dir, "geosite.dat", sites...)
|
||||||
store := NewStore(dir)
|
store := NewStore(dir)
|
||||||
@@ -421,9 +479,9 @@ func TestIndexCacheInvalidatedWhenFileChanges(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
path := writeSiteDB(t, dir, "geosite.dat",
|
path := writeSiteDB(t, dir, "geosite.dat",
|
||||||
site("google", domain(xraygeodata.Domain_Domain, "google.com")),
|
site("google", domain(DomainDomain, "google.com")),
|
||||||
site("cn", domain(xraygeodata.Domain_Domain, "baidu.com")),
|
site("cn", domain(DomainDomain, "baidu.com")),
|
||||||
site("telegram", domain(xraygeodata.Domain_Domain, "t.me")),
|
site("telegram", domain(DomainDomain, "t.me")),
|
||||||
)
|
)
|
||||||
touch(t, path, time.Now().Add(time.Second))
|
touch(t, path, time.Now().Add(time.Second))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user