From 8f39c015ea24bfabeabfceaab8cef918e2793ee7 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Thu, 11 Sep 2025 19:25:05 -0400 Subject: [PATCH] Tests now pass with local authentication This is the first step of a multi-step SaaS engine extraction. Looking ahead to an open source release, we need to make sure that local authentication is treated as an "official" option, and not just a hack I added for Kevin to do load testing outside our DC. So this PR gets to green, and adds a CI step in "local authentication" mode. This all probably feels a little hacky to you, Reader, but the goal of this change is to ease the next step, which will be extracting the 37id and Queenbee integrations into a proprietary "SaaS mode" engine. In service of that goal, this commit simply wraps all of the dependent code and tests with a conditional check on `config.x.local_authentication`. --- Gemfile | 3 +- Gemfile.lock | 6 +- app/helpers/login_helper.rb | 14 +- app/models/account/signal_account.rb | 4 +- app/models/user.rb | 4 +- config/ci.rb | 7 +- config/initializers/authentication.rb | 1 + config/initializers/queenbee.rb | 18 +- config/initializers/signal_id.rb | 36 ++- config/routes.rb | 4 +- .../controller_authentication_test.rb | 44 ++-- .../sessions/launchpad_controller_test.rb | 62 ++--- test/controllers/sessions_controller_test.rb | 80 +++---- .../signup/accounts_controller_test.rb | 162 ++++++------- test/fixtures/accounts.yml | 2 + test/fixtures/users.yml | 6 + test/models/account/signal_account_test.rb | 56 ++--- test/models/period_highlights_test.rb | 35 +-- test/models/signup_test.rb | 220 +++++++++--------- test/models/user/highlights_test.rb | 45 ++-- test/models/user/signal_user_test.rb | 14 +- test/models/user_test.rb | 4 +- test/test_helper.rb | 32 ++- test/test_helpers/session_test_helper.rb | 7 +- 24 files changed, 447 insertions(+), 419 deletions(-) create mode 100644 config/initializers/authentication.rb diff --git a/Gemfile b/Gemfile index 5377cf92b..d60542c70 100644 --- a/Gemfile +++ b/Gemfile @@ -35,7 +35,8 @@ gem "web-push" gem "net-http-persistent" # 37id and Queenbee integration -gem "signal_id", bc: "signal_id", branch: "rails4" +need_signal_id = ENV.fetch("LOCAL_AUTHENTICATION", "") == "" +gem "signal_id", bc: "signal_id", branch: "rails4", require: need_signal_id gem "mysql2", github: "jeremy/mysql2", branch: "force_latin1_to_utf8" # needed by signal_id gem "queuety", bc: "queuety", branch: "rails4" # needed by signal_id gem "service_concurrency_prevention", bc: "service_concurrency_prevention" # needed by queuety diff --git a/Gemfile.lock b/Gemfile.lock index a6d153b1a..6db5eeff3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -203,10 +203,10 @@ GEM activerecord (>= 8.1.alpha) railties (>= 8.1.alpha) zeitwerk - activeresource (6.1.4) - activemodel (>= 6.0) + activeresource (6.2.0) + activemodel (>= 7.0) activemodel-serializers-xml (~> 1.0) - activesupport (>= 6.0) + activesupport (>= 7.0) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) ast (2.4.3) diff --git a/app/helpers/login_helper.rb b/app/helpers/login_helper.rb index 483c1a31c..5d84dbbb1 100644 --- a/app/helpers/login_helper.rb +++ b/app/helpers/login_helper.rb @@ -1,13 +1,13 @@ module LoginHelper def login_url - if ApplicationRecord.current_tenant - if Rails.application.config.x.local_authentication - new_session_path - else - Launchpad.login_url(product: true, account: Account.sole) - end + if Rails.application.config.x.local_authentication + new_session_path else - Launchpad.login_url(product: true) + if ApplicationRecord.current_tenant + Launchpad.login_url(product: true, account: Account.sole) + else + Launchpad.login_url(product: true) + end end end diff --git a/app/models/account/signal_account.rb b/app/models/account/signal_account.rb index 55dc43e33..dbacfd04e 100644 --- a/app/models/account/signal_account.rb +++ b/app/models/account/signal_account.rb @@ -2,7 +2,9 @@ module Account::SignalAccount extend ActiveSupport::Concern included do - belongs_to :signal_account, class_name: "SignalId::Account", primary_key: :queenbee_id, foreign_key: :queenbee_id, optional: true + unless Rails.application.config.x.local_authentication + belongs_to :signal_account, class_name: "SignalId::Account", primary_key: :queenbee_id, foreign_key: :queenbee_id, optional: true + end end class_methods do diff --git a/app/models/user.rb b/app/models/user.rb index 64a5d1769..1cc1b7a44 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -21,7 +21,9 @@ class User < ApplicationRecord def deactivate sessions.delete_all accesses.destroy_all - SignalId::Database.on_master { signal_user&.destroy } + unless Rails.application.config.x.local_authentication + SignalId::Database.on_master { signal_user&.destroy } + end update! active: false, email_address: deactived_email_address end diff --git a/config/ci.rb b/config/ci.rb index 5ded71515..121deb06a 100644 --- a/config/ci.rb +++ b/config/ci.rb @@ -9,9 +9,10 @@ CI.run do step "Security: Importmap audit", "bin/importmap audit" step "Security: Brakeman audit", "bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error" - step "Tests: Rails", "bin/rails test" - step "Tests: 37id", "bin/rails 37id:test:units" - step "Tests: System", "bin/rails test:system" + step "Tests: Rails with 37id auth", "bin/rails test" + step "Tests: Rails with local auth", "LOCAL_AUTHENTICATION=1 bin/rails test" + step "Tests: 37id", "bin/rails 37id:test:units" + step "Tests: System", "bin/rails test:system" if success? step "Signoff: All systems go. Ready for merge and deploy.", "gh signoff" diff --git a/config/initializers/authentication.rb b/config/initializers/authentication.rb new file mode 100644 index 000000000..94d030264 --- /dev/null +++ b/config/initializers/authentication.rb @@ -0,0 +1 @@ +Rails.application.config.x.local_authentication = ENV["LOCAL_AUTHENTICATION"].present? diff --git a/config/initializers/queenbee.rb b/config/initializers/queenbee.rb index 9708bc352..9c7fdf9a5 100644 --- a/config/initializers/queenbee.rb +++ b/config/initializers/queenbee.rb @@ -1,12 +1,14 @@ -Queenbee.host_app = Fizzy +unless Rails.application.config.x.local_authentication + Queenbee.host_app = Fizzy -Rails.application.config.to_prepare do - Queenbee::Subscription.short_names = Subscription::SHORT_NAMES - Queenbee::ApiToken.token = Rails.application.credentials.dig(:queenbee_api_token) + Rails.application.config.to_prepare do + Queenbee::Subscription.short_names = Subscription::SHORT_NAMES + Queenbee::ApiToken.token = Rails.application.credentials.dig(:queenbee_api_token) - Subscription::SHORT_NAMES.each do |short_name| - const_name = "#{short_name}Subscription" - ::Object.send(:remove_const, const_name) if ::Object.const_defined?(const_name) - ::Object.const_set const_name, Subscription.const_get(short_name, false) + Subscription::SHORT_NAMES.each do |short_name| + const_name = "#{short_name}Subscription" + ::Object.send(:remove_const, const_name) if ::Object.const_defined?(const_name) + ::Object.const_set const_name, Subscription.const_get(short_name, false) + end end end diff --git a/config/initializers/signal_id.rb b/config/initializers/signal_id.rb index afb6ff60c..6a25119d3 100644 --- a/config/initializers/signal_id.rb +++ b/config/initializers/signal_id.rb @@ -1,27 +1,25 @@ -require "signal_id" +unless Rails.application.config.x.local_authentication + ENV["SIGNAL_ID_SECRET"] = Rails.application.credentials.signal_id_secret -Rails.application.config.x.local_authentication = ENV["LOCAL_AUTHENTICATION"].present? + Rails.application.config.to_prepare do + SignalId.product = "fizzy" -ENV["SIGNAL_ID_SECRET"] = Rails.application.credentials.signal_id_secret + db_config = SignalId::Database.default_configuration + if Rails.application.config.x.local_authentication + db_config.each do |name, config| + config["connect_timeout"] = 1 + end + end + SignalId::Database.load_configuration db_config + SignalId::Database.enable_rw_splitting! -Rails.application.config.to_prepare do - SignalId.product = "fizzy" - - db_config = SignalId::Database.default_configuration - if Rails.application.config.x.local_authentication - db_config.each do |name, config| - config["connect_timeout"] = 1 + silence_warnings do + SignalId::Account::Peer = Account + SignalId::User::Peer = User end end - SignalId::Database.load_configuration db_config - SignalId::Database.enable_rw_splitting! - silence_warnings do - SignalId::Account::Peer = Account - SignalId::User::Peer = User + Rails.application.config.after_initialize do + ActiveRecord.yaml_column_permitted_classes << SignalId::PersonName end end - -Rails.application.config.after_initialize do - ActiveRecord.yaml_column_permitted_classes << SignalId::PersonName -end diff --git a/config/routes.rb b/config/routes.rb index 505bcf240..1a462f9ed 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -190,7 +190,9 @@ Rails.application.routes.draw do root "events#index" - Queenbee.routes(self) + unless Rails.application.config.x.local_authentication + Queenbee.routes(self) + end namespace :admin do mount MissionControl::Jobs::Engine, at: "/jobs" diff --git a/test/controllers/controller_authentication_test.rb b/test/controllers/controller_authentication_test.rb index bc75172d2..e3b81dc0e 100644 --- a/test/controllers/controller_authentication_test.rb +++ b/test/controllers/controller_authentication_test.rb @@ -1,36 +1,34 @@ require "test_helper" class ControllerAuthenticationTest < ActionDispatch::IntegrationTest - test "access without an account slug redirects to launchpad" do - integration_session.default_url_options[:script_name] = "" # no tenant + if Rails.application.config.x.local_authentication + test "access without an account slug redirects to new session" do + integration_session.default_url_options[:script_name] = "" # no tenant - get cards_path - - assert_redirected_to Launchpad.login_url(product: true) - end - - test "access with an account slug but no session redirects to launchpad" do - get cards_path - - assert_redirected_to Launchpad.login_url(product: true, account: Account.sole) - end - - test "local auth, access without an account slug redirects to launchpad" do - integration_session.default_url_options[:script_name] = "" # no tenant - - with_local_auth do get cards_path + + assert_redirected_to new_session_path end - assert_redirected_to Launchpad.login_url(product: true) - end - - test "local auth, access with an account slug but no session redirects to new session" do - with_local_auth do + test "access with an account slug but no session redirects to new session" do get cards_path + + assert_redirected_to new_session_path + end + else + test "access without an account slug redirects to launchpad" do + integration_session.default_url_options[:script_name] = "" # no tenant + + get cards_path + + assert_redirected_to Launchpad.login_url(product: true) end - assert_redirected_to new_session_path + test "access with an account slug but no session redirects to launchpad" do + get cards_path + + assert_redirected_to Launchpad.login_url(product: true, account: Account.sole) + end end test "access with an account slug and a session allows functional access" do diff --git a/test/controllers/sessions/launchpad_controller_test.rb b/test/controllers/sessions/launchpad_controller_test.rb index bd31eca8e..c885695c2 100644 --- a/test/controllers/sessions/launchpad_controller_test.rb +++ b/test/controllers/sessions/launchpad_controller_test.rb @@ -1,38 +1,40 @@ require "test_helper" class Sessions::LaunchpadControllerTest < ActionDispatch::IntegrationTest - test "show renders when not signed in" do - get session_launchpad_path(params: { sig: "test-sig" }) + unless Rails.application.config.x.local_authentication + test "show renders when not signed in" do + get session_launchpad_path(params: { sig: "test-sig" }) - assert_response :success + assert_response :success - assert_select "form input#sig" do |node| - assert_equal node.length, 1 - assert_equal node.first["value"], "test-sig" + assert_select "form input#sig" do |node| + assert_equal node.length, 1 + assert_equal node.first["value"], "test-sig" + end + end + + test "create establishes a session when the sig is valid" do + user = users(:david) + + put session_launchpad_path(params: { sig: user.signal_user.perishable_signature }) + + assert_redirected_to root_url + assert parsed_cookies.signed[:session_token] + end + + test "create checks user.active?" do + user = users(:david) + user.update! active: false + + put session_launchpad_path(params: { sig: user.signal_user.perishable_signature }) + + assert_response :unauthorized + end + + test "returns 401 when the sig is invalid" do + put session_launchpad_path(params: { sig: "invalid" }) + + assert_response :unauthorized end end - - test "create establishes a session when the sig is valid" do - user = users(:david) - - put session_launchpad_path(params: { sig: user.signal_user.perishable_signature }) - - assert_redirected_to root_url - assert parsed_cookies.signed[:session_token] - end - - test "create checks user.active?" do - user = users(:david) - user.update! active: false - - put session_launchpad_path(params: { sig: user.signal_user.perishable_signature }) - - assert_response :unauthorized - end - - test "returns 401 when the sig is invalid" do - put session_launchpad_path(params: { sig: "invalid" }) - - assert_response :unauthorized - end end diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb index 6613488d4..b83918744 100644 --- a/test/controllers/sessions_controller_test.rb +++ b/test/controllers/sessions_controller_test.rb @@ -1,61 +1,55 @@ require "test_helper" class SessionsControllerTest < ActionDispatch::IntegrationTest - test "destroy" do - sign_in_as :kevin + unless Rails.application.config.x.local_authentication + test "destroy" do + sign_in_as :kevin - delete session_path - - assert_redirected_to Launchpad.logout_url - assert_not cookies[:session_token].present? - end - - test "local auth, destroy" do - sign_in_as :kevin - - with_local_auth do delete session_path + + assert_redirected_to Launchpad.logout_url + assert_not cookies[:session_token].present? end - assert_redirected_to new_session_path - assert_not cookies[:session_token].present? - end - - test "new" do - get new_session_path - - assert_response :forbidden - end - - test "local auth, new" do - with_local_auth do + test "new" do get new_session_path + + assert_response :forbidden end - assert_response :success - end - - test "create" do - post session_path, params: { email_address: "david@37signals.com", password: "secret123456" } - - assert_response :forbidden - end - - test "local auth, create with valid credentials" do - with_local_auth do + test "create" do post session_path, params: { email_address: "david@37signals.com", password: "secret123456" } + + assert_response :forbidden + end + else + test "destroy" do + sign_in_as :kevin + + delete session_path + + assert_redirected_to new_session_path + assert_not cookies[:session_token].present? end - assert_redirected_to root_path - assert cookies[:session_token].present? - end + test "new" do + get new_session_path - test "local auth, create with invalid credentials" do - with_local_auth do + assert_response :success + end + + test "create with valid credentials" do + post session_path, params: { email_address: "david@37signals.com", password: "secret123456" } + + assert_redirected_to root_path + assert cookies[:session_token].present? + end + + test "create with invalid credentials" do post session_path, params: { email_address: "david@37signals.com", password: "wrong" } - end - assert_redirected_to new_session_path - assert_not cookies[:session_token].present? + assert_redirected_to new_session_path + assert_not cookies[:session_token].present? + end end end diff --git a/test/controllers/signup/accounts_controller_test.rb b/test/controllers/signup/accounts_controller_test.rb index 413cf54c1..e43917765 100644 --- a/test/controllers/signup/accounts_controller_test.rb +++ b/test/controllers/signup/accounts_controller_test.rb @@ -1,103 +1,105 @@ require "test_helper" class Signup::AccountsControllerTest < ActionDispatch::IntegrationTest - test "new under a tenanted URL redirects to the root" do - get new_signup_account_url + unless Rails.application.config.x.local_authentication + test "new under a tenanted URL redirects to the root" do + get new_signup_account_url - assert_redirected_to root_url - end + assert_redirected_to root_url + end - test "new under an untenanted URL is OK" do - integration_session.default_url_options[:script_name] = "" # no tenant + test "new under an untenanted URL is OK" do + integration_session.default_url_options[:script_name] = "" # no tenant - get new_signup_account_url, headers: auth_headers + get new_signup_account_url, headers: auth_headers - assert_response :success - end + assert_response :success + end - test "create with invalid params" do - integration_session.default_url_options[:script_name] = "" # no tenant + test "create with invalid params" do + integration_session.default_url_options[:script_name] = "" # no tenant - post signup_accounts_url, - headers: auth_headers, - params: { signup: { full_name: "Jim", email_address: "jim@example.com", password: "", company_name: "" } } + post signup_accounts_url, + headers: auth_headers, + params: { signup: { full_name: "Jim", email_address: "jim@example.com", password: "", company_name: "" } } - assert_response :unprocessable_entity - assert_select "div.alert--error", text: /you need to choose a password/i - end + assert_response :unprocessable_entity + assert_select "div.alert--error", text: /you need to choose a password/i + end - test "create for a new " do - integration_session.default_url_options[:script_name] = "" # no tenant + test "create for a new " do + integration_session.default_url_options[:script_name] = "" # no tenant - assert_difference -> { SignalId::Identity.count }, +1 do - assert_difference -> { SignalId::Account.count }, +1 do - post signup_accounts_url, headers: auth_headers, - params: { - signup: { - full_name: "Jim", - email_address: "jim@example.com", - password: SecureRandom.hex(12), - company_name: "signup-accounts-controller-test-1" + assert_difference -> { SignalId::Identity.count }, +1 do + assert_difference -> { SignalId::Account.count }, +1 do + post signup_accounts_url, headers: auth_headers, + params: { + signup: { + full_name: "Jim", + email_address: "jim@example.com", + password: SecureRandom.hex(12), + company_name: "signup-accounts-controller-test-1" + } } - } + end + end + + signal_account = SignalId::Account.last + assert_redirected_to(/#{signal_account.login_url}/) + end + + test "create for an existing identity" do + integration_session.default_url_options[:script_name] = "" # no tenant + + identity = signal_identities(:david) + + post signup_accounts_url, headers: auth_headers, + params: { signup: { email_address: identity.email_address, company_name: "signup-accounts-controller-test-2" } } + + assert_authentication_requested_for identity + + assert_no_difference -> { SignalId::Identity.count } do + assert_difference -> { SignalId::Account.count } do + authenticate_via_launchpad_as(identity) + assert_redirected_to_account + assert_nil session[:signup] + end + end + + signal_account = SignalId::Account.last + ApplicationRecord.with_tenant(signal_account.queenbee_id) do + assert_equal Account.last, signal_account.peer end end - signal_account = SignalId::Account.last - assert_redirected_to(/#{signal_account.login_url}/) - end + test "actions require HTTP basic authentication while we're in internal-only mode" do + integration_session.default_url_options[:script_name] = "" # no tenant - test "create for an existing identity" do - integration_session.default_url_options[:script_name] = "" # no tenant + get new_signup_account_url - identity = signal_identities(:david) + assert_response :unauthorized + end - post signup_accounts_url, headers: auth_headers, - params: { signup: { email_address: identity.email_address, company_name: "signup-accounts-controller-test-2" } } - - assert_authentication_requested_for identity - - assert_no_difference -> { SignalId::Identity.count } do - assert_difference -> { SignalId::Account.count } do - authenticate_via_launchpad_as(identity) - assert_redirected_to_account - assert_nil session[:signup] + private + def auth_headers + { "HTTP_AUTHORIZATION" => ActionController::HttpAuthentication::Basic.encode_credentials("testname", "testpassword") } end - end - signal_account = SignalId::Account.last - ApplicationRecord.with_tenant(signal_account.queenbee_id) do - assert_equal Account.last, signal_account.peer - end + def assert_authentication_requested_for(identity) + assert_response :redirect + assert_match(/#{Regexp.escape(Launchpad.url("/authenticate", login_hint: identity.email_address))}.*&purpose=signup/, redirect_to_url) + end + + def authenticate_via_launchpad_as(identity) + get signup_session_url, headers: auth_headers, params: { sig: identity.perishable_signature } + assert_equal identity.id, session[:signup]["identity_id"] + assert_redirected_to new_signup_completion_url + post signup_completions_url, headers: auth_headers + end + + def assert_redirected_to_account(signal_account = SignalId::Account.last) + assert_response :redirect + assert_match(/#{Regexp.escape(signal_account.url("/session/launchpad"))}.*&sig=/, redirect_to_url) + end end - - test "actions require HTTP basic authentication while we're in internal-only mode" do - integration_session.default_url_options[:script_name] = "" # no tenant - - get new_signup_account_url - - assert_response :unauthorized - end - - private - def auth_headers - { "HTTP_AUTHORIZATION" => ActionController::HttpAuthentication::Basic.encode_credentials("testname", "testpassword") } - end - - def assert_authentication_requested_for(identity) - assert_response :redirect - assert_match(/#{Regexp.escape(Launchpad.url("/authenticate", login_hint: identity.email_address))}.*&purpose=signup/, redirect_to_url) - end - - def authenticate_via_launchpad_as(identity) - get signup_session_url, headers: auth_headers, params: { sig: identity.perishable_signature } - assert_equal identity.id, session[:signup]["identity_id"] - assert_redirected_to new_signup_completion_url - post signup_completions_url, headers: auth_headers - end - - def assert_redirected_to_account(signal_account = SignalId::Account.last) - assert_response :redirect - assert_match(/#{Regexp.escape(signal_account.url("/session/launchpad"))}.*&sig=/, redirect_to_url) - end end diff --git a/test/fixtures/accounts.yml b/test/fixtures/accounts.yml index d394aedc3..12c4f06aa 100644 --- a/test/fixtures/accounts.yml +++ b/test/fixtures/accounts.yml @@ -1,4 +1,6 @@ 37s: name: 37signals join_code: "ejpP-THlQ-Cc2f" +<% unless Rails.application.config.x.local_authentication %> queenbee_id: <%= ActiveRecord::FixtureSet.identify :'37s_fizzy' %> +<% end %> diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml index 3339d12d6..219dbe9e6 100644 --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -5,21 +5,27 @@ david: email_address: david@37signals.com password_digest: <%= digest %> role: member +<% unless Rails.application.config.x.local_authentication %> signal_user: 37s_fizzy_david +<% end %> jz: name: JZ email_address: jz@37signals.com password_digest: <%= digest %> role: member +<% unless Rails.application.config.x.local_authentication %> signal_user: 37s_fizzy_jzimdars +<% end %> kevin: name: Kevin email_address: kevin@37signals.com password_digest: <%= digest %> role: admin +<% unless Rails.application.config.x.local_authentication %> signal_user: 37s_fizzy_kevin +<% end %> system: name: System diff --git a/test/models/account/signal_account_test.rb b/test/models/account/signal_account_test.rb index cee8ba0bf..90c26dde4 100644 --- a/test/models/account/signal_account_test.rb +++ b/test/models/account/signal_account_test.rb @@ -1,38 +1,40 @@ require "test_helper" class Account::SignalAccountTest < ActiveSupport::TestCase - # # TODO(MIKE): Queenbee client API compliance tests - # include Queenbee::Testing::Client + unless Rails.application.config.x.local_authentication + # # TODO(MIKE): Queenbee client API compliance tests + # include Queenbee::Testing::Client - setup do - @account = accounts("37s") - end + setup do + @account = accounts("37s") + end - test "belongs to a signal_account via a shared queenbee_id" do - assert_not_nil @account.queenbee_id - assert_equal @account.queenbee_id, Account.new(signal_account: @account.signal_account).queenbee_id - assert_equal @account.signal_account, Account.new(queenbee_id: @account.queenbee_id).signal_account - end + test "belongs to a signal_account via a shared queenbee_id" do + assert_not_nil @account.queenbee_id + assert_equal @account.queenbee_id, Account.new(signal_account: @account.signal_account).queenbee_id + assert_equal @account.signal_account, Account.new(queenbee_id: @account.queenbee_id).signal_account + end - test ".create_with_admin_user creates a new local account and user peers" do - ApplicationRecord.create_tenant("account-create-with-dependents") do - signal_account = signal_accounts(:honcho_fizzy) - account = Account.create_with_admin_user(queenbee_id: signal_account.queenbee_id) + test ".create_with_admin_user creates a new local account and user peers" do + ApplicationRecord.create_tenant("account-create-with-dependents") do + signal_account = signal_accounts(:honcho_fizzy) + account = Account.create_with_admin_user(queenbee_id: signal_account.queenbee_id) - assert_not_nil account - assert account.persisted? - assert_equal 1, Account.count - assert_equal signal_account.queenbee_id, account.queenbee_id - assert_equal signal_account.name, account.name - assert_equal account, signal_account.peer + assert_not_nil account + assert account.persisted? + assert_equal 1, Account.count + assert_equal signal_account.queenbee_id, account.queenbee_id + assert_equal signal_account.name, account.name + assert_equal account, signal_account.peer - assert_equal 1, User.count - User.first.tap do |user| - assert signal_account.owner.name, user.name - assert signal_account.owner.email_address, user.email_address - assert signal_account.owner.id, user.signal_user_id - assert_equal "admin", user.role - assert_equal user, signal_account.owner.peer + assert_equal 1, User.count + User.first.tap do |user| + assert signal_account.owner.name, user.name + assert signal_account.owner.email_address, user.email_address + assert signal_account.owner.id, user.signal_user_id + assert_equal "admin", user.role + assert_equal user, signal_account.owner.peer + end end end end diff --git a/test/models/period_highlights_test.rb b/test/models/period_highlights_test.rb index 5bcfca99a..cf7627201 100644 --- a/test/models/period_highlights_test.rb +++ b/test/models/period_highlights_test.rb @@ -1,27 +1,30 @@ require "test_helper" class PeriodHighlightTest < ActiveSupport::TestCase - include VcrTestHelper + # Skipping when locally authenticating because the VCR cassettes would need to be re-recorded. + unless Rails.application.config.x.local_authentication + include VcrTestHelper - setup do - @user = users(:david) - end - - test "generate period highlights" do - period_highlights = assert_difference -> { PeriodHighlights.count }, 1 do - PeriodHighlights.create_or_find_for(@user.collections, starts_at: 1.month.ago, duration: 2.months) + setup do + @user = users(:david) end - assert_match /logo/i, period_highlights.to_html - end + test "generate period highlights" do + period_highlights = assert_difference -> { PeriodHighlights.count }, 1 do + PeriodHighlights.create_or_find_for(@user.collections, starts_at: 1.month.ago, duration: 2.months) + end - test "don't generate highlights for existing periods" do - new_period_highlights = PeriodHighlights.create_or_find_for(@user.collections, starts_at: 1.month.ago, duration: 2.months) - - existing_period_highlights = assert_no_difference -> { PeriodHighlights.count } do - PeriodHighlights.create_or_find_for(@user.collections, starts_at: 1.month.ago, duration: 2.months) + assert_match /logo/i, period_highlights.to_html end - assert_equal new_period_highlights, existing_period_highlights + test "don't generate highlights for existing periods" do + new_period_highlights = PeriodHighlights.create_or_find_for(@user.collections, starts_at: 1.month.ago, duration: 2.months) + + existing_period_highlights = assert_no_difference -> { PeriodHighlights.count } do + PeriodHighlights.create_or_find_for(@user.collections, starts_at: 1.month.ago, duration: 2.months) + end + + assert_equal new_period_highlights, existing_period_highlights + end end end diff --git a/test/models/signup_test.rb b/test/models/signup_test.rb index 37d96bd6f..d1f2abaf9 100644 --- a/test/models/signup_test.rb +++ b/test/models/signup_test.rb @@ -1,151 +1,153 @@ require "test_helper" class SignupTest < ActiveSupport::TestCase - setup do - @signup = Signup.new( - email_address: "brian@example.com", - full_name: "Brian Wilson", - company_name: "Beach Boys", - password: SecureRandom.hex(16) - ) - end + unless Rails.application.config.x.local_authentication + setup do + @signup = Signup.new( + email_address: "brian@example.com", + full_name: "Brian Wilson", + company_name: "Beach Boys", + password: SecureRandom.hex(16) + ) + end - test "#to_h allows persistence of the signup data" do - actual = @signup.to_h - expected = { - email_address: @signup.email_address, - full_name: @signup.full_name, - company_name: @signup.company_name - } - assert_equal expected, actual - end + test "#to_h allows persistence of the signup data" do + actual = @signup.to_h + expected = { + email_address: @signup.email_address, + full_name: @signup.full_name, + company_name: @signup.company_name + } + assert_equal expected, actual + end - test "#process creates all the necessary objects for a new identity" do - Account.any_instance.expects(:setup_basic_template).once + test "#process creates all the necessary objects for a new identity" do + Account.any_instance.expects(:setup_basic_template).once - assert @signup.process, @signup.errors.full_messages.to_sentence(words_connector: ". ") - assert_empty @signup.errors + assert @signup.process, @signup.errors.full_messages.to_sentence(words_connector: ". ") + assert_empty @signup.errors - assert @signup.signal_identity - assert @signup.signal_identity.persisted? + assert @signup.signal_identity + assert @signup.signal_identity.persisted? - assert @signup.queenbee_account - assert @signup.queenbee_account.id + assert @signup.queenbee_account + assert @signup.queenbee_account.id - assert @signup.signal_account - assert @signup.signal_account.persisted? - assert_equal @signup.signal_identity, @signup.signal_account.owner.identity - assert_equal @signup.company_name, @signup.signal_account.name + assert @signup.signal_account + assert @signup.signal_account.persisted? + assert_equal @signup.signal_identity, @signup.signal_account.owner.identity + assert_equal @signup.company_name, @signup.signal_account.name - assert @signup.account - assert @signup.account.persisted? + assert @signup.account + assert @signup.account.persisted? - assert @signup.user - assert @signup.user.persisted? - assert_equal @signup.user.signal_user, @signup.signal_account.owner + assert @signup.user + assert @signup.user.persisted? + assert_equal @signup.user.signal_user, @signup.signal_account.owner - assert_equal @signup.queenbee_account.id.to_s, @signup.tenant_name - assert_includes ApplicationRecord.tenants, @signup.tenant_name - end + assert_equal @signup.queenbee_account.id.to_s, @signup.tenant_name + assert_includes ApplicationRecord.tenants, @signup.tenant_name + end - test "the new account is named with company name if present" do - assert_equal @signup.company_name, @signup.send(:queenbee_account_name) - end + test "the new account is named with company name if present" do + assert_equal @signup.company_name, @signup.send(:queenbee_account_name) + end - test "in beta, the new account is named distinctly" do - Rails.stubs(:env).returns(stub(beta?: true)) + test "in beta, the new account is named distinctly" do + Rails.stubs(:env).returns(stub(beta?: true)) - assert_equal "#{@signup.company_name} (Beta)", @signup.send(:queenbee_account_name) - end + assert_equal "#{@signup.company_name} (Beta)", @signup.send(:queenbee_account_name) + end - test "#process creates all the necessary objects for an existing identity" do - signal_identity = SignalId::Identity.find_by_email_address("david@37signals.com") + test "#process creates all the necessary objects for an existing identity" do + signal_identity = SignalId::Identity.find_by_email_address("david@37signals.com") - @signup = Signup.new( - signal_identity: signal_identity, - company_name: "Team LeMans", - ) + @signup = Signup.new( + signal_identity: signal_identity, + company_name: "Team LeMans", + ) - Account.any_instance.expects(:setup_basic_template).once + Account.any_instance.expects(:setup_basic_template).once - assert @signup.process, @signup.errors.full_messages.to_sentence(words_connector: ". ") - assert_empty @signup.errors + assert @signup.process, @signup.errors.full_messages.to_sentence(words_connector: ". ") + assert_empty @signup.errors - assert @signup.signal_identity - assert_equal signal_identity, @signup.signal_identity + assert @signup.signal_identity + assert_equal signal_identity, @signup.signal_identity - assert @signup.queenbee_account - assert @signup.queenbee_account.id + assert @signup.queenbee_account + assert @signup.queenbee_account.id - assert @signup.signal_account - assert @signup.signal_account.persisted? - assert_equal @signup.signal_identity, @signup.signal_account.owner.identity + assert @signup.signal_account + assert @signup.signal_account.persisted? + assert_equal @signup.signal_identity, @signup.signal_account.owner.identity - assert @signup.account - assert @signup.account.persisted? + assert @signup.account + assert @signup.account.persisted? - assert @signup.user - assert @signup.user.persisted? - assert_equal @signup.user.signal_user, @signup.signal_account.owner + assert @signup.user + assert @signup.user.persisted? + assert_equal @signup.user.signal_user, @signup.signal_account.owner - assert_equal @signup.queenbee_account.id.to_s, @signup.tenant_name - assert_includes ApplicationRecord.tenants, @signup.tenant_name - end + assert_equal @signup.queenbee_account.id.to_s, @signup.tenant_name + assert_includes ApplicationRecord.tenants, @signup.tenant_name + end - test "#process does nothing if a validation error occurs creating identity" do - @signup.password = "" + test "#process does nothing if a validation error occurs creating identity" do + @signup.password = "" - assert_not @signup.process - assert_not_empty @signup.errors[:password] + assert_not @signup.process + assert_not_empty @signup.errors[:password] - assert_nil @signup.signal_identity - assert_nil @signup.queenbee_account - assert_nil @signup.account - assert_nil @signup.user - end + assert_nil @signup.signal_identity + assert_nil @signup.queenbee_account + assert_nil @signup.account + assert_nil @signup.user + end - test "#process does nothing if a validation error occurs creating the queenbee account" do - Queenbee::Remote::Account.stubs(:create!).raises(RuntimeError, "Invalid account data") + test "#process does nothing if a validation error occurs creating the queenbee account" do + Queenbee::Remote::Account.stubs(:create!).raises(RuntimeError, "Invalid account data") - SignalId::Identity.any_instance.expects(:destroy).once + SignalId::Identity.any_instance.expects(:destroy).once - assert_not @signup.process - assert_not_empty @signup.errors[:base] + assert_not @signup.process + assert_not_empty @signup.errors[:base] - assert_nil @signup.signal_identity - assert_nil @signup.queenbee_account - assert_nil @signup.account - assert_nil @signup.user - end + assert_nil @signup.signal_identity + assert_nil @signup.queenbee_account + assert_nil @signup.account + assert_nil @signup.user + end - test "#process does nothing if a validation error occurs creating the tenant" do - ApplicationRecord.stubs(:create_tenant).raises(RuntimeError, "Tenant already exists") + test "#process does nothing if a validation error occurs creating the tenant" do + ApplicationRecord.stubs(:create_tenant).raises(RuntimeError, "Tenant already exists") - Queenbee::Remote::Account.any_instance.expects(:cancel).once - SignalId::Identity.any_instance.expects(:destroy).once + Queenbee::Remote::Account.any_instance.expects(:cancel).once + SignalId::Identity.any_instance.expects(:destroy).once - assert_not @signup.process - assert_not_empty @signup.errors[:base] + assert_not @signup.process + assert_not_empty @signup.errors[:base] - assert_nil @signup.signal_identity - assert_nil @signup.queenbee_account - assert_nil @signup.account - assert_nil @signup.user - end + assert_nil @signup.signal_identity + assert_nil @signup.queenbee_account + assert_nil @signup.account + assert_nil @signup.user + end - test "#process does nothing if a validation error occurs creating the account" do - Account.stubs(:create_with_admin_user).raises(RuntimeError, "Account creation failed") + test "#process does nothing if a validation error occurs creating the account" do + Account.stubs(:create_with_admin_user).raises(RuntimeError, "Account creation failed") - ApplicationRecord.expects(:destroy_tenant).once - Queenbee::Remote::Account.any_instance.expects(:cancel).once - SignalId::Identity.any_instance.expects(:destroy).once + ApplicationRecord.expects(:destroy_tenant).once + Queenbee::Remote::Account.any_instance.expects(:cancel).once + SignalId::Identity.any_instance.expects(:destroy).once - assert_not @signup.process - assert_not_empty @signup.errors[:base] + assert_not @signup.process + assert_not_empty @signup.errors[:base] - assert_nil @signup.signal_identity - assert_nil @signup.queenbee_account - assert_nil @signup.account - assert_nil @signup.user + assert_nil @signup.signal_identity + assert_nil @signup.queenbee_account + assert_nil @signup.account + assert_nil @signup.user + end end end diff --git a/test/models/user/highlights_test.rb b/test/models/user/highlights_test.rb index 8238dec5b..97b775325 100644 --- a/test/models/user/highlights_test.rb +++ b/test/models/user/highlights_test.rb @@ -1,33 +1,36 @@ require "test_helper" class User::HighlightsTest < ActiveSupport::TestCase - include VcrTestHelper + # Skipping when locally authenticating because the VCR cassettes would need to be re-recorded. + unless Rails.application.config.x.local_authentication + include VcrTestHelper - setup do - @user = users(:david) - travel_to 1.week.ago + 2.days - end - - test "generate weekly highlights" do - stub_const(PeriodHighlights::Period, :MIN_EVENTS_TO_BE_INTERESTING, 3) do - period_highlights = assert_difference -> { PeriodHighlights.count }, 1 do - @user.generate_weekly_highlights - end - - assert_match /logo/i, period_highlights.to_html + setup do + @user = users(:david) + travel_to 1.week.ago + 2.days end - end - test "don't generate highlights for existing periods" do - stub_const(PeriodHighlights::Period, :MIN_EVENTS_TO_BE_INTERESTING, 3) do - new_period_highlights = @user.generate_weekly_highlights - assert_not_nil new_period_highlights + test "generate weekly highlights" do + stub_const(PeriodHighlights::Period, :MIN_EVENTS_TO_BE_INTERESTING, 3) do + period_highlights = assert_difference -> { PeriodHighlights.count }, 1 do + @user.generate_weekly_highlights + end - existing_period_highlights = assert_no_difference -> { PeriodHighlights.count } do - @user.generate_weekly_highlights + assert_match /logo/i, period_highlights.to_html end + end - assert_equal new_period_highlights, existing_period_highlights + test "don't generate highlights for existing periods" do + stub_const(PeriodHighlights::Period, :MIN_EVENTS_TO_BE_INTERESTING, 3) do + new_period_highlights = @user.generate_weekly_highlights + assert_not_nil new_period_highlights + + existing_period_highlights = assert_no_difference -> { PeriodHighlights.count } do + @user.generate_weekly_highlights + end + + assert_equal new_period_highlights, existing_period_highlights + end end end end diff --git a/test/models/user/signal_user_test.rb b/test/models/user/signal_user_test.rb index 81071517b..1931950ab 100644 --- a/test/models/user/signal_user_test.rb +++ b/test/models/user/signal_user_test.rb @@ -1,12 +1,14 @@ require "test_helper" class User::SignalUserTest < ActiveSupport::TestCase - setup do - @user = users(:david) - end + unless Rails.application.config.x.local_authentication + setup do + @user = users(:david) + end - test "belongs to a Signal::User" do - assert_not_nil @user.signal_user_id - assert_equal signal_users("37s_fizzy_david"), @user.signal_user + test "belongs to a Signal::User" do + assert_not_nil @user.signal_user_id + assert_equal signal_users("37s_fizzy_david"), @user.signal_user + end end end diff --git a/test/models/user_test.rb b/test/models/user_test.rb index d10fc73bc..f2fbf3945 100644 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -31,7 +31,9 @@ class UserTest < ActiveSupport::TestCase end end end - assert_nil users(:jz).reload.signal_user + unless Rails.application.config.x.local_authentication + assert_nil users(:jz).reload.signal_user + end end test "initials" do diff --git a/test/test_helper.rb b/test/test_helper.rb index 3941b8829..0a03b2407 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -6,8 +6,10 @@ Rails.application.config.active_record_tenanted.default_tenant = ActiveRecord::F require "rails/test_help" require "webmock/minitest" require "vcr" -require "signal_id/testing" -require "queenbee/testing/mocks" +unless Rails.application.config.x.local_authentication + require "signal_id/testing" + require "queenbee/testing/mocks" +end require "mocha/minitest" WebMock.allow_net_connect! @@ -47,18 +49,10 @@ module ActiveSupport fixtures :all include ActiveJob::TestHelper - include SignalId::Testing - include ActionTextTestHelper, CardTestHelper, ChangeTestHelper, SessionTestHelper - - def with_local_auth - begin - old_local_auth = Rails.application.config.x.local_authentication - Rails.application.config.x.local_authentication = true - yield - ensure - Rails.application.config.x.local_authentication = old_local_auth - end + unless Rails.application.config.x.local_authentication + include SignalId::Testing end + include ActionTextTestHelper, CardTestHelper, ChangeTestHelper, SessionTestHelper end end @@ -78,10 +72,12 @@ RubyLLM.configure do |config| config.openai_api_key ||= "DUMMY-TEST-KEY" # Run tests with VCR without having to configure OpenAI API key locally. end -Queenbee::Remote::Account.class_eval do - # because we use the account ID as the tenant name, we need it to be unique in each test to avoid - # parallelized tests clobbering each other. - def next_id - super + Random.rand(1000000) +unless Rails.application.config.x.local_authentication + Queenbee::Remote::Account.class_eval do + # because we use the account ID as the tenant name, we need it to be unique in each test to avoid + # parallelized tests clobbering each other. + def next_id + super + Random.rand(1000000) + end end end diff --git a/test/test_helpers/session_test_helper.rb b/test/test_helpers/session_test_helper.rb index bff7d8e90..dfbe465f4 100644 --- a/test/test_helpers/session_test_helper.rb +++ b/test/test_helpers/session_test_helper.rb @@ -6,7 +6,12 @@ module SessionTestHelper def sign_in_as(user) cookies.delete :session_token user = users(user) unless user.is_a? User - put session_launchpad_path, params: { sig: user.signal_user.perishable_signature } + + if Rails.application.config.x.local_authentication + post session_path, params: { email_address: user.email_address, password: "secret123456" } + else + put session_launchpad_path, params: { sig: user.signal_user.perishable_signature } + end cookie = cookies.get_cookie "session_token" assert_not_nil cookie, "Expected session_token cookie to be set after sign in"