tests: update config to use testify

we should port the other test cases to testify too
This commit is contained in:
Nicola Murino
2020-05-02 15:47:23 +02:00
parent b006c5f914
commit da90dbe645
3 changed files with 97 additions and 125 deletions

View File

@@ -13,239 +13,207 @@ import (
"github.com/drakkan/sftpgo/httpclient" "github.com/drakkan/sftpgo/httpclient"
"github.com/drakkan/sftpgo/httpd" "github.com/drakkan/sftpgo/httpd"
"github.com/drakkan/sftpgo/sftpd" "github.com/drakkan/sftpgo/sftpd"
"github.com/stretchr/testify/assert"
) )
const ( const (
tempConfigName = "temp" tempConfigName = "temp"
configName = "sftpgo"
) )
func TestLoadConfigTest(t *testing.T) { func TestLoadConfigTest(t *testing.T) {
configDir := ".." configDir := ".."
err := config.LoadConfig(configDir, "") err := config.LoadConfig(configDir, configName)
if err != nil { assert.Nil(t, err)
t.Errorf("error loading config") assert.NotEqual(t, httpd.Conf{}, config.GetHTTPConfig())
} assert.NotEqual(t, dataprovider.Config{}, config.GetProviderConf())
emptyHTTPDConf := httpd.Conf{} assert.NotEqual(t, sftpd.Configuration{}, config.GetSFTPDConfig())
if config.GetHTTPDConfig() == emptyHTTPDConf { assert.NotEqual(t, httpclient.Config{}, config.GetHTTPConfig())
t.Errorf("error loading httpd conf")
}
emptyProviderConf := dataprovider.Config{}
if config.GetProviderConf().Driver == emptyProviderConf.Driver {
t.Errorf("error loading provider conf")
}
emptySFTPDConf := sftpd.Configuration{}
if config.GetSFTPDConfig().BindPort == emptySFTPDConf.BindPort {
t.Errorf("error loading SFTPD conf")
}
emptyHTTPConfig := httpclient.Config{}
if config.GetHTTPConfig().Timeout == emptyHTTPConfig.Timeout {
t.Errorf("error loading HTTP conf")
}
confName := tempConfigName + ".json" confName := tempConfigName + ".json"
configFilePath := filepath.Join(configDir, confName) configFilePath := filepath.Join(configDir, confName)
err = config.LoadConfig(configDir, tempConfigName) err = config.LoadConfig(configDir, tempConfigName)
if err == nil { assert.NotNil(t, err)
t.Errorf("loading a non existent config file must fail") err = ioutil.WriteFile(configFilePath, []byte("{invalid json}"), 0666)
} assert.Nil(t, err)
ioutil.WriteFile(configFilePath, []byte("{invalid json}"), 0666)
err = config.LoadConfig(configDir, tempConfigName) err = config.LoadConfig(configDir, tempConfigName)
if err == nil { assert.NotNil(t, err)
t.Errorf("loading an invalid config file must fail") err = ioutil.WriteFile(configFilePath, []byte("{\"sftpd\": {\"bind_port\": \"a\"}}"), 0666)
} assert.Nil(t, err)
ioutil.WriteFile(configFilePath, []byte("{\"sftpd\": {\"bind_port\": \"a\"}}"), 0666)
err = config.LoadConfig(configDir, tempConfigName) err = config.LoadConfig(configDir, tempConfigName)
if err == nil { assert.NotNil(t, err)
t.Errorf("loading a config with an invalid bond_port must fail") err = os.Remove(configFilePath)
} assert.Nil(t, err)
os.Remove(configFilePath)
} }
func TestEmptyBanner(t *testing.T) { func TestEmptyBanner(t *testing.T) {
configDir := ".." configDir := ".."
confName := tempConfigName + ".json" confName := tempConfigName + ".json"
configFilePath := filepath.Join(configDir, confName) configFilePath := filepath.Join(configDir, confName)
config.LoadConfig(configDir, "") err := config.LoadConfig(configDir, configName)
assert.Nil(t, err)
sftpdConf := config.GetSFTPDConfig() sftpdConf := config.GetSFTPDConfig()
sftpdConf.Banner = " " sftpdConf.Banner = " "
c := make(map[string]sftpd.Configuration) c := make(map[string]sftpd.Configuration)
c["sftpd"] = sftpdConf c["sftpd"] = sftpdConf
jsonConf, _ := json.Marshal(c) jsonConf, _ := json.Marshal(c)
err := ioutil.WriteFile(configFilePath, jsonConf, 0666) err = ioutil.WriteFile(configFilePath, jsonConf, 0666)
if err != nil { assert.Nil(t, err)
t.Errorf("error saving temporary configuration") err = config.LoadConfig(configDir, tempConfigName)
} assert.Nil(t, err)
config.LoadConfig(configDir, tempConfigName)
sftpdConf = config.GetSFTPDConfig() sftpdConf = config.GetSFTPDConfig()
if strings.TrimSpace(sftpdConf.Banner) == "" { assert.NotEmpty(t, strings.TrimSpace(sftpdConf.Banner))
t.Errorf("SFTPD banner cannot be empty") err = os.Remove(configFilePath)
} assert.Nil(t, err)
os.Remove(configFilePath)
} }
func TestInvalidUploadMode(t *testing.T) { func TestInvalidUploadMode(t *testing.T) {
configDir := ".." configDir := ".."
confName := tempConfigName + ".json" confName := tempConfigName + ".json"
configFilePath := filepath.Join(configDir, confName) configFilePath := filepath.Join(configDir, confName)
config.LoadConfig(configDir, "") err := config.LoadConfig(configDir, configName)
assert.Nil(t, err)
sftpdConf := config.GetSFTPDConfig() sftpdConf := config.GetSFTPDConfig()
sftpdConf.UploadMode = 10 sftpdConf.UploadMode = 10
c := make(map[string]sftpd.Configuration) c := make(map[string]sftpd.Configuration)
c["sftpd"] = sftpdConf c["sftpd"] = sftpdConf
jsonConf, _ := json.Marshal(c) jsonConf, err := json.Marshal(c)
err := ioutil.WriteFile(configFilePath, jsonConf, 0666) assert.Nil(t, err)
if err != nil { err = ioutil.WriteFile(configFilePath, jsonConf, 0666)
t.Errorf("error saving temporary configuration") assert.Nil(t, err)
}
err = config.LoadConfig(configDir, tempConfigName) err = config.LoadConfig(configDir, tempConfigName)
if err == nil { assert.NotNil(t, err)
t.Errorf("Loading configuration with invalid upload_mode must fail") err = os.Remove(configFilePath)
} assert.Nil(t, err)
os.Remove(configFilePath)
} }
func TestInvalidExternalAuthScope(t *testing.T) { func TestInvalidExternalAuthScope(t *testing.T) {
configDir := ".." configDir := ".."
confName := tempConfigName + ".json" confName := tempConfigName + ".json"
configFilePath := filepath.Join(configDir, confName) configFilePath := filepath.Join(configDir, confName)
config.LoadConfig(configDir, "") err := config.LoadConfig(configDir, configName)
assert.Nil(t, err)
providerConf := config.GetProviderConf() providerConf := config.GetProviderConf()
providerConf.ExternalAuthScope = 10 providerConf.ExternalAuthScope = 10
c := make(map[string]dataprovider.Config) c := make(map[string]dataprovider.Config)
c["data_provider"] = providerConf c["data_provider"] = providerConf
jsonConf, _ := json.Marshal(c) jsonConf, err := json.Marshal(c)
err := ioutil.WriteFile(configFilePath, jsonConf, 0666) assert.Nil(t, err)
if err != nil { err = ioutil.WriteFile(configFilePath, jsonConf, 0666)
t.Errorf("error saving temporary configuration") assert.Nil(t, err)
}
err = config.LoadConfig(configDir, tempConfigName) err = config.LoadConfig(configDir, tempConfigName)
if err == nil { assert.NotNil(t, err)
t.Errorf("Loading configuration with invalid external_auth_scope must fail") err = os.Remove(configFilePath)
} assert.Nil(t, err)
os.Remove(configFilePath)
} }
func TestInvalidCredentialsPath(t *testing.T) { func TestInvalidCredentialsPath(t *testing.T) {
configDir := ".." configDir := ".."
confName := tempConfigName + ".json" confName := tempConfigName + ".json"
configFilePath := filepath.Join(configDir, confName) configFilePath := filepath.Join(configDir, confName)
config.LoadConfig(configDir, "") err := config.LoadConfig(configDir, configName)
assert.Nil(t, err)
providerConf := config.GetProviderConf() providerConf := config.GetProviderConf()
providerConf.CredentialsPath = "" providerConf.CredentialsPath = ""
c := make(map[string]dataprovider.Config) c := make(map[string]dataprovider.Config)
c["data_provider"] = providerConf c["data_provider"] = providerConf
jsonConf, _ := json.Marshal(c) jsonConf, err := json.Marshal(c)
err := ioutil.WriteFile(configFilePath, jsonConf, 0666) assert.Nil(t, err)
if err != nil { err = ioutil.WriteFile(configFilePath, jsonConf, 0666)
t.Errorf("error saving temporary configuration") assert.Nil(t, err)
}
err = config.LoadConfig(configDir, tempConfigName) err = config.LoadConfig(configDir, tempConfigName)
if err == nil { assert.NotNil(t, err)
t.Errorf("Loading configuration with credentials path must fail") err = os.Remove(configFilePath)
} assert.Nil(t, err)
os.Remove(configFilePath)
} }
func TestInvalidProxyProtocol(t *testing.T) { func TestInvalidProxyProtocol(t *testing.T) {
configDir := ".." configDir := ".."
confName := tempConfigName + ".json" confName := tempConfigName + ".json"
configFilePath := filepath.Join(configDir, confName) configFilePath := filepath.Join(configDir, confName)
config.LoadConfig(configDir, "") err := config.LoadConfig(configDir, configName)
assert.Nil(t, err)
sftpdConf := config.GetSFTPDConfig() sftpdConf := config.GetSFTPDConfig()
sftpdConf.ProxyProtocol = 10 sftpdConf.ProxyProtocol = 10
c := make(map[string]sftpd.Configuration) c := make(map[string]sftpd.Configuration)
c["sftpd"] = sftpdConf c["sftpd"] = sftpdConf
jsonConf, _ := json.Marshal(c) jsonConf, err := json.Marshal(c)
err := ioutil.WriteFile(configFilePath, jsonConf, 0666) assert.Nil(t, err)
if err != nil { err = ioutil.WriteFile(configFilePath, jsonConf, 0666)
t.Errorf("error saving temporary configuration") assert.Nil(t, err)
}
err = config.LoadConfig(configDir, tempConfigName) err = config.LoadConfig(configDir, tempConfigName)
if err == nil { assert.NotNil(t, err)
t.Errorf("Loading configuration with invalid proxy_protocol must fail") err = os.Remove(configFilePath)
} assert.Nil(t, err)
os.Remove(configFilePath)
} }
func TestInvalidUsersBaseDir(t *testing.T) { func TestInvalidUsersBaseDir(t *testing.T) {
configDir := ".." configDir := ".."
confName := tempConfigName + ".json" confName := tempConfigName + ".json"
configFilePath := filepath.Join(configDir, confName) configFilePath := filepath.Join(configDir, confName)
config.LoadConfig(configDir, "") err := config.LoadConfig(configDir, configName)
assert.Nil(t, err)
providerConf := config.GetProviderConf() providerConf := config.GetProviderConf()
providerConf.UsersBaseDir = "." providerConf.UsersBaseDir = "."
c := make(map[string]dataprovider.Config) c := make(map[string]dataprovider.Config)
c["data_provider"] = providerConf c["data_provider"] = providerConf
jsonConf, _ := json.Marshal(c) jsonConf, err := json.Marshal(c)
err := ioutil.WriteFile(configFilePath, jsonConf, 0666) assert.Nil(t, err)
if err != nil { err = ioutil.WriteFile(configFilePath, jsonConf, 0666)
t.Errorf("error saving temporary configuration") assert.Nil(t, err)
}
err = config.LoadConfig(configDir, tempConfigName) err = config.LoadConfig(configDir, tempConfigName)
if err == nil { assert.NotNil(t, err)
t.Errorf("Loading configuration with invalid users base dir must fail") err = os.Remove(configFilePath)
} assert.Nil(t, err)
os.Remove(configFilePath)
} }
func TestHookCompatibity(t *testing.T) { func TestHookCompatibity(t *testing.T) {
configDir := ".." configDir := ".."
confName := tempConfigName + ".json" confName := tempConfigName + ".json"
configFilePath := filepath.Join(configDir, confName) configFilePath := filepath.Join(configDir, confName)
config.LoadConfig(configDir, "") err := config.LoadConfig(configDir, configName)
assert.Nil(t, err)
providerConf := config.GetProviderConf() providerConf := config.GetProviderConf()
providerConf.ExternalAuthProgram = "ext_auth_program" providerConf.ExternalAuthProgram = "ext_auth_program"
providerConf.PreLoginProgram = "pre_login_program" providerConf.PreLoginProgram = "pre_login_program"
c := make(map[string]dataprovider.Config) c := make(map[string]dataprovider.Config)
c["data_provider"] = providerConf c["data_provider"] = providerConf
jsonConf, _ := json.Marshal(c) jsonConf, err := json.Marshal(c)
err := ioutil.WriteFile(configFilePath, jsonConf, 0666) assert.Nil(t, err)
if err != nil { err = ioutil.WriteFile(configFilePath, jsonConf, 0666)
t.Errorf("error saving temporary configuration") assert.Nil(t, err)
} err = config.LoadConfig(configDir, tempConfigName)
config.LoadConfig(configDir, tempConfigName)
providerConf = config.GetProviderConf() providerConf = config.GetProviderConf()
if providerConf.ExternalAuthHook != "ext_auth_program" { assert.Equal(t, "ext_auth_program", providerConf.ExternalAuthHook)
t.Error("unexpected external auth hook") assert.Equal(t, "pre_login_program", providerConf.PreLoginHook)
} err = os.Remove(configFilePath)
if providerConf.PreLoginHook != "pre_login_program" { assert.Nil(t, err)
t.Error("unexpected pre-login hook")
}
os.Remove(configFilePath)
sftpdConf := config.GetSFTPDConfig() sftpdConf := config.GetSFTPDConfig()
sftpdConf.KeyboardInteractiveProgram = "key_int_program" sftpdConf.KeyboardInteractiveProgram = "key_int_program"
cnf := make(map[string]sftpd.Configuration) cnf := make(map[string]sftpd.Configuration)
cnf["sftpd"] = sftpdConf cnf["sftpd"] = sftpdConf
jsonConf, _ = json.Marshal(cnf) jsonConf, err = json.Marshal(cnf)
assert.Nil(t, err)
err = ioutil.WriteFile(configFilePath, jsonConf, 0666) err = ioutil.WriteFile(configFilePath, jsonConf, 0666)
if err != nil { assert.Nil(t, err)
t.Errorf("error saving temporary configuration") err = config.LoadConfig(configDir, tempConfigName)
} assert.Nil(t, err)
config.LoadConfig(configDir, tempConfigName)
sftpdConf = config.GetSFTPDConfig() sftpdConf = config.GetSFTPDConfig()
if sftpdConf.KeyboardInteractiveHook != "key_int_program" { assert.Equal(t, "key_int_program", sftpdConf.KeyboardInteractiveHook)
t.Error("unexpected keyboard interactive hook") err = os.Remove(configFilePath)
} assert.Nil(t, err)
os.Remove(configFilePath)
} }
func TestSetGetConfig(t *testing.T) { func TestSetGetConfig(t *testing.T) {
sftpdConf := config.GetSFTPDConfig() sftpdConf := config.GetSFTPDConfig()
sftpdConf.IdleTimeout = 3 sftpdConf.IdleTimeout = 3
config.SetSFTPDConfig(sftpdConf) config.SetSFTPDConfig(sftpdConf)
if config.GetSFTPDConfig().IdleTimeout != sftpdConf.IdleTimeout { assert.Equal(t, sftpdConf.IdleTimeout, config.GetSFTPDConfig().IdleTimeout)
t.Errorf("set sftpd conf failed")
}
dataProviderConf := config.GetProviderConf() dataProviderConf := config.GetProviderConf()
dataProviderConf.Host = "test host" dataProviderConf.Host = "test host"
config.SetProviderConf(dataProviderConf) config.SetProviderConf(dataProviderConf)
if config.GetProviderConf().Host != dataProviderConf.Host { assert.Equal(t, dataProviderConf.Host, config.GetProviderConf().Host)
t.Errorf("set data provider conf failed")
}
httpdConf := config.GetHTTPDConfig() httpdConf := config.GetHTTPDConfig()
httpdConf.BindAddress = "0.0.0.0" httpdConf.BindAddress = "0.0.0.0"
config.SetHTTPDConfig(httpdConf) config.SetHTTPDConfig(httpdConf)
if config.GetHTTPDConfig().BindAddress != httpdConf.BindAddress { assert.Equal(t, httpdConf.BindAddress, config.GetHTTPDConfig().BindAddress)
t.Errorf("set httpd conf failed")
}
} }

1
go.mod
View File

@@ -32,6 +32,7 @@ require (
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.6.3 github.com/spf13/viper v1.6.3
github.com/stretchr/testify v1.5.1
go.etcd.io/bbolt v1.3.4 go.etcd.io/bbolt v1.3.4
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d golang.org/x/sys v0.0.0-20200331124033-c3d80250170d

3
go.sum
View File

@@ -59,6 +59,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
@@ -194,6 +195,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
@@ -253,6 +255,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=