Merge pull request #10863 from colinux/maintenance-tasks-template
ETQ dev, je peux programmer des maintenance tasks à jouer lors de leur déploiement
This commit is contained in:
commit
fbb9405e50
40 changed files with 349 additions and 41 deletions
|
@ -2,6 +2,9 @@
|
|||
|
||||
module Maintenance
|
||||
class BackfillBulkMessagesWithProcedureIdTask < MaintenanceTasks::Task
|
||||
# Périmètre: envoi d’un email groupé aux usagers ayant dossiers en brouillon.
|
||||
# Change la manière dont ces messages sont liés aux démarches.
|
||||
# 2024-03-12-01 PR #10071
|
||||
def collection
|
||||
BulkMessage
|
||||
.where(procedure: nil)
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
module Maintenance
|
||||
class BackfillCityNameTask < MaintenanceTasks::Task
|
||||
# corrige des données du champ adresse suite à un bug
|
||||
# introduit pendant quelques jours début mars
|
||||
# 2024-04-09-02 PR #10290
|
||||
attribute :champ_ids, :string
|
||||
validates :champ_ids, presence: true
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
module Maintenance
|
||||
class BackfillClonedChampsPrivatePieceJustificativesTask < MaintenanceTasks::Task
|
||||
# Supprime les PJ d’annotations privées
|
||||
# qui étaient conservées par erreur lorsqu’un dossier était cloné
|
||||
# 2024-05-27-01 PR #10435
|
||||
def collection
|
||||
Dossier.en_brouillon.where.not(parent_dossier_id: nil)
|
||||
end
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
module Maintenance
|
||||
class BackfillClosingReasonInClosedProceduresTask < MaintenanceTasks::Task
|
||||
# Remet les messages de cloture d'une démarche proprement (sinon affichage KO).
|
||||
# Suite de UpdateClosingReasonIfNoReplacedByIdTask
|
||||
# 2024-05-27-01 PR #9930
|
||||
def collection
|
||||
Procedure
|
||||
.with_discarded
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
module Maintenance
|
||||
class BackfillCommuneCodeFromNameTask < MaintenanceTasks::Task
|
||||
# corrige structure champs commune pour une démarche donnée. Suite à un bug ?
|
||||
# 2024-05-31-01 PR #10469
|
||||
|
||||
attribute :procedure_id, :string
|
||||
validates :procedure_id, presence: true
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
module Maintenance
|
||||
class BackfillDepartementServicesTask < MaintenanceTasks::Task
|
||||
# Fait le lien service – département pour permettre
|
||||
# le filtrage des démarches par département
|
||||
# 2023-10-30-01 PR #9647
|
||||
def collection
|
||||
Service.where.not(etablissement_infos: nil)
|
||||
end
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module Maintenance
|
||||
class BackfillDeposeAtOnDeletedDossiersTask < MaintenanceTasks::Task
|
||||
# Améliore les stats à propos des dates de dépôts pour les dossiers supprimés
|
||||
# 2024-04-05-01 PR #10259
|
||||
def collection
|
||||
DeletedDossier.where(depose_at: nil)
|
||||
end
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module Maintenance
|
||||
class BackfillEffectifAnnuelAnneeTask < MaintenanceTasks::Task
|
||||
# API entreprise: rattrape les informations d'effectif
|
||||
# 2024-05-27-01 PR #10053
|
||||
def collection
|
||||
Etablissement.where.not(entreprise_effectif_annuel: nil).where(entreprise_effectif_annuel_annee: nil)
|
||||
end
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module Maintenance
|
||||
class BackfillInvalidDossiersForTiersTask < MaintenanceTasks::Task
|
||||
# Corrige les dossiers declarés pour un tiers mais sans avoir renseigné les infos du tiers
|
||||
# 2024-05-22-01
|
||||
def collection
|
||||
Dossier.where(for_tiers: true).where(mandataire_first_name: nil)
|
||||
end
|
||||
|
|
24
app/tasks/maintenance/concerns/runnable_on_deploy_concern.rb
Normal file
24
app/tasks/maintenance/concerns/runnable_on_deploy_concern.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Maintenance
|
||||
module RunnableOnDeployConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
def run_on_first_deploy
|
||||
@run_on_first_deploy = true
|
||||
end
|
||||
|
||||
def run_on_deploy?
|
||||
return false unless @run_on_first_deploy
|
||||
|
||||
task = MaintenanceTasks::TaskDataShow.new(name)
|
||||
|
||||
return false if task.completed_runs.not_errored.any?
|
||||
return false if task.active_runs.any?
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
25
app/tasks/maintenance/concerns/statements_helpers_concern.rb
Normal file
25
app/tasks/maintenance/concerns/statements_helpers_concern.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Maintenance
|
||||
module StatementsHelpersConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
# Execute block in transaction with a local statement timeout.
|
||||
# A value of 0 disable the timeout.
|
||||
#
|
||||
# Example:
|
||||
# def collection
|
||||
# with_statement_timeout("5min") do
|
||||
# Dossier.all
|
||||
# end
|
||||
# end
|
||||
def with_statement_timeout(timeout)
|
||||
ApplicationRecord.transaction do
|
||||
ApplicationRecord.connection.execute("SET LOCAL statement_timeout = '#{timeout}'")
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
module Maintenance
|
||||
class CreatePreviewsForPjOfLatestDossiersTask < MaintenanceTasks::Task
|
||||
# Génère les vignettes de PJ existantes pour les dossiers déposés entre 2 dates (facultatif)
|
||||
# Elles sont affichées dans le nouvel onglet "Pièces jointes" des instructeurs.
|
||||
# 2024-07-11-01
|
||||
attribute :start_text, :string
|
||||
validates :start_text, presence: true
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
module Maintenance
|
||||
class CreateVariantsForPjOfLatestDossiersTask < MaintenanceTasks::Task
|
||||
# Génère les vignettes de fichiers PDF pour les dossiers déposés entre 2 dates (facultatif)
|
||||
# Elles sont affichées dans le nouvel onglet "Pièces jointes" des instructeurs.
|
||||
# 2024-07-11-01
|
||||
attribute :start_text, :string
|
||||
validates :start_text, presence: true
|
||||
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
module Maintenance
|
||||
class DeleteDraftRevisionTypeDeChampsTask < MaintenanceTasks::Task
|
||||
csv_collection
|
||||
|
||||
# Modifie le form d’une démarche à partir d’un CSV (dev spécifique Fonds Verts).
|
||||
# See UpdateDraftRevisionTypeDeChampsTask for more information
|
||||
# Just add delete_flag with "true" to effectively remove the type de champ from the draft.
|
||||
# Just add delete_flag with "true" in CSV to effectively remove the type de champ from the draft.
|
||||
csv_collection
|
||||
|
||||
def process(row)
|
||||
return unless row["delete_flag"] == "true"
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
module Maintenance
|
||||
class DestroyIncompleteBulkMessagesTask < MaintenanceTasks::Task
|
||||
# Périmètre: envoi d’un email groupé aux usagers ayant dossiers en brouillon.
|
||||
# Change la manière dont ces messages sont liés aux démarches.
|
||||
# Suite de BackfillBulkMessagesWithProcedureIdTask
|
||||
# 2024-03-12-01 PR #10071
|
||||
def collection
|
||||
BulkMessage.where(procedure: nil).where.missing(:groupe_instructeurs)
|
||||
end
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module Maintenance
|
||||
class DestroyProcedureWithoutAdministrateurAndWithoutDossierTask < MaintenanceTasks::Task
|
||||
# suppression de procédures closes sans admin et sans dossier
|
||||
# 2024-03-18-01 PR #10125
|
||||
def collection
|
||||
Procedure.with_discarded.where.missing(:administrateurs, :dossiers)
|
||||
end
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module Maintenance
|
||||
class DisableRemainingInvalidMonAvisTask < MaintenanceTasks::Task
|
||||
# Supprime les codes d’intégration « mon avis » invalides
|
||||
# 2024-03-18-01 PR #10120
|
||||
def collection
|
||||
# rubocop:disable DS/Unscoped
|
||||
Procedure.unscoped.where.not(monavis_embed: nil)
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
module Maintenance
|
||||
class FixDecimalNumberWithSpacesTask < MaintenanceTasks::Task
|
||||
# normalise les champs nombres en y supprimant les éventuels espaces
|
||||
# 2024-07-01-01 PR #10554
|
||||
|
||||
ANY_SPACES = /[[:space:]]/
|
||||
def collection
|
||||
Champs::DecimalNumberChamp.where.not(value: nil)
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
module Maintenance
|
||||
class FixDureeConservationGreaterThanMaxDureeConservationTask < MaintenanceTasks::Task
|
||||
# Corrige la durée de conservation des dossiers :
|
||||
# pour toutes les démarches dont la durée de conservation est supérieure
|
||||
# à celle de l'instance, on prend la durée max de DS (12 mois)
|
||||
# 2024-05-27-01 PR #10107
|
||||
def collection
|
||||
Procedure.where('duree_conservation_dossiers_dans_ds > max_duree_conservation_dossiers_dans_ds')
|
||||
end
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module Maintenance
|
||||
class FixOpenProceduresWithClosingReasonTask < MaintenanceTasks::Task
|
||||
# Corrige des démarches avec un motif de fermerture alors qu’elles ont été publiées
|
||||
# 2024-05-27-01 PR #10181
|
||||
def collection
|
||||
Procedure
|
||||
.with_discarded
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
module Maintenance
|
||||
class MoveDolToColdStorageTask < MaintenanceTasks::Task
|
||||
# Opération de rattrapage suite à un cron qui ne fonctionnait plus.
|
||||
# Permet de déplacer toutes les traces fonctionnelles (DossierOperationLog)
|
||||
# vers le stockage object plutot que de les conserver en BDD
|
||||
# 2024-04-15-01
|
||||
attribute :start_text, :string
|
||||
validates :start_text, presence: true
|
||||
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
module Maintenance
|
||||
class RecomputeBlobChecksumTask < MaintenanceTasks::Task
|
||||
# Avant février 2024, les filigranes ont corrompu les hash des fichiers.
|
||||
# Régulièrement, des dossiers en brouillon étaient déposés avec ce problème
|
||||
# (on retrouve les fichiers corrompu dans l'onglet retry de sidekiq).
|
||||
# Cette tache recalcule les hashes.
|
||||
# 2024-05-27-01
|
||||
attribute :blob_ids, :string
|
||||
validates :blob_ids, presence: true
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
module Maintenance
|
||||
class SamsungBrowserIsSupportedTask < MaintenanceTasks::Task
|
||||
# Corrige une donnée si le navigateur utilisé
|
||||
# dans l’historique des Traitements des dossiers
|
||||
# 2024-02-21-01
|
||||
def collection
|
||||
Traitement.where(browser_name: 'Samsung Browser', browser_version: 12..)
|
||||
end
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module Maintenance
|
||||
class SpreadDossierDeletionTask < MaintenanceTasks::Task
|
||||
# Contourne un égorgement de suppression de millions de dossiers qui aurait eu lieu le même jour
|
||||
# 2024-05-27-01 PR #10062
|
||||
ERROR_OCCURED_AT = Date.new(2024, 2, 14)
|
||||
ERROR_OCCURED_RANGE = ERROR_OCCURED_AT.at_midnight..(ERROR_OCCURED_AT + 1.day)
|
||||
SPREAD_DURATION_IN_DAYS = 150
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Maintenance
|
||||
class T20241009NoopAttemptRunOnDeployTask < MaintenanceTasks::Task
|
||||
# Documentation: cette tâche ne fait rien mais sert à vérifier
|
||||
# qu'elle sera bien exécutée sur le déploiement suivant
|
||||
# pour remplacer after party.
|
||||
|
||||
include RunnableOnDeployConcern
|
||||
include StatementsHelpersConcern
|
||||
|
||||
# Uncomment only if this task MUST run imperatively on its first deployment.
|
||||
# If possible, leave commented for manual execution later.
|
||||
run_on_first_deploy
|
||||
|
||||
def collection
|
||||
1.upto(10).to_a
|
||||
end
|
||||
|
||||
def process(element)
|
||||
# NOOP
|
||||
end
|
||||
|
||||
def count
|
||||
10
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
module Maintenance
|
||||
class UpdateClosingReasonIfNoReplacedByIdTask < MaintenanceTasks::Task
|
||||
# Remet les messages de cloture d'une démarche proprement (sinon affichage KO).
|
||||
# Avant BackfillClosingReasonInClosedProceduresTask
|
||||
# 2024-03-21-01 PR #10158
|
||||
def collection
|
||||
Procedure
|
||||
.with_discarded
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
module Maintenance
|
||||
class UpdateConditionsBasedOnCommuneOrEpciChampTask < MaintenanceTasks::Task
|
||||
# Met à jour les conditions et règles de routage
|
||||
# pour les champs communes et ECPI suite à l'ajout de nouveaux opérateurs
|
||||
# Voir aussi UpdateRoutingRulesBasedOnCommuneOrEpciChampTask
|
||||
# 2023-12-20-01 PR #9850
|
||||
include Logic
|
||||
|
||||
def collection
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module Maintenance
|
||||
class UpdateDraftRevisionTypeDeChampsTask < MaintenanceTasks::Task
|
||||
# Modifie le form d’une démarche à partir d’un CSV (dev pour les Fonds Verts)
|
||||
|
||||
csv_collection
|
||||
|
||||
# CSV structure:
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
module Maintenance
|
||||
class UpdateRoutingRulesBasedOnCommuneOrEpciChampTask < MaintenanceTasks::Task
|
||||
# Ces 2 tâches mettent à jour les conditions et règles de routage
|
||||
# pour les champs communes et ECPI suite à l'ajout de nouveaux opérateurs
|
||||
# Voir aussi UpdateConditionsBasedOnCommuneOrEpciChampTask
|
||||
# 2023-12-20-01 PR #9850
|
||||
include Logic
|
||||
|
||||
def collection
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
module Maintenance
|
||||
class UpdateServiceEtablissementInfosTask < MaintenanceTasks::Task
|
||||
# Géocode les services à partir des établissements
|
||||
# 2024-05-27-01 PR #10106
|
||||
|
||||
# No more 20 geocoding by 10 seconds window
|
||||
THROTTLE_LIMIT = 20
|
||||
THROTTLE_PERIOD = 10.seconds
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
module Maintenance
|
||||
class UpdateZonesTask < MaintenanceTasks::Task
|
||||
# Synchronise les zones en base à partir du fichier de config zones.yml
|
||||
# 2024-05-27-01 PR #10077
|
||||
def collection
|
||||
config = Psych.safe_load(Rails.root.join("config", "zones.yml").read)
|
||||
config['ministeres']
|
||||
|
|
|
@ -37,6 +37,9 @@ FileUtils.chdir APP_ROOT do
|
|||
puts "\n== Running after_party tasks =="
|
||||
system! 'bin/rails after_party:run'
|
||||
|
||||
puts "\n== Running on deploy maintenance tasks =="
|
||||
system! 'bin/rails deploy:maintenance_tasks'
|
||||
|
||||
puts "\n== Removing old logs =="
|
||||
system! 'bin/rails log:clear'
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ module TPS
|
|||
Rails.autoloaders.main.ignore(Rails.root.join('lib/cops'))
|
||||
Rails.autoloaders.main.ignore(Rails.root.join('lib/linters'))
|
||||
Rails.autoloaders.main.ignore(Rails.root.join('lib/tasks/task_helper.rb'))
|
||||
Rails.autoloaders.main.collapse('app/tasks/maintenance/concerns')
|
||||
config.paths.add Rails.root.join('spec/mailers/previews').to_s, eager_load: true
|
||||
config.autoload_paths << "#{Rails.root}/app/jobs/concerns"
|
||||
|
||||
|
|
|
@ -113,6 +113,29 @@
|
|||
],
|
||||
"note": ""
|
||||
},
|
||||
{
|
||||
"warning_type": "SQL Injection",
|
||||
"warning_code": 0,
|
||||
"fingerprint": "7dc4935d5b68365bedb8f6b953f01b396cff4daa533c98ee56a84249ca5a1f90",
|
||||
"check_name": "SQL",
|
||||
"message": "Possible SQL injection",
|
||||
"file": "app/tasks/maintenance/concerns/statements_helpers_concern.rb",
|
||||
"line": 19,
|
||||
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
|
||||
"code": "ApplicationRecord.connection.execute(\"SET LOCAL statement_timeout = '#{timeout}'\")",
|
||||
"render_path": null,
|
||||
"location": {
|
||||
"type": "method",
|
||||
"class": "Maintenance::StatementsHelpersConcern",
|
||||
"method": "with_statement_timeout"
|
||||
},
|
||||
"user_input": "timeout",
|
||||
"confidence": "Medium",
|
||||
"cwe_id": [
|
||||
89
|
||||
],
|
||||
"note": ""
|
||||
},
|
||||
{
|
||||
"warning_type": "Cross-Site Scripting",
|
||||
"warning_code": 2,
|
||||
|
|
20
config/initializers/maintenance_tasks.rb
Normal file
20
config/initializers/maintenance_tasks.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Rails.application.config.after_initialize do
|
||||
if defined?(Rails::Generators)
|
||||
require "generators/maintenance_tasks/task_generator"
|
||||
|
||||
class MaintenanceTasks::TaskGenerator
|
||||
alias_method :original_assign_names!, :assign_names!
|
||||
source_paths << Rails.root.join("lib/templates/maintenance_tasks")
|
||||
|
||||
private
|
||||
|
||||
# Prefix the task name with a date so the tasks are better sorted.
|
||||
def assign_names!(name)
|
||||
timestamped_name = "T#{Date.current.strftime("%Y%m%d")}#{name}"
|
||||
original_assign_names!(timestamped_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
17
lib/generators/after_party/task/task_generator.rb
Normal file
17
lib/generators/after_party/task/task_generator.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails/generators'
|
||||
|
||||
Rails.application.config.after_initialize do
|
||||
module AfterParty
|
||||
module Generators
|
||||
class TaskGenerator
|
||||
prepend Module.new {
|
||||
def invoke_all
|
||||
warn "[DEPRECATION] 'after_party:task' is deprecated. Use 'rails generate maintenance_tasks:task #{name}' instead."
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,42 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
def domains_for_stage
|
||||
if ENV['DOMAINS'].present?
|
||||
ENV['DOMAINS'].split
|
||||
else
|
||||
raise "DOMAINS is empty. It must be something like DOMAINS='web1.dev web2.dev'"
|
||||
end
|
||||
end
|
||||
|
||||
task :setup do
|
||||
domains = domains_for_stage
|
||||
|
||||
domains.each do |domain|
|
||||
sh "mina setup domain=#{domain}"
|
||||
end
|
||||
end
|
||||
|
||||
task :deploy do
|
||||
domains = domains_for_stage
|
||||
branch = ENV.fetch('BRANCH')
|
||||
|
||||
domains.each do |domain|
|
||||
sh "mina deploy domain=#{domain} branch=#{branch} force_asset_precompile=true"
|
||||
end
|
||||
end
|
||||
|
||||
task :post_deploy do
|
||||
domains = domains_for_stage
|
||||
branch = ENV.fetch('BRANCH')
|
||||
|
||||
sh "mina post_deploy domain=#{domains.first} branch=#{branch}"
|
||||
end
|
||||
|
||||
task :rollback do
|
||||
domains = domains_for_stage
|
||||
branch = ENV.fetch('BRANCH')
|
||||
|
||||
domains.each do |domain|
|
||||
sh "mina rollback service:restart_puma service:reload_nginx service:restart_delayed_job domain=#{domain} branch=#{branch}"
|
||||
namespace :deploy do
|
||||
task maintenance_tasks: :environment do
|
||||
tasks = MaintenanceTasks::Task
|
||||
.load_all
|
||||
.filter { _1.respond_to?(:run_on_deploy?) && _1.run_on_deploy? }
|
||||
|
||||
tasks.each do |task|
|
||||
Rails.logger.info { "MaintenanceTask run on deploy #{task.name}" }
|
||||
MaintenanceTasks::Runner.run(name: task.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
32
lib/templates/maintenance_tasks/task.rb.tt
Normal file
32
lib/templates/maintenance_tasks/task.rb.tt
Normal file
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module <%= tasks_module %>
|
||||
<% module_namespacing do -%>
|
||||
class <%= class_name %>Task < MaintenanceTasks::Task
|
||||
# Documentation: cette tâche modifie les données pour…
|
||||
|
||||
include RunnableOnDeployConcern
|
||||
include StatementsHelpersConcern
|
||||
|
||||
# Uncomment only if this task MUST run imperatively on its first deployment.
|
||||
# If possible, leave commented for manual execution later.
|
||||
# run_on_first_deploy
|
||||
|
||||
def collection
|
||||
# Collection to be iterated over
|
||||
# Must be Active Record Relation or Array
|
||||
end
|
||||
|
||||
def process(element)
|
||||
# The work to be done in a single iteration of the task.
|
||||
# This should be idempotent, as the same element may be processed more
|
||||
# than once if the task is interrupted and resumed.
|
||||
end
|
||||
|
||||
def count
|
||||
# Optionally, define the number of rows that will be iterated over
|
||||
# This is used to track the task's progress
|
||||
end
|
||||
end
|
||||
<% end -%>
|
||||
end
|
|
@ -0,0 +1,53 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Maintenance::RunnableOnDeployConcern do
|
||||
let(:test_class) do
|
||||
Class.new do
|
||||
include Maintenance::RunnableOnDeployConcern
|
||||
end
|
||||
end
|
||||
|
||||
describe '.run_on_deploy?' do
|
||||
context 'when run_on_first_deploy is not set' do
|
||||
it 'returns false' do
|
||||
expect(test_class.run_on_deploy?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when run_on_first_deploy is set' do
|
||||
before do
|
||||
test_class.run_on_first_deploy
|
||||
allow(MaintenanceTasks::TaskDataShow).to receive(:new).and_return(task_data_show)
|
||||
end
|
||||
|
||||
let(:task_data_show) { instance_double(MaintenanceTasks::TaskDataShow, completed_runs: completed_runs, active_runs: active_runs) }
|
||||
let(:completed_runs) { double(ActiveRecord::Relation, not_errored: not_errored_runs) }
|
||||
let(:active_runs) { [] }
|
||||
let(:not_errored_runs) { [] }
|
||||
|
||||
context 'when there are no run yet' do
|
||||
it 'returns true' do
|
||||
expect(test_class.run_on_deploy?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are completed runs without errors' do
|
||||
let(:not_errored_runs) { [instance_double(MaintenanceTasks::Run)] }
|
||||
|
||||
it 'returns false' do
|
||||
expect(test_class.run_on_deploy?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are active runs' do
|
||||
let(:active_runs) { [instance_double(MaintenanceTasks::Run)] }
|
||||
|
||||
it 'returns false' do
|
||||
expect(test_class.run_on_deploy?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Maintenance::StatementsHelpersConcern do
|
||||
let(:dummy_class) do
|
||||
Class.new do
|
||||
include Maintenance::StatementsHelpersConcern
|
||||
end
|
||||
end
|
||||
|
||||
let(:instance) { dummy_class.new }
|
||||
|
||||
describe '#with_statement_timeout' do
|
||||
it 'applies the statement timeout and raises an error for long-running queries' do
|
||||
expect {
|
||||
instance.with_statement_timeout('1ms') do
|
||||
# Cette requête devrait prendre plus de 1ms et donc déclencher un timeout
|
||||
ActiveRecord::Base.connection.execute("SELECT pg_sleep(1)")
|
||||
end
|
||||
}.to raise_error(ActiveRecord::StatementInvalid, /canceling statement due to statement timeout/i)
|
||||
end
|
||||
|
||||
it 'allows queries to complete within the timeout and returns the result' do
|
||||
result = instance.with_statement_timeout('1s') do
|
||||
ActiveRecord::Base.connection.execute("SELECT 42 AS answer").first['answer']
|
||||
end
|
||||
expect(result).to eq 42
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue