Browse Source

CLDC-3945 Update validation generation (#3011)

* Allow passing a year to generating lettings validations docs task

* Allow passing a year to generating sales validations docs task

* Refactor

* Include missing validations

* Describe all soft validations

* Update model

* Refactor
pull/3043/head
kosiakkatrina 3 weeks ago committed by GitHub
parent
commit
f5973585ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 54
      app/services/documentation_generator.rb
  2. 81
      lib/tasks/generate_lettings_documentation.rake
  3. 81
      lib/tasks/generate_sales_documentation.rake
  4. 202
      spec/lib/tasks/generate_lettings_documentation_spec.rb
  5. 202
      spec/lib/tasks/generate_sales_documentation_spec.rb
  6. 28
      spec/services/documentation_generator_spec.rb

54
app/services/documentation_generator.rb

@ -8,10 +8,14 @@ class DocumentationGenerator
include Validations::LocalAuthorityValidations include Validations::LocalAuthorityValidations
include Validations::SoftValidations include Validations::SoftValidations
include Validations::Sales::SoftValidations include Validations::Sales::SoftValidations
include Validations::SetupValidations
def describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type) include Validations::HouseholdValidations
form = FormHandler.instance.forms["current_#{log_type}"] include Validations::PropertyValidations
include Validations::FinancialValidations
include Validations::TenancyValidations
include Validations::DateValidations
def describe_hard_validations(client, form, all_validation_methods, all_helper_methods, log_type)
all_validation_methods.each do |meth| all_validation_methods.each do |meth|
if LogValidation.where(validation_name: meth.to_s, bulk_upload_specific: false, log_type:, collection_year: "#{form.start_date.year}/#{form.start_date.year + 1}").exists? if LogValidation.where(validation_name: meth.to_s, bulk_upload_specific: false, log_type:, collection_year: "#{form.start_date.year}/#{form.start_date.year + 1}").exists?
Rails.logger.info("Validation #{meth} already exists for #{form.start_date.year}") Rails.logger.info("Validation #{meth} already exists for #{form.start_date.year}")
@ -69,9 +73,9 @@ class DocumentationGenerator
end end
end end
def describe_soft_validations(client, all_validation_methods, all_helper_methods, log_type) def describe_soft_validations(client, form, all_validation_methods, all_helper_methods, log_type)
validation_descriptions = {} validation_descriptions = {}
all_validation_methods[0..5].each do |meth| all_validation_methods.each do |meth|
validation_source = method(meth).source validation_source = method(meth).source
helper_methods_source = all_helper_methods.map { |helper_method| helper_methods_source = all_helper_methods.map { |helper_method|
if validation_source.include?(helper_method.to_s) if validation_source.include?(helper_method.to_s)
@ -87,20 +91,34 @@ class DocumentationGenerator
validation_descriptions[meth.to_s] = result validation_descriptions[meth.to_s] = result
end end
current_form = FormHandler.instance.forms["current_#{log_type}"] interruption_screen_pages = form.pages.select { |page| page.questions.first.type == "interruption_screen" }
previous_form = FormHandler.instance.forms["previous_#{log_type}"] interruption_screen_pages_grouped_by_question = interruption_screen_pages.group_by { |page| page.questions.first.id }
interruption_screen_pages_grouped_by_question.each do |_question_id, pages|
[current_form, previous_form].each do |form| pages.map do |page|
interruption_screen_pages = form.pages.select { |page| page.questions.first.type == "interruption_screen" } save_soft_validation(form, page, validation_descriptions, log_type)
interruption_screen_pages_grouped_by_question = interruption_screen_pages.group_by { |page| page.questions.first.id }
interruption_screen_pages_grouped_by_question.each do |_question_id, pages|
pages.map do |page|
save_soft_validation(form, page, validation_descriptions, log_type)
end
end end
end end
end end
def validation_and_helper_methods(validation_classes)
all_validation_methods = validation_classes.map(&:instance_methods).flatten.select { |method| method.starts_with?("validate_") }.uniq
all_methods = validation_classes.map { |x| x.instance_methods + x.private_instance_methods }.flatten.uniq
all_helper_methods = all_methods - all_validation_methods
[all_validation_methods, all_helper_methods]
end
def get_soft_sales_methods
all_helper_methods = Validations::SoftValidations.private_instance_methods + Validations::Sales::SoftValidations.private_instance_methods
all_validation_methods = Validations::SoftValidations.instance_methods + Validations::Sales::SoftValidations.instance_methods
[all_helper_methods, all_validation_methods]
end
def get_soft_lettings_methods
all_helper_methods = Validations::SoftValidations.private_instance_methods
all_validation_methods = Validations::SoftValidations.instance_methods
[all_helper_methods, all_validation_methods]
end
private private
def describe_hard_validation(client, meth, validation_source, helper_methods_source, form, file_path) def describe_hard_validation(client, meth, validation_source, helper_methods_source, form, file_path)
@ -109,7 +127,7 @@ private
begin begin
client.chat( client.chat(
parameters: { parameters: {
model: "gpt-3.5-turbo", model: "gpt-4o-mini",
messages: [ messages: [
{ {
role: "system", role: "system",
@ -201,7 +219,7 @@ private
def soft_validation_description(client, meth, validation_source, helper_methods_source) def soft_validation_description(client, meth, validation_source, helper_methods_source)
client.chat( client.chat(
parameters: { parameters: {
model: "gpt-3.5-turbo", model: "gpt-4o-mini",
messages: [ messages: [
{ {
role: "system", role: "system",

81
lib/tasks/generate_lettings_documentation.rake

@ -1,57 +1,68 @@
namespace :generate_lettings_documentation do namespace :generate_lettings_documentation do
desc "Generate documentation for hard lettings validations" desc "Generate documentation for hard lettings validations"
task describe_lettings_validations: :environment do task :describe_lettings_validations, %i[year] => :environment do |_task, args|
form_year = args[:year]&.to_i
raise "Usage: rake generate_lettings_documentation:describe_lettings_validations['year']" if form_year.blank?
form = FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(form_year, "lettings")]
raise "No form found for given year" if form.blank?
client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"]) client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"])
include Validations::SetupValidations
include Validations::HouseholdValidations documentation_generator = DocumentationGenerator.new
include Validations::PropertyValidations validation_classes = [Validations::SetupValidations,
include Validations::FinancialValidations Validations::HouseholdValidations,
include Validations::TenancyValidations Validations::PropertyValidations,
include Validations::DateValidations Validations::FinancialValidations,
include Validations::LocalAuthorityValidations Validations::TenancyValidations,
all_validation_methods = public_methods.select { |method| method.starts_with?("validate_") } Validations::DateValidations,
all_methods = [Validations::SetupValidations, Validations::LocalAuthorityValidations]
Validations::HouseholdValidations, all_validation_methods, all_helper_methods = documentation_generator.validation_and_helper_methods(validation_classes)
Validations::PropertyValidations,
Validations::FinancialValidations, documentation_generator.describe_hard_validations(client, form, all_validation_methods, all_helper_methods, "lettings")
Validations::TenancyValidations,
Validations::DateValidations,
Validations::LocalAuthorityValidations].map { |x| x.instance_methods + x.private_instance_methods }.flatten
all_helper_methods = all_methods - all_validation_methods
DocumentationGenerator.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, "lettings")
end end
desc "Generate documentation for soft lettings validations" desc "Generate documentation for soft lettings validations"
task describe_soft_lettings_validations: :environment do task :describe_soft_lettings_validations, %i[year] => :environment do |_task, args|
include Validations::SoftValidations form_year = args[:year]&.to_i
raise "Usage: rake generate_lettings_documentation:describe_soft_lettings_validations['year']" if form_year.blank?
client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"]) form = FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(form_year, "lettings")]
raise "No form found for given year" if form.blank?
all_helper_methods = Validations::SoftValidations.private_instance_methods client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"])
all_validation_methods = Validations::SoftValidations.instance_methods documentation_generator = DocumentationGenerator.new
all_helper_methods, all_validation_methods = documentation_generator.get_soft_lettings_methods
DocumentationGenerator.new.describe_soft_validations(client, all_validation_methods, all_helper_methods, "lettings") documentation_generator.describe_soft_validations(client, form, all_validation_methods, all_helper_methods, "lettings")
end end
desc "Generate documentation for hard bu lettings validations" desc "Generate documentation for hard bu lettings validations"
task describe_bu_lettings_validations: :environment do task :describe_bu_lettings_validations, %i[year] => :environment do |_task, args|
form_year = args[:year]&.to_i
raise "Usage: rake generate_lettings_documentation:describe_bu_lettings_validations['year']" if form_year.blank?
form = FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(form_year, "lettings")]
raise "No form found for given year" if form.blank?
row_parser_class = "BulkUpload::Lettings::Year#{form_year}::RowParser".constantize
client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"]) client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"])
[[FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(2023, "lettings")], BulkUpload::Lettings::Year2023::RowParser], all_validation_methods = row_parser_class.private_instance_methods.select { |method| method.starts_with?("validate_") }
[FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(2024, "lettings")], BulkUpload::Lettings::Year2024::RowParser]].each do |form, row_parser_class|
all_validation_methods = row_parser_class.private_instance_methods.select { |method| method.starts_with?("validate_") }
all_helper_methods = row_parser_class.private_instance_methods(false) + row_parser_class.instance_methods(false) - all_validation_methods all_helper_methods = row_parser_class.private_instance_methods(false) + row_parser_class.instance_methods(false) - all_validation_methods
field_mapping_for_errors = row_parser_class.new.send("field_mapping_for_errors") field_mapping_for_errors = row_parser_class.new.send("field_mapping_for_errors")
DocumentationGenerator.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, "lettings") DocumentationGenerator.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, "lettings")
end
end end
desc "Generate documentation for lettings numeric validations" desc "Generate documentation for lettings numeric validations"
task add_numeric_lettings_validations: :environment do task :add_numeric_lettings_validations, %i[year] => :environment do |_task, args|
form = FormHandler.instance.forms["current_lettings"] form_year = args[:year]&.to_i
raise "Usage: rake generate_lettings_documentation:add_numeric_lettings_validations['year']" if form_year.blank?
form = FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(form_year, "lettings")]
raise "No form found for given year" if form.blank?
form.numeric_questions.each do |question| form.numeric_questions.each do |question|
next unless question.min || question.max next unless question.min || question.max

81
lib/tasks/generate_sales_documentation.rake

@ -1,56 +1,65 @@
namespace :generate_sales_documentation do namespace :generate_sales_documentation do
desc "Generate documentation for hard sales validations" desc "Generate documentation for hard sales validations"
task describe_sales_validations: :environment do task :describe_sales_validations, %i[year] => :environment do |_task, args|
form_year = args[:year]&.to_i
raise "Usage: rake generate_sales_documentation:describe_sales_validations['year']" if form_year.blank?
form = FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(form_year, "sales")]
raise "No form found for given year" if form.blank?
client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"]) client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"])
include Validations::Sales::SetupValidations
include Validations::Sales::HouseholdValidations documentation_generator = DocumentationGenerator.new
include Validations::Sales::PropertyValidations validation_classes = [Validations::Sales::SetupValidations,
include Validations::Sales::FinancialValidations Validations::Sales::HouseholdValidations,
include Validations::Sales::SaleInformationValidations Validations::Sales::PropertyValidations,
include Validations::SharedValidations Validations::Sales::FinancialValidations,
include Validations::LocalAuthorityValidations Validations::Sales::SaleInformationValidations,
all_validation_methods = public_methods.select { |method| method.starts_with?("validate_") } Validations::SharedValidations,
all_methods = [Validations::Sales::SetupValidations, Validations::LocalAuthorityValidations]
Validations::Sales::HouseholdValidations, all_validation_methods, all_helper_methods = documentation_generator.validation_and_helper_methods(validation_classes)
Validations::Sales::PropertyValidations, documentation_generator.describe_hard_validations(client, form, all_validation_methods, all_helper_methods, "sales")
Validations::Sales::FinancialValidations,
Validations::Sales::SaleInformationValidations,
Validations::SharedValidations,
Validations::LocalAuthorityValidations].map { |x| x.instance_methods + x.private_instance_methods }.flatten
all_helper_methods = all_methods - all_validation_methods
DocumentationGenerator.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, "sales")
end end
desc "Generate documentation for soft sales validations" desc "Generate documentation for soft sales validations"
task describe_soft_sales_validations: :environment do task :describe_soft_sales_validations, %i[year] => :environment do |_task, args|
include Validations::SoftValidations form_year = args[:year]&.to_i
include Validations::Sales::SoftValidations raise "Usage: rake generate_sales_documentation:describe_soft_sales_validations['year']" if form_year.blank?
form = FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(form_year, "sales")]
raise "No form found for given year" if form.blank?
client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"]) client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"])
all_helper_methods = Validations::SoftValidations.private_instance_methods + Validations::Sales::SoftValidations.private_instance_methods documentation_generator = DocumentationGenerator.new
all_validation_methods = Validations::SoftValidations.instance_methods + Validations::Sales::SoftValidations.instance_methods all_helper_methods, all_validation_methods = documentation_generator.get_soft_sales_methods
DocumentationGenerator.new.describe_soft_validations(client, all_validation_methods, all_helper_methods, "sales") documentation_generator.describe_soft_validations(client, form, all_validation_methods, all_helper_methods, "sales")
end end
desc "Generate documentation for hard bu sales validations" desc "Generate documentation for hard bu sales validations"
task describe_bu_sales_validations: :environment do task :describe_bu_sales_validations, %i[year] => :environment do |_task, args|
form_year = args[:year]&.to_i
raise "Usage: rake generate_sales_documentation:describe_bu_sales_validations['year']" if form_year.blank?
form = FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(form_year, "sales")]
raise "No form found for given year" if form.blank?
row_parser_class = "BulkUpload::Sales::Year#{form_year}::RowParser".constantize
client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"]) client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"])
[[FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(2023, "sales")], BulkUpload::Sales::Year2023::RowParser], all_validation_methods = row_parser_class.private_instance_methods.select { |method| method.starts_with?("validate_") }
[FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(2024, "sales")], BulkUpload::Sales::Year2024::RowParser]].each do |form, row_parser_class| all_helper_methods = row_parser_class.private_instance_methods(false) + row_parser_class.instance_methods(false) - all_validation_methods
all_validation_methods = row_parser_class.private_instance_methods.select { |method| method.starts_with?("validate_") } field_mapping_for_errors = row_parser_class.new.send("field_mapping_for_errors")
all_helper_methods = row_parser_class.private_instance_methods(false) + row_parser_class.instance_methods(false) - all_validation_methods
field_mapping_for_errors = row_parser_class.new.send("field_mapping_for_errors")
DocumentationGenerator.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, "sales") DocumentationGenerator.new.describe_bu_validations(client, form, row_parser_class, all_validation_methods, all_helper_methods, field_mapping_for_errors, "sales")
end
end end
desc "Generate documentation for sales numeric validations" desc "Generate documentation for sales numeric validations"
task add_numeric_sales_validations: :environment do task :add_numeric_sales_validations, %i[year] => :environment do |_task, args|
form = FormHandler.instance.forms["current_sales"] form_year = args[:year]&.to_i
raise "Usage: rake generate_sales_documentation:add_numeric_sales_validations['year']" if form_year.blank?
form = FormHandler.instance.forms[FormHandler.instance.form_name_from_start_year(form_year, "sales")]
raise "No form found for given year" if form.blank?
form.numeric_questions.each do |question| form.numeric_questions.each do |question|
next unless question.min || question.max next unless question.min || question.max

202
spec/lib/tasks/generate_lettings_documentation_spec.rb

@ -6,14 +6,24 @@ RSpec.describe "generate_lettings_documentation" do
subject(:task) { Rake::Task["generate_lettings_documentation:add_numeric_lettings_validations"] } subject(:task) { Rake::Task["generate_lettings_documentation:add_numeric_lettings_validations"] }
before do before do
Timecop.freeze(Time.zone.local(2025, 1, 1))
Singleton.__init__(FormHandler)
allow(FormHandler.instance).to receive(:forms).and_return({ "current_lettings" => FormHandler.instance.forms["current_lettings"], "previous_lettings" => "2023_form", "next_lettings" => "2025_form" })
Rake.application.rake_require("tasks/generate_lettings_documentation") Rake.application.rake_require("tasks/generate_lettings_documentation")
Rake::Task.define_task(:environment) Rake::Task.define_task(:environment)
task.reenable task.reenable
end end
after do
Timecop.return
Singleton.__init__(FormHandler)
end
context "when the rake task is run" do context "when the rake task is run" do
it "creates new validation documentation records" do it "creates new validation documentation records" do
expect { task.invoke }.to change(LogValidation, :count) allow(FormHandler.instance.forms).to receive(:[]).with("current_lettings").and_return(FormHandler.instance.forms["current_lettings"])
expect { task.invoke(2024) }.to change(LogValidation, :count)
expect(LogValidation.where(validation_name: "minimum").count).to be_positive expect(LogValidation.where(validation_name: "minimum").count).to be_positive
expect(LogValidation.where(validation_name: "range").count).to be_positive expect(LogValidation.where(validation_name: "range").count).to be_positive
any_min_validation = LogValidation.where(validation_name: "minimum").first any_min_validation = LogValidation.where(validation_name: "minimum").first
@ -30,8 +40,194 @@ RSpec.describe "generate_lettings_documentation" do
end end
it "skips if the validation already exists in the database" do it "skips if the validation already exists in the database" do
task.invoke task.invoke(2024)
expect { task.invoke }.not_to change(LogValidation, :count) expect { task.invoke(2024) }.not_to change(LogValidation, :count)
end
context "with no year given" do
it "raises an error" do
expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake generate_lettings_documentation:add_numeric_lettings_validations['year']")
end
end
context "with an invalid year given" do
it "raises an error" do
expect { task.invoke("abc") }.to raise_error(RuntimeError, "No form found for given year")
end
end
context "with a year for non existing form" do
it "raises an error" do
expect { task.invoke("2022") }.to raise_error(RuntimeError, "No form found for given year")
end
end
end
end
describe ":describe_lettings_validations", type: :task do
subject(:task) { Rake::Task["generate_lettings_documentation:describe_lettings_validations"] }
let(:documentation_generator) { instance_double(DocumentationGenerator, describe_bu_validations: nil, validation_and_helper_methods: []) }
let(:client) { instance_double(OpenAI::Client) }
before do
allow(OpenAI::Client).to receive(:new).and_return(client)
allow(DocumentationGenerator).to receive(:new).and_return(documentation_generator)
Timecop.freeze(Time.zone.local(2025, 1, 1))
Singleton.__init__(FormHandler)
allow(FormHandler.instance).to receive(:forms).and_return({ "current_lettings" => "2024_form", "previous_lettings" => "2023_form", "next_lettings" => "2025_form" })
Rake.application.rake_require("tasks/generate_lettings_documentation")
Rake::Task.define_task(:environment)
task.reenable
end
after do
Timecop.return
Singleton.__init__(FormHandler)
end
context "with a year given" do
it "gets the correct form for next year" do
allow(FormHandler.instance.forms).to receive(:[]).with("next_lettings").and_return("2025_form")
expect(documentation_generator).to receive(:describe_hard_validations).with(client, "2025_form", anything, anything, "lettings")
task.invoke("2025")
end
it "gets the correct form for current year" do
allow(FormHandler.instance.forms).to receive(:[]).with("current_lettings").and_return("2024_form")
expect(documentation_generator).to receive(:describe_hard_validations).with(client, "2024_form", anything, anything, "lettings")
task.invoke("2024")
end
end
context "with no year given" do
it "raises an error" do
expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake generate_lettings_documentation:describe_lettings_validations['year']")
end
end
context "with an invalid year given" do
it "raises an error" do
expect { task.invoke("abc") }.to raise_error(RuntimeError, "No form found for given year")
end
end
context "with a year for non existing form" do
it "raises an error" do
expect { task.invoke("2022") }.to raise_error(RuntimeError, "No form found for given year")
end
end
end
describe ":describe_bu_lettings_validations", type: :task do
subject(:task) { Rake::Task["generate_lettings_documentation:describe_bu_lettings_validations"] }
let(:documentation_generator) { instance_double(DocumentationGenerator, describe_bu_validations: nil) }
let(:client) { instance_double(OpenAI::Client) }
before do
allow(OpenAI::Client).to receive(:new).and_return(client)
allow(DocumentationGenerator).to receive(:new).and_return(documentation_generator)
Timecop.freeze(Time.zone.local(2025, 1, 1))
Singleton.__init__(FormHandler)
allow(FormHandler.instance).to receive(:forms).and_return({ "current_lettings" => "2024_form", "previous_lettings" => "2023_form", "next_lettings" => "2025_form" })
Rake.application.rake_require("tasks/generate_lettings_documentation")
Rake::Task.define_task(:environment)
task.reenable
end
after do
Timecop.return
Singleton.__init__(FormHandler)
end
context "with a year given" do
it "gets the correct form for next year" do
allow(FormHandler.instance.forms).to receive(:[]).with("next_lettings").and_return("2025_form")
expect(documentation_generator).to receive(:describe_bu_validations).with(client, "2025_form", anything, anything, anything, anything, "lettings")
task.invoke("2025")
end
it "gets the correct form for current year" do
allow(FormHandler.instance.forms).to receive(:[]).with("current_lettings").and_return("2024_form")
expect(documentation_generator).to receive(:describe_bu_validations).with(client, "2024_form", anything, anything, anything, anything, "lettings")
task.invoke("2024")
end
end
context "with no year given" do
it "raises an error" do
expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake generate_lettings_documentation:describe_bu_lettings_validations['year']")
end
end
context "with an invalid year given" do
it "raises an error" do
expect { task.invoke("abc") }.to raise_error(RuntimeError, "No form found for given year")
end
end
context "with a year for non existing form" do
it "raises an error" do
expect { task.invoke("2022") }.to raise_error(RuntimeError, "No form found for given year")
end
end
end
describe ":describe_soft_lettings_validations", type: :task do
subject(:task) { Rake::Task["generate_lettings_documentation:describe_soft_lettings_validations"] }
let(:documentation_generator) { instance_double(DocumentationGenerator, describe_bu_validations: nil, get_soft_lettings_methods: []) }
let(:client) { instance_double(OpenAI::Client) }
before do
allow(OpenAI::Client).to receive(:new).and_return(client)
allow(DocumentationGenerator).to receive(:new).and_return(documentation_generator)
Timecop.freeze(Time.zone.local(2025, 1, 1))
Singleton.__init__(FormHandler)
allow(FormHandler.instance).to receive(:forms).and_return({ "current_lettings" => "2024_form", "previous_lettings" => "2023_form", "next_lettings" => "2025_form" })
Rake.application.rake_require("tasks/generate_lettings_documentation")
Rake::Task.define_task(:environment)
task.reenable
end
after do
Timecop.return
Singleton.__init__(FormHandler)
end
context "with a year given" do
it "gets the correct form for next year" do
allow(FormHandler.instance.forms).to receive(:[]).with("next_lettings").and_return("2025_form")
expect(documentation_generator).to receive(:describe_soft_validations).with(client, "2025_form", anything, anything, "lettings")
task.invoke("2025")
end
it "gets the correct form for current year" do
allow(FormHandler.instance.forms).to receive(:[]).with("current_lettings").and_return("2024_form")
expect(documentation_generator).to receive(:describe_soft_validations).with(client, "2024_form", anything, anything, "lettings")
task.invoke("2024")
end
end
context "with no year given" do
it "raises an error" do
expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake generate_lettings_documentation:describe_soft_lettings_validations['year']")
end
end
context "with an invalid year given" do
it "raises an error" do
expect { task.invoke("abc") }.to raise_error(RuntimeError, "No form found for given year")
end
end
context "with a year for non existing form" do
it "raises an error" do
expect { task.invoke("2022") }.to raise_error(RuntimeError, "No form found for given year")
end end
end end
end end

202
spec/lib/tasks/generate_sales_documentation_spec.rb

@ -6,14 +6,24 @@ RSpec.describe "generate_sales_documentation" do
subject(:task) { Rake::Task["generate_sales_documentation:add_numeric_sales_validations"] } subject(:task) { Rake::Task["generate_sales_documentation:add_numeric_sales_validations"] }
before do before do
Timecop.freeze(Time.zone.local(2025, 1, 1))
Singleton.__init__(FormHandler)
allow(FormHandler.instance).to receive(:forms).and_return({ "current_sales" => FormHandler.instance.forms["current_sales"], "previous_sales" => "2023_form", "next_sales" => "2025_form" })
Rake.application.rake_require("tasks/generate_sales_documentation") Rake.application.rake_require("tasks/generate_sales_documentation")
Rake::Task.define_task(:environment) Rake::Task.define_task(:environment)
task.reenable task.reenable
end end
after do
Timecop.return
Singleton.__init__(FormHandler)
end
context "when the rake task is run" do context "when the rake task is run" do
it "creates new validation documentation records" do it "creates new validation documentation records" do
expect { task.invoke }.to change(LogValidation, :count) allow(FormHandler.instance.forms).to receive(:[]).with("current_sales").and_return(FormHandler.instance.forms["current_sales"])
expect { task.invoke(2024) }.to change(LogValidation, :count)
expect(LogValidation.where(validation_name: "minimum").count).to be_positive expect(LogValidation.where(validation_name: "minimum").count).to be_positive
expect(LogValidation.where(validation_name: "range").count).to be_positive expect(LogValidation.where(validation_name: "range").count).to be_positive
any_min_validation = LogValidation.where(validation_name: "minimum").first any_min_validation = LogValidation.where(validation_name: "minimum").first
@ -30,8 +40,194 @@ RSpec.describe "generate_sales_documentation" do
end end
it "skips if the validation already exists in the database" do it "skips if the validation already exists in the database" do
task.invoke task.invoke(2024)
expect { task.invoke }.not_to change(LogValidation, :count) expect { task.invoke(2024) }.not_to change(LogValidation, :count)
end
context "with no year given" do
it "raises an error" do
expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake generate_sales_documentation:add_numeric_sales_validations['year']")
end
end
context "with an invalid year given" do
it "raises an error" do
expect { task.invoke("abc") }.to raise_error(RuntimeError, "No form found for given year")
end
end
context "with a year for non existing form" do
it "raises an error" do
expect { task.invoke("2022") }.to raise_error(RuntimeError, "No form found for given year")
end
end
end
end
describe ":describe_sales_validations", type: :task do
subject(:task) { Rake::Task["generate_sales_documentation:describe_sales_validations"] }
let(:documentation_generator) { instance_double(DocumentationGenerator, describe_bu_validations: nil, validation_and_helper_methods: []) }
let(:client) { instance_double(OpenAI::Client) }
before do
allow(OpenAI::Client).to receive(:new).and_return(client)
allow(DocumentationGenerator).to receive(:new).and_return(documentation_generator)
Timecop.freeze(Time.zone.local(2025, 1, 1))
Singleton.__init__(FormHandler)
allow(FormHandler.instance).to receive(:forms).and_return({ "current_sales" => "2024_form", "previous_sales" => "2023_form", "next_sales" => "2025_form" })
Rake.application.rake_require("tasks/generate_sales_documentation")
Rake::Task.define_task(:environment)
task.reenable
end
after do
Timecop.return
Singleton.__init__(FormHandler)
end
context "with a year given" do
it "gets the correct form for next year" do
allow(FormHandler.instance.forms).to receive(:[]).with("next_sales").and_return("2025_form")
expect(documentation_generator).to receive(:describe_hard_validations).with(client, "2025_form", anything, anything, "sales")
task.invoke("2025")
end
it "gets the correct form for current year" do
allow(FormHandler.instance.forms).to receive(:[]).with("current_sales").and_return("2024_form")
expect(documentation_generator).to receive(:describe_hard_validations).with(client, "2024_form", anything, anything, "sales")
task.invoke("2024")
end
end
context "with no year given" do
it "raises an error" do
expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake generate_sales_documentation:describe_sales_validations['year']")
end
end
context "with an invalid year given" do
it "raises an error" do
expect { task.invoke("abc") }.to raise_error(RuntimeError, "No form found for given year")
end
end
context "with a year for non existing form" do
it "raises an error" do
expect { task.invoke("2022") }.to raise_error(RuntimeError, "No form found for given year")
end
end
end
describe ":describe_bu_sales_validations", type: :task do
subject(:task) { Rake::Task["generate_sales_documentation:describe_bu_sales_validations"] }
let(:documentation_generator) { instance_double(DocumentationGenerator, describe_bu_validations: nil) }
let(:client) { instance_double(OpenAI::Client) }
before do
allow(OpenAI::Client).to receive(:new).and_return(client)
allow(DocumentationGenerator).to receive(:new).and_return(documentation_generator)
Timecop.freeze(Time.zone.local(2025, 1, 1))
Singleton.__init__(FormHandler)
allow(FormHandler.instance).to receive(:forms).and_return({ "current_sales" => "2024_form", "previous_sales" => "2023_form", "next_sales" => "2025_form" })
Rake.application.rake_require("tasks/generate_sales_documentation")
Rake::Task.define_task(:environment)
task.reenable
end
after do
Timecop.return
Singleton.__init__(FormHandler)
end
context "with a year given" do
it "gets the correct form for next year" do
allow(FormHandler.instance.forms).to receive(:[]).with("next_sales").and_return("2025_form")
expect(documentation_generator).to receive(:describe_bu_validations).with(client, "2025_form", anything, anything, anything, anything, "sales")
task.invoke("2025")
end
it "gets the correct form for current year" do
allow(FormHandler.instance.forms).to receive(:[]).with("current_sales").and_return("2024_form")
expect(documentation_generator).to receive(:describe_bu_validations).with(client, "2024_form", anything, anything, anything, anything, "sales")
task.invoke("2024")
end
end
context "with no year given" do
it "raises an error" do
expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake generate_sales_documentation:describe_bu_sales_validations['year']")
end
end
context "with an invalid year given" do
it "raises an error" do
expect { task.invoke("abc") }.to raise_error(RuntimeError, "No form found for given year")
end
end
context "with a year for non existing form" do
it "raises an error" do
expect { task.invoke("2022") }.to raise_error(RuntimeError, "No form found for given year")
end
end
end
describe ":describe_soft_sales_validations", type: :task do
subject(:task) { Rake::Task["generate_sales_documentation:describe_soft_sales_validations"] }
let(:documentation_generator) { instance_double(DocumentationGenerator, describe_bu_validations: nil, get_soft_sales_methods: []) }
let(:client) { instance_double(OpenAI::Client) }
before do
allow(OpenAI::Client).to receive(:new).and_return(client)
allow(DocumentationGenerator).to receive(:new).and_return(documentation_generator)
Timecop.freeze(Time.zone.local(2025, 1, 1))
Singleton.__init__(FormHandler)
allow(FormHandler.instance).to receive(:forms).and_return({ "current_sales" => "2024_form", "previous_sales" => "2023_form", "next_sales" => "2025_form" })
Rake.application.rake_require("tasks/generate_sales_documentation")
Rake::Task.define_task(:environment)
task.reenable
end
after do
Timecop.return
Singleton.__init__(FormHandler)
end
context "with a year given" do
it "gets the correct form for next year" do
allow(FormHandler.instance.forms).to receive(:[]).with("next_sales").and_return("2025_form")
expect(documentation_generator).to receive(:describe_soft_validations).with(client, "2025_form", anything, anything, "sales")
task.invoke("2025")
end
it "gets the correct form for current year" do
allow(FormHandler.instance.forms).to receive(:[]).with("current_sales").and_return("2024_form")
expect(documentation_generator).to receive(:describe_soft_validations).with(client, "2024_form", anything, anything, "sales")
task.invoke("2024")
end
end
context "with no year given" do
it "raises an error" do
expect { task.invoke(nil) }.to raise_error(RuntimeError, "Usage: rake generate_sales_documentation:describe_soft_sales_validations['year']")
end
end
context "with an invalid year given" do
it "raises an error" do
expect { task.invoke("abc") }.to raise_error(RuntimeError, "No form found for given year")
end
end
context "with a year for non existing form" do
it "raises an error" do
expect { task.invoke("2022") }.to raise_error(RuntimeError, "No form found for given year")
end end
end end
end end

28
spec/services/documentation_generator_spec.rb

@ -17,10 +17,11 @@ describe DocumentationGenerator do
describe ":describe_hard_validations" do describe ":describe_hard_validations" do
context "when the service is run with lettings type" do context "when the service is run with lettings type" do
let(:log_type) { "lettings" } let(:log_type) { "lettings" }
let(:form) { FormHandler.instance.forms["current_lettings"] }
it "creates new validation documentation records" do it "creates new validation documentation records" do
expect(Rails.logger).to receive(:info).with(/described/).at_least(:once) 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 { described_class.new.describe_hard_validations(client, form, 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) expect(LogValidation.where(validation_name: "validate_numeric_min_max").count).to eq(1)
any_validation = LogValidation.first any_validation = LogValidation.first
expect(any_validation.description).to eq("Validates the format.") expect(any_validation.description).to eq("Validates the format.")
@ -37,12 +38,12 @@ describe DocumentationGenerator do
it "calls the client" do it "calls the client" do
expect(client).to receive(:chat) expect(client).to receive(:chat)
described_class.new.describe_hard_validations(client, all_validation_methods, all_helper_methods, log_type) described_class.new.describe_hard_validations(client, form, all_validation_methods, all_helper_methods, log_type)
end end
it "skips if the validation already exists in the database" do 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) described_class.new.describe_hard_validations(client, form, 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) expect { described_class.new.describe_hard_validations(client, form, all_validation_methods, all_helper_methods, log_type) }.not_to change(LogValidation, :count)
end end
context "when the response is not a JSON" do context "when the response is not a JSON" do
@ -51,7 +52,7 @@ describe DocumentationGenerator do
it "raises an error" do 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(/Failed to save/).at_least(:once)
expect(Rails.logger).to receive(:error).with(/Error/).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) described_class.new.describe_hard_validations(client, form, all_validation_methods, all_helper_methods, log_type)
end end
end end
@ -61,17 +62,18 @@ describe DocumentationGenerator do
it "raises an error" do 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(/Failed to save/).at_least(:once)
expect(Rails.logger).to receive(:error).with(/Error/).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) described_class.new.describe_hard_validations(client, form, all_validation_methods, all_helper_methods, log_type)
end end
end end
end end
context "when the service is run with sales type" do context "when the service is run with sales type" do
let(:log_type) { "sales" } let(:log_type) { "sales" }
let(:form) { FormHandler.instance.forms["current_sales"] }
it "creates new validation documentation records" do it "creates new validation documentation records" do
expect(Rails.logger).to receive(:info).with(/described/).at_least(:once) 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 { described_class.new.describe_hard_validations(client, form, 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) expect(LogValidation.where(validation_name: "validate_numeric_min_max").count).to eq(1)
any_validation = LogValidation.first any_validation = LogValidation.first
expect(any_validation.description).to eq("Validates the format.") expect(any_validation.description).to eq("Validates the format.")
@ -97,9 +99,10 @@ describe DocumentationGenerator do
context "when the service is run for lettings" do context "when the service is run for lettings" do
let(:log_type) { "lettings" } let(:log_type) { "lettings" }
let(:form) { FormHandler.instance.forms["current_lettings"] }
it "creates new validation documentation records" do 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 { described_class.new.describe_soft_validations(client, form, all_validation_methods, all_helper_methods, log_type) }.to change(LogValidation, :count)
expect(LogValidation.where(validation_name: "rent_soft_validation_triggered?").count).to be_positive expect(LogValidation.where(validation_name: "rent_soft_validation_triggered?").count).to be_positive
any_validation = LogValidation.first any_validation = LogValidation.first
expect(any_validation.description).to eq("Validates the format.") expect(any_validation.description).to eq("Validates the format.")
@ -115,21 +118,22 @@ describe DocumentationGenerator do
it "calls the client" do it "calls the client" do
expect(client).to receive(:chat) expect(client).to receive(:chat)
described_class.new.describe_soft_validations(client, all_validation_methods, all_helper_methods, log_type) described_class.new.describe_soft_validations(client, form, all_validation_methods, all_helper_methods, log_type)
end end
it "skips if the validation already exists in the database" do 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) described_class.new.describe_soft_validations(client, form, 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) expect { described_class.new.describe_soft_validations(client, form, all_validation_methods, all_helper_methods, log_type) }.not_to change(LogValidation, :count)
end end
end end
context "when the service is run for sales" do context "when the service is run for sales" do
let(:log_type) { "sales" } let(:log_type) { "sales" }
let(:form) { FormHandler.instance.forms["current_sales"] }
let(:all_validation_methods) { ["income2_outside_soft_range_for_ecstat?"] } let(:all_validation_methods) { ["income2_outside_soft_range_for_ecstat?"] }
it "creates new validation documentation records" do 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 { described_class.new.describe_soft_validations(client, form, all_validation_methods, all_helper_methods, log_type) }.to change(LogValidation, :count)
expect(LogValidation.where(validation_name: "income2_outside_soft_range_for_ecstat?").count).to be_positive expect(LogValidation.where(validation_name: "income2_outside_soft_range_for_ecstat?").count).to be_positive
any_validation = LogValidation.first any_validation = LogValidation.first
expect(any_validation.description).to eq("Validates the format.") expect(any_validation.description).to eq("Validates the format.")

Loading…
Cancel
Save