Update golang to 1.16.5, migrate to go modules
This commit is contained in:
-119
@@ -1,119 +0,0 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNoAuth(t *testing.T) {
|
||||
req := bytes.NewBuffer(nil)
|
||||
req.Write([]byte{1, NoAuth})
|
||||
var resp bytes.Buffer
|
||||
|
||||
s, _ := New(&Config{})
|
||||
ctx, err := s.authenticate(&resp, req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if ctx.Method != NoAuth {
|
||||
t.Fatal("Invalid Context Method")
|
||||
}
|
||||
|
||||
out := resp.Bytes()
|
||||
if !bytes.Equal(out, []byte{socks5Version, NoAuth}) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPasswordAuth_Valid(t *testing.T) {
|
||||
req := bytes.NewBuffer(nil)
|
||||
req.Write([]byte{2, NoAuth, UserPassAuth})
|
||||
req.Write([]byte{1, 3, 'f', 'o', 'o', 3, 'b', 'a', 'r'})
|
||||
var resp bytes.Buffer
|
||||
|
||||
cred := StaticCredentials{
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
cator := UserPassAuthenticator{Credentials: cred}
|
||||
|
||||
s, _ := New(&Config{AuthMethods: []Authenticator{cator}})
|
||||
|
||||
ctx, err := s.authenticate(&resp, req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if ctx.Method != UserPassAuth {
|
||||
t.Fatal("Invalid Context Method")
|
||||
}
|
||||
|
||||
val, ok := ctx.Payload["Username"]
|
||||
if !ok {
|
||||
t.Fatal("Missing key Username in auth context's payload")
|
||||
}
|
||||
|
||||
if val != "foo" {
|
||||
t.Fatal("Invalid Username in auth context's payload")
|
||||
}
|
||||
|
||||
out := resp.Bytes()
|
||||
if !bytes.Equal(out, []byte{socks5Version, UserPassAuth, 1, authSuccess}) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPasswordAuth_Invalid(t *testing.T) {
|
||||
req := bytes.NewBuffer(nil)
|
||||
req.Write([]byte{2, NoAuth, UserPassAuth})
|
||||
req.Write([]byte{1, 3, 'f', 'o', 'o', 3, 'b', 'a', 'z'})
|
||||
var resp bytes.Buffer
|
||||
|
||||
cred := StaticCredentials{
|
||||
"foo": "bar",
|
||||
}
|
||||
cator := UserPassAuthenticator{Credentials: cred}
|
||||
s, _ := New(&Config{AuthMethods: []Authenticator{cator}})
|
||||
|
||||
ctx, err := s.authenticate(&resp, req)
|
||||
if err != UserAuthFailed {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if ctx != nil {
|
||||
t.Fatal("Invalid Context Method")
|
||||
}
|
||||
|
||||
out := resp.Bytes()
|
||||
if !bytes.Equal(out, []byte{socks5Version, UserPassAuth, 1, authFailure}) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoSupportedAuth(t *testing.T) {
|
||||
req := bytes.NewBuffer(nil)
|
||||
req.Write([]byte{1, NoAuth})
|
||||
var resp bytes.Buffer
|
||||
|
||||
cred := StaticCredentials{
|
||||
"foo": "bar",
|
||||
}
|
||||
cator := UserPassAuthenticator{Credentials: cred}
|
||||
|
||||
s, _ := New(&Config{AuthMethods: []Authenticator{cator}})
|
||||
|
||||
ctx, err := s.authenticate(&resp, req)
|
||||
if err != NoSupportedAuth {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if ctx != nil {
|
||||
t.Fatal("Invalid Context Method")
|
||||
}
|
||||
|
||||
out := resp.Bytes()
|
||||
if !bytes.Equal(out, []byte{socks5Version, noAcceptable}) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStaticCredentials(t *testing.T) {
|
||||
creds := StaticCredentials{
|
||||
"foo": "bar",
|
||||
"baz": "",
|
||||
}
|
||||
|
||||
if !creds.Valid("foo", "bar") {
|
||||
t.Fatalf("expect valid")
|
||||
}
|
||||
|
||||
if !creds.Valid("baz", "") {
|
||||
t.Fatalf("expect valid")
|
||||
}
|
||||
|
||||
if creds.Valid("foo", "") {
|
||||
t.Fatalf("expect invalid")
|
||||
}
|
||||
}
|
||||
-169
@@ -1,169 +0,0 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type MockConn struct {
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
func (m *MockConn) Write(b []byte) (int, error) {
|
||||
return m.buf.Write(b)
|
||||
}
|
||||
|
||||
func (m *MockConn) RemoteAddr() net.Addr {
|
||||
return &net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: 65432}
|
||||
}
|
||||
|
||||
func TestRequest_Connect(t *testing.T) {
|
||||
// Create a local listener
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
go func() {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadAtLeast(conn, buf, 4); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf, []byte("ping")) {
|
||||
t.Fatalf("bad: %v", buf)
|
||||
}
|
||||
conn.Write([]byte("pong"))
|
||||
}()
|
||||
lAddr := l.Addr().(*net.TCPAddr)
|
||||
|
||||
// Make server
|
||||
s := &Server{config: &Config{
|
||||
Rules: PermitAll(),
|
||||
Resolver: DNSResolver{},
|
||||
Logger: log.New(os.Stdout, "", log.LstdFlags),
|
||||
}}
|
||||
|
||||
// Create the connect request
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.Write([]byte{5, 1, 0, 1, 127, 0, 0, 1})
|
||||
|
||||
port := []byte{0, 0}
|
||||
binary.BigEndian.PutUint16(port, uint16(lAddr.Port))
|
||||
buf.Write(port)
|
||||
|
||||
// Send a ping
|
||||
buf.Write([]byte("ping"))
|
||||
|
||||
// Handle the request
|
||||
resp := &MockConn{}
|
||||
req, err := NewRequest(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if err := s.handleRequest(req, resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Verify response
|
||||
out := resp.buf.Bytes()
|
||||
expected := []byte{
|
||||
5,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
127, 0, 0, 1,
|
||||
0, 0,
|
||||
'p', 'o', 'n', 'g',
|
||||
}
|
||||
|
||||
// Ignore the port for both
|
||||
out[8] = 0
|
||||
out[9] = 0
|
||||
|
||||
if !bytes.Equal(out, expected) {
|
||||
t.Fatalf("bad: %v %v", out, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequest_Connect_RuleFail(t *testing.T) {
|
||||
// Create a local listener
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
go func() {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadAtLeast(conn, buf, 4); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf, []byte("ping")) {
|
||||
t.Fatalf("bad: %v", buf)
|
||||
}
|
||||
conn.Write([]byte("pong"))
|
||||
}()
|
||||
lAddr := l.Addr().(*net.TCPAddr)
|
||||
|
||||
// Make server
|
||||
s := &Server{config: &Config{
|
||||
Rules: PermitNone(),
|
||||
Resolver: DNSResolver{},
|
||||
Logger: log.New(os.Stdout, "", log.LstdFlags),
|
||||
}}
|
||||
|
||||
// Create the connect request
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.Write([]byte{5, 1, 0, 1, 127, 0, 0, 1})
|
||||
|
||||
port := []byte{0, 0}
|
||||
binary.BigEndian.PutUint16(port, uint16(lAddr.Port))
|
||||
buf.Write(port)
|
||||
|
||||
// Send a ping
|
||||
buf.Write([]byte("ping"))
|
||||
|
||||
// Handle the request
|
||||
resp := &MockConn{}
|
||||
req, err := NewRequest(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if err := s.handleRequest(req, resp); !strings.Contains(err.Error(), "blocked by rules") {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Verify response
|
||||
out := resp.buf.Bytes()
|
||||
expected := []byte{
|
||||
5,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0, 0, 0, 0,
|
||||
0, 0,
|
||||
}
|
||||
|
||||
if !bytes.Equal(out, expected) {
|
||||
t.Fatalf("bad: %v %v", out, expected)
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestDNSResolver(t *testing.T) {
|
||||
d := DNSResolver{}
|
||||
ctx := context.Background()
|
||||
|
||||
_, addr, err := d.Resolve(ctx, "localhost")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !addr.IsLoopback() {
|
||||
t.Fatalf("expected loopback")
|
||||
}
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestPermitCommand(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
r := &PermitCommand{true, false, false}
|
||||
|
||||
if _, ok := r.Allow(ctx, &Request{Command: ConnectCommand}); !ok {
|
||||
t.Fatalf("expect connect")
|
||||
}
|
||||
|
||||
if _, ok := r.Allow(ctx, &Request{Command: BindCommand}); ok {
|
||||
t.Fatalf("do not expect bind")
|
||||
}
|
||||
|
||||
if _, ok := r.Allow(ctx, &Request{Command: AssociateCommand}); ok {
|
||||
t.Fatalf("do not expect associate")
|
||||
}
|
||||
}
|
||||
-110
@@ -1,110 +0,0 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSOCKS5_Connect(t *testing.T) {
|
||||
// Create a local listener
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
go func() {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadAtLeast(conn, buf, 4); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf, []byte("ping")) {
|
||||
t.Fatalf("bad: %v", buf)
|
||||
}
|
||||
conn.Write([]byte("pong"))
|
||||
}()
|
||||
lAddr := l.Addr().(*net.TCPAddr)
|
||||
|
||||
// Create a socks server
|
||||
creds := StaticCredentials{
|
||||
"foo": "bar",
|
||||
}
|
||||
cator := UserPassAuthenticator{Credentials: creds}
|
||||
conf := &Config{
|
||||
AuthMethods: []Authenticator{cator},
|
||||
Logger: log.New(os.Stdout, "", log.LstdFlags),
|
||||
}
|
||||
serv, err := New(conf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Start listening
|
||||
go func() {
|
||||
if err := serv.ListenAndServe("tcp", "127.0.0.1:12365"); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// Get a local conn
|
||||
conn, err := net.Dial("tcp", "127.0.0.1:12365")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Connect, auth and connec to local
|
||||
req := bytes.NewBuffer(nil)
|
||||
req.Write([]byte{5})
|
||||
req.Write([]byte{2, NoAuth, UserPassAuth})
|
||||
req.Write([]byte{1, 3, 'f', 'o', 'o', 3, 'b', 'a', 'r'})
|
||||
req.Write([]byte{5, 1, 0, 1, 127, 0, 0, 1})
|
||||
|
||||
port := []byte{0, 0}
|
||||
binary.BigEndian.PutUint16(port, uint16(lAddr.Port))
|
||||
req.Write(port)
|
||||
|
||||
// Send a ping
|
||||
req.Write([]byte("ping"))
|
||||
|
||||
// Send all the bytes
|
||||
conn.Write(req.Bytes())
|
||||
|
||||
// Verify response
|
||||
expected := []byte{
|
||||
socks5Version, UserPassAuth,
|
||||
1, authSuccess,
|
||||
5,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
127, 0, 0, 1,
|
||||
0, 0,
|
||||
'p', 'o', 'n', 'g',
|
||||
}
|
||||
out := make([]byte, len(expected))
|
||||
|
||||
conn.SetDeadline(time.Now().Add(time.Second))
|
||||
if _, err := io.ReadAtLeast(conn, out, len(out)); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Ignore the port
|
||||
out[12] = 0
|
||||
out[13] = 0
|
||||
|
||||
if !bytes.Equal(out, expected) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
-550
@@ -1,550 +0,0 @@
|
||||
package env_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/caarlos0/env"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Some string `env:"somevar"`
|
||||
Other bool `env:"othervar"`
|
||||
Port int `env:"PORT"`
|
||||
Int64Val int64 `env:"INT64VAL"`
|
||||
UintVal uint `env:"UINTVAL"`
|
||||
Uint64Val uint64 `env:"UINT64VAL"`
|
||||
NotAnEnv string
|
||||
DatabaseURL string `env:"DATABASE_URL" envDefault:"postgres://localhost:5432/db"`
|
||||
Strings []string `env:"STRINGS"`
|
||||
SepStrings []string `env:"SEPSTRINGS" envSeparator:":"`
|
||||
Numbers []int `env:"NUMBERS"`
|
||||
Numbers64 []int64 `env:"NUMBERS64"`
|
||||
UNumbers64 []uint64 `env:"UNUMBERS64"`
|
||||
Bools []bool `env:"BOOLS"`
|
||||
Duration time.Duration `env:"DURATION"`
|
||||
Float32 float32 `env:"FLOAT32"`
|
||||
Float64 float64 `env:"FLOAT64"`
|
||||
Float32s []float32 `env:"FLOAT32S"`
|
||||
Float64s []float64 `env:"FLOAT64S"`
|
||||
Durations []time.Duration `env:"DURATIONS"`
|
||||
}
|
||||
|
||||
type ParentStruct struct {
|
||||
InnerStruct *InnerStruct
|
||||
unexported *InnerStruct
|
||||
Ignored *http.Client
|
||||
}
|
||||
|
||||
type InnerStruct struct {
|
||||
Inner string `env:"innervar"`
|
||||
Number uint `env:"innernum"`
|
||||
}
|
||||
|
||||
func TestParsesEnv(t *testing.T) {
|
||||
os.Setenv("somevar", "somevalue")
|
||||
os.Setenv("othervar", "true")
|
||||
os.Setenv("PORT", "8080")
|
||||
os.Setenv("STRINGS", "string1,string2,string3")
|
||||
os.Setenv("SEPSTRINGS", "string1:string2:string3")
|
||||
os.Setenv("NUMBERS", "1,2,3,4")
|
||||
os.Setenv("NUMBERS64", "1,2,2147483640,-2147483640")
|
||||
os.Setenv("UNUMBERS64", "1,2,214748364011,9147483641")
|
||||
os.Setenv("BOOLS", "t,TRUE,0,1")
|
||||
os.Setenv("DURATION", "1s")
|
||||
os.Setenv("FLOAT32", "3.40282346638528859811704183484516925440e+38")
|
||||
os.Setenv("FLOAT64", "1.797693134862315708145274237317043567981e+308")
|
||||
os.Setenv("FLOAT32S", "1.0,2.0,3.0")
|
||||
os.Setenv("FLOAT64S", "1.0,2.0,3.0")
|
||||
os.Setenv("UINTVAL", "44")
|
||||
os.Setenv("UINT64VAL", "6464")
|
||||
os.Setenv("INT64VAL", "-7575")
|
||||
os.Setenv("DURATIONS", "1s,2s,3s")
|
||||
|
||||
defer os.Clearenv()
|
||||
|
||||
cfg := Config{}
|
||||
assert.NoError(t, env.Parse(&cfg))
|
||||
assert.Equal(t, "somevalue", cfg.Some)
|
||||
assert.Equal(t, true, cfg.Other)
|
||||
assert.Equal(t, 8080, cfg.Port)
|
||||
assert.Equal(t, uint(44), cfg.UintVal)
|
||||
assert.Equal(t, int64(-7575), cfg.Int64Val)
|
||||
assert.Equal(t, uint64(6464), cfg.Uint64Val)
|
||||
assert.Equal(t, []string{"string1", "string2", "string3"}, cfg.Strings)
|
||||
assert.Equal(t, []string{"string1", "string2", "string3"}, cfg.SepStrings)
|
||||
assert.Equal(t, []int{1, 2, 3, 4}, cfg.Numbers)
|
||||
assert.Equal(t, []int64{1, 2, 2147483640, -2147483640}, cfg.Numbers64)
|
||||
assert.Equal(t, []uint64{1, 2, 214748364011, 9147483641}, cfg.UNumbers64)
|
||||
assert.Equal(t, []bool{true, true, false, true}, cfg.Bools)
|
||||
d1, _ := time.ParseDuration("1s")
|
||||
assert.Equal(t, d1, cfg.Duration)
|
||||
f32 := float32(3.40282346638528859811704183484516925440e+38)
|
||||
assert.Equal(t, f32, cfg.Float32)
|
||||
f64 := float64(1.797693134862315708145274237317043567981e+308)
|
||||
assert.Equal(t, f64, cfg.Float64)
|
||||
assert.Equal(t, []float32{float32(1.0), float32(2.0), float32(3.0)}, cfg.Float32s)
|
||||
assert.Equal(t, []float64{float64(1.0), float64(2.0), float64(3.0)}, cfg.Float64s)
|
||||
d2, _ := time.ParseDuration("2s")
|
||||
d3, _ := time.ParseDuration("3s")
|
||||
assert.Equal(t, []time.Duration{d1, d2, d3}, cfg.Durations)
|
||||
}
|
||||
|
||||
func TestParsesEnvInner(t *testing.T) {
|
||||
os.Setenv("innervar", "someinnervalue")
|
||||
defer os.Clearenv()
|
||||
cfg := ParentStruct{
|
||||
InnerStruct: &InnerStruct{},
|
||||
unexported: &InnerStruct{},
|
||||
}
|
||||
assert.NoError(t, env.Parse(&cfg))
|
||||
assert.Equal(t, "someinnervalue", cfg.InnerStruct.Inner)
|
||||
}
|
||||
|
||||
func TestParsesEnvInnerNil(t *testing.T) {
|
||||
os.Setenv("innervar", "someinnervalue")
|
||||
defer os.Clearenv()
|
||||
cfg := ParentStruct{}
|
||||
assert.NoError(t, env.Parse(&cfg))
|
||||
}
|
||||
|
||||
func TestParsesEnvInnerInvalid(t *testing.T) {
|
||||
os.Setenv("innernum", "-547")
|
||||
defer os.Clearenv()
|
||||
cfg := ParentStruct{
|
||||
InnerStruct: &InnerStruct{},
|
||||
}
|
||||
assert.Error(t, env.Parse(&cfg))
|
||||
}
|
||||
|
||||
func TestEmptyVars(t *testing.T) {
|
||||
cfg := Config{}
|
||||
assert.NoError(t, env.Parse(&cfg))
|
||||
assert.Equal(t, "", cfg.Some)
|
||||
assert.Equal(t, false, cfg.Other)
|
||||
assert.Equal(t, 0, cfg.Port)
|
||||
assert.Equal(t, uint(0), cfg.UintVal)
|
||||
assert.Equal(t, uint64(0), cfg.Uint64Val)
|
||||
assert.Equal(t, int64(0), cfg.Int64Val)
|
||||
assert.Equal(t, 0, len(cfg.Strings))
|
||||
assert.Equal(t, 0, len(cfg.SepStrings))
|
||||
assert.Equal(t, 0, len(cfg.Numbers))
|
||||
assert.Equal(t, 0, len(cfg.Bools))
|
||||
}
|
||||
|
||||
func TestPassAnInvalidPtr(t *testing.T) {
|
||||
var thisShouldBreak int
|
||||
assert.Error(t, env.Parse(&thisShouldBreak))
|
||||
}
|
||||
|
||||
func TestPassReference(t *testing.T) {
|
||||
cfg := Config{}
|
||||
assert.Error(t, env.Parse(cfg))
|
||||
}
|
||||
|
||||
func TestInvalidBool(t *testing.T) {
|
||||
os.Setenv("othervar", "should-be-a-bool")
|
||||
defer os.Clearenv()
|
||||
|
||||
cfg := Config{}
|
||||
assert.Error(t, env.Parse(&cfg))
|
||||
}
|
||||
|
||||
func TestInvalidInt(t *testing.T) {
|
||||
os.Setenv("PORT", "should-be-an-int")
|
||||
defer os.Clearenv()
|
||||
|
||||
cfg := Config{}
|
||||
assert.Error(t, env.Parse(&cfg))
|
||||
}
|
||||
|
||||
func TestInvalidUint(t *testing.T) {
|
||||
os.Setenv("UINTVAL", "-44")
|
||||
defer os.Clearenv()
|
||||
|
||||
cfg := Config{}
|
||||
assert.Error(t, env.Parse(&cfg))
|
||||
}
|
||||
|
||||
func TestInvalidFloat32(t *testing.T) {
|
||||
os.Setenv("FLOAT32", "AAA")
|
||||
defer os.Clearenv()
|
||||
|
||||
cfg := Config{}
|
||||
assert.Error(t, env.Parse(&cfg))
|
||||
}
|
||||
|
||||
func TestInvalidFloat64(t *testing.T) {
|
||||
os.Setenv("FLOAT64", "AAA")
|
||||
defer os.Clearenv()
|
||||
|
||||
cfg := Config{}
|
||||
assert.Error(t, env.Parse(&cfg))
|
||||
}
|
||||
|
||||
func TestInvalidUint64(t *testing.T) {
|
||||
os.Setenv("UINT64VAL", "AAA")
|
||||
defer os.Clearenv()
|
||||
|
||||
cfg := Config{}
|
||||
assert.Error(t, env.Parse(&cfg))
|
||||
}
|
||||
|
||||
func TestInvalidInt64(t *testing.T) {
|
||||
os.Setenv("INT64VAL", "AAA")
|
||||
defer os.Clearenv()
|
||||
|
||||
cfg := Config{}
|
||||
assert.Error(t, env.Parse(&cfg))
|
||||
}
|
||||
|
||||
func TestInvalidInt64Slice(t *testing.T) {
|
||||
type config struct {
|
||||
BadFloats []int64 `env:"BADINTS"`
|
||||
}
|
||||
|
||||
os.Setenv("BADINTS", "A,2,3")
|
||||
cfg := &config{}
|
||||
assert.Error(t, env.Parse(cfg))
|
||||
}
|
||||
|
||||
func TestInvalidUInt64Slice(t *testing.T) {
|
||||
type config struct {
|
||||
BadFloats []uint64 `env:"BADINTS"`
|
||||
}
|
||||
|
||||
os.Setenv("BADFLOATS", "A,2,3")
|
||||
cfg := &config{}
|
||||
assert.Error(t, env.Parse(cfg))
|
||||
}
|
||||
|
||||
func TestInvalidFloat32Slice(t *testing.T) {
|
||||
type config struct {
|
||||
BadFloats []float32 `env:"BADFLOATS"`
|
||||
}
|
||||
|
||||
os.Setenv("BADFLOATS", "A,2.0,3.0")
|
||||
cfg := &config{}
|
||||
assert.Error(t, env.Parse(cfg))
|
||||
}
|
||||
|
||||
func TestInvalidFloat64Slice(t *testing.T) {
|
||||
type config struct {
|
||||
BadFloats []float64 `env:"BADFLOATS"`
|
||||
}
|
||||
|
||||
os.Setenv("BADFLOATS", "A,2.0,3.0")
|
||||
cfg := &config{}
|
||||
assert.Error(t, env.Parse(cfg))
|
||||
}
|
||||
|
||||
func TestInvalidBoolsSlice(t *testing.T) {
|
||||
type config struct {
|
||||
BadBools []bool `env:"BADBOOLS"`
|
||||
}
|
||||
|
||||
os.Setenv("BADBOOLS", "t,f,TRUE,faaaalse")
|
||||
cfg := &config{}
|
||||
assert.Error(t, env.Parse(cfg))
|
||||
}
|
||||
|
||||
func TestInvalidDuration(t *testing.T) {
|
||||
os.Setenv("DURATION", "should-be-a-valid-duration")
|
||||
defer os.Clearenv()
|
||||
|
||||
cfg := Config{}
|
||||
assert.Error(t, env.Parse(&cfg))
|
||||
}
|
||||
|
||||
func TestInvalidDurations(t *testing.T) {
|
||||
os.Setenv("DURATIONS", "1s,contains-an-invalid-duration,3s")
|
||||
defer os.Clearenv()
|
||||
|
||||
cfg := Config{}
|
||||
assert.Error(t, env.Parse(&cfg))
|
||||
}
|
||||
|
||||
func TestParsesDefaultConfig(t *testing.T) {
|
||||
cfg := Config{}
|
||||
assert.NoError(t, env.Parse(&cfg))
|
||||
assert.Equal(t, "postgres://localhost:5432/db", cfg.DatabaseURL)
|
||||
}
|
||||
|
||||
func TestParseStructWithoutEnvTag(t *testing.T) {
|
||||
cfg := Config{}
|
||||
assert.NoError(t, env.Parse(&cfg))
|
||||
assert.Empty(t, cfg.NotAnEnv)
|
||||
}
|
||||
|
||||
func TestParseStructWithInvalidFieldKind(t *testing.T) {
|
||||
type config struct {
|
||||
WontWorkByte byte `env:"BLAH"`
|
||||
}
|
||||
os.Setenv("BLAH", "a")
|
||||
cfg := config{}
|
||||
assert.Error(t, env.Parse(&cfg))
|
||||
}
|
||||
|
||||
func TestUnsupportedSliceType(t *testing.T) {
|
||||
type config struct {
|
||||
WontWork []map[int]int `env:"WONTWORK"`
|
||||
}
|
||||
|
||||
os.Setenv("WONTWORK", "1,2,3")
|
||||
defer os.Clearenv()
|
||||
|
||||
cfg := &config{}
|
||||
assert.Error(t, env.Parse(cfg))
|
||||
}
|
||||
|
||||
func TestBadSeparator(t *testing.T) {
|
||||
type config struct {
|
||||
WontWork []int `env:"WONTWORK" envSeparator:":"`
|
||||
}
|
||||
|
||||
cfg := &config{}
|
||||
os.Setenv("WONTWORK", "1,2,3,4")
|
||||
defer os.Clearenv()
|
||||
|
||||
assert.Error(t, env.Parse(cfg))
|
||||
}
|
||||
|
||||
func TestNoErrorRequiredSet(t *testing.T) {
|
||||
type config struct {
|
||||
IsRequired string `env:"IS_REQUIRED,required"`
|
||||
}
|
||||
|
||||
cfg := &config{}
|
||||
|
||||
os.Setenv("IS_REQUIRED", "val")
|
||||
defer os.Clearenv()
|
||||
assert.NoError(t, env.Parse(cfg))
|
||||
assert.Equal(t, "val", cfg.IsRequired)
|
||||
}
|
||||
|
||||
func TestErrorRequiredNotSet(t *testing.T) {
|
||||
type config struct {
|
||||
IsRequired string `env:"IS_REQUIRED,required"`
|
||||
}
|
||||
|
||||
cfg := &config{}
|
||||
assert.Error(t, env.Parse(cfg))
|
||||
}
|
||||
|
||||
func TestCustomParser(t *testing.T) {
|
||||
type foo struct {
|
||||
name string
|
||||
}
|
||||
|
||||
type config struct {
|
||||
Var foo `env:"VAR"`
|
||||
}
|
||||
|
||||
os.Setenv("VAR", "test")
|
||||
|
||||
customParserFunc := func(v string) (interface{}, error) {
|
||||
return foo{name: v}, nil
|
||||
}
|
||||
|
||||
cfg := &config{}
|
||||
err := env.ParseWithFuncs(cfg, map[reflect.Type]env.ParserFunc{
|
||||
reflect.TypeOf(foo{}): customParserFunc,
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, cfg.Var.name, "test")
|
||||
}
|
||||
|
||||
func TestParseWithFuncsNoPtr(t *testing.T) {
|
||||
type foo struct{}
|
||||
err := env.ParseWithFuncs(foo{}, nil)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, err, env.ErrNotAStructPtr)
|
||||
}
|
||||
|
||||
func TestParseWithFuncsInvalidType(t *testing.T) {
|
||||
var c int
|
||||
err := env.ParseWithFuncs(&c, nil)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, err, env.ErrNotAStructPtr)
|
||||
}
|
||||
|
||||
func TestCustomParserError(t *testing.T) {
|
||||
type foo struct {
|
||||
name string
|
||||
}
|
||||
|
||||
type config struct {
|
||||
Var foo `env:"VAR"`
|
||||
}
|
||||
|
||||
os.Setenv("VAR", "test")
|
||||
|
||||
customParserFunc := func(v string) (interface{}, error) {
|
||||
return nil, errors.New("something broke")
|
||||
}
|
||||
|
||||
cfg := &config{}
|
||||
err := env.ParseWithFuncs(cfg, map[reflect.Type]env.ParserFunc{
|
||||
reflect.TypeOf(foo{}): customParserFunc,
|
||||
})
|
||||
|
||||
assert.Empty(t, cfg.Var.name, "Var.name should not be filled out when parse errors")
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, err.Error(), "Custom parser error: something broke")
|
||||
}
|
||||
|
||||
func TestCustomParserBasicType(t *testing.T) {
|
||||
type ConstT int32
|
||||
|
||||
type config struct {
|
||||
Const ConstT `env:"CONST_VAL"`
|
||||
}
|
||||
|
||||
exp := ConstT(123)
|
||||
os.Setenv("CONST_VAL", fmt.Sprintf("%d", exp))
|
||||
|
||||
customParserFunc := func(v string) (interface{}, error) {
|
||||
i, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := ConstT(i)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
cfg := &config{}
|
||||
err := env.ParseWithFuncs(cfg, map[reflect.Type]env.ParserFunc{
|
||||
reflect.TypeOf(ConstT(0)): customParserFunc,
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, exp, cfg.Const)
|
||||
}
|
||||
|
||||
func TypeCustomParserBasicInvalid(t *testing.T) {
|
||||
type ConstT int32
|
||||
|
||||
type config struct {
|
||||
Const ConstT `env:"CONST_VAL"`
|
||||
}
|
||||
|
||||
os.Setenv("CONST_VAL", "foobar")
|
||||
|
||||
expErr := errors.New("Random error")
|
||||
customParserFunc := func(_ string) (interface{}, error) {
|
||||
return nil, expErr
|
||||
}
|
||||
|
||||
cfg := &config{}
|
||||
err := env.ParseWithFuncs(cfg, map[reflect.Type]env.ParserFunc{
|
||||
reflect.TypeOf(ConstT(0)): customParserFunc,
|
||||
})
|
||||
|
||||
assert.Empty(t, cfg.Const)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, expErr, err)
|
||||
}
|
||||
|
||||
func TestCustomParserBasicUnsupported(t *testing.T) {
|
||||
type ConstT int32
|
||||
|
||||
type config struct {
|
||||
Const ConstT `env:"CONST_VAL"`
|
||||
}
|
||||
|
||||
exp := ConstT(123)
|
||||
os.Setenv("CONST_VAL", fmt.Sprintf("%d", exp))
|
||||
|
||||
cfg := &config{}
|
||||
err := env.Parse(cfg)
|
||||
|
||||
assert.Zero(t, cfg.Const)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, env.ErrUnsupportedType, err)
|
||||
}
|
||||
|
||||
func TestUnsupportedStructType(t *testing.T) {
|
||||
type config struct {
|
||||
Foo http.Client `env:"FOO"`
|
||||
}
|
||||
|
||||
os.Setenv("FOO", "foo")
|
||||
|
||||
cfg := &config{}
|
||||
err := env.Parse(cfg)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, env.ErrUnsupportedType, err)
|
||||
}
|
||||
|
||||
func TestEmptyOption(t *testing.T) {
|
||||
type config struct {
|
||||
Var string `env:"VAR,"`
|
||||
}
|
||||
|
||||
cfg := &config{}
|
||||
|
||||
os.Setenv("VAR", "val")
|
||||
defer os.Clearenv()
|
||||
assert.NoError(t, env.Parse(cfg))
|
||||
assert.Equal(t, "val", cfg.Var)
|
||||
}
|
||||
|
||||
func TestErrorOptionNotRecognized(t *testing.T) {
|
||||
type config struct {
|
||||
Var string `env:"VAR,not_supported!"`
|
||||
}
|
||||
|
||||
cfg := &config{}
|
||||
assert.Error(t, env.Parse(cfg))
|
||||
|
||||
}
|
||||
|
||||
func ExampleParse() {
|
||||
type config struct {
|
||||
Home string `env:"HOME"`
|
||||
Port int `env:"PORT" envDefault:"3000"`
|
||||
IsProduction bool `env:"PRODUCTION"`
|
||||
}
|
||||
os.Setenv("HOME", "/tmp/fakehome")
|
||||
cfg := config{}
|
||||
env.Parse(&cfg)
|
||||
fmt.Println(cfg)
|
||||
// Output: {/tmp/fakehome 3000 false}
|
||||
}
|
||||
|
||||
func ExampleParseRequiredField() {
|
||||
type config struct {
|
||||
Home string `env:"HOME"`
|
||||
Port int `env:"PORT" envDefault:"3000"`
|
||||
IsProduction bool `env:"PRODUCTION"`
|
||||
SecretKey string `env:"SECRET_KEY,required"`
|
||||
}
|
||||
os.Setenv("HOME", "/tmp/fakehome")
|
||||
cfg := config{}
|
||||
err := env.Parse(&cfg)
|
||||
fmt.Println(err)
|
||||
// Output: Required environment variable SECRET_KEY is not set
|
||||
}
|
||||
|
||||
func ExampleParseMultipleOptions() {
|
||||
type config struct {
|
||||
Home string `env:"HOME"`
|
||||
Port int `env:"PORT" envDefault:"3000"`
|
||||
IsProduction bool `env:"PRODUCTION"`
|
||||
SecretKey string `env:"SECRET_KEY,required,option1"`
|
||||
}
|
||||
os.Setenv("HOME", "/tmp/fakehome")
|
||||
cfg := config{}
|
||||
err := env.Parse(&cfg)
|
||||
fmt.Println(err)
|
||||
// Output: Env tag option option1 not supported.
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/caarlos0/env"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
Home string `env:"HOME"`
|
||||
Port int `env:"PORT" envDefault:"3000"`
|
||||
IsProduction bool `env:"PRODUCTION"`
|
||||
Hosts []string `env:"HOSTS" envSeparator:":"`
|
||||
Duration time.Duration `env:"DURATION"`
|
||||
ExampleFoo Foo `env:"EXAMPLE_FOO"`
|
||||
}
|
||||
|
||||
type Foo struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := config{}
|
||||
|
||||
// Parse for built-in types
|
||||
if err := env.Parse(&cfg); err != nil {
|
||||
log.Fatal("Unable to parse envs: ", err)
|
||||
}
|
||||
|
||||
// OR w/ a custom parser for `Foo`
|
||||
//
|
||||
// if err := env.ParseWithFuncs(&cfg, env.CustomParsers{
|
||||
// reflect.TypeOf(Foo{}): fooParser,
|
||||
// }); err != nil {
|
||||
// log.Fatal("Unable to parse envs: ", err)
|
||||
// }
|
||||
|
||||
fmt.Printf("%+v\n", cfg)
|
||||
}
|
||||
|
||||
func fooParser(value string) (interface{}, error) {
|
||||
return Foo{
|
||||
Name: strings.ToUpper(value),
|
||||
}, nil
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
parsers
|
||||
=======
|
||||
This directory contains pre-built, custom parsers that can be used with `env.ParseWithFuncs`
|
||||
to facilitate the parsing of envs that are not basic types.
|
||||
|
||||
Example Usage:
|
||||
|
||||
```golang
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
|
||||
"github.com/caarlos0/env"
|
||||
"github.com/caarlos0/env/parsers"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
ExampleURL url.URL `env:"EXAMPLE_URL" envDefault:"https://google.com"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := config{}
|
||||
|
||||
if err := env.ParseWithFuncs(&cfg, env.CustomParsers{
|
||||
parsers.URLType: parsers.URLFunc,
|
||||
}); err != nil {
|
||||
log.Fatal("Unable to parse envs: ", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Scheme: %v Host: %v\n", cfg.ExampleURL.Scheme, cfg.ExampleURL.Host)
|
||||
}
|
||||
```
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
// Package parsers contains custom parser funcs for common, non-built-in types
|
||||
package parsers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
// URLType is a helper var that represents the `reflect.Type`` of `url.URL`
|
||||
URLType = reflect.TypeOf(url.URL{})
|
||||
)
|
||||
|
||||
// URLFunc is a basic parser for the url.URL type that should be used with `env.ParseWithFuncs()`
|
||||
func URLFunc(v string) (interface{}, error) {
|
||||
u, err := url.Parse(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to complete URL parse: %v", err)
|
||||
}
|
||||
|
||||
return *u, nil
|
||||
}
|
||||
Reference in New Issue
Block a user