diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 379e205175d..dd618a068a2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -362,6 +362,7 @@ app/policies/mpi_policy.rb @department-of-veterans-affairs/octo-identity app/policies/ppiu_policy.rb @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group app/policies/vet360_policy.rb @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group app/policies/va_profile_policy.rb @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group +app/policies/vye_policy.rb @department-of-veterans-affairs/govcio-vfep-codereviewers @department-of-veterans-affairs/backend-review-group app/serializers/appointment_serializer.rb @department-of-veterans-affairs/vfs-vaos @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group app/serializers/async_transaction @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group app/serializers/async_transaction/base_serializer.rb @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group @@ -1024,6 +1025,7 @@ lib/virtual_regional_office @department-of-veterans-affairs/va-api-engineers @de lib/vre @department-of-veterans-affairs/benefits-non-disability @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group lib/res @department-of-veterans-affairs/benefits-non-disability @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group lib/va_notify @department-of-veterans-affairs/pension-and-burials @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group +lib/vye @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/govcio-vfep-codereviewers lib/webhooks @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group lib/zero_silent_failures @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group modules/accredited_representative_portal @department-of-veterans-affairs/benefits-accredited-rep-facing-engineers @department-of-veterans-affairs/backend-review-group @@ -1064,7 +1066,7 @@ modules/veteran @department-of-veterans-affairs/lighthouse-dash @department-of-v modules/pensions @department-of-veterans-affairs/pension-and-burials @department-of-veterans-affairs/backend-review-group modules/veteran_confirmation @department-of-veterans-affairs/lighthouse-ninjapigs @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group modules/travel_pay @department-of-veterans-affairs/travel-pay-integration @department-of-veterans-affairs/backend-review-group -modules/vye @department-of-veterans-affairs/backend-review-group #@department-of-veterans-affairs/govcio-vye-codereviewers +modules/vye @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/govcio-vfep-codereviewers modules/decision_reviews @department-of-veterans-affairs/benefits-decision-reviews-be @department-of-veterans-affairs/backend-review-group postman/vets-api.pm-collection.json @department-of-veterans-affairs/backend-review-group postman/Dockerfile @department-of-veterans-affairs/backend-review-group @@ -1340,6 +1342,7 @@ spec/fixtures/supplemental_claims @department-of-veterans-affairs/benefits-decis spec/fixtures/va_profile @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/fixtures/vba_documents @department-of-veterans-affairs/lighthouse-banana-peels @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/fixtures/vbms @department-of-veterans-affairs/benefits-dependents-management @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group +spec/lib/vye @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/govcio-vfep-codereviewers spec/sidekiq/account_login_statistics_job_spec.rb @department-of-veterans-affairs/octo-identity spec/sidekiq/benefits_intake_status_job_spec.rb @department-of-veterans-affairs/platform-va-product-forms @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/sidekiq/bgs @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/benefits-dependents-management diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 2d355a5b986..be760d9fbad 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -315,6 +315,7 @@ Lint/MissingSuper: - 'modules/va_notify/lib/va_notify/service.rb' - 'modules/vaos/app/services/vaos/session_service.rb' - 'modules/veteran/app/models/veteran/user.rb' + - 'lib/vye/dgib/service.rb' # Offense count: 5 Lint/NoReturnInBeginEndBlocks: diff --git a/app/policies/vye_policy.rb b/app/policies/vye_policy.rb new file mode 100644 index 00000000000..b07fa2733a8 --- /dev/null +++ b/app/policies/vye_policy.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +VyePolicy = Struct.new(:user, :user_info) do + def access? + user.present? + end +end diff --git a/config/initializers/breakers.rb b/config/initializers/breakers.rb index 18b0b7c4f57..eff62e33c70 100644 --- a/config/initializers/breakers.rb +++ b/config/initializers/breakers.rb @@ -6,6 +6,7 @@ require 'central_mail/configuration' require 'debt_management_center/debts_configuration' require 'decision_review/configuration' +require 'vye/dgib/service' require 'dgi/automation/configuration' require 'dgi/eligibility/configuration' require 'dgi/status/configuration' @@ -84,6 +85,7 @@ SearchTypeahead::Configuration.instance.breakers_service, SearchClickTracking::Configuration.instance.breakers_service, VAOS::Configuration.instance.breakers_service, + Vye::DGIB::Configuration.instance.breakers_service, IAMSSOeOAuth::Configuration.instance.breakers_service, CovidVaccine::V0::VetextConfiguration.instance.breakers_service, VEText::Configuration.instance.breakers_service, diff --git a/config/settings.yml b/config/settings.yml index cdb11ce7577..c8afe037810 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -1442,10 +1442,19 @@ genisis: pass: bogus # Settings for connecting AFS Veteran Services +# For locahost we can use the existing certs as long as we don't call out dgi: jwt: public_key_path: modules/meb_api/spec/fixtures/dgi_public_test.pem private_key_path: modules/meb_api/spec/fixtures/dgi_private_test.pem + vye: + jwt: + public_key_path: modules/vye/spec/fixtures/dgi_public_test.pem + private_key_path: modules/vye/spec/fixtures/dgi_private_test.pem + public_ica11_rca2_key_path: modules/vye/spec/fixtures/ICA11-RCA2-combined-cert.pem + vets: + url: "" + mock: false vets: url: "https://jenkins.ld.afsp.io:32512/vets-service/v1/" # Docker setup for microservice mock: false diff --git a/lib/vye/dgib/authentication_token_service.rb b/lib/vye/dgib/authentication_token_service.rb new file mode 100644 index 00000000000..2f48056c69a --- /dev/null +++ b/lib/vye/dgib/authentication_token_service.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Vye + module DGIB + class AuthenticationTokenService + ALGORITHM_TYPE = 'RS256' + E = 'AQAB' + TYP = 'JWT' + KID = 'vye' + USE = 'sig' + SIGNING_KEY = Settings.dgi.vye.jwt.private_key_path + RSA_PRIVATE = OpenSSL::PKey::RSA.new(File.read(SIGNING_KEY)) + + def self.call + payload = { + exp: 5.minutes.from_now.to_i, # JWT expiration time (5 minutes) + nbf: Time.now.to_i, + realm_access: { + roles: ['VYE'] + } + } + + header_fields = { kid: KID, typ: TYP } + + JWT.encode payload, RSA_PRIVATE, ALGORITHM_TYPE, header_fields + end + end + end +end diff --git a/lib/vye/dgib/configuration.rb b/lib/vye/dgib/configuration.rb new file mode 100644 index 00000000000..36616a18d15 --- /dev/null +++ b/lib/vye/dgib/configuration.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Vye + module DGIB + class Configuration < Common::Client::Configuration::REST + def connection + @conn ||= Faraday.new(base_path, headers: base_request_headers, request: request_options) do |faraday| + faraday.use :breakers + faraday.ssl[:ca_file] = Settings.dgi.vye.jwt.public_ica11_rca2_key_path + faraday.request :json + faraday.use Faraday::Response::RaiseError + faraday.response :betamocks if mock_enabled? + faraday.response :snakecase, symbolize: false + faraday.response :json, content_type: /\bjson$/ # ensures only json content types parsed + faraday.adapter Faraday.default_adapter + end + end + + def base_path + Settings.dgi.vye.vets.url.to_s + end + + def service_name + 'VYE/DGIB' + end + + def mock_enabled? + Settings.dgi.vye.vets.mock || false + end + end + end +end diff --git a/lib/vye/dgib/response.rb b/lib/vye/dgib/response.rb new file mode 100644 index 00000000000..fe28fa37d75 --- /dev/null +++ b/lib/vye/dgib/response.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +require 'common/client/concerns/service_status' +require 'common/models/base' + +module Vye + module DGIB + class Response < Common::Base + include Common::Client::Concerns::ServiceStatus + + attribute :status, Integer + + def initialize(status, attributes = nil) + super(attributes) if attributes + self.status = status + end + + def ok? + status == 200 + end + + def cache? + ok? + end + + def metadata + { status: response_status } + end + + def response_status + case status + when 200 + RESPONSE_STATUS[:ok] + when 204 + RESPONSE_STATUS[:no_content] + when 403 + RESPONSE_STATUS[:not_authorized] + when 404 + RESPONSE_STATUS[:not_found] + when 500 + RESPONSE_STATUS[:internal_server_error] + else + RESPONSE_STATUS[:server_error] + end + end + end + + class ClaimantStatusResponse < Response + attribute :claimant_id, Integer + attribute :delimiting_date, String + attribute :verified_details, Array + attribute :payment_on_hold, Boolean + + def initialize(status, response = nil) + attributes = { + claimant_id: response.body['claimant_id'], + delimiting_date: response.body['delimiting_date'], + verified_details: response.body['verified_details'], + payment_on_hold: response.body['payment_on_hold'] + } + + super(status, attributes) + end + end + + class ClaimantLookupResponse < Response + attribute :claimant_id, Integer + + def initialize(status, response = nil) + attributes = { claimant_id: response.body['claimant_id'] } + + super(status, attributes) + end + end + + class VerificationRecordResponse < Response + attribute :claimant_id, Integer + attribute :delimiting_date, String + attribute :enrollment_verifications, Array + attribute :verified_details, Array + attribute :payment_on_hold, Boolean + + def initialize(status, response = nil) + attributes = { + claimant_id: response.body['claimant_id'], + delimiting_date: response.body['delimiting_date'], + enrollment_verifications: response.body['enrollment_verifications'], + verified_details: response.body['verified_details'], + payment_on_hold: response.body['payment_on_hold'] + } + + super(status, attributes) + end + end + + class VerifyClaimantResponse < Response + attribute :claimant_id, Integer + attribute :delimiting_date, String + attribute :verified_details, Array + attribute :payment_on_hold, Boolean + + def initialize(status, response = nil) + attributes = { + claimant_id: response.body['claimant_id'], + delimiting_date: response.body['delimiting_date'], + verified_details: response.body['verified_details'], + payment_on_hold: response.body['payment_on_hold'] + } + + super(status, attributes) + end + end + end +end diff --git a/lib/vye/dgib/service.rb b/lib/vye/dgib/service.rb new file mode 100644 index 00000000000..6a3b7761527 --- /dev/null +++ b/lib/vye/dgib/service.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +require 'common/client/base' +require_relative 'configuration' +require_relative 'response' +require_relative 'authentication_token_service' + +module Vye + module DGIB + class Service < Common::Client::Base + include Common::Client::Concerns::Monitoring + + STATSD_KEY_PREFIX = 'api.vye.dgib' + configuration Vye::DGIB::Configuration + + def initialize(user) + @user = user + end + + def camelize_keys_for_java_service(params) + local_params = params[0] || params + + local_params.permit!.to_h.deep_transform_keys do |key| + if key.include?('_') + split_keys = key.split('_') + split_keys.collect { |key_part| split_keys[0] == key_part ? key_part : key_part.capitalize }.join + else + key + end + end + end + + def claimant_lookup(ssn) + params = ActionController::Parameters.new({ ssn: }) + with_monitoring do + headers = request_headers + options = { timeout: 60 } + response = perform(:post, claimant_lookup_end_point, camelize_keys_for_java_service(params).to_json, headers, + options) + ClaimantLookupResponse.new(response.status, response) + end + end + + def get_claimant_status(claimant_id) + with_monitoring do + headers = request_headers + options = { timeout: 60 } + raw_response = perform(:get, claimant_status_end_point(claimant_id), {}, headers, options) + ClaimantStatusRecordResponse.new(raw_response.status, raw_response) + end + end + + # rubocop:disable Metrics/ParameterLists + def verify_claimant( + claimant_id, + verified_period_begin_date, + verified_period_end_date, + verified_through_date, + verification_method, + response_type + ) + params = ActionController::Parameters.new({ + claimant_id:, + verified_period_begin_date:, + verified_period_end_date:, + verified_through_date:, + verification_method:, + app_communication: { response_type: } + }) + # rubocop:enable Metrics/ParameterLists + + with_monitoring do + headers = request_headers + options = { timeout: 60 } + response = perform(:post, verify_claimant_end_point, camelize_keys_for_java_service(params).to_json, headers, + options) + VerifyClaimantResponse.new(response.status, response) + end + end + + def get_verification_record(claimant_id) + with_monitoring do + headers = request_headers + options = { timeout: 60 } + raw_response = perform(:get, verification_record_end_point(claimant_id), {}, headers, options) + VerificationRecordResponse.new(raw_response.status, raw_response) + end + end + + private + + def claimant_lookup_end_point + 'dgi/vye/claimantLookup' + end + + def claimant_status_end_point(claimant_id) + "verifications/vye/#{claimant_id}/status" + end + + def verify_claimant_end_point + 'verifications/vye/verify' + end + + def verification_record_end_point(claimant_id) + "verifications/vye/#{claimant_id}/verification-record" + end + + def json + nil + end + + def request_headers + { + Authorization: "Bearer #{AuthenticationTokenService.call}" + } + end + end + end +end diff --git a/modules/vye/app/controllers/vye/application_controller.rb b/modules/vye/app/controllers/vye/application_controller.rb index 8c09151afb5..84ae9cee6ee 100644 --- a/modules/vye/app/controllers/vye/application_controller.rb +++ b/modules/vye/app/controllers/vye/application_controller.rb @@ -2,8 +2,6 @@ module Vye class ApplicationController < ::ApplicationController - include Pundit::Authorization - service_tag 'verify-your-enrollment' rescue_from Pundit::NotAuthorizedError, with: -> { render json: { error: 'Forbidden' }, status: :forbidden } diff --git a/modules/vye/app/controllers/vye/v1/dgib_verifications_controller.rb b/modules/vye/app/controllers/vye/v1/dgib_verifications_controller.rb new file mode 100644 index 00000000000..bcfd236b535 --- /dev/null +++ b/modules/vye/app/controllers/vye/v1/dgib_verifications_controller.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'vye/dgib/service' + +module Vye + module V1 + class DgibVerificationsController < Vye::V1::ApplicationController + before_action { authorize :vye, :access? } + + def verification_record + response = service.get_verification_record(params[:claimant_id]) + serializer = ClaimantVerificationSerializer + process_response(response, serializer) + end + + def verify_claimant + response = service.verify_claimant( + params[:claimant_id], + params[:verified_period_begin_date], + params[:verified_period_end_date], + params[:verified_through_date], + params[:verification_method], + params.dig(:app_communication, :response_type) + ) + + serializer = VerifyClaimantSerializer + process_response(response, serializer) + end + + # the serializer for this endpoint is the same as for verify_claimant + def claimant_status + response = service.get_claimant_status(params[:claimant_id]) + serializer = VerifyClaimantSerializer + process_response(response, serializer) + end + + def claimant_lookup + response = service.claimant_lookup(current_user.ssn) + serializer = ClaimantLookupSerializer + process_response(response, serializer) + end + + private + + # Vye Services related stuff + def service + Vye::DGIB::Service.new(@current_user) + end + + def process_response(response, serializer) + Rails.logger.debug { "Processing response with status: #{response&.status}" } + case response.status + when 200 + Rails.logger.debug 'Rendering JSON response' + render json: serializer.new(response).to_json + when 204 + Rails.logger.debug 'Sending no content' + head :no_content + when 403 + Rails.logger.debug 'Sending forbidden' + head :forbidden + when 404 + Rails.logger.debug 'Sending not found' + head :not_found + when 422 + Rails.logger.debug 'Sending unprocessable entity' + head :unprocessable_entity + when nil + Rails.logger.debug 'No response from server' + else + Rails.logger.debug 'Sending internal server error' + head :internal_server_error + end + end + # End Vye Services + end + end +end diff --git a/modules/vye/app/serializers/vye/verification_serializer.rb b/modules/vye/app/serializers/vye/verification_serializer.rb index d3da3928e3c..0decff980ff 100644 --- a/modules/vye/app/serializers/vye/verification_serializer.rb +++ b/modules/vye/app/serializers/vye/verification_serializer.rb @@ -12,13 +12,13 @@ def to_json(*) def serializable_hash { - award_id: @resource.award_id, - act_begin: @resource.act_begin, - act_end: @resource.act_end, - transact_date: @resource.transact_date, - monthly_rate: @resource.monthly_rate, - number_hours: @resource.number_hours, - source_ind: @resource.source_ind + award_id: @resource&.award_id, + act_begin: @resource&.act_begin, + act_end: @resource&.act_end, + transact_date: @resource&.transact_date, + monthly_rate: @resource&.monthly_rate, + number_hours: @resource&.number_hours, + source_ind: @resource&.source_ind } end end diff --git a/modules/vye/app/serializers/vye/vye_serializer.rb b/modules/vye/app/serializers/vye/vye_serializer.rb new file mode 100644 index 00000000000..9f1e492fbc9 --- /dev/null +++ b/modules/vye/app/serializers/vye/vye_serializer.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Vye + class VyeSerializer + attr_reader :resource + + def initialize(resource) + @resource = resource + end + + def to_json(*) + Oj.dump(serializable_hash, mode: :compat, time_format: :ruby) + end + + def status + @resource&.status + end + end + + class ClaimantLookupSerializer < VyeSerializer + def serializable_hash + { + claimant_id: @resource&.claimant_id + } + end + end + + class ClaimantVerificationSerializer < VyeSerializer + def serializable_hash + { + claimant_id: @resource&.claimant_id, + delimiting_date: @resource&.delimiting_date, + enrollment_verifications: @resource&.enrollment_verifications, + verified_details: @resource&.verified_details, + payment_on_hold: @resource&.payment_on_hold + } + end + end + + class VerifyClaimantSerializer < VyeSerializer + def serializable_hash + { + claimant_id: @resource&.claimant_id, + delimiting_date: @resource&.delimiting_date, + verified_details: @resource&.verified_details, + payment_on_hold: @resource&.payment_on_hold + } + end + end +end diff --git a/modules/vye/config/routes.rb b/modules/vye/config/routes.rb index fe48109250b..87f5c14ac34 100644 --- a/modules/vye/config/routes.rb +++ b/modules/vye/config/routes.rb @@ -6,5 +6,10 @@ resource :verifications, only: [:create], path: '/verify' resource :address_changes, only: [:create], path: '/address' resource :direct_deposit_changes, only: [:create], path: '/bank_info' + + post 'dgib_verifications/verification_record', to: 'dgib_verifications#verification_record' + post 'dgib_verifications/verify_claimant', to: 'dgib_verifications#verify_claimant' + post 'dgib_verifications/claimant_status', to: 'dgib_verifications#claimant_status' + get 'dgib_verifications/claimant_lookup', to: 'dgib_verifications#claimant_lookup' end end diff --git a/modules/vye/spec/controllers/vye/v1/dgib_verifications_controller_spec.rb b/modules/vye/spec/controllers/vye/v1/dgib_verifications_controller_spec.rb new file mode 100644 index 00000000000..c823e4981b8 --- /dev/null +++ b/modules/vye/spec/controllers/vye/v1/dgib_verifications_controller_spec.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'support/controller_spec_helper' +require 'vye/vye_serializer' + +RSpec.describe Vye::V1::DgibVerificationsController, type: :controller do + routes { Vye::Engine.routes } + + let!(:current_user) { create(:user, :accountable) } + let(:claimant_id) { '1' } + + before do + subject.instance_variable_set(:@_response, ActionDispatch::Response.new) + sign_in_as(current_user) + allow_any_instance_of(ApplicationController).to receive(:validate_session).and_return(true) + allow_any_instance_of(ApplicationController).to receive(:current_user).and_return(current_user) + controller.instance_variable_set(:@current_user, current_user) + + # Mock the VyePolicy + policy = instance_double(VyePolicy, access?: true) + allow(VyePolicy).to receive(:new).and_return(policy) + # Skip the verify_authorized check + allow(controller).to receive(:verify_authorized).and_return(true) + # Allow the authorize call to pass + allow(controller).to receive(:authorize).with(:vye, :access?).and_return(true) + end + + describe '#claimant_lookup' do + let(:claimant_service_response) { create_claimant_response } + let(:serializer) { Vye::ClaimantLookupSerializer.new(claimant_service_response) } + + before do + allow_any_instance_of(Vye::DGIB::Service) + .to receive(:claimant_lookup) + .and_return(claimant_service_response) + + allow(Vye::ClaimantLookupSerializer) + .to receive(:new) + .with(claimant_service_response) + .and_return(serializer) + end + + context 'when the service returns a successful response' do + it 'calls the claimant_lookup_service' do + expect_any_instance_of(Vye::DGIB::Service).to receive(:claimant_lookup) + post :claimant_lookup + end + + it 'renders the serialized response with a 200 status' do + expect(controller).to receive(:render).with(json: serializer.serializable_hash.to_json) + post :claimant_lookup + end + end + end + + def create_claimant_response + response_struct = Struct.new(:body) + response = response_struct.new({ 'claimant_id' => 1 }) + Vye::DGIB::ClaimantLookupResponse.new(200, response) + end + + describe '#verify_claimant' do + let(:verify_claimant_response) { create_verify_claimant_response } + let(:serializer) { Vye::VerifyClaimantSerializer.new(verify_claimant_response) } + let(:verified_period_begin_date) { '2024-11-01' } + let(:verified_period_end_date) { '2024-11-30' } + let(:verified_through_date) { '2023-11-30' } + + before do + allow_any_instance_of(Vye::DGIB::Service) + .to receive(:verify_claimant) + .and_return(verify_claimant_response) + + allow(Vye::VerifyClaimantSerializer) + .to receive(:new) + .with(verify_claimant_response) + .and_return(serializer) + end + + context 'when the service returns a successful response' do + it 'calls the verify_claimant_service' do + expect_any_instance_of(Vye::DGIB::Service).to receive(:verify_claimant) + + post :verify_claimant, params: { + claimant_id: claimant_id, + verified_period_begin_date: verified_period_begin_date, + verified_period_end_date: verified_period_end_date, + verified_through_date: verified_through_date + } + end + + it 'renders the serialized response with a 200 status' do + expect(controller).to receive(:render).with(json: serializer.serializable_hash.to_json) + + post :verify_claimant, params: { + claimant_id: claimant_id, + verified_period_begin_date: verified_period_begin_date, + verified_period_end_date: verified_period_end_date, + verified_through_date: verified_through_date + } + end + end + end + + def create_verify_claimant_response + response_struct = Struct.new(:body) + response = response_struct.new( + { + 'claimant_id' => 1, + 'delimiting_date' => '2024-11-01', + 'verified_details' => { + 'benefit_type' => 'CH33', + 'verification_through_date' => '2024-11-01', + 'verification_method' => 'Initial' + }, + 'payment_on_hold' => true + } + ) + + Vye::DGIB::VerifyClaimantResponse.new(200, response) + end +end diff --git a/modules/vye/spec/fixtures/ICA11-RCA2-combined-cert.pem b/modules/vye/spec/fixtures/ICA11-RCA2-combined-cert.pem new file mode 100644 index 00000000000..4b9bb4cae2c --- /dev/null +++ b/modules/vye/spec/fixtures/ICA11-RCA2-combined-cert.pem @@ -0,0 +1,37 @@ +-----BEGIN CERTIFICATE----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCDNdZ9MEQBWAL9 +3qnW/t364q9dQZ8rVARk0KUEYSgLB9U/v10DsXABoQyYiRe4cDpI5qGUTA/dHRRr +im8pGge9g/SZ/qVjQPRUyzRllyygXO9E0hiKnEgq8PgiAzaywMagypbu9IxXmP0x +U04cQ+fXnDB5HRh/cEjN4OpJl3evImP82xCoHgQSg5GwMSN/5DgtUzcymS9pCy3C +spIhlTl4TA6tKfY/oQZGlspkoO3AMYWxypAi6mgq5UuBYWqQovQgogfmEoiIXj0L +tqDZA4syCzXCaIb3TRQSs1tqzXhoG4c5IiRU2hMSZaFgVJSJmo+cEU5uDUUlcN4s +E96nsxaXAgMBAAECggEAKp1AtVYIsDsKdbNsi9Qo+kPHWs1mSac26htNjPeVDsgT +fWnEZt+pFg5dTekwmAlzFJw7HDP77v9z012Zrsqyet2xgpZO1saQ1D1PHjOUHd0x +JLPrnpEVa0TqAfnzOOkdWbmSfbdVIVoh2XRJ6Sow0Tj8EUne2/z22E7VKOOVzn4v +PfENMUC1JYD/1IhTvrTZBLfOiZX332SPxAP25bSa3a4N1qbp2/M9Kb/RUxbZNzr/ +aAWjKpwMmxI0KDJ9LAIo5yNhZZp1oFeJM9xSeVRA6SR58X+70UnY5AAMckmizXHN +3fHEyIpG79Uejt95wWGi1NfdMTuUfZSj1gD+tWSX6QKBgQDX7nMTqknNW6d2CFx5 +VCNNbu4fc+6oZvCC5ZomtqRc4Bp/Qg0tmLSRm//rGpwFbtUjL7BrYQbEK1RPS5SA +REA7XWkhmoi3kWYptwPap0RuPUr4TUltLxb4LNPiBb2UJR4j40mm6WsZr0P8P4Jy +2ZPmmOSIu9i6PMRrEYYXvBYP2wKBgQCbjtFPK4ihM3XZLLGgQpL8+3WXUCdXNSSA ++lMpZ3V9x8ATr4Ouy4uij/9Q4ztyiLUsoDrX6Y1wUNY2F60rvqP4erf2BuN28XJT +cX9OOgld0HKzCUPXFCS3JcMuRL5uFLnHdAOu3CbzAu/Szstprzcp3diRrVZCIUvd +pTfPSWLe9QKBgQCQXyOd4Ftb7I34wvphPLolyad4bCEDg2AHhNAeVjtqxYOx1k3N +UK5aNL1VSgNLNqFW10nTYZoOixHhupzdbcSsbHYgO5xxb8uo1G7FQNTmCCEaB0Ky +t6tZ308DbeclJ/QQ9qSF7sbm82I3Q5dKhuL2aP9T1JTBYGPLWnoXYHA3xQKBgQCI +vNmwRgwr5xhIdskMnnimeEO9Zm+ucMBNv70qQA8/KT7mDvopxDJSC1HawGOZIiJf +v9j5wgraF7S0h8tpEIMCAJ2EXjnEbCiawMGu+cUR+hFY32QKw2Iiu8b9/N+H1CAZ ++xjhtBNoucQmAjE8DRDodGHSbBPOvYX6pyiPxWproQKBgEk2humgc7y7RnkV16kO +ta1JuteBnAZyCV/1myfbqJrJWsjIX7RtFJeAfV27JT/OTVsrFvAGyr4p2mYeyqgx +nzQ15o1wR/uxbO8hLoWaDSw1F6pwFgKwx4wC7TH5cH1StqPgmH30rB+uooGGKgqp +moEikt5Xw3sGj0aroIShuSIQ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgzXWfTBEAVgC/d6p1v7d ++uKvXUGfK1QEZNClBGEoCwfVP79dA7FwAaEMmIkXuHA6SOahlEwP3R0Ua4pvKRoH +vYP0mf6lY0D0VMs0ZZcsoFzvRNIYipxIKvD4IgM2ssDGoMqW7vSMV5j9MVNOHEPn +15wweR0Yf3BIzeDqSZd3ryJj/NsQqB4EEoORsDEjf+Q4LVM3MpkvaQstwrKSIZU5 +eEwOrSn2P6EGRpbKZKDtwDGFscqQIupoKuVLgWFqkKL0IKIH5hKIiF49C7ag2QOL +Mgs1wmiG900UErNbas14aBuHOSIkVNoTEmWhYFSUiZqPnBFObg1FJXDeLBPep7MW +lwIDAQAB +-----END CERTIFICATE----- diff --git a/modules/vye/spec/fixtures/claimant_lookup_response.json b/modules/vye/spec/fixtures/claimant_lookup_response.json new file mode 100644 index 00000000000..f11076e3387 --- /dev/null +++ b/modules/vye/spec/fixtures/claimant_lookup_response.json @@ -0,0 +1 @@ +{ "claimantId" : "600010259" } diff --git a/modules/vye/spec/fixtures/claimant_response.json b/modules/vye/spec/fixtures/claimant_response.json new file mode 100644 index 00000000000..a8b032c0d8e --- /dev/null +++ b/modules/vye/spec/fixtures/claimant_response.json @@ -0,0 +1,6 @@ +{ + "claimantId" : "600010259", + "delimitingDate" : "2022-02-09", + "verifiedDetails" : "['verifiedd1', 'verifiedd2', 'verifiedd3']", + "paymentOnHold" : false +} diff --git a/modules/vye/spec/fixtures/dgi_private_test.pem b/modules/vye/spec/fixtures/dgi_private_test.pem new file mode 100644 index 00000000000..afb66d9e0d5 --- /dev/null +++ b/modules/vye/spec/fixtures/dgi_private_test.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCDNdZ9MEQBWAL9 +3qnW/t364q9dQZ8rVARk0KUEYSgLB9U/v10DsXABoQyYiRe4cDpI5qGUTA/dHRRr +im8pGge9g/SZ/qVjQPRUyzRllyygXO9E0hiKnEgq8PgiAzaywMagypbu9IxXmP0x +U04cQ+fXnDB5HRh/cEjN4OpJl3evImP82xCoHgQSg5GwMSN/5DgtUzcymS9pCy3C +spIhlTl4TA6tKfY/oQZGlspkoO3AMYWxypAi6mgq5UuBYWqQovQgogfmEoiIXj0L +tqDZA4syCzXCaIb3TRQSs1tqzXhoG4c5IiRU2hMSZaFgVJSJmo+cEU5uDUUlcN4s +E96nsxaXAgMBAAECggEAKp1AtVYIsDsKdbNsi9Qo+kPHWs1mSac26htNjPeVDsgT +fWnEZt+pFg5dTekwmAlzFJw7HDP77v9z012Zrsqyet2xgpZO1saQ1D1PHjOUHd0x +JLPrnpEVa0TqAfnzOOkdWbmSfbdVIVoh2XRJ6Sow0Tj8EUne2/z22E7VKOOVzn4v +PfENMUC1JYD/1IhTvrTZBLfOiZX332SPxAP25bSa3a4N1qbp2/M9Kb/RUxbZNzr/ +aAWjKpwMmxI0KDJ9LAIo5yNhZZp1oFeJM9xSeVRA6SR58X+70UnY5AAMckmizXHN +3fHEyIpG79Uejt95wWGi1NfdMTuUfZSj1gD+tWSX6QKBgQDX7nMTqknNW6d2CFx5 +VCNNbu4fc+6oZvCC5ZomtqRc4Bp/Qg0tmLSRm//rGpwFbtUjL7BrYQbEK1RPS5SA +REA7XWkhmoi3kWYptwPap0RuPUr4TUltLxb4LNPiBb2UJR4j40mm6WsZr0P8P4Jy +2ZPmmOSIu9i6PMRrEYYXvBYP2wKBgQCbjtFPK4ihM3XZLLGgQpL8+3WXUCdXNSSA ++lMpZ3V9x8ATr4Ouy4uij/9Q4ztyiLUsoDrX6Y1wUNY2F60rvqP4erf2BuN28XJT +cX9OOgld0HKzCUPXFCS3JcMuRL5uFLnHdAOu3CbzAu/Szstprzcp3diRrVZCIUvd +pTfPSWLe9QKBgQCQXyOd4Ftb7I34wvphPLolyad4bCEDg2AHhNAeVjtqxYOx1k3N +UK5aNL1VSgNLNqFW10nTYZoOixHhupzdbcSsbHYgO5xxb8uo1G7FQNTmCCEaB0Ky +t6tZ308DbeclJ/QQ9qSF7sbm82I3Q5dKhuL2aP9T1JTBYGPLWnoXYHA3xQKBgQCI +vNmwRgwr5xhIdskMnnimeEO9Zm+ucMBNv70qQA8/KT7mDvopxDJSC1HawGOZIiJf +v9j5wgraF7S0h8tpEIMCAJ2EXjnEbCiawMGu+cUR+hFY32QKw2Iiu8b9/N+H1CAZ ++xjhtBNoucQmAjE8DRDodGHSbBPOvYX6pyiPxWproQKBgEk2humgc7y7RnkV16kO +ta1JuteBnAZyCV/1myfbqJrJWsjIX7RtFJeAfV27JT/OTVsrFvAGyr4p2mYeyqgx +nzQ15o1wR/uxbO8hLoWaDSw1F6pwFgKwx4wC7TH5cH1StqPgmH30rB+uooGGKgqp +moEikt5Xw3sGj0aroIShuSIQ +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/modules/vye/spec/fixtures/dgi_public_test.pem b/modules/vye/spec/fixtures/dgi_public_test.pem new file mode 100644 index 00000000000..7b0701605da --- /dev/null +++ b/modules/vye/spec/fixtures/dgi_public_test.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgzXWfTBEAVgC/d6p1v7d ++uKvXUGfK1QEZNClBGEoCwfVP79dA7FwAaEMmIkXuHA6SOahlEwP3R0Ua4pvKRoH +vYP0mf6lY0D0VMs0ZZcsoFzvRNIYipxIKvD4IgM2ssDGoMqW7vSMV5j9MVNOHEPn +15wweR0Yf3BIzeDqSZd3ryJj/NsQqB4EEoORsDEjf+Q4LVM3MpkvaQstwrKSIZU5 +eEwOrSn2P6EGRpbKZKDtwDGFscqQIupoKuVLgWFqkKL0IKIH5hKIiF49C7ag2QOL +Mgs1wmiG900UErNbas14aBuHOSIkVNoTEmWhYFSUiZqPnBFObg1FJXDeLBPep7MW +lwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/modules/vye/spec/fixtures/mock_verification_record_response.json b/modules/vye/spec/fixtures/mock_verification_record_response.json new file mode 100644 index 00000000000..daffa3da1f7 --- /dev/null +++ b/modules/vye/spec/fixtures/mock_verification_record_response.json @@ -0,0 +1,28 @@ +{ + "claimantId": 0, + "delimitingDate": "2024-11-01", + "enrollmentVerifications": [ + { + "verificationMonth": "December 2023", + "verificationBeginDate": "2024-11-01", + "verificationEndDate": "2024-11-01", + "verificationThroughDate": "2024-11-01", + "createdDate": "2024-11-01", + "verificationMethod": "", + "verificationResponse": "Y", + "facilityName": "string", + "totalCreditHours": 0, + "paymentTransmissionDate": "2024-11-01", + "lastDepositAmount": 0, + "remainingEntitlement": "53-01" + } + ], + "verifiedDetails": [ + { + "benefitType": "CH33", + "verifiedThroughDate": "2024-11-01", + "verificationMethod": "Initial" + } + ], + "paymentOnHold": true + } \ No newline at end of file diff --git a/modules/vye/spec/fixtures/status_response.json b/modules/vye/spec/fixtures/status_response.json new file mode 100644 index 00000000000..a8b032c0d8e --- /dev/null +++ b/modules/vye/spec/fixtures/status_response.json @@ -0,0 +1,6 @@ +{ + "claimantId" : "600010259", + "delimitingDate" : "2022-02-09", + "verifiedDetails" : "['verifiedd1', 'verifiedd2', 'verifiedd3']", + "paymentOnHold" : false +} diff --git a/modules/vye/spec/fixtures/verify_claimant_response.json b/modules/vye/spec/fixtures/verify_claimant_response.json new file mode 100644 index 00000000000..a8b032c0d8e --- /dev/null +++ b/modules/vye/spec/fixtures/verify_claimant_response.json @@ -0,0 +1,6 @@ +{ + "claimantId" : "600010259", + "delimitingDate" : "2022-02-09", + "verifiedDetails" : "['verifiedd1', 'verifiedd2', 'verifiedd3']", + "paymentOnHold" : false +} diff --git a/modules/vye/spec/serializers/claimant_verification_serializer_spec.rb b/modules/vye/spec/serializers/claimant_verification_serializer_spec.rb new file mode 100644 index 00000000000..cd3e0c7a0b4 --- /dev/null +++ b/modules/vye/spec/serializers/claimant_verification_serializer_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'rails_helper' +require Vye::Engine.root / 'spec/rails_helper' + +RSpec.describe Vye::ClaimantVerificationSerializer, type: :serializer do + let(:json_body) { 'modules/vye/spec/fixtures/claimant_response.json' } + let(:claimant_verfication) { File.read(json_body) } + let(:data) { JSON.parse(claimant_verfication) } + + it 'includes :claimant_id' do + expect(data['claimant_id']).to eq claimant_verfication['claimant_id'] + end + + it 'includes :delimiting_date' do + expect(data['delimiting_date']).to eq claimant_verfication['delimiting_date'] + end + + it 'includes :enrollment_verifications' do + expect(data['enrollment_verifications']).to eq claimant_verfication['enrollment_verifications'] + end + + it 'includes :verified_details' do + expect(data['verified_details']).to eq claimant_verfication['verified_details'] + end + + it 'includes :payment_on_hold' do + expect(data['payment_on_hold']).to eq claimant_verfication['payment_on_hold'] + end +end diff --git a/spec/lib/vye/dgib/service_spec.rb b/spec/lib/vye/dgib/service_spec.rb new file mode 100644 index 00000000000..6a13213e598 --- /dev/null +++ b/spec/lib/vye/dgib/service_spec.rb @@ -0,0 +1,116 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'vye/dgib/service' + +RSpec.describe Vye::DGIB::Service do + include ActiveSupport::Testing::TimeHelpers + let(:user) { FactoryBot.create(:user, :loa3) } + let(:service) { described_class.new(user) } + + describe '#claimant_lookup' do + let(:ssn) { '123-45-6789' } + let(:json_body) { 'modules/vye/spec/fixtures/claimant_lookup_response.json' } + let(:response_body) { JSON.parse(File.read(json_body)) } + let(:successful_mocked_response) { double('faraday_response', status: 200, body: response_body, ok?: true) } + + before do + allow(service).to receive(:claimant_lookup).with(ssn).and_return(successful_mocked_response) + end + + context 'when successful' do + it 'returns a status of 200' do + travel_to Time.zone.local(2022, 2, 9, 12) do + response = service.claimant_lookup(ssn) + + expect(response.status).to eq(200) + expect(response.ok?).to be(true) + end + end + end + end + + describe '#get_claimant_status' do + let(:claimant_id) { 600_010_259 } + let(:json_body) { 'modules/vye/spec/fixtures/claimant_response.json' } + let(:response_body) { JSON.parse(File.read(json_body)) } + let(:successful_mocked_response) { double('faraday_response', status: 200, body: response_body, ok?: true) } + + before do + allow(service).to receive(:get_claimant_status).with(claimant_id).and_return(successful_mocked_response) + end + + context 'when successful' do + it 'returns a status of 200' do + travel_to Time.zone.local(2022, 2, 9, 12) do + response = service.get_claimant_status(claimant_id) + expect(response.status).to eq(200) + expect(response.ok?).to be(true) + end + end + end + end + + describe '#get_verification_record' do + let(:claimant_id) { 600_010_259 } + let(:json_body) { 'modules/vye/spec/fixtures/claimant_response.json' } + let(:response_body) { JSON.parse(File.read(json_body)) } + let(:successful_mocked_response) { double('faraday_response', status: 200, body: response_body, ok?: true) } + + before do + allow(service).to receive(:get_verification_record).with(claimant_id).and_return(successful_mocked_response) + end + + context 'when successful' do + it 'returns a status of 200' do + travel_to Time.zone.local(2022, 2, 9, 12) do + response = service.get_verification_record(claimant_id) + expect(response.status).to eq(200) + expect(response.ok?).to be(true) + end + end + end + end + + describe '#get_verify_claimant' do + let(:claimant_id) { 600_010_259 } + let(:verified_period_begin_date) { '2022-01-01' } + let(:verified_period_end_date) { '2022-01-31' } + let(:verified_through_date) { '2022-01-31' } + let(:verification_method) { 'Initial' } + let(:response_type) { '200' } + let(:json_body) { 'modules/vye/spec/fixtures/claimant_lookup_response.json' } + let(:response_body) { JSON.parse(File.read(json_body)) } + let(:successful_mocked_response) { double('faraday_response', status: 200, body: response_body, ok?: true) } + + before do + allow(service).to receive(:verify_claimant) + .with( + claimant_id, + verified_period_begin_date, + verified_period_end_date, + verified_through_date, + verification_method, + response_type + ) + .and_return(successful_mocked_response) + end + + context 'when successful' do + it 'returns a status of 200' do + travel_to Time.zone.local(2022, 2, 9, 12) do + response = service + .verify_claimant(claimant_id, + verified_period_begin_date, + verified_period_end_date, + verified_through_date, + verification_method, + response_type) + + expect(response.status).to eq(200) + expect(response.ok?).to be(true) + end + end + end + end +end