Merge branch 'hotfix/4.11.2' into develop
This commit is contained in:
61
spec/models/custom_attribute_definition_spec.rb
Normal file
61
spec/models/custom_attribute_definition_spec.rb
Normal file
@@ -0,0 +1,61 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe CustomAttributeDefinition do
|
||||
let(:account) { create(:account) }
|
||||
|
||||
describe 'validations' do
|
||||
describe 'attribute_key format' do
|
||||
it 'allows alphanumeric keys with underscores' do
|
||||
cad = build(:custom_attribute_definition, account: account, attribute_key: 'order_date_1')
|
||||
expect(cad).to be_valid
|
||||
end
|
||||
|
||||
it 'allows hyphens and dots' do
|
||||
cad = build(:custom_attribute_definition, account: account, attribute_key: 'order-date.v2')
|
||||
expect(cad).to be_valid
|
||||
end
|
||||
|
||||
it 'allows Unicode letters' do
|
||||
cad = build(:custom_attribute_definition, account: account, attribute_key: '客户类型')
|
||||
expect(cad).to be_valid
|
||||
end
|
||||
|
||||
it 'rejects keys with single quotes' do
|
||||
cad = build(:custom_attribute_definition, account: account, attribute_key: "x'||(SELECT 1)||'")
|
||||
expect(cad).not_to be_valid
|
||||
expect(cad.errors[:attribute_key]).to be_present
|
||||
end
|
||||
|
||||
it 'rejects keys with spaces' do
|
||||
cad = build(:custom_attribute_definition, account: account, attribute_key: 'order date')
|
||||
expect(cad).not_to be_valid
|
||||
end
|
||||
|
||||
it 'rejects keys with semicolons' do
|
||||
cad = build(:custom_attribute_definition, account: account, attribute_key: 'key; DROP TABLE users--')
|
||||
expect(cad).not_to be_valid
|
||||
end
|
||||
|
||||
it 'rejects keys with parentheses' do
|
||||
cad = build(:custom_attribute_definition, account: account, attribute_key: 'key()')
|
||||
expect(cad).not_to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'callbacks' do
|
||||
describe '#strip_attribute_key' do
|
||||
it 'strips leading and trailing whitespace from attribute_key' do
|
||||
cad = create(:custom_attribute_definition, account: account, attribute_key: ' order_date ')
|
||||
expect(cad.attribute_key).to eq('order_date')
|
||||
end
|
||||
|
||||
it 'strips leading and trailing whitespace from attribute_display_name' do
|
||||
cad = create(:custom_attribute_definition, account: account, attribute_display_name: ' Order Date ')
|
||||
expect(cad.attribute_display_name).to eq('Order Date')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -49,6 +49,11 @@ describe Contacts::FilterService do
|
||||
account: account,
|
||||
attribute_model: 'contact_attribute',
|
||||
attribute_display_type: 'date')
|
||||
create(:custom_attribute_definition,
|
||||
attribute_key: 'lifetime_value',
|
||||
account: account,
|
||||
attribute_model: 'contact_attribute',
|
||||
attribute_display_type: 'number')
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
@@ -60,7 +65,7 @@ describe Contacts::FilterService do
|
||||
|
||||
en_contact.update!(custom_attributes: { contact_additional_information: 'test custom data' })
|
||||
el_contact.update!(custom_attributes: { contact_additional_information: 'test custom data', customer_type: 'platinum' })
|
||||
cs_contact.update!(custom_attributes: { customer_type: 'platinum', signed_in_at: '2022-01-19' })
|
||||
cs_contact.update!(custom_attributes: { customer_type: 'platinum', signed_in_at: '2022-01-19', lifetime_value: '120.50' })
|
||||
end
|
||||
|
||||
context 'with standard attributes - name' do
|
||||
@@ -272,6 +277,39 @@ describe Contacts::FilterService do
|
||||
expect(result[:contacts].pluck(:id)).to include(cs_contact.id)
|
||||
expect(result[:contacts].pluck(:id)).not_to include(en_contact.id)
|
||||
end
|
||||
|
||||
it 'binds last_activity_at comparison values as dates' do
|
||||
date_value = '2024-01-01'
|
||||
params[:payload] = [
|
||||
{
|
||||
attribute_key: 'last_activity_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: [date_value],
|
||||
query_operator: nil
|
||||
}.with_indifferent_access
|
||||
]
|
||||
|
||||
service = filter_service.new(account, first_user, params)
|
||||
filters = service.instance_variable_get(:@filters)['contacts']
|
||||
condition_query = service.send(:build_condition_query, filters, params[:payload].first, 0)
|
||||
|
||||
expect(condition_query).to include('(contacts.last_activity_at)::date > :value_0')
|
||||
expect(service.instance_variable_get(:@filter_values)['value_0']).to eq(Date.iso8601(date_value))
|
||||
end
|
||||
|
||||
it 'rejects invalid last_activity_at comparison values' do
|
||||
malicious_value = "2024-01-01'::date OR (SELECT pg_sleep(5)) IS NOT NULL --"
|
||||
params[:payload] = [
|
||||
{
|
||||
attribute_key: 'last_activity_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: [malicious_value],
|
||||
query_operator: nil
|
||||
}.with_indifferent_access
|
||||
]
|
||||
|
||||
expect { filter_service.new(account, first_user, params).perform }.to raise_error(CustomExceptions::CustomFilter::InvalidValue)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with additional attributes' do
|
||||
@@ -369,6 +407,72 @@ describe Contacts::FilterService do
|
||||
expect(result[:contacts].length).to be expected_count
|
||||
expect(result[:contacts].pluck(:id)).to include(el_contact.id)
|
||||
end
|
||||
|
||||
it 'binds custom date comparison values as dates' do
|
||||
date_value = '2024-01-01'
|
||||
params[:payload] = [
|
||||
{
|
||||
attribute_key: 'signed_in_at',
|
||||
filter_operator: 'is_less_than',
|
||||
values: [date_value],
|
||||
query_operator: nil
|
||||
}.with_indifferent_access
|
||||
]
|
||||
|
||||
service = filter_service.new(account, first_user, params)
|
||||
filters = service.instance_variable_get(:@filters)['contacts']
|
||||
condition_query = service.send(:build_condition_query, filters, params[:payload].first, 0)
|
||||
|
||||
expect(condition_query).to include("(contacts.custom_attributes ->> 'signed_in_at')::date < :value_0")
|
||||
expect(service.instance_variable_get(:@filter_values)['value_0']).to eq(Date.iso8601(date_value))
|
||||
end
|
||||
|
||||
it 'binds custom numeric comparison values as decimals' do
|
||||
params[:payload] = [
|
||||
{
|
||||
attribute_key: 'lifetime_value',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: ['100.25'],
|
||||
query_operator: nil
|
||||
}.with_indifferent_access
|
||||
]
|
||||
|
||||
service = filter_service.new(account, first_user, params)
|
||||
filters = service.instance_variable_get(:@filters)['contacts']
|
||||
condition_query = service.send(:build_condition_query, filters, params[:payload].first, 0)
|
||||
|
||||
expect(condition_query).to include("(contacts.custom_attributes ->> 'lifetime_value')::numeric > :value_0")
|
||||
expect(service.instance_variable_get(:@filter_values)['value_0']).to eq(BigDecimal('100.25'))
|
||||
end
|
||||
|
||||
it 'filters by custom numeric attributes' do
|
||||
params[:payload] = [
|
||||
{
|
||||
attribute_key: 'lifetime_value',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: ['100.25'],
|
||||
query_operator: nil
|
||||
}.with_indifferent_access
|
||||
]
|
||||
|
||||
result = filter_service.new(account, first_user, params).perform
|
||||
|
||||
expect(result[:contacts].pluck(:id)).to eq([cs_contact.id])
|
||||
end
|
||||
|
||||
it 'rejects invalid custom date comparison values' do
|
||||
malicious_value = "2024-01-01'::date OR (SELECT pg_sleep(5)) IS NOT NULL --"
|
||||
params[:payload] = [
|
||||
{
|
||||
attribute_key: 'signed_in_at',
|
||||
filter_operator: 'is_less_than',
|
||||
values: [malicious_value],
|
||||
query_operator: nil
|
||||
}.with_indifferent_access
|
||||
]
|
||||
|
||||
expect { filter_service.new(account, first_user, params).perform }.to raise_error(CustomExceptions::CustomFilter::InvalidValue)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -417,6 +417,41 @@ describe Conversations::FilterService do
|
||||
expect(result[:conversations].length).to be expected_count
|
||||
end
|
||||
|
||||
it 'binds created_at comparison values as dates' do
|
||||
date_value = '2024-01-01'
|
||||
params[:payload] = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: [date_value],
|
||||
query_operator: nil,
|
||||
custom_attribute_type: ''
|
||||
}.with_indifferent_access
|
||||
]
|
||||
|
||||
service = filter_service.new(params, user_1, account)
|
||||
filters = service.instance_variable_get(:@filters)['conversations']
|
||||
condition_query = service.send(:build_condition_query, filters, params[:payload].first, 0)
|
||||
|
||||
expect(condition_query).to include('(conversations.created_at)::date > :value_0')
|
||||
expect(service.instance_variable_get(:@filter_values)['value_0']).to eq(Date.iso8601(date_value))
|
||||
end
|
||||
|
||||
it 'rejects invalid created_at comparison values' do
|
||||
malicious_value = "2024-01-01'::date OR (SELECT pg_sleep(5)) IS NOT NULL --"
|
||||
params[:payload] = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: [malicious_value],
|
||||
query_operator: nil,
|
||||
custom_attribute_type: ''
|
||||
}.with_indifferent_access
|
||||
]
|
||||
|
||||
expect { filter_service.new(params, user_1, account).perform }.to raise_error(CustomExceptions::CustomFilter::InvalidValue)
|
||||
end
|
||||
|
||||
it 'filter by created_at and conversation_type' do
|
||||
params[:payload] = [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user