assignment_v2: import assignment policy controllers/models/services/job/policy/specs
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AssignmentPolicy, type: :model do
|
||||
let(:account) { create(:account) }
|
||||
let(:assignment_policy) { create(:assignment_policy, account: account) }
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:account) }
|
||||
it { is_expected.to have_many(:inbox_assignment_policies).dependent(:destroy) }
|
||||
it { is_expected.to have_many(:inboxes).through(:inbox_assignment_policies) }
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
subject { assignment_policy }
|
||||
|
||||
it { is_expected.to validate_presence_of(:name) }
|
||||
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:account_id) }
|
||||
it { is_expected.to validate_length_of(:name).is_at_most(255) }
|
||||
it { is_expected.to validate_length_of(:description).is_at_most(1000) }
|
||||
|
||||
it { is_expected.to validate_presence_of(:fair_distribution_limit) }
|
||||
it { is_expected.to validate_numericality_of(:fair_distribution_limit).is_greater_than(0).is_less_than_or_equal_to(100) }
|
||||
|
||||
it { is_expected.to validate_presence_of(:fair_distribution_window) }
|
||||
it { is_expected.to validate_numericality_of(:fair_distribution_window).is_greater_than(60).is_less_than_or_equal_to(86_400) }
|
||||
|
||||
context 'with balanced assignment validation' do
|
||||
let(:enterprise_account) { create(:account) }
|
||||
|
||||
before do
|
||||
allow(enterprise_account).to receive(:feature_enabled?).with(:enterprise_agent_capacity).and_return(true)
|
||||
end
|
||||
|
||||
it 'allows balanced assignment for enterprise accounts' do
|
||||
policy = build(:assignment_policy, account: enterprise_account, assignment_order: :balanced)
|
||||
expect(policy).to be_valid
|
||||
end
|
||||
|
||||
it 'rejects balanced assignment for non-enterprise accounts' do
|
||||
policy = build(:assignment_policy, account: account, assignment_order: :balanced)
|
||||
expect(policy).not_to be_valid
|
||||
expect(policy.errors[:assignment_order]).to include('Balanced assignment is only available for enterprise accounts')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'enums' do
|
||||
it { is_expected.to define_enum_for(:assignment_order).with_values(round_robin: 0, balanced: 1) }
|
||||
it { is_expected.to define_enum_for(:conversation_priority).with_values(earliest_created: 0, longest_waiting: 1) }
|
||||
end
|
||||
|
||||
describe 'scopes' do
|
||||
let!(:enabled_policy) { create(:assignment_policy, account: account, enabled: true) }
|
||||
let!(:disabled_policy) { create(:assignment_policy, account: account, enabled: false) }
|
||||
|
||||
it 'filters enabled policies' do
|
||||
expect(described_class.enabled).to include(enabled_policy)
|
||||
expect(described_class.enabled).not_to include(disabled_policy)
|
||||
end
|
||||
|
||||
it 'filters disabled policies' do
|
||||
expect(described_class.disabled).to include(disabled_policy)
|
||||
expect(described_class.disabled).not_to include(enabled_policy)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#can_use_balanced_assignment?' do
|
||||
context 'when account has enterprise agent capacity feature' do
|
||||
before do
|
||||
allow(account).to receive(:feature_enabled?).with(:enterprise_agent_capacity).and_return(true)
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(assignment_policy.can_use_balanced_assignment?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account does not have enterprise features' do
|
||||
before do
|
||||
allow(account).to receive(:feature_enabled?).with(:enterprise_agent_capacity).and_return(false)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(assignment_policy.can_use_balanced_assignment?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#webhook_data' do
|
||||
it 'returns correct data structure' do
|
||||
data = assignment_policy.webhook_data
|
||||
|
||||
expect(data).to include(
|
||||
id: assignment_policy.id,
|
||||
name: assignment_policy.name,
|
||||
description: assignment_policy.description,
|
||||
assignment_order: assignment_policy.assignment_order,
|
||||
conversation_priority: assignment_policy.conversation_priority,
|
||||
fair_distribution_limit: assignment_policy.fair_distribution_limit,
|
||||
fair_distribution_window: assignment_policy.fair_distribution_window,
|
||||
enabled: assignment_policy.enabled
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'cache invalidation' do
|
||||
let(:inbox) { create(:inbox, account: account) }
|
||||
|
||||
before { create(:inbox_assignment_policy, inbox: inbox, assignment_policy: assignment_policy) }
|
||||
|
||||
it 'clears assignment caches on update' do
|
||||
expect(Rails.cache).to receive(:delete).with("assignment_v2:policy:#{assignment_policy.id}")
|
||||
expect(Rails.cache).to receive(:delete).with("assignment_v2:inbox_policy:#{inbox.id}")
|
||||
|
||||
assignment_policy.update!(name: 'Updated Policy')
|
||||
end
|
||||
|
||||
it 'clears assignment caches on destroy' do
|
||||
expect(Rails.cache).to receive(:delete).with("assignment_v2:policy:#{assignment_policy.id}")
|
||||
expect(Rails.cache).to receive(:delete).with("assignment_v2:inbox_policy:#{inbox.id}")
|
||||
|
||||
assignment_policy.destroy!
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,165 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe InboxAssignmentPolicy, type: :model do
|
||||
let(:account) { create(:account) }
|
||||
let(:inbox) { create(:inbox, account: account) }
|
||||
let(:assignment_policy) { create(:assignment_policy, account: account) }
|
||||
let(:inbox_assignment_policy) { create(:inbox_assignment_policy, inbox: inbox, assignment_policy: assignment_policy) }
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:inbox) }
|
||||
it { is_expected.to belong_to(:assignment_policy) }
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
subject { inbox_assignment_policy }
|
||||
|
||||
it { is_expected.to validate_uniqueness_of(:inbox_id) }
|
||||
|
||||
context 'with inbox and policy from different accounts' do
|
||||
let(:other_account) { create(:account) }
|
||||
let(:other_policy) { create(:assignment_policy, account: other_account) }
|
||||
|
||||
it 'validates inbox belongs to same account as policy' do
|
||||
# Build without the factory callback that sets the accounts to be the same
|
||||
invalid_policy = described_class.new(inbox: inbox, assignment_policy: other_policy)
|
||||
|
||||
expect(invalid_policy).not_to be_valid
|
||||
expect(invalid_policy.errors[:inbox]).to include('must belong to the same account as the assignment policy')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'delegations' do
|
||||
it 'delegates account to inbox' do
|
||||
expect(inbox_assignment_policy.account).to eq(account)
|
||||
end
|
||||
|
||||
it 'delegates policy attributes' do
|
||||
expect(inbox_assignment_policy.policy_name).to eq(assignment_policy.name)
|
||||
expect(inbox_assignment_policy.policy_description).to eq(assignment_policy.description)
|
||||
expect(inbox_assignment_policy.policy_assignment_order).to eq(assignment_policy.assignment_order)
|
||||
expect(inbox_assignment_policy.policy_conversation_priority).to eq(assignment_policy.conversation_priority)
|
||||
expect(inbox_assignment_policy.policy_fair_distribution_limit).to eq(assignment_policy.fair_distribution_limit)
|
||||
expect(inbox_assignment_policy.policy_fair_distribution_window).to eq(assignment_policy.fair_distribution_window)
|
||||
expect(inbox_assignment_policy.policy_enabled?).to eq(assignment_policy.enabled?)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'scopes' do
|
||||
let!(:enabled_policy) { create(:assignment_policy, account: account, enabled: true) }
|
||||
let!(:disabled_policy) { create(:assignment_policy, account: account, enabled: false) }
|
||||
let(:inbox2) { create(:inbox, account: account) }
|
||||
let!(:enabled_inbox_policy) { create(:inbox_assignment_policy, inbox: inbox, assignment_policy: enabled_policy) }
|
||||
let!(:disabled_inbox_policy) { create(:inbox_assignment_policy, inbox: inbox2, assignment_policy: disabled_policy) }
|
||||
|
||||
describe '.enabled' do
|
||||
it 'returns only inbox policies with enabled assignment policies' do
|
||||
expect(described_class.enabled).to include(enabled_inbox_policy)
|
||||
expect(described_class.enabled).not_to include(disabled_inbox_policy)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.disabled' do
|
||||
it 'returns only inbox policies with disabled assignment policies' do
|
||||
expect(described_class.disabled).to include(disabled_inbox_policy)
|
||||
expect(described_class.disabled).not_to include(enabled_inbox_policy)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#webhook_data' do
|
||||
it 'returns correct data structure' do
|
||||
data = inbox_assignment_policy.webhook_data
|
||||
|
||||
expect(data).to include(
|
||||
id: inbox_assignment_policy.id,
|
||||
inbox_id: inbox.id,
|
||||
assignment_policy_id: assignment_policy.id
|
||||
)
|
||||
|
||||
expect(data[:policy]).to eq(assignment_policy.webhook_data)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'cache management' do
|
||||
it 'clears inbox cache on create' do
|
||||
expect(Rails.cache).to receive(:delete).with("assignment_v2:inbox_policy:#{inbox.id}")
|
||||
|
||||
create(:inbox_assignment_policy, inbox: inbox, assignment_policy: assignment_policy)
|
||||
end
|
||||
|
||||
it 'clears inbox cache on update' do
|
||||
expect(Rails.cache).to receive(:delete).with("assignment_v2:inbox_policy:#{inbox.id}").at_least(:once)
|
||||
|
||||
inbox_assignment_policy.update!(updated_at: Time.current)
|
||||
end
|
||||
|
||||
it 'clears inbox cache on destroy' do
|
||||
expect(Rails.cache).to receive(:delete).with("assignment_v2:inbox_policy:#{inbox.id}").at_least(:once)
|
||||
|
||||
inbox_assignment_policy.destroy!
|
||||
end
|
||||
|
||||
it 'updates account cache' do
|
||||
# AccountCacheRevalidator concern should trigger cache update
|
||||
expect(inbox_assignment_policy).to receive(:update_account_cache)
|
||||
|
||||
inbox_assignment_policy.send(:clear_inbox_cache)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'business logic constraints' do
|
||||
it 'prevents multiple policies per inbox' do
|
||||
# Ensure first policy exists
|
||||
inbox_assignment_policy
|
||||
|
||||
policy2 = create(:assignment_policy, account: account)
|
||||
|
||||
# Try to create a second policy for the same inbox
|
||||
duplicate_policy = described_class.new(inbox: inbox, assignment_policy: policy2)
|
||||
expect(duplicate_policy).not_to be_valid
|
||||
expect(duplicate_policy.errors[:inbox_id]).to include('has already been taken')
|
||||
end
|
||||
|
||||
it 'allows reassigning to different policy' do
|
||||
policy2 = create(:assignment_policy, account: account)
|
||||
|
||||
expect do
|
||||
inbox_assignment_policy.update!(assignment_policy: policy2)
|
||||
end.not_to raise_error
|
||||
|
||||
expect(inbox_assignment_policy.reload.assignment_policy).to eq(policy2)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'edge cases' do
|
||||
it 'handles nil associations gracefully' do
|
||||
# Build without saving to test nil handling
|
||||
policy = build(:inbox_assignment_policy, inbox: nil, assignment_policy: nil)
|
||||
|
||||
expect { policy.valid? }.not_to raise_error
|
||||
expect(policy).not_to be_valid
|
||||
end
|
||||
|
||||
it 'handles policy deletion cascade' do
|
||||
inbox_policy_id = inbox_assignment_policy.id
|
||||
|
||||
# Deleting policy should delete inbox assignment
|
||||
assignment_policy.destroy!
|
||||
|
||||
expect(described_class.find_by(id: inbox_policy_id)).to be_nil
|
||||
end
|
||||
|
||||
it 'handles inbox deletion cascade' do
|
||||
inbox_policy_id = inbox_assignment_policy.id
|
||||
|
||||
# Deleting inbox should delete inbox assignment
|
||||
inbox.destroy!
|
||||
|
||||
expect(described_class.find_by(id: inbox_policy_id)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user