2019-09-02 14:29:19 +03:00
defmodule PlausibleWeb.AuthControllerTest do
2022-12-26 16:20:29 +03:00
use PlausibleWeb.ConnCase , async : true
2019-09-02 14:29:19 +03:00
use Bamboo.Test
2020-12-15 12:30:45 +03:00
use Plausible.Repo
2019-09-02 14:29:19 +03:00
2023-10-10 20:35:17 +03:00
import Plausible.Test.Support.HTML
2022-10-17 14:16:59 +03:00
import Mox
2023-09-25 11:27:29 +03:00
2023-10-16 14:21:18 +03:00
require Logger
2023-10-10 20:35:17 +03:00
require Plausible.Billing.Subscription.Status
2023-10-16 14:21:18 +03:00
alias Plausible.Auth
2023-09-25 11:27:29 +03:00
alias Plausible.Auth.User
2023-10-10 20:35:17 +03:00
alias Plausible.Billing.Subscription
2023-09-25 11:27:29 +03:00
2022-10-17 14:16:59 +03:00
setup :verify_on_exit!
2023-10-10 20:35:17 +03:00
@v3_plan_id " 749355 "
2023-11-16 18:40:50 +03:00
@v4_plan_id " 857097 "
2023-10-10 20:35:17 +03:00
@configured_enterprise_plan_paddle_plan_id " 123 "
2019-09-02 14:29:19 +03:00
describe " GET /register " do
test " shows the register form " , %{ conn : conn } do
conn = get ( conn , " /register " )
2020-03-23 12:34:25 +03:00
assert html_response ( conn , 200 ) =~ " Enter your details "
2019-09-02 14:29:19 +03:00
end
2020-12-15 12:30:45 +03:00
end
2019-09-02 14:29:19 +03:00
2020-12-15 12:30:45 +03:00
describe " POST /register " do
2022-12-26 16:20:29 +03:00
test " registering sends an activation link " , %{ conn : conn } do
2023-09-25 11:27:29 +03:00
Repo . insert! (
User . new ( %{
name : " Jane Doe " ,
email : " user@example.com " ,
password : " very-secret-and-very-long-123 " ,
password_confirmation : " very-secret-and-very-long-123 "
} )
)
2020-06-08 10:35:13 +03:00
post ( conn , " /register " ,
user : %{
2020-12-15 12:30:45 +03:00
email : " user@example.com " ,
2023-09-25 11:27:29 +03:00
password : " very-secret-and-very-long-123 "
2020-12-15 12:30:45 +03:00
}
)
assert_delivered_email_matches ( %{ to : [ { _ , user_email } ] , subject : subject } )
assert user_email == " user@example.com "
assert subject =~ " is your Plausible email verification code "
end
2021-10-26 11:59:14 +03:00
test " user is redirected to activate page after registration " , %{ conn : conn } do
2023-09-25 11:27:29 +03:00
Repo . insert! (
User . new ( %{
name : " Jane Doe " ,
email : " user@example.com " ,
password : " very-secret-and-very-long-123 " ,
password_confirmation : " very-secret-and-very-long-123 "
} )
)
2021-10-26 11:59:14 +03:00
conn =
post ( conn , " /register " ,
user : %{
email : " user@example.com " ,
2023-09-25 11:27:29 +03:00
password : " very-secret-and-very-long-123 "
2021-10-26 11:59:14 +03:00
}
)
assert redirected_to ( conn , 302 ) == " /activate "
end
2023-09-25 11:27:29 +03:00
test " logs the user in " , %{ conn : conn } do
Repo . insert! (
User . new ( %{
2020-12-15 12:30:45 +03:00
name : " Jane Doe " ,
email : " user@example.com " ,
2023-09-25 11:27:29 +03:00
password : " very-secret-and-very-long-123 " ,
password_confirmation : " very-secret-and-very-long-123 "
} )
2020-06-08 10:35:13 +03:00
)
2019-09-02 14:29:19 +03:00
2020-12-15 12:30:45 +03:00
conn =
post ( conn , " /register " ,
user : %{
email : " user@example.com " ,
2023-09-25 11:27:29 +03:00
password : " very-secret-and-very-long-123 "
2020-12-15 12:30:45 +03:00
}
)
assert get_session ( conn , :current_user_id )
2019-09-02 14:29:19 +03:00
end
2021-09-08 15:15:37 +03:00
end
describe " GET /register/invitations/:invitation_id " do
test " shows the register form " , %{ conn : conn } do
inviter = insert ( :user )
site = insert ( :site , members : [ inviter ] )
invitation =
insert ( :invitation ,
site_id : site . id ,
inviter : inviter ,
email : " user@email.co " ,
role : :admin
)
conn = get ( conn , " /register/invitation/ #{ invitation . invitation_id } " )
assert html_response ( conn , 200 ) =~ " Enter your details "
end
end
describe " POST /register/invitation/:invitation_id " do
setup do
inviter = insert ( :user )
site = insert ( :site , members : [ inviter ] )
invitation =
insert ( :invitation ,
site_id : site . id ,
inviter : inviter ,
email : " user@email.co " ,
role : :admin
)
2023-09-25 11:27:29 +03:00
Repo . insert! (
User . new ( %{
name : " Jane Doe " ,
email : " user@example.com " ,
password : " very-secret-and-very-long-123 " ,
password_confirmation : " very-secret-and-very-long-123 "
} )
)
2021-09-08 15:15:37 +03:00
{ :ok , %{ site : site , invitation : invitation } }
end
test " registering sends an activation link " , %{ conn : conn , invitation : invitation } do
post ( conn , " /register/invitation/ #{ invitation . invitation_id } " ,
user : %{
name : " Jane Doe " ,
email : " user@example.com " ,
2023-09-25 11:27:29 +03:00
password : " very-secret-and-very-long-123 " ,
password_confirmation : " very-secret-and-very-long-123 "
2021-09-08 15:15:37 +03:00
}
)
assert_delivered_email_matches ( %{ to : [ { _ , user_email } ] , subject : subject } )
assert user_email == " user@example.com "
assert subject =~ " is your Plausible email verification code "
end
2021-10-26 11:59:14 +03:00
test " user is redirected to activate page after registration " , %{
conn : conn ,
invitation : invitation
} do
conn =
post ( conn , " /register/invitation/ #{ invitation . invitation_id } " ,
user : %{
name : " Jane Doe " ,
email : " user@example.com " ,
2023-09-25 11:27:29 +03:00
password : " very-secret-and-very-long-123 " ,
password_confirmation : " very-secret-and-very-long-123 "
2021-10-26 11:59:14 +03:00
}
)
assert redirected_to ( conn , 302 ) == " /activate "
end
2021-09-08 15:15:37 +03:00
test " logs the user in " , %{ conn : conn , invitation : invitation } do
conn =
post ( conn , " /register/invitation/ #{ invitation . invitation_id } " ,
user : %{
name : " Jane Doe " ,
email : " user@example.com " ,
2023-09-25 11:27:29 +03:00
password : " very-secret-and-very-long-123 " ,
password_confirmation : " very-secret-and-very-long-123 "
2021-09-08 15:15:37 +03:00
}
)
assert get_session ( conn , :current_user_id )
end
2020-12-15 12:30:45 +03:00
end
describe " GET /activate " do
setup [ :create_user , :log_in ]
test " if user does not have a code: prompts user to request activation code " , %{ conn : conn } do
conn = get ( conn , " /activate " )
assert html_response ( conn , 200 ) =~ " Request activation code "
end
2020-12-29 16:17:27 +03:00
test " if user does have a code: prompts user to enter the activation code from their email " ,
%{ conn : conn } do
conn =
post ( conn , " /activate/request-code " )
|> get ( " /activate " )
2020-12-15 12:30:45 +03:00
assert html_response ( conn , 200 ) =~ " Please enter the 4-digit code we sent to "
end
end
describe " POST /activate/request-code " do
setup [ :create_user , :log_in ]
2023-10-16 14:21:18 +03:00
test " generates an activation pin for user account " , %{ conn : conn , user : user } do
2020-12-15 12:30:45 +03:00
post ( conn , " /activate/request-code " )
2023-10-16 14:21:18 +03:00
assert code = Repo . get_by ( Auth.EmailActivationCode , user_id : user . id )
assert code . user_id == user . id
refute Plausible.Auth.EmailVerification . expired? ( code )
end
test " regenerates an activation pin even if there's one already " , %{ conn : conn , user : user } do
five_minutes_ago =
NaiveDateTime . utc_now ( )
|> Timex . shift ( minutes : - 5 )
|> NaiveDateTime . truncate ( :second )
{ :ok , verification } = Auth.EmailVerification . issue_code ( user , five_minutes_ago )
post ( conn , " /activate/request-code " )
assert new_verification = Repo . get_by ( Auth.EmailActivationCode , user_id : user . id )
assert verification . id == new_verification . id
assert verification . user_id == new_verification . user_id
# this actually has a chance to fail 1 in 8999 runs
# but at the same time it's good to have a confirmation
# that it indeed generates a new code
if verification . code == new_verification . code do
2023-10-24 11:33:48 +03:00
Logger . warning (
2023-10-16 14:21:18 +03:00
" Congratulations! You you have hit 1 in 8999 chance of the same " <>
" email verification code repeating twice in a row! "
2020-12-29 16:17:27 +03:00
)
2023-10-16 14:21:18 +03:00
end
2020-12-15 12:30:45 +03:00
2023-10-16 14:21:18 +03:00
assert NaiveDateTime . compare ( verification . issued_at , new_verification . issued_at ) == :lt
2020-12-15 12:30:45 +03:00
end
test " sends activation email to user " , %{ conn : conn , user : user } do
post ( conn , " /activate/request-code " )
assert_delivered_email_matches ( %{ to : [ { _ , user_email } ] , subject : subject } )
assert user_email == user . email
assert subject =~ " is your Plausible email verification code "
2019-09-02 14:29:19 +03:00
end
2021-10-26 11:59:14 +03:00
2023-10-16 14:21:18 +03:00
test " redirects user to /activate " , %{ conn : conn } do
2021-10-26 11:59:14 +03:00
conn = post ( conn , " /activate/request-code " )
assert redirected_to ( conn , 302 ) == " /activate "
end
2019-09-02 14:29:19 +03:00
end
2020-12-15 12:30:45 +03:00
describe " POST /activate " do
setup [ :create_user , :log_in ]
2019-09-02 14:29:19 +03:00
2020-12-15 12:30:45 +03:00
test " with wrong pin - reloads the form with error " , %{ conn : conn } do
conn = post ( conn , " /activate " , %{ code : " 1234 " } )
assert html_response ( conn , 200 ) =~ " Incorrect activation code "
2019-09-02 14:29:19 +03:00
end
2020-12-15 12:30:45 +03:00
test " with expired pin - reloads the form with error " , %{ conn : conn , user : user } do
2023-10-16 14:21:18 +03:00
one_day_ago =
NaiveDateTime . utc_now ( )
|> Timex . shift ( days : - 1 )
|> NaiveDateTime . truncate ( :second )
2020-12-15 12:30:45 +03:00
2023-10-16 14:21:18 +03:00
{ :ok , verification } = Auth.EmailVerification . issue_code ( user , one_day_ago )
conn = post ( conn , " /activate " , %{ code : verification . code } )
2020-03-23 12:34:25 +03:00
2020-12-15 12:30:45 +03:00
assert html_response ( conn , 200 ) =~ " Code is expired, please request another one "
2020-03-23 12:34:25 +03:00
end
2020-12-15 12:30:45 +03:00
test " marks the user account as active " , %{ conn : conn , user : user } do
Repo . update! ( Plausible.Auth.User . changeset ( user , %{ email_verified : false } ) )
post ( conn , " /activate/request-code " )
2023-10-16 14:21:18 +03:00
verification = Repo . get_by! ( Auth.EmailActivationCode , user_id : user . id )
2019-09-02 14:29:19 +03:00
2023-10-16 14:21:18 +03:00
conn = post ( conn , " /activate " , %{ code : verification . code } )
2020-12-15 12:30:45 +03:00
user = Repo . get_by ( Plausible.Auth.User , id : user . id )
assert user . email_verified
assert redirected_to ( conn ) == " /sites/new "
2019-09-02 14:29:19 +03:00
end
2021-10-26 11:59:14 +03:00
test " redirects to /sites if user has invitation " , %{ conn : conn , user : user } do
site = insert ( :site )
insert ( :invitation , inviter : build ( :user ) , site : site , email : user . email )
Repo . update! ( Plausible.Auth.User . changeset ( user , %{ email_verified : false } ) )
post ( conn , " /activate/request-code " )
2023-10-16 14:21:18 +03:00
verification = Repo . get_by! ( Auth.EmailActivationCode , user_id : user . id )
2021-10-26 11:59:14 +03:00
2023-10-16 14:21:18 +03:00
conn = post ( conn , " /activate " , %{ code : verification . code } )
2021-10-26 11:59:14 +03:00
assert redirected_to ( conn ) == " /sites "
end
2023-10-16 14:21:18 +03:00
test " removes used up verification code " , %{ conn : conn , user : user } do
2020-12-15 12:30:45 +03:00
Repo . update! ( Plausible.Auth.User . changeset ( user , %{ email_verified : false } ) )
post ( conn , " /activate/request-code " )
2023-10-16 14:21:18 +03:00
verification = Repo . get_by! ( Auth.EmailActivationCode , user_id : user . id )
2019-09-02 14:29:19 +03:00
2023-10-16 14:21:18 +03:00
post ( conn , " /activate " , %{ code : verification . code } )
2019-09-02 14:29:19 +03:00
2023-10-16 14:21:18 +03:00
refute Repo . get_by ( Auth.EmailActivationCode , user_id : user . id )
2019-09-02 14:29:19 +03:00
end
end
describe " GET /login_form " do
test " shows the login form " , %{ conn : conn } do
conn = get ( conn , " /login " )
assert html_response ( conn , 200 ) =~ " Enter your email and password "
end
end
describe " POST /login " do
test " valid email and password - logs the user in " , %{ conn : conn } do
user = insert ( :user , password : " password " )
conn = post ( conn , " /login " , email : user . email , password : " password " )
assert get_session ( conn , :current_user_id ) == user . id
2020-04-01 10:37:30 +03:00
assert redirected_to ( conn ) == " /sites "
2019-09-02 14:29:19 +03:00
end
test " email does not exist - renders login form again " , %{ conn : conn } do
conn = post ( conn , " /login " , email : " user@example.com " , password : " password " )
assert get_session ( conn , :current_user_id ) == nil
assert html_response ( conn , 200 ) =~ " Enter your email and password "
end
test " bad password - renders login form again " , %{ conn : conn } do
user = insert ( :user , password : " password " )
conn = post ( conn , " /login " , email : user . email , password : " wrong " )
assert get_session ( conn , :current_user_id ) == nil
assert html_response ( conn , 200 ) =~ " Enter your email and password "
end
2021-05-25 11:32:54 +03:00
test " limits login attempts to 5 per minute " do
user = insert ( :user , password : " password " )
build_conn ( )
|> put_req_header ( " x-forwarded-for " , " 1.1.1.1 " )
|> post ( " /login " , email : user . email , password : " wrong " )
build_conn ( )
|> put_req_header ( " x-forwarded-for " , " 1.1.1.1 " )
|> post ( " /login " , email : user . email , password : " wrong " )
build_conn ( )
|> put_req_header ( " x-forwarded-for " , " 1.1.1.1 " )
|> post ( " /login " , email : user . email , password : " wrong " )
build_conn ( )
|> put_req_header ( " x-forwarded-for " , " 1.1.1.1 " )
|> post ( " /login " , email : user . email , password : " wrong " )
build_conn ( )
|> put_req_header ( " x-forwarded-for " , " 1.1.1.1 " )
|> post ( " /login " , email : user . email , password : " wrong " )
conn =
build_conn ( )
|> put_req_header ( " x-forwarded-for " , " 1.1.1.1 " )
|> post ( " /login " , email : user . email , password : " wrong " )
assert get_session ( conn , :current_user_id ) == nil
assert html_response ( conn , 429 ) =~ " Too many login attempts "
end
2019-09-02 14:29:19 +03:00
end
describe " GET /password/request-reset " do
test " renders the form " , %{ conn : conn } do
conn = get ( conn , " /password/request-reset " )
assert html_response ( conn , 200 ) =~ " Enter your email so we can send a password reset link "
end
end
describe " POST /password/request-reset " do
test " email is empty - renders form with error " , %{ conn : conn } do
conn = post ( conn , " /password/request-reset " , %{ email : " " } )
assert html_response ( conn , 200 ) =~ " Enter your email so we can send a password reset link "
end
test " email is present and exists - sends password reset email " , %{ conn : conn } do
2022-10-17 14:16:59 +03:00
mock_captcha_success ( )
2019-09-02 14:29:19 +03:00
user = insert ( :user )
conn = post ( conn , " /password/request-reset " , %{ email : user . email } )
assert html_response ( conn , 200 ) =~ " Success! "
assert_email_delivered_with ( subject : " Plausible password reset " )
end
2022-10-17 14:16:59 +03:00
test " renders captcha errors in case of captcha input verification failure " , %{ conn : conn } do
mock_captcha_failure ( )
user = insert ( :user )
conn = post ( conn , " /password/request-reset " , %{ email : user . email } )
assert html_response ( conn , 200 ) =~ " Please complete the captcha "
end
2019-09-02 14:29:19 +03:00
end
describe " GET /password/reset " do
test " with valid token - shows form " , %{ conn : conn } do
2023-09-25 11:27:29 +03:00
user = insert ( :user )
token = Plausible.Auth.Token . sign_password_reset ( user . email )
2019-09-02 14:29:19 +03:00
conn = get ( conn , " /password/reset " , %{ token : token } )
assert html_response ( conn , 200 ) =~ " Reset your password "
end
test " with invalid token - shows error page " , %{ conn : conn } do
conn = get ( conn , " /password/reset " , %{ token : " blabla " } )
assert html_response ( conn , 401 ) =~ " Your token is invalid "
end
2023-10-02 16:11:59 +03:00
test " without token - shows error page " , %{ conn : conn } do
conn = get ( conn , " /password/reset " , %{ } )
assert html_response ( conn , 401 ) =~ " Your token is invalid "
end
2019-09-02 14:29:19 +03:00
end
describe " POST /password/reset " do
2023-09-25 11:27:29 +03:00
test " redirects the user to login and shows success message " , %{ conn : conn } do
2023-10-02 16:11:59 +03:00
conn = post ( conn , " /password/reset " , %{ } )
2021-10-26 11:59:14 +03:00
2023-05-09 11:51:35 +03:00
assert location = " /login " = redirected_to ( conn , 302 )
conn = get ( recycle ( conn ) , location )
assert html_response ( conn , 200 ) =~ " Password updated successfully "
2021-10-26 11:59:14 +03:00
end
2019-09-02 14:29:19 +03:00
end
describe " GET /settings " do
setup [ :create_user , :log_in ]
test " shows the form " , %{ conn : conn } do
conn = get ( conn , " /settings " )
2023-10-11 11:25:00 +03:00
assert resp = html_response ( conn , 200 )
assert resp =~ " Change account name "
assert resp =~ " Change email address "
2019-09-02 14:29:19 +03:00
end
2020-08-18 14:00:02 +03:00
test " shows subscription " , %{ conn : conn , user : user } do
insert ( :subscription , paddle_plan_id : " 558018 " , user : user )
conn = get ( conn , " /settings " )
assert html_response ( conn , 200 ) =~ " 10k pageviews "
assert html_response ( conn , 200 ) =~ " monthly billing "
end
2021-05-06 11:46:22 +03:00
test " shows yearly subscription " , %{ conn : conn , user : user } do
insert ( :subscription , paddle_plan_id : " 590752 " , user : user )
conn = get ( conn , " /settings " )
assert html_response ( conn , 200 ) =~ " 100k pageviews "
assert html_response ( conn , 200 ) =~ " yearly billing "
end
test " shows free subscription " , %{ conn : conn , user : user } do
insert ( :subscription , paddle_plan_id : " free_10k " , user : user )
conn = get ( conn , " /settings " )
assert html_response ( conn , 200 ) =~ " 10k pageviews "
assert html_response ( conn , 200 ) =~ " N/A billing "
end
2021-12-09 16:49:57 +03:00
2022-03-29 13:38:35 +03:00
test " shows enterprise plan subscription " , %{ conn : conn , user : user } do
insert ( :subscription , paddle_plan_id : " 123 " , user : user )
2023-10-10 20:35:17 +03:00
configure_enterprise_plan ( user )
conn = get ( conn , " /settings " )
assert html_response ( conn , 200 ) =~ " 20M pageviews "
assert html_response ( conn , 200 ) =~ " yearly billing "
end
test " shows current enterprise plan subscription when user has a new one to upgrade to " , %{
conn : conn ,
user : user
} do
2023-10-24 11:33:48 +03:00
insert ( :subscription ,
paddle_plan_id : @configured_enterprise_plan_paddle_plan_id ,
user : user
)
2023-10-10 20:35:17 +03:00
2022-03-29 13:38:35 +03:00
insert ( :enterprise_plan ,
2023-10-10 20:35:17 +03:00
paddle_plan_id : " 1234 " ,
2022-03-29 13:38:35 +03:00
user : user ,
monthly_pageview_limit : 10_000_000 ,
billing_interval : :yearly
)
2023-10-10 20:35:17 +03:00
configure_enterprise_plan ( user )
2022-03-29 13:38:35 +03:00
conn = get ( conn , " /settings " )
2023-10-10 20:35:17 +03:00
assert html_response ( conn , 200 ) =~ " 20M pageviews "
2022-03-29 13:38:35 +03:00
assert html_response ( conn , 200 ) =~ " yearly billing "
end
2023-11-08 16:24:30 +03:00
test " renders two links to '/billing/choose-plan` with the text 'Upgrade' " , %{ conn : conn } do
2023-10-10 20:35:17 +03:00
doc =
get ( conn , " /settings " )
|> html_response ( 200 )
upgrade_link_1 = find ( doc , " # monthly-quota-box a " )
upgrade_link_2 = find ( doc , " # upgrade-link-2 " )
assert text ( upgrade_link_1 ) == " Upgrade "
assert text_of_attr ( upgrade_link_1 , " href " ) == Routes . billing_path ( conn , :choose_plan )
assert text ( upgrade_link_2 ) == " Upgrade "
assert text_of_attr ( upgrade_link_2 , " href " ) == Routes . billing_path ( conn , :choose_plan )
end
2023-11-08 16:24:30 +03:00
test " renders a link to '/billing/choose-plan' with the text 'Change plan' + cancel link " , %{
2022-03-29 13:38:35 +03:00
conn : conn ,
user : user
} do
2023-10-10 20:35:17 +03:00
insert ( :subscription , paddle_plan_id : @v3_plan_id , user : user )
2022-03-29 13:38:35 +03:00
2023-10-10 20:35:17 +03:00
doc =
get ( conn , " /settings " )
|> html_response ( 200 )
refute element_exists? ( doc , " # upgrade-link-2 " )
assert doc =~ " Cancel my subscription "
change_plan_link = find ( doc , " # monthly-quota-box a " )
assert text ( change_plan_link ) == " Change plan "
assert text_of_attr ( change_plan_link , " href " ) == Routes . billing_path ( conn , :choose_plan )
end
2023-11-08 16:24:30 +03:00
test " /billing/choose-plan link does not show up when enterprise subscription is past_due " , %{
2023-10-10 20:35:17 +03:00
conn : conn ,
user : user
} do
configure_enterprise_plan ( user )
insert ( :subscription ,
2022-03-29 13:38:35 +03:00
user : user ,
2023-10-10 20:35:17 +03:00
status : Subscription.Status . past_due ( ) ,
paddle_plan_id : @configured_enterprise_plan_paddle_plan_id
2022-03-29 13:38:35 +03:00
)
2023-10-10 20:35:17 +03:00
doc =
conn
|> get ( Routes . auth_path ( conn , :user_settings ) )
|> html_response ( 200 )
refute element_exists? ( doc , " # upgrade-or-change-plan-link " )
end
2023-11-08 16:24:30 +03:00
test " /billing/choose-plan link does not show up when enterprise subscription is paused " , %{
2023-10-10 20:35:17 +03:00
conn : conn ,
user : user
} do
configure_enterprise_plan ( user )
insert ( :subscription ,
2022-03-29 13:38:35 +03:00
user : user ,
2023-10-10 20:35:17 +03:00
status : Subscription.Status . paused ( ) ,
paddle_plan_id : @configured_enterprise_plan_paddle_plan_id
2022-03-29 13:38:35 +03:00
)
2023-10-10 20:35:17 +03:00
doc =
conn
|> get ( Routes . auth_path ( conn , :user_settings ) )
|> html_response ( 200 )
refute element_exists? ( doc , " # upgrade-or-change-plan-link " )
end
2023-11-08 16:24:30 +03:00
test " renders two links to '/billing/choose-plan' with the text 'Upgrade' for a configured enterprise plan " ,
2023-10-10 20:35:17 +03:00
%{ conn : conn , user : user } do
configure_enterprise_plan ( user )
doc =
get ( conn , " /settings " )
|> html_response ( 200 )
upgrade_link_1 = find ( doc , " # monthly-quota-box a " )
upgrade_link_2 = find ( doc , " # upgrade-link-2 " )
assert text ( upgrade_link_1 ) == " Upgrade "
assert text_of_attr ( upgrade_link_1 , " href " ) ==
2023-11-08 16:24:30 +03:00
Routes . billing_path ( conn , :choose_plan )
2023-10-10 20:35:17 +03:00
assert text ( upgrade_link_2 ) == " Upgrade "
assert text_of_attr ( upgrade_link_2 , " href " ) ==
2023-11-08 16:24:30 +03:00
Routes . billing_path ( conn , :choose_plan )
2023-10-10 20:35:17 +03:00
end
2023-11-08 16:24:30 +03:00
test " links to '/billing/choose-plan' with the text 'Change plan' for a configured enterprise plan with an existing subscription + renders cancel button " ,
2023-10-10 20:35:17 +03:00
%{ conn : conn , user : user } do
insert ( :subscription , paddle_plan_id : @v3_plan_id , user : user )
configure_enterprise_plan ( user )
doc =
get ( conn , " /settings " )
|> html_response ( 200 )
refute element_exists? ( doc , " # upgrade-link-2 " )
assert doc =~ " Cancel my subscription "
change_plan_link = find ( doc , " # monthly-quota-box a " )
assert text ( change_plan_link ) == " Change plan "
assert text_of_attr ( change_plan_link , " href " ) ==
2023-11-08 16:24:30 +03:00
Routes . billing_path ( conn , :choose_plan )
2022-03-29 13:38:35 +03:00
end
2023-11-16 18:40:50 +03:00
test " renders cancelled subscription notice " , %{ conn : conn , user : user } do
insert ( :subscription ,
paddle_plan_id : @v4_plan_id ,
user : user ,
status : :deleted ,
next_bill_date : ~D[ 2023-01-01 ]
)
notice_text =
get ( conn , " /settings " )
|> html_response ( 200 )
|> text_of_element ( " # global-subscription-cancelled-notice " )
assert notice_text =~ " Subscription cancelled "
assert notice_text =~ " Upgrade your subscription to get access to your stats again "
end
test " renders cancelled subscription notice with some subscription days still left " , %{
conn : conn ,
user : user
} do
insert ( :subscription ,
paddle_plan_id : @v4_plan_id ,
user : user ,
status : :deleted ,
next_bill_date : Timex . shift ( Timex . today ( ) , days : 10 )
)
notice_text =
get ( conn , " /settings " )
|> html_response ( 200 )
|> text_of_element ( " # global-subscription-cancelled-notice " )
assert notice_text =~ " Subscription cancelled "
assert notice_text =~ " You have access to your stats until "
assert notice_text =~ " Upgrade your subscription to make sure you don't lose access "
end
test " renders cancelled subscription notice with a warning about losing grandfathering " , %{
conn : conn ,
user : user
} do
insert ( :subscription ,
paddle_plan_id : @v3_plan_id ,
user : user ,
status : :deleted ,
next_bill_date : Timex . shift ( Timex . today ( ) , days : 10 )
)
notice_text =
get ( conn , " /settings " )
|> html_response ( 200 )
|> text_of_element ( " # global-subscription-cancelled-notice " )
assert notice_text =~ " Subscription cancelled "
assert notice_text =~ " You have access to your stats until "
assert notice_text =~
" by letting your subscription expire, you lose access to our grandfathered terms "
end
2021-12-09 16:49:57 +03:00
test " shows invoices for subscribed user " , %{ conn : conn , user : user } do
insert ( :subscription ,
paddle_plan_id : " 558018 " ,
paddle_subscription_id : " redundant " ,
user : user
)
conn = get ( conn , " /settings " )
assert html_response ( conn , 200 ) =~ " Dec 24, 2020 "
assert html_response ( conn , 200 ) =~ " €11.11 "
assert html_response ( conn , 200 ) =~ " Nov 24, 2020 "
assert html_response ( conn , 200 ) =~ " $22.00 "
end
test " shows 'something went wrong' on failed invoice request' " , %{ conn : conn , user : user } do
insert ( :subscription ,
paddle_plan_id : " 558018 " ,
paddle_subscription_id : " invalid_subscription_id " ,
user : user
)
conn = get ( conn , " /settings " )
assert html_response ( conn , 200 ) =~ " Invoices "
assert html_response ( conn , 200 ) =~ " Something went wrong "
end
test " does not show invoice section for a user with no subscription " , %{ conn : conn } do
conn = get ( conn , " /settings " )
2022-09-22 23:25:24 +03:00
refute html_response ( conn , 200 ) =~ " Invoices "
end
test " does not show invoice section for a free subscription " , %{ conn : conn , user : user } do
Plausible.Billing.Subscription . free ( %{ user_id : user . id , currency_code : " EUR " } )
|> Repo . insert! ( )
conn = get ( conn , " /settings " )
refute html_response ( conn , 200 ) =~ " Invoices "
2021-12-09 16:49:57 +03:00
end
2019-09-02 14:29:19 +03:00
end
2020-04-14 14:04:35 +03:00
describe " PUT /settings " do
setup [ :create_user , :log_in ]
test " updates user record " , %{ conn : conn , user : user } do
2020-06-08 10:35:13 +03:00
put ( conn , " /settings " , %{ " user " = > %{ " name " = > " New name " } } )
2020-04-14 14:04:35 +03:00
user = Plausible.Repo . get ( Plausible.Auth.User , user . id )
assert user . name == " New name "
end
2021-10-26 11:59:14 +03:00
2023-09-28 12:44:39 +03:00
test " does not allow setting non-profile fields " , %{ conn : conn , user : user } do
expiry_date = user . trial_expiry_date
assert % Date { } = expiry_date
put ( conn , " /settings " , %{
" user " = > %{ " name " = > " New name " , " trial_expiry_date " = > " 2023-07-14 " }
} )
assert Repo . reload! ( user ) . trial_expiry_date == expiry_date
end
2021-10-26 11:59:14 +03:00
test " redirects user to /settings " , %{ conn : conn } do
conn = put ( conn , " /settings " , %{ " user " = > %{ " name " = > " New name " } } )
assert redirected_to ( conn , 302 ) == " /settings "
end
2022-09-28 14:56:07 +03:00
2022-10-11 15:42:14 +03:00
test " renders form with error if form validations fail " , %{ conn : conn } do
2022-09-28 14:56:07 +03:00
conn = put ( conn , " /settings " , %{ " user " = > %{ " name " = > " " } } )
assert html_response ( conn , 200 ) =~ " can& # 39;t be blank "
end
2020-04-14 14:04:35 +03:00
end
2023-10-11 11:25:00 +03:00
describe " PUT /settings/email " do
setup [ :create_user , :log_in ]
test " updates email and forces reverification " , %{ conn : conn , user : user } do
password = " very-long-very-secret-123 "
user
|> User . set_password ( password )
|> Repo . update! ( )
assert user . email_verified
conn =
put ( conn , " /settings/email " , %{
" user " = > %{ " email " = > " new " <> user . email , " password " = > password }
} )
assert redirected_to ( conn , 302 ) == Routes . auth_path ( conn , :activate )
updated_user = Repo . reload! ( user )
assert updated_user . email == " new " <> user . email
assert updated_user . previous_email == user . email
refute updated_user . email_verified
assert_delivered_email_matches ( %{ to : [ { _ , user_email } ] , subject : subject } )
assert user_email == updated_user . email
assert subject =~ " is your Plausible email verification code "
end
test " renders form with error on no fields filled " , %{ conn : conn } do
conn = put ( conn , " /settings/email " , %{ " user " = > %{ } } )
assert html_response ( conn , 200 ) =~ " can& # 39;t be blank "
end
test " renders form with error on invalid password " , %{ conn : conn , user : user } do
conn =
put ( conn , " /settings/email " , %{
" user " = > %{ " password " = > " invalid " , " email " = > " new " <> user . email }
} )
assert html_response ( conn , 200 ) =~ " is invalid "
end
test " renders form with error on already taken email " , %{ conn : conn , user : user } do
other_user = insert ( :user )
password = " very-long-very-secret-123 "
user
|> User . set_password ( password )
|> Repo . update! ( )
conn =
put ( conn , " /settings/email " , %{
" user " = > %{ " password " = > password , " email " = > other_user . email }
} )
assert html_response ( conn , 200 ) =~ " has already been taken "
end
test " renders form with error when email is identical with the current one " , %{
conn : conn ,
user : user
} do
password = " very-long-very-secret-123 "
user
|> User . set_password ( password )
|> Repo . update! ( )
conn =
put ( conn , " /settings/email " , %{
" user " = > %{ " password " = > password , " email " = > user . email }
} )
assert html_response ( conn , 200 ) =~ " can& # 39;t be the same "
end
end
describe " POST /settings/email/cancel " do
setup [ :create_user , :log_in ]
test " cancels email reverification in progress " , %{ conn : conn , user : user } do
user =
user
|> Ecto.Changeset . change (
email_verified : false ,
email : " new " <> user . email ,
previous_email : user . email
)
|> Repo . update! ( )
conn = post ( conn , " /settings/email/cancel " )
assert redirected_to ( conn , 302 ) ==
Routes . auth_path ( conn , :user_settings ) <> " # change-email-address "
updated_user = Repo . reload! ( user )
assert updated_user . email_verified
assert updated_user . email == user . previous_email
refute updated_user . previous_email
end
test " fails to cancel reverification when previous email is already retaken " , %{
conn : conn ,
user : user
} do
user =
user
|> Ecto.Changeset . change (
email_verified : false ,
email : " new " <> user . email ,
previous_email : user . email
)
|> Repo . update! ( )
_other_user = insert ( :user , email : user . previous_email )
conn = post ( conn , " /settings/email/cancel " )
assert redirected_to ( conn , 302 ) == Routes . auth_path ( conn , :activate_form )
assert Phoenix.Flash . get ( conn . assigns . flash , :error ) =~
" Could not cancel email update "
end
test " crashes when previous email is empty on cancel (should not happen) " , %{
conn : conn ,
user : user
} do
user
|> Ecto.Changeset . change (
email_verified : false ,
email : " new " <> user . email ,
previous_email : nil
)
|> Repo . update! ( )
assert_raise RuntimeError , ~r/ Previous email is empty for user / , fn ->
post ( conn , " /settings/email/cancel " )
end
end
end
2019-09-02 14:29:19 +03:00
describe " DELETE /me " do
2021-11-26 17:40:39 +03:00
setup [ :create_user , :log_in , :create_new_site ]
2019-09-02 14:29:19 +03:00
use Plausible.Repo
2020-01-06 12:08:36 +03:00
test " deletes the user " , %{ conn : conn , user : user , site : site } do
2020-06-08 10:35:13 +03:00
Repo . insert_all ( " intro_emails " , [
%{
user_id : user . id ,
timestamp : NaiveDateTime . utc_now ( )
}
] )
Repo . insert_all ( " feedback_emails " , [
%{
user_id : user . id ,
timestamp : NaiveDateTime . utc_now ( )
}
] )
2019-09-02 14:29:19 +03:00
2020-12-30 12:00:37 +03:00
Repo . insert_all ( " create_site_emails " , [
%{
user_id : user . id ,
timestamp : NaiveDateTime . utc_now ( )
}
] )
Repo . insert_all ( " check_stats_emails " , [
%{
user_id : user . id ,
2023-01-02 17:46:18 +03:00
timestamp : NaiveDateTime . utc_now ( )
}
] )
Repo . insert_all ( " sent_renewal_notifications " , [
%{
user_id : user . id ,
2020-12-30 12:00:37 +03:00
timestamp : NaiveDateTime . utc_now ( )
}
] )
2020-01-06 12:08:36 +03:00
insert ( :google_auth , site : site , user : user )
2023-10-10 20:35:17 +03:00
insert ( :subscription , user : user , status : Subscription.Status . deleted ( ) )
insert ( :subscription , user : user , status : Subscription.Status . active ( ) )
2020-01-06 12:08:36 +03:00
2019-09-02 14:29:19 +03:00
conn = delete ( conn , " /me " )
assert redirected_to ( conn ) == " / "
2023-05-24 14:23:23 +03:00
assert Repo . reload ( site ) == nil
assert Repo . reload ( user ) == nil
2023-08-02 14:45:49 +03:00
assert Repo . all ( Plausible.Billing.Subscription ) == [ ]
2019-09-02 14:29:19 +03:00
end
2021-09-08 11:09:58 +03:00
test " deletes sites that the user owns " , %{ conn : conn , user : user , site : owner_site } do
viewer_site = insert ( :site )
insert ( :site_membership , site : viewer_site , user : user , role : " viewer " )
delete ( conn , " /me " )
assert Repo . get ( Plausible.Site , viewer_site . id )
refute Repo . get ( Plausible.Site , owner_site . id )
end
2019-09-02 14:29:19 +03:00
end
2022-08-05 10:24:24 +03:00
describe " POST /settings/api-keys " do
setup [ :create_user , :log_in ]
import Ecto.Query
2023-05-23 11:37:58 +03:00
test " can create an API key " , %{ conn : conn , user : user } do
2023-10-11 15:40:01 +03:00
insert ( :site , memberships : [ build ( :site_membership , user : user , role : " owner " ) ] )
2023-05-23 11:37:58 +03:00
conn =
post ( conn , " /settings/api-keys " , %{
" api_key " = > %{
" user_id " = > user . id ,
" name " = > " all your code are belong to us " ,
" key " = > " swordfish "
}
} )
key = Plausible.Auth.ApiKey |> where ( user_id : ^ user . id ) |> Repo . one ( )
assert conn . status == 302
assert key . name == " all your code are belong to us "
end
test " cannot create a duplicate API key " , %{ conn : conn , user : user } do
2023-10-11 15:40:01 +03:00
insert ( :site , memberships : [ build ( :site_membership , user : user , role : " owner " ) ] )
2023-05-23 11:37:58 +03:00
conn =
post ( conn , " /settings/api-keys " , %{
" api_key " = > %{
" user_id " = > user . id ,
" name " = > " all your code are belong to us " ,
" key " = > " swordfish "
}
} )
conn2 =
post ( conn , " /settings/api-keys " , %{
" api_key " = > %{
" user_id " = > user . id ,
" name " = > " all your code are belong to us " ,
" key " = > " swordfish "
}
} )
assert html_response ( conn2 , 200 ) =~ " has already been taken "
end
2022-08-05 10:24:24 +03:00
test " can't create api key into another site " , %{ conn : conn , user : me } do
2023-10-11 15:40:01 +03:00
_my_site = insert ( :site , memberships : [ build ( :site_membership , user : me , role : " owner " ) ] )
2022-08-05 10:24:24 +03:00
other_user = insert ( :user )
2023-10-11 15:40:01 +03:00
_other_site =
insert ( :site , memberships : [ build ( :site_membership , user : other_user , role : " owner " ) ] )
2022-08-05 10:24:24 +03:00
conn =
post ( conn , " /settings/api-keys " , %{
" api_key " = > %{
" user_id " = > other_user . id ,
" name " = > " all your code are belong to us " ,
" key " = > " swordfish "
}
} )
assert conn . status == 302
refute Plausible.Auth.ApiKey |> where ( user_id : ^ other_user . id ) |> Repo . one ( )
end
end
describe " DELETE /settings/api-keys/:id " do
setup [ :create_user , :log_in ]
alias Plausible.Auth.ApiKey
test " can't delete api key that doesn't belong to me " , %{ conn : conn } do
other_user = insert ( :user )
2023-10-11 15:40:01 +03:00
insert ( :site , memberships : [ build ( :site_membership , user : other_user , role : " owner " ) ] )
2022-08-05 10:24:24 +03:00
assert { :ok , % ApiKey { } = api_key } =
% ApiKey { user_id : other_user . id }
|> ApiKey . changeset ( %{ " name " = > " other user's key " } )
|> Repo . insert ( )
2023-10-11 23:24:16 +03:00
conn = delete ( conn , " /settings/api-keys/ #{ api_key . id } " )
assert Phoenix.Flash . get ( conn . assigns . flash , :error ) == " Could not find API Key to delete "
2022-08-05 10:24:24 +03:00
assert Repo . get ( ApiKey , api_key . id )
end
end
2022-10-17 14:16:59 +03:00
2022-12-08 05:32:14 +03:00
describe " GET /auth/google/callback " do
test " shows error and redirects back to settings when authentication fails " , %{ conn : conn } do
site = insert ( :site )
callback_params = %{ " error " = > " access_denied " , " state " = > " [ #{ site . id } , \" import \" ] " }
conn = get ( conn , Routes . auth_path ( conn , :google_auth_callback ) , callback_params )
assert redirected_to ( conn , 302 ) == Routes . site_path ( conn , :settings_general , site . domain )
2023-05-09 11:51:35 +03:00
assert Phoenix.Flash . get ( conn . assigns . flash , :error ) =~
" unable to authenticate your Google Analytics "
2022-12-08 05:32:14 +03:00
end
end
2022-10-17 14:16:59 +03:00
defp mock_captcha_success ( ) do
mock_captcha ( true )
end
defp mock_captcha_failure ( ) do
mock_captcha ( false )
end
defp mock_captcha ( success ) do
expect (
Plausible.HTTPClient.Mock ,
:post ,
fn _ , _ , _ ->
{ :ok ,
% Finch.Response {
status : 200 ,
headers : [ { " content-type " , " application/json " } ] ,
body : %{ " success " = > success }
} }
end
)
end
2023-10-10 20:35:17 +03:00
defp configure_enterprise_plan ( user ) do
insert ( :enterprise_plan ,
paddle_plan_id : @configured_enterprise_plan_paddle_plan_id ,
user : user ,
monthly_pageview_limit : 20_000_000 ,
billing_interval : :yearly
)
end
2019-09-02 14:29:19 +03:00
end