You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
docuseal/spec/helpers/prefill_fields_helper_spec.rb

351 lines
13 KiB

# frozen_string_literal: true
require 'rails_helper'
RSpec.describe PrefillFieldsHelper, type: :helper do
# Clear cache before each test to ensure clean state
before do
Rails.cache.clear
end
describe '#extract_ats_prefill_fields' do
it 'extracts valid field names from base64 encoded parameter' do
fields = %w[employee_first_name employee_email manager_firstname]
encoded = Base64.urlsafe_encode64(fields.to_json)
allow(helper).to receive(:params).and_return({ ats_fields: encoded })
result = helper.extract_ats_prefill_fields
expect(result).to eq(fields)
end
it 'returns empty array for invalid base64' do
allow(helper).to receive(:params).and_return({ ats_fields: 'invalid_base64' })
result = helper.extract_ats_prefill_fields
expect(result).to eq([])
end
it 'returns empty array for invalid JSON' do
invalid_json = Base64.urlsafe_encode64('invalid json')
allow(helper).to receive(:params).and_return({ ats_fields: invalid_json })
result = helper.extract_ats_prefill_fields
expect(result).to eq([])
end
it 'filters out invalid field names' do
fields = %w[employee_first_name malicious_field account_name invalid-field]
encoded = Base64.urlsafe_encode64(fields.to_json)
allow(helper).to receive(:params).and_return({ ats_fields: encoded })
result = helper.extract_ats_prefill_fields
expect(result).to eq(%w[employee_first_name account_name])
end
it 'returns empty array when no ats_fields parameter' do
allow(helper).to receive(:params).and_return({})
result = helper.extract_ats_prefill_fields
expect(result).to eq([])
end
it 'returns empty array when ats_fields parameter is empty' do
allow(helper).to receive(:params).and_return({ ats_fields: '' })
result = helper.extract_ats_prefill_fields
expect(result).to eq([])
end
it 'returns empty array when decoded JSON is not an array' do
not_array = Base64.urlsafe_encode64({ field: 'employee_name' }.to_json)
allow(helper).to receive(:params).and_return({ ats_fields: not_array })
result = helper.extract_ats_prefill_fields
expect(result).to eq([])
end
it 'returns empty array when array contains non-string values' do
mixed_array = ['employee_first_name', 123, 'manager_firstname']
encoded = Base64.urlsafe_encode64(mixed_array.to_json)
allow(helper).to receive(:params).and_return({ ats_fields: encoded })
result = helper.extract_ats_prefill_fields
expect(result).to eq([])
end
it 'accepts all valid field name patterns' do
fields = %w[
employee_first_name
employee_middle_name
employee_last_name
employee_email
manager_firstname
manager_lastname
account_name
location_name
location_street
]
encoded = Base64.urlsafe_encode64(fields.to_json)
allow(helper).to receive(:params).and_return({ ats_fields: encoded })
result = helper.extract_ats_prefill_fields
expect(result).to eq(fields)
end
it 'logs successful field reception on cache miss' do
fields = %w[employee_first_name employee_email]
encoded = Base64.urlsafe_encode64(fields.to_json)
allow(helper).to receive(:params).and_return({ ats_fields: encoded })
allow(Rails.logger).to receive(:info)
allow(Rails.logger).to receive(:debug)
helper.extract_ats_prefill_fields
expect(Rails.logger).to have_received(:info).with(
'Processed and cached 2 ATS prefill fields: employee_first_name, employee_email'
)
end
it 'logs parsing errors and caches empty result' do
allow(helper).to receive(:params).and_return({ ats_fields: 'invalid_base64' })
allow(Rails.logger).to receive(:warn)
allow(Rails.logger).to receive(:debug)
result = helper.extract_ats_prefill_fields
expect(result).to eq([])
expect(Rails.logger).to have_received(:warn).with(
a_string_matching(/Failed to parse ATS prefill fields:/)
)
end
# Caching-specific tests
describe 'caching behavior' do
let(:fields) { %w[employee_first_name employee_email manager_firstname] }
let(:encoded) { Base64.urlsafe_encode64(fields.to_json) }
# Use memory store for caching tests since test environment uses null_store
around do |example|
original_cache = Rails.cache
Rails.cache = ActiveSupport::Cache::MemoryStore.new
example.run
Rails.cache = original_cache
end
it 'caches successful parsing results' do
allow(helper).to receive(:params).and_return({ ats_fields: encoded })
allow(Rails.logger).to receive(:info)
allow(Rails.logger).to receive(:debug)
# First call should parse and cache
result1 = helper.extract_ats_prefill_fields
expect(result1).to eq(fields)
# Verify cache write occurred
cache_key = helper.send(:ats_fields_cache_key, encoded)
cached_value = Rails.cache.read(cache_key)
expect(cached_value).to eq(fields)
end
it 'returns cached results on subsequent calls' do
allow(helper).to receive(:params).and_return({ ats_fields: encoded })
allow(Rails.logger).to receive(:info)
allow(Rails.logger).to receive(:debug)
# First call - cache miss
result1 = helper.extract_ats_prefill_fields
expect(result1).to eq(fields)
# Verify cache miss was logged
expect(Rails.logger).to have_received(:debug).at_least(:once) do |&block|
block&.call&.include?('cache miss')
end
# Reset logger expectations
allow(Rails.logger).to receive(:debug)
# Second call - should be cache hit
result2 = helper.extract_ats_prefill_fields
expect(result2).to eq(fields)
# Verify cache hit was logged
expect(Rails.logger).to have_received(:debug).at_least(:once) do |&block|
block&.call&.include?('cache hit')
end
end
it 'caches empty results for parsing errors' do
allow(helper).to receive(:params).and_return({ ats_fields: 'invalid_base64' })
allow(Rails.logger).to receive(:warn)
allow(Rails.logger).to receive(:debug)
# First call should fail and cache empty result
result1 = helper.extract_ats_prefill_fields
expect(result1).to eq([])
# Verify empty result is cached
cache_key = helper.send(:ats_fields_cache_key, 'invalid_base64')
cached_value = Rails.cache.read(cache_key)
expect(cached_value).to eq([])
# Reset logger expectations
allow(Rails.logger).to receive(:debug)
# Second call should return cached empty result
result2 = helper.extract_ats_prefill_fields
expect(result2).to eq([])
# Verify cache hit was logged
expect(Rails.logger).to have_received(:debug).at_least(:once) do |&block|
block&.call&.include?('cache hit')
end
end
it 'generates consistent cache keys for same input' do
key1 = helper.send(:ats_fields_cache_key, encoded)
key2 = helper.send(:ats_fields_cache_key, encoded)
expect(key1).to eq(key2)
expect(key1).to start_with('ats_fields:')
expect(key1.length).to be > 20 # Should be a reasonable hash length
end
it 'generates different cache keys for different inputs' do
fields2 = %w[manager_lastname location_name]
encoded2 = Base64.urlsafe_encode64(fields2.to_json)
key1 = helper.send(:ats_fields_cache_key, encoded)
key2 = helper.send(:ats_fields_cache_key, encoded2)
expect(key1).not_to eq(key2)
end
it 'respects cache TTL for successful results' do
allow(helper).to receive(:params).and_return({ ats_fields: encoded })
allow(Rails.cache).to receive(:write).and_call_original
helper.extract_ats_prefill_fields
expect(Rails.cache).to have_received(:write).with(
anything,
fields,
expires_in: PrefillFieldsHelper::ATS_FIELDS_CACHE_TTL
)
end
it 'uses shorter TTL for error results' do
allow(helper).to receive(:params).and_return({ ats_fields: 'invalid_base64' })
allow(Rails.cache).to receive(:write).and_call_original
allow(Rails.logger).to receive(:warn)
helper.extract_ats_prefill_fields
expect(Rails.cache).to have_received(:write).with(
anything,
[],
expires_in: 5.minutes
)
end
it 'handles cache read failures gracefully' do
allow(helper).to receive(:params).and_return({ ats_fields: encoded })
allow(Rails.cache).to receive(:read).and_raise(StandardError.new('Cache error'))
allow(Rails.logger).to receive(:info)
allow(Rails.logger).to receive(:debug)
allow(Rails.logger).to receive(:warn)
# Should fall back to normal processing
result = helper.extract_ats_prefill_fields
expect(result).to eq(fields)
expect(Rails.logger).to have_received(:warn).with('Cache read failed for ATS fields: Cache error')
end
it 'handles cache write failures gracefully' do
allow(helper).to receive(:params).and_return({ ats_fields: encoded })
allow(Rails.cache).to receive(:write).and_raise(StandardError.new('Cache error'))
allow(Rails.logger).to receive(:info)
allow(Rails.logger).to receive(:debug)
allow(Rails.logger).to receive(:warn)
# Should still return correct result even if caching fails
result = helper.extract_ats_prefill_fields
expect(result).to eq(fields)
expect(Rails.logger).to have_received(:warn).with('Cache write failed for ATS fields: Cache error')
end
end
describe 'performance characteristics' do
let(:fields) { %w[employee_first_name employee_email manager_firstname] }
let(:encoded) { Base64.urlsafe_encode64(fields.to_json) }
# Use memory store for performance tests since test environment uses null_store
around do |example|
original_cache = Rails.cache
Rails.cache = ActiveSupport::Cache::MemoryStore.new
example.run
Rails.cache = original_cache
end
it 'avoids expensive operations on cache hits' do
allow(helper).to receive(:params).and_return({ ats_fields: encoded })
allow(Rails.logger).to receive(:info)
allow(Rails.logger).to receive(:debug)
# First call to populate cache
helper.extract_ats_prefill_fields
# Mock expensive operations to verify they're not called on cache hit
allow(Base64).to receive(:urlsafe_decode64).and_call_original
allow(JSON).to receive(:parse).and_call_original
# Second call should use cache
result = helper.extract_ats_prefill_fields
expect(result).to eq(fields)
# Verify expensive operations were not called on second call
expect(Base64).not_to have_received(:urlsafe_decode64)
expect(JSON).not_to have_received(:parse)
end
end
end
describe '#valid_ats_field_name?' do
it 'returns true for valid employee field names' do
expect(helper.send(:valid_ats_field_name?, 'employee_first_name')).to be true
expect(helper.send(:valid_ats_field_name?, 'employee_email')).to be true
expect(helper.send(:valid_ats_field_name?, 'employee_phone_number')).to be true
end
it 'returns true for valid manager field names' do
expect(helper.send(:valid_ats_field_name?, 'manager_firstname')).to be true
expect(helper.send(:valid_ats_field_name?, 'manager_lastname')).to be true
expect(helper.send(:valid_ats_field_name?, 'manager_email')).to be true
end
it 'returns true for valid account field names' do
expect(helper.send(:valid_ats_field_name?, 'account_name')).to be true
expect(helper.send(:valid_ats_field_name?, 'account_id')).to be true
end
it 'returns true for valid location field names' do
expect(helper.send(:valid_ats_field_name?, 'location_name')).to be true
expect(helper.send(:valid_ats_field_name?, 'location_street')).to be true
expect(helper.send(:valid_ats_field_name?, 'location_city')).to be true
end
it 'returns false for invalid field names' do
expect(helper.send(:valid_ats_field_name?, 'malicious_field')).to be false
expect(helper.send(:valid_ats_field_name?, 'invalid-field')).to be false
expect(helper.send(:valid_ats_field_name?, 'EMPLOYEE_NAME')).to be false
expect(helper.send(:valid_ats_field_name?, 'employee')).to be false
expect(helper.send(:valid_ats_field_name?, 'employee_')).to be false
expect(helper.send(:valid_ats_field_name?, '_employee_name')).to be false
end
end
end