Files
Sanaei f727d04f65
CI / go-test (push) Canceled after 0s
CI / postgres-durable-first (push) Canceled after 0s
CI / codegen (push) Canceled after 0s
CI / govulncheck (push) Canceled after 0s
CI / race (push) Canceled after 0s
CI / fuzz-smoke (push) Canceled after 0s
CI / golangci (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CodeQL Advanced / Analyze (go) (push) Canceled after 0s
CodeQL Advanced / Analyze (actions) (push) Canceled after 0s
CodeQL Advanced / Analyze (javascript-typescript) (push) Canceled after 0s
Docs CI / build (push) Canceled after 0s
Docs Deploy (GitHub Pages) / 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
Deploy Smoke Tests / noninteractive-install (ubuntu-24.04-arm) (push) Canceled after 0s
Deploy Smoke Tests / noninteractive-install (ubuntu-latest) (push) Canceled after 0s
Deploy Smoke Tests / release-tag-install (ubuntu-24.04-arm) (push) Canceled after 0s
Deploy Smoke Tests / release-tag-install (ubuntu-latest) (push) Canceled after 0s
Docs Deploy (GitHub Pages) / deploy (push) Canceled after 0s
Release 3X-UI / Publish rolling dev release (push) Canceled after 0s
v3.7.0
2026-08-24 15:07:15 +02:00

176 lines
5.2 KiB
Go

package geodata
import (
"errors"
"testing"
)
func TestParseReference(t *testing.T) {
tests := []struct {
name string
token string
kind GeoKind
want Reference
wantErr error
}{
{
name: "geosite shorthand",
token: "geosite:google",
kind: KindSite,
want: Reference{File: "geosite.dat", Code: "google"},
},
{
name: "geosite code is case insensitive",
token: "geosite:GOOGLE",
kind: KindSite,
want: Reference{File: "geosite.dat", Code: "google"},
},
{
name: "geosite with attribute",
token: "geosite:google@ads",
kind: KindSite,
want: Reference{File: "geosite.dat", Code: "google", Attributes: []string{"ads"}},
},
{
name: "geosite with several attributes",
token: "geosite:google@ads@cn",
kind: KindSite,
want: Reference{File: "geosite.dat", Code: "google", Attributes: []string{"ads", "cn"}},
},
{
name: "ext form",
token: "ext:my_rules.dat:corp",
kind: KindSite,
want: Reference{File: "my_rules.dat", Code: "corp"},
},
{
name: "ext-site form",
token: "ext-site:my_rules.dat:corp",
kind: KindSite,
want: Reference{File: "my_rules.dat", Code: "corp"},
},
{
name: "ext-domain form",
token: "ext-domain:my_rules.dat:corp",
kind: KindSite,
want: Reference{File: "my_rules.dat", Code: "corp"},
},
{
name: "surrounding spaces",
token: " geosite:google ",
kind: KindSite,
want: Reference{File: "geosite.dat", Code: "google"},
},
{
name: "plain domain needs no database",
token: "google.com",
kind: KindSite,
want: Reference{},
},
{
name: "domain keyword rule needs no database",
token: "keyword:google",
kind: KindSite,
want: Reference{},
},
{
name: "geoip shorthand",
token: "geoip:private",
kind: KindIP,
want: Reference{File: "geoip.dat", Code: "private"},
},
{
name: "geoip reverse before the prefix",
token: "!geoip:cn",
kind: KindIP,
want: Reference{File: "geoip.dat", Code: "cn", Reverse: true},
},
{
name: "geoip reverse before the code",
token: "geoip:!cn",
kind: KindIP,
want: Reference{File: "geoip.dat", Code: "cn", Reverse: true},
},
{
name: "ext-ip form",
token: "ext-ip:my_ips.dat:office",
kind: KindIP,
want: Reference{File: "my_ips.dat", Code: "office"},
},
{
name: "plain cidr needs no database",
token: "10.0.0.0/8",
kind: KindIP,
want: Reference{},
},
{name: "empty token", token: " ", kind: KindSite, wantErr: ErrInvalidToken},
{name: "ext without code", token: "ext:geosite.dat", kind: KindSite, wantErr: ErrInvalidToken},
{name: "ext with empty code", token: "ext:geosite.dat:", kind: KindSite, wantErr: ErrInvalidToken},
{name: "ext with empty file", token: "ext::google", kind: KindSite, wantErr: ErrInvalidToken},
{name: "geosite without code", token: "geosite:", kind: KindSite, wantErr: ErrInvalidToken},
{name: "bare ext prefix", token: "ext:", kind: KindSite, wantErr: ErrInvalidToken},
{name: "geoip token in a domain field", token: "geoip:cn", kind: KindSite, wantErr: ErrWrongKind},
{name: "geosite token in an ip field", token: "geosite:cn", kind: KindIP, wantErr: ErrWrongKind},
{name: "empty attribute", token: "geosite:cn@", kind: KindSite, wantErr: ErrInvalidToken},
{
name: "a space inside the code stays part of the code",
token: "geosite: cn",
kind: KindSite,
want: Reference{File: "geosite.dat", Code: " cn"},
},
{
name: "a space inside an attribute stays part of the attribute",
token: "geosite:cn@ ads",
kind: KindSite,
want: Reference{File: "geosite.dat", Code: "cn", Attributes: []string{" ads"}},
},
{name: "empty attribute between two others", token: "geosite:cn@@ads", kind: KindSite, wantErr: ErrInvalidToken},
{
name: "double negation folds back to a plain match",
token: "!!geoip:cn",
kind: KindIP,
want: Reference{File: "geoip.dat", Code: "cn"},
},
{
name: "negation on both sides of the prefix cancels out",
token: "!geoip:!cn",
kind: KindIP,
want: Reference{File: "geoip.dat", Code: "cn"},
},
{name: "bare ext-ip prefix", token: "ext-ip:", kind: KindIP, wantErr: ErrInvalidToken},
{
name: "an attribute suffix is part of the code for ip rules",
token: "geoip:cn@x",
kind: KindIP,
want: Reference{File: "geoip.dat", Code: "cn@x"},
},
{name: "attribute only", token: "geosite:@ads", kind: KindSite, wantErr: ErrInvalidToken},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseReference(tt.token, tt.kind)
if tt.wantErr != nil {
if !errors.Is(err, tt.wantErr) {
t.Fatalf("error = %v, want %v", err, tt.wantErr)
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got.File != tt.want.File || got.Code != tt.want.Code || got.Reverse != tt.want.Reverse {
t.Errorf("reference = %+v, want %+v", got, tt.want)
}
if len(got.Attributes) != len(tt.want.Attributes) {
t.Fatalf("attributes = %v, want %v", got.Attributes, tt.want.Attributes)
}
for i, attribute := range tt.want.Attributes {
if got.Attributes[i] != attribute {
t.Errorf("attribute %d = %q, want %q", i, got.Attributes[i], attribute)
}
}
})
}
}