Merge branch 'develop' into feat/voice-as-twilio-capability
This commit is contained in:
@@ -83,6 +83,37 @@ describe Enterprise::Billing::HandleStripeEventService do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'subscription quantity update' do
|
||||
before do
|
||||
allow(subscription).to receive(:[]).with('plan')
|
||||
.and_return({ 'id' => 'price_startups', 'product' => 'plan_id_startups', 'name' => 'Startups' })
|
||||
end
|
||||
|
||||
it 'updates subscribed_quantity' do
|
||||
allow(subscription).to receive(:[]).with('quantity').and_return(6)
|
||||
|
||||
stripe_event_service.new.perform(event: event)
|
||||
|
||||
expect(account.reload.custom_attributes['subscribed_quantity']).to eq(6)
|
||||
end
|
||||
|
||||
it 'persists quantity even when increment_response_usage runs concurrently' do
|
||||
allow(subscription).to receive(:[]).with('quantity').and_return(6)
|
||||
account.update!(custom_attributes: account.custom_attributes.merge('captain_responses_usage' => 100))
|
||||
|
||||
# Simulate: webhook updates quantity, then a concurrent increment_response_usage writes usage
|
||||
stripe_event_service.new.perform(event: event)
|
||||
account.reload
|
||||
|
||||
# Simulate concurrent increment_response_usage (atomic jsonb_set, not full hash overwrite)
|
||||
account.increment_response_usage
|
||||
|
||||
# Quantity must survive the concurrent usage update
|
||||
expect(account.reload.custom_attributes['subscribed_quantity']).to eq(6)
|
||||
expect(account.reload.custom_attributes['captain_responses_usage']).to eq(101)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'subscription deletion handling' do
|
||||
it 'calls CreateStripeCustomerService on subscription deletion' do
|
||||
allow(event).to receive(:type).and_return('customer.subscription.deleted')
|
||||
|
||||
@@ -220,6 +220,15 @@ describe AutomationRuleListener do
|
||||
expect(AutomationRules::ActionService).not_to have_received(:new)
|
||||
end
|
||||
|
||||
it 'calls AutomationRules::ActionService if message is a private note' do
|
||||
message.update!(private: true)
|
||||
allow(condition_match).to receive(:present?).and_return(true)
|
||||
|
||||
listener.message_created(event)
|
||||
|
||||
expect(AutomationRules::ActionService).to have_received(:new).with(automation_rule, account, conversation)
|
||||
end
|
||||
|
||||
it 'does not call AutomationRules::ActionService if conditions do not match based on content' do
|
||||
message.update!(processed_message_content: 'hi', content: "hi\n\nhello")
|
||||
allow(condition_match).to receive(:present?).and_return(false)
|
||||
|
||||
@@ -256,6 +256,29 @@ RSpec.describe Account do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when support_email is set' do
|
||||
it 'allows a plain email address' do
|
||||
account.support_email = 'support@example.com'
|
||||
expect(account).to be_valid
|
||||
end
|
||||
|
||||
it 'allows display-name format' do
|
||||
account.support_email = 'Support Team <support@example.com>'
|
||||
expect(account).to be_valid
|
||||
end
|
||||
|
||||
it 'allows blank values' do
|
||||
account.support_email = ''
|
||||
expect(account).to be_valid
|
||||
end
|
||||
|
||||
it 'rejects malformed strings with no email part' do
|
||||
account.support_email = 'Smith Smith'
|
||||
expect(account).not_to be_valid
|
||||
expect(account.errors[:support_email]).to include(I18n.t('errors.account.support_email.invalid'))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when reporting_timezone is set' do
|
||||
it 'allows valid timezone names' do
|
||||
account.reporting_timezone = 'America/New_York'
|
||||
|
||||
@@ -86,6 +86,19 @@ RSpec.describe AutomationRule do
|
||||
rule = FactoryBot.build(:automation_rule, params)
|
||||
expect(rule.valid?).to be true
|
||||
end
|
||||
|
||||
it 'allows private_note as a valid condition attribute' do
|
||||
params[:conditions] = [
|
||||
{
|
||||
attribute_key: 'private_note',
|
||||
filter_operator: 'equal_to',
|
||||
values: [true],
|
||||
query_operator: nil
|
||||
}
|
||||
]
|
||||
rule = FactoryBot.build(:automation_rule, params)
|
||||
expect(rule.valid?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe 'reauthorizable' do
|
||||
|
||||
@@ -10,7 +10,8 @@ RSpec.describe AutomationRules::ConditionValidationService do
|
||||
rule.conditions = [
|
||||
{ 'values': ['open'], 'attribute_key': 'status', 'query_operator': nil, 'filter_operator': 'equal_to' },
|
||||
{ 'values': ['+918484'], 'attribute_key': 'phone_number', 'query_operator': 'OR', 'filter_operator': 'contains' },
|
||||
{ 'values': ['test'], 'attribute_key': 'email', 'query_operator': nil, 'filter_operator': 'contains' }
|
||||
{ 'values': ['test'], 'attribute_key': 'email', 'query_operator': 'OR', 'filter_operator': 'contains' },
|
||||
{ 'values': [true], 'attribute_key': 'private_note', 'query_operator': nil, 'filter_operator': 'equal_to' }
|
||||
]
|
||||
rule.save
|
||||
end
|
||||
|
||||
@@ -83,6 +83,27 @@ RSpec.describe AutomationRules::ConditionsFilterService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when filtering private notes' do
|
||||
before do
|
||||
rule.conditions = [
|
||||
{ 'values': [true], 'attribute_key': 'private_note', 'query_operator': nil, 'filter_operator': 'equal_to' }
|
||||
]
|
||||
rule.save
|
||||
end
|
||||
|
||||
it 'will return true when the message is a private note' do
|
||||
message.update!(private: true)
|
||||
|
||||
expect(described_class.new(rule, conversation, { message: message, changed_attributes: {} }).perform).to be(true)
|
||||
end
|
||||
|
||||
it 'will return false when the message is not a private note' do
|
||||
message.update!(private: false)
|
||||
|
||||
expect(described_class.new(rule, conversation, { message: message, changed_attributes: {} }).perform).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when filter_operator is on processed_message_content' do
|
||||
before do
|
||||
rule.conditions = [
|
||||
|
||||
Reference in New Issue
Block a user