diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 80695439671..0b03f0a0043 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -282,6 +282,7 @@ app/models/inherited_proof_verified_user_account.rb @department-of-veterans-affa app/models/in_progress_form.rb @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/benefits-dependents-management app/models/intent_to_file_queue_exhaustion.rb @department-of-veterans-affairs/pensions @department-of-veterans-affairs/backend-review-group app/models/ivc_champva_form.rb @department-of-veterans-affairs/champva-engineering @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group +app/models/lighthouse526_document_upload.rb @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group app/models/lighthouse_document.rb @department-of-veterans-affairs/backend-review-group app/models/maintenance_window.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group app/models/message_draft.rb @department-of-veterans-affairs/vfs-mhv-secure-messaging @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group @@ -1167,6 +1168,7 @@ spec/factories/form526_job_statuses.rb @department-of-veterans-affairs/Disabilit spec/factories/form526_submissions.rb @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/factories/form526_submission_remediations.rb @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/factories/form5655_submission.rb @department-of-veterans-affairs/vsa-debt-resolution @department-of-veterans-affairs/backend-review-group +spec/factories/form_attachments.rb @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/factories/form_submission_attempts.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/factories/form_submissions.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/factories/gi_bill_feedback.rb @department-of-veterans-affairs/my-education-benefits @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group @@ -1183,6 +1185,7 @@ spec/factories/inherited_proofing @department-of-veterans-affairs/octo-identity spec/factories/in_progress_forms @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/factories/ivc_champva_forms.rb @department-of-veterans-affairs/champva-engineering @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/factories/lighthouse @department-of-veterans-affairs/vfs-facilities-frontend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group +spec/factories/lighthouse526_document_uploads.rb @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/factories/maintenance_windows.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/factories/message_drafts.rb @department-of-veterans-affairs/vfs-mhv-secure-messaging @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/factories/message_threads.rb @department-of-veterans-affairs/vfs-mhv-secure-messaging @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group @@ -1555,6 +1558,7 @@ spec/models/inherited_proof_verified_user_account_spec.rb @department-of-veteran spec/models/in_progress_form_spec.rb @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/benefits-dependents-management spec/models/intent_to_file_queue_exhaustion_spec.rb @department-of-veterans-affairs/pensions @department-of-veterans-affairs/backend-review-group spec/models/ivc_champva_forms_spec.rb @department-of-veterans-affairs/champva-engineering @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group +spec/models/lighthouse526_document_upload_spec.rb @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/models/message_spec.rb @department-of-veterans-affairs/vfs-mhv-secure-messaging @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/models/mhv_opt_in_flag_spec.rb @department-of-veterans-affairs/vfs-mhv-secure-messaging @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/models/mpi_data_spec.rb @department-of-veterans-affairs/octo-identity diff --git a/app/models/lighthouse526_document_upload.rb b/app/models/lighthouse526_document_upload.rb new file mode 100644 index 00000000000..0b2a0708748 --- /dev/null +++ b/app/models/lighthouse526_document_upload.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +class Lighthouse526DocumentUpload < ApplicationRecord + include AASM + + VETERAN_UPLOAD_DOCUMENT_TYPE = 'Veteran Upload' + BDD_INSTRUCTIONS_DOCUMENT_TYPE = 'BDD Instructions' + FORM_0781_DOCUMENT_TYPE = 'Form 0781' + FORM_0781A_DOCUMENT_TYPE = 'Form 0781a' + + VALID_DOCUMENT_TYPES = [ + BDD_INSTRUCTIONS_DOCUMENT_TYPE, + FORM_0781_DOCUMENT_TYPE, + FORM_0781A_DOCUMENT_TYPE, + VETERAN_UPLOAD_DOCUMENT_TYPE + ].freeze + + belongs_to :form526_submission + belongs_to :form_attachment, optional: true + + validates :lighthouse_document_request_id, presence: true + validates :document_type, presence: true, inclusion: { in: VALID_DOCUMENT_TYPES } + + # Veteran Uploads must reference a FormAttachment record, where a Veteran-submitted file is stored + validates :form_attachment, presence: true, if: :veteran_upload? + + aasm do + state :pending, initial: true + state :completed, :failed + + event :complete do + transitions from: :pending, to: :completed, guard: :end_time_saved? + end + + event :fail do + transitions from: :pending, to: :failed, guard: %i[end_time_saved? error_message_saved?] + end + end + + private + + def veteran_upload? + document_type == VETERAN_UPLOAD_DOCUMENT_TYPE + end + + def end_time_saved? + lighthouse_processing_ended_at != nil + end + + def error_message_saved? + error_message != nil + end +end diff --git a/spec/factories/form_attachments.rb b/spec/factories/form_attachments.rb new file mode 100644 index 00000000000..893adf293e0 --- /dev/null +++ b/spec/factories/form_attachments.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :form_attachment do + guid { Faker::Internet.uuid } + file_data { Faker::Json.to_s } + type { 'SupportingEvidenceAttachment' } + end +end diff --git a/spec/factories/lighthouse526_document_uploads.rb b/spec/factories/lighthouse526_document_uploads.rb new file mode 100644 index 00000000000..e467954e8d1 --- /dev/null +++ b/spec/factories/lighthouse526_document_uploads.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :lighthouse526_document_upload do + association :form526_submission + association :form_attachment + lighthouse_document_request_id { Faker::Internet.uuid } + aasm_state { 'pending' } # initial status + document_type { 'BDD Instructions' } + error_message { 'Something Broke' } + lighthouse_processing_started_at { nil } + lighthouse_processing_ended_at { nil } + status_last_polled_at { nil } + last_status_response { nil } + end +end diff --git a/spec/models/lighthouse526_document_upload_spec.rb b/spec/models/lighthouse526_document_upload_spec.rb new file mode 100644 index 00000000000..7c7486b8128 --- /dev/null +++ b/spec/models/lighthouse526_document_upload_spec.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Lighthouse526DocumentUpload do + # Benefts Documents API /uploads/status endpoint payload examples available at: + # https://dev-developer.va.gov/explore/api/benefits-documents/docs?version=current + + it 'is created with an initial aasm status of pending' do + expect(build(:lighthouse526_document_upload).aasm_state).to eq('pending') + end + + context 'for a new record' do + describe 'with valid attributes' do + it 'is valid with a valid document_type' do + ['BDD Instructions', 'Form 0781', 'Form 0781a', 'Veteran Upload'].each do |document_type| + expect(build(:lighthouse526_document_upload, document_type:)).to be_valid + end + end + + context 'with a document type that is not Veteran Upload' do + it 'is valid without a form_attachment_id' do + expect(build(:lighthouse526_document_upload, document_type: 'BDD Instructions', form_attachment_id: nil)) + .to be_valid + end + end + end + + describe 'with invalid attributes' do + it 'is invalid without a form526_submission_id' do + expect(build(:lighthouse526_document_upload, form526_submission_id: nil)).not_to be_valid + end + + it 'is invalid without a lighthouse_document_request_id' do + expect(build(:lighthouse526_document_upload, lighthouse_document_request_id: nil)).not_to be_valid + end + + it 'is invalid without a document_type' do + expect(build(:lighthouse526_document_upload, document_type: nil)).not_to be_valid + end + + it 'is invalid without a valid document_type' do + expect(build(:lighthouse526_document_upload, document_type: 'Receipt')).not_to be_valid + end + + context 'with a document_type of Veteran Upload' do + it 'is invalid without a form_attachment_id' do + expect(build(:lighthouse526_document_upload, document_type: 'Veteran Upload', form_attachment_id: nil)) + .not_to be_valid + end + end + end + + describe 'state transtions' do + # Both completed and failed uploads have an end time in Lighthouse + let(:finished_lighthouse526_document_upload) do + create(:lighthouse526_document_upload, lighthouse_processing_ended_at: DateTime.now) + end + + it 'transitions to a completed state' do + expect(finished_lighthouse526_document_upload).to transition_from(:pending).to(:completed).on_event(:complete!) + end + + it 'transitions to a failed state' do + expect(finished_lighthouse526_document_upload).to transition_from(:pending).to(:failed).on_event(:fail!) + end + + describe 'transition guards' do + context 'when transitioning to a completed state' do + it 'transitions if lighthouse_processing_ended_at is saved' do + upload = create(:lighthouse526_document_upload, lighthouse_processing_ended_at: DateTime.now) + expect { upload.complete! }.not_to raise_error(AASM::InvalidTransition) + end + + it 'does not transition if no lighthouse_processing_ended_at is saved' do + upload = create(:lighthouse526_document_upload, lighthouse_processing_ended_at: nil) + expect { upload.complete! }.to raise_error(AASM::InvalidTransition) + end + end + + context 'when transitioning to a failed state' do + it 'transitions if lighthouse_processing_ended_at is saved' do + upload = create(:lighthouse526_document_upload, lighthouse_processing_ended_at: DateTime.now) + expect { upload.fail! }.not_to raise_error(AASM::InvalidTransition) + end + + it 'does not transition if no lighthouse_processing_ended_at is saved' do + upload = create(:lighthouse526_document_upload, lighthouse_processing_ended_at: nil) + expect { upload.fail! }.to raise_error(AASM::InvalidTransition) + end + + it 'transitions if error_message is saved' do + upload = create( + :lighthouse526_document_upload, + lighthouse_processing_ended_at: DateTime.now, + error_message: { status: 'Something broke' }.to_json + ) + + expect { upload.fail! }.not_to raise_error(AASM::InvalidTransition) + end + + it 'does not transition if no error_message is saved' do + upload = create(:lighthouse526_document_upload, error_message: nil) + expect { upload.fail! }.to raise_error(AASM::InvalidTransition) + end + end + end + end + end +end