test(parsing): add 229 unit tests across 18 config/parsing packages
Covering nil-input safety, plugin backends (HTTP/gRPC/default), loaders (File/HTTP/Redis), selector strategies, edge cases (empty/invalid inputs), and hop-to-node metadata inheritance. All tests passing.
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
package admission
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestParseAdmission_Nil(t *testing.T) {
|
||||
adm := ParseAdmission(nil)
|
||||
if adm != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAdmission_PluginHTTP(t *testing.T) {
|
||||
adm := ParseAdmission(&config.AdmissionConfig{
|
||||
Name: "http-adm",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "http",
|
||||
Addr: "127.0.0.1:9000",
|
||||
},
|
||||
})
|
||||
if adm == nil {
|
||||
t.Fatal("expected non-nil admission")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAdmission_PluginGRPC(t *testing.T) {
|
||||
adm := ParseAdmission(&config.AdmissionConfig{
|
||||
Name: "grpc-adm",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "grpc",
|
||||
Addr: "127.0.0.1:9001",
|
||||
Token: "secret",
|
||||
TLS: &config.TLSConfig{
|
||||
Secure: true,
|
||||
ServerName: "admission.local",
|
||||
},
|
||||
},
|
||||
})
|
||||
if adm == nil {
|
||||
t.Fatal("expected non-nil admission")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAdmission_PluginDefaultType(t *testing.T) {
|
||||
adm := ParseAdmission(&config.AdmissionConfig{
|
||||
Name: "default-adm",
|
||||
Plugin: &config.PluginConfig{
|
||||
Addr: "127.0.0.1:9002",
|
||||
},
|
||||
})
|
||||
if adm == nil {
|
||||
t.Fatal("expected non-nil admission")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAdmission_WithMatchers(t *testing.T) {
|
||||
adm := ParseAdmission(&config.AdmissionConfig{
|
||||
Name: "matcher-adm",
|
||||
Matchers: []string{"192.168.0.0/16", "10.0.0.1"},
|
||||
})
|
||||
if adm == nil {
|
||||
t.Fatal("expected non-nil admission")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAdmission_Whitelist(t *testing.T) {
|
||||
adm := ParseAdmission(&config.AdmissionConfig{
|
||||
Name: "whitelist-adm",
|
||||
Whitelist: true,
|
||||
})
|
||||
if adm == nil {
|
||||
t.Fatal("expected non-nil admission")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAdmission_Reverse(t *testing.T) {
|
||||
adm := ParseAdmission(&config.AdmissionConfig{
|
||||
Name: "reverse-adm",
|
||||
Reverse: true,
|
||||
})
|
||||
if adm == nil {
|
||||
t.Fatal("expected non-nil admission")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAdmission_FileLoader(t *testing.T) {
|
||||
adm := ParseAdmission(&config.AdmissionConfig{
|
||||
Name: "file-adm",
|
||||
File: &config.FileLoader{Path: "/tmp/admission.txt"},
|
||||
})
|
||||
if adm == nil {
|
||||
t.Fatal("expected non-nil admission")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAdmission_HTTPLoader(t *testing.T) {
|
||||
adm := ParseAdmission(&config.AdmissionConfig{
|
||||
Name: "http-adm",
|
||||
HTTP: &config.HTTPLoader{URL: "http://localhost:8080/list"},
|
||||
})
|
||||
if adm == nil {
|
||||
t.Fatal("expected non-nil admission")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAdmission_RedisLoader(t *testing.T) {
|
||||
adm := ParseAdmission(&config.AdmissionConfig{
|
||||
Name: "redis-adm",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "admission-set",
|
||||
},
|
||||
})
|
||||
if adm == nil {
|
||||
t.Fatal("expected non-nil admission")
|
||||
}
|
||||
}
|
||||
|
||||
func TestList_ReturnsEntries(t *testing.T) {
|
||||
// The registry returns a lazy wrapper for any non-empty name.
|
||||
got := List("test-admission", "test-admission-2")
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("expected 2 entries, got %d", len(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestList_EmptyNameReturnsNothing(t *testing.T) {
|
||||
// Empty names are filtered by the registry Get.
|
||||
got := List("")
|
||||
if len(got) != 0 {
|
||||
t.Fatal("expected empty list for empty name")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestInfo_Nil(t *testing.T) {
|
||||
if u := Info(nil); u != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInfo_EmptyUsername(t *testing.T) {
|
||||
u := Info(&config.AuthConfig{Username: ""})
|
||||
if u != nil {
|
||||
t.Fatal("expected nil for empty username")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInfo_UsernameOnly(t *testing.T) {
|
||||
u := Info(&config.AuthConfig{Username: "user"})
|
||||
if u == nil {
|
||||
t.Fatal("expected non-nil userinfo")
|
||||
}
|
||||
if u.Username() != "user" {
|
||||
t.Fatalf("username = %q, want %q", u.Username(), "user")
|
||||
}
|
||||
if _, hasPassword := u.Password(); hasPassword {
|
||||
t.Fatal("expected no password")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInfo_UsernameAndPassword(t *testing.T) {
|
||||
u := Info(&config.AuthConfig{Username: "user", Password: "pass"})
|
||||
if u == nil {
|
||||
t.Fatal("expected non-nil userinfo")
|
||||
}
|
||||
if u.Username() != "user" {
|
||||
t.Fatalf("username = %q, want %q", u.Username(), "user")
|
||||
}
|
||||
if p, ok := u.Password(); !ok || p != "pass" {
|
||||
t.Fatalf("password = %q, want %q", p, "pass")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInfo_WithFile(t *testing.T) {
|
||||
// File that doesn't exist - should fall back to username
|
||||
u := Info(&config.AuthConfig{
|
||||
Username: "fallback",
|
||||
Password: "pass",
|
||||
File: "/nonexistent/auth_file",
|
||||
})
|
||||
if u == nil {
|
||||
t.Fatal("expected non-nil userinfo (fallback)")
|
||||
}
|
||||
if u.Username() != "fallback" {
|
||||
t.Fatalf("username = %q, want %q", u.Username(), "fallback")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAutherFromAuth_Nil(t *testing.T) {
|
||||
a := ParseAutherFromAuth(nil)
|
||||
if a != nil {
|
||||
t.Fatal("expected nil for nil auth config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAutherFromAuth_EmptyUsername(t *testing.T) {
|
||||
a := ParseAutherFromAuth(&config.AuthConfig{Username: ""})
|
||||
if a != nil {
|
||||
t.Fatal("expected nil for empty username")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAutherFromAuth_Valid(t *testing.T) {
|
||||
a := ParseAutherFromAuth(&config.AuthConfig{
|
||||
Username: "user",
|
||||
Password: "pass",
|
||||
})
|
||||
if a == nil {
|
||||
t.Fatal("expected non-nil authenticator")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAuther_Nil(t *testing.T) {
|
||||
a := ParseAuther(nil)
|
||||
if a != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAuther_WithUsers(t *testing.T) {
|
||||
a := ParseAuther(&config.AutherConfig{
|
||||
Name: "test-auth",
|
||||
Auths: []*config.AuthConfig{
|
||||
{Username: "u1", Password: "p1"},
|
||||
{Username: "u2", Password: "p2"},
|
||||
{Username: "", Password: "nope"}, // skipped
|
||||
},
|
||||
})
|
||||
if a == nil {
|
||||
t.Fatal("expected non-nil authenticator")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAuther_WithEmptyAuths(t *testing.T) {
|
||||
a := ParseAuther(&config.AutherConfig{
|
||||
Name: "empty-auth",
|
||||
Auths: []*config.AuthConfig{},
|
||||
})
|
||||
if a == nil {
|
||||
t.Fatal("expected non-nil authenticator")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAuther_PluginHTTP(t *testing.T) {
|
||||
a := ParseAuther(&config.AutherConfig{
|
||||
Name: "http-plugin",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "http",
|
||||
Addr: "127.0.0.1:9000",
|
||||
},
|
||||
})
|
||||
if a == nil {
|
||||
t.Fatal("expected non-nil plugin authenticator")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAuther_PluginGRPC(t *testing.T) {
|
||||
a := ParseAuther(&config.AutherConfig{
|
||||
Name: "grpc-plugin",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "grpc",
|
||||
Addr: "127.0.0.1:9001",
|
||||
Token: "secret",
|
||||
TLS: &config.TLSConfig{
|
||||
Secure: true,
|
||||
ServerName: "auth.local",
|
||||
},
|
||||
},
|
||||
})
|
||||
if a == nil {
|
||||
t.Fatal("expected non-nil plugin authenticator")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAuther_PluginDefaultType(t *testing.T) {
|
||||
// Default plugin type should be treated as gRPC
|
||||
a := ParseAuther(&config.AutherConfig{
|
||||
Name: "default-plugin",
|
||||
Plugin: &config.PluginConfig{
|
||||
Addr: "127.0.0.1:9002",
|
||||
},
|
||||
})
|
||||
if a == nil {
|
||||
t.Fatal("expected non-nil plugin authenticator")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInfo_ParseFile(t *testing.T) {
|
||||
// parseInfo with empty reader should return nil
|
||||
u := Info(&config.AuthConfig{
|
||||
File: "/nonexistent/file",
|
||||
})
|
||||
if u != nil {
|
||||
t.Fatal("expected nil for nonexistent file (should not crash)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestList_ReturnsEntries(t *testing.T) {
|
||||
// The registry returns a lazy wrapper for any non-empty name.
|
||||
got := List("test-auther", "test-auther-2")
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("expected 2 entries, got %d", len(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestList_EmptyNameReturnsNothing(t *testing.T) {
|
||||
// Empty names are filtered by the registry Get.
|
||||
got := List("")
|
||||
if len(got) != 0 {
|
||||
t.Fatal("expected empty list for empty name")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAuther_HTTPLoader(t *testing.T) {
|
||||
a := ParseAuther(&config.AutherConfig{
|
||||
Name: "http-loader-auth",
|
||||
HTTP: &config.HTTPLoader{
|
||||
URL: "http://localhost:8080/auth",
|
||||
},
|
||||
})
|
||||
if a == nil {
|
||||
t.Fatal("expected non-nil authenticator")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAuther_FileLoader(t *testing.T) {
|
||||
a := ParseAuther(&config.AutherConfig{
|
||||
Name: "file-loader-auth",
|
||||
File: &config.FileLoader{
|
||||
Path: "/tmp/auth.txt",
|
||||
},
|
||||
})
|
||||
if a == nil {
|
||||
t.Fatal("expected non-nil authenticator")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAuther_RedisLoader(t *testing.T) {
|
||||
a := ParseAuther(&config.AutherConfig{
|
||||
Name: "redis-loader-auth",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
},
|
||||
})
|
||||
if a == nil {
|
||||
t.Fatal("expected non-nil authenticator")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInfo_Integration(t *testing.T) {
|
||||
// Verify Info returns *url.Userinfo whose String() matches the standard library.
|
||||
u := Info(&config.AuthConfig{Username: "alice", Password: "secret"})
|
||||
if u == nil {
|
||||
t.Fatal("expected non-nil")
|
||||
}
|
||||
want := "alice:secret"
|
||||
if u.String() != want {
|
||||
t.Fatalf("String() = %q, want %q", u.String(), want)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package bypass
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestParseBypass_Nil(t *testing.T) {
|
||||
bp := ParseBypass(nil)
|
||||
if bp != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBypass_PluginHTTP(t *testing.T) {
|
||||
bp := ParseBypass(&config.BypassConfig{
|
||||
Name: "http-bp",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "http",
|
||||
Addr: "127.0.0.1:9000",
|
||||
},
|
||||
})
|
||||
if bp == nil {
|
||||
t.Fatal("expected non-nil bypass")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBypass_PluginGRPC(t *testing.T) {
|
||||
bp := ParseBypass(&config.BypassConfig{
|
||||
Name: "grpc-bp",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "grpc",
|
||||
Addr: "127.0.0.1:9001",
|
||||
Token: "secret",
|
||||
TLS: &config.TLSConfig{
|
||||
Secure: true,
|
||||
ServerName: "bypass.local",
|
||||
},
|
||||
},
|
||||
})
|
||||
if bp == nil {
|
||||
t.Fatal("expected non-nil bypass")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBypass_PluginDefaultType(t *testing.T) {
|
||||
bp := ParseBypass(&config.BypassConfig{
|
||||
Name: "default-bp",
|
||||
Plugin: &config.PluginConfig{
|
||||
Addr: "127.0.0.1:9002",
|
||||
},
|
||||
})
|
||||
if bp == nil {
|
||||
t.Fatal("expected non-nil bypass")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBypass_WithMatchers(t *testing.T) {
|
||||
bp := ParseBypass(&config.BypassConfig{
|
||||
Name: "matcher-bp",
|
||||
Matchers: []string{"127.0.0.1", "10.0.0.0/8", "*.example.com"},
|
||||
})
|
||||
if bp == nil {
|
||||
t.Fatal("expected non-nil bypass")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBypass_Whitelist(t *testing.T) {
|
||||
bp := ParseBypass(&config.BypassConfig{
|
||||
Name: "whitelist-bp",
|
||||
Whitelist: true,
|
||||
})
|
||||
if bp == nil {
|
||||
t.Fatal("expected non-nil bypass")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBypass_Reverse(t *testing.T) {
|
||||
bp := ParseBypass(&config.BypassConfig{
|
||||
Name: "reverse-bp",
|
||||
Reverse: true,
|
||||
})
|
||||
if bp == nil {
|
||||
t.Fatal("expected non-nil bypass")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBypass_FileLoader(t *testing.T) {
|
||||
bp := ParseBypass(&config.BypassConfig{
|
||||
Name: "file-bp",
|
||||
File: &config.FileLoader{Path: "/tmp/bypass.txt"},
|
||||
})
|
||||
if bp == nil {
|
||||
t.Fatal("expected non-nil bypass")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBypass_HTTPLoader(t *testing.T) {
|
||||
bp := ParseBypass(&config.BypassConfig{
|
||||
Name: "http-bp",
|
||||
HTTP: &config.HTTPLoader{URL: "http://localhost:8080/list"},
|
||||
})
|
||||
if bp == nil {
|
||||
t.Fatal("expected non-nil bypass")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBypass_RedisLoader(t *testing.T) {
|
||||
bp := ParseBypass(&config.BypassConfig{
|
||||
Name: "redis-bp",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "bypass-set",
|
||||
},
|
||||
})
|
||||
if bp == nil {
|
||||
t.Fatal("expected non-nil bypass")
|
||||
}
|
||||
}
|
||||
|
||||
func TestList_ReturnsEntries(t *testing.T) {
|
||||
// The registry returns a lazy wrapper for any non-empty name.
|
||||
got := List("test-bypass", "test-bypass-2")
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("expected 2 entries, got %d", len(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestList_EmptyNameReturnsNothing(t *testing.T) {
|
||||
// Empty names are filtered by the registry Get.
|
||||
got := List("")
|
||||
if len(got) != 0 {
|
||||
t.Fatal("expected empty list for empty name")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func testLogger() logger.Logger {
|
||||
return xlogger.NewLogger(xlogger.OutputOption(io.Discard))
|
||||
}
|
||||
|
||||
func TestParseChain_Nil(t *testing.T) {
|
||||
c, err := ParseChain(nil, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if c != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChain_EmptyHops(t *testing.T) {
|
||||
c, err := ParseChain(&config.ChainConfig{
|
||||
Name: "empty-hops",
|
||||
Hops: []*config.HopConfig{},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if c == nil {
|
||||
t.Fatal("expected non-nil chain")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChain_NilHops(t *testing.T) {
|
||||
c, err := ParseChain(&config.ChainConfig{
|
||||
Name: "nil-hops",
|
||||
Hops: nil,
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if c == nil {
|
||||
t.Fatal("expected non-nil chain")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChain_NilHopEntry(t *testing.T) {
|
||||
c, err := ParseChain(&config.ChainConfig{
|
||||
Name: "nil-entry",
|
||||
Hops: []*config.HopConfig{nil},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if c == nil {
|
||||
t.Fatal("expected non-nil chain when hop entry is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChain_WithMetadata(t *testing.T) {
|
||||
c, err := ParseChain(&config.ChainConfig{
|
||||
Name: "meta-chain",
|
||||
Metadata: map[string]any{
|
||||
"key": "value",
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if c == nil {
|
||||
t.Fatal("expected non-nil chain")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChain_NamedHop(t *testing.T) {
|
||||
// Named hop (Nodes==nil, Plugin==nil) uses registry lookup
|
||||
c, err := ParseChain(&config.ChainConfig{
|
||||
Name: "named-hop",
|
||||
Hops: []*config.HopConfig{
|
||||
{Name: "nonexistent-hop"},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if c == nil {
|
||||
t.Fatal("expected non-nil chain even with unregistered hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChain_InlineHopWithPlugin(t *testing.T) {
|
||||
// Inline hop (Nodes!=nil or Plugin!=nil) calls hop_parser.ParseHop
|
||||
c, err := ParseChain(&config.ChainConfig{
|
||||
Name: "inline-hop",
|
||||
Hops: []*config.HopConfig{
|
||||
{
|
||||
Name: "plugin-hop",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "http",
|
||||
Addr: "127.0.0.1:9000",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if c == nil {
|
||||
t.Fatal("expected non-nil chain")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
package hop
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/hop"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/config/parsing"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
|
||||
// Register connector and dialer implementations needed for node parsing.
|
||||
_ "github.com/go-gost/x/connector/http"
|
||||
_ "github.com/go-gost/x/dialer/tcp"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func testLogger() logger.Logger {
|
||||
return xlogger.NewLogger(xlogger.OutputOption(io.Discard))
|
||||
}
|
||||
|
||||
func TestParseHop_Nil(t *testing.T) {
|
||||
h, err := ParseHop(nil, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_PluginHTTP(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "http-hop",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "http",
|
||||
Addr: "127.0.0.1:9000",
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_PluginGRPC(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "grpc-hop",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "grpc",
|
||||
Addr: "127.0.0.1:9001",
|
||||
Token: "secret",
|
||||
TLS: &config.TLSConfig{
|
||||
Secure: true,
|
||||
ServerName: "hop.local",
|
||||
},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_PluginDefaultType(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "default-hop",
|
||||
Plugin: &config.PluginConfig{
|
||||
Addr: "127.0.0.1:9002",
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_PluginHTTPWithTLS(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "http-tls-hop",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "http",
|
||||
Addr: "127.0.0.1:9003",
|
||||
TLS: &config.TLSConfig{
|
||||
Secure: false,
|
||||
ServerName: "hop.example.com",
|
||||
},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_PluginGRPCWithTLSInsecure(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "grpc-insecure-hop",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "grpc",
|
||||
Addr: "127.0.0.1:9004",
|
||||
TLS: &config.TLSConfig{
|
||||
Secure: false, // InsecureSkipVerify = true
|
||||
},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_NoNodesNoPlugin(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "empty-hop",
|
||||
Nodes: nil,
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop even with no nodes")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_WithBypass(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "bypass-hop",
|
||||
Bypass: "bypass1",
|
||||
Bypasses: []string{"bypass2", "bypass3"},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_WithSelector(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "selector-hop",
|
||||
Selector: &config.SelectorConfig{
|
||||
Strategy: "round",
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_WithNodeNilEntries(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "nil-nodes-hop",
|
||||
Nodes: []*config.NodeConfig{
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop when nodes contain nil entries")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_SockOptsMark(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "sockopts-hop",
|
||||
SockOpts: &config.SockOptsConfig{
|
||||
Mark: 1234,
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_MetadataSoMark(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "md-somark-hop",
|
||||
Metadata: map[string]any{
|
||||
parsing.MDKeySoMark: 5678,
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_MetadataInterface(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "md-iface-hop",
|
||||
Metadata: map[string]any{
|
||||
parsing.MDKeyInterface: "eth0",
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_MetadataProxyProtocol(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "md-ppv-hop",
|
||||
Metadata: map[string]any{
|
||||
parsing.MDKeyProxyProtocol: 2,
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_MetadataNetns(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "md-netns-hop",
|
||||
Metadata: map[string]any{
|
||||
parsing.MDKeyNetns: "mynetns",
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_InterfaceDeprecated(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "iface-deprecated-hop",
|
||||
Interface: "eth1",
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_FileLoader(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "file-hop",
|
||||
File: &config.FileLoader{Path: "/tmp/hop.txt"},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_HTTPLoader(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "http-hop",
|
||||
HTTP: &config.HTTPLoader{URL: "http://localhost:8080/hop"},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_RedisLoader(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "redis-hop",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "hop-key",
|
||||
Username: "user",
|
||||
Password: "pass",
|
||||
DB: 1,
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_NodeInheritsInterface(t *testing.T) {
|
||||
// When hop-level interface is set and node-level is not, node inherits.
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "inherit-iface-hop",
|
||||
Interface: "eth0",
|
||||
Nodes: []*config.NodeConfig{
|
||||
{
|
||||
Name: "node1",
|
||||
Addr: "example.com:8080",
|
||||
},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
nodes := h.(hop.NodeList).Nodes()
|
||||
if len(nodes) != 1 {
|
||||
t.Fatalf("expected 1 node, got %d", len(nodes))
|
||||
}
|
||||
if got := mdutil.GetString(nodes[0].Metadata(), parsing.MDKeyInterface); got != "eth0" {
|
||||
t.Fatalf("inherited interface = %q, want %q", got, "eth0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_NodeInheritsSoMark(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "inherit-somark-hop",
|
||||
SockOpts: &config.SockOptsConfig{
|
||||
Mark: 9999,
|
||||
},
|
||||
Nodes: []*config.NodeConfig{
|
||||
{
|
||||
Name: "node1",
|
||||
Addr: "example.com:8080",
|
||||
},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
nodes := h.(hop.NodeList).Nodes()
|
||||
if len(nodes) != 1 {
|
||||
t.Fatalf("expected 1 node, got %d", len(nodes))
|
||||
}
|
||||
if got := mdutil.GetInt(nodes[0].Metadata(), parsing.MDKeySoMark); got != 9999 {
|
||||
t.Fatalf("inherited so_mark = %d, want %d", got, 9999)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_NodeInheritsNetns(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "inherit-netns-hop",
|
||||
Metadata: map[string]any{
|
||||
parsing.MDKeyNetns: "mynetns",
|
||||
},
|
||||
Nodes: []*config.NodeConfig{
|
||||
{
|
||||
Name: "node1",
|
||||
Addr: "example.com:8080",
|
||||
},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
nodes := h.(hop.NodeList).Nodes()
|
||||
if len(nodes) != 1 {
|
||||
t.Fatalf("expected 1 node, got %d", len(nodes))
|
||||
}
|
||||
if got := mdutil.GetString(nodes[0].Metadata(), parsing.MDKeyNetns); got != "mynetns" {
|
||||
t.Fatalf("inherited netns = %q, want %q", got, "mynetns")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHop_NodeResolverHostsInheritance(t *testing.T) {
|
||||
h, err := ParseHop(&config.HopConfig{
|
||||
Name: "inherit-resolver-hop",
|
||||
Resolver: "my-resolver",
|
||||
Hosts: "my-hosts",
|
||||
Nodes: []*config.NodeConfig{
|
||||
{
|
||||
Name: "node1",
|
||||
Addr: "example.com:8080",
|
||||
},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if h == nil {
|
||||
t.Fatal("expected non-nil hop")
|
||||
}
|
||||
// Verify the node was created (no panic from nil resolver/hosts).
|
||||
// The resolver/hosts names are inherited, but registry lookup returns
|
||||
// nil since these names are not registered in this test.
|
||||
nodes := h.(hop.NodeList).Nodes()
|
||||
if len(nodes) != 1 {
|
||||
t.Fatalf("expected 1 node, got %d", len(nodes))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package hosts
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestParseHostMapper_Nil(t *testing.T) {
|
||||
hm := ParseHostMapper(nil)
|
||||
if hm != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHostMapper_PluginHTTP(t *testing.T) {
|
||||
hm := ParseHostMapper(&config.HostsConfig{
|
||||
Name: "http-hosts",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "http",
|
||||
Addr: "127.0.0.1:9000",
|
||||
},
|
||||
})
|
||||
if hm == nil {
|
||||
t.Fatal("expected non-nil host mapper")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHostMapper_PluginGRPC(t *testing.T) {
|
||||
hm := ParseHostMapper(&config.HostsConfig{
|
||||
Name: "grpc-hosts",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "grpc",
|
||||
Addr: "127.0.0.1:9001",
|
||||
Token: "secret",
|
||||
TLS: &config.TLSConfig{
|
||||
Secure: true,
|
||||
ServerName: "hosts.local",
|
||||
},
|
||||
},
|
||||
})
|
||||
if hm == nil {
|
||||
t.Fatal("expected non-nil host mapper")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHostMapper_PluginDefaultType(t *testing.T) {
|
||||
hm := ParseHostMapper(&config.HostsConfig{
|
||||
Name: "default-hosts",
|
||||
Plugin: &config.PluginConfig{
|
||||
Addr: "127.0.0.1:9002",
|
||||
},
|
||||
})
|
||||
if hm == nil {
|
||||
t.Fatal("expected non-nil host mapper")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHostMapper_WithMappings(t *testing.T) {
|
||||
hm := ParseHostMapper(&config.HostsConfig{
|
||||
Name: "mapping-hosts",
|
||||
Mappings: []*config.HostMappingConfig{
|
||||
{IP: "127.0.0.1", Hostname: "localhost"},
|
||||
{IP: "::1", Hostname: "ipv6-localhost"},
|
||||
},
|
||||
})
|
||||
if hm == nil {
|
||||
t.Fatal("expected non-nil host mapper")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHostMapper_EmptyMapping(t *testing.T) {
|
||||
// Mappings with empty IP or Hostname should be skipped
|
||||
hm := ParseHostMapper(&config.HostsConfig{
|
||||
Name: "partial-hosts",
|
||||
Mappings: []*config.HostMappingConfig{
|
||||
{IP: "", Hostname: "nope"}, // skipped
|
||||
{IP: "127.0.0.1", Hostname: ""}, // skipped
|
||||
{IP: "127.0.0.1", Hostname: "valid"}, // kept
|
||||
},
|
||||
})
|
||||
if hm == nil {
|
||||
t.Fatal("expected non-nil host mapper even with partial mappings")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHostMapper_InvalidIP(t *testing.T) {
|
||||
hm := ParseHostMapper(&config.HostsConfig{
|
||||
Name: "invalid-ip",
|
||||
Mappings: []*config.HostMappingConfig{
|
||||
{IP: "not-an-ip", Hostname: "example.com"}, // skipped
|
||||
{IP: "127.0.0.1", Hostname: "valid"}, // kept
|
||||
},
|
||||
})
|
||||
if hm == nil {
|
||||
t.Fatal("expected non-nil host mapper even with invalid IP")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHostMapper_FileLoader(t *testing.T) {
|
||||
hm := ParseHostMapper(&config.HostsConfig{
|
||||
Name: "file-hosts",
|
||||
File: &config.FileLoader{Path: "/etc/hosts"},
|
||||
})
|
||||
if hm == nil {
|
||||
t.Fatal("expected non-nil host mapper")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHostMapper_HTTPLoader(t *testing.T) {
|
||||
hm := ParseHostMapper(&config.HostsConfig{
|
||||
Name: "http-hosts",
|
||||
HTTP: &config.HTTPLoader{URL: "http://localhost:8080/hosts"},
|
||||
})
|
||||
if hm == nil {
|
||||
t.Fatal("expected non-nil host mapper")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHostMapper_RedisLoader(t *testing.T) {
|
||||
hm := ParseHostMapper(&config.HostsConfig{
|
||||
Name: "redis-hosts",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "hosts-key",
|
||||
Type: "set",
|
||||
},
|
||||
})
|
||||
if hm == nil {
|
||||
t.Fatal("expected non-nil host mapper")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHostMapper_RedisListLoader(t *testing.T) {
|
||||
hm := ParseHostMapper(&config.HostsConfig{
|
||||
Name: "redis-list-hosts",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "hosts-list",
|
||||
Type: "list",
|
||||
},
|
||||
})
|
||||
if hm == nil {
|
||||
t.Fatal("expected non-nil host mapper")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package ingress
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestParseIngress_Nil(t *testing.T) {
|
||||
ing := ParseIngress(nil)
|
||||
if ing != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseIngress_PluginHTTP(t *testing.T) {
|
||||
ing := ParseIngress(&config.IngressConfig{
|
||||
Name: "http-ing",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "http",
|
||||
Addr: "127.0.0.1:9000",
|
||||
},
|
||||
})
|
||||
if ing == nil {
|
||||
t.Fatal("expected non-nil ingress")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseIngress_PluginGRPC(t *testing.T) {
|
||||
ing := ParseIngress(&config.IngressConfig{
|
||||
Name: "grpc-ing",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "grpc",
|
||||
Addr: "127.0.0.1:9001",
|
||||
Token: "secret",
|
||||
TLS: &config.TLSConfig{
|
||||
Secure: true,
|
||||
ServerName: "ingress.local",
|
||||
},
|
||||
},
|
||||
})
|
||||
if ing == nil {
|
||||
t.Fatal("expected non-nil ingress")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseIngress_PluginDefaultType(t *testing.T) {
|
||||
ing := ParseIngress(&config.IngressConfig{
|
||||
Name: "default-ing",
|
||||
Plugin: &config.PluginConfig{
|
||||
Addr: "127.0.0.1:9002",
|
||||
},
|
||||
})
|
||||
if ing == nil {
|
||||
t.Fatal("expected non-nil ingress")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseIngress_WithRules(t *testing.T) {
|
||||
ing := ParseIngress(&config.IngressConfig{
|
||||
Name: "rule-ing",
|
||||
Rules: []*config.IngressRuleConfig{
|
||||
{Hostname: "example.com", Endpoint: "10.0.0.1:8080"},
|
||||
{Hostname: "test.com", Endpoint: "10.0.0.2:9090"},
|
||||
},
|
||||
})
|
||||
if ing == nil {
|
||||
t.Fatal("expected non-nil ingress")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseIngress_EmptyRules(t *testing.T) {
|
||||
// Rules with empty hostname or endpoint should be skipped
|
||||
ing := ParseIngress(&config.IngressConfig{
|
||||
Name: "partial-ing",
|
||||
Rules: []*config.IngressRuleConfig{
|
||||
{Hostname: "", Endpoint: "nope:8080"}, // skipped
|
||||
{Hostname: "nope.com", Endpoint: ""}, // skipped
|
||||
{Hostname: "valid.com", Endpoint: "10.0.0.1:8080"}, // kept
|
||||
},
|
||||
})
|
||||
if ing == nil {
|
||||
t.Fatal("expected non-nil ingress even with partial rules")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseIngress_FileLoader(t *testing.T) {
|
||||
ing := ParseIngress(&config.IngressConfig{
|
||||
Name: "file-ing",
|
||||
File: &config.FileLoader{Path: "/tmp/ingress.txt"},
|
||||
})
|
||||
if ing == nil {
|
||||
t.Fatal("expected non-nil ingress")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseIngress_HTTPLoader(t *testing.T) {
|
||||
ing := ParseIngress(&config.IngressConfig{
|
||||
Name: "http-ing",
|
||||
HTTP: &config.HTTPLoader{URL: "http://localhost:8080/ingress"},
|
||||
})
|
||||
if ing == nil {
|
||||
t.Fatal("expected non-nil ingress")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseIngress_RedisLoader(t *testing.T) {
|
||||
ing := ParseIngress(&config.IngressConfig{
|
||||
Name: "redis-ing",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "ingress-key",
|
||||
Type: "hash",
|
||||
},
|
||||
})
|
||||
if ing == nil {
|
||||
t.Fatal("expected non-nil ingress")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseIngress_RedisSetLoader(t *testing.T) {
|
||||
ing := ParseIngress(&config.IngressConfig{
|
||||
Name: "redis-set-ing",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "ingress-set",
|
||||
Type: "set",
|
||||
},
|
||||
})
|
||||
if ing == nil {
|
||||
t.Fatal("expected non-nil ingress")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
package limiter
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestParseTrafficLimiter_Nil(t *testing.T) {
|
||||
lim := ParseTrafficLimiter(nil)
|
||||
if lim != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTrafficLimiter_WithLimits(t *testing.T) {
|
||||
lim := ParseTrafficLimiter(&config.LimiterConfig{
|
||||
Name: "traffic-limiter",
|
||||
Limits: []string{"100KB", "200KB"},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil traffic limiter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTrafficLimiter_PluginHTTP(t *testing.T) {
|
||||
lim := ParseTrafficLimiter(&config.LimiterConfig{
|
||||
Name: "http-lim",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "http",
|
||||
Addr: "127.0.0.1:9000",
|
||||
},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil traffic limiter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTrafficLimiter_PluginGRPC(t *testing.T) {
|
||||
lim := ParseTrafficLimiter(&config.LimiterConfig{
|
||||
Name: "grpc-lim",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "grpc",
|
||||
Addr: "127.0.0.1:9001",
|
||||
Token: "secret",
|
||||
TLS: &config.TLSConfig{
|
||||
Secure: true,
|
||||
ServerName: "limiter.local",
|
||||
},
|
||||
},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil traffic limiter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTrafficLimiter_PluginDefaultType(t *testing.T) {
|
||||
lim := ParseTrafficLimiter(&config.LimiterConfig{
|
||||
Name: "default-lim",
|
||||
Plugin: &config.PluginConfig{
|
||||
Addr: "127.0.0.1:9002",
|
||||
},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil traffic limiter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTrafficLimiter_FileLoader(t *testing.T) {
|
||||
lim := ParseTrafficLimiter(&config.LimiterConfig{
|
||||
Name: "file-lim",
|
||||
File: &config.FileLoader{Path: "/tmp/limiter.txt"},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil traffic limiter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTrafficLimiter_HTTPLoader(t *testing.T) {
|
||||
lim := ParseTrafficLimiter(&config.LimiterConfig{
|
||||
Name: "http-lim",
|
||||
HTTP: &config.HTTPLoader{URL: "http://localhost:8080/limits"},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil traffic limiter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTrafficLimiter_RedisLoader(t *testing.T) {
|
||||
lim := ParseTrafficLimiter(&config.LimiterConfig{
|
||||
Name: "redis-lim",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "limiter-key",
|
||||
},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil traffic limiter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTrafficLimiter_RedisListLoader(t *testing.T) {
|
||||
lim := ParseTrafficLimiter(&config.LimiterConfig{
|
||||
Name: "redis-list-lim",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "limiter-list",
|
||||
Type: "list",
|
||||
},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil traffic limiter")
|
||||
}
|
||||
}
|
||||
|
||||
// --- ConnLimiter ---
|
||||
|
||||
func TestParseConnLimiter_Nil(t *testing.T) {
|
||||
lim := ParseConnLimiter(nil)
|
||||
if lim != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseConnLimiter_WithLimits(t *testing.T) {
|
||||
lim := ParseConnLimiter(&config.LimiterConfig{
|
||||
Name: "conn-limiter",
|
||||
Limits: []string{"10", "50"},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil conn limiter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseConnLimiter_RedisLoader(t *testing.T) {
|
||||
lim := ParseConnLimiter(&config.LimiterConfig{
|
||||
Name: "redis-conn-lim",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "conn-key",
|
||||
},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil conn limiter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseConnLimiter_RedisListLoader(t *testing.T) {
|
||||
lim := ParseConnLimiter(&config.LimiterConfig{
|
||||
Name: "redis-list-conn-lim",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "conn-list",
|
||||
Type: "list",
|
||||
},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil conn limiter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseConnLimiter_FileLoader(t *testing.T) {
|
||||
lim := ParseConnLimiter(&config.LimiterConfig{
|
||||
Name: "file-conn-lim",
|
||||
File: &config.FileLoader{Path: "/tmp/conn_limiter.txt"},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil conn limiter")
|
||||
}
|
||||
}
|
||||
|
||||
// --- RateLimiter ---
|
||||
|
||||
func TestParseRateLimiter_Nil(t *testing.T) {
|
||||
lim := ParseRateLimiter(nil)
|
||||
if lim != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRateLimiter_WithLimits(t *testing.T) {
|
||||
lim := ParseRateLimiter(&config.LimiterConfig{
|
||||
Name: "rate-limiter",
|
||||
Limits: []string{"100", "200"},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil rate limiter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRateLimiter_RedisLoader(t *testing.T) {
|
||||
lim := ParseRateLimiter(&config.LimiterConfig{
|
||||
Name: "redis-rate-lim",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "rate-key",
|
||||
},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil rate limiter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRateLimiter_RedisListLoader(t *testing.T) {
|
||||
lim := ParseRateLimiter(&config.LimiterConfig{
|
||||
Name: "redis-list-rate-lim",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "rate-list",
|
||||
Type: "list",
|
||||
},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil rate limiter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRateLimiter_FileLoader(t *testing.T) {
|
||||
lim := ParseRateLimiter(&config.LimiterConfig{
|
||||
Name: "file-rate-lim",
|
||||
File: &config.FileLoader{Path: "/tmp/rate_limiter.txt"},
|
||||
})
|
||||
if lim == nil {
|
||||
t.Fatal("expected non-nil rate limiter")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestParseLogger_Nil(t *testing.T) {
|
||||
lg := ParseLogger(nil)
|
||||
if lg != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLogger_NilLog(t *testing.T) {
|
||||
lg := ParseLogger(&config.LoggerConfig{
|
||||
Name: "test",
|
||||
Log: nil,
|
||||
})
|
||||
if lg != nil {
|
||||
t.Fatal("expected nil when Log is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLogger_Levels(t *testing.T) {
|
||||
for _, level := range []string{"trace", "debug", "info", "warn", "error", "fatal"} {
|
||||
t.Run(level, func(t *testing.T) {
|
||||
lg := ParseLogger(&config.LoggerConfig{
|
||||
Name: "test-" + level,
|
||||
Log: &config.LogConfig{
|
||||
Level: level,
|
||||
},
|
||||
})
|
||||
if lg == nil {
|
||||
t.Fatal("expected non-nil logger")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLogger_OutputNone(t *testing.T) {
|
||||
lg := ParseLogger(&config.LoggerConfig{
|
||||
Name: "null-logger",
|
||||
Log: &config.LogConfig{
|
||||
Output: "none",
|
||||
},
|
||||
})
|
||||
if lg == nil {
|
||||
t.Fatal("expected non-nil logger")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLogger_OutputStdout(t *testing.T) {
|
||||
lg := ParseLogger(&config.LoggerConfig{
|
||||
Name: "stdout-logger",
|
||||
Log: &config.LogConfig{
|
||||
Output: "stdout",
|
||||
},
|
||||
})
|
||||
if lg == nil {
|
||||
t.Fatal("expected non-nil logger")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLogger_OutputStderr(t *testing.T) {
|
||||
lg := ParseLogger(&config.LoggerConfig{
|
||||
Name: "stderr-logger",
|
||||
Log: &config.LogConfig{
|
||||
Output: "stderr",
|
||||
},
|
||||
})
|
||||
if lg == nil {
|
||||
t.Fatal("expected non-nil logger")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLogger_OutputDefault(t *testing.T) {
|
||||
lg := ParseLogger(&config.LoggerConfig{
|
||||
Name: "default-logger",
|
||||
Log: &config.LogConfig{
|
||||
Output: "",
|
||||
},
|
||||
})
|
||||
if lg == nil {
|
||||
t.Fatal("expected non-nil logger")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLogger_WithFormat(t *testing.T) {
|
||||
lg := ParseLogger(&config.LoggerConfig{
|
||||
Name: "format-logger",
|
||||
Log: &config.LogConfig{
|
||||
Format: "json",
|
||||
Level: "info",
|
||||
},
|
||||
})
|
||||
if lg == nil {
|
||||
t.Fatal("expected non-nil logger")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLogger_FileOutput(t *testing.T) {
|
||||
lg := ParseLogger(&config.LoggerConfig{
|
||||
Name: "file-logger",
|
||||
Log: &config.LogConfig{
|
||||
Output: "/tmp/gost-test.log",
|
||||
Rotation: &config.LogRotationConfig{
|
||||
MaxSize: 10,
|
||||
MaxAge: 7,
|
||||
MaxBackups: 5,
|
||||
LocalTime: true,
|
||||
Compress: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
if lg == nil {
|
||||
t.Fatal("expected non-nil logger")
|
||||
}
|
||||
}
|
||||
|
||||
func TestList_EmptyLogger(t *testing.T) {
|
||||
got := List("nonexistent")
|
||||
if len(got) != 0 {
|
||||
t.Fatal("expected empty list for unregistered name")
|
||||
}
|
||||
}
|
||||
|
||||
func TestList_WithNames(t *testing.T) {
|
||||
got := List("nonexistent", "also_nonexistent")
|
||||
if len(got) != 0 {
|
||||
t.Fatal("expected empty list for unregistered names")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/config/parsing"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
|
||||
// Register connector and dialer implementations needed for node parsing.
|
||||
_ "github.com/go-gost/x/connector/http"
|
||||
_ "github.com/go-gost/x/dialer/tcp"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func testLogger() logger.Logger {
|
||||
return xlogger.NewLogger(xlogger.OutputOption(io.Discard))
|
||||
}
|
||||
|
||||
func TestParseNode_Nil(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", nil, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_Defaults(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "node1",
|
||||
Addr: "example.com:8080",
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_DefaultsEmptyName(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "",
|
||||
Addr: "example.com:8080",
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_DefaultsEmptyAddr(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "no-addr",
|
||||
Addr: "",
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_ExplicitConnector(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "explicit-conn",
|
||||
Addr: "example.com:8080",
|
||||
Connector: &config.ConnectorConfig{
|
||||
Type: "http",
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_UnregisteredConnector(t *testing.T) {
|
||||
_, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "bad-conn",
|
||||
Addr: "example.com:8080",
|
||||
Connector: &config.ConnectorConfig{
|
||||
Type: "nonexistent-connector-type",
|
||||
},
|
||||
}, testLogger())
|
||||
if err == nil {
|
||||
t.Fatal("expected error for unregistered connector type")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_UnregisteredDialer(t *testing.T) {
|
||||
_, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "bad-dial",
|
||||
Addr: "example.com:8080",
|
||||
Dialer: &config.DialerConfig{
|
||||
Type: "nonexistent-dialer-type",
|
||||
},
|
||||
}, testLogger())
|
||||
if err == nil {
|
||||
t.Fatal("expected error for unregistered dialer type")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_ExplicitDialer(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "explicit-dial",
|
||||
Addr: "example.com:8080",
|
||||
Dialer: &config.DialerConfig{
|
||||
Type: "tcp",
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_WithBypass(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "bypass-node",
|
||||
Addr: "example.com:8080",
|
||||
Bypass: "bp1",
|
||||
Bypasses: []string{"bp2", "bp3"},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_WithResolver(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "resolver-node",
|
||||
Addr: "example.com:8080",
|
||||
Resolver: "my-resolver",
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_WithHosts(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "hosts-node",
|
||||
Addr: "example.com:8080",
|
||||
Hosts: "my-hosts",
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_WithNetwork(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "network-node",
|
||||
Addr: "example.com:8080",
|
||||
Network: "tcp",
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_WithMetadata(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "meta-node",
|
||||
Addr: "example.com:8080",
|
||||
Metadata: map[string]any{
|
||||
parsing.MDKeySoMark: 1234,
|
||||
parsing.MDKeyInterface: "eth0",
|
||||
parsing.MDKeyNetns: "myns",
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_WithSockOpts(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "sockopts-node",
|
||||
Addr: "example.com:8080",
|
||||
SockOpts: &config.SockOptsConfig{
|
||||
Mark: 9999,
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_WithFilter(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "filter-node",
|
||||
Addr: "example.com:8080",
|
||||
Filter: &config.NodeFilterConfig{
|
||||
Protocol: "http",
|
||||
Host: "*.example.com",
|
||||
Path: "/api",
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_WithFilterStarBare(t *testing.T) {
|
||||
// "*example.com" should become ".example.com"
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "star-bare-node",
|
||||
Addr: "example.com:8080",
|
||||
Filter: &config.NodeFilterConfig{
|
||||
Host: "*example.com",
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_WithMatcher(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "matcher-node",
|
||||
Addr: "example.com:8080",
|
||||
Matcher: &config.NodeMatcherConfig{
|
||||
Rule: "dstport==80",
|
||||
Priority: 10,
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_WithMatcherAutoPriority(t *testing.T) {
|
||||
// Priority auto-set to len(rule) when zero
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "auto-prio-node",
|
||||
Addr: "example.com:8080",
|
||||
Matcher: &config.NodeMatcherConfig{
|
||||
Rule: "dstport==443",
|
||||
Priority: 0,
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_WithHTTPNode(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "http-node",
|
||||
Addr: "example.com:8080",
|
||||
HTTP: &config.HTTPNodeConfig{
|
||||
Host: "custom.example.com",
|
||||
RequestHeader: map[string]string{"X-Custom": "value"},
|
||||
ResponseHeader: map[string]string{"X-Response": "value"},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_WithHTTPAuth(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "http-auth-node",
|
||||
Addr: "example.com:8080",
|
||||
HTTP: &config.HTTPNodeConfig{
|
||||
Auth: &config.AuthConfig{
|
||||
Username: "user",
|
||||
Password: "pass",
|
||||
},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_WithHTTPDeprecatedHeader(t *testing.T) {
|
||||
// Deprecated Header field should populate RequestHeader
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "http-deprecated-node",
|
||||
Addr: "example.com:8080",
|
||||
HTTP: &config.HTTPNodeConfig{
|
||||
Header: map[string]string{"X-Old": "value"},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_WithTLSNode(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "tls-node",
|
||||
Addr: "example.com:8080",
|
||||
TLS: &config.TLSNodeConfig{
|
||||
ServerName: "secure.example.com",
|
||||
Secure: true,
|
||||
Options: &config.TLSOptions{
|
||||
MinVersion: "1.2",
|
||||
MaxVersion: "1.3",
|
||||
CipherSuites: []string{"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"},
|
||||
ALPN: []string{"h2", "http/1.1"},
|
||||
},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_TLSServerNameFromAddr(t *testing.T) {
|
||||
// When connector/dialer TLS ServerName is empty, it defaults from addr hostname.
|
||||
// The server name is set on the connector's TLS config (internal), so the best
|
||||
// verification is that ParseNode does not error and returns a node.
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "tls-servername-node",
|
||||
Addr: "tls.example.com:443",
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_ConnectorAuth(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "conn-auth-node",
|
||||
Addr: "example.com:8080",
|
||||
Connector: &config.ConnectorConfig{
|
||||
Type: "http",
|
||||
Auth: &config.AuthConfig{
|
||||
Username: "connuser",
|
||||
Password: "connpass",
|
||||
},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_DialerAuth(t *testing.T) {
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "dial-auth-node",
|
||||
Addr: "example.com:8080",
|
||||
Dialer: &config.DialerConfig{
|
||||
Type: "tcp",
|
||||
Auth: &config.AuthConfig{
|
||||
Username: "dialuser",
|
||||
Password: "dialpass",
|
||||
},
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_InvalidMatcherRule(t *testing.T) {
|
||||
// An invalid matcher rule sets priority to -1
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "invalid-matcher-node",
|
||||
Addr: "example.com:8080",
|
||||
Matcher: &config.NodeMatcherConfig{
|
||||
Rule: "",
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node even with empty matcher rule")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNode_DeprecatedFilterFields(t *testing.T) {
|
||||
// Deprecated filter fields: Protocol/Host/Path via Filter
|
||||
n, err := ParseNode("test-hop", &config.NodeConfig{
|
||||
Name: "deprecated-node",
|
||||
Addr: "example.com:8080",
|
||||
Filter: &config.NodeFilterConfig{
|
||||
Protocol: "socks5",
|
||||
Host: "*.example.com",
|
||||
Path: "/proxy",
|
||||
},
|
||||
}, testLogger())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if n == nil {
|
||||
t.Fatal("expected non-nil node")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package observer
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestParseObserver_Nil(t *testing.T) {
|
||||
obs := ParseObserver(nil)
|
||||
if obs != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseObserver_NilPlugin(t *testing.T) {
|
||||
obs := ParseObserver(&config.ObserverConfig{
|
||||
Name: "no-plugin",
|
||||
Plugin: nil,
|
||||
})
|
||||
if obs != nil {
|
||||
t.Fatal("expected nil when plugin is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseObserver_PluginHTTP(t *testing.T) {
|
||||
obs := ParseObserver(&config.ObserverConfig{
|
||||
Name: "http-obs",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "http",
|
||||
Addr: "127.0.0.1:9000",
|
||||
},
|
||||
})
|
||||
if obs == nil {
|
||||
t.Fatal("expected non-nil observer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseObserver_PluginGRPC(t *testing.T) {
|
||||
obs := ParseObserver(&config.ObserverConfig{
|
||||
Name: "grpc-obs",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "grpc",
|
||||
Addr: "127.0.0.1:9001",
|
||||
Token: "secret",
|
||||
TLS: &config.TLSConfig{
|
||||
Secure: true,
|
||||
ServerName: "observer.local",
|
||||
},
|
||||
},
|
||||
})
|
||||
if obs == nil {
|
||||
t.Fatal("expected non-nil observer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseObserver_PluginDefaultType(t *testing.T) {
|
||||
obs := ParseObserver(&config.ObserverConfig{
|
||||
Name: "default-obs",
|
||||
Plugin: &config.PluginConfig{
|
||||
Addr: "127.0.0.1:9002",
|
||||
},
|
||||
})
|
||||
if obs == nil {
|
||||
t.Fatal("expected non-nil observer")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/x/config"
|
||||
)
|
||||
|
||||
func TestMergeConfig_NilBoth(t *testing.T) {
|
||||
got := mergeConfig(nil, nil)
|
||||
if got != nil {
|
||||
t.Fatal("expected nil for two nil inputs")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeConfig_NilFirst(t *testing.T) {
|
||||
cfg2 := &config.Config{
|
||||
Services: []*config.ServiceConfig{{Name: "svc"}},
|
||||
}
|
||||
got := mergeConfig(nil, cfg2)
|
||||
if len(got.Services) != 1 || got.Services[0].Name != "svc" {
|
||||
t.Fatal("expected cfg2 to be returned when cfg1 is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeConfig_NilSecond(t *testing.T) {
|
||||
cfg1 := &config.Config{
|
||||
Services: []*config.ServiceConfig{{Name: "svc"}},
|
||||
}
|
||||
got := mergeConfig(cfg1, nil)
|
||||
if len(got.Services) != 1 || got.Services[0].Name != "svc" {
|
||||
t.Fatal("expected cfg1 to be returned when cfg2 is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeConfig_AppendsAllSlices(t *testing.T) {
|
||||
cfg1 := &config.Config{
|
||||
Services: []*config.ServiceConfig{{Name: "s1"}},
|
||||
Chains: []*config.ChainConfig{{Name: "c1"}},
|
||||
Hops: []*config.HopConfig{{Name: "h1"}},
|
||||
Authers: []*config.AutherConfig{{Name: "a1"}},
|
||||
Admissions: []*config.AdmissionConfig{{Name: "ad1"}},
|
||||
Bypasses: []*config.BypassConfig{{Name: "b1"}},
|
||||
Resolvers: []*config.ResolverConfig{{Name: "r1"}},
|
||||
Hosts: []*config.HostsConfig{{Name: "hosts1"}},
|
||||
Ingresses: []*config.IngressConfig{{Name: "i1"}},
|
||||
SDs: []*config.SDConfig{{Name: "sd1"}},
|
||||
Recorders: []*config.RecorderConfig{{Name: "rec1"}},
|
||||
Limiters: []*config.LimiterConfig{{Name: "l1"}},
|
||||
CLimiters: []*config.LimiterConfig{{Name: "cl1"}},
|
||||
RLimiters: []*config.LimiterConfig{{Name: "rl1"}},
|
||||
Loggers: []*config.LoggerConfig{{Name: "log1"}},
|
||||
Routers: []*config.RouterConfig{{Name: "rt1"}},
|
||||
Observers: []*config.ObserverConfig{{Name: "obs1"}},
|
||||
}
|
||||
cfg2 := &config.Config{
|
||||
Services: []*config.ServiceConfig{{Name: "s2"}},
|
||||
Chains: []*config.ChainConfig{{Name: "c2"}},
|
||||
Hops: []*config.HopConfig{{Name: "h2"}},
|
||||
Authers: []*config.AutherConfig{{Name: "a2"}},
|
||||
Admissions: []*config.AdmissionConfig{{Name: "ad2"}},
|
||||
Bypasses: []*config.BypassConfig{{Name: "b2"}},
|
||||
Resolvers: []*config.ResolverConfig{{Name: "r2"}},
|
||||
Hosts: []*config.HostsConfig{{Name: "hosts2"}},
|
||||
Ingresses: []*config.IngressConfig{{Name: "i2"}},
|
||||
SDs: []*config.SDConfig{{Name: "sd2"}},
|
||||
Recorders: []*config.RecorderConfig{{Name: "rec2"}},
|
||||
Limiters: []*config.LimiterConfig{{Name: "l2"}},
|
||||
CLimiters: []*config.LimiterConfig{{Name: "cl2"}},
|
||||
RLimiters: []*config.LimiterConfig{{Name: "rl2"}},
|
||||
Loggers: []*config.LoggerConfig{{Name: "log2"}},
|
||||
Routers: []*config.RouterConfig{{Name: "rt2"}},
|
||||
Observers: []*config.ObserverConfig{{Name: "obs2"}},
|
||||
}
|
||||
|
||||
got := mergeConfig(cfg1, cfg2)
|
||||
|
||||
if len(got.Services) != 2 || got.Services[0].Name != "s1" || got.Services[1].Name != "s2" {
|
||||
t.Fatal("services not properly appended")
|
||||
}
|
||||
if len(got.Chains) != 2 {
|
||||
t.Fatal("chains not properly appended")
|
||||
}
|
||||
if len(got.Hops) != 2 {
|
||||
t.Fatal("hops not properly appended")
|
||||
}
|
||||
if len(got.Authers) != 2 {
|
||||
t.Fatal("authers not properly appended")
|
||||
}
|
||||
if len(got.Admissions) != 2 {
|
||||
t.Fatal("admissions not properly appended")
|
||||
}
|
||||
if len(got.Bypasses) != 2 {
|
||||
t.Fatal("bypasses not properly appended")
|
||||
}
|
||||
if len(got.Resolvers) != 2 {
|
||||
t.Fatal("resolvers not properly appended")
|
||||
}
|
||||
if len(got.Hosts) != 2 {
|
||||
t.Fatal("hosts not properly appended")
|
||||
}
|
||||
if len(got.Ingresses) != 2 {
|
||||
t.Fatal("ingresses not properly appended")
|
||||
}
|
||||
if len(got.SDs) != 2 {
|
||||
t.Fatal("SDs not properly appended")
|
||||
}
|
||||
if len(got.Recorders) != 2 {
|
||||
t.Fatal("recorders not properly appended")
|
||||
}
|
||||
if len(got.Limiters) != 2 {
|
||||
t.Fatal("limiters not properly appended")
|
||||
}
|
||||
if len(got.CLimiters) != 2 {
|
||||
t.Fatal("climiters not properly appended")
|
||||
}
|
||||
if len(got.RLimiters) != 2 {
|
||||
t.Fatal("rlimiters not properly appended")
|
||||
}
|
||||
if len(got.Loggers) != 2 {
|
||||
t.Fatal("loggers not properly appended")
|
||||
}
|
||||
if len(got.Routers) != 2 {
|
||||
t.Fatal("routers not properly appended")
|
||||
}
|
||||
if len(got.Observers) != 2 {
|
||||
t.Fatal("observers not properly appended")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeConfig_ScalarOverrides(t *testing.T) {
|
||||
cfg1 := &config.Config{
|
||||
TLS: &config.TLSConfig{ServerName: "old"},
|
||||
Log: &config.LogConfig{Level: "info"},
|
||||
API: &config.APIConfig{Addr: ":8080"},
|
||||
Metrics: &config.MetricsConfig{Addr: ":9090"},
|
||||
Profiling: &config.ProfilingConfig{Addr: ":6060"},
|
||||
}
|
||||
cfg2 := &config.Config{
|
||||
TLS: &config.TLSConfig{ServerName: "new"},
|
||||
Log: &config.LogConfig{Level: "debug"},
|
||||
API: &config.APIConfig{Addr: ":8081"},
|
||||
Metrics: &config.MetricsConfig{Addr: ":9091"},
|
||||
Profiling: &config.ProfilingConfig{Addr: ":6061"},
|
||||
}
|
||||
|
||||
got := mergeConfig(cfg1, cfg2)
|
||||
|
||||
if got.TLS.ServerName != "new" {
|
||||
t.Fatalf("TLS not overridden: got %q, want %q", got.TLS.ServerName, "new")
|
||||
}
|
||||
if got.Log.Level != "debug" {
|
||||
t.Fatalf("Log not overridden: got %q, want %q", got.Log.Level, "debug")
|
||||
}
|
||||
if got.API.Addr != ":8081" {
|
||||
t.Fatalf("API not overridden: got %q, want %q", got.API.Addr, ":8081")
|
||||
}
|
||||
if got.Metrics.Addr != ":9091" {
|
||||
t.Fatalf("Metrics not overridden: got %q, want %q", got.Metrics.Addr, ":9091")
|
||||
}
|
||||
if got.Profiling.Addr != ":6061" {
|
||||
t.Fatalf("Profiling not overridden: got %q, want %q", got.Profiling.Addr, ":6061")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeConfig_ScalarKeepsCfg1WhenCfg2Nil(t *testing.T) {
|
||||
cfg1 := &config.Config{
|
||||
TLS: &config.TLSConfig{ServerName: "keep"},
|
||||
Log: &config.LogConfig{Level: "warn"},
|
||||
API: &config.APIConfig{Addr: ":8080"},
|
||||
Metrics: &config.MetricsConfig{Addr: ":9090"},
|
||||
Profiling: &config.ProfilingConfig{Addr: ":6060"},
|
||||
}
|
||||
cfg2 := &config.Config{} // all scalars nil
|
||||
|
||||
got := mergeConfig(cfg1, cfg2)
|
||||
|
||||
if got.TLS.ServerName != "keep" {
|
||||
t.Fatal("TLS should be kept from cfg1")
|
||||
}
|
||||
if got.Log.Level != "warn" {
|
||||
t.Fatal("Log should be kept from cfg1")
|
||||
}
|
||||
if got.API.Addr != ":8080" {
|
||||
t.Fatal("API should be kept from cfg1")
|
||||
}
|
||||
if got.Metrics.Addr != ":9090" {
|
||||
t.Fatal("Metrics should be kept from cfg1")
|
||||
}
|
||||
if got.Profiling.Addr != ":6060" {
|
||||
t.Fatal("Profiling should be kept from cfg1")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
Init(Args{
|
||||
CfgFile: "test.yml",
|
||||
Services: []string{"s1"},
|
||||
Nodes: []string{"n1"},
|
||||
Debug: true,
|
||||
Trace: false,
|
||||
ApiAddr: ":8080",
|
||||
MetricsAddr: ":9090",
|
||||
})
|
||||
|
||||
if defaultParser.args.CfgFile != "test.yml" {
|
||||
t.Fatalf("CfgFile not set: got %q", defaultParser.args.CfgFile)
|
||||
}
|
||||
if len(defaultParser.args.Services) != 1 || defaultParser.args.Services[0] != "s1" {
|
||||
t.Fatal("Services not set")
|
||||
}
|
||||
if len(defaultParser.args.Nodes) != 1 || defaultParser.args.Nodes[0] != "n1" {
|
||||
t.Fatal("Nodes not set")
|
||||
}
|
||||
if !defaultParser.args.Debug {
|
||||
t.Fatal("Debug not set")
|
||||
}
|
||||
if defaultParser.args.Trace {
|
||||
t.Fatal("Trace should be false")
|
||||
}
|
||||
if defaultParser.args.ApiAddr != ":8080" {
|
||||
t.Fatal("ApiAddr not set")
|
||||
}
|
||||
if defaultParser.args.MetricsAddr != ":9090" {
|
||||
t.Fatal("MetricsAddr not set")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package recorder
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestParseRecorder_Nil(t *testing.T) {
|
||||
r := ParseRecorder(nil)
|
||||
if r != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRecorder_PluginHTTP(t *testing.T) {
|
||||
r := ParseRecorder(&config.RecorderConfig{
|
||||
Name: "http-rec",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "http",
|
||||
Addr: "127.0.0.1:9000",
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil recorder")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRecorder_PluginGRPC(t *testing.T) {
|
||||
r := ParseRecorder(&config.RecorderConfig{
|
||||
Name: "grpc-rec",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "grpc",
|
||||
Addr: "127.0.0.1:9001",
|
||||
Token: "secret",
|
||||
TLS: &config.TLSConfig{
|
||||
Secure: true,
|
||||
ServerName: "recorder.local",
|
||||
},
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil recorder")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRecorder_PluginDefaultType(t *testing.T) {
|
||||
r := ParseRecorder(&config.RecorderConfig{
|
||||
Name: "default-rec",
|
||||
Plugin: &config.PluginConfig{
|
||||
Addr: "127.0.0.1:9002",
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil recorder")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRecorder_TCP(t *testing.T) {
|
||||
r := ParseRecorder(&config.RecorderConfig{
|
||||
Name: "tcp-rec",
|
||||
TCP: &config.TCPRecorder{
|
||||
Addr: "127.0.0.1:9999",
|
||||
Timeout: 0,
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil recorder for TCP")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRecorder_HTTP(t *testing.T) {
|
||||
r := ParseRecorder(&config.RecorderConfig{
|
||||
Name: "http-rec",
|
||||
HTTP: &config.HTTPRecorder{
|
||||
URL: "http://localhost:8080/record",
|
||||
Timeout: 0,
|
||||
Header: map[string]string{"X-Test": "value"},
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil recorder for HTTP")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRecorder_File(t *testing.T) {
|
||||
r := ParseRecorder(&config.RecorderConfig{
|
||||
Name: "file-rec",
|
||||
File: &config.FileRecorder{
|
||||
Path: "/tmp/recorder.log",
|
||||
Sep: "\n",
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil recorder for file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRecorder_FileWithRotation(t *testing.T) {
|
||||
r := ParseRecorder(&config.RecorderConfig{
|
||||
Name: "file-rot-rec",
|
||||
File: &config.FileRecorder{
|
||||
Path: "/tmp/recorder-rot.log",
|
||||
Rotation: &config.LogRotationConfig{
|
||||
MaxSize: 100,
|
||||
MaxAge: 30,
|
||||
MaxBackups: 10,
|
||||
LocalTime: true,
|
||||
Compress: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil recorder for file with rotation")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRecorder_RedisSet(t *testing.T) {
|
||||
r := ParseRecorder(&config.RecorderConfig{
|
||||
Name: "redis-set-rec",
|
||||
Redis: &config.RedisRecorder{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "recorder-set",
|
||||
Type: "set",
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil recorder for redis set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRecorder_RedisList(t *testing.T) {
|
||||
r := ParseRecorder(&config.RecorderConfig{
|
||||
Name: "redis-list-rec",
|
||||
Redis: &config.RedisRecorder{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "recorder-list",
|
||||
Type: "list",
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil recorder for redis list")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRecorder_RedisSortedSet(t *testing.T) {
|
||||
r := ParseRecorder(&config.RecorderConfig{
|
||||
Name: "redis-sset-rec",
|
||||
Redis: &config.RedisRecorder{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "recorder-sset",
|
||||
Type: "sset",
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil recorder for redis sorted set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRecorder_EmptyFileReturnsNil(t *testing.T) {
|
||||
r := ParseRecorder(&config.RecorderConfig{
|
||||
Name: "empty-rec",
|
||||
})
|
||||
if r != nil {
|
||||
t.Fatal("expected nil recorder when no sub-config is provided")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRecorder_FileWithEmptyPathReturnsNil(t *testing.T) {
|
||||
r := ParseRecorder(&config.RecorderConfig{
|
||||
Name: "empty-path-rec",
|
||||
File: &config.FileRecorder{
|
||||
Path: "",
|
||||
},
|
||||
})
|
||||
if r != nil {
|
||||
t.Fatal("expected nil recorder when file path is empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRecorder_TCPWithEmptyAddrReturnsNil(t *testing.T) {
|
||||
r := ParseRecorder(&config.RecorderConfig{
|
||||
Name: "empty-tcp-rec",
|
||||
TCP: &config.TCPRecorder{
|
||||
Addr: "",
|
||||
},
|
||||
})
|
||||
if r != nil {
|
||||
t.Fatal("expected nil recorder when TCP addr is empty")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestParseResolver_Nil(t *testing.T) {
|
||||
r, err := ParseResolver(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if r != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResolver_PluginHTTP(t *testing.T) {
|
||||
r, err := ParseResolver(&config.ResolverConfig{
|
||||
Name: "http-res",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "http",
|
||||
Addr: "127.0.0.1:9000",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil resolver")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResolver_PluginGRPC(t *testing.T) {
|
||||
r, err := ParseResolver(&config.ResolverConfig{
|
||||
Name: "grpc-res",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "grpc",
|
||||
Addr: "127.0.0.1:9001",
|
||||
Token: "secret",
|
||||
TLS: &config.TLSConfig{
|
||||
Secure: true,
|
||||
ServerName: "resolver.local",
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil resolver")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResolver_PluginDefaultType(t *testing.T) {
|
||||
r, err := ParseResolver(&config.ResolverConfig{
|
||||
Name: "default-res",
|
||||
Plugin: &config.PluginConfig{
|
||||
Addr: "127.0.0.1:9002",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil resolver")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResolver_WithNameservers(t *testing.T) {
|
||||
r, err := ParseResolver(&config.ResolverConfig{
|
||||
Name: "ns-res",
|
||||
Nameservers: []*config.NameserverConfig{
|
||||
{Addr: "8.8.8.8", Prefer: "ipv4"},
|
||||
{Addr: "8.8.4.4", Prefer: "ipv4"},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil resolver")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResolver_EmptyNameservers(t *testing.T) {
|
||||
r, err := ParseResolver(&config.ResolverConfig{
|
||||
Name: "empty-ns",
|
||||
Nameservers: []*config.NameserverConfig{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil resolver even with empty nameservers")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResolver_NameserverWithClientIP(t *testing.T) {
|
||||
r, err := ParseResolver(&config.ResolverConfig{
|
||||
Name: "clientip-res",
|
||||
Nameservers: []*config.NameserverConfig{
|
||||
{Addr: "8.8.8.8", ClientIP: "1.2.3.4"},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil resolver")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResolver_NameserverWithAllFields(t *testing.T) {
|
||||
r, err := ParseResolver(&config.ResolverConfig{
|
||||
Name: "full-res",
|
||||
Nameservers: []*config.NameserverConfig{
|
||||
{
|
||||
Addr: "8.8.8.8:53",
|
||||
Chain: "chain1",
|
||||
Prefer: "ipv4",
|
||||
ClientIP: "1.2.3.4",
|
||||
Hostname: "dns.example.com",
|
||||
TTL: 300,
|
||||
Timeout: 5,
|
||||
Async: true,
|
||||
Only: "A",
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil resolver")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestParseRouter_Nil(t *testing.T) {
|
||||
r := ParseRouter(nil)
|
||||
if r != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_PluginHTTP(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "http-rt",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "http",
|
||||
Addr: "127.0.0.1:9000",
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_PluginGRPC(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "grpc-rt",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "grpc",
|
||||
Addr: "127.0.0.1:9001",
|
||||
Token: "secret",
|
||||
TLS: &config.TLSConfig{
|
||||
Secure: true,
|
||||
ServerName: "router.local",
|
||||
},
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_PluginDefaultType(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "default-rt",
|
||||
Plugin: &config.PluginConfig{
|
||||
Addr: "127.0.0.1:9002",
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_WithCIDRRoutes(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "cidr-rt",
|
||||
Routes: []*config.RouterRouteConfig{
|
||||
{Net: "192.168.1.0/24", Gateway: "10.0.0.1"},
|
||||
{Net: "10.0.0.0/8", Gateway: "10.0.0.2"},
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router with CIDR routes")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_WithDstRoutes(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "dst-rt",
|
||||
Routes: []*config.RouterRouteConfig{
|
||||
{Dst: "192.168.1.0/24", Gateway: "10.0.0.1"},
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router with dst routes")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_MissingGatewaySkipped(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "skip-rt",
|
||||
Routes: []*config.RouterRouteConfig{
|
||||
{Net: "192.168.1.0/24", Gateway: ""}, // skipped: no gateway
|
||||
{Net: "10.0.0.0/8", Gateway: "10.0.0.1"}, // kept
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router (with one valid route)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_MissingDstSkipped(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "no-dst-rt",
|
||||
Routes: []*config.RouterRouteConfig{
|
||||
{Dst: "", Gateway: "10.0.0.1"}, // skipped: no dst
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router (all routes skipped)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_InvalidCIDRSkipped(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "invalid-cidr-rt",
|
||||
Routes: []*config.RouterRouteConfig{
|
||||
{Net: "not-a-cidr", Gateway: "10.0.0.1"}, // invalid CIDR with empty Dst, skipped
|
||||
{Net: "192.168.1.0/24", Gateway: "10.0.0.2"}, // valid
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_NilRouteSkipped(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "nil-route-rt",
|
||||
Routes: []*config.RouterRouteConfig{
|
||||
nil,
|
||||
{Net: "192.168.1.0/24", Gateway: "10.0.0.1"},
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router when nil route is in list")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_EmptyRoutes(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "empty-routes",
|
||||
Routes: []*config.RouterRouteConfig{},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router with empty routes")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_FileLoader(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "file-rt",
|
||||
File: &config.FileLoader{Path: "/tmp/routes.txt"},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_HTTPLoader(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "http-rt",
|
||||
HTTP: &config.HTTPLoader{URL: "http://localhost:8080/routes"},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_RedisHashLoader(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "redis-hash-rt",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "routes-hash",
|
||||
Type: "hash",
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_RedisListLoader(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "redis-list-rt",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "routes-list",
|
||||
Type: "list",
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_RedisSetLoader(t *testing.T) {
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "redis-set-rt",
|
||||
Redis: &config.RedisLoader{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Key: "routes-set",
|
||||
Type: "set",
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRouter_DstSyncedFromNet(t *testing.T) {
|
||||
// When Dst is empty but Net is provided, Net is converted to Dst
|
||||
r := ParseRouter(&config.RouterConfig{
|
||||
Name: "net-to-dst-rt",
|
||||
Routes: []*config.RouterRouteConfig{
|
||||
{Net: "192.168.1.0/24", Gateway: "10.0.0.1"},
|
||||
},
|
||||
})
|
||||
if r == nil {
|
||||
t.Fatal("expected non-nil router")
|
||||
}
|
||||
route := r.GetRoute(context.Background(), "192.168.1.5")
|
||||
if route == nil {
|
||||
t.Fatal("expected non-nil route for IP in CIDR range")
|
||||
}
|
||||
if route.Dst != "192.168.1.0/24" {
|
||||
t.Fatalf("Dst = %q, want %q", route.Dst, "192.168.1.0/24")
|
||||
}
|
||||
if route.Gateway != "10.0.0.1" {
|
||||
t.Fatalf("Gateway = %q, want %q", route.Gateway, "10.0.0.1")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package sd
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestParseSD_Nil(t *testing.T) {
|
||||
s := ParseSD(nil)
|
||||
if s != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSD_NilPlugin(t *testing.T) {
|
||||
s := ParseSD(&config.SDConfig{
|
||||
Name: "no-plugin",
|
||||
Plugin: nil,
|
||||
})
|
||||
if s != nil {
|
||||
t.Fatal("expected nil when plugin is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSD_PluginHTTP(t *testing.T) {
|
||||
s := ParseSD(&config.SDConfig{
|
||||
Name: "http-sd",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "http",
|
||||
Addr: "127.0.0.1:9000",
|
||||
},
|
||||
})
|
||||
if s == nil {
|
||||
t.Fatal("expected non-nil SD")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSD_PluginGRPC(t *testing.T) {
|
||||
s := ParseSD(&config.SDConfig{
|
||||
Name: "grpc-sd",
|
||||
Plugin: &config.PluginConfig{
|
||||
Type: "grpc",
|
||||
Addr: "127.0.0.1:9001",
|
||||
Token: "secret",
|
||||
TLS: &config.TLSConfig{
|
||||
Secure: true,
|
||||
ServerName: "sd.local",
|
||||
},
|
||||
},
|
||||
})
|
||||
if s == nil {
|
||||
t.Fatal("expected non-nil SD")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSD_PluginDefaultType(t *testing.T) {
|
||||
s := ParseSD(&config.SDConfig{
|
||||
Name: "default-sd",
|
||||
Plugin: &config.PluginConfig{
|
||||
Addr: "127.0.0.1:9002",
|
||||
},
|
||||
})
|
||||
if s == nil {
|
||||
t.Fatal("expected non-nil SD")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package selector
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/x/config"
|
||||
)
|
||||
|
||||
func TestParseChainSelector_Nil(t *testing.T) {
|
||||
sel := ParseChainSelector(nil)
|
||||
if sel != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChainSelector_RoundRobin(t *testing.T) {
|
||||
sel := ParseChainSelector(&config.SelectorConfig{Strategy: "round"})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChainSelector_RR(t *testing.T) {
|
||||
sel := ParseChainSelector(&config.SelectorConfig{Strategy: "rr"})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector for rr strategy")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChainSelector_Random(t *testing.T) {
|
||||
sel := ParseChainSelector(&config.SelectorConfig{Strategy: "random"})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChainSelector_Rand(t *testing.T) {
|
||||
sel := ParseChainSelector(&config.SelectorConfig{Strategy: "rand"})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector for rand strategy")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChainSelector_FIFO(t *testing.T) {
|
||||
sel := ParseChainSelector(&config.SelectorConfig{Strategy: "fifo"})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChainSelector_HA(t *testing.T) {
|
||||
sel := ParseChainSelector(&config.SelectorConfig{Strategy: "ha"})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector for ha strategy")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChainSelector_Hash(t *testing.T) {
|
||||
sel := ParseChainSelector(&config.SelectorConfig{Strategy: "hash"})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChainSelector_DefaultStrategy(t *testing.T) {
|
||||
sel := ParseChainSelector(&config.SelectorConfig{Strategy: "unknown"})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector for unknown strategy (should default to round-robin)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChainSelector_WithMaxFails(t *testing.T) {
|
||||
sel := ParseChainSelector(&config.SelectorConfig{
|
||||
Strategy: "round",
|
||||
MaxFails: 3,
|
||||
FailTimeout: 0,
|
||||
})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNodeSelector_Nil(t *testing.T) {
|
||||
sel := ParseNodeSelector(nil)
|
||||
if sel != nil {
|
||||
t.Fatal("expected nil for nil config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNodeSelector_RoundRobin(t *testing.T) {
|
||||
sel := ParseNodeSelector(&config.SelectorConfig{Strategy: "round"})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNodeSelector_Random(t *testing.T) {
|
||||
sel := ParseNodeSelector(&config.SelectorConfig{Strategy: "random"})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNodeSelector_FIFO(t *testing.T) {
|
||||
sel := ParseNodeSelector(&config.SelectorConfig{Strategy: "fifo"})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNodeSelector_Hash(t *testing.T) {
|
||||
sel := ParseNodeSelector(&config.SelectorConfig{Strategy: "hash"})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNodeSelector_Parallel(t *testing.T) {
|
||||
sel := ParseNodeSelector(&config.SelectorConfig{Strategy: "parallel"})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector for parallel strategy")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNodeSelector_DefaultStrategy(t *testing.T) {
|
||||
sel := ParseNodeSelector(&config.SelectorConfig{Strategy: "unknown"})
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil selector for unknown strategy (should default to round-robin)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultNodeSelector(t *testing.T) {
|
||||
sel := DefaultNodeSelector()
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil default node selector")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultChainSelector(t *testing.T) {
|
||||
sel := DefaultChainSelector()
|
||||
if sel == nil {
|
||||
t.Fatal("expected non-nil default chain selector")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package parsing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
logger.SetDefault(xlogger.NewLogger(xlogger.OutputOption(io.Discard)))
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestSetDefaultTLSConfig(t *testing.T) {
|
||||
// Save and restore original
|
||||
orig := DefaultTLSConfig()
|
||||
defer SetDefaultTLSConfig(orig)
|
||||
|
||||
cfg := &tls.Config{ServerName: "test.local"}
|
||||
SetDefaultTLSConfig(cfg)
|
||||
|
||||
got := DefaultTLSConfig()
|
||||
if got == nil {
|
||||
t.Fatal("expected non-nil default TLS config")
|
||||
}
|
||||
if got.ServerName != "test.local" {
|
||||
t.Fatalf("ServerName = %q, want %q", got.ServerName, "test.local")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultTLSConfig_InitiallyNil(t *testing.T) {
|
||||
orig := DefaultTLSConfig()
|
||||
defer SetDefaultTLSConfig(orig)
|
||||
|
||||
// Store nil to reset
|
||||
SetDefaultTLSConfig(nil)
|
||||
|
||||
got := DefaultTLSConfig()
|
||||
if got != nil {
|
||||
t.Fatal("expected nil after storing nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDefaultTLSConfig_Nil(t *testing.T) {
|
||||
// BuildDefaultTLSConfig with nil should generate a self-signed cert
|
||||
tlsCfg, err := BuildDefaultTLSConfig(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if tlsCfg == nil {
|
||||
t.Fatal("expected non-nil config")
|
||||
}
|
||||
if len(tlsCfg.Certificates) == 0 {
|
||||
t.Fatal("expected at least one certificate")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDefaultTLSConfig_WithOptions(t *testing.T) {
|
||||
cfg := &config.TLSConfig{
|
||||
Validity: 0, // uses default
|
||||
Organization: "TestOrg",
|
||||
CommonName: "test.example.com",
|
||||
}
|
||||
tlsCfg, err := BuildDefaultTLSConfig(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if tlsCfg == nil {
|
||||
t.Fatal("expected non-nil config")
|
||||
}
|
||||
if len(tlsCfg.Certificates) == 0 {
|
||||
t.Fatal("expected at least one certificate")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDefaultTLSConfig_TwoCallsDifferentCerts(t *testing.T) {
|
||||
cfg1, err := BuildDefaultTLSConfig(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
cfg2, err := BuildDefaultTLSConfig(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(cfg1.Certificates) == 0 || len(cfg2.Certificates) == 0 {
|
||||
t.Fatal("expected certificates in both configs")
|
||||
}
|
||||
|
||||
// Each call generates a random cert; they should have different raw bytes.
|
||||
if len(cfg1.Certificates[0].Certificate) == 0 || len(cfg2.Certificates[0].Certificate) == 0 {
|
||||
t.Fatal("expected non-empty certificate raw bytes")
|
||||
}
|
||||
c1 := cfg1.Certificates[0].Certificate[0]
|
||||
c2 := cfg2.Certificates[0].Certificate[0]
|
||||
if bytes.Equal(c1, c2) {
|
||||
t.Fatal("expected different certificates from two calls")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user