From 1cc3e9f0a1bb99e84486c20dab9aa9baef414b9c Mon Sep 17 00:00:00 2001 From: Matthew Rider Date: Wed, 28 Aug 2024 10:12:21 +0200 Subject: [PATCH] Add many-to-many relationship between users and organizations (#1971) ### What changed in this PR and why This introduces the many-to-many relationship that is a requirement for some of our clients to belong to more than one organization. Instead of a simply HABTM, it uses `has_many, through:`. This gives us more flexibility to save information on the relationship itself. For example, how do we handle active/inactive state. One idea would be to add a `deactivated_at` in the `users_organizations` table. ### TODOs - [ ] Add automated tests that test for a user that belongs to multiple organizations - [x] Decide on the deactivated/reactivated logic - [x] When an admin is creating a new user, try to find the user by their email first and do something - ie either simply add the user to the organization, or throw a validation error and direct the admin to edit the existing user. EDIT: The email is already validated for uniqueness by the clearance gem. An error is displayed if someone tries to create a user with the same email. --- app/controllers/admin/users_controller.rb | 1 - app/controllers/application_controller.rb | 17 +++++++++-- app/controllers/onboarding_controller.rb | 2 +- app/controllers/organizations_controller.rb | 10 ++++--- app/controllers/otp_auth_controller.rb | 5 ++-- app/controllers/otp_setup_controller.rb | 6 ++-- app/controllers/passwords_controller.rb | 2 +- app/controllers/profile_controller.rb | 2 +- app/controllers/sessions_controller.rb | 3 +- app/controllers/threema/webhook_controller.rb | 2 +- .../three_sixty_dialog_webhook_controller.rb | 7 +---- .../whats_app/webhook_controller.rb | 2 +- app/dashboards/user_dashboard.rb | 4 ++- app/models/organization.rb | 3 +- app/models/user.rb | 11 ++++++-- app/models/users_organization.rb | 6 ++++ ...18_migrate_users_to_users_organizations.rb | 13 +++++++++ db/data_schema.rb | 2 +- ...240812102032_create_users_organizations.rb | 12 ++++++++ db/schema.rb | 13 ++++++++- db/seeds/multi_tenancy.rb | 2 +- spec/factories/organizations.rb | 2 +- spec/factories/users_organizations.rb | 8 ++++++ ..._inactive_contributor_inactive_job_spec.rb | 4 +-- spec/jobs/resubscribe_contributor_job_spec.rb | 2 +- spec/models/contributor_spec.rb | 27 +++++------------- spec/models/message_spec.rb | 2 +- spec/models/request_spec.rb | 2 +- spec/models/user_spec.rb | 4 +-- spec/models/users_organization_spec.rb | 7 +++++ spec/requests/charts_spec.rb | 2 +- spec/requests/contributors_spec.rb | 15 +++++----- spec/requests/dashboard_spec.rb | 28 +++++++++++++++++++ spec/requests/invites_spec.rb | 2 +- spec/requests/messages_request_spec.rb | 18 ++++++------ spec/requests/messages_spec.rb | 6 ++-- spec/requests/otp_auth_request_spec.rb | 6 ++-- spec/requests/profile_spec.rb | 6 ++-- spec/requests/requests_spec.rb | 8 +++--- spec/requests/search_request_spec.rb | 2 +- spec/requests/sessions_request_spec.rb | 7 +++-- spec/requests/telegram/webhook_spec.rb | 5 ++-- spec/requests/threema/webhook_spec.rb | 7 +++-- .../shared_examples/activity_notifications.rb | 4 +-- .../contributor_resubscribes.rb | 2 +- .../contributor_unsubscribes.rb | 2 +- spec/system/admin/users_spec.rb | 8 ++++-- spec/system/auth/otp_setup_spec.rb | 2 +- spec/system/auth/password_reset_spec.rb | 2 +- spec/system/auth/sign_in_spec.rb | 2 +- .../system/contributors/conversations_spec.rb | 2 +- spec/system/contributors/filter_spec.rb | 2 +- .../contributors/profile_picture_spec.rb | 2 +- .../dashboard/activity_notifications_spec.rb | 4 +-- spec/system/dashboard/index_spec.rb | 3 +- .../system/requests/deleting_requests_spec.rb | 2 +- spec/system/requests/editing_requests_spec.rb | 2 +- spec/system/requests/personalization_spec.rb | 2 +- .../requests/scheduling_requests_spec.rb | 2 +- spec/system/requests/sending_images_spec.rb | 2 +- spec/system/settings/image_uploads_spec.rb | 2 +- spec/system/settings/settings_form_spec.rb | 2 +- 62 files changed, 217 insertions(+), 127 deletions(-) create mode 100644 app/models/users_organization.rb create mode 100644 db/data/20240812102718_migrate_users_to_users_organizations.rb create mode 100644 db/migrate/20240812102032_create_users_organizations.rb create mode 100644 spec/factories/users_organizations.rb create mode 100644 spec/models/users_organization_spec.rb create mode 100644 spec/requests/dashboard_spec.rb diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 505c9f7c3..056a1ddcd 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -4,7 +4,6 @@ module Admin class UsersController < Admin::ApplicationController def create user = User.new(resource_params) - user.organization = Organization.last unless user.admin? if user.save redirect_to admin_users_path(user), flash: { success: 'User was successfully created.' } diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6d308d613..c1a6af20a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,8 +2,7 @@ class ApplicationController < ActionController::Base include Clearance::Controller - before_action :require_login, :require_otp_setup - before_action :set_organization + before_action :require_login, :require_otp_setup, :set_organization, :user_permitted? def require_otp_setup redirect_to otp_setup_path if signed_in? && !current_user.otp_enabled? @@ -14,7 +13,7 @@ def sign_in(user) super(user) do |status| if status.success? - redirect_to organizations_path + redirect_to redirect_path else flash.now.alert = status.failure_message render template: 'sessions/new', status: :unauthorized @@ -34,9 +33,21 @@ def delete_otp_session_variables private + def user_permitted? + raise ActionController::RoutingError, 'Not Found' unless current_user.admin? || @organization.in?(current_user.organizations) + end + def set_organization @organization = Organization.find(params[:organization_id]) if params[:organization_id].present? rescue ActiveRecord::RecordNotFound raise ActionController::RoutingError, 'Not Found' end + + def redirect_path + if current_user.admin? || current_user.organizations.count > 1 + organizations_path + else + organization_dashboard_path(current_user.organizations.first) + end + end end diff --git a/app/controllers/onboarding_controller.rb b/app/controllers/onboarding_controller.rb index 5fe449024..fabbe0762 100644 --- a/app/controllers/onboarding_controller.rb +++ b/app/controllers/onboarding_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class OnboardingController < ApplicationController - skip_before_action :require_login + skip_before_action :require_login, :user_permitted? before_action :ensure_onboarding_allowed before_action :verify_jwt, except: :success before_action :resume_telegram_onboarding, only: %i[index show] diff --git a/app/controllers/organizations_controller.rb b/app/controllers/organizations_controller.rb index ea30ba90b..4e0244e07 100644 --- a/app/controllers/organizations_controller.rb +++ b/app/controllers/organizations_controller.rb @@ -1,13 +1,15 @@ # frozen_string_literal: true class OrganizationsController < ApplicationController - skip_before_action :set_organization + skip_before_action :user_permitted?, :set_organization layout 'minimal' def index - # TODO: change this when having a organization habtm user relation - redirect_to organization_dashboard_path(current_user.organization) if current_user.organization.present? - @organizations = Organization.all end + + def set_organization + organization = Organization.find(params[:organization_id]) + redirect_to organization_dashboard_path(organization) + end end diff --git a/app/controllers/otp_auth_controller.rb b/app/controllers/otp_auth_controller.rb index 793270cad..7602ce909 100644 --- a/app/controllers/otp_auth_controller.rb +++ b/app/controllers/otp_auth_controller.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true class OtpAuthController < ApplicationController - skip_before_action :require_login - skip_before_action :require_otp_setup + skip_before_action :require_login, :require_otp_setup, :user_permitted?, :set_organization before_action :redirect_if_signed_in, :redirect_unless_user_set, :reset_when_inactive layout 'minimal' @@ -36,6 +35,6 @@ def reset_when_inactive end def redirect_if_signed_in - redirect_to organizations_path if signed_in? + redirect_to redirect_path if signed_in? end end diff --git a/app/controllers/otp_setup_controller.rb b/app/controllers/otp_setup_controller.rb index 741b6925e..44a2078f9 100644 --- a/app/controllers/otp_setup_controller.rb +++ b/app/controllers/otp_setup_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class OtpSetupController < ApplicationController - skip_before_action :require_otp_setup + skip_before_action :require_otp_setup, :user_permitted?, :set_organization before_action :redirect_if_set_up layout 'minimal' @@ -14,7 +14,7 @@ def create current_user.save! session[:otp_verified_for_user] = current_user.id - redirect_back_or organizations_path + redirect_back_or redirect_path else flash.now[:error] = I18n.t('sessions.errors.otp_incorrect') render :show, status: :unauthorized @@ -24,7 +24,7 @@ def create private def redirect_if_set_up - redirect_to organizations_path if current_user.otp_enabled? + redirect_to redirect_path if current_user.otp_enabled? end def otp_params diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb index 563ae7e59..21c6faeb3 100644 --- a/app/controllers/passwords_controller.rb +++ b/app/controllers/passwords_controller.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class PasswordsController < Clearance::PasswordsController - skip_before_action :require_login + skip_before_action :require_login, :user_permitted?, :set_organization end diff --git a/app/controllers/profile_controller.rb b/app/controllers/profile_controller.rb index bfc7d67ce..330c9d932 100644 --- a/app/controllers/profile_controller.rb +++ b/app/controllers/profile_controller.rb @@ -7,7 +7,7 @@ def index; end def create_user password = SecureRandom.alphanumeric(20) - user = User.new(user_params.merge(password: password, organization: @organization)) + user = User.new(user_params.merge(password: password, organizations: [@organization])) if user.save redirect_to organization_profile_path(@organization), flash: { success: I18n.t('profile.user.created_successfully') } else diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 10488ba0f..c982ec9ee 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true class SessionsController < Clearance::SessionsController - skip_before_action :require_login - skip_before_action :require_otp_setup + skip_before_action :require_login, :require_otp_setup, :user_permitted?, :set_organization def create user = authenticate(params) diff --git a/app/controllers/threema/webhook_controller.rb b/app/controllers/threema/webhook_controller.rb index a55ae50c5..dc45256aa 100644 --- a/app/controllers/threema/webhook_controller.rb +++ b/app/controllers/threema/webhook_controller.rb @@ -3,7 +3,7 @@ require 'openssl' class Threema::WebhookController < ApplicationController - skip_before_action :require_login, :verify_authenticity_token + skip_before_action :require_login, :verify_authenticity_token, :user_permitted?, :set_organization attr_reader :adapter diff --git a/app/controllers/whats_app/three_sixty_dialog_webhook_controller.rb b/app/controllers/whats_app/three_sixty_dialog_webhook_controller.rb index 7a130cc64..7998a17aa 100644 --- a/app/controllers/whats_app/three_sixty_dialog_webhook_controller.rb +++ b/app/controllers/whats_app/three_sixty_dialog_webhook_controller.rb @@ -4,8 +4,7 @@ module WhatsApp class ThreeSixtyDialogWebhookController < ApplicationController include WhatsAppHandleCallbacks - skip_before_action :require_login, :verify_authenticity_token - before_action :organization + skip_before_action :require_login, :verify_authenticity_token, :user_permitted? # rubocop:disable Metrics/AbcSize def message @@ -57,10 +56,6 @@ def create_api_key private - def organization - @organization ||= Organization.find(params['organization_id']) - end - def message_params params.permit({ three_sixty_dialog_webhook: [contacts: [:wa_id, { profile: [:name] }], diff --git a/app/controllers/whats_app/webhook_controller.rb b/app/controllers/whats_app/webhook_controller.rb index c8d66a83a..6e388708f 100644 --- a/app/controllers/whats_app/webhook_controller.rb +++ b/app/controllers/whats_app/webhook_controller.rb @@ -6,7 +6,7 @@ module WhatsApp class WebhookController < ApplicationController include WhatsAppHandleCallbacks - skip_before_action :require_login, :verify_authenticity_token + skip_before_action :require_login, :verify_authenticity_token, :user_permitted? before_action :set_organization, only: :status before_action :set_contributor, only: :status diff --git a/app/dashboards/user_dashboard.rb b/app/dashboards/user_dashboard.rb index 3f28f85da..0a32a0386 100644 --- a/app/dashboards/user_dashboard.rb +++ b/app/dashboards/user_dashboard.rb @@ -13,7 +13,8 @@ class UserDashboard < Administrate::BaseDashboard created_at: Field::DateTime, updated_at: Field::DateTime, deactivated_at: Field::DateTime, - active: Field::Boolean + active: Field::Boolean, + organizations: Field::HasMany }.freeze COLLECTION_ATTRIBUTES = %i[ @@ -45,6 +46,7 @@ class UserDashboard < Administrate::BaseDashboard admin otp_enabled active + organizations ].freeze COLLECTION_FILTERS = {}.freeze diff --git a/app/models/organization.rb b/app/models/organization.rb index d77480494..f953b8fba 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -9,7 +9,8 @@ class Organization < ApplicationRecord belongs_to :business_plan belongs_to :contact_person, class_name: 'User', optional: true - has_many :users, class_name: 'User', dependent: :destroy + has_many :users_organizations, dependent: :destroy + has_many :users, through: :users_organizations has_many :contributors, dependent: :destroy has_many :requests, dependent: :destroy has_many :notifications_as_mentioned, class_name: 'ActivityNotification', dependent: :destroy diff --git a/app/models/user.rb b/app/models/user.rb index e7b64e1c1..c07379429 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -9,7 +9,8 @@ class User < ApplicationRecord dependent: :destroy has_many :notifications_as_mentioned, class_name: 'ActivityNotification', dependent: :destroy has_many :messages, as: :sender, dependent: :destroy - belongs_to :organization, optional: true + has_many :users_organizations, dependent: :destroy + has_many :organizations, through: :users_organizations has_one_time_password validates :password, length: { in: 8..128 }, unless: :skip_password_validation? @@ -48,10 +49,14 @@ def active=(value) private def notify_admin - return unless organization && User.admin(false).count > organization.business_plan.number_of_users + return unless organizations.any? { |organization| organization.users.admin(false).count > organization.business_plan.number_of_users } User.admin.find_each do |admin| - PostmarkAdapter::Outbound.send_user_count_exceeds_plan_limit_message!(admin, organization) + organizations.each do |organization| + next unless organization.users.admin(false).count > organization.business_plan.number_of_users + + PostmarkAdapter::Outbound.send_user_count_exceeds_plan_limit_message!(admin, organization) + end end end diff --git a/app/models/users_organization.rb b/app/models/users_organization.rb new file mode 100644 index 000000000..5803853c3 --- /dev/null +++ b/app/models/users_organization.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class UsersOrganization < ApplicationRecord + belongs_to :user + belongs_to :organization +end diff --git a/db/data/20240812102718_migrate_users_to_users_organizations.rb b/db/data/20240812102718_migrate_users_to_users_organizations.rb new file mode 100644 index 000000000..d049bedc2 --- /dev/null +++ b/db/data/20240812102718_migrate_users_to_users_organizations.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class MigrateUsersToUsersOrganizations < ActiveRecord::Migration[6.1] + def up + User.admin(false).find_each do |user| + UsersOrganization.create!(user_id: user.id, organization_id: user.organization_id) + end + end + + def down + UsersOrganization.destroy_all + end +end diff --git a/db/data_schema.rb b/db/data_schema.rb index 05ab7451c..1f484b632 100644 --- a/db/data_schema.rb +++ b/db/data_schema.rb @@ -1,3 +1,3 @@ # frozen_string_literal: true -DataMigrate::Data.define(version: 20_240_730_085_839) +DataMigrate::Data.define(version: 20_240_812_102_718) diff --git a/db/migrate/20240812102032_create_users_organizations.rb b/db/migrate/20240812102032_create_users_organizations.rb new file mode 100644 index 000000000..c1408fba1 --- /dev/null +++ b/db/migrate/20240812102032_create_users_organizations.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateUsersOrganizations < ActiveRecord::Migration[6.1] + def change + create_table :users_organizations do |t| + t.references :user, null: false, foreign_key: true + t.references :organization, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 4d882cf03..31e50d632 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2024_07_26_065204) do +ActiveRecord::Schema.define(version: 2024_08_12_102032) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" @@ -336,6 +336,15 @@ t.index ["remember_token"], name: "index_users_on_remember_token" end + create_table "users_organizations", force: :cascade do |t| + t.bigint "user_id", null: false + t.bigint "organization_id", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["organization_id"], name: "index_users_organizations_on_organization_id" + t.index ["user_id"], name: "index_users_organizations_on_user_id" + end + add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "activity_notifications", "contributors" @@ -356,4 +365,6 @@ add_foreign_key "requests", "users" add_foreign_key "taggings", "tags" add_foreign_key "users", "organizations" + add_foreign_key "users_organizations", "organizations" + add_foreign_key "users_organizations", "users" end diff --git a/db/seeds/multi_tenancy.rb b/db/seeds/multi_tenancy.rb index 684a0285a..3b5f66bcb 100644 --- a/db/seeds/multi_tenancy.rb +++ b/db/seeds/multi_tenancy.rb @@ -15,7 +15,7 @@ ) end users = 10.times.collect do - FactoryBot.create(:user, organization: organizations.sample) + FactoryBot.create(:user, organizations: [organizations.sample]) end # images = 10.times.map { URI(Faker::Avatar.image(size: '50x50', format: 'png', set: 'set5')) } diff --git a/spec/factories/organizations.rb b/spec/factories/organizations.rb index 355e7d1b2..6bacf686a 100644 --- a/spec/factories/organizations.rb +++ b/spec/factories/organizations.rb @@ -22,7 +22,7 @@ after(:create) do |org, evaluator| if evaluator.users_count.positive? - org.users << create_list(:user, evaluator.users_count, organization: org) + org.users << create_list(:user, evaluator.users_count, organizations: [org]) org.save! end end diff --git a/spec/factories/users_organizations.rb b/spec/factories/users_organizations.rb new file mode 100644 index 000000000..6a9c832a2 --- /dev/null +++ b/spec/factories/users_organizations.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :users_organization do + user { nil } + organization { nil } + end +end diff --git a/spec/jobs/mark_inactive_contributor_inactive_job_spec.rb b/spec/jobs/mark_inactive_contributor_inactive_job_spec.rb index ee4b9f589..484686c80 100644 --- a/spec/jobs/mark_inactive_contributor_inactive_job_spec.rb +++ b/spec/jobs/mark_inactive_contributor_inactive_job_spec.rb @@ -34,9 +34,7 @@ context 'that does belong to the organization' do before do contributor.update(organization_id: organization.id) - non_admin_user.update(organization_id: organization.id) - organization.users << non_admin_user - organization.save! + non_admin_user.update(organizations: [organization]) end it { is_expected.to change { contributor.reload.deactivated_at }.from(nil).to(kind_of(ActiveSupport::TimeWithZone)) } diff --git a/spec/jobs/resubscribe_contributor_job_spec.rb b/spec/jobs/resubscribe_contributor_job_spec.rb index a9d57952f..566926bef 100644 --- a/spec/jobs/resubscribe_contributor_job_spec.rb +++ b/spec/jobs/resubscribe_contributor_job_spec.rb @@ -4,7 +4,7 @@ RSpec.describe ResubscribeContributorJob do describe '#perform_later(contributor_id, adapter)' do - let(:user) { create(:user, organization: organization) } + let(:user) { create(:user, organizations: [organization]) } let(:organization) { create(:organization) } subject { -> { described_class.new.perform(organization.id, contributor.id, adapter) } } diff --git a/spec/models/contributor_spec.rb b/spec/models/contributor_spec.rb index e1c48e583..8e4121433 100644 --- a/spec/models/contributor_spec.rb +++ b/spec/models/contributor_spec.rb @@ -8,7 +8,7 @@ organization: organization, user: user) end let(:organization) { create(:organization) } - let(:user) { create(:user) } + let!(:user) { create(:user, organizations: [organization]) } let(:contributor) { create(:contributor, email: 'contributor@example.org', organization: organization) } it 'is sorted in alphabetical order' do @@ -428,7 +428,7 @@ context 'ActivityNotifications' do let!(:admin) { create(:user, admin: true) } - it_behaves_like 'an ActivityNotification', 'MessageReceived', 3 + it_behaves_like 'an ActivityNotification', 'MessageReceived', 4 end end end @@ -468,7 +468,7 @@ context 'ActivityNotifications' do let!(:admin) { create(:user, admin: true) } - it_behaves_like 'an ActivityNotification', 'MessageReceived', 3 + it_behaves_like 'an ActivityNotification', 'MessageReceived', 4 end end end @@ -521,7 +521,7 @@ context 'ActivityNotifications' do let!(:admin) { create(:user, admin: true) } - it_behaves_like 'an ActivityNotification', 'MessageReceived', 3 + it_behaves_like 'an ActivityNotification', 'MessageReceived', 4 end end end @@ -571,7 +571,7 @@ context 'ActivityNotifications' do let!(:admin) { create(:user, admin: true) } - it_behaves_like 'an ActivityNotification', 'MessageReceived', 3 + it_behaves_like 'an ActivityNotification', 'MessageReceived', 4 end end @@ -951,21 +951,8 @@ describe '#after_create_commit' do subject { create(:contributor, organization: organization) } - before do - users = create_list(:user, 5, organization: organization) - organization.update(users: users) - create(:user, admin: true) - end - - it 'behaves like an ActivityNotification' do - expect { subject }.to change(ActivityNotification.where(type: 'OnboardingCompleted'), :count).by(6) - end + before { create(:user, admin: true) } - it 'for each user' do - subject - recipient_ids = ActivityNotification.where(type: 'OnboardingCompleted').pluck(:recipient_id).uniq.sort - user_ids = User.pluck(:id).sort - expect(recipient_ids).to eq(user_ids) - end + it_behaves_like 'an ActivityNotification', 'OnboardingCompleted', 2 end end diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb index b143a84ec..8af424e96 100644 --- a/spec/models/message_spec.rb +++ b/spec/models/message_spec.rb @@ -132,7 +132,7 @@ before do Contributor.skip_callback(:commit, :after, :notify_recipient, raise: false) - organization.update!(users: create_list(:user, 5, organization: organization)) + organization.update!(users: create_list(:user, 5, organizations: [organization])) end after do diff --git a/spec/models/request_spec.rb b/spec/models/request_spec.rb index 80eaebdb7..579f28b9d 100644 --- a/spec/models/request_spec.rb +++ b/spec/models/request_spec.rb @@ -5,7 +5,7 @@ RSpec.describe Request, type: :model do let(:organization) { create(:organization) } let(:contributor) { create(:contributor, organization: organization) } - let(:user) { create(:user, organization: organization) } + let(:user) { create(:user, organizations: [organization]) } let(:request) do Request.new( diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index bc05e1ae0..b5304f27a 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -4,7 +4,7 @@ RSpec.describe User do let(:organization) { create(:organization, business_plan_name: 'Editorial pro') } - let!(:users) { create_list(:user, 3, organization: organization) } + let!(:users) { create_list(:user, 3, organizations: [organization]) } describe 'validations' do describe '#email' do @@ -17,7 +17,7 @@ end describe '#notify_admin' do - subject { create(:user, organization: organization) } + subject { create(:user, organizations: [organization]) } context 'non-admin' do it 'does not schedule a job' do diff --git a/spec/models/users_organization_spec.rb b/spec/models/users_organization_spec.rb new file mode 100644 index 000000000..18fec4a6f --- /dev/null +++ b/spec/models/users_organization_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe UsersOrganization, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/requests/charts_spec.rb b/spec/requests/charts_spec.rb index 07a665b2f..356f250b6 100644 --- a/spec/requests/charts_spec.rb +++ b/spec/requests/charts_spec.rb @@ -4,7 +4,7 @@ RSpec.describe 'Charts' do let(:organization) { create(:organization) } - let(:user) { create(:user, organization: organization) } + let!(:user) { create(:user, organizations: [organization]) } let(:last_friday_midnight) { Time.zone.today.beginning_of_day.prev_occurring(:friday) } let(:request) { create(:request, broadcasted_at: last_friday_midnight, organization: organization) } let(:message) { create(:message, created_at: last_friday_midnight, request: request) } diff --git a/spec/requests/contributors_spec.rb b/spec/requests/contributors_spec.rb index a848a8d34..86c6790d7 100644 --- a/spec/requests/contributors_spec.rb +++ b/spec/requests/contributors_spec.rb @@ -5,9 +5,9 @@ RSpec.describe '/{organization_id}/contributors', type: :request do let(:organization) { create(:organization) } - let!(:contributor) { create(:contributor) } + let!(:contributor) { create(:contributor, organization: organization) } let(:the_request) { create(:request, organization: organization) } - let(:user) { create(:user) } + let(:user) { create(:user, organizations: [organization]) } describe 'GET /index' do it 'should be successful' do @@ -85,7 +85,7 @@ let(:updated_attrs) do { tag_list: 'ops' } end - let(:contributor) { create(:contributor, tag_list: %w[dev ops]) } + let(:contributor) { create(:contributor, tag_list: %w[dev ops], organization: organization) } it 'is supported' do patch organization_contributor_url(organization, id: contributor.id, as: user), params: { contributor: updated_attrs } @@ -106,7 +106,9 @@ end context 'given a manually created contributor' do - let(:contributor) { create(:contributor, :skip_validations, data_processing_consent: false, first_name: 'John') } + let(:contributor) do + create(:contributor, :skip_validations, data_processing_consent: false, first_name: 'John', organization: organization) + end it 'updates contributor' do expect { subject.call }.to(change { contributor.reload.first_name }.from('John').to('Zora')) @@ -133,7 +135,7 @@ context 'given a Threema contributor' do let(:threema) { instance_double(Threema) } let(:threema_lookup_double) { instance_double(Threema::Lookup) } - let(:contributor) { create(:contributor, :skip_validations, threema_id: 'VALID123') } + let(:contributor) { create(:contributor, :skip_validations, threema_id: 'VALID123', organization: organization) } let(:new_attrs) { { threema_id: 'INVALID!' } } before do @@ -195,9 +197,6 @@ end describe 'given a contributor' do - let(:params) { {} } - let(:contributor) { create(:contributor, **params) } - describe 'response' do before(:each) { subject.call } it { expect(response).to have_http_status(:bad_request) } diff --git a/spec/requests/dashboard_spec.rb b/spec/requests/dashboard_spec.rb new file mode 100644 index 000000000..37ee9cb5c --- /dev/null +++ b/spec/requests/dashboard_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe '/dashboard' do + let(:user) { create(:user, organizations: [organization]) } + let(:organization) { create(:organization) } + + describe 'GET /index' do + describe 'Permissions' do + describe 'Not part of organization' do + before { user.update(organizations: [create(:organization)]) } + + it 'should return not found' do + get organization_dashboard_url(organization, as: user) + expect(response).to have_http_status(:not_found) + end + end + + describe 'Part of organization' do + it 'should be successful' do + get organization_dashboard_url(organization, as: user) + expect(response).to be_successful + end + end + end + end +end diff --git a/spec/requests/invites_spec.rb b/spec/requests/invites_spec.rb index ae88b8139..5303bb430 100644 --- a/spec/requests/invites_spec.rb +++ b/spec/requests/invites_spec.rb @@ -4,7 +4,7 @@ RSpec.describe 'Invites', type: :request do let(:organization) { create(:organization) } - let(:user) { create(:user, organization: organization) } + let(:user) { create(:user, organizations: [organization]) } describe 'POST /invites' do subject { -> { post organization_invites_path(organization, as: user) } } diff --git a/spec/requests/messages_request_spec.rb b/spec/requests/messages_request_spec.rb index 526ab5165..c1f741d90 100644 --- a/spec/requests/messages_request_spec.rb +++ b/spec/requests/messages_request_spec.rb @@ -3,9 +3,12 @@ require 'rails_helper' RSpec.describe 'Messages', type: :request do + let(:organization) { create(:organization) } + let(:request) { create(:request, organization: organization) } + describe 'PATCH /messages/:id/highlight' do let(:params) { {} } - let(:user) { create(:user) } + let(:user) { create(:user, organizations: [organization]) } subject do lambda do @@ -15,7 +18,7 @@ end describe 'given an non-highlighted message' do - let(:message) { create(:message, highlighted: false) } + let(:message) { create(:message, highlighted: false, request: request) } describe 'given highlighted=true' do let(:params) { { highlighted: true } } @@ -29,7 +32,7 @@ end describe 'given a highlighted message' do - let(:message) { create(:message, highlighted: true) } + let(:message) { create(:message, highlighted: true, request: request) } describe 'given highlighted=true' do let(:params) { { highlighted: true } } @@ -44,13 +47,13 @@ end describe 'GET /request' do - let(:user) { create(:user) } - let(:contributor) { create(:contributor, first_name: 'Zora', last_name: 'Zimmermann') } + let(:user) { create(:user, organizations: [message.organization]) } + let(:contributor) { create(:contributor, first_name: 'Zora', last_name: 'Zimmermann', organization: organization) } before(:each) { get(organization_message_request_url(message.organization, message, as: user)) } context 'given an inbound message' do - let(:message) { create(:message, sender: contributor, recipient: nil) } + let(:message) { create(:message, sender: contributor, recipient: nil, request: request) } it 'renders successfully' do expect(response).to be_successful @@ -69,8 +72,7 @@ end describe 'POST /request' do - let(:user) { create(:user) } - let(:request) { create(:request) } + let(:user) { create(:user, organizations: [organization]) } subject { -> { patch(organization_message_request_url(message.organization, message, as: user), params: params) } } diff --git a/spec/requests/messages_spec.rb b/spec/requests/messages_spec.rb index 3a766220d..f4ca119c1 100644 --- a/spec/requests/messages_spec.rb +++ b/spec/requests/messages_spec.rb @@ -6,7 +6,7 @@ let(:organization) { create(:organization) } let(:contributor) { create(:contributor, organization: organization) } let(:request) { create(:request, organization: organization) } - let(:user) { create(:user, organization: organization) } + let(:user) { create(:user, organizations: [organization]) } let(:message) { create(:message, request: request) } describe 'GET /new' do @@ -45,7 +45,7 @@ describe 'PATCH /message/:id' do let(:previous_text) { 'Previous text' } - let(:message) { create(:message, creator_id: user.id, text: previous_text) } + let(:message) { create(:message, creator_id: user.id, text: previous_text, request: request) } let(:new_attrs) { { text: 'Grab your coat and get your hat' } } subject { -> { patch organization_message_url(message.organization, message, as: user), params: { message: new_attrs } } } @@ -62,7 +62,7 @@ end context 'not manually created message' do - let(:message) { create(:message, creator_id: nil) } + let(:message) { create(:message, creator_id: nil, request: request) } it 'does not update the requested message' do subject.call diff --git a/spec/requests/otp_auth_request_spec.rb b/spec/requests/otp_auth_request_spec.rb index f4afae89a..fafb22cd2 100644 --- a/spec/requests/otp_auth_request_spec.rb +++ b/spec/requests/otp_auth_request_spec.rb @@ -6,14 +6,14 @@ include ActiveSupport::Testing::TimeHelpers let(:organization) { create(:organization) } - let!(:user) { create(:user, email: 'zora@example.org', password: '12345678', otp_enabled: true) } + let!(:user) { create(:user, email: 'zora@example.org', password: '12345678', otp_enabled: true, organizations: [organization]) } describe 'GET /otp_auth' do before(:each) { get otp_auth_path(as: user) } subject { response } context 'if user is already signed in' do - it { should redirect_to(organizations_path) } + it { should redirect_to(organization_dashboard_path(organization)) } end end @@ -42,7 +42,7 @@ context 'and correct OTP' do let(:otp_param) { user.otp_code } - it { should redirect_to(organizations_path) } + it { should redirect_to(organization_dashboard_path(organization)) } it { should have_current_user(user) } end end diff --git a/spec/requests/profile_spec.rb b/spec/requests/profile_spec.rb index 9324b5092..a28145d29 100644 --- a/spec/requests/profile_spec.rb +++ b/spec/requests/profile_spec.rb @@ -2,8 +2,8 @@ require 'rails_helper' -RSpec.describe '/contributors' do - let!(:user) { create(:user, organization: organization) } +RSpec.describe '/profile' do + let!(:user) { create(:user, organizations: [organization]) } let(:contact_person) { create(:user) } let(:organization) { create(:organization, contact_person: contact_person, business_plan_name: 'Editorial pro') } @@ -48,7 +48,7 @@ end describe 'exceeds number of users of plan' do - let!(:last_user_allocated_by_plan) { create(:user, organization: organization) } + let!(:last_user_allocated_by_plan) { create(:user, organizations: [organization]) } let!(:admin) { create(:user, admin: true) } it 'creates a user' do diff --git a/spec/requests/requests_spec.rb b/spec/requests/requests_spec.rb index 4d1afa3e2..a3be7b418 100644 --- a/spec/requests/requests_spec.rb +++ b/spec/requests/requests_spec.rb @@ -10,7 +10,7 @@ before(:each) { allow(Request).to receive(:broadcast!).and_call_original } # is stubbed for every other test subject { -> { post organization_requests_path(organization, as: user), params: params } } let(:params) { { request: { title: 'Example Question', text: 'How do you do?', hints: ['confidential'] } } } - let(:user) { create(:user) } + let(:user) { create(:user, organizations: [organization]) } it { should change { Request.count }.from(0).to(1) } @@ -113,7 +113,7 @@ } end - let(:user) { create(:user) } + let(:user) { create(:user, organizations: [organization]) } context 'broadcasted request' do let!(:request) { create(:request, organization: organization) } @@ -157,10 +157,10 @@ end describe 'GET /{organization_id}/notifications' do - let(:request) { create(:request) } + let(:request) { create(:request, organization: organization) } let!(:older_message) { create(:message, request_id: request.id, created_at: 2.minutes.ago) } let(:params) { { last_updated_at: 1.minute.ago } } - let(:user) { create(:user) } + let(:user) { create(:user, organizations: [organization]) } subject { -> { get notifications_organization_request_path(request.organization, request, as: user), params: params } } diff --git a/spec/requests/search_request_spec.rb b/spec/requests/search_request_spec.rb index 282021839..67c56c8b3 100644 --- a/spec/requests/search_request_spec.rb +++ b/spec/requests/search_request_spec.rb @@ -7,7 +7,7 @@ describe 'GET /index' do it 'returns http success' do - get organization_search_path(organization, as: create(:user)) + get organization_search_path(organization, as: create(:user, organizations: [organization])) expect(response).to have_http_status(:success) end end diff --git a/spec/requests/sessions_request_spec.rb b/spec/requests/sessions_request_spec.rb index c16382ad0..0447d3e40 100644 --- a/spec/requests/sessions_request_spec.rb +++ b/spec/requests/sessions_request_spec.rb @@ -4,13 +4,14 @@ RSpec.describe 'Sessions', type: :request do let(:organization) { create(:organization) } - let!(:user) { create(:user, email: 'zora@example.org', password: '12345678', otp_enabled: otp_enabled, organization: organization) } + let!(:user) { create(:user, email: 'zora@example.org', password: '12345678', otp_enabled: otp_enabled, organizations: [organization]) } let(:otp_enabled) { false } describe 'GET /sign_in' do - before(:each) { get sign_in_path(as: user) } + before { get sign_in_path(as: user) } subject { response } + # This redirect comes from Clearance.configuration.redirect_url and is hard-coded in clearance initializer. context 'if user is already signed-in' do it { should redirect_to(organizations_path) } end @@ -32,7 +33,7 @@ context 'with correct email and password' do let(:password_param) { '12345678' } - it { should redirect_to(organizations_path) } + it { should redirect_to(organization_dashboard_path(organization)) } it { should have_current_user(user) } end end diff --git a/spec/requests/telegram/webhook_spec.rb b/spec/requests/telegram/webhook_spec.rb index 921b14cf6..7fe119f57 100644 --- a/spec/requests/telegram/webhook_spec.rb +++ b/spec/requests/telegram/webhook_spec.rb @@ -18,6 +18,7 @@ ) end let!(:admin) { create_list(:user, 2, admin: true) } + let!(:user) { create(:user, organizations: [organization]) } let(:bot) { organization.telegram_bot } let(:controller_path) do "/telegram/#{Telegram::Bot::RoutesHelper.token_hash(organization.telegram_bot_api_key)}" @@ -140,10 +141,10 @@ it { expect { subject.call }.not_to(change { Message.count }) } context 'given a recent request' do - before { create(:request, organization: organization, user: create(:user)) } + before { create(:request, organization: organization, user: user) } it { expect { subject.call }.to(change { Message.count }.from(0).to(1)) } it { expect { subject.call }.not_to respond_with_message } - it_behaves_like 'an ActivityNotification', 'MessageReceived', 3 + it_behaves_like 'an ActivityNotification', 'MessageReceived', 4 end context ' message has a document' do diff --git a/spec/requests/threema/webhook_spec.rb b/spec/requests/threema/webhook_spec.rb index f161513d7..91aacba33 100644 --- a/spec/requests/threema/webhook_spec.rb +++ b/spec/requests/threema/webhook_spec.rb @@ -21,6 +21,7 @@ let(:threema_lookup_double) { instance_double(Threema::Lookup) } let!(:organization) { create(:organization, threemarb_api_identity: '*100EYES', users_count: 1) } let!(:admin) { create_list(:user, 2, admin: true) } + let!(:user) { create(:user, organizations: [organization]) } before do allow(Threema).to receive(:new).and_return(threema) @@ -48,7 +49,7 @@ context 'With known contributor' do let!(:contributor) { create(:contributor, :skip_validations, threema_id: 'V5EA564T', organization: organization) } - let!(:request) { create(:request, organization: organization, user: create(:user)) } + let!(:request) { create(:request, organization: organization, user: user) } before do allow(threema_mock).to receive(:instance_of?).with(Threema::Receive::Text).and_return(true) @@ -60,7 +61,7 @@ expect { subject }.to change(Message, :count).from(0).to(1) end - it_behaves_like 'an ActivityNotification', 'MessageReceived', 3 + it_behaves_like 'an ActivityNotification', 'MessageReceived', 4 describe 'DeliveryReceipt' do let(:threema_mock) do @@ -129,7 +130,7 @@ expect { subject }.to change(Message, :count).from(0).to(1) end - it_behaves_like 'an ActivityNotification', 'MessageReceived', 3 + it_behaves_like 'an ActivityNotification', 'MessageReceived', 4 end describe 'Unsupported content' do diff --git a/spec/support/shared_examples/activity_notifications.rb b/spec/support/shared_examples/activity_notifications.rb index 4d984e891..f7c27bf9c 100644 --- a/spec/support/shared_examples/activity_notifications.rb +++ b/spec/support/shared_examples/activity_notifications.rb @@ -6,10 +6,10 @@ expect { run_action(subject) }.to change(ActivityNotification.where(type: event_type), :count).by(count) end - it 'for each user' do + it 'for each user and admin' do run_action(subject) recipient_ids = ActivityNotification.where(type: event_type).pluck(:recipient_id).uniq.sort - user_ids = organization.users.pluck(:id) + user_ids = organization.users.pluck(:id).uniq admin_ids = User.admin.pluck(:id) all_org_user_plus_admin = (user_ids + admin_ids).sort expect(recipient_ids).to eq(all_org_user_plus_admin) diff --git a/spec/support/shared_examples/contributor_resubscribes.rb b/spec/support/shared_examples/contributor_resubscribes.rb index 97cee7384..69fb8b3c3 100644 --- a/spec/support/shared_examples/contributor_resubscribes.rb +++ b/spec/support/shared_examples/contributor_resubscribes.rb @@ -3,7 +3,7 @@ RSpec.shared_examples 'a Contributor resubscribes' do |adapter| let!(:request) { create(:request, organization: organization, user: non_admin_user) } let!(:admin) { create_list(:user, 2, admin: true) } - let!(:non_admin_user) { create(:user, organization: organization) } + let!(:non_admin_user) { create(:user, organizations: [organization]) } let(:welcome_message) do organization.onboarding_success_text end diff --git a/spec/support/shared_examples/contributor_unsubscribes.rb b/spec/support/shared_examples/contributor_unsubscribes.rb index 70c41573e..55d394952 100644 --- a/spec/support/shared_examples/contributor_unsubscribes.rb +++ b/spec/support/shared_examples/contributor_unsubscribes.rb @@ -3,7 +3,7 @@ RSpec.shared_examples 'a Contributor unsubscribes' do |adapter| let!(:request) { create(:request, organization: organization, user: non_admin_user) } let!(:admin) { create_list(:user, 2, admin: true) } - let!(:non_admin_user) { create(:user, organization: organization) } + let!(:non_admin_user) { create(:user, organizations: [organization]) } let(:unsubscribe_successful_message) do [I18n.t('adapter.shared.unsubscribe.successful')].join("\n\n") end diff --git a/spec/system/admin/users_spec.rb b/spec/system/admin/users_spec.rb index c30032f55..218d30021 100644 --- a/spec/system/admin/users_spec.rb +++ b/spec/system/admin/users_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe 'Users' do - context 'as user with admin permissions' do + context 'as user with admin permissions', js: true do let(:user) { create(:user, first_name: 'Max', last_name: 'Mustermann', admin: true, password: '12345678') } let!(:organization) { create(:organization) } @@ -15,8 +15,10 @@ fill_in 'First name', with: 'Zora' fill_in 'Last name', with: 'Zimmermann' fill_in 'Email', with: 'zimmermann@example.org' + input = find('input[name="user[organization_ids][]"]', visible: false) + input.set(organization.id) click_on 'Sign up' - expect(User.find_by(email: 'zimmermann@example.org').reload.organization).to eq(organization) + expect(User.find_by(email: 'zimmermann@example.org').reload.organizations).to eq([organization]) expect(page).to have_text('User was successfully created.') end @@ -32,7 +34,7 @@ check 'Admin' click_on 'Sign up' - expect(User.find_by(email: 'new_admin@example.org').reload.organization).to eq(nil) + expect(User.find_by(email: 'new_admin@example.org').reload.organizations).to eq([]) expect(page).to have_text('User was successfully created.') end diff --git a/spec/system/auth/otp_setup_spec.rb b/spec/system/auth/otp_setup_spec.rb index 456a8700f..23a0f14e2 100644 --- a/spec/system/auth/otp_setup_spec.rb +++ b/spec/system/auth/otp_setup_spec.rb @@ -8,7 +8,7 @@ let(:password) { Faker::Internet.password(min_length: 8, max_length: 128) } let(:new_password) { Faker::Internet.password(min_length: 8, max_length: 128) } let(:otp_enabled) { true } - let(:user) { create(:user, email: email, password: password, otp_enabled: otp_enabled, organization: organization) } + let(:user) { create(:user, email: email, password: password, otp_enabled: otp_enabled, organizations: [organization]) } describe 'without 2FA set up' do let(:otp_enabled) { false } diff --git a/spec/system/auth/password_reset_spec.rb b/spec/system/auth/password_reset_spec.rb index 21d3ff49b..6a72e7931 100644 --- a/spec/system/auth/password_reset_spec.rb +++ b/spec/system/auth/password_reset_spec.rb @@ -7,7 +7,7 @@ let(:email) { Faker::Internet.email } let(:password) { Faker::Internet.password(min_length: 8, max_length: 128) } let(:new_password) { Faker::Internet.password(min_length: 8, max_length: 128) } - let!(:user) { create(:user, email: email, password: password, otp_enabled: otp_enabled, organization: organization) } + let!(:user) { create(:user, email: email, password: password, otp_enabled: otp_enabled, organizations: [organization]) } describe 'without 2FA set up' do let(:otp_enabled) { false } diff --git a/spec/system/auth/sign_in_spec.rb b/spec/system/auth/sign_in_spec.rb index ff4f5c04c..7af4dda37 100644 --- a/spec/system/auth/sign_in_spec.rb +++ b/spec/system/auth/sign_in_spec.rb @@ -7,7 +7,7 @@ let(:email) { 'zora@example.org' } let(:password) { '12345678' } let(:otp_enabled) { true } - let!(:user) { create(:user, email: email, password: password, otp_enabled: otp_enabled, organization: organization) } + let!(:user) { create(:user, email: email, password: password, otp_enabled: otp_enabled, organizations: [organization]) } it 'editor tries to visit protected page' do visit organization_dashboard_path(organization) diff --git a/spec/system/contributors/conversations_spec.rb b/spec/system/contributors/conversations_spec.rb index b456cd397..9dfcc1c0d 100644 --- a/spec/system/contributors/conversations_spec.rb +++ b/spec/system/contributors/conversations_spec.rb @@ -4,7 +4,7 @@ RSpec.describe 'Conversation interactions', js: false do let(:organization) { create(:organization) } - let(:user) { create(:user, organization: organization) } + let(:user) { create(:user, organizations: [organization]) } let(:contributor) { create(:contributor, email: 'contributor@example.org') } let(:request) { create(:request, organization: organization) } let(:first_received_message) do diff --git a/spec/system/contributors/filter_spec.rb b/spec/system/contributors/filter_spec.rb index a178b620d..9ef1f808c 100644 --- a/spec/system/contributors/filter_spec.rb +++ b/spec/system/contributors/filter_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe 'Filter contributors' do - let(:user) { create(:user) } + let(:user) { create(:user, organizations: [organization]) } let(:organization) { create(:organization) } let!(:active_contributor) { create(:contributor, tag_list: ['entwickler'], organization: organization) } let!(:inactive_contributor) { create(:contributor, :inactive, tag_list: ['entwickler'], organization: organization) } diff --git a/spec/system/contributors/profile_picture_spec.rb b/spec/system/contributors/profile_picture_spec.rb index 6098adadf..4a34eefbf 100644 --- a/spec/system/contributors/profile_picture_spec.rb +++ b/spec/system/contributors/profile_picture_spec.rb @@ -4,7 +4,7 @@ RSpec.describe 'Profile pictures' do let(:organization) { create(:organization) } - let(:user) { create(:user, organization: organization) } + let(:user) { create(:user, organizations: [organization]) } let(:contributor) { create(:contributor, organization: organization) } it 'Editor uploads new picture' do diff --git a/spec/system/dashboard/activity_notifications_spec.rb b/spec/system/dashboard/activity_notifications_spec.rb index 668fef1d5..f90b9281e 100644 --- a/spec/system/dashboard/activity_notifications_spec.rb +++ b/spec/system/dashboard/activity_notifications_spec.rb @@ -11,11 +11,11 @@ let(:otp_enabled) { true } let(:user) do create(:user, first_name: 'Johnny', last_name: 'Appleseed', email: email, password: password, otp_enabled: otp_enabled, - organization: organization) + organizations: [organization]) end let!(:coworker) do create(:user, first_name: 'Coworker', last_name: 'Extraordinaire', email: coworker_email, password: password, - otp_enabled: otp_enabled, organization: organization) + otp_enabled: otp_enabled, organizations: [organization]) end let(:request) { create(:request, user: user, organization: organization) } let(:contributor_without_avatar) { create(:contributor, organization: organization) } diff --git a/spec/system/dashboard/index_spec.rb b/spec/system/dashboard/index_spec.rb index 8d8b71539..beac183b2 100644 --- a/spec/system/dashboard/index_spec.rb +++ b/spec/system/dashboard/index_spec.rb @@ -7,9 +7,10 @@ let(:email) { Faker::Internet.email } let(:password) { Faker::Internet.password(min_length: 8, max_length: 128) } let(:otp_enabled) { true } + let(:organization) { create(:organization) } let(:user) do create(:user, first_name: 'Dennis', last_name: 'Schroeder', email: email, password: password, otp_enabled: otp_enabled, - organization: organization) + organizations: [organization]) end let(:contributor) { create(:contributor) } diff --git a/spec/system/requests/deleting_requests_spec.rb b/spec/system/requests/deleting_requests_spec.rb index c734e4f60..294adfa78 100644 --- a/spec/system/requests/deleting_requests_spec.rb +++ b/spec/system/requests/deleting_requests_spec.rb @@ -4,7 +4,7 @@ RSpec.describe 'Deleting requests' do let(:organization) { create(:organization) } - let(:user) { create(:user, organization: organization) } + let(:user) { create(:user, organizations: [organization]) } let!(:broadcasted_request) { create(:request, organization: organization, user: user) } let!(:planned_request) { create(:request, schedule_send_for: 1.hour.from_now, organization: organization, user: user) } let!(:another_planned_request) { create(:request, schedule_send_for: 5.minutes.from_now, organization: organization, user: user) } diff --git a/spec/system/requests/editing_requests_spec.rb b/spec/system/requests/editing_requests_spec.rb index f88936245..2f7bf358c 100644 --- a/spec/system/requests/editing_requests_spec.rb +++ b/spec/system/requests/editing_requests_spec.rb @@ -4,7 +4,7 @@ RSpec.describe 'Editing requests', js: true do let(:organization) { create(:organization) } - let(:user) { create(:user, organization: organization) } + let(:user) { create(:user, organizations: [organization]) } let(:sent_request) { create(:request, organization: organization) } let(:request_scheduled_in_future) { create(:request, schedule_send_for: 2.minutes.from_now, organization: organization) } diff --git a/spec/system/requests/personalization_spec.rb b/spec/system/requests/personalization_spec.rb index 800bfe20c..f12db4ca2 100644 --- a/spec/system/requests/personalization_spec.rb +++ b/spec/system/requests/personalization_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe 'Request personalization' do - let(:user) { create(:user) } + let(:user) { create(:user, organizations: [organization]) } let(:organization) { create(:organization) } context 'given two contributors' diff --git a/spec/system/requests/scheduling_requests_spec.rb b/spec/system/requests/scheduling_requests_spec.rb index c4d5204bd..5fdaddac7 100644 --- a/spec/system/requests/scheduling_requests_spec.rb +++ b/spec/system/requests/scheduling_requests_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe 'Scheduling requests', js: true do - let(:user) { create(:user) } + let(:user) { create(:user, organizations: [organization]) } let(:organization) { create(:organization) } context 'given contributors' do diff --git a/spec/system/requests/sending_images_spec.rb b/spec/system/requests/sending_images_spec.rb index ac5d92d9e..2ab9db6da 100644 --- a/spec/system/requests/sending_images_spec.rb +++ b/spec/system/requests/sending_images_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe 'Sending image files', js: true do - let(:user) { create(:user) } + let(:user) { create(:user, organizations: [organization]) } let(:organization) { create(:organization) } context 'given contributors' do diff --git a/spec/system/settings/image_uploads_spec.rb b/spec/system/settings/image_uploads_spec.rb index 0a600ab5f..5c6a3d1a5 100644 --- a/spec/system/settings/image_uploads_spec.rb +++ b/spec/system/settings/image_uploads_spec.rb @@ -4,7 +4,7 @@ RSpec.describe 'Image uploads' do let!(:organization) { create(:organization, project_name: 'Die Lokal-Community!') } - let(:user) { create(:user, organization: organization) } + let(:user) { create(:user, organizations: [organization]) } let(:jwt) { JsonWebToken.encode({ invite_code: 'ONBOARDING_TOKEN', action: 'onboarding' }) } it 'Upload new onboarding logo' do diff --git a/spec/system/settings/settings_form_spec.rb b/spec/system/settings/settings_form_spec.rb index 1f6e67263..d75c1d500 100644 --- a/spec/system/settings/settings_form_spec.rb +++ b/spec/system/settings/settings_form_spec.rb @@ -4,7 +4,7 @@ RSpec.describe 'Settings' do let(:organization) { create(:organization) } - let(:user) { create(:user, organization: organization) } + let(:user) { create(:user, organizations: [organization]) } it 'Exposes certain fields only to admin' do visit organization_settings_path(organization, as: user)