diff --git a/app/services/documentation_generator.rb b/app/services/documentation_generator.rb index 8119e9eea..d77a4e093 100644 --- a/app/services/documentation_generator.rb +++ b/app/services/documentation_generator.rb @@ -13,7 +13,7 @@ class DocumentationGenerator form = FormHandler.instance.forms["current_#{log_type}"] all_validation_methods.each do |meth| - if LogValidation.where(validation_name: meth.to_s, bulk_upload_specific: false).exists? + if LogValidation.where(validation_name: meth.to_s, bulk_upload_specific: false, log_type:).exists? Rails.logger.info("Validation #{meth} already exists") next end @@ -41,7 +41,7 @@ class DocumentationGenerator def describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, log_type) all_validation_methods.each do |meth| - if LogValidation.where(validation_name: meth.to_s, bulk_upload_specific: true, from: form.start_date).exists? + if LogValidation.where(validation_name: meth.to_s, bulk_upload_specific: true, from: form.start_date, log_type:).exists? Rails.logger.info("Validation #{meth} already exists for #{form.start_date.year}") next end @@ -265,17 +265,17 @@ Look at these helper methods where needed to understand what is being checked in result["cases"].each do |case_info| case_info["errors"].each do |error| LogValidation.create!(log_type:, - validation_name: meth.to_s, - description: result["description"], - field: error["field"], - error_message: error["error_message"], - case: case_info["case_description"], - section: form.get_question(error["field"], nil)&.subsection&.id, - from: case_info["from"] || "", - to: case_info["to"] || "", - validation_type: case_info["validation_type"], - hard_soft: "hard", - other_validated_models: case_info["other_validated_models"]) + validation_name: meth.to_s, + description: result["description"], + field: error["field"], + error_message: error["error_message"], + case: case_info["case_description"], + section: form.get_question(error["field"], nil)&.subsection&.id, + from: case_info["from"] || "", + to: case_info["to"] || "", + validation_type: case_info["validation_type"], + hard_soft: "hard", + other_validated_models: case_info["other_validated_models"]) end end @@ -289,18 +289,18 @@ Look at these helper methods where needed to understand what is being checked in error_fields = [error["field"]] if error_fields.empty? error_fields.each do |error_field| LogValidation.create!(log_type:, - validation_name: meth.to_s, - description: result["description"], - field: error_field, - error_message: error["error_message"], - case: case_info["case_description"], - section: form.get_question(error_field, nil)&.subsection&.id, - from: form.start_date, - to: form.start_date + 1.year, - validation_type: case_info["validation_type"], - hard_soft: "hard", - other_validated_models: case_info["other_validated_models"], - bulk_upload_specific: true) + validation_name: meth.to_s, + description: result["description"], + field: error_field, + error_message: error["error_message"], + case: case_info["case_description"], + section: form.get_question(error_field, nil)&.subsection&.id, + from: form.start_date, + to: form.start_date + 1.year, + validation_type: case_info["validation_type"], + hard_soft: "hard", + other_validated_models: case_info["other_validated_models"], + bulk_upload_specific: true) end end end @@ -333,7 +333,7 @@ Look at these helper methods where needed to understand what is being checked in return end - if LogValidation.where(validation_name: validation_depends_on_hash.keys.first, field: page_the_validation_applied_to.questions.first.id, from: form.start_date).exists? + if LogValidation.where(validation_name: validation_depends_on_hash.keys.first, field: page_the_validation_applied_to.questions.first.id, from: form.start_date, log_type:).exists? Rails.logger.info("Validation #{validation_depends_on_hash.keys.first} already exists for #{page_the_validation_applied_to.questions.first.id} for start year #{form.start_date.year}") return end @@ -354,17 +354,17 @@ Look at these helper methods where needed to understand what is being checked in case_info = page.depends_on.first.values.first ? "Provided values fulfill the description" : "Provided values do not fulfill the description" LogValidation.create!(log_type:, - validation_name: validation_depends_on_hash.keys.first.to_s, - description: result["description"], - field: page_the_validation_applied_to.questions.first.id, - error_message:, - case: case_info, - section: form.get_question(page_the_validation_applied_to.questions.first.id, nil)&.subsection&.id, - from: form.start_date, - to: form.start_date + 1.year, - validation_type: result["validation_type"], - hard_soft: "soft", - other_validated_models: result["other_validated_models"]) + validation_name: validation_depends_on_hash.keys.first.to_s, + description: result["description"], + field: page_the_validation_applied_to.questions.first.id, + error_message:, + case: case_info, + section: form.get_question(page_the_validation_applied_to.questions.first.id, nil)&.subsection&.id, + from: form.start_date, + to: form.start_date + 1.year, + validation_type: result["validation_type"], + hard_soft: "soft", + other_validated_models: result["other_validated_models"]) Rails.logger.info("******** described #{validation_depends_on_hash.keys.first} for #{page_the_validation_applied_to.questions.first.id} ********") end diff --git a/config/initializers/rails_admin.rb b/config/initializers/rails_admin.rb index bdf319cbb..87e44ebe9 100644 --- a/config/initializers/rails_admin.rb +++ b/config/initializers/rails_admin.rb @@ -26,7 +26,7 @@ RailsAdmin.config do |config| ## == Gravatar integration == ## To disable Gravatar integration in Navigation Bar set to false # config.show_gravatar = true - config.included_models = %w[Validation] + config.included_models = %w[LogValidation] config.actions do dashboard # mandatory diff --git a/lib/tasks/generate_lettings_documentation.rake b/lib/tasks/generate_lettings_documentation.rake index 55cdf402d..70d1f84f5 100644 --- a/lib/tasks/generate_lettings_documentation.rake +++ b/lib/tasks/generate_lettings_documentation.rake @@ -77,14 +77,14 @@ namespace :generate_lettings_documentation do end LogValidation.create!(log_type: "lettings", - validation_name:, - description: validation_description, - field:, - error_message:, - case: validation_description, - section: form.get_question(field, nil)&.subsection&.id, - validation_type: validation_name, - hard_soft: "hard") + validation_name:, + description: validation_description, + field:, + error_message:, + case: validation_description, + section: form.get_question(field, nil)&.subsection&.id, + validation_type: validation_name, + hard_soft: "hard") end end end diff --git a/lib/tasks/generate_sales_documentation.rake b/lib/tasks/generate_sales_documentation.rake index d34e907c7..e19374ec6 100644 --- a/lib/tasks/generate_sales_documentation.rake +++ b/lib/tasks/generate_sales_documentation.rake @@ -69,13 +69,13 @@ namespace :generate_sales_documentation do validation_description = "Field value is lower than the minimum value or higher than the maximum value" end - if Validation.where(validation_name:, field:).exists? + if LogValidation.where(validation_name:, field:).exists? Rails.logger.info("Validation #{validation_name} already exists for #{field}") next end - Validation.create!(log_type: "sales", + LogValidation.create!(log_type: "sales", validation_name:, description: validation_description, field:, diff --git a/spec/lib/tasks/generate_lettings_documentation_spec.rb b/spec/lib/tasks/generate_lettings_documentation_spec.rb index 9434dcfcd..eca123b4e 100644 --- a/spec/lib/tasks/generate_lettings_documentation_spec.rb +++ b/spec/lib/tasks/generate_lettings_documentation_spec.rb @@ -2,189 +2,6 @@ require "rails_helper" require "rake" RSpec.describe "generate_lettings_documentation" do - describe ":describe_lettings_validations", type: :task do - subject(:task) { Rake::Task["generate_lettings_documentation:describe_lettings_validations"] } - - let(:client) { instance_double(OpenAI::Client) } - let(:response) do - { "choices" => [{ "message" => { "tool_calls" => [{ "function" => { "arguments" => - "{\n \"description\": \"Validates the format.\",\n \"cases\": [\n {\n \"case_description\": \"Previous postcode is known and current postcode is blank\",\n \"errors\": [\n {\n \"error_message\": \"Enter a valid postcode\",\n \"field\": \"ppostcode_full\"\n }\n ],\n \"validation_type\": \"format\",\n \"other_validated_models\": \"User\" }]\n}" } }] } }] } - end - - before do - Rake.application.rake_require("tasks/generate_lettings_documentation") - Rake::Task.define_task(:environment) - task.reenable - allow(OpenAI::Client).to receive(:new).and_return(client) - allow(client).to receive(:chat).and_return(response) - end - - context "when the rake task is run" do - it "creates new validation documentation records" do - expect(Rails.logger).to receive(:info).with(/described/).at_least(:once) - expect { task.invoke }.to change(Validation, :count) - expect(LogValidation.where(validation_name: "validate_numeric_min_max").count).to eq(1) - expect(LogValidation.where(validation_name: "validate_layear").count).to eq(1) - any_validation = LogValidation.first - expect(any_validation.description).to eq("Validates the format.") - expect(any_validation.field).to eq("ppostcode_full") - expect(any_validation.error_message).to eq("Enter a valid postcode") - expect(any_validation.case).to eq("Previous postcode is known and current postcode is blank") - expect(any_validation.from).to be_nil - expect(any_validation.to).to be_nil - expect(any_validation.validation_type).to eq("format") - expect(any_validation.hard_soft).to eq("hard") - expect(any_validation.other_validated_models).to eq("User") - expect(any_validation.log_type).to eq("lettings") - end - - it "calls openAI client" do - expect(client).to receive(:chat) - task.invoke - end - - it "skips if the validation already exists in the database" do - task.invoke - expect { task.invoke }.not_to change(Validation, :count) - end - - context "when openAI response is not a JSON" do - let(:response) { "not a JSON" } - - it "raises an error" do - expect(Rails.logger).to receive(:error).with(/Failed to save/).at_least(:once) - expect(Rails.logger).to receive(:error).with(/Error/).at_least(:once) - task.invoke - end - end - - context "when openAI response does not have expected fields" do - let(:response) { { "choices" => [{ "message" => { "tool_calls" => [{ "function" => { "arguments" => "{}" } }] } }] } } - - it "raises an error" do - expect(Rails.logger).to receive(:error).with(/Failed to save/).at_least(:once) - expect(Rails.logger).to receive(:error).with(/Error/).at_least(:once) - task.invoke - end - end - end - end - - describe ":describe_soft_lettings_validations", type: :task do - subject(:task) { Rake::Task["generate_lettings_documentation:describe_soft_lettings_validations"] } - - let(:client) { instance_double(OpenAI::Client) } - let(:response) do - { "choices" => [{ "message" => { "tool_calls" => [{ "function" => { "arguments" => - "{\n \"description\": \"Validates the format.\",\n \"validation_type\": \"format\",\n \"other_validated_models\": \"User\"}" } }] } }] } - end - - before do - Rake.application.rake_require("tasks/generate_lettings_documentation") - Rake::Task.define_task(:environment) - task.reenable - allow(OpenAI::Client).to receive(:new).and_return(client) - allow(client).to receive(:chat).and_return(response) - end - - context "when the rake task is run" do - it "creates new validation documentation records" do - expect { task.invoke }.to change(Validation, :count) - expect(LogValidation.where(validation_name: "rent_in_soft_min_range?").count).to be_positive - expect(LogValidation.where(validation_name: "major_repairs_date_in_soft_range?").count).to be_positive - any_validation = LogValidation.first - expect(any_validation.description).to eq("Validates the format.") - expect(any_validation.field).not_to be_empty - expect(any_validation.error_message).not_to be_empty - expect(any_validation.case).to eq("Provided values fulfill the description") - expect(any_validation.from).not_to be_nil - expect(any_validation.to).not_to be_nil - expect(any_validation.validation_type).to eq("format") - expect(any_validation.hard_soft).to eq("soft") - expect(any_validation.other_validated_models).to eq("User") - expect(any_validation.log_type).to eq("lettings") - end - - it "calls openAI client" do - expect(client).to receive(:chat) - task.invoke - end - - it "skips if the validation already exists in the database" do - task.invoke - expect { task.invoke }.not_to change(Validation, :count) - end - end - end - - describe ":describe_bu_lettings_validations", type: :task do - subject(:task) { Rake::Task["generate_lettings_documentation:describe_bu_lettings_validations"] } - - let(:client) { instance_double(OpenAI::Client) } - let(:response) do - { "choices" => [{ "message" => { "tool_calls" => [{ "function" => { "arguments" => - "{\n \"description\": \"Validates the format.\",\n \"cases\": [\n {\n \"case_description\": \"Previous postcode is known and current postcode is blank\",\n \"errors\": [\n {\n \"error_message\": \"Enter a valid postcode\",\n \"field\": \"ppostcode_full\"\n }\n ],\n \"validation_type\": \"format\",\n \"other_validated_models\": \"User\" }]\n}" } }] } }] } - end - - before do - Rake.application.rake_require("tasks/generate_lettings_documentation") - Rake::Task.define_task(:environment) - task.reenable - allow(OpenAI::Client).to receive(:new).and_return(client) - allow(client).to receive(:chat).and_return(response) - end - - context "when the rake task is run" do - it "creates new validation documentation records" do - expect(Rails.logger).to receive(:info).with(/described/).at_least(:once) - expect { task.invoke }.to change(Validation, :count) - expect(LogValidation.where(validation_name: "validate_needs_type_present").count).to eq(2) # for both years - expect(LogValidation.where(validation_name: "validate_data_types").count).to eq(2) - any_validation = LogValidation.first - expect(any_validation.description).to eq("Validates the format.") - expect(any_validation.field).to eq("ppostcode_full") - expect(any_validation.error_message).to eq("Enter a valid postcode") - expect(any_validation.case).to eq("Previous postcode is known and current postcode is blank") - expect(any_validation.from).not_to be_nil - expect(any_validation.to).not_to be_nil - expect(any_validation.validation_type).to eq("format") - expect(any_validation.hard_soft).to eq("hard") - expect(any_validation.other_validated_models).to eq("User") - expect(any_validation.log_type).to eq("lettings") - end - - it "calls openAI client" do - expect(client).to receive(:chat) - task.invoke - end - - it "skips if the validation already exists in the database" do - task.invoke - expect { task.invoke }.not_to change(Validation, :count) - end - - context "when openAI response is not a JSON" do - let(:response) { "not a JSON" } - - it "raises an error" do - expect(Rails.logger).to receive(:error).with(/Failed to save/).at_least(:once) - expect(Rails.logger).to receive(:error).with(/Error/).at_least(:once) - task.invoke - end - end - - context "when openAI response does not have expected fields" do - let(:response) { { "choices" => [{ "message" => { "tool_calls" => [{ "function" => { "arguments" => "{}" } }] } }] } } - - it "raises an error" do - expect(Rails.logger).to receive(:error).with(/Failed to save/).at_least(:once) - expect(Rails.logger).to receive(:error).with(/Error/).at_least(:once) - task.invoke - end - end - end - end - describe ":add_numeric_lettings_validations", type: :task do subject(:task) { Rake::Task["generate_lettings_documentation:add_numeric_lettings_validations"] } @@ -196,7 +13,7 @@ RSpec.describe "generate_lettings_documentation" do context "when the rake task is run" do it "creates new validation documentation records" do - expect { task.invoke }.to change(Validation, :count) + expect { task.invoke }.to change(LogValidation, :count) expect(LogValidation.where(validation_name: "minimum").count).to be_positive expect(LogValidation.where(validation_name: "range").count).to be_positive any_min_validation = LogValidation.where(validation_name: "minimum").first @@ -214,7 +31,7 @@ RSpec.describe "generate_lettings_documentation" do it "skips if the validation already exists in the database" do task.invoke - expect { task.invoke }.not_to change(Validation, :count) + expect { task.invoke }.not_to change(LogValidation, :count) end end end diff --git a/spec/lib/tasks/generate_sales_documentation_spec.rb b/spec/lib/tasks/generate_sales_documentation_spec.rb index cd9bff037..4626f1a42 100644 --- a/spec/lib/tasks/generate_sales_documentation_spec.rb +++ b/spec/lib/tasks/generate_sales_documentation_spec.rb @@ -2,189 +2,6 @@ require "rails_helper" require "rake" RSpec.describe "generate_sales_documentation" do - describe ":describe_sales_validations", type: :task do - subject(:task) { Rake::Task["generate_sales_documentation:describe_sales_validations"] } - - let(:client) { instance_double(OpenAI::Client) } - let(:response) do - { "choices" => [{ "message" => { "tool_calls" => [{ "function" => { "arguments" => - "{\n \"description\": \"Validates the format.\",\n \"cases\": [\n {\n \"case_description\": \"Previous postcode is known and current postcode is blank\",\n \"errors\": [\n {\n \"error_message\": \"Enter a valid postcode\",\n \"field\": \"ppostcode_full\"\n }\n ],\n \"validation_type\": \"format\",\n \"other_validated_models\": \"User\" }]\n}" } }] } }] } - end - - before do - Rake.application.rake_require("tasks/generate_sales_documentation") - Rake::Task.define_task(:environment) - task.reenable - allow(OpenAI::Client).to receive(:new).and_return(client) - allow(client).to receive(:chat).and_return(response) - end - - context "when the rake task is run" do - it "creates new validation documentation records" do - expect(Rails.logger).to receive(:info).with(/described/).at_least(:once) - expect { task.invoke }.to change(Validation, :count) - expect(LogValidation.where(validation_name: "validate_saledate_collection_year").count).to eq(1) - expect(LogValidation.where(validation_name: "validate_partner_count").count).to eq(1) - any_validation = LogValidation.first - expect(any_validation.description).to eq("Validates the format.") - expect(any_validation.field).to eq("ppostcode_full") - expect(any_validation.error_message).to eq("Enter a valid postcode") - expect(any_validation.case).to eq("Previous postcode is known and current postcode is blank") - expect(any_validation.from).to be_nil - expect(any_validation.to).to be_nil - expect(any_validation.validation_type).to eq("format") - expect(any_validation.hard_soft).to eq("hard") - expect(any_validation.other_validated_models).to eq("User") - expect(any_validation.log_type).to eq("sales") - end - - it "calls openAI client" do - expect(client).to receive(:chat) - task.invoke - end - - it "skips if the validation already exists in the database" do - task.invoke - expect { task.invoke }.not_to change(Validation, :count) - end - - context "when openAI response is not a JSON" do - let(:response) { "not a JSON" } - - it "raises an error" do - expect(Rails.logger).to receive(:error).with(/Failed to save/).at_least(:once) - expect(Rails.logger).to receive(:error).with(/Error/).at_least(:once) - task.invoke - end - end - - context "when openAI response does not have expected fields" do - let(:response) { { "choices" => [{ "message" => { "tool_calls" => [{ "function" => { "arguments" => "{}" } }] } }] } } - - it "raises an error" do - expect(Rails.logger).to receive(:error).with(/Failed to save/).at_least(:once) - expect(Rails.logger).to receive(:error).with(/Error/).at_least(:once) - task.invoke - end - end - end - end - - describe ":describe_soft_sales_validations", type: :task do - subject(:task) { Rake::Task["generate_sales_documentation:describe_soft_sales_validations"] } - - let(:client) { instance_double(OpenAI::Client) } - let(:response) do - { "choices" => [{ "message" => { "tool_calls" => [{ "function" => { "arguments" => - "{\n \"description\": \"Validates the format.\",\n \"validation_type\": \"format\",\n \"other_validated_models\": \"User\"}" } }] } }] } - end - - before do - Rake.application.rake_require("tasks/generate_sales_documentation") - Rake::Task.define_task(:environment) - task.reenable - allow(OpenAI::Client).to receive(:new).and_return(client) - allow(client).to receive(:chat).and_return(response) - end - - context "when the rake task is run" do - it "creates new validation documentation records" do - expect { task.invoke }.to change(Validation, :count) - expect(LogValidation.where(validation_name: "income2_under_soft_min?").count).to be_positive - expect(LogValidation.where(validation_name: "deposit_over_soft_max?").count).to be_positive - any_validation = LogValidation.first - expect(any_validation.description).to eq("Validates the format.") - expect(any_validation.field).not_to be_empty - expect(any_validation.error_message).not_to be_empty - expect(any_validation.case).to eq("Provided values fulfill the description") - expect(any_validation.from).not_to be_nil - expect(any_validation.to).not_to be_nil - expect(any_validation.validation_type).to eq("format") - expect(any_validation.hard_soft).to eq("soft") - expect(any_validation.other_validated_models).to eq("User") - expect(any_validation.log_type).to eq("sales") - end - - it "calls openAI client" do - expect(client).to receive(:chat) - task.invoke - end - - it "skips if the validation already exists in the database" do - task.invoke - expect { task.invoke }.not_to change(Validation, :count) - end - end - end - - describe ":describe_bu_sales_validations", type: :task do - subject(:task) { Rake::Task["generate_sales_documentation:describe_bu_sales_validations"] } - - let(:client) { instance_double(OpenAI::Client) } - let(:response) do - { "choices" => [{ "message" => { "tool_calls" => [{ "function" => { "arguments" => - "{\n \"description\": \"Validates the format.\",\n \"cases\": [\n {\n \"case_description\": \"Previous postcode is known and current postcode is blank\",\n \"errors\": [\n {\n \"error_message\": \"Enter a valid postcode\",\n \"field\": \"ppostcode_full\"\n }\n ],\n \"validation_type\": \"format\",\n \"other_validated_models\": \"User\" }]\n}" } }] } }] } - end - - before do - Rake.application.rake_require("tasks/generate_sales_documentation") - Rake::Task.define_task(:environment) - task.reenable - allow(OpenAI::Client).to receive(:new).and_return(client) - allow(client).to receive(:chat).and_return(response) - end - - context "when the rake task is run" do - it "creates new validation documentation records" do - expect(Rails.logger).to receive(:info).with(/described/).at_least(:once) - expect { task.invoke }.to change(Validation, :count) - expect(LogValidation.where(validation_name: "validate_owning_org_data_given").count).to eq(2) # for both years - expect(LogValidation.where(validation_name: "validate_assigned_to_exists").count).to eq(2) - any_validation = LogValidation.first - expect(any_validation.description).to eq("Validates the format.") - expect(any_validation.field).to eq("ppostcode_full") - expect(any_validation.error_message).to eq("Enter a valid postcode") - expect(any_validation.case).to eq("Previous postcode is known and current postcode is blank") - expect(any_validation.from).not_to be_nil - expect(any_validation.to).not_to be_nil - expect(any_validation.validation_type).to eq("format") - expect(any_validation.hard_soft).to eq("hard") - expect(any_validation.other_validated_models).to eq("User") - expect(any_validation.log_type).to eq("sales") - end - - it "calls openAI client" do - expect(client).to receive(:chat) - task.invoke - end - - it "skips if the validation already exists in the database" do - task.invoke - expect { task.invoke }.not_to change(Validation, :count) - end - - context "when openAI response is not a JSON" do - let(:response) { "not a JSON" } - - it "raises an error" do - expect(Rails.logger).to receive(:error).with(/Failed to save/).at_least(:once) - expect(Rails.logger).to receive(:error).with(/Error/).at_least(:once) - task.invoke - end - end - - context "when openAI response does not have expected fields" do - let(:response) { { "choices" => [{ "message" => { "tool_calls" => [{ "function" => { "arguments" => "{}" } }] } }] } } - - it "raises an error" do - expect(Rails.logger).to receive(:error).with(/Failed to save/).at_least(:once) - expect(Rails.logger).to receive(:error).with(/Error/).at_least(:once) - task.invoke - end - end - end - end - describe ":add_numeric_sales_validations", type: :task do subject(:task) { Rake::Task["generate_sales_documentation:add_numeric_sales_validations"] } @@ -196,7 +13,7 @@ RSpec.describe "generate_sales_documentation" do context "when the rake task is run" do it "creates new validation documentation records" do - expect { task.invoke }.to change(Validation, :count) + expect { task.invoke }.to change(LogValidation, :count) expect(LogValidation.where(validation_name: "minimum").count).to be_positive expect(LogValidation.where(validation_name: "range").count).to be_positive any_min_validation = LogValidation.where(validation_name: "minimum").first @@ -214,7 +31,7 @@ RSpec.describe "generate_sales_documentation" do it "skips if the validation already exists in the database" do task.invoke - expect { task.invoke }.not_to change(Validation, :count) + expect { task.invoke }.not_to change(LogValidation, :count) end end end diff --git a/spec/services/documentation_generator_spec.rb b/spec/services/documentation_generator_spec.rb new file mode 100644 index 000000000..47bc813f7 --- /dev/null +++ b/spec/services/documentation_generator_spec.rb @@ -0,0 +1,230 @@ +require "rails_helper" + +describe DocumentationGenerator do + let(:client) { instance_double(OpenAI::Client) } + let(:response) do + { "choices" => [{ "message" => { "tool_calls" => [{ "function" => { "arguments" => + "{\n \"description\": \"Validates the format.\",\n \"cases\": [\n {\n \"case_description\": \"Previous postcode is known and current postcode is blank\",\n \"errors\": [\n {\n \"error_message\": \"Enter a valid postcode\",\n \"field\": \"ppostcode_full\"\n }\n ],\n \"validation_type\": \"format\",\n \"other_validated_models\": \"User\" }]\n}" } }] } }] } + end + let(:all_validation_methods) { %w[validate_numeric_min_max] } + let(:all_helper_methods) { [] } + let(:log_type) { "lettings" } + + before do + allow(client).to receive(:chat).and_return(response) + end + + describe ":describe_hard_validations" do + context "when the service is run with lettings type" do + let(:log_type) { "lettings" } + + it "creates new validation documentation records" do + expect(Rails.logger).to receive(:info).with(/described/).at_least(:once) + expect { described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type) }.to change(LogValidation, :count) + expect(LogValidation.where(validation_name: "validate_numeric_min_max").count).to eq(1) + any_validation = LogValidation.first + expect(any_validation.description).to eq("Validates the format.") + expect(any_validation.field).to eq("ppostcode_full") + expect(any_validation.error_message).to eq("Enter a valid postcode") + expect(any_validation.case).to eq("Previous postcode is known and current postcode is blank") + expect(any_validation.from).to be_nil + expect(any_validation.to).to be_nil + expect(any_validation.validation_type).to eq("format") + expect(any_validation.hard_soft).to eq("hard") + expect(any_validation.other_validated_models).to eq("User") + expect(any_validation.log_type).to eq("lettings") + end + + it "calls the client" do + expect(client).to receive(:chat) + described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type) + end + + it "skips if the validation already exists in the database" do + described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type) + expect { described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type) }.not_to change(LogValidation, :count) + end + + context "when the response is not a JSON" do + let(:response) { "not a JSON" } + + it "raises an error" do + expect(Rails.logger).to receive(:error).with(/Failed to save/).at_least(:once) + expect(Rails.logger).to receive(:error).with(/Error/).at_least(:once) + described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type) + end + end + + context "when the response does not have expected fields" do + let(:response) { { "choices" => [{ "message" => { "tool_calls" => [{ "function" => { "arguments" => "{}" } }] } }] } } + + it "raises an error" do + expect(Rails.logger).to receive(:error).with(/Failed to save/).at_least(:once) + expect(Rails.logger).to receive(:error).with(/Error/).at_least(:once) + described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type) + end + end + end + + context "when the service is run with sales type" do + let(:log_type) { "sales" } + + it "creates new validation documentation records" do + expect(Rails.logger).to receive(:info).with(/described/).at_least(:once) + expect { described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type) }.to change(LogValidation, :count) + expect(LogValidation.where(validation_name: "validate_numeric_min_max").count).to eq(1) + any_validation = LogValidation.first + expect(any_validation.description).to eq("Validates the format.") + expect(any_validation.field).to eq("ppostcode_full") + expect(any_validation.error_message).to eq("Enter a valid postcode") + expect(any_validation.case).to eq("Previous postcode is known and current postcode is blank") + expect(any_validation.from).to be_nil + expect(any_validation.to).to be_nil + expect(any_validation.validation_type).to eq("format") + expect(any_validation.hard_soft).to eq("hard") + expect(any_validation.other_validated_models).to eq("User") + expect(any_validation.log_type).to eq("sales") + end + end + end + + describe ":describe_soft_validations" do + let(:all_validation_methods) { ["rent_in_soft_min_range?"] } + let(:response) do + { "choices" => [{ "message" => { "tool_calls" => [{ "function" => { "arguments" => + "{\n \"description\": \"Validates the format.\",\n \"validation_type\": \"format\",\n \"other_validated_models\": \"User\"}" } }] } }] } + end + + context "when the service is run for lettings" do + let(:log_type) { "lettings" } + + it "creates new validation documentation records" do + expect { described_class.new.describe_soft_validations(client, all_validation_methods, all_helper_methods, log_type) }.to change(LogValidation, :count) + expect(LogValidation.where(validation_name: "rent_in_soft_min_range?").count).to be_positive + any_validation = LogValidation.first + expect(any_validation.description).to eq("Validates the format.") + expect(any_validation.field).not_to be_empty + expect(any_validation.error_message).not_to be_empty + expect(any_validation.case).to eq("Provided values fulfill the description") + expect(any_validation.from).not_to be_nil + expect(any_validation.to).not_to be_nil + expect(any_validation.validation_type).to eq("format") + expect(any_validation.hard_soft).to eq("soft") + expect(any_validation.other_validated_models).to eq("User") + expect(any_validation.log_type).to eq("lettings") + end + + it "calls the client" do + expect(client).to receive(:chat) + described_class.new.describe_soft_validations(client, all_validation_methods, all_helper_methods, log_type) + end + + it "skips if the validation already exists in the database" do + described_class.new.describe_soft_validations(client, all_validation_methods, all_helper_methods, log_type) + expect { described_class.new.describe_soft_validations(client, all_validation_methods, all_helper_methods, log_type) }.not_to change(LogValidation, :count) + end + end + + context "when the service is run for sales" do + let(:log_type) { "sales" } + let(:all_validation_methods) { ["income2_under_soft_min?"] } + + it "creates new validation documentation records" do + expect { described_class.new.describe_soft_validations(client, all_validation_methods, all_helper_methods, log_type) }.to change(LogValidation, :count) + expect(LogValidation.where(validation_name: "income2_under_soft_min?").count).to be_positive + any_validation = LogValidation.first + expect(any_validation.description).to eq("Validates the format.") + expect(any_validation.field).not_to be_empty + expect(any_validation.error_message).not_to be_empty + expect(any_validation.case).to eq("Provided values fulfill the description") + expect(any_validation.from).not_to be_nil + expect(any_validation.to).not_to be_nil + expect(any_validation.validation_type).to eq("format") + expect(any_validation.hard_soft).to eq("soft") + expect(any_validation.other_validated_models).to eq("User") + expect(any_validation.log_type).to eq("sales") + end + end + end + + describe ":describe_bu_validations", type: :task do + let(:all_validation_methods) { %w[validate_owning_org_data_given] } + let(:field_mapping_for_errors) { row_parser_class.new.send("field_mapping_for_errors") } + + context "when the service is run for lettings" do + let(:log_type) { "lettings" } + let(:form) { FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(2023, "lettings")] } + let(:row_parser_class) { BulkUpload::Lettings::Year2023::RowParser } + + it "creates new validation documentation records" do + expect(Rails.logger).to receive(:info).with(/described/).at_least(:once) + expect { described_class.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, log_type) }.to change(LogValidation, :count) + expect(LogValidation.where(validation_name: "validate_owning_org_data_given").count).to eq(1) + any_validation = LogValidation.first + expect(any_validation.description).to eq("Validates the format.") + expect(any_validation.field).to eq("ppostcode_full") + expect(any_validation.error_message).to eq("Enter a valid postcode") + expect(any_validation.case).to eq("Previous postcode is known and current postcode is blank") + expect(any_validation.from).not_to be_nil + expect(any_validation.to).not_to be_nil + expect(any_validation.validation_type).to eq("format") + expect(any_validation.hard_soft).to eq("hard") + expect(any_validation.other_validated_models).to eq("User") + expect(any_validation.log_type).to eq("lettings") + end + + it "calls the client" do + expect(client).to receive(:chat) + described_class.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, log_type) + end + + it "skips if the validation already exists in the database" do + described_class.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, log_type) + expect { described_class.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, log_type) }.not_to change(LogValidation, :count) + end + + context "when the response is not a JSON" do + let(:response) { "not a JSON" } + + it "raises an error" do + expect(Rails.logger).to receive(:error).with(/Failed to save/).at_least(:once) + expect(Rails.logger).to receive(:error).with(/Error/).at_least(:once) + described_class.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, log_type) + end + end + + context "when the response does not have expected fields" do + let(:response) { { "choices" => [{ "message" => { "tool_calls" => [{ "function" => { "arguments" => "{}" } }] } }] } } + + it "raises an error" do + expect(Rails.logger).to receive(:error).with(/Failed to save/).at_least(:once) + expect(Rails.logger).to receive(:error).with(/Error/).at_least(:once) + described_class.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, log_type) + end + end + end + + context "when the service is run for sales" do + let(:log_type) { "sales" } + let(:form) { FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(2023, "sales")] } + let(:row_parser_class) { BulkUpload::Sales::Year2023::RowParser } + + it "creates new validation documentation records" do + expect(Rails.logger).to receive(:info).with(/described/).at_least(:once) + expect { described_class.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, log_type) }.to change(LogValidation, :count) + expect(LogValidation.where(validation_name: "validate_owning_org_data_given").count).to eq(1) + any_validation = LogValidation.first + expect(any_validation.description).to eq("Validates the format.") + expect(any_validation.field).to eq("ppostcode_full") + expect(any_validation.error_message).to eq("Enter a valid postcode") + expect(any_validation.case).to eq("Previous postcode is known and current postcode is blank") + expect(any_validation.from).not_to be_nil + expect(any_validation.to).not_to be_nil + expect(any_validation.validation_type).to eq("format") + expect(any_validation.hard_soft).to eq("hard") + expect(any_validation.other_validated_models).to eq("User") + expect(any_validation.log_type).to eq("sales") + end + end + end +end