From 36e817df9234947c9c1e00c9326f100e28880cc8 Mon Sep 17 00:00:00 2001 From: James Elliott Date: Wed, 25 Jan 2023 15:11:05 +1100 Subject: [PATCH] test(suites): load environment into suites (#4762) * test(suites): load environment into suites * test(suites): default setup suite * test(suites): create base suite * test(suites): fix nil ptr * test(suites): add logging * test: fix missing devworkflow path * refactor: apply suggestions * refactor: log * fix: dev workflow requires env file to trigger vite hmr * fix(suites): fix dynamic configuration in dev workflow for all proxies * refactor: apply final suggestions * fix: pass log level to suites * fix(suites): include pathprefix to prevent react router basename issues * fix: missing setup logging calls * fix: gate suite setup funcs * test: fix lint * test: fix tmp dir * fix(suites): fix gitignore of .env.development with vite hmr Co-authored-by: Amir Zarrinkafsh --- cmd/authelia-scripts/cmd/suites.go | 2 + cmd/authelia-suites/main.go | 4 +- internal/suites/const.go | 6 +- .../authelia/docker-compose.backend.dev.yml | 4 +- .../suites/example/compose/caddy/Caddyfile | 4 + .../suites/example/compose/envoy/envoy.yaml | 4 + .../example/compose/haproxy/haproxy.cfg | 11 +- .../suites/scenario_available_methods_test.go | 2 +- .../suites/scenario_bypass_policy_test.go | 2 +- .../suites/scenario_custom_headers_test.go | 2 +- .../scenario_default_redirection_url_test.go | 2 +- internal/suites/scenario_inactivity_test.go | 2 +- .../scenario_multiple_cookie_domain_test.go | 2 +- internal/suites/scenario_oidc_test.go | 2 +- internal/suites/scenario_one_factor_test.go | 4 +- .../scenario_password_complexity_test.go | 2 +- .../suites/scenario_redirection_check_test.go | 2 +- .../suites/scenario_redirection_url_test.go | 2 +- internal/suites/scenario_regulation_test.go | 2 +- .../suites/scenario_reset_password_test.go | 2 +- internal/suites/scenario_signin_email_test.go | 2 +- internal/suites/scenario_two_factor_test.go | 2 +- .../suites/scenario_user_preferences_test.go | 2 +- internal/suites/suite_activedirectory_test.go | 4 +- internal/suites/suite_bypass_all_test.go | 14 ++- internal/suites/suite_caddy_test.go | 4 +- internal/suites/suite_cli_test.go | 10 +- internal/suites/suite_docker_test.go | 4 +- internal/suites/suite_duo_push_test.go | 16 ++- internal/suites/suite_envoy_test.go | 4 +- internal/suites/suite_haproxy_test.go | 4 +- .../suites/suite_high_availability_test.go | 16 ++- internal/suites/suite_kubernetes_test.go | 4 +- internal/suites/suite_ldap_test.go | 4 +- internal/suites/suite_mariadb_test.go | 4 +- .../suites/suite_multi_cookie_domain_test.go | 8 +- internal/suites/suite_mysql_test.go | 4 +- internal/suites/suite_network_acl_test.go | 8 +- internal/suites/suite_oidc_test.go | 4 +- internal/suites/suite_oidc_traefik_test.go | 4 +- internal/suites/suite_one_factor_only_test.go | 36 +++--- internal/suites/suite_pathprefix_test.go | 4 +- internal/suites/suite_postgres_test.go | 4 +- internal/suites/suite_short_timeouts_test.go | 4 +- internal/suites/suite_standalone_test.go | 14 ++- internal/suites/suite_traefik2_test.go | 4 +- internal/suites/suite_traefik_test.go | 4 +- internal/suites/suites.go | 18 ++- internal/suites/utils.go | 107 +++++++++++++++--- internal/utils/crypto_test.go | 4 +- 50 files changed, 285 insertions(+), 99 deletions(-) diff --git a/cmd/authelia-scripts/cmd/suites.go b/cmd/authelia-scripts/cmd/suites.go index be903996d..40bc76376 100644 --- a/cmd/authelia-scripts/cmd/suites.go +++ b/cmd/authelia-scripts/cmd/suites.go @@ -348,6 +348,8 @@ func runSuiteTests(suiteName string, withEnv bool) error { cmd.Env = append(cmd.Env, "HEADLESS=y") } + cmd.Env = append(cmd.Env, "SUITES_LOG_LEVEL="+log.GetLevel().String()) + testErr := cmd.Run() // If the tests failed, run the error hook. diff --git a/cmd/authelia-suites/main.go b/cmd/authelia-suites/main.go index 578f84d1b..d6ed7e344 100644 --- a/cmd/authelia-suites/main.go +++ b/cmd/authelia-suites/main.go @@ -140,9 +140,7 @@ func setupSuite(cmd *cobra.Command, args []string) { log.Fatal(err) } - err = s.SetUp(suiteTmpDirectory) - - if err != nil { + if err = s.SetUp(suiteTmpDirectory); err != nil { log.Error("Failure during environment deployment.") teardownSuite(nil, args) log.Fatal(err) diff --git a/internal/suites/const.go b/internal/suites/const.go index 45cd89a00..b1fec2124 100644 --- a/internal/suites/const.go +++ b/internal/suites/const.go @@ -2,7 +2,6 @@ package suites import ( "fmt" - "os" "github.com/authelia/authelia/v4/internal/configuration/schema" ) @@ -14,9 +13,6 @@ var ( Example3DotCom = "example3.com:8080" ) -// PathPrefix the prefix/url_base of the login portal. -var PathPrefix = os.Getenv("PathPrefix") - // LoginBaseURLFmt the base URL of the login portal for specified baseDomain. func LoginBaseURLFmt(baseDomain string) string { if baseDomain == "" { @@ -81,6 +77,8 @@ const ( ) const ( + envFileProd = "./web/.env.production" + envFileDev = "./web/.env.development" namespaceAuthelia = "authelia" namespaceDashboard = "kubernetes-dashboard" namespaceKube = "kube-system" diff --git a/internal/suites/example/compose/authelia/docker-compose.backend.dev.yml b/internal/suites/example/compose/authelia/docker-compose.backend.dev.yml index 70b015131..73ded6bcd 100644 --- a/internal/suites/example/compose/authelia/docker-compose.backend.dev.yml +++ b/internal/suites/example/compose/authelia/docker-compose.backend.dev.yml @@ -21,11 +21,11 @@ services: - '${GOPATH}:/go' labels: # Traefik 1.x - - 'traefik.frontend.rule=Host:login.example.com;PathPrefix:/api,/locales' + - 'traefik.frontend.rule=Host:login.example.com;PathPrefix:/.well-known,/api,/locales,/devworkflow,/jwks.json' - 'traefik.protocol=https' # Traefik 2.x - 'traefik.enable=true' - - 'traefik.http.routers.authelia_backend.rule=Host(`login.example.com`) && PathPrefix(`/.well-known`) || Host(`login.example.com`) && PathPrefix(`${PathPrefix}/.well-known`) || Host(`login.example.com`) && PathPrefix(`/api`) || Host(`login.example.com`) && PathPrefix(`${PathPrefix}/api`) || Host(`login.example.com`) && PathPrefix(`/locales`) || Host(`login.example.com`) && PathPrefix(`${PathPrefix}/locales`) || Host(`login.example.com`) && Path(`/jwks.json`) || Host(`login.example.com`) && Path(`${PathPrefix}/jwks.json`)' # yamllint disable-line rule:line-length + - 'traefik.http.routers.authelia_backend.rule=Host(`login.example.com`) && PathPrefix(`/.well-known`) || Host(`login.example.com`) && PathPrefix(`${PathPrefix}/.well-known`) || Host(`login.example.com`) && PathPrefix(`/api`) || Host(`login.example.com`) && PathPrefix(`${PathPrefix}/api`) || Host(`login.example.com`) && PathPrefix(`/locales`) || Host(`login.example.com`) && PathPrefix(`${PathPrefix}/locales`) || Host(`login.example.com`) && Path(`/devworkflow`) || Host(`login.example.com`) && Path(`${PathPrefix}/devworkflow`) || Host(`login.example.com`) && Path(`/jwks.json`) || Host(`login.example.com`) && Path(`${PathPrefix}/jwks.json`) || Host(`login.example.com`) && Path(`/static/media/logo.png`) || Host(`login.example.com`) && Path(`${PathPrefix}/static/media/logo.png`)' # yamllint disable-line rule:line-length - 'traefik.http.routers.authelia_backend.entrypoints=https' - 'traefik.http.routers.authelia_backend.tls=true' - 'traefik.http.services.authelia_backend.loadbalancer.server.scheme=https' diff --git a/internal/suites/example/compose/caddy/Caddyfile b/internal/suites/example/compose/caddy/Caddyfile index c0fc6d90e..b9d0d89ee 100644 --- a/internal/suites/example/compose/caddy/Caddyfile +++ b/internal/suites/example/compose/caddy/Caddyfile @@ -29,6 +29,10 @@ login.example.com:8080 { import tls-transport } + reverse_proxy /devworkflow authelia-backend:9091 { + import tls-transport + } + reverse_proxy /jwks.json authelia-backend:9091 { import tls-transport } diff --git a/internal/suites/example/compose/envoy/envoy.yaml b/internal/suites/example/compose/envoy/envoy.yaml index fc5039ebe..796558927 100644 --- a/internal/suites/example/compose/envoy/envoy.yaml +++ b/internal/suites/example/compose/envoy/envoy.yaml @@ -40,6 +40,10 @@ static_resources: prefix: "/locales/" route: cluster: authelia-backend + - match: + path: "/devworkflow" + route: + cluster: authelia-backend - match: path: "/jwks.json" route: diff --git a/internal/suites/example/compose/haproxy/haproxy.cfg b/internal/suites/example/compose/haproxy/haproxy.cfg index a73fbc48f..e1ecdefd4 100644 --- a/internal/suites/example/compose/haproxy/haproxy.cfg +++ b/internal/suites/example/compose/haproxy/haproxy.cfg @@ -25,6 +25,10 @@ frontend fe_http bind *:8080 ssl crt /usr/local/etc/haproxy/haproxy.pem acl api-path path_beg -i /api + acl wellknown-path path_beg -i /.well-known + acl locales-path path_beg -i /locales + acl devworkflow-path path -i -m end /devworkflow + acl jwks-path path -i -m end /jwks.json acl headers-path path -i -m end /headers acl host-authelia-portal hdr(host) -i login.example.com:8080 acl protected-frontends hdr(host) -m reg -i ^(?i)(admin|home|public|secure|singlefactor)\.example\.com @@ -54,18 +58,16 @@ frontend fe_http http-request redirect location https://login.example.com:8080/?rd=%[var(req.scheme)]://%[base]%[var(req.questionmark)]%[query] if protected-frontends !{ var(txn.auth_response_successful) -m bool } - use_backend be_authelia if host-authelia-portal api-path + use_backend be_authelia if host-authelia-portal api-path || wellknown-path || locales-path || devworkflow-path || jwks-path use_backend fe_authelia if host-authelia-portal !api-path use_backend be_httpbin if protected-frontends headers-path use_backend be_mail if { hdr(host) -i mail.example.com:8080 } use_backend be_protected if protected-frontends backend be_auth_request - mode http server proxy 127.0.0.1:8085 listen be_auth_request_proxy - mode http bind 127.0.0.1:8085 server authelia-backend authelia-backend:9091 resolvers docker ssl verify none @@ -73,6 +75,9 @@ backend be_authelia server authelia-backend authelia-backend:9091 resolvers docker ssl verify none backend fe_authelia + option httpchk + http-check expect rstatus ^2 + server authelia-frontend authelia-frontend:3000 check resolvers docker server authelia-backend authelia-backend:9091 check backup resolvers docker ssl verify none diff --git a/internal/suites/scenario_available_methods_test.go b/internal/suites/scenario_available_methods_test.go index 69bba6434..708ab5559 100644 --- a/internal/suites/scenario_available_methods_test.go +++ b/internal/suites/scenario_available_methods_test.go @@ -16,7 +16,7 @@ type AvailableMethodsScenario struct { func NewAvailableMethodsScenario(methods []string) *AvailableMethodsScenario { return &AvailableMethodsScenario{ - RodSuite: new(RodSuite), + RodSuite: NewRodSuite(""), methods: methods, } } diff --git a/internal/suites/scenario_bypass_policy_test.go b/internal/suites/scenario_bypass_policy_test.go index 497b837e6..1c3273258 100644 --- a/internal/suites/scenario_bypass_policy_test.go +++ b/internal/suites/scenario_bypass_policy_test.go @@ -16,7 +16,7 @@ type BypassPolicyScenario struct { func NewBypassPolicyScenario() *BypassPolicyScenario { return &BypassPolicyScenario{ - RodSuite: new(RodSuite), + RodSuite: NewRodSuite(""), } } diff --git a/internal/suites/scenario_custom_headers_test.go b/internal/suites/scenario_custom_headers_test.go index 38e0c7168..355113c55 100644 --- a/internal/suites/scenario_custom_headers_test.go +++ b/internal/suites/scenario_custom_headers_test.go @@ -19,7 +19,7 @@ type CustomHeadersScenario struct { func NewCustomHeadersScenario() *CustomHeadersScenario { return &CustomHeadersScenario{ - RodSuite: new(RodSuite), + RodSuite: NewRodSuite(""), } } diff --git a/internal/suites/scenario_default_redirection_url_test.go b/internal/suites/scenario_default_redirection_url_test.go index 31fca33d0..b6adeee9c 100644 --- a/internal/suites/scenario_default_redirection_url_test.go +++ b/internal/suites/scenario_default_redirection_url_test.go @@ -18,7 +18,7 @@ type DefaultRedirectionURLScenario struct { func NewDefaultRedirectionURLScenario() *DefaultRedirectionURLScenario { return &DefaultRedirectionURLScenario{ - RodSuite: new(RodSuite), + RodSuite: NewRodSuite(""), } } diff --git a/internal/suites/scenario_inactivity_test.go b/internal/suites/scenario_inactivity_test.go index 5f6a4bdfe..0af42ed79 100644 --- a/internal/suites/scenario_inactivity_test.go +++ b/internal/suites/scenario_inactivity_test.go @@ -18,7 +18,7 @@ type InactivityScenario struct { func NewInactivityScenario() *InactivityScenario { return &InactivityScenario{ - RodSuite: new(RodSuite), + RodSuite: NewRodSuite(""), } } diff --git a/internal/suites/scenario_multiple_cookie_domain_test.go b/internal/suites/scenario_multiple_cookie_domain_test.go index 15016ceae..b0d0604a7 100644 --- a/internal/suites/scenario_multiple_cookie_domain_test.go +++ b/internal/suites/scenario_multiple_cookie_domain_test.go @@ -19,7 +19,7 @@ type MultiCookieDomainScenario struct { // NewMultiCookieDomainScenario returns a new Multi Cookie Domain Test Scenario. func NewMultiCookieDomainScenario(domain, nextDomain string, remember bool) *MultiCookieDomainScenario { return &MultiCookieDomainScenario{ - RodSuite: new(RodSuite), + RodSuite: NewRodSuite(""), domain: domain, nextDomain: nextDomain, remember: remember, diff --git a/internal/suites/scenario_oidc_test.go b/internal/suites/scenario_oidc_test.go index a79d9bf59..d145c190d 100644 --- a/internal/suites/scenario_oidc_test.go +++ b/internal/suites/scenario_oidc_test.go @@ -22,7 +22,7 @@ type OIDCScenario struct { func NewOIDCScenario() *OIDCScenario { return &OIDCScenario{ - RodSuite: new(RodSuite), + RodSuite: NewRodSuite(""), } } diff --git a/internal/suites/scenario_one_factor_test.go b/internal/suites/scenario_one_factor_test.go index 6fd6a5523..adef3c575 100644 --- a/internal/suites/scenario_one_factor_test.go +++ b/internal/suites/scenario_one_factor_test.go @@ -16,7 +16,7 @@ type OneFactorSuite struct { func New1FAScenario() *OneFactorSuite { return &OneFactorSuite{ - RodSuite: new(RodSuite), + RodSuite: NewRodSuite(""), } } @@ -49,7 +49,7 @@ func (s *OneFactorSuite) TearDownTest() { } func (s *OneFactorSuite) TestShouldAuthorizeSecretAfterOneFactor() { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer func() { cancel() s.collectScreenshot(ctx.Err(), s.Page) diff --git a/internal/suites/scenario_password_complexity_test.go b/internal/suites/scenario_password_complexity_test.go index d3a0a3b24..5c894986a 100644 --- a/internal/suites/scenario_password_complexity_test.go +++ b/internal/suites/scenario_password_complexity_test.go @@ -14,7 +14,7 @@ type PasswordComplexityScenario struct { } func NewPasswordComplexityScenario() *PasswordComplexityScenario { - return &PasswordComplexityScenario{RodSuite: new(RodSuite)} + return &PasswordComplexityScenario{RodSuite: NewRodSuite("")} } func (s *PasswordComplexityScenario) SetupSuite() { diff --git a/internal/suites/scenario_redirection_check_test.go b/internal/suites/scenario_redirection_check_test.go index fd47004b4..a75465822 100644 --- a/internal/suites/scenario_redirection_check_test.go +++ b/internal/suites/scenario_redirection_check_test.go @@ -15,7 +15,7 @@ type RedirectionCheckScenario struct { func NewRedirectionCheckScenario() *RedirectionCheckScenario { return &RedirectionCheckScenario{ - RodSuite: new(RodSuite), + RodSuite: NewRodSuite(""), } } diff --git a/internal/suites/scenario_redirection_url_test.go b/internal/suites/scenario_redirection_url_test.go index e101287aa..dd67d5b7c 100644 --- a/internal/suites/scenario_redirection_url_test.go +++ b/internal/suites/scenario_redirection_url_test.go @@ -16,7 +16,7 @@ type RedirectionURLScenario struct { func NewRedirectionURLScenario() *RedirectionURLScenario { return &RedirectionURLScenario{ - RodSuite: new(RodSuite), + RodSuite: NewRodSuite(""), } } diff --git a/internal/suites/scenario_regulation_test.go b/internal/suites/scenario_regulation_test.go index 336d6b814..4f8df635e 100644 --- a/internal/suites/scenario_regulation_test.go +++ b/internal/suites/scenario_regulation_test.go @@ -16,7 +16,7 @@ type RegulationScenario struct { func NewRegulationScenario() *RegulationScenario { return &RegulationScenario{ - RodSuite: new(RodSuite), + RodSuite: NewRodSuite(""), } } diff --git a/internal/suites/scenario_reset_password_test.go b/internal/suites/scenario_reset_password_test.go index e5e93a3f6..7c3e0da6b 100644 --- a/internal/suites/scenario_reset_password_test.go +++ b/internal/suites/scenario_reset_password_test.go @@ -14,7 +14,7 @@ type ResetPasswordScenario struct { } func NewResetPasswordScenario() *ResetPasswordScenario { - return &ResetPasswordScenario{RodSuite: new(RodSuite)} + return &ResetPasswordScenario{RodSuite: NewRodSuite("")} } func (s *ResetPasswordScenario) SetupSuite() { diff --git a/internal/suites/scenario_signin_email_test.go b/internal/suites/scenario_signin_email_test.go index 686c8ab3c..9c8b6501a 100644 --- a/internal/suites/scenario_signin_email_test.go +++ b/internal/suites/scenario_signin_email_test.go @@ -18,7 +18,7 @@ type SigninEmailScenario struct { func NewSigninEmailScenario() *SigninEmailScenario { return &SigninEmailScenario{ - RodSuite: new(RodSuite), + RodSuite: NewRodSuite(""), } } diff --git a/internal/suites/scenario_two_factor_test.go b/internal/suites/scenario_two_factor_test.go index 3c10cb100..caf898d09 100644 --- a/internal/suites/scenario_two_factor_test.go +++ b/internal/suites/scenario_two_factor_test.go @@ -18,7 +18,7 @@ type TwoFactorSuite struct { func New2FAScenario() *TwoFactorSuite { return &TwoFactorSuite{ - RodSuite: new(RodSuite), + RodSuite: NewRodSuite(""), } } diff --git a/internal/suites/scenario_user_preferences_test.go b/internal/suites/scenario_user_preferences_test.go index 75db5cd53..133d09ec8 100644 --- a/internal/suites/scenario_user_preferences_test.go +++ b/internal/suites/scenario_user_preferences_test.go @@ -15,7 +15,7 @@ type UserPreferencesScenario struct { func NewUserPreferencesScenario() *UserPreferencesScenario { return &UserPreferencesScenario{ - RodSuite: new(RodSuite), + RodSuite: NewRodSuite(""), } } diff --git a/internal/suites/suite_activedirectory_test.go b/internal/suites/suite_activedirectory_test.go index 9e7bce985..4abb258c4 100644 --- a/internal/suites/suite_activedirectory_test.go +++ b/internal/suites/suite_activedirectory_test.go @@ -11,7 +11,9 @@ type ActiveDirectorySuite struct { } func NewActiveDirectorySuite() *ActiveDirectorySuite { - return &ActiveDirectorySuite{RodSuite: new(RodSuite)} + return &ActiveDirectorySuite{ + RodSuite: NewRodSuite(activedirectorySuiteName), + } } func (s *ActiveDirectorySuite) Test1FAScenario() { diff --git a/internal/suites/suite_bypass_all_test.go b/internal/suites/suite_bypass_all_test.go index 4450d6206..6eaaccba8 100644 --- a/internal/suites/suite_bypass_all_test.go +++ b/internal/suites/suite_bypass_all_test.go @@ -15,10 +15,14 @@ type BypassAllWebDriverSuite struct { } func NewBypassAllWebDriverSuite() *BypassAllWebDriverSuite { - return &BypassAllWebDriverSuite{RodSuite: new(RodSuite)} + return &BypassAllWebDriverSuite{ + RodSuite: NewRodSuite(""), + } } func (s *BypassAllWebDriverSuite) SetupSuite() { + s.BaseSuite.SetupSuite() + browser, err := StartRod() if err != nil { @@ -61,11 +65,15 @@ func (s *BypassAllWebDriverSuite) TestShouldAccessPublicResource() { } type BypassAllSuite struct { - suite.Suite + *BaseSuite } func NewBypassAllSuite() *BypassAllSuite { - return &BypassAllSuite{} + return &BypassAllSuite{ + BaseSuite: &BaseSuite{ + Name: bypassAllSuiteName, + }, + } } func (s *BypassAllSuite) TestBypassAllWebDriverSuite() { diff --git a/internal/suites/suite_caddy_test.go b/internal/suites/suite_caddy_test.go index a366dd220..6df9d0ffd 100644 --- a/internal/suites/suite_caddy_test.go +++ b/internal/suites/suite_caddy_test.go @@ -11,7 +11,9 @@ type CaddySuite struct { } func NewCaddySuite() *CaddySuite { - return &CaddySuite{RodSuite: new(RodSuite)} + return &CaddySuite{ + RodSuite: NewRodSuite(caddySuiteName), + } } func (s *CaddySuite) Test1FAScenario() { diff --git a/internal/suites/suite_cli_test.go b/internal/suites/suite_cli_test.go index 957609804..cb7229f63 100644 --- a/internal/suites/suite_cli_test.go +++ b/internal/suites/suite_cli_test.go @@ -23,10 +23,18 @@ type CLISuite struct { } func NewCLISuite() *CLISuite { - return &CLISuite{CommandSuite: new(CommandSuite)} + return &CLISuite{ + CommandSuite: &CommandSuite{ + BaseSuite: &BaseSuite{ + Name: cliSuiteName, + }, + }, + } } func (s *CLISuite) SetupSuite() { + s.BaseSuite.SetupSuite() + dockerEnvironment := NewDockerEnvironment([]string{ "internal/suites/docker-compose.yml", "internal/suites/CLI/docker-compose.yml", diff --git a/internal/suites/suite_docker_test.go b/internal/suites/suite_docker_test.go index 68df0c6ca..6f6e1c982 100644 --- a/internal/suites/suite_docker_test.go +++ b/internal/suites/suite_docker_test.go @@ -11,7 +11,9 @@ type DockerSuite struct { } func NewDockerSuite() *DockerSuite { - return &DockerSuite{RodSuite: new(RodSuite)} + return &DockerSuite{ + RodSuite: NewRodSuite(dockerSuiteName), + } } func (s *DockerSuite) Test1FAScenario() { diff --git a/internal/suites/suite_duo_push_test.go b/internal/suites/suite_duo_push_test.go index 2958388aa..705a102a7 100644 --- a/internal/suites/suite_duo_push_test.go +++ b/internal/suites/suite_duo_push_test.go @@ -20,7 +20,9 @@ type DuoPushWebDriverSuite struct { } func NewDuoPushWebDriverSuite() *DuoPushWebDriverSuite { - return &DuoPushWebDriverSuite{RodSuite: new(RodSuite)} + return &DuoPushWebDriverSuite{ + RodSuite: NewRodSuite(""), + } } func (s *DuoPushWebDriverSuite) SetupSuite() { @@ -386,10 +388,12 @@ type DuoPushDefaultRedirectionSuite struct { } func NewDuoPushDefaultRedirectionSuite() *DuoPushDefaultRedirectionSuite { - return &DuoPushDefaultRedirectionSuite{RodSuite: new(RodSuite)} + return &DuoPushDefaultRedirectionSuite{RodSuite: NewRodSuite(duoPushSuiteName)} } func (s *DuoPushDefaultRedirectionSuite) SetupSuite() { + s.BaseSuite.SetupSuite() + browser, err := StartRod() if err != nil { @@ -444,11 +448,15 @@ func (s *DuoPushDefaultRedirectionSuite) TestUserIsRedirectedToDefaultURL() { } type DuoPushSuite struct { - suite.Suite + *BaseSuite } func NewDuoPushSuite() *DuoPushSuite { - return &DuoPushSuite{} + return &DuoPushSuite{ + BaseSuite: &BaseSuite{ + Name: duoPushSuiteName, + }, + } } func (s *DuoPushSuite) TestDuoPushWebDriverSuite() { diff --git a/internal/suites/suite_envoy_test.go b/internal/suites/suite_envoy_test.go index ca7cb1149..040a47b9a 100644 --- a/internal/suites/suite_envoy_test.go +++ b/internal/suites/suite_envoy_test.go @@ -11,7 +11,9 @@ type EnvoySuite struct { } func NewEnvoySuite() *EnvoySuite { - return &EnvoySuite{RodSuite: new(RodSuite)} + return &EnvoySuite{ + RodSuite: NewRodSuite(envoySuiteName), + } } func (s *EnvoySuite) Test1FAScenario() { diff --git a/internal/suites/suite_haproxy_test.go b/internal/suites/suite_haproxy_test.go index 96ef115f9..2c935ea7e 100644 --- a/internal/suites/suite_haproxy_test.go +++ b/internal/suites/suite_haproxy_test.go @@ -11,7 +11,9 @@ type HAProxySuite struct { } func NewHAProxySuite() *HAProxySuite { - return &HAProxySuite{RodSuite: new(RodSuite)} + return &HAProxySuite{ + RodSuite: NewRodSuite(haproxySuiteName), + } } func (s *HAProxySuite) Test1FAScenario() { diff --git a/internal/suites/suite_high_availability_test.go b/internal/suites/suite_high_availability_test.go index bbe1b0a7d..d8077d28e 100644 --- a/internal/suites/suite_high_availability_test.go +++ b/internal/suites/suite_high_availability_test.go @@ -17,10 +17,14 @@ type HighAvailabilityWebDriverSuite struct { } func NewHighAvailabilityWebDriverSuite() *HighAvailabilityWebDriverSuite { - return &HighAvailabilityWebDriverSuite{RodSuite: new(RodSuite)} + return &HighAvailabilityWebDriverSuite{ + RodSuite: NewRodSuite(""), + } } func (s *HighAvailabilityWebDriverSuite) SetupSuite() { + s.BaseSuite.SetupSuite() + browser, err := StartRod() if err != nil { @@ -183,7 +187,9 @@ func (s *HighAvailabilityWebDriverSuite) TestShouldKeepSessionAfterAutheliaResta } var UserJohn = "john" + var UserBob = "bob" + var UserHarry = "harry" var Users = []string{UserJohn, UserBob, UserHarry} @@ -263,11 +269,15 @@ func (s *HighAvailabilityWebDriverSuite) TestShouldVerifyAccessControl() { } type HighAvailabilitySuite struct { - suite.Suite + *BaseSuite } func NewHighAvailabilitySuite() *HighAvailabilitySuite { - return &HighAvailabilitySuite{} + return &HighAvailabilitySuite{ + BaseSuite: &BaseSuite{ + Name: highAvailabilitySuiteName, + }, + } } func DoGetWithAuth(t *testing.T, username, password string) int { diff --git a/internal/suites/suite_kubernetes_test.go b/internal/suites/suite_kubernetes_test.go index fe382ee10..c766301fa 100644 --- a/internal/suites/suite_kubernetes_test.go +++ b/internal/suites/suite_kubernetes_test.go @@ -11,7 +11,9 @@ type KubernetesSuite struct { } func NewKubernetesSuite() *KubernetesSuite { - return &KubernetesSuite{RodSuite: new(RodSuite)} + return &KubernetesSuite{ + RodSuite: NewRodSuite(kubernetesSuiteName), + } } func (s *KubernetesSuite) Test1FAScenario() { diff --git a/internal/suites/suite_ldap_test.go b/internal/suites/suite_ldap_test.go index c75627806..382354d3a 100644 --- a/internal/suites/suite_ldap_test.go +++ b/internal/suites/suite_ldap_test.go @@ -11,7 +11,9 @@ type LDAPSuite struct { } func NewLDAPSuite() *LDAPSuite { - return &LDAPSuite{RodSuite: new(RodSuite)} + return &LDAPSuite{ + RodSuite: NewRodSuite(ldapSuiteName), + } } func (s *LDAPSuite) Test1FAScenario() { diff --git a/internal/suites/suite_mariadb_test.go b/internal/suites/suite_mariadb_test.go index 35b71f23b..694c1a2f3 100644 --- a/internal/suites/suite_mariadb_test.go +++ b/internal/suites/suite_mariadb_test.go @@ -11,7 +11,9 @@ type MariaDBSuite struct { } func NewMariaDBSuite() *MariaDBSuite { - return &MariaDBSuite{RodSuite: new(RodSuite)} + return &MariaDBSuite{ + RodSuite: NewRodSuite(mariadbSuiteName), + } } func (s *MariaDBSuite) Test1FAScenario() { diff --git a/internal/suites/suite_multi_cookie_domain_test.go b/internal/suites/suite_multi_cookie_domain_test.go index ff7281c34..4c27e9200 100644 --- a/internal/suites/suite_multi_cookie_domain_test.go +++ b/internal/suites/suite_multi_cookie_domain_test.go @@ -7,11 +7,15 @@ import ( ) func NewMultiCookieDomainSuite() *MultiCookieDomainSuite { - return &MultiCookieDomainSuite{} + return &MultiCookieDomainSuite{ + BaseSuite: &BaseSuite{ + Name: multiCookieDomainSuiteName, + }, + } } type MultiCookieDomainSuite struct { - suite.Suite + *BaseSuite } func (s *MultiCookieDomainSuite) TestMultiCookieDomainFirstDomainScenario() { diff --git a/internal/suites/suite_mysql_test.go b/internal/suites/suite_mysql_test.go index a2e90e01d..4a3618827 100644 --- a/internal/suites/suite_mysql_test.go +++ b/internal/suites/suite_mysql_test.go @@ -11,7 +11,9 @@ type MySQLSuite struct { } func NewMySQLSuite() *MySQLSuite { - return &MySQLSuite{RodSuite: new(RodSuite)} + return &MySQLSuite{ + RodSuite: NewRodSuite(mysqlSuiteName), + } } func (s *MySQLSuite) Test1FAScenario() { diff --git a/internal/suites/suite_network_acl_test.go b/internal/suites/suite_network_acl_test.go index 1b3063d6e..88178a288 100644 --- a/internal/suites/suite_network_acl_test.go +++ b/internal/suites/suite_network_acl_test.go @@ -10,11 +10,15 @@ import ( ) type NetworkACLSuite struct { - suite.Suite + *BaseSuite } func NewNetworkACLSuite() *NetworkACLSuite { - return &NetworkACLSuite{} + return &NetworkACLSuite{ + BaseSuite: &BaseSuite{ + Name: networkACLSuiteName, + }, + } } func (s *NetworkACLSuite) TestShouldAccessSecretUpon2FA() { diff --git a/internal/suites/suite_oidc_test.go b/internal/suites/suite_oidc_test.go index acf224dd8..a3320954e 100644 --- a/internal/suites/suite_oidc_test.go +++ b/internal/suites/suite_oidc_test.go @@ -11,7 +11,9 @@ type OIDCSuite struct { } func NewOIDCSuite() *OIDCSuite { - return &OIDCSuite{RodSuite: new(RodSuite)} + return &OIDCSuite{ + RodSuite: NewRodSuite(oidcSuiteName), + } } func (s *OIDCSuite) TestOIDCScenario() { diff --git a/internal/suites/suite_oidc_traefik_test.go b/internal/suites/suite_oidc_traefik_test.go index eae84c4d6..2e4a14bab 100644 --- a/internal/suites/suite_oidc_traefik_test.go +++ b/internal/suites/suite_oidc_traefik_test.go @@ -11,7 +11,9 @@ type OIDCTraefikSuite struct { } func NewOIDCTraefikSuite() *OIDCTraefikSuite { - return &OIDCTraefikSuite{RodSuite: new(RodSuite)} + return &OIDCTraefikSuite{ + RodSuite: NewRodSuite(oidcTraefikSuiteName), + } } func (s *OIDCTraefikSuite) TestOIDCScenario() { diff --git a/internal/suites/suite_one_factor_only_test.go b/internal/suites/suite_one_factor_only_test.go index f669bc360..688b73bd9 100644 --- a/internal/suites/suite_one_factor_only_test.go +++ b/internal/suites/suite_one_factor_only_test.go @@ -11,18 +11,18 @@ import ( ) type OneFactorOnlySuite struct { - suite.Suite -} - -type OneFactorOnlyWebSuite struct { *RodSuite } -func NewOneFactorOnlyWebSuite() *OneFactorOnlyWebSuite { - return &OneFactorOnlyWebSuite{RodSuite: new(RodSuite)} +func NewOneFactorOnlySuite() *OneFactorOnlySuite { + return &OneFactorOnlySuite{ + RodSuite: NewRodSuite(oneFactorOnlySuiteName), + } } -func (s *OneFactorOnlyWebSuite) SetupSuite() { +func (s *OneFactorOnlySuite) SetupSuite() { + s.BaseSuite.SetupSuite() + browser, err := StartRod() if err != nil { @@ -32,7 +32,7 @@ func (s *OneFactorOnlyWebSuite) SetupSuite() { s.RodSession = browser } -func (s *OneFactorOnlyWebSuite) TearDownSuite() { +func (s *OneFactorOnlySuite) TearDownSuite() { err := s.RodSession.Stop() if err != nil { @@ -40,18 +40,18 @@ func (s *OneFactorOnlyWebSuite) TearDownSuite() { } } -func (s *OneFactorOnlyWebSuite) SetupTest() { +func (s *OneFactorOnlySuite) SetupTest() { s.Page = s.doCreateTab(s.T(), HomeBaseURL) s.verifyIsHome(s.T(), s.Page) } -func (s *OneFactorOnlyWebSuite) TearDownTest() { +func (s *OneFactorOnlySuite) TearDownTest() { s.collectCoverage(s.Page) s.MustClose() } // No target url is provided, then the user should be redirect to the default url. -func (s *OneFactorOnlyWebSuite) TestShouldRedirectUserToDefaultURL() { +func (s *OneFactorOnlySuite) TestShouldRedirectUserToDefaultURL() { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer func() { cancel() @@ -63,7 +63,7 @@ func (s *OneFactorOnlyWebSuite) TestShouldRedirectUserToDefaultURL() { } // Unsafe URL is provided, then the user should be redirect to the default url. -func (s *OneFactorOnlyWebSuite) TestShouldRedirectUserToDefaultURLWhenURLIsUnsafe() { +func (s *OneFactorOnlySuite) TestShouldRedirectUserToDefaultURLWhenURLIsUnsafe() { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer func() { cancel() @@ -75,7 +75,7 @@ func (s *OneFactorOnlyWebSuite) TestShouldRedirectUserToDefaultURLWhenURLIsUnsaf } // When use logged in and visit the portal again, she gets redirect to the authenticated view. -func (s *OneFactorOnlyWebSuite) TestShouldDisplayAuthenticatedView() { +func (s *OneFactorOnlySuite) TestShouldDisplayAuthenticatedView() { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer func() { cancel() @@ -88,7 +88,7 @@ func (s *OneFactorOnlyWebSuite) TestShouldDisplayAuthenticatedView() { s.verifyIsAuthenticatedPage(s.T(), s.Context(ctx)) } -func (s *OneFactorOnlyWebSuite) TestShouldRedirectAlreadyAuthenticatedUser() { +func (s *OneFactorOnlySuite) TestShouldRedirectAlreadyAuthenticatedUser() { ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer func() { cancel() @@ -103,7 +103,7 @@ func (s *OneFactorOnlyWebSuite) TestShouldRedirectAlreadyAuthenticatedUser() { s.verifyURLIs(s.T(), s.Context(ctx), "https://singlefactor.example.com:8080/secret.html") } -func (s *OneFactorOnlyWebSuite) TestShouldNotRedirectAlreadyAuthenticatedUserToUnsafeURL() { +func (s *OneFactorOnlySuite) TestShouldNotRedirectAlreadyAuthenticatedUserToUnsafeURL() { ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer func() { cancel() @@ -118,14 +118,10 @@ func (s *OneFactorOnlyWebSuite) TestShouldNotRedirectAlreadyAuthenticatedUserToU s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Redirection was determined to be unsafe and aborted. Ensure the redirection URL is correct.") } -func (s *OneFactorOnlySuite) TestWeb() { - suite.Run(s.T(), NewOneFactorOnlyWebSuite()) -} - func TestOneFactorOnlySuite(t *testing.T) { if testing.Short() { t.Skip("skipping suite test in short mode") } - suite.Run(t, new(OneFactorOnlySuite)) + suite.Run(t, NewOneFactorOnlySuite()) } diff --git a/internal/suites/suite_pathprefix_test.go b/internal/suites/suite_pathprefix_test.go index 1f310b149..9f2cd5912 100644 --- a/internal/suites/suite_pathprefix_test.go +++ b/internal/suites/suite_pathprefix_test.go @@ -11,7 +11,9 @@ type PathPrefixSuite struct { } func NewPathPrefixSuite() *PathPrefixSuite { - return &PathPrefixSuite{RodSuite: new(RodSuite)} + return &PathPrefixSuite{ + RodSuite: NewRodSuite(pathPrefixSuiteName), + } } func (s *PathPrefixSuite) Test1FAScenario() { diff --git a/internal/suites/suite_postgres_test.go b/internal/suites/suite_postgres_test.go index fb737f8c4..4d3fb1c0e 100644 --- a/internal/suites/suite_postgres_test.go +++ b/internal/suites/suite_postgres_test.go @@ -11,7 +11,9 @@ type PostgresSuite struct { } func NewPostgresSuite() *PostgresSuite { - return &PostgresSuite{RodSuite: new(RodSuite)} + return &PostgresSuite{ + RodSuite: NewRodSuite(postgresSuiteName), + } } func (s *PostgresSuite) Test1FAScenario() { diff --git a/internal/suites/suite_short_timeouts_test.go b/internal/suites/suite_short_timeouts_test.go index f59ebdce8..678657e60 100644 --- a/internal/suites/suite_short_timeouts_test.go +++ b/internal/suites/suite_short_timeouts_test.go @@ -11,7 +11,9 @@ type ShortTimeoutsSuite struct { } func NewShortTimeoutsSuite() *ShortTimeoutsSuite { - return &ShortTimeoutsSuite{RodSuite: new(RodSuite)} + return &ShortTimeoutsSuite{ + RodSuite: NewRodSuite(shortTimeoutsSuiteName), + } } func (s *ShortTimeoutsSuite) TestDefaultRedirectionURLScenario() { diff --git a/internal/suites/suite_standalone_test.go b/internal/suites/suite_standalone_test.go index e126b3dd8..f82acbe41 100644 --- a/internal/suites/suite_standalone_test.go +++ b/internal/suites/suite_standalone_test.go @@ -22,10 +22,14 @@ type StandaloneWebDriverSuite struct { } func NewStandaloneWebDriverSuite() *StandaloneWebDriverSuite { - return &StandaloneWebDriverSuite{RodSuite: new(RodSuite)} + return &StandaloneWebDriverSuite{ + RodSuite: NewRodSuite(""), + } } func (s *StandaloneWebDriverSuite) SetupSuite() { + s.BaseSuite.SetupSuite() + browser, err := StartRod() if err != nil { @@ -169,11 +173,15 @@ func (s *StandaloneWebDriverSuite) TestShouldCheckUserIsAskedToRegisterDevice() } type StandaloneSuite struct { - suite.Suite + *BaseSuite } func NewStandaloneSuite() *StandaloneSuite { - return &StandaloneSuite{} + return &StandaloneSuite{ + BaseSuite: &BaseSuite{ + Name: standaloneSuiteName, + }, + } } func (s *StandaloneSuite) TestShouldRespectMethodsACL() { diff --git a/internal/suites/suite_traefik2_test.go b/internal/suites/suite_traefik2_test.go index 9a057134c..af51ea748 100644 --- a/internal/suites/suite_traefik2_test.go +++ b/internal/suites/suite_traefik2_test.go @@ -14,7 +14,9 @@ type Traefik2Suite struct { } func NewTraefik2Suite() *Traefik2Suite { - return &Traefik2Suite{RodSuite: new(RodSuite)} + return &Traefik2Suite{ + RodSuite: NewRodSuite(traefik2SuiteName), + } } func (s *Traefik2Suite) Test1FAScenario() { diff --git a/internal/suites/suite_traefik_test.go b/internal/suites/suite_traefik_test.go index 195184673..8b183e44f 100644 --- a/internal/suites/suite_traefik_test.go +++ b/internal/suites/suite_traefik_test.go @@ -11,7 +11,9 @@ type TraefikSuite struct { } func NewTraefikSuite() *TraefikSuite { - return &TraefikSuite{RodSuite: new(RodSuite)} + return &TraefikSuite{ + RodSuite: NewRodSuite(traefikSuiteName), + } } func (s *TraefikSuite) Test1FAScenario() { diff --git a/internal/suites/suites.go b/internal/suites/suites.go index 4595cf524..96ebf4633 100644 --- a/internal/suites/suites.go +++ b/internal/suites/suites.go @@ -5,17 +5,31 @@ import ( "github.com/stretchr/testify/suite" ) +func NewRodSuite(name string) *RodSuite { + return &RodSuite{ + BaseSuite: &BaseSuite{ + Name: name, + }, + } +} + // RodSuite is a go-rod suite. type RodSuite struct { - suite.Suite + *BaseSuite *RodSession *rod.Page } +type BaseSuite struct { + suite.Suite + + Name string +} + // CommandSuite is a command line interface suite. type CommandSuite struct { - suite.Suite + *BaseSuite testArg string //nolint:structcheck // TODO: Remove when bug fixed: https://github.com/golangci/golangci-lint/issues/537. coverageArg string //nolint:structcheck // TODO: Remove when bug fixed: https://github.com/golangci/golangci-lint/issues/537. diff --git a/internal/suites/utils.go b/internal/suites/utils.go index df4fb4f18..f6b8e89eb 100644 --- a/internal/suites/utils.go +++ b/internal/suites/utils.go @@ -1,12 +1,12 @@ package suites import ( + "bufio" "context" "crypto/tls" "encoding/json" "fmt" "io" - "log" "net/http" "os" "path/filepath" @@ -16,6 +16,7 @@ import ( "github.com/go-rod/rod" "github.com/google/uuid" + log "github.com/sirupsen/logrus" ) var browserPaths = []string{"/usr/bin/chromium-browser", "/usr/bin/chromium"} @@ -52,8 +53,8 @@ func GetBrowserPath() (path string, err error) { // GetLoginBaseURL returns the URL of the login portal and the path prefix if specified. func GetLoginBaseURL(baseDomain string) string { - if PathPrefix != "" { - return LoginBaseURLFmt(baseDomain) + PathPrefix + if pathPrefix := os.Getenv("PathPrefix"); pathPrefix != "" { + return LoginBaseURLFmt(baseDomain) + pathPrefix } return LoginBaseURLFmt(baseDomain) @@ -84,6 +85,87 @@ func (rs *RodSession) collectCoverage(page *rod.Page) { } } +func (s *BaseSuite) SetupSuite() { + s.SetupLogging() + s.SetupEnvironment() +} + +func (s *BaseSuite) SetupLogging() { + if os.Getenv("SUITE_SETUP_LOGGING") == t { + return + } + + var ( + level string + ok bool + ) + + if level, ok = os.LookupEnv("SUITES_LOG_LEVEL"); !ok { + return + } + + l, err := log.ParseLevel(level) + + s.NoError(err) + + log.SetLevel(l) + + log.SetFormatter(&log.TextFormatter{ + ForceColors: true, + }) + + s.T().Setenv("SUITE_SETUP_LOGGING", t) +} + +func (s *BaseSuite) SetupEnvironment() { + if s.Name == "" || os.Getenv("SUITE_SETUP_ENVIRONMENT") == t { + return + } + + log.Debugf("Checking Suite %s for .env file", s.Name) + + path := filepath.Join(s.Name, ".env") + + var ( + info os.FileInfo + err error + ) + + path, err = filepath.Abs(path) + + s.Require().NoError(err) + + if info, err = os.Stat(path); err != nil { + s.Assert().True(os.IsNotExist(err)) + + log.Debugf("Suite %s does not have an .env file or it can't be read: %v", s.Name, err) + + return + } + + s.Require().False(info.IsDir()) + + log.Debugf("Suite %s does have an .env file at path: %s", s.Name, path) + + var file *os.File + + file, err = os.Open(path) + + s.Require().NoError(err) + + scanner := bufio.NewScanner(file) + + for scanner.Scan() { + v := strings.Split(scanner.Text(), "=") + + s.Require().Len(v, 2) + + s.T().Setenv(v[0], v[1]) + } + + s.T().Setenv("SUITE_SETUP_ENVIRONMENT", t) +} + func (rs *RodSession) collectScreenshot(err error, page *rod.Page) { if err == context.DeadlineExceeded && os.Getenv("CI") == t { base := "/buildkite/screenshots" @@ -140,7 +222,7 @@ func fixCoveragePath(path string, file os.FileInfo, err error) error { } // getEnvInfoFromURL gets environments variables for specified cookie domain -// this func makes a http call to https://login./override and is only useful for suite tests. +// this func makes a http call to https://login./devworkflow and is only useful for suite tests. func getDomainEnvInfo(domain string) (map[string]string, error) { info := make(map[string]string) @@ -186,18 +268,12 @@ func getDomainEnvInfo(domain string) (map[string]string, error) { // generateDevEnvFile generates web/.env.development based on opts. func generateDevEnvFile(opts map[string]string) error { - wd, _ := os.Getwd() - path := strings.TrimSuffix(wd, "internal/suites") - - src := fmt.Sprintf("%s/web/.env.production", path) - dst := fmt.Sprintf("%s/web/.env.development", path) - - tmpl, err := template.ParseFiles(src) + tmpl, err := template.ParseFiles(envFileProd) if err != nil { return err } - file, _ := os.Create(dst) + file, _ := os.Create(envFileDev) defer file.Close() if err := tmpl.Execute(file, opts); err != nil { @@ -210,10 +286,15 @@ func generateDevEnvFile(opts map[string]string) error { // updateDevEnvFileForDomain updates web/.env.development. // this function only affects local dev environments. func updateDevEnvFileForDomain(domain string, setup bool) error { - if os.Getenv("CI") == "true" { + if os.Getenv("CI") == t { return nil } + if _, err := os.Stat(envFileDev); err != nil && os.IsNotExist(err) { + file, _ := os.Create(envFileDev) + file.Close() + } + info, err := getDomainEnvInfo(domain) if err != nil { return err diff --git a/internal/utils/crypto_test.go b/internal/utils/crypto_test.go index dcc065721..dc7889996 100644 --- a/internal/utils/crypto_test.go +++ b/internal/utils/crypto_test.go @@ -36,7 +36,9 @@ func TestShouldReturnErrWhenX509DirectoryNotExist(t *testing.T) { } func TestShouldNotReturnErrWhenX509DirectoryExist(t *testing.T) { - pool, warnings, errors := NewX509CertPool("/tmp") + dir := t.TempDir() + + pool, warnings, errors := NewX509CertPool(dir) assert.NotNil(t, pool) if runtime.GOOS == windows {