Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/features.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3105,6 +3105,11 @@ features:
va_notify_v2_in_progress_form_reminder:
actor_type: user
description: If enabled, InProgressFormReminder uses VANotify::V2::QueueUserAccountJob instead of VANotify::UserAccountJob
va_notify_v2_ivc_champva_email:
actor_type: user
description: >-
If enabled, IvcChampva::Email will use VANotify::V2::QueueEmailJob
instead of VANotify::EmailJob
va_notify_v2_meb_confirmation_email:
actor_type: user
description: >-
Expand Down
45 changes: 33 additions & 12 deletions modules/ivc_champva/app/services/ivc_champva/email.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,19 @@ def initialize(data)
def send_email
return false unless valid_environment?

VANotify::EmailJob.perform_async(
data[:email],
data[:template_id] ? EMAIL_TEMPLATE_MAP[data[:template_id]] : EMAIL_TEMPLATE_MAP[data[:form_number]],
# Create a subset of `data` - Using .index_with rather than .slice so that keys
# default to `nil` if not present in `data`. This helps us w/ testing.
%i[first_name last_name file_count pega_status date_submitted form_uuid]
.index_with { |k| data[k] },
Settings.vanotify.services.ivc_champva.api_key,
# If no callback_klass is provided, should fail safely per va_notify implementation.
# See: https://github.com/department-of-veterans-affairs/vets-api/tree/master/modules/va_notify#how-teams-can-integrate-with-callbacks
{ callback_klass: data[:callback_klass], callback_metadata: data[:callback_metadata] }
)
template_id = data[:template_id] ? EMAIL_TEMPLATE_MAP[data[:template_id]] : EMAIL_TEMPLATE_MAP[data[:form_number]]

# Create a subset of `data` - Using .index_with rather than .slice so that keys
# default to `nil` if not present in `data`. This helps us w/ testing.
personalisation = %i[first_name last_name file_count pega_status date_submitted form_uuid]
.index_with { |k| data[k] }

# If no callback_klass is provided, should fail safely per va_notify implementation.
# See: https://github.com/department-of-veterans-affairs/vets-api/tree/master/modules/va_notify#how-teams-can-integrate-with-callbacks
callback_options = { callback_klass: data[:callback_klass], callback_metadata: data[:callback_metadata] }

perform_email_send(template_id, personalisation, callback_options)

Rails.logger.info "Pega Status Update Email: #{data[:file_count].to_i} file(s)"

true
Expand All @@ -50,6 +51,26 @@ def send_email

private

def perform_email_send(template_id, personalisation, callback_options)
if Flipper.enabled?(:va_notify_v2_ivc_champva_email)
VANotify::V2::QueueEmailJob.enqueue(
data[:email],
template_id,
personalisation,
'Settings.vanotify.services.ivc_champva.api_key',
callback_options
)
else
VANotify::EmailJob.perform_async(
data[:email],
template_id,
personalisation,
Settings.vanotify.services.ivc_champva.api_key,
callback_options
)
end
end

def valid_environment?
%w[production staging].include?(Rails.env)
end
Expand Down
95 changes: 88 additions & 7 deletions modules/ivc_champva/spec/services/email_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,90 @@
}
end

let(:expected_template_id) { Settings.vanotify.services.ivc_champva.template_id.form_10_10d_email }
let(:expected_personalisation) do
%i[first_name last_name file_count pega_status date_submitted form_uuid].index_with { |k| data[k] }
end
Comment on lines +22 to +25
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected_personalisation is built with data.slice(...), but the service under test builds personalisation via %i[...].index_with { |k| data[k] }, which intentionally includes keys even when they’re missing from data (nil values). There are real call sites that omit first_name/last_name (e.g., the Pega alert payload builders), so this spec won’t catch regressions where the nil keys are dropped. Update the expectation to mirror the index_with behavior and add a case covering a payload missing optional keys.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sarahwcodes can you take a look at this?

let(:expected_callback_options) { { callback_klass: nil, callback_metadata: nil } }

describe '#send_email' do
context 'in valid environments' do
before do
allow(Rails).to receive(:env).and_return('staging')
end

context 'when va_notify_v2_ivc_champva_email flipper is disabled' do
before do
allow(Rails).to receive(:env).and_return('staging')
allow(Flipper).to receive(:enabled?).with(:va_notify_v2_ivc_champva_email).and_return(false)
end

it 'enqueues VANotify::EmailJob with correct parameters' do
expect(VANotify::EmailJob).to receive(:perform_async).with(
data[:email],
Settings.vanotify.services.ivc_champva.template_id.form_10_10d_email,
data.slice(:first_name, :last_name, :file_count, :pega_status, :date_submitted, :form_uuid),
expected_template_id,
expected_personalisation,
Settings.vanotify.services.ivc_champva.api_key,
{ callback_klass: nil, callback_metadata: nil }
expected_callback_options
)
subject.send_email
end

it 'returns true on success' do
allow(VANotify::EmailJob).to receive(:perform_async)
expect(subject.send_email).to be true
end
end

context 'when va_notify_v2_ivc_champva_email flipper is enabled' do
before do
allow(Flipper).to receive(:enabled?).with(:va_notify_v2_ivc_champva_email).and_return(true)
end

it 'enqueues VANotify::V2::QueueEmailJob with correct parameters' do
expect(VANotify::V2::QueueEmailJob).to receive(:enqueue).with(
data[:email],
expected_template_id,
expected_personalisation,
'Settings.vanotify.services.ivc_champva.api_key',
expected_callback_options
)
subject.send_email
end

it 'returns true on success' do
allow(VANotify::V2::QueueEmailJob).to receive(:enqueue)
expect(subject.send_email).to be true
end

context 'when an error occurs' do
before do
allow(VANotify::V2::QueueEmailJob).to receive(:enqueue).and_raise(StandardError.new('Test error'))
end

it 'handles the error and logs it' do
allow(Rails.logger).to receive(:error)

expect { subject.send_email }.not_to raise_error

expect(Rails.logger).to have_received(:error).with('Pega Status Update Email Error: Test error')
end
end
end

context 'in invalid environments' do
before do
allow(Rails).to receive(:env).and_return('development')
end

it 'does not enqueue VANotify::EmailJob' do
it 'does not enqueue any email job' do
expect(VANotify::EmailJob).not_to receive(:perform_async)
expect(VANotify::V2::QueueEmailJob).not_to receive(:enqueue)
subject.send_email
end
end

context 'when an error occurs' do
before do
allow(Rails).to receive(:env).and_return('staging')
allow(Flipper).to receive(:enabled?).with(:va_notify_v2_ivc_champva_email).and_return(false)
allow(VANotify::EmailJob).to receive(:perform_async).and_raise(StandardError.new('Test error'))
end

Expand All @@ -62,5 +114,34 @@
expect(Rails.logger).to have_received(:error).with('Pega Status Update Email Error: Test error')
end
end

context 'when optional keys are missing from payload' do
let(:data) do
{
email: 'pega-team@example.com',
form_number: '10-10D',
file_count: 1,
pega_status: 'Missing',
date_submitted: Time.zone.now.to_s,
form_uuid: '4171e61a-03b5-49f3-8717-dbf340310473'
}
end

before do
allow(Rails).to receive(:env).and_return('staging')
allow(Flipper).to receive(:enabled?).with(:va_notify_v2_ivc_champva_email).and_return(false)
end

it 'includes nil values for missing optional keys in personalisation' do
expect(VANotify::EmailJob).to receive(:perform_async).with(
data[:email],
expected_template_id,
hash_including(first_name: nil, last_name: nil),
Settings.vanotify.services.ivc_champva.api_key,
expected_callback_options
)
subject.send_email
end
end
end
end
Loading