mirror of https://github.com/docusealco/docuseal
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.
184 lines
6.0 KiB
184 lines
6.0 KiB
# frozen_string_literal: true
|
|
|
|
require 'rails_helper'
|
|
require 'templates/field_detection'
|
|
require 'templates/field_detection/config_based'
|
|
|
|
RSpec.describe Templates::FieldDetection do
|
|
before { described_class.reset! }
|
|
|
|
after { described_class.reset! }
|
|
|
|
describe '.register' do
|
|
it 'stores a handler by downcased name' do
|
|
handler = ->(_template, _documents) { [] }
|
|
described_class.register('MyAlgo', handler)
|
|
|
|
expect(described_class.registered_scripts).to include('myalgo' => handler)
|
|
end
|
|
|
|
it 'strips whitespace from name' do
|
|
handler = ->(_template, _documents) { [] }
|
|
described_class.register(' spaced ', handler)
|
|
|
|
expect(described_class.registered_scripts).to include('spaced' => handler)
|
|
end
|
|
end
|
|
|
|
describe '.available_algorithms' do
|
|
it 'returns registered scripts merged with external YAML profiles' do
|
|
handler = ->(_template, _documents) { [] }
|
|
described_class.register('script_algo', handler)
|
|
|
|
allow(described_class).to receive(:external_algorithms).and_return(
|
|
'yaml_profile' => { 'fields' => [] }
|
|
)
|
|
|
|
algos = described_class.available_algorithms
|
|
expect(algos).to include('script_algo' => handler)
|
|
expect(algos).to include('yaml_profile' => { 'fields' => [] })
|
|
end
|
|
end
|
|
|
|
describe '.profile_names' do
|
|
it 'returns sorted list of all algorithm names' do
|
|
described_class.register('zebra', ->(_t, _d) { [] })
|
|
described_class.register('alpha', ->(_t, _d) { [] })
|
|
|
|
allow(described_class).to receive(:external_algorithms).and_return(
|
|
'middle' => { 'fields' => [] }
|
|
)
|
|
|
|
expect(described_class.profile_names).to eq(%w[alpha middle zebra])
|
|
end
|
|
end
|
|
|
|
describe '.load_scripts!' do
|
|
it 'loads .rb files from SCRIPTS_DIR' do
|
|
Dir.mktmpdir do |dir|
|
|
File.write(File.join(dir, 'test_algo.rb'), <<~RUBY)
|
|
Templates::FieldDetection.register('test_algo', ->(_t, _d) { :loaded })
|
|
RUBY
|
|
|
|
stub_const('Templates::FieldDetection::SCRIPTS_DIR', dir)
|
|
described_class.reset!
|
|
described_class.load_scripts!
|
|
|
|
expect(described_class.registered_scripts).to include('test_algo')
|
|
expect(described_class.registered_scripts['test_algo'].call(nil, nil)).to eq(:loaded)
|
|
end
|
|
end
|
|
|
|
it 'logs warning and continues on script error' do
|
|
Dir.mktmpdir do |dir|
|
|
File.write(File.join(dir, 'bad_script.rb'), 'raise "boom"')
|
|
File.write(File.join(dir, 'good_script.rb'), <<~RUBY)
|
|
Templates::FieldDetection.register('good', ->(_t, _d) { :ok })
|
|
RUBY
|
|
|
|
stub_const('Templates::FieldDetection::SCRIPTS_DIR', dir)
|
|
described_class.reset!
|
|
|
|
allow(Rails.logger).to receive(:warn)
|
|
described_class.load_scripts!
|
|
|
|
expect(Rails.logger).to have_received(:warn).with(/Failed to load script.*bad_script\.rb.*boom/)
|
|
expect(described_class.registered_scripts).to include('good')
|
|
expect(described_class.registered_scripts).not_to include('bad_script')
|
|
end
|
|
end
|
|
|
|
it 'skips loading when SCRIPTS_DIR does not exist' do
|
|
stub_const('Templates::FieldDetection::SCRIPTS_DIR', '/nonexistent/path')
|
|
described_class.reset!
|
|
|
|
expect { described_class.load_scripts! }.not_to raise_error
|
|
end
|
|
end
|
|
|
|
describe '.external_algorithms' do
|
|
it 'parses YAML files from CONFIG_DIR' do
|
|
Dir.mktmpdir do |dir|
|
|
File.write(File.join(dir, 'profile_x.yml'), <<~YAML)
|
|
submitters:
|
|
- name: signer
|
|
fields:
|
|
- name: sig
|
|
type: signature
|
|
YAML
|
|
|
|
stub_const('Templates::FieldDetection::CONFIG_DIR', dir)
|
|
|
|
algos = described_class.external_algorithms
|
|
expect(algos).to include('profile_x')
|
|
expect(algos['profile_x']).to be_a(Hash)
|
|
expect(algos['profile_x']['fields'].first['name']).to eq('sig')
|
|
end
|
|
end
|
|
|
|
it 'returns empty hash when CONFIG_DIR does not exist' do
|
|
stub_const('Templates::FieldDetection::CONFIG_DIR', '/nonexistent/path')
|
|
|
|
expect(described_class.external_algorithms).to eq({})
|
|
end
|
|
|
|
it 'skips malformed YAML files' do
|
|
Dir.mktmpdir do |dir|
|
|
File.write(File.join(dir, 'bad.yml'), ': invalid: yaml: [')
|
|
File.write(File.join(dir, 'good.yml'), "submitters:\n - name: s\n")
|
|
|
|
stub_const('Templates::FieldDetection::CONFIG_DIR', dir)
|
|
|
|
algos = described_class.external_algorithms
|
|
expect(algos).to include('good')
|
|
expect(algos).not_to include('bad')
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '.call' do
|
|
it 'raises ArgumentError for unknown algorithm' do
|
|
expect { described_class.call(instance_double(Template), 'nonexistent') }
|
|
.to raise_error(ArgumentError, /Unknown algorithm: 'nonexistent'/)
|
|
end
|
|
|
|
it 'invokes registered script handler' do
|
|
template = instance_double(Template)
|
|
documents = instance_double(ActiveRecord::Relation)
|
|
handler = ->(_t, _d) { :script_result }
|
|
|
|
described_class.register('my_script', handler)
|
|
|
|
result = described_class.call(template, 'my_script', documents)
|
|
expect(result).to eq(:script_result)
|
|
end
|
|
|
|
it 'dispatches YAML profile to ConfigBased.call' do
|
|
template = instance_double(Template)
|
|
documents = instance_double(ActiveRecord::Relation)
|
|
config = { 'fields' => [], 'submitters' => [] }
|
|
|
|
allow(described_class).to receive(:external_algorithms).and_return(
|
|
'yaml_test' => config
|
|
)
|
|
|
|
allow(Templates::FieldDetection::ConfigBased).to receive(:call)
|
|
.with(template, config, documents)
|
|
.and_return([])
|
|
|
|
result = described_class.call(template, 'yaml_test', documents)
|
|
expect(result).to eq([])
|
|
expect(Templates::FieldDetection::ConfigBased).to have_received(:call)
|
|
.with(template, config, documents)
|
|
end
|
|
|
|
it 'is case-insensitive for algorithm names' do
|
|
handler = ->(_t, _d) { :found }
|
|
described_class.register('CaseSensitive', handler)
|
|
|
|
result = described_class.call(instance_double(Template), 'casesensitive')
|
|
expect(result).to eq(:found)
|
|
end
|
|
end
|
|
end
|