diff --git a/app/services/proofing/aamva/applicant.rb b/app/services/proofing/aamva/applicant.rb
index 24dece1cb46..c0e06c06107 100644
--- a/app/services/proofing/aamva/applicant.rb
+++ b/app/services/proofing/aamva/applicant.rb
@@ -32,6 +32,27 @@ module Aamva
keyword_init: true,
).freeze
+ # @param applicant [Hash, Struct]
+ # @option applicant [String, nil] :uuid
+ # @option applicant [String, nil] :first_name
+ # @option applicant [String, nil] :middle_name
+ # @option applicant [String, nil] :last_name
+ # @option applicant [String, nil] :name_suffix
+ # @option applicant [String, nil] :dob
+ # @option applicant [String, nil] :sex
+ # @option applicant [Integer, nil] :height in inches
+ # @option applicant [String, nil] :weight
+ # @option applicant [String, nil] :eye_color
+ # @option applicant [String, nil] :address1
+ # @option applicant [String, nil] :address2
+ # @option applicant [String, nil] :city
+ # @option applicant [String, nil] :state
+ # @option applicant [String, nil] :zipcode
+ # @option applicant [String, nil] :state_id_number
+ # @option applicant [String, nil] :state_id_jurisdiction
+ # @option applicant [String, nil] :state_id_type
+ # @option applicant [String, nil] :state_id_issued
+ # @option applicant [String, nil] :state_id_expiration
# @return [Applicant]
def self.from_proofer_applicant(applicant)
new(
diff --git a/app/services/proofing/aamva/proofer.rb b/app/services/proofing/aamva/proofer.rb
index 4763275cabd..82e393cfa2f 100644
--- a/app/services/proofing/aamva/proofer.rb
+++ b/app/services/proofing/aamva/proofer.rb
@@ -49,36 +49,42 @@ def initialize(config)
@config = Config.new(config)
end
+ # @param applicant [Hash]
def proof(applicant)
aamva_applicant = Aamva::Applicant.from_proofer_applicant(applicant)
+ verification_request = build_verification_request(aamva_applicant)
+ verification_response = verification_request.send
+ jurisdiction = applicant[:state_id_jurisdiction]
- response = Aamva::VerificationClient.new(
- config,
- ).send_verification_request(
- applicant: aamva_applicant,
- )
-
- build_result_from_response(response, applicant[:state_id_jurisdiction])
+ build_result(verification_request:, verification_response:, jurisdiction:)
rescue => exception
Proofing::StateIdResult.new(
- success: false, errors: {}, exception: exception, vendor_name: 'aamva:state_id',
- transaction_id: nil, verified_attributes: [],
- jurisdiction_in_maintenance_window: jurisdiction_in_maintenance_window?(
- applicant[:state_id_jurisdiction],
- )
+ success: false,
+ errors: {},
+ exception: exception,
+ vendor_name: 'aamva:state_id',
+ transaction_id: nil,
+ verified_attributes: [],
+ requested_attributes: requested_attributes(verification_request),
+ jurisdiction_in_maintenance_window: jurisdiction_in_maintenance_window?(jurisdiction),
)
end
private
- def build_result_from_response(verification_response, jurisdiction)
+ def build_verification_request(applicant)
+ Aamva::VerificationClient.new(config)
+ .build_verification_request(applicant:)
+ end
+
+ def build_result(verification_request:, verification_response:, jurisdiction:)
Proofing::StateIdResult.new(
success: successful?(verification_response),
errors: parse_verification_errors(verification_response),
exception: nil,
vendor_name: 'aamva:state_id',
transaction_id: verification_response.transaction_locator_id,
- requested_attributes: requested_attributes(verification_response).index_with(1),
+ requested_attributes: requested_attributes(verification_request),
verified_attributes: verified_attributes(verification_response),
jurisdiction_in_maintenance_window: jurisdiction_in_maintenance_window?(jurisdiction),
)
@@ -98,13 +104,23 @@ def parse_verification_errors(verification_response)
errors
end
- def requested_attributes(verification_response)
- attributes = verification_response
- .verification_results.filter { |_, verified| !verified.nil? }
+ # @param verification_request [Proofing::Aamva::Request::VerificationRequest]
+ def requested_attributes(verification_request)
+ return if verification_request.nil?
+ present_attributes = verification_request
+ .requested_attributes
+ .compact
+ .filter { |_k, v| v == :present }
.keys
.to_set
- normalize_address_attributes(attributes)
+ blank_attributes = verification_request
+ .requested_attributes
+ .filter { |_k, v| v == :missing }
+ .transform_values { |_v| 0 }
+
+ normalized = normalize_address_attributes(present_attributes).index_with(1)
+ normalized.merge(blank_attributes)
end
def verified_attributes(verification_response)
diff --git a/app/services/proofing/aamva/request/verification_request.rb b/app/services/proofing/aamva/request/verification_request.rb
index 34dfae42a90..30dc750dc6c 100644
--- a/app/services/proofing/aamva/request/verification_request.rb
+++ b/app/services/proofing/aamva/request/verification_request.rb
@@ -10,6 +10,7 @@
module Proofing
module Aamva
module Request
+ RequestAttribute = Data.define(:xpath, :required).freeze
class VerificationRequest
CONTENT_TYPE = 'application/soap+xml;charset=UTF-8'
DEFAULT_VERIFICATION_URL =
@@ -17,20 +18,45 @@ class VerificationRequest
SOAP_ACTION =
'"http://aamva.org/dldv/wsdl/2.1/IDLDVService21/VerifyDriverLicenseData"'
+ VERIFICATION_REQUESTED_ATTRS = {
+ first_name: RequestAttribute.new(xpath: '//nc:PersonGivenName', required: true),
+ middle_name: RequestAttribute.new(xpath: '//nc:PersonMiddleName', required: false),
+ last_name: RequestAttribute.new('//nc:PersonSurName', true),
+ name_suffix: RequestAttribute.new('//nc:PersonNameSuffixText', false),
+ dob: RequestAttribute.new('//aa:PersonBirthDate', true),
+ address1: RequestAttribute.new('//nc:AddressDeliveryPointText', true),
+ address2: RequestAttribute.new('//nc:AddressDeliveryPointText[2]', false),
+ city: RequestAttribute.new('//nc:LocationCityName', true),
+ state: RequestAttribute.new('//nc:LocationStateUsPostalServiceCode', true),
+ zipcode: RequestAttribute.new('//nc:LocationPostalCode', true),
+ state_id_number: RequestAttribute.new('//nc:IdentificationID', true),
+ state_id_type: RequestAttribute.new('//aa:DocumentCategoryCode', false),
+ state_id_expiration: RequestAttribute.new('//aa:DriverLicenseExpirationDate', false),
+ state_id_jurisdiction: RequestAttribute.new('//aa:MessageDestinationId', true),
+ state_id_issued: RequestAttribute.new('//aa:DriverLicenseIssueDate', false),
+ eye_color: RequestAttribute.new('//aa:PersonEyeColorCode', false),
+ height: RequestAttribute.new('//aa:PersonHeightMeasure', false),
+ sex: RequestAttribute.new('//aa:PersonSexCode', false),
+ weight: RequestAttribute.new('//aa:PersonWeightMeasure', false),
+ }.freeze
+
extend Forwardable
attr_reader :config, :body, :headers, :url
+ # @param applicant [Proofing::Aamva::Applicant]
def initialize(config:, applicant:, session_id:, auth_token:)
@config = config
@applicant = applicant
@transaction_id = session_id
@auth_token = auth_token
+ @requested_attributes = {}
@url = verification_url
@body = build_request_body
@headers = build_request_headers
end
+ # @return [Proofing::Aamva::Response::VerificationResponse]
def send
Response::VerificationResponse.new(
http_client.post(url, body, headers) do |req|
@@ -46,9 +72,21 @@ def verification_url
config.verification_url || DEFAULT_VERIFICATION_URL
end
+ # The requested attributes in the applicant PII hash. Values are:
+ # - +:present+ - value present
+ # - +:missing+ - field is required, but value was blank
+ #
+ # @see Proofing::Aamva::Applicant#from_proofer_applicant for fields
+ # @return [Hash{Symbol => Symbol}]
+ def requested_attributes
+ { **@requested_attributes }
+ end
+
private
- attr_reader :applicant, :transaction_id, :auth_token
+ # @return [Proofing::Aamva::Applicant]
+ attr_reader :applicant
+ attr_reader :transaction_id, :auth_token
def http_client
Faraday.new(request: { open_timeout: timeout, timeout: timeout }) do |faraday|
@@ -57,9 +95,10 @@ def http_client
end
end
- def add_user_provided_data_to_body
+ def add_user_provided_data_to_body(body)
document = REXML::Document.new(body)
- user_provided_data_map.each do |xpath, data|
+ user_provided_data_map.each do |attribute, data|
+ xpath = VERIFICATION_REQUESTED_ATTRS[attribute].xpath
REXML::XPath.first(document, xpath).add_text(data)
end
@@ -126,17 +165,18 @@ def add_user_provided_data_to_body
document,
)
- @body = document.to_s
+ update_requested_attributes(document)
+ document.to_s
end
def add_state_id_type(id_type, document)
category_code = case id_type
- when 'drivers_license'
- 1
- when 'drivers_permit'
- 2
- when 'state_id_card'
- 3
+ when 'drivers_license'
+ 1
+ when 'drivers_permit'
+ 2
+ when 'state_id_card'
+ 3
end
if category_code
@@ -151,10 +191,10 @@ def add_state_id_type(id_type, document)
def add_sex_code(sex_value, document)
sex_code = case sex_value
- when 'male'
- 1
- when 'female'
- 2
+ when 'male'
+ 1
+ when 'female'
+ 2
end
if sex_code
@@ -181,10 +221,22 @@ def add_optional_element(name, value:, document:, inside: nil, after: nil)
end
end
+ # @param document [REXML::Document]
+ def update_requested_attributes(document)
+ VERIFICATION_REQUESTED_ATTRS.each do |attribute, rule|
+ value = REXML::XPath.first(document, rule.xpath)&.text
+ if value.present?
+ @requested_attributes[attribute] = :present
+ elsif rule.required
+ @requested_attributes[attribute] = :missing
+ end
+ end
+ end
+
def build_request_body
renderer = ERB.new(request_body_template)
- @body = renderer.result(binding)
- add_user_provided_data_to_body
+ tmp_body = renderer.result(binding)
+ add_user_provided_data_to_body(tmp_body)
end
def build_request_headers
@@ -222,24 +274,24 @@ def transaction_locator_id
def user_provided_data_map
{
- '//nc:IdentificationID' => state_id_number,
- '//aa:MessageDestinationId' => message_destination_id,
- '//nc:PersonGivenName' => applicant.first_name,
- '//nc:PersonSurName' => applicant.last_name,
- '//aa:PersonBirthDate' => applicant.dob,
- '//nc:AddressDeliveryPointText' => applicant.address1,
- '//nc:LocationCityName' => applicant.city,
- '//nc:LocationStateUsPostalServiceCode' => applicant.state,
- '//nc:LocationPostalCode' => applicant.zipcode,
+ state_id_number: state_id_number,
+ state_id_jurisdiction: message_destination_id,
+ first_name: applicant.first_name,
+ last_name: applicant.last_name,
+ dob: applicant.dob,
+ address1: applicant.address1,
+ city: applicant.city,
+ state: applicant.state,
+ zipcode: applicant.zipcode,
}
end
def state_id_number
case applicant.state_id_data.state_id_jurisdiction
- when 'SC'
- applicant.state_id_data.state_id_number.rjust(8, '0')
- else
- applicant.state_id_data.state_id_number
+ when 'SC'
+ applicant.state_id_data.state_id_number.rjust(8, '0')
+ else
+ applicant.state_id_data.state_id_number
end
end
diff --git a/app/services/proofing/aamva/verification_client.rb b/app/services/proofing/aamva/verification_client.rb
index 4f4818156f9..fb01331d1c9 100644
--- a/app/services/proofing/aamva/verification_client.rb
+++ b/app/services/proofing/aamva/verification_client.rb
@@ -10,13 +10,17 @@ def initialize(config)
@config = config
end
- def send_verification_request(applicant:, session_id: nil)
+ def build_verification_request(applicant:, session_id: nil)
Request::VerificationRequest.new(
applicant: applicant,
session_id: session_id,
auth_token: auth_token,
config: config,
- ).send
+ )
+ end
+
+ def send_verification_request(applicant:, session_id: nil)
+ build_verification_request(applicant:, session_id:).send
end
private
diff --git a/spec/services/proofing/aamva/proofer_spec.rb b/spec/services/proofing/aamva/proofer_spec.rb
index 26a650347ce..3e08c2595be 100644
--- a/spec/services/proofing/aamva/proofer_spec.rb
+++ b/spec/services/proofing/aamva/proofer_spec.rb
@@ -2,15 +2,36 @@
require 'ostruct'
RSpec.describe Proofing::Aamva::Proofer do
- let(:aamva_applicant) do
- Aamva::Applicant.from_proofer_applicant(state_id_data)
- end
+ let(:attribute) { :unknown }
let(:state_id_data) do
{
state_id_number: '1234567890',
state_id_jurisdiction: 'VA',
state_id_type: 'drivers_license',
+ state_id_issued: '2024-05-06',
+ state_id_expiration: '2034-10-29',
+ }
+ end
+
+ let(:applicant) do
+ {
+ uuid: '1234-abcd-efgh',
+ first_name: 'Testy',
+ last_name: 'McTesterson',
+ middle_name: 'Spectacle',
+ name_suffix: 'III',
+ dob: '10/29/1942',
+ address1: '123 Sunnyside way',
+ address2: nil,
+ city: 'Sterling',
+ state: 'VA',
+ zipcode: '20176-1234',
+ eye_color: 'brn',
+ height: 63,
+ weight: 179,
+ sex: 'female',
+ **state_id_data,
}
end
@@ -27,26 +48,28 @@
}
end
+ let(:config) { AamvaFixtures.example_config }
+
subject do
- described_class.new(AamvaFixtures.example_config.to_h)
+ described_class.new(config.to_h)
end
let(:verification_response) { AamvaFixtures.verification_response }
before do
- stub_request(:post, AamvaFixtures.example_config.auth_url)
+ stub_request(:post, config.auth_url)
.to_return(
{ body: AamvaFixtures.security_token_response },
{ body: AamvaFixtures.authentication_token_response },
)
- stub_request(:post, AamvaFixtures.example_config.verification_url)
+ stub_request(:post, config.verification_url)
.to_return(body: verification_response)
end
describe '#proof' do
describe 'individual attributes' do
subject(:result) do
- described_class.new(AamvaFixtures.example_config.to_h).proof(state_id_data)
+ described_class.new(config.to_h).proof(applicant.compact_blank)
end
def self.when_missing(&block)
@@ -58,6 +81,10 @@ def self.when_missing(&block)
)
end
+ before do
+ applicant[attribute] = nil
+ end
+
instance_eval(&block)
end
end
@@ -81,6 +108,7 @@ def self.test_in_requested_attributes(logged_attribute = nil)
it "does not stop #{logged_attribute} from appearing in requested_attributes" do
expect(result.requested_attributes).to include(logged_attribute => 1)
end
+
it 'does not itself appear in requested_attributes' do
expect(result.requested_attributes).not_to include(attribute => 1)
end
@@ -187,7 +215,7 @@ def self.test_not_successful
end
describe '#state' do
- let(:attribute) { :city }
+ let(:attribute) { :state }
let(:match_indicator_name) { 'AddressStateCodeMatchIndicator' }
when_unverified do
@@ -444,7 +472,7 @@ def self.test_not_successful
context 'when verification is successful' do
it 'the result is successful' do
- result = subject.proof(state_id_data)
+ result = subject.proof(applicant)
expect(result.success?).to eq(true)
# TODO: Find a better way to express this than errors
@@ -475,7 +503,7 @@ def self.test_not_successful
end
it 'includes requested_attributes' do
- result = subject.proof(state_id_data)
+ result = subject.proof(applicant)
expect(result.requested_attributes).to eq(
{
dob: 1,
@@ -483,6 +511,7 @@ def self.test_not_successful
state_id_expiration: 1,
state_id_number: 1,
state_id_type: 1,
+ state_id_jurisdiction: 1,
last_name: 1,
first_name: 1,
middle_name: 1,
@@ -507,7 +536,7 @@ def self.test_not_successful
end
it 'the result should be failed' do
- result = subject.proof(state_id_data)
+ result = subject.proof(applicant)
expect(result.success?).to eq(false)
expect(result.errors).to include(dob: ['UNVERIFIED'])
@@ -536,12 +565,13 @@ def self.test_not_successful
end
it 'includes requested_attributes' do
- result = subject.proof(state_id_data)
+ result = subject.proof(applicant)
expect(result.requested_attributes).to eq(
{
dob: 1,
state_id_expiration: 1,
state_id_issued: 1,
+ state_id_jurisdiction: 1,
state_id_number: 1,
state_id_type: 1,
last_name: 1,
@@ -567,7 +597,7 @@ def self.test_not_successful
end
it 'the result should be failed' do
- result = subject.proof(state_id_data)
+ result = subject.proof(applicant)
expect(result.success?).to eq(false)
expect(result.errors).to include(dob: ['MISSING'])
@@ -596,11 +626,12 @@ def self.test_not_successful
end
it 'includes requested_attributes' do
- result = subject.proof(state_id_data)
+ result = subject.proof(applicant)
expect(result.requested_attributes).to eq(
{
state_id_expiration: 1,
state_id_issued: 1,
+ state_id_jurisdiction: 1,
state_id_number: 1,
state_id_type: 1,
last_name: 1,
@@ -612,35 +643,12 @@ def self.test_not_successful
sex: 1,
weight: 1,
eye_color: 1,
+ dob: 1,
},
)
end
end
- context 'when issue / expiration present' do
- let(:state_id_data) do
- {
- state_id_number: '1234567890',
- state_id_jurisdiction: 'VA',
- state_id_type: 'drivers_license',
- state_id_issued: '2023-04-05',
- state_id_expiration: '2030-01-02',
- }
- end
-
- it 'includes them' do
- expect(Proofing::Aamva::Request::VerificationRequest).to receive(:new).with(
- hash_including(
- applicant: satisfy do |a|
- expect(a.state_id_data.state_id_issued).to eql('2023-04-05')
- expect(a.state_id_data.state_id_expiration).to eql('2030-01-02')
- end,
- ),
- )
- subject.proof(state_id_data)
- end
- end
-
context 'when AAMVA throws an exception' do
let(:exception) { RuntimeError.new }
@@ -650,7 +658,7 @@ def self.test_not_successful
end
it 'includes exception in result' do
- result = subject.proof(state_id_data)
+ result = subject.proof(applicant)
expect(result.success?).to eq(false)
expect(result.exception).to eq(exception)
@@ -661,7 +669,7 @@ def self.test_not_successful
let(:exception) { Proofing::TimeoutError.new }
it 'returns false for mva exception attributes in result' do
- result = subject.proof(state_id_data)
+ result = subject.proof(applicant)
expect(result.success?).to eq(false)
expect(result.exception).to eq(exception)
@@ -680,7 +688,7 @@ def self.test_not_successful
end
it 'returns true for mva_unavailable?' do
- result = subject.proof(state_id_data)
+ result = subject.proof(applicant)
expect(result.success?).to eq(false)
expect(result.exception).to eq(exception)
@@ -699,7 +707,7 @@ def self.test_not_successful
end
it 'returns true for mva_system_error?' do
- result = subject.proof(state_id_data)
+ result = subject.proof(applicant)
expect(result.success?).to eq(false)
expect(result.exception).to eq(exception)
@@ -718,7 +726,7 @@ def self.test_not_successful
end
it 'returns true for mva_timeout?' do
- result = subject.proof(state_id_data)
+ result = subject.proof(applicant)
expect(result.success?).to eq(false)
expect(result.exception).to eq(exception)
@@ -735,7 +743,7 @@ def self.test_not_successful
end
it 'sets jurisdiction_in_maintenance_window to true' do
- result = subject.proof(state_id_data)
+ result = subject.proof(applicant)
expect(result.jurisdiction_in_maintenance_window?).to eq(true)
end
end
@@ -749,7 +757,7 @@ def self.test_not_successful
end
it 'sets jurisdiction_in_maintenance_window to true' do
- result = subject.proof(state_id_data)
+ result = subject.proof(applicant)
expect(result.jurisdiction_in_maintenance_window?).to eq(true)
end
end
@@ -761,7 +769,7 @@ def self.test_not_successful
end
it 'sets jurisdiction_in_maintenance_window to false' do
- result = subject.proof(state_id_data)
+ result = subject.proof(applicant)
expect(result.jurisdiction_in_maintenance_window?).to eq(false)
end
end
diff --git a/spec/services/proofing/aamva/request/verification_request_spec.rb b/spec/services/proofing/aamva/request/verification_request_spec.rb
index 1a9a5865bd0..3a24ee8a7cd 100644
--- a/spec/services/proofing/aamva/request/verification_request_spec.rb
+++ b/spec/services/proofing/aamva/request/verification_request_spec.rb
@@ -1,26 +1,40 @@
require 'rails_helper'
RSpec.describe Proofing::Aamva::Request::VerificationRequest do
+ let(:auth_token) { 'KEYKEYKEY' }
+ let(:transaction_id) { '1234-abcd-efgh' }
+ let(:config) { AamvaFixtures.example_config }
let(:state_id_jurisdiction) { 'CA' }
let(:state_id_number) { '123456789' }
- let(:applicant) do
- Proofing::Aamva::Applicant.from_proofer_applicant(
+
+ let(:applicant_data) do
+ {
uuid: '1234-abcd-efgh',
first_name: 'Testy',
+ middle_name: nil,
last_name: 'McTesterson',
+ name_suffix: nil,
dob: '10/29/1942',
address1: '123 Sunnyside way',
+ address2: nil,
city: 'Sterling',
state: 'VA',
zipcode: '20176-1234',
+ eye_color: nil,
+ height: nil,
+ weight: nil,
+ sex: nil,
state_id_number: state_id_number,
state_id_jurisdiction: state_id_jurisdiction,
state_id_type: 'drivers_license',
- )
+ state_id_expiration: nil,
+ state_id_issued: nil,
+ }
+ end
+
+ let(:applicant) do
+ Proofing::Aamva::Applicant.from_proofer_applicant(**applicant_data.compact_blank)
end
- let(:auth_token) { 'KEYKEYKEY' }
- let(:transaction_id) { '1234-abcd-efgh' }
- let(:config) { AamvaFixtures.example_config }
subject do
described_class.new(
@@ -73,6 +87,8 @@
applicant.zipcode,
],
)
+
+ expect(subject.requested_attributes).to include(address2: :present)
end
it 'includes issue date if present' do
@@ -80,6 +96,7 @@
expect(subject.body).to include(
'2024-05-06',
)
+ expect(subject.requested_attributes).to include(state_id_issued: :present)
end
it 'includes expiration date if present' do
@@ -87,6 +104,7 @@
expect(subject.body).to include(
'2030-01-02',
)
+ expect(subject.requested_attributes).to include(state_id_expiration: :present)
end
it 'includes height if it is present' do
@@ -94,6 +112,7 @@
expect(subject.body).to include(
'63',
)
+ expect(subject.requested_attributes).to include(height: :present)
end
it 'includes weight if it is present' do
@@ -101,6 +120,7 @@
expect(subject.body).to include(
'190',
)
+ expect(subject.requested_attributes).to include(weight: :present)
end
it 'includes eye_color if it is present' do
@@ -108,6 +128,7 @@
expect(subject.body).to include(
'blu',
)
+ expect(subject.requested_attributes).to include(eye_color: :present)
end
it 'includes name_suffix if it is present' do
@@ -115,6 +136,7 @@
expect(subject.body).to include(
'JR',
)
+ expect(subject.requested_attributes).to include(name_suffix: :present)
end
it 'includes middle_name if it is present' do
@@ -122,6 +144,7 @@
expect(subject.body).to include(
'test_name',
)
+ expect(subject.requested_attributes).to include(middle_name: :present)
end
context '#sex' do
@@ -131,6 +154,7 @@
expect(subject.body).to include(
'1',
)
+ expect(subject.requested_attributes).to include(:sex)
end
end
@@ -140,6 +164,7 @@
expect(subject.body).to include(
'2',
)
+ expect(subject.requested_attributes).to include(:sex)
end
end
@@ -147,6 +172,15 @@
it 'does not send a sex code value' do
applicant.sex = nil
expect(subject.body).to_not include('')
+ expect(subject.requested_attributes).to_not include(:sex)
+ end
+ end
+
+ context 'when the sex is unsupported' do
+ it 'does not send a sex code value' do
+ applicant.sex = 'X'
+ expect(subject.body).to_not include('')
+ expect(subject.requested_attributes).to_not include(:sex)
end
end
end
@@ -158,6 +192,7 @@
expect(subject.body).to include(
'1',
)
+ expect(subject.requested_attributes).to include(:state_id_type)
end
end
@@ -167,6 +202,7 @@
expect(subject.body).to include(
'2',
)
+ expect(subject.requested_attributes).to include(:state_id_type)
end
end
@@ -176,6 +212,7 @@
expect(subject.body).to include(
'3',
)
+ expect(subject.requested_attributes).to include(:state_id_type)
end
end
@@ -183,11 +220,13 @@
it 'does not add a DocumentCategoryCode for nil ID type' do
applicant.state_id_data.state_id_type = nil
expect(subject.body).to_not include('')
+ expect(subject.requested_attributes).to_not include(:state_id_type)
end
it 'does not add a DocumentCategoryCode for invalid ID types' do
applicant.state_id_data.state_id_type = 'License to Keep an Alpaca'
expect(subject.body).to_not include('')
+ expect(subject.requested_attributes).to_not include(:state_id_type)
end
end
end
@@ -258,8 +297,46 @@
end
end
+ describe '#requested_attributes' do
+ let(:applicant_data) do
+ {
+ first_name: 'Testy',
+ last_name: 'McTesterson',
+ dob: '10/29/1942',
+ address1: '123 Sunnyside way',
+ city: 'Sterling',
+ state: 'VA',
+ zipcode: '20176-1234',
+ state_id_number: '98765421',
+ state_id_jurisdiction: 'VA',
+ state_id_type: 'drivers_license',
+ }
+ end
+
+ it 'should set present fields to :present' do
+ expect(subject.requested_attributes).to match(
+ first_name: :present,
+ last_name: :present,
+ dob: :present,
+ address1: :present,
+ city: :present,
+ state: :present,
+ zipcode: :present,
+ state_id_number: :present,
+ state_id_type: :present,
+ state_id_jurisdiction: :present,
+ )
+ end
+
+ it 'should set required blank fields to :missing' do
+ applicant.first_name = nil
+ expect(subject.requested_attributes).to include(first_name: :missing)
+ end
+ end
+
describe 'South Carolina id number padding' do
let(:state_id_jurisdiction) { 'SC' }
+
let(:rendered_state_id_number) do
body = REXML::Document.new(subject.body)
REXML::XPath.first(body, '//nc:IdentificationID')&.text