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`.
This commit is contained in:
Mike Dalessio
2025-09-11 19:25:05 -04:00
parent 424edb6291
commit 8f39c015ea
24 changed files with 447 additions and 419 deletions
+2 -1
View File
@@ -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
+3 -3
View File
@@ -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)
+7 -7
View File
@@ -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
+3 -1
View File
@@ -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
+3 -1
View File
@@ -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
+4 -3
View File
@@ -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"
+1
View File
@@ -0,0 +1 @@
Rails.application.config.x.local_authentication = ENV["LOCAL_AUTHENTICATION"].present?
+10 -8
View File
@@ -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
+17 -19
View File
@@ -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
+3 -1
View File
@@ -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"
@@ -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
@@ -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
+37 -43
View File
@@ -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
@@ -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
+2
View File
@@ -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 %>
+6
View File
@@ -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
+29 -27
View File
@@ -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
+19 -16
View File
@@ -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
+111 -109
View File
@@ -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
+24 -21
View File
@@ -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
+8 -6
View File
@@ -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
+3 -1
View File
@@ -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
+14 -18
View File
@@ -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
+6 -1
View File
@@ -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"