diff --git a/Gemfile b/Gemfile index b92fc4cbe..dbefab4c0 100644 --- a/Gemfile +++ b/Gemfile @@ -62,6 +62,7 @@ gem 'fog' gem 'fog-openstack' gem 'pg' +gem 'scenic' gem 'rgeo-geojson' gem 'leaflet-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 706c620e8..a42023ff3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -482,6 +482,9 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) + scenic (1.3.0) + activerecord (>= 4.0.0) + railties (>= 4.0.0) sdoc (0.4.1) json (~> 1.7, >= 1.7.7) rdoc (~> 4.0) @@ -635,6 +638,7 @@ DEPENDENCIES rubocop-checkstyle_formatter rubocop-rspec sass-rails (~> 5.0) + scenic sdoc (~> 0.4.0) selenium-webdriver sentry-raven @@ -654,4 +658,4 @@ DEPENDENCIES will_paginate-bootstrap BUNDLED WITH - 1.12.5 + 1.13.2 diff --git a/app/controllers/backoffice/dossiers_controller.rb b/app/controllers/backoffice/dossiers_controller.rb index 9c4895798..4de0b31a2 100644 --- a/app/controllers/backoffice/dossiers_controller.rb +++ b/app/controllers/backoffice/dossiers_controller.rb @@ -27,7 +27,12 @@ class Backoffice::DossiersController < Backoffice::DossiersListController def search @search_terms = params[:q] - @dossier = Dossier.search(current_gestionnaire, @search_terms) + + @dossier = Search.new( + gestionnaire: current_gestionnaire, + query: @search_terms, + page: params[:page] + ).results smartlisting_dossier @dossier, 'search' diff --git a/app/models/dossier.rb b/app/models/dossier.rb index e7b9599d0..c0cb20197 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -258,37 +258,6 @@ class Dossier < ActiveRecord::Base where(state: TERMINE, archived: false).order("updated_at #{order}") end - def self.search current_gestionnaire, terms - return [] if terms.blank? - - dossiers = Dossier.arel_table - users = User.arel_table - etablissements = Etablissement.arel_table - entreprises = Entreprise.arel_table - - composed_scope = self.joins('LEFT OUTER JOIN users ON users.id = dossiers.user_id') - .joins('LEFT OUTER JOIN entreprises ON entreprises.dossier_id = dossiers.id') - .joins('LEFT OUTER JOIN etablissements ON etablissements.dossier_id = dossiers.id') - - terms.split.each do |word| - query_string = "%#{word}%" - query_string_start_with = "#{word}%" - - composed_scope = composed_scope.where( - users[:email].matches(query_string).or\ - etablissements[:siret].matches(query_string_start_with).or\ - entreprises[:raison_sociale].matches(query_string).or\ - dossiers[:id].eq(word_is_an_integer word)) - end - - composed_scope = composed_scope.where( - dossiers[:id].eq_any(current_gestionnaire.dossiers.ids).and\ - dossiers[:state].does_not_match('draft').and\ - dossiers[:archived].eq(false)) - - composed_scope - end - def cerfa_available? procedure.cerfa_flag? && cerfa.size != 0 end diff --git a/app/models/search.rb b/app/models/search.rb new file mode 100644 index 000000000..6b4557677 --- /dev/null +++ b/app/models/search.rb @@ -0,0 +1,82 @@ +# See: +# - https://robots.thoughtbot.com/implementing-multi-table-full-text-search-with-postgres +# - http://calebthompson.io/talks/search.html +class Search < ActiveRecord::Base + # :nodoc: + # + # Englobs a search result (actually a collection of Search objects) so it acts + # like a collection of regular Dossier objects, which can be decorated, + # paginated, ... + class Results + include Enumerable + + def initialize(results) + @results = results + end + + def each + @results.each do |search| + yield search.dossier + end + end + + def method_missing(name, *args, &block) + @results.__send__(name, *args, &block) + end + + def decorate! + @results.each do |search| + search.dossier = search.dossier.decorate + end + end + end + + attr_accessor :gestionnaire + attr_accessor :query + attr_accessor :page + + belongs_to :dossier + + def results + unless @query.present? + return Search.none + end + + search_term = Search.connection.quote(to_tsquery) + + dossier_ids = @gestionnaire.dossiers + .select(:id) + .where(archived: false) + .where.not(state: "draft") + + q = Search + .select("DISTINCT(searches.dossier_id)") + .select("COALESCE(ts_rank(to_tsvector('french', searches.term::text), to_tsquery('french', #{search_term})), 0) AS rank") + .joins(:dossier) + .where(dossier_id: dossier_ids) + .where("to_tsvector('french', searches.term::text) @@ to_tsquery('french', #{search_term})") + .order("rank DESC") + .preload(:dossier) + + if @page.present? + q = q.paginate(page: @page) + end + + Results.new(q) + end + + #def self.refresh + # # TODO: could be executed concurrently + # # See https://github.com/thoughtbot/scenic#what-about-materialized-views + # Scenic.database.refresh_materialized_view(table_name, concurrently: false) + #end + + private + + def to_tsquery + @query.gsub(/['?\\:&|!]/, "") # drop disallowed characters + .split(/\s+/) # split words + .map { |x| "#{x}:*" } # enable prefix matching + .join(" & ") + end +end diff --git a/app/views/backoffice/dossiers/_list.html.haml b/app/views/backoffice/dossiers/_list.html.haml index 509b7a00d..7b2f7dbd5 100644 --- a/app/views/backoffice/dossiers/_list.html.haml +++ b/app/views/backoffice/dossiers/_list.html.haml @@ -45,4 +45,4 @@ - if smart_listing.empty? %h4.center - Aucun dossier \ No newline at end of file + Aucun dossier diff --git a/db/migrate/20161025150900_create_searches.rb b/db/migrate/20161025150900_create_searches.rb new file mode 100644 index 000000000..911948c7d --- /dev/null +++ b/db/migrate/20161025150900_create_searches.rb @@ -0,0 +1,27 @@ +class CreateSearches < ActiveRecord::Migration + def up + add_index :champs, :dossier_id + add_index :champs, :type_de_champ_id + add_index :drop_down_lists, :type_de_champ_id + add_index :etablissements, :dossier_id + add_index :entreprises, :dossier_id + add_index :france_connect_informations, :user_id + add_index :individuals, :dossier_id + add_index :pieces_justificatives, :dossier_id + add_index :rna_informations, :entreprise_id + create_view :searches #, materialized: true + end + + def down + remove_index :champs, :dossier_id + remove_index :champs, :type_de_champ_id + remove_index :drop_down_lists, :type_de_champ_id + remove_index :etablissements, :dossier_id + remove_index :entreprises, :dossier_id + remove_index :france_connect_informations, :user_id + remove_index :individuals, :dossier_id + remove_index :pieces_justificatives, :dossier_id + remove_index :rna_informations, :entreprise_id + drop_view :searches #, materialized: true + end +end diff --git a/db/migrate/20161102154835_update_searches_to_version_2.rb b/db/migrate/20161102154835_update_searches_to_version_2.rb new file mode 100644 index 000000000..55223b4cf --- /dev/null +++ b/db/migrate/20161102154835_update_searches_to_version_2.rb @@ -0,0 +1,9 @@ +class UpdateSearchesToVersion2 < ActiveRecord::Migration + def up + replace_view :searches, version: 2 + end + + def down + replace_view :searches, version: 1 + end +end diff --git a/db/schema.rb b/db/schema.rb index 17762a2b4..7ef1e735e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161011125345) do +ActiveRecord::Schema.define(version: 20161102154835) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -102,6 +102,9 @@ ActiveRecord::Schema.define(version: 20161011125345) do t.string "type" end + add_index "champs", ["dossier_id"], name: "index_champs_on_dossier_id", using: :btree + add_index "champs", ["type_de_champ_id"], name: "index_champs_on_type_de_champ_id", using: :btree + create_table "commentaires", force: :cascade do |t| t.string "email" t.datetime "created_at", null: false @@ -135,6 +138,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do t.integer "type_de_champ_id" end + add_index "drop_down_lists", ["type_de_champ_id"], name: "index_drop_down_lists_on_type_de_champ_id", using: :btree + create_table "entreprises", force: :cascade do |t| t.string "siren" t.integer "capital_social" @@ -151,6 +156,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do t.integer "dossier_id" end + add_index "entreprises", ["dossier_id"], name: "index_entreprises_on_dossier_id", using: :btree + create_table "etablissements", force: :cascade do |t| t.string "siret" t.boolean "siege_social" @@ -168,6 +175,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do t.integer "entreprise_id" end + add_index "etablissements", ["dossier_id"], name: "index_etablissements_on_dossier_id", using: :btree + create_table "exercices", force: :cascade do |t| t.string "ca" t.datetime "dateFinExercice" @@ -194,6 +203,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do t.string "email_france_connect" end + add_index "france_connect_informations", ["user_id"], name: "index_france_connect_informations_on_user_id", using: :btree + create_table "gestionnaires", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false @@ -221,6 +232,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do t.string "gender" end + add_index "individuals", ["dossier_id"], name: "index_individuals_on_dossier_id", using: :btree + create_table "invites", force: :cascade do |t| t.string "email" t.string "email_sender" @@ -255,6 +268,7 @@ ActiveRecord::Schema.define(version: 20161011125345) do t.string "content_secure_token" end + add_index "pieces_justificatives", ["dossier_id"], name: "index_pieces_justificatives_on_dossier_id", using: :btree add_index "pieces_justificatives", ["type_de_piece_justificative_id"], name: "index_pieces_justificatives_on_type_de_piece_justificative_id", using: :btree create_table "preference_list_dossiers", force: :cascade do |t| @@ -324,6 +338,8 @@ ActiveRecord::Schema.define(version: 20161011125345) do t.integer "entreprise_id" end + add_index "rna_informations", ["entreprise_id"], name: "index_rna_informations_on_entreprise_id", using: :btree + create_table "types_de_champ", force: :cascade do |t| t.string "libelle" t.string "type_champ" @@ -369,4 +385,21 @@ ActiveRecord::Schema.define(version: 20161011125345) do add_foreign_key "dossiers", "users" add_foreign_key "procedure_paths", "administrateurs" add_foreign_key "procedure_paths", "procedures" + + create_view :searches, sql_definition: <<-SQL + SELECT dossiers.id AS dossier_id, + (((((((((((((((((((((((((((((((((((((((((((((((((((((((COALESCE(users.email, ''::character varying))::text || ' '::text) || (COALESCE(france_connect_informations.given_name, ''::character varying))::text) || ' '::text) || (COALESCE(france_connect_informations.family_name, ''::character varying))::text) || ' '::text) || (COALESCE(cerfas.content, ''::character varying))::text) || ' '::text) || (COALESCE(champs.value, ''::character varying))::text) || ' '::text) || (COALESCE(drop_down_lists.value, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.siren, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.numero_tva_intracommunautaire, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.forme_juridique, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.forme_juridique_code, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.nom_commercial, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.raison_sociale, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.siret_siege_social, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.nom, ''::character varying))::text) || ' '::text) || (COALESCE(entreprises.prenom, ''::character varying))::text) || ' '::text) || (COALESCE(rna_informations.association_id, ''::character varying))::text) || ' '::text) || (COALESCE(rna_informations.titre, ''::character varying))::text) || ' '::text) || COALESCE(rna_informations.objet, ''::text)) || ' '::text) || (COALESCE(etablissements.siret, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.naf, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.libelle_naf, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.adresse, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.code_postal, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.localite, ''::character varying))::text) || ' '::text) || (COALESCE(etablissements.code_insee_localite, ''::character varying))::text) || ' '::text) || (COALESCE(individuals.nom, ''::character varying))::text) || ' '::text) || (COALESCE(individuals.prenom, ''::character varying))::text) || ' '::text) || (COALESCE(pieces_justificatives.content, ''::character varying))::text) AS term + FROM ((((((((((dossiers + JOIN users ON ((users.id = dossiers.user_id))) + LEFT JOIN france_connect_informations ON ((france_connect_informations.user_id = dossiers.user_id))) + LEFT JOIN cerfas ON ((cerfas.dossier_id = dossiers.id))) + LEFT JOIN champs ON ((champs.dossier_id = dossiers.id))) + LEFT JOIN drop_down_lists ON ((drop_down_lists.type_de_champ_id = champs.type_de_champ_id))) + LEFT JOIN entreprises ON ((entreprises.dossier_id = dossiers.id))) + LEFT JOIN rna_informations ON ((rna_informations.entreprise_id = entreprises.id))) + LEFT JOIN etablissements ON ((etablissements.dossier_id = dossiers.id))) + LEFT JOIN individuals ON ((individuals.dossier_id = dossiers.id))) + LEFT JOIN pieces_justificatives ON ((pieces_justificatives.dossier_id = dossiers.id))); + SQL + end diff --git a/db/views/searches_v01.sql b/db/views/searches_v01.sql new file mode 100644 index 000000000..e5f2cfd71 --- /dev/null +++ b/db/views/searches_v01.sql @@ -0,0 +1,59 @@ +-- this version allows to search for a single term within many tables, +-- but behaves badly with multiple terms scattered in multiple tables. + +SELECT dossiers.id AS dossier_id, + dossiers.id::text || ' ' || + COALESCE(users.email, '') AS term + FROM dossiers + INNER JOIN users ON users.id = dossiers.user_id + +UNION SELECT cerfas.dossier_id, + COALESCE(cerfas.content, '') AS term + FROM cerfas + +UNION SELECT champs.dossier_id, + COALESCE(champs.value, '') || ' ' || + COALESCE(drop_down_lists.value, '') AS term + FROM champs + INNER JOIN drop_down_lists ON drop_down_lists.type_de_champ_id = champs.type_de_champ_id + +UNION SELECT entreprises.dossier_id, + COALESCE(entreprises.siren, '') || ' ' || + COALESCE(entreprises.numero_tva_intracommunautaire, '') || ' ' || + COALESCE(entreprises.forme_juridique, '') || ' ' || + COALESCE(entreprises.forme_juridique_code, '') || ' ' || + COALESCE(entreprises.nom_commercial, '') || ' ' || + COALESCE(entreprises.raison_sociale, '') || ' ' || + COALESCE(entreprises.siret_siege_social, '') || ' ' || + COALESCE(entreprises.nom, '') || ' ' || + COALESCE(entreprises.prenom, '') || ' ' || + COALESCE(rna_informations.association_id, '') || ' ' || + COALESCE(rna_informations.titre, '') || ' ' || + COALESCE(rna_informations.objet, '') AS term + FROM entreprises + LEFT JOIN rna_informations ON rna_informations.entreprise_id = entreprises.id + +UNION SELECT etablissements.dossier_id, + COALESCE(etablissements.siret, '') || ' ' || + COALESCE(etablissements.naf, '') || ' ' || + COALESCE(etablissements.libelle_naf, '') || ' ' || + COALESCE(etablissements.adresse, '') || ' ' || + COALESCE(etablissements.code_postal, '') || ' ' || + COALESCE(etablissements.localite, '') || ' ' || + COALESCE(etablissements.code_insee_localite, '') AS term + FROM etablissements + +UNION SELECT individuals.dossier_id, + COALESCE(individuals.nom, '') || ' ' || + COALESCE(individuals.prenom, '') AS term + FROM individuals + +UNION SELECT pieces_justificatives.dossier_id, + COALESCE(pieces_justificatives.content, '') AS term + FROM pieces_justificatives + +UNION SELECT dossiers.id, + COALESCE(france_connect_informations.given_name, '') || ' ' || + COALESCE(france_connect_informations.family_name, '') AS term + FROM france_connect_informations + INNER JOIN dossiers ON dossiers.user_id = france_connect_informations.user_id diff --git a/db/views/searches_v02.sql b/db/views/searches_v02.sql new file mode 100644 index 000000000..8a168f465 --- /dev/null +++ b/db/views/searches_v02.sql @@ -0,0 +1,43 @@ +-- this version merges all possible search terms together, complicating the +-- view, but enables searching for multiple terms from multiple tables at once. + +SELECT dossiers.id AS dossier_id, + COALESCE(users.email, '') || ' ' || + COALESCE(france_connect_informations.given_name, '') || ' ' || + COALESCE(france_connect_informations.family_name, '') || ' ' || + COALESCE(cerfas.content, '') || ' ' || + COALESCE(champs.value, '') || ' ' || + COALESCE(drop_down_lists.value, '') || ' ' || + COALESCE(entreprises.siren, '') || ' ' || + COALESCE(entreprises.numero_tva_intracommunautaire, '') || ' ' || + COALESCE(entreprises.forme_juridique, '') || ' ' || + COALESCE(entreprises.forme_juridique_code, '') || ' ' || + COALESCE(entreprises.nom_commercial, '') || ' ' || + COALESCE(entreprises.raison_sociale, '') || ' ' || + COALESCE(entreprises.siret_siege_social, '') || ' ' || + COALESCE(entreprises.nom, '') || ' ' || + COALESCE(entreprises.prenom, '') || ' ' || + COALESCE(rna_informations.association_id, '') || ' ' || + COALESCE(rna_informations.titre, '') || ' ' || + COALESCE(rna_informations.objet, '') || ' ' || + COALESCE(etablissements.siret, '') || ' ' || + COALESCE(etablissements.naf, '') || ' ' || + COALESCE(etablissements.libelle_naf, '') || ' ' || + COALESCE(etablissements.adresse, '') || ' ' || + COALESCE(etablissements.code_postal, '') || ' ' || + COALESCE(etablissements.localite, '') || ' ' || + COALESCE(etablissements.code_insee_localite, '') || ' ' || + COALESCE(individuals.nom, '') || ' ' || + COALESCE(individuals.prenom, '') || ' ' || + COALESCE(pieces_justificatives.content, '') AS term +FROM dossiers +INNER JOIN users ON users.id = dossiers.user_id +LEFT JOIN france_connect_informations ON france_connect_informations.user_id = dossiers.user_id +LEFT JOIN cerfas ON cerfas.dossier_id = dossiers.id +LEFT JOIN champs ON champs.dossier_id = dossiers.id +LEFT JOIN drop_down_lists ON drop_down_lists.type_de_champ_id = champs.type_de_champ_id +LEFT JOIN entreprises ON entreprises.dossier_id = dossiers.id +LEFT JOIN rna_informations ON rna_informations.entreprise_id = entreprises.id +LEFT JOIN etablissements ON etablissements.dossier_id = dossiers.id +LEFT JOIN individuals ON individuals.dossier_id = dossiers.id +LEFT JOIN pieces_justificatives ON pieces_justificatives.dossier_id = dossiers.id diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 5fb127019..b4ee3c005 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -556,87 +556,6 @@ describe Dossier do end end - describe '.search' do - subject { liste_dossiers } - - let(:liste_dossiers) { described_class.search(gestionnaire_1, terms) } - # let(:dossier) { described_class.search(gestionnaire_1, terms)[1] } - - let(:administrateur_1) { create(:administrateur) } - let(:administrateur_2) { create(:administrateur) } - - let(:gestionnaire_1) { create(:gestionnaire, administrateurs: [administrateur_1]) } - let(:gestionnaire_2) { create(:gestionnaire, administrateurs: [administrateur_2]) } - - before do - create :assign_to, gestionnaire: gestionnaire_1, procedure: procedure_1 - create :assign_to, gestionnaire: gestionnaire_2, procedure: procedure_2 - end - - let(:procedure_1) { create(:procedure, administrateur: administrateur_1) } - let(:procedure_2) { create(:procedure, administrateur: administrateur_2) } - - let!(:dossier_0) { create(:dossier, state: 'draft', procedure: procedure_1, user: create(:user, email: 'brouillon@clap.fr')) } - let!(:dossier_1) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'contact@test.com')) } - let!(:dossier_2) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'plop@gmail.com')) } - let!(:dossier_3) { create(:dossier, state: 'initiated', procedure: procedure_2, user: create(:user, email: 'peace@clap.fr')) } - let!(:dossier_archived) { create(:dossier, state: 'initiated', procedure: procedure_1, archived: true, user: create(:user, email: 'brouillonArchived@clap.fr')) } - - let!(:etablissement_1) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'OCTO Academy', dossier: dossier_1), dossier: dossier_1, siret: '41636169600051') } - let!(:etablissement_2) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'Plop octo', dossier: dossier_2), dossier: dossier_2, siret: '41816602300012') } - let!(:etablissement_3) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'OCTO Technology', dossier: dossier_3), dossier: dossier_3, siret: '41816609600051') } - - describe 'search is empty' do - let(:terms) { '' } - - it { expect(subject.size).to eq(0) } - end - - describe 'search draft file' do - let(:terms) { 'brouillon' } - - it { expect(subject.size).to eq(0) } - it { expect(subject.class).to eq Dossier::ActiveRecord_Relation } - end - - describe 'search on contact email' do - let(:terms) { 'clap' } - - it { expect(subject.size).to eq(0) } - end - - describe 'search on ID dossier' do - let(:terms) { "#{dossier_2.id}" } - - it { expect(subject.size).to eq(1) } - end - - describe 'search on SIRET' do - context 'when is part of SIRET' do - let(:terms) { '4181' } - - it { expect(subject.size).to eq(1) } - end - - context 'when is a complet SIRET' do - let(:terms) { '41816602300012' } - - it { expect(subject.size).to eq(1) } - end - end - - describe 'search on raison social' do - let(:terms) { 'OCTO' } - - it { expect(subject.size).to eq(2) } - end - - describe 'search on multiple fields' do - let(:terms) { 'octo test' } - - it { expect(subject.size).to eq(1) } - end - end end describe '#cerfa_available?' do diff --git a/spec/models/search_spec.rb b/spec/models/search_spec.rb new file mode 100644 index 000000000..4e7d20ec2 --- /dev/null +++ b/spec/models/search_spec.rb @@ -0,0 +1,79 @@ +require 'rails_helper' + +describe Search do + describe '.results' do + subject { liste_dossiers } + + let(:liste_dossiers) do + described_class.new(gestionnaire: gestionnaire_1, query: terms).results + end + + let(:administrateur_1) { create(:administrateur) } + let(:administrateur_2) { create(:administrateur) } + + let(:gestionnaire_1) { create(:gestionnaire, administrateurs: [administrateur_1]) } + let(:gestionnaire_2) { create(:gestionnaire, administrateurs: [administrateur_2]) } + + before do + create :assign_to, gestionnaire: gestionnaire_1, procedure: procedure_1 + create :assign_to, gestionnaire: gestionnaire_2, procedure: procedure_2 + end + + let(:procedure_1) { create(:procedure, administrateur: administrateur_1) } + let(:procedure_2) { create(:procedure, administrateur: administrateur_2) } + + let!(:dossier_0) { create(:dossier, state: 'draft', procedure: procedure_1, user: create(:user, email: 'brouillon@clap.fr')) } + let!(:dossier_1) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'contact@test.com')) } + let!(:dossier_2) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'plop@gmail.com')) } + let!(:dossier_3) { create(:dossier, state: 'initiated', procedure: procedure_2, user: create(:user, email: 'peace@clap.fr')) } + let!(:dossier_archived) { create(:dossier, state: 'initiated', procedure: procedure_1, archived: true, user: create(:user, email: 'brouillonArchived@clap.fr')) } + + let!(:etablissement_1) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'OCTO Academy', dossier: dossier_1), dossier: dossier_1, siret: '41636169600051') } + let!(:etablissement_2) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'Plop octo', dossier: dossier_2), dossier: dossier_2, siret: '41816602300012') } + let!(:etablissement_3) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'OCTO Technology', dossier: dossier_3), dossier: dossier_3, siret: '41816609600051') } + + describe 'search is empty' do + let(:terms) { '' } + + it { expect(subject.size).to eq(0) } + end + + describe 'search draft file' do + let(:terms) { 'brouillon' } + + it { expect(subject.size).to eq(0) } + end + + describe 'search on contact email' do + let(:terms) { 'clap' } + + it { expect(subject.size).to eq(0) } + end + + describe 'search on SIRET' do + context 'when is part of SIRET' do + let(:terms) { '4181' } + + it { expect(subject.size).to eq(1) } + end + + context 'when is a complet SIRET' do + let(:terms) { '41816602300012' } + + it { expect(subject.size).to eq(1) } + end + end + + describe 'search on raison social' do + let(:terms) { 'OCTO' } + + it { expect(subject.size).to eq(2) } + end + + describe 'search on multiple fields' do + let(:terms) { 'octo plop' } + + it { expect(subject.size).to eq(1) } + end + end +end