+
\ No newline at end of file
diff --git a/app/views/state_file/state_file_pages/about_page.html.erb b/app/views/state_file/state_file_pages/about_page.html.erb
index 7f60c9e235..b9d54ff9d2 100644
--- a/app/views/state_file/state_file_pages/about_page.html.erb
+++ b/app/views/state_file/state_file_pages/about_page.html.erb
@@ -42,4 +42,13 @@
<% end %>
-<% end %>
\ No newline at end of file
+<% end %>
+
+
+ <% if Flipper.enabled?(:get_your_pdf) %>
+ <%= t(".looking_for_return_html")%>
+
diff --git a/config/locales/en.yml b/config/locales/en.yml
index bbc9fe8516..d054bfb7be 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -2063,6 +2063,16 @@ en:
04_detail_links_html: See our Terms and Conditions and Privacy Policy.
subheader: Please sign up here to receive a notification when we open in January.
state_file:
+ archived_intakes:
+ email_address:
+ edit:
+ enter_email: Enter the email address linked to your account
+ verification_code:
+ edit:
+ error_message: Incorrect verification code. After 2 failed attempts, accounts are locked.
+ subtitle_html: If you didn’t receive a code, please check your spam folder.
+ subtitle_html_2: If you still need help resubscribing, chat with us.
+ title_html: We’ve sent your code to %{email_address}
faq:
index:
title: Common questions from %{state} taxpayers
@@ -3828,6 +3838,7 @@ en:
We're closed for the tax season. Unfortunately you can no longer file your state return with us this year.
Already filed your state taxes with us? You can download a copy of your state return until December 31, 2024
header: A free state filing service for taxpayers using IRS Direct File
+ looking_for_return_html: "Looking for your 2023 Arizona or New York State Tax Return?"
section1_html: |
How does it work?
@@ -3842,6 +3853,7 @@ en:
subheader_1_html: "FileYourStateTaxes integrates with IRS Direct File to help you complete your state tax return for free."
subheader_2_html: IRS Direct File is now broadly available to all eligible taxpayers through April 15, 2024. For more information on timeline and eligibility, or to start filing your federal return, go to directfile.irs.gov
subheader_3_html: Already filed your federal return with IRS Direct File and need to complete your state return? Sign in here.
+ tax_return_link: Click here to access your tax return
card_postscript:
responses_saved_html: Your responses are saved. If you need a break, you can come back and log in to your account at fileyourstatetaxes.org.
coming_soon:
diff --git a/config/locales/es.yml b/config/locales/es.yml
index b8eab4a065..ea12e07f5f 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -2015,6 +2015,16 @@ es:
04_detail_links_html: Favor de consultar nuestras Condiciones generales y Política de privacidad.
subheader: Regístrese aquí para recibir una notificación cuando abramos en enero.
state_file:
+ archived_intakes:
+ email_address:
+ edit:
+ enter_email: Ingresa la dirección de correo electrónico vinculada a tu cuenta
+ verification_code:
+ edit:
+ error_message: Después de 2 intentos fallidos, las cuentas serán bloqueadas.
+ subtitle_html: Si no recibiste un código, revisa tu carpeta de spam. Si aún necesitas ayuda para volver a suscribirte, chatea con nosotros.
+ subtitle_html_2: Si aún necesitas ayuda para resuscribirte, chatea con nosotros.
+ title_html: Hemos enviado tu código a %{email_address}
faq:
index:
title: 'Preguntas frecuentes de los contribuyentes de %{state}:'
@@ -3802,6 +3812,7 @@ es:
Estamos cerrados por la temporada de impuestos. Desafortunadamente, no puede presentar tu declaración estatal con nosotros este año.
¿Ya presentó tus impuestos estatales con nosotros? Puedes descargar una copia de tu declaración estatal hasta el 31 de diciembre de 2024
header: Una herramienta sin costo para presentar impuestos estatales para los contribuyentes utilizando IRS Direct File
+ looking_for_return_html: "¿Está buscando su declaración de impuestos del estado de Arizona o Nueva York de 2023?"
section1_html: |
¿Cómo funciona?
@@ -3816,6 +3827,7 @@ es:
subheader_1_html: "FileYourStateTaxes se integra con IRS Direct File para ayudarte a completar tu declaración de impuestos estatales sin costo."
subheader_2_html: El IRS Direct File ahora está ampliamente disponible para todos los contribuyentes elegibles hasta el 15 de abril de 2024. Para obtener más información sobre el cronograma y la elegibilidad, o para comenzar a presentar tu declaración federal, ve a directfile.irs.gov.
subheader_3_html: "¿Ya presentaste tu declaración federal con IRS Direct File y necesitas completar tu declaración estatal? Inicia sesión aquí."
+ tax_return_link: Haga clic aquí para acceder a su declaración de impuestos.
card_postscript:
responses_saved_html: Tus respuestas han sido guardadas. Si necesitas hacer una pausa, puedes regresar e iniciar sesión en tu cuenta en fileyourstatetaxes.org.
coming_soon:
diff --git a/config/routes.rb b/config/routes.rb
index 6d167bb0ce..0b7da310f9 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,5 @@
Rails.application.routes.draw do
+ devise_for :state_file_archived_intake_requests
active_state_codes = StateFile::StateInformationService.active_state_codes
active_state_codes.each do |code|
@@ -555,6 +556,12 @@ def scoped_navigation_routes(context, navigation)
constraints(Routes::StateFileDomain.new) do
scope '(:locale)', locale: /#{I18n.available_locales.join('|')}/ do
namespace :state_file do
+ namespace :archived_intakes do
+ get 'email_address/edit', to: 'email_address#edit', as: 'edit_email_address'
+ patch 'email_address', to: 'email_address#update'
+ get 'verification_code/edit', to: 'verification_code#edit', as: 'edit_verification_code'
+ patch 'verification_code', to: 'verification_code#update'
+ end
namespace :questions do
get "show_xml", to: "confirmation#show_xml"
get "explain_calculations", to: "confirmation#explain_calculations"
diff --git a/db/migrate/20241227174414_create_state_file_archived_intake_access_logs.rb b/db/migrate/20241227174414_create_state_file_archived_intake_access_logs.rb
index 0462ef3a36..9a0b9c385b 100644
--- a/db/migrate/20241227174414_create_state_file_archived_intake_access_logs.rb
+++ b/db/migrate/20241227174414_create_state_file_archived_intake_access_logs.rb
@@ -3,7 +3,6 @@ def change
create_table :state_file_archived_intake_access_logs do |t|
t.belongs_to 'state_file_archived_intakes', foreign_key: true
t.integer 'event_type'
- t.string 'ip_address'
t.jsonb 'details', default: '{}'
t.timestamps
end
diff --git a/db/migrate/20250108221853_state_file_archived_intake_requests.rb b/db/migrate/20250108221853_state_file_archived_intake_requests.rb
new file mode 100644
index 0000000000..55c19af010
--- /dev/null
+++ b/db/migrate/20250108221853_state_file_archived_intake_requests.rb
@@ -0,0 +1,14 @@
+class StateFileArchivedIntakeRequests < ActiveRecord::Migration[7.1]
+ def change
+ create_table :state_file_archived_intake_requests do |t|
+ t.belongs_to :state_file_archived_intakes, foreign_key: true
+ t.string 'ip_address'
+ t.string 'email_address'
+ t.jsonb 'details', default: '{}'
+ t.timestamps
+ end
+
+ remove_foreign_key :state_file_archived_intake_access_logs, :state_file_archived_intakes
+ safety_assured { remove_column :state_file_archived_intake_access_logs, :state_file_archived_intakes_id }
+ end
+end
diff --git a/db/migrate/20250108223733_add_devise_to_state_file_archived_intake_requests.rb b/db/migrate/20250108223733_add_devise_to_state_file_archived_intake_requests.rb
new file mode 100644
index 0000000000..4f453ec1fb
--- /dev/null
+++ b/db/migrate/20250108223733_add_devise_to_state_file_archived_intake_requests.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class AddDeviseToStateFileArchivedIntakeRequests < ActiveRecord::Migration[7.1]
+ def change
+ add_column :state_file_archived_intake_requests, :failed_attempts, :integer, default: 0, null: false
+ add_column :state_file_archived_intake_requests, :locked_at, :datetime
+ end
+end
diff --git a/db/migrate/20250108230315_add_foreign_keyto_state_file_archived_intake_access_logs.rb b/db/migrate/20250108230315_add_foreign_keyto_state_file_archived_intake_access_logs.rb
new file mode 100644
index 0000000000..062afa8263
--- /dev/null
+++ b/db/migrate/20250108230315_add_foreign_keyto_state_file_archived_intake_access_logs.rb
@@ -0,0 +1,6 @@
+class AddForeignKeytoStateFileArchivedIntakeAccessLogs < ActiveRecord::Migration[7.1]
+ def change
+ add_column :state_file_archived_intake_access_logs, :state_file_archived_intake_request_id, :bigint
+ add_foreign_key :state_file_archived_intake_access_logs, :state_file_archived_intake_requests, column: :state_file_archived_intake_request_id, validate: false
+ end
+end
diff --git a/db/migrate/20250108231212_validate_foreign_keyfor_state_file_archived_intake_access_logs.rb b/db/migrate/20250108231212_validate_foreign_keyfor_state_file_archived_intake_access_logs.rb
new file mode 100644
index 0000000000..0f849150dc
--- /dev/null
+++ b/db/migrate/20250108231212_validate_foreign_keyfor_state_file_archived_intake_access_logs.rb
@@ -0,0 +1,5 @@
+class ValidateForeignKeyforStateFileArchivedIntakeAccessLogs < ActiveRecord::Migration[7.1]
+ def change
+ validate_foreign_key :state_file_archived_intake_access_logs, :state_file_archived_intake_requests
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 8f0ae6dbfa..40c98bce9e 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1688,10 +1688,20 @@
t.datetime "created_at", null: false
t.jsonb "details", default: "{}"
t.integer "event_type"
+ t.bigint "state_file_archived_intake_request_id"
+ t.datetime "updated_at", null: false
+ end
+
+ create_table "state_file_archived_intake_requests", force: :cascade do |t|
+ t.datetime "created_at", null: false
+ t.jsonb "details", default: "{}"
+ t.string "email_address"
+ t.integer "failed_attempts", default: 0, null: false
t.string "ip_address"
+ t.datetime "locked_at"
t.bigint "state_file_archived_intakes_id"
t.datetime "updated_at", null: false
- t.index ["state_file_archived_intakes_id"], name: "idx_on_state_file_archived_intakes_id_e878049c06"
+ t.index ["state_file_archived_intakes_id"], name: "idx_on_state_file_archived_intakes_id_31501c23f8"
end
create_table "state_file_archived_intakes", force: :cascade do |t|
@@ -2829,7 +2839,8 @@
add_foreign_key "site_coordinator_roles_vita_partners", "site_coordinator_roles"
add_foreign_key "site_coordinator_roles_vita_partners", "vita_partners"
add_foreign_key "source_parameters", "vita_partners"
- add_foreign_key "state_file_archived_intake_access_logs", "state_file_archived_intakes", column: "state_file_archived_intakes_id"
+ add_foreign_key "state_file_archived_intake_access_logs", "state_file_archived_intake_requests"
+ add_foreign_key "state_file_archived_intake_requests", "state_file_archived_intakes", column: "state_file_archived_intakes_id"
add_foreign_key "state_routing_fractions", "state_routing_targets"
add_foreign_key "state_routing_fractions", "vita_partners"
add_foreign_key "system_notes", "clients"
diff --git a/spec/controllers/state_file/archived_intake/archived_intake_controller_spec.rb b/spec/controllers/state_file/archived_intake/archived_intake_controller_spec.rb
new file mode 100644
index 0000000000..299a0f3bac
--- /dev/null
+++ b/spec/controllers/state_file/archived_intake/archived_intake_controller_spec.rb
@@ -0,0 +1,58 @@
+require 'rails_helper'
+
+describe StateFile::ArchivedIntakes::ArchivedIntakeController, type: :controller do
+ let(:ip_address) { '192.168.0.1' }
+ let(:email_address) { 'test@example.com' }
+ let(:request_instance) { instance_double(StateFileArchivedIntakeRequest) }
+
+ before do
+ allow(controller).to receive(:ip_for_irs).and_return(ip_address)
+ allow(session).to receive(:[]).with(:email_address).and_return(email_address)
+ end
+
+ describe '#current_request' do
+ it 'finds the StateFileArchivedIntakeRequest by IP and email address' do
+ expect(StateFileArchivedIntakeRequest).to receive(:find_by).with(
+ ip_address: ip_address,
+ email_address: email_address
+ ).and_return(request_instance)
+
+ expect(controller.current_request).to eq(request_instance)
+ end
+
+ it 'returns nil if no request is found' do
+ expect(StateFileArchivedIntakeRequest).to receive(:find_by).with(
+ ip_address: ip_address,
+ email_address: email_address
+ ).and_return(nil)
+
+ expect(controller.current_request).to be_nil
+ end
+ end
+
+ describe '#create_state_file_access_log' do
+ let(:event_type) { 'access_granted' }
+ let(:access_log_instance) { instance_double(StateFileArchivedIntakeAccessLog) }
+
+ before do
+ allow(controller).to receive(:current_request).and_return(request_instance)
+ end
+
+ it 'creates a StateFileArchivedIntakeAccessLog with the correct attributes' do
+ expect(StateFileArchivedIntakeAccessLog).to receive(:create!).with(
+ event_type: event_type,
+ state_file_archived_intake_request: request_instance
+ ).and_return(access_log_instance)
+
+ controller.create_state_file_access_log(event_type)
+ end
+
+ it 'raises an error if the log cannot be created' do
+ allow(StateFileArchivedIntakeAccessLog).to receive(:create!).and_raise(ActiveRecord::RecordInvalid)
+
+ expect {
+ controller.create_state_file_access_log(event_type)
+ }.to raise_error(ActiveRecord::RecordInvalid)
+ end
+ end
+end
diff --git a/spec/controllers/state_file/archived_intake/email_address_controller_spec.rb b/spec/controllers/state_file/archived_intake/email_address_controller_spec.rb
new file mode 100644
index 0000000000..84a0cd9d4d
--- /dev/null
+++ b/spec/controllers/state_file/archived_intake/email_address_controller_spec.rb
@@ -0,0 +1,85 @@
+require "rails_helper"
+
+RSpec.describe StateFile::ArchivedIntakes::EmailAddressController, type: :controller do
+ before do
+ Flipper.enable(:get_your_pdf)
+ end
+ describe "GET #edit" do
+ it "renders the edit template with a new EmailAddressForm" do
+ get :edit
+
+ expect(assigns(:form)).to be_a(StateFile::ArchivedIntakes::EmailAddressForm)
+ expect(response).to render_template(:edit)
+ end
+ end
+
+ describe "POST #update" do
+ let(:valid_email_address) { "test@example.com" }
+ let(:invalid_email_address) { "" }
+ let(:ip_address) { "127.0.0.1" }
+
+ before do
+ allow(controller).to receive(:ip_for_irs).and_return(ip_address)
+ end
+
+ context "when the form is valid" do
+ context "and a archived intake exists with the email address" do
+ let!(:archived_intake) { create :state_file_archived_intake, email_address: valid_email_address }
+ it "creates an access log create a request and redirects to the verification code page" do
+ post :update, params: {
+ state_file_archived_intakes_email_address_form: { email_address: valid_email_address }
+ }
+ expect(assigns(:form)).to be_valid
+
+ request = StateFileArchivedIntakeRequest.last
+ expect(request.ip_address).to eq(ip_address)
+ expect(request.email_address).to eq(valid_email_address)
+ expect(request.state_file_archived_intakes_id).to eq(archived_intake.id)
+
+ log = StateFileArchivedIntakeAccessLog.last
+ expect(log.state_file_archived_intake_request_id).to eq(request.id)
+ expect(log.event_type).to eq("issued_email_challenge")
+
+ expect(response).to redirect_to(
+ state_file_archived_intakes_edit_verification_code_path
+ )
+ end
+ end
+
+ context "and a archived does not exist with the email address" do
+ it "creates an access log create a request and redirects to the verification code page" do
+ post :update, params: {
+ state_file_archived_intakes_email_address_form: { email_address: valid_email_address }
+ }
+ expect(assigns(:form)).to be_valid
+
+ request = StateFileArchivedIntakeRequest.last
+ expect(request.ip_address).to eq(ip_address)
+ expect(request.email_address).to eq(valid_email_address)
+ expect(request.state_file_archived_intakes_id).to eq(nil)
+
+ log = StateFileArchivedIntakeAccessLog.last
+ expect(log.state_file_archived_intake_request_id).to eq(request.id)
+ expect(log.event_type).to eq("issued_email_challenge")
+
+ expect(response).to redirect_to(
+ state_file_archived_intakes_edit_verification_code_path
+ )
+ end
+ end
+ end
+ context "when the form is invalid" do
+ it "renders the edit template" do
+ post :update, params: {
+ state_file_archived_intakes_email_address_form: { email_address: invalid_email_address }
+ }
+
+ expect(assigns(:form)).not_to be_valid
+
+ expect(StateFileArchivedIntakeAccessLog.count).to eq(0)
+
+ expect(response).to render_template(:edit)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/state_file/archived_intake/verification_code_controller_spec.rb b/spec/controllers/state_file/archived_intake/verification_code_controller_spec.rb
new file mode 100644
index 0000000000..d984941885
--- /dev/null
+++ b/spec/controllers/state_file/archived_intake/verification_code_controller_spec.rb
@@ -0,0 +1,83 @@
+require "rails_helper"
+
+RSpec.describe StateFile::ArchivedIntakes::VerificationCodeController, type: :controller do
+ let(:current_request) { create(:state_file_archived_intake_request, failed_attempts: 0) }
+ let(:email_address) { "test@example.com" }
+ let(:valid_verification_code) { "123456" }
+ let(:invalid_verification_code) { "654321" }
+
+ before do
+ Flipper.enable(:get_your_pdf)
+ allow(controller).to receive(:current_request).and_return(current_request)
+ allow(current_request).to receive(:email_address).and_return(email_address)
+ allow(I18n).to receive(:locale).and_return(:en)
+ end
+
+ describe "GET #edit" do
+ it "renders the edit template with a new VerificationCodeForm and queues a job" do
+ expect(ArchivedIntakeEmailVerificationCodeJob).to receive(:perform_later).with(
+ email_address: email_address,
+ locale: :en
+ )
+
+ get :edit
+
+ expect(assigns(:form)).to be_a(StateFile::ArchivedIntakes::VerificationCodeForm)
+ expect(assigns(:email_address)).to eq(email_address)
+ expect(response).to render_template(:edit)
+ end
+ end
+
+ describe "POST #update" do
+ context "with a valid verification code" do
+ before do
+ allow_any_instance_of(StateFile::ArchivedIntakes::VerificationCodeForm).to receive(:valid?).and_return(true)
+ end
+
+ it "creates a success access log and does not increment failed_attempts" do
+ expect {
+ post :update, params: { state_file_archived_intakes_verification_code_form: { verification_code: valid_verification_code } }
+ }.to change(StateFileArchivedIntakeAccessLog, :count).by(1)
+
+ log = StateFileArchivedIntakeAccessLog.last
+ expect(log.event_type).to eq("correct_email_code")
+ expect(current_request.failed_attempts).to eq(0)
+ expect(response).to redirect_to(root_path)
+ end
+ end
+
+ context "with an invalid verification code" do
+ before do
+ allow_any_instance_of(StateFile::ArchivedIntakes::VerificationCodeForm).to receive(:valid?).and_return(false)
+ end
+
+ it "creates a failure access log, increments failed_attempts, and re-renders edit on first failed attempt" do
+ expect {
+ post :update, params: { state_file_archived_intakes_verification_code_form: { verification_code: invalid_verification_code } }
+ }.to change(StateFileArchivedIntakeAccessLog, :count).by(1)
+
+ log = StateFileArchivedIntakeAccessLog.last
+ expect(log.event_type).to eq("incorrect_email_code")
+
+ expect(current_request.reload.failed_attempts).to eq(1)
+ expect(assigns(:form)).to be_a(StateFile::ArchivedIntakes::VerificationCodeForm)
+ expect(response).to render_template(:edit)
+ end
+
+ it "locks the account and redirects to root path after multiple failed attempts" do
+ current_request.update!(failed_attempts: 1)
+
+ expect {
+ post :update, params: { state_file_archived_intakes_verification_code_form: { verification_code: invalid_verification_code } }
+ }.to change(StateFileArchivedIntakeAccessLog, :count).by(2)
+
+ log = StateFileArchivedIntakeAccessLog.last
+ expect(log.event_type).to eq("client_lockout_begin")
+
+ expect(current_request.reload.failed_attempts).to eq(2)
+ expect(current_request.reload.access_locked?).to be_truthy
+ expect(response).to redirect_to(root_path)
+ end
+ end
+ end
+end
diff --git a/spec/factories/state_file/state_file_archived_intake_requests.rb b/spec/factories/state_file/state_file_archived_intake_requests.rb
new file mode 100644
index 0000000000..bc6b96dc4c
--- /dev/null
+++ b/spec/factories/state_file/state_file_archived_intake_requests.rb
@@ -0,0 +1,11 @@
+FactoryBot.define do
+ factory :state_file_archived_intake_request do
+ email_address { "geddy_lee@gmail.com" }
+ failed_attempts { 0 }
+ locked_at { nil }
+
+ trait :locked do
+ locked_at { Time.current }
+ end
+ end
+end
diff --git a/spec/factories/state_file_archived_intake_access_logs.rb b/spec/factories/state_file_archived_intake_access_logs.rb
index 38637da7b9..6c094cd6dd 100644
--- a/spec/factories/state_file_archived_intake_access_logs.rb
+++ b/spec/factories/state_file_archived_intake_access_logs.rb
@@ -2,21 +2,16 @@
#
# Table name: state_file_archived_intake_access_logs
#
-# id :bigint not null, primary key
-# details :jsonb
-# event_type :integer
-# ip_address :string
-# created_at :datetime not null
-# updated_at :datetime not null
-# state_file_archived_intakes_id :bigint
-#
-# Indexes
-#
-# idx_on_state_file_archived_intakes_id_e878049c06 (state_file_archived_intakes_id)
+# id :bigint not null, primary key
+# details :jsonb
+# event_type :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# state_file_archived_intake_request_id :bigint
#
# Foreign Keys
#
-# fk_rails_... (state_file_archived_intakes_id => state_file_archived_intakes.id)
+# fk_rails_... (state_file_archived_intake_request_id => state_file_archived_intake_requests.id)
#
FactoryBot.define do
factory :state_file_archived_intake_access_log do
diff --git a/spec/factories/state_file_archived_intakes.rb b/spec/factories/state_file_archived_intakes.rb
index 0f68fb4bac..3c61fa00a5 100644
--- a/spec/factories/state_file_archived_intakes.rb
+++ b/spec/factories/state_file_archived_intakes.rb
@@ -17,6 +17,15 @@
#
FactoryBot.define do
factory :state_file_archived_intake do
+ email_address { "geddy_lee@example.com" }
+ hashed_ssn { "hashed_ssn_value" }
+ mailing_apartment { "Apt 1" }
+ mailing_city { "Test City" }
+ mailing_state { "CA" }
+ mailing_street { "123 Test Street" }
+ mailing_zip { "12345" }
+ state_code { "CA" }
+ tax_year { 2023 }
submission_pdf { nil }
end
-end
+end
\ No newline at end of file
diff --git a/spec/forms/state_file/archived_intakes/email_address_form_spec.rb b/spec/forms/state_file/archived_intakes/email_address_form_spec.rb
new file mode 100644
index 0000000000..8f3b88d4d9
--- /dev/null
+++ b/spec/forms/state_file/archived_intakes/email_address_form_spec.rb
@@ -0,0 +1,55 @@
+require "rails_helper"
+
+RSpec.describe StateFile::ArchivedIntakes::EmailAddressForm do
+ describe "#valid?" do
+ context "when the email address is valid" do
+ it "returns true" do
+ form = StateFile::ArchivedIntakes::EmailAddressForm.new(email_address: "test@example.com")
+
+ expect(form.valid?).to be true
+ end
+ end
+
+ context "when the email address is invalid" do
+ it "returns false for an improperly formatted email" do
+ form = StateFile::ArchivedIntakes::EmailAddressForm.new(email_address: "invalid-email")
+
+ expect(form.valid?).to be false
+ expect(form.errors[:email_address]).to include("Please enter a valid email address.")
+ end
+
+ it "returns false when the email is blank" do
+ form = StateFile::ArchivedIntakes::EmailAddressForm.new(email_address: "")
+
+ expect(form.valid?).to be false
+ expect(form.errors[:email_address]).to include("Can't be blank.")
+ end
+ end
+ end
+
+ describe "#save" do
+ context "when the form is valid" do
+ it "returns true" do
+ form = StateFile::ArchivedIntakes::EmailAddressForm.new(email_address: "test@example.com")
+
+ expect(form.save).to be true
+ end
+ end
+
+ context "when the form is invalid" do
+ it "returns false" do
+ form = StateFile::ArchivedIntakes::EmailAddressForm.new(email_address: "")
+
+ expect(form.save).to be false
+ end
+ end
+ end
+
+ describe "#initialize" do
+ it "assigns attributes correctly" do
+ form = StateFile::ArchivedIntakes::EmailAddressForm.new(email_address: "test@example.com")
+
+ expect(form.email_address).to eq("test@example.com")
+ end
+ end
+end
diff --git a/spec/forms/state_file/archived_intakes/verification_code_form_spec.rb b/spec/forms/state_file/archived_intakes/verification_code_form_spec.rb
new file mode 100644
index 0000000000..3bce7d1a55
--- /dev/null
+++ b/spec/forms/state_file/archived_intakes/verification_code_form_spec.rb
@@ -0,0 +1,100 @@
+require "rails_helper"
+
+RSpec.describe StateFile::ArchivedIntakes::VerificationCodeForm do
+ describe "#valid?" do
+ context "when the verification code is present and valid" do
+ it "returns true" do
+ allow(VerificationCodeService).to receive(:hash_verification_code_with_contact_info)
+ .with("test@example.com", "123456")
+ .and_return("hashed_code")
+
+ allow(EmailAccessToken).to receive_message_chain(:lookup, :exists?).and_return(true)
+
+ form = StateFile::ArchivedIntakes::VerificationCodeForm.new(
+ { verification_code: "123456" },
+ email_address: "test@example.com"
+ )
+
+ expect(form.valid?).to be true
+ end
+ end
+
+ context "when the verification code is present but invalid" do
+ it "adds an error and returns false" do
+ allow(VerificationCodeService).to receive(:hash_verification_code_with_contact_info)
+ .with("test@example.com", "123456")
+ .and_return("hashed_code")
+
+ allow(EmailAccessToken).to receive_message_chain(:lookup, :exists?).and_return(false)
+
+ form = StateFile::ArchivedIntakes::VerificationCodeForm.new(
+ { verification_code: "123456" },
+ email_address: "test@example.com"
+ )
+
+ expect(form.valid?).to be false
+ expect(form.errors[:verification_code]).to include("Incorrect verification code. After 2 failed attempts, accounts are locked.")
+ end
+ end
+
+ context "when the verification code is blank" do
+ it "adds an error and returns false" do
+ form = StateFile::ArchivedIntakes::VerificationCodeForm.new(
+ { verification_code: "" },
+ email_address: "test@example.com"
+ )
+
+ expect(form.valid?).to be false
+ expect(form.errors[:verification_code]).to include("Incorrect verification code. After 2 failed attempts, accounts are locked.")
+ end
+ end
+ end
+
+ describe "#save" do
+ context "when the form is valid" do
+ it "returns true" do
+ allow(VerificationCodeService).to receive(:hash_verification_code_with_contact_info)
+ .with("test@example.com", "123456")
+ .and_return("hashed_code")
+
+ allow(EmailAccessToken).to receive_message_chain(:lookup, :exists?).and_return(true)
+
+ form = StateFile::ArchivedIntakes::VerificationCodeForm.new(
+ { verification_code: "123456" },
+ email_address: "test@example.com"
+ )
+
+ expect(form.save).to be true
+ end
+ end
+
+ context "when the form is invalid" do
+ it "returns false" do
+ allow(VerificationCodeService).to receive(:hash_verification_code_with_contact_info)
+ .with("test@example.com", "")
+ .and_return("hashed_code")
+
+ allow(EmailAccessToken).to receive_message_chain(:lookup, :exists?).and_return(false)
+
+ form = StateFile::ArchivedIntakes::VerificationCodeForm.new(
+ { verification_code: "" },
+ email_address: "test@example.com"
+ )
+
+ expect(form.save).to be false
+ end
+ end
+ end
+
+ describe "#initialize" do
+ it "assigns attributes correctly" do
+ form = StateFile::ArchivedIntakes::VerificationCodeForm.new(
+ { verification_code: "123456" },
+ email_address: "test@example.com"
+ )
+
+ expect(form.verification_code).to eq("123456")
+ expect(form.email_address).to eq("test@example.com")
+ end
+ end
+end
diff --git a/spec/models/state_file_archived_intake_access_log_spec.rb b/spec/models/state_file_archived_intake_access_log_spec.rb
index 0c8b0e1989..b65c56e7bd 100644
--- a/spec/models/state_file_archived_intake_access_log_spec.rb
+++ b/spec/models/state_file_archived_intake_access_log_spec.rb
@@ -2,21 +2,16 @@
#
# Table name: state_file_archived_intake_access_logs
#
-# id :bigint not null, primary key
-# details :jsonb
-# event_type :integer
-# ip_address :string
-# created_at :datetime not null
-# updated_at :datetime not null
-# state_file_archived_intakes_id :bigint
-#
-# Indexes
-#
-# idx_on_state_file_archived_intakes_id_e878049c06 (state_file_archived_intakes_id)
+# id :bigint not null, primary key
+# details :jsonb
+# event_type :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# state_file_archived_intake_request_id :bigint
#
# Foreign Keys
#
-# fk_rails_... (state_file_archived_intakes_id => state_file_archived_intakes.id)
+# fk_rails_... (state_file_archived_intake_request_id => state_file_archived_intake_requests.id)
#
require 'rails_helper'