demarches-normaliennes/spec/controllers/api/v2/graphql_controller_spec.rb
2022-11-25 16:27:11 +01:00

2007 lines
62 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

describe API::V2::GraphqlController do
let(:admin) { create(:administrateur) }
let(:token) { admin.renew_api_token }
let(:legacy_token) { APIToken.new(token).token }
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, administrateurs: [admin]) }
let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
let(:dossier1) { create(:dossier, :en_construction, :with_individual, procedure: procedure, en_construction_at: 1.day.ago) }
let(:dossier2) { create(:dossier, :en_construction, :with_individual, :archived, procedure: procedure, en_construction_at: 3.days.ago) }
let(:dossiers) { [dossier] }
let(:instructeur) { create(:instructeur, followed_dossiers: dossiers) }
def compute_checksum_in_chunks(io)
Digest::MD5.new.tap do |checksum|
while (chunk = io.read(5.megabytes))
checksum << chunk
end
io.rewind
end.base64digest
end
let(:file) { fixture_file_upload('spec/fixtures/files/logo_test_procedure.png', 'image/png') }
let(:blob_info) do
{
filename: file.original_filename,
byte_size: file.size,
checksum: compute_checksum_in_chunks(file),
content_type: file.content_type,
# we don't want to run virus scanner on this file
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE }
}
end
let(:blob) do
blob = ActiveStorage::Blob.create_before_direct_upload!(**blob_info)
blob.upload(file)
blob
end
before do
instructeur.assign_to_procedure(procedure)
end
let(:query) do
"{
demarche(number: #{procedure.id}) {
id
number
title
description
state
dateCreation
dateDerniereModification
dateFermeture
groupeInstructeurs {
label
instructeurs {
email
}
}
revisions {
id
}
draftRevision {
id
}
publishedRevision {
id
champDescriptors {
type
}
}
service {
nom
typeOrganisme
organisme
}
champDescriptors {
id
type
label
description
required
champDescriptors {
id
type
}
options
}
dossiers {
nodes {
id
}
}
}
}"
end
let(:variables) { {} }
let(:operation_name) { nil }
let(:query_id) { nil }
let(:body) { JSON.parse(subject.body, symbolize_names: true) }
let(:gql_data) { body[:data] }
let(:gql_errors) { body[:errors] }
subject { post :execute, params: { query: query, variables: variables, operationName: operation_name, queryId: query_id }.compact, as: :json }
context "when authenticated with legacy token" do
let(:authorization_header) { ActionController::HttpAuthentication::Token.encode_credentials(legacy_token) }
before do
request.env['HTTP_AUTHORIZATION'] = authorization_header
end
it "returns the demarche" do
expect(gql_errors).to eq(nil)
expect(gql_data[:demarche][:id]).to eq(procedure.to_typed_id)
end
end
context "when authenticated" do
let(:authorization_header) { ActionController::HttpAuthentication::Token.encode_credentials(token) }
before do
request.env['HTTP_AUTHORIZATION'] = authorization_header
end
describe "token authentication" do
it {
expect(gql_errors).to eq(nil)
expect(gql_data).not_to be_nil
}
context "when the token is invalid" do
before do
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Token.encode_credentials('invalid')
end
it {
expect(gql_errors.first[:message]).to eq("An object of type Demarche was hidden due to permissions")
}
end
context "when the token is revoked" do
before do
admin.update(encrypted_token: nil)
end
it {
expect(token).not_to be_nil
expect(gql_errors.first[:message]).to eq("An object of type Demarche was hidden due to permissions")
}
end
end
describe "demarche" do
describe "query a demarche" do
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, :with_all_champs, :with_all_annotations, administrateurs: [admin]) }
it "returns the demarche" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(demarche: {
id: procedure.to_typed_id,
number: procedure.id,
title: procedure.libelle,
description: procedure.description,
state: 'publiee',
dateFermeture: nil,
dateCreation: procedure.created_at.iso8601,
dateDerniereModification: procedure.updated_at.iso8601,
groupeInstructeurs: [
{
instructeurs: [{ email: instructeur.email }],
label: "défaut"
}
],
revisions: procedure.revisions.map { |revision| { id: revision.to_typed_id } },
draftRevision: { id: procedure.draft_revision.to_typed_id },
publishedRevision: {
id: procedure.published_revision.to_typed_id,
champDescriptors: procedure.published_revision.types_de_champ_public.map do |tdc|
{
type: tdc.type_champ
}
end
},
service: {
nom: procedure.service.nom,
typeOrganisme: procedure.service.type_organisme,
organisme: procedure.service.organisme
},
champDescriptors: procedure.active_revision.types_de_champ_public.map do |tdc|
{
id: tdc.to_typed_id,
label: tdc.libelle,
type: tdc.type_champ,
description: tdc.description,
required: tdc.mandatory?,
champDescriptors: tdc.repetition? ? procedure.active_revision.children_of(tdc.reload).map { |tdc| { id: tdc.to_typed_id, type: tdc.type_champ } } : nil,
options: tdc.drop_down_list? ? tdc.drop_down_list_options.reject(&:empty?) : nil
}
end,
dossiers: {
nodes: dossiers.map { |dossier| { id: dossier.to_typed_id } }
}
})
end
end
describe "filter dossiers" do
let(:dossiers) { [dossier, dossier1, dossier2] }
let(:query) do
"{
demarche(number: #{procedure.id}) {
id
number
dossiers(createdSince: \"#{2.days.ago.iso8601}\") {
nodes {
id
}
}
}
}"
end
it "should be returned" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(demarche: {
id: procedure.to_typed_id,
number: procedure.id,
dossiers: {
nodes: [{ id: dossier1.to_typed_id }, { id: dossier.to_typed_id }]
}
})
end
end
describe "filter archived dossiers" do
let(:dossiers) { [dossier, dossier1, dossier2] }
let(:query) do
"{
demarche(number: #{procedure.id}) {
id
number
dossiers(archived: #{archived_filter}) {
nodes {
id
}
}
}
}"
end
context 'with archived=true' do
let(:archived_filter) { 'true' }
it 'returns only archived dossiers' do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(demarche: {
id: procedure.to_typed_id,
number: procedure.id,
dossiers: {
nodes: [{ id: dossier2.to_typed_id }]
}
})
end
end
context 'with archived=false' do
let(:archived_filter) { 'false' }
it 'returns only non-archived dossiers' do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(demarche: {
id: procedure.to_typed_id,
number: procedure.id,
dossiers: {
nodes: [{ id: dossier1.to_typed_id }, { id: dossier.to_typed_id }]
}
})
end
end
end
describe "filter by minRevision" do
let(:query) do
"{
demarche(number: #{procedure.id}) {
id
number
dossiers(minRevision: \"#{procedure.revisions.first.to_typed_id}\") {
nodes {
id
}
}
}
}"
end
it "should be returned" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(demarche: {
id: procedure.to_typed_id,
number: procedure.id,
dossiers: {
nodes: procedure.dossiers.order(:created_at).map do |dossier|
{ id: dossier.to_typed_id }
end
}
})
end
end
describe "filter by maxRevision" do
let(:query) do
"{
demarche(number: #{procedure.id}) {
id
number
dossiers(maxRevision: \"#{procedure.revisions.last.to_typed_id}\") {
nodes {
id
}
}
}
}"
end
it "should be returned" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(demarche: {
id: procedure.to_typed_id,
number: procedure.id,
dossiers: {
nodes: procedure.dossiers.order(:created_at).map do |dossier|
{ id: dossier.to_typed_id }
end
}
})
end
end
end
describe "dossier" do
let(:dossier) do
dossier = create(:dossier,
:en_construction,
:with_populated_champs,
:with_populated_annotations,
:with_individual,
procedure: procedure)
create(:commentaire, :with_file, dossier: dossier, email: 'test@test.com')
dossier
end
context "for individual" do
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, :with_all_champs, :with_all_annotations, administrateurs: [admin]) }
let(:query) do
"{
dossier(number: #{dossier.id}) {
id
number
state
dateDerniereModification
datePassageEnConstruction
datePassageEnInstruction
dateTraitement
dateDepot
motivation
motivationAttachment {
url
}
demarche {
number
title
state
}
usager {
id
email
}
demandeur {
id
... on PersonnePhysique {
nom
prenom
civilite
dateDeNaissance
}
}
instructeurs {
id
email
}
groupeInstructeur {
id
number
label
}
revision {
id
champDescriptors {
type
}
}
messages {
email
body
attachment {
filename
checksum
byteSize
contentType
}
}
avis {
expert {
email
}
question
reponse
dateQuestion
dateReponse
attachment {
url
filename
}
}
champs {
id
label
stringValue
}
}
}"
end
it "should be returned" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossier: {
id: dossier.to_typed_id,
number: dossier.id,
state: 'en_construction',
dateDerniereModification: dossier.updated_at.iso8601,
datePassageEnConstruction: dossier.en_construction_at.iso8601,
dateDepot: dossier.depose_at.iso8601,
datePassageEnInstruction: nil,
dateTraitement: nil,
motivation: nil,
motivationAttachment: nil,
demarche: {
number: dossier.procedure.id,
title: dossier.procedure.libelle,
state: 'publiee'
},
usager: {
id: dossier.user.to_typed_id,
email: dossier.user.email
},
instructeurs: [
{
id: instructeur.to_typed_id,
email: instructeur.email
}
],
groupeInstructeur: {
id: dossier.groupe_instructeur.to_typed_id,
number: dossier.groupe_instructeur.id,
label: dossier.groupe_instructeur.label
},
revision: {
id: dossier.revision.to_typed_id,
champDescriptors: dossier.types_de_champ.map do |tdc|
{
type: tdc.type_champ
}
end
},
demandeur: {
id: dossier.individual.to_typed_id,
nom: dossier.individual.nom,
prenom: dossier.individual.prenom,
civilite: 'M',
dateDeNaissance: '1991-11-01'
},
messages: dossier.commentaires.map do |commentaire|
{
body: commentaire.body,
attachment: {
filename: commentaire.piece_jointe.filename.to_s,
contentType: commentaire.piece_jointe.content_type,
checksum: commentaire.piece_jointe.checksum,
byteSize: commentaire.piece_jointe.byte_size
},
email: commentaire.email
}
end,
avis: [],
champs: dossier.champs_public.map do |champ|
{
id: champ.to_typed_id,
label: champ.libelle,
stringValue: champ.for_api_v2
}
end
})
expect(gql_data[:dossier][:champs][0][:id]).to eq(dossier.champs_public[0].type_de_champ.to_typed_id)
end
end
context "for entreprise" do
let(:procedure_for_entreprise) { create(:procedure, :published, administrateurs: [admin]) }
let(:dossier) { create(:dossier, :en_construction, :with_entreprise, procedure: procedure_for_entreprise) }
let(:query) do
"{
dossier(number: #{dossier.id}) {
id
number
usager {
id
email
}
demandeur {
id
... on PersonneMorale {
siret
siegeSocial
numeroVoie
typeVoie
entreprise {
siren
dateCreation
capitalSocial
codeEffectifEntreprise
numeroTvaIntracommunautaire
}
}
}
}
}"
end
context "in the nominal case" do
it "should be returned" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossier: {
id: dossier.to_typed_id,
number: dossier.id,
usager: {
id: dossier.user.to_typed_id,
email: dossier.user.email
},
demandeur: {
id: dossier.etablissement.to_typed_id,
siret: dossier.etablissement.siret,
siegeSocial: dossier.etablissement.siege_social,
numeroVoie: dossier.etablissement.numero_voie.to_s,
typeVoie: dossier.etablissement.type_voie.to_s,
entreprise: {
siren: dossier.etablissement.entreprise_siren,
dateCreation: dossier.etablissement.entreprise_date_creation.iso8601,
capitalSocial: dossier.etablissement.entreprise_capital_social.to_s,
codeEffectifEntreprise: dossier.etablissement.entreprise_code_effectif_entreprise.to_s,
numeroTvaIntracommunautaire: dossier.etablissement.entreprise_numero_tva_intracommunautaire
}
}
})
end
end
context "with links" do
let(:dossier) { create(:dossier, :accepte, :with_attestation, procedure: procedure) }
let(:query) do
"{
dossier(number: #{dossier.id}) {
id
number
pdf {
url
}
geojson {
url
}
attestation {
url
}
}
}"
end
it "urls should be returned" do
expect(gql_errors).to eq(nil)
expect(gql_data[:dossier][:pdf][:url]).not_to be_nil
expect(gql_data[:dossier][:geojson][:url]).not_to be_nil
expect(gql_data[:dossier][:attestation][:url]).not_to be_nil
end
end
context "when there are missing data" do
before do
dossier.etablissement.update!(entreprise_code_effectif_entreprise: nil, entreprise_capital_social: nil,
numero_voie: nil, type_voie: nil, entreprise_numero_tva_intracommunautaire: nil)
end
it "should be returned" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossier: {
id: dossier.to_typed_id,
number: dossier.id,
usager: {
id: dossier.user.to_typed_id,
email: dossier.user.email
},
demandeur: {
id: dossier.etablissement.to_typed_id,
siret: dossier.etablissement.siret,
siegeSocial: dossier.etablissement.siege_social,
numeroVoie: nil,
typeVoie: nil,
entreprise: {
siren: dossier.etablissement.entreprise_siren,
dateCreation: dossier.etablissement.entreprise_date_creation.iso8601,
capitalSocial: '-1',
codeEffectifEntreprise: nil,
numeroTvaIntracommunautaire: nil
}
}
})
end
end
end
context "champs" do
let(:procedure) { create(:procedure, :published, :for_individual, administrateurs: [admin], types_de_champ_public: [{ type: :date }, { type: :datetime }]) }
let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
let(:champ_date) { dossier.champs_public.first }
let(:champ_datetime) { dossier.champs_public.second }
before do
champ_date.update(value: '2019-07-10')
champ_datetime.update(value: '15/09/1962 15:35')
end
context "with Date" do
let(:query) do
"{
dossier(number: #{dossier.id}) {
champs {
id
label
... on DateChamp {
value
}
}
}
}"
end
it "should be returned" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossier: {
champs: [
{
id: champ_date.to_typed_id,
label: champ_date.libelle,
value: '2019-07-10T00:00:00+02:00'
},
{
id: champ_datetime.to_typed_id,
label: champ_datetime.libelle,
value: '1962-09-15T15:35:00+01:00'
}
]
})
end
end
context "with Datetime" do
let(:query) do
"{
dossier(number: #{dossier.id}) {
champs {
id
label
... on DateChamp {
value
date
}
... on DatetimeChamp {
datetime
}
}
}
}"
end
it "should be returned" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossier: {
champs: [
{
id: champ_date.to_typed_id,
label: champ_date.libelle,
value: '2019-07-10T00:00:00+02:00',
date: '2019-07-10'
},
{
id: champ_datetime.to_typed_id,
label: champ_datetime.libelle,
datetime: '1962-09-15T15:35:00+01:00'
}
]
})
end
end
end
end
describe "deletedDossiers" do
let(:query) do
"{
demarche(number: #{procedure.id}) {
deletedDossiers {
nodes {
id
number
state
reason
dateSupression
}
}
}
}"
end
let(:deleted_dossier) { create(:deleted_dossier, procedure: procedure) }
before { deleted_dossier }
it "should be returned" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(demarche: {
deletedDossiers: {
nodes: [
{
id: deleted_dossier.to_typed_id,
number: deleted_dossier.dossier_id,
state: deleted_dossier.state,
reason: deleted_dossier.reason,
dateSupression: deleted_dossier.deleted_at.iso8601
}
]
}
})
end
end
describe "champ" do
let(:champ) { create(:champ_piece_justificative, dossier: dossier) }
let(:byte_size) { 2712286911 }
context "byteSize" do
let(:query) do
"{
dossier(number: #{dossier.id}) {
champs(id: \"#{champ.to_typed_id}\") {
... on PieceJustificativeChamp {
file { byteSize }
}
}
}
}"
end
it {
expect(gql_errors).to be_nil
expect(gql_data).to eq(dossier: { champs: [{ file: { byteSize: 4 } }] })
}
end
context "when the file is really big" do
before do
champ.piece_justificative_file.blob.update(byte_size: byte_size)
end
context "byteSize" do
let(:query) do
"{
dossier(number: #{dossier.id}) {
champs(id: \"#{champ.to_typed_id}\") {
... on PieceJustificativeChamp {
file { byteSize }
}
}
}
}"
end
it {
expect(gql_errors).not_to be_nil
}
end
context "byteSizeBigInt" do
let(:query) do
"{
dossier(number: #{dossier.id}) {
champs(id: \"#{champ.to_typed_id}\") {
... on PieceJustificativeChamp {
file { byteSizeBigInt }
}
}
}
}"
end
it {
expect(gql_errors).to be_nil
expect(gql_data).to eq(dossier: { champs: [{ file: { byteSizeBigInt: '2712286911' } }] })
}
end
end
end
describe "groupeInstructeur" do
let(:groupe_instructeur) { procedure.groupe_instructeurs.first }
let(:query) do
"{
groupeInstructeur(number: #{groupe_instructeur.id}) {
id
number
label
dossiers {
nodes {
id
}
}
}
}"
end
it "should be returned" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(groupeInstructeur: {
id: groupe_instructeur.to_typed_id,
number: groupe_instructeur.id,
label: groupe_instructeur.label,
dossiers: {
nodes: dossiers.map { |dossier| { id: dossier.to_typed_id } }
}
})
end
end
describe 'stored queries' do
let(:procedure) { create(:procedure, :published, :for_individual, administrateurs: [admin]) }
let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
let(:query_id) { 'ds-query-v2' }
context 'not found operation id' do
let(:query_id) { 'ds-query-v0' }
it {
expect(gql_errors.first[:message]).to eq('No query with id "ds-query-v0"')
}
end
context 'not found operation name' do
let(:operation_name) { 'getStuff' }
it {
expect(gql_errors.first[:message]).to eq('No operation named "getStuff"')
}
end
context 'getDossier' do
let(:variables) { { dossierNumber: dossier.id } }
let(:operation_name) { 'getDossier' }
it {
expect(gql_errors).to be_nil
expect(gql_data[:dossier][:id]).to eq(dossier.to_typed_id)
}
end
context 'getDemarche' do
let(:variables) { { demarcheNumber: procedure.id } }
let(:operation_name) { 'getDemarche' }
before { dossier }
it {
expect(gql_errors).to be_nil
expect(gql_data[:demarche][:id]).to eq(procedure.to_typed_id)
expect(gql_data[:demarche][:dossiers]).to be_nil
}
context 'include Dossiers' do
let(:variables) { { demarcheNumber: procedure.id, includeDossiers: true } }
it {
expect(gql_errors).to be_nil
expect(gql_data[:demarche][:id]).to eq(procedure.to_typed_id)
expect(gql_data[:demarche][:dossiers][:nodes].size).to eq(1)
}
end
end
context 'getGroupeInstructeur' do
let(:groupe_instructeur) { procedure.groupe_instructeurs.first }
let(:variables) { { groupeInstructeurNumber: groupe_instructeur.id } }
let(:operation_name) { 'getGroupeInstructeur' }
before { dossier }
it {
expect(gql_errors).to be_nil
expect(gql_data[:groupeInstructeur][:id]).to eq(groupe_instructeur.to_typed_id)
expect(gql_data[:groupeInstructeur][:dossiers]).to be_nil
}
context 'include Dossiers' do
let(:variables) { { groupeInstructeurNumber: groupe_instructeur.id, includeDossiers: true } }
it {
expect(gql_errors).to be_nil
expect(gql_data[:groupeInstructeur][:id]).to eq(groupe_instructeur.to_typed_id)
expect(gql_data[:groupeInstructeur][:dossiers][:nodes].size).to eq(1)
}
end
end
context 'getDemarcheDescriptor' do
let(:operation_name) { 'getDemarcheDescriptor' }
context 'find by number' do
let(:variables) { { demarche: { number: procedure.id } } }
it {
expect(gql_errors).to be_nil
expect(gql_data[:demarcheDescriptor][:id]).to eq(procedure.to_typed_id)
}
end
context 'find by id' do
let(:variables) { { demarche: { id: procedure.to_typed_id } } }
it {
expect(gql_errors).to be_nil
expect(gql_data[:demarcheDescriptor][:id]).to eq(procedure.to_typed_id)
}
end
context 'not opendata' do
let(:variables) { { demarche: { id: procedure.to_typed_id } } }
before { procedure.update(opendata: false) }
it {
expect(gql_errors).to be_nil
expect(gql_data[:demarcheDescriptor][:id]).to eq(procedure.to_typed_id)
}
end
context 'without authorization token' do
let(:authorization_header) { nil }
context 'opendata' do
let(:variables) { { demarche: { id: procedure.to_typed_id } } }
it {
expect(gql_errors).to be_nil
expect(gql_data[:demarcheDescriptor][:id]).to eq(procedure.to_typed_id)
}
end
context 'not opendata' do
let(:variables) { { demarche: { id: procedure.to_typed_id } } }
before { procedure.update(opendata: false) }
it {
expect(gql_errors).not_to be_nil
expect(gql_errors.first[:message]).to eq('An object of type DemarcheDescriptor was hidden due to permissions')
}
end
end
end
context 'mutation' do
let(:query_id) { 'ds-mutation-v2' }
context 'not found operation name' do
let(:operation_name) { 'dossierStuff' }
it {
expect(gql_errors.first[:message]).to eq('No operation named "dossierStuff"')
}
end
context 'dossierArchiver' do
let(:dossier) { create(:dossier, :refuse, :with_individual, procedure: procedure) }
let(:variables) { { input: { dossierId: dossier.to_typed_id, instructeurId: instructeur.to_typed_id } } }
let(:operation_name) { 'dossierArchiver' }
it {
expect(gql_errors).to be_nil
expect(gql_data[:dossierArchiver][:errors]).to be_nil
expect(gql_data[:dossierArchiver][:dossier][:id]).to eq(dossier.to_typed_id)
expect(gql_data[:dossierArchiver][:dossier][:archived]).to be_truthy
}
end
context 'dossierPasserEnInstruction' do
let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
let(:variables) { { input: { dossierId: dossier.to_typed_id, instructeurId: instructeur.to_typed_id } } }
let(:operation_name) { 'dossierPasserEnInstruction' }
it {
expect(gql_errors).to be_nil
expect(gql_data[:dossierPasserEnInstruction][:errors]).to be_nil
expect(gql_data[:dossierPasserEnInstruction][:dossier][:id]).to eq(dossier.to_typed_id)
expect(gql_data[:dossierPasserEnInstruction][:dossier][:state]).to eq('en_instruction')
}
end
context 'dossierRepasserEnConstruction' do
let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) }
let(:variables) { { input: { dossierId: dossier.to_typed_id, instructeurId: instructeur.to_typed_id } } }
let(:operation_name) { 'dossierRepasserEnConstruction' }
it {
expect(gql_errors).to be_nil
expect(gql_data[:dossierRepasserEnConstruction][:errors]).to be_nil
expect(gql_data[:dossierRepasserEnConstruction][:dossier][:id]).to eq(dossier.to_typed_id)
expect(gql_data[:dossierRepasserEnConstruction][:dossier][:state]).to eq('en_construction')
}
end
context 'dossierRepasserEnInstruction' do
let(:dossier) { create(:dossier, :refuse, :with_individual, procedure: procedure) }
let(:variables) { { input: { dossierId: dossier.to_typed_id, instructeurId: instructeur.to_typed_id } } }
let(:operation_name) { 'dossierRepasserEnInstruction' }
it {
expect(gql_errors).to be_nil
expect(gql_data[:dossierRepasserEnInstruction][:errors]).to be_nil
expect(gql_data[:dossierRepasserEnInstruction][:dossier][:id]).to eq(dossier.to_typed_id)
expect(gql_data[:dossierRepasserEnInstruction][:dossier][:state]).to eq('en_instruction')
}
end
context 'dossierAccepter' do
let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) }
let(:variables) { { input: { dossierId: dossier.to_typed_id, instructeurId: instructeur.to_typed_id } } }
let(:operation_name) { 'dossierAccepter' }
it {
expect(gql_errors).to be_nil
expect(gql_data[:dossierAccepter][:errors]).to be_nil
expect(gql_data[:dossierAccepter][:dossier][:id]).to eq(dossier.to_typed_id)
expect(gql_data[:dossierAccepter][:dossier][:state]).to eq('accepte')
}
end
context 'dossierRefuser' do
let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) }
let(:variables) { { input: { dossierId: dossier.to_typed_id, instructeurId: instructeur.to_typed_id, motivation: 'yolo' } } }
let(:operation_name) { 'dossierRefuser' }
it {
expect(gql_errors).to be_nil
expect(gql_data[:dossierRefuser][:errors]).to be_nil
expect(gql_data[:dossierRefuser][:dossier][:id]).to eq(dossier.to_typed_id)
expect(gql_data[:dossierRefuser][:dossier][:state]).to eq('refuse')
}
end
context 'dossierClasserSansSuite' do
let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) }
let(:variables) { { input: { dossierId: dossier.to_typed_id, instructeurId: instructeur.to_typed_id, motivation: 'yolo' } } }
let(:operation_name) { 'dossierClasserSansSuite' }
it {
expect(gql_errors).to be_nil
expect(gql_data[:dossierClasserSansSuite][:errors]).to be_nil
expect(gql_data[:dossierClasserSansSuite][:dossier][:id]).to eq(dossier.to_typed_id)
expect(gql_data[:dossierClasserSansSuite][:dossier][:state]).to eq('sans_suite')
}
end
context 'groupeInstructeurModifier' do
let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) }
let(:variables) { { input: { groupeInstructeurId: dossier.groupe_instructeur.to_typed_id, label: 'nouveau groupe instructeur' } } }
let(:operation_name) { 'groupeInstructeurModifier' }
it {
expect(gql_errors).to be_nil
expect(gql_data[:groupeInstructeurModifier][:errors]).to be_nil
expect(gql_data[:groupeInstructeurModifier][:groupeInstructeur][:id]).to eq(dossier.groupe_instructeur.to_typed_id)
expect(dossier.groupe_instructeur.reload.label).to eq('nouveau groupe instructeur')
}
context 'close groupe instructeur' do
let(:variables) { { input: { groupeInstructeurId: dossier.groupe_instructeur.to_typed_id, closed: true } } }
context 'with multiple groupes' do
before do
create(:groupe_instructeur, procedure: procedure)
end
it {
expect(gql_errors).to be_nil
expect(gql_data[:groupeInstructeurModifier][:errors]).to be_nil
expect(gql_data[:groupeInstructeurModifier][:groupeInstructeur][:id]).to eq(dossier.groupe_instructeur.to_typed_id)
expect(dossier.groupe_instructeur.reload.closed).to be_truthy
}
end
context 'validation error' do
it {
expect(gql_errors).to be_nil
expect(gql_data[:groupeInstructeurModifier][:errors].first[:message]).to eq('Il doit y avoir au moins un groupe instructeur actif sur chaque démarche')
}
end
end
end
end
end
describe "mutations" do
describe 'dossierEnvoyerMessage' do
let(:query) do
"mutation($input: DossierEnvoyerMessageInput!) {
dossierEnvoyerMessage(input: $input) {
message {
body
}
errors {
message
}
}
}"
end
let(:variables) { { input: input } }
let(:input) do
{
dossierId: dossier_id,
instructeurId: instructeur_id,
body: input_body,
attachment: attachment
}
end
let(:dossier_id) { dossier.to_typed_id }
let(:instructeur_id) { instructeur.to_typed_id }
let(:input_body) { "Bonjour" }
let(:attachment) { nil }
context 'success' do
let(:attachment) { blob.signed_id }
it "should post a message" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierEnvoyerMessage: {
message: {
body: "Bonjour"
},
errors: nil
})
end
end
context 'schema error' do
let(:input) do
{
dossierId: dossier_id,
instructeurId: instructeur_id
}
end
it "should fail" do
expect(gql_data).to eq(nil)
expect(gql_errors).not_to eq(nil)
expect(body[:errors].first[:message]).to eq("Variable $input of type DossierEnvoyerMessageInput! was provided invalid value for body (Expected value to not be null)")
expect(body[:errors].first.key?(:backtrace)).to be_falsey
end
end
context 'variables error' do
let(:variables) { "{" }
it "should fail" do
expect(gql_data).to eq(nil)
expect(gql_errors).not_to eq(nil)
expect(body[:errors].first[:message]).to eq("809: unexpected token at '{'")
expect(body[:errors].first.key?(:backtrace)).to be_falsey
end
end
context 'validation error' do
let(:input_body) { "" }
it "should fail" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierEnvoyerMessage: {
errors: [{ message: "Votre message ne peut être vide" }],
message: nil
})
end
end
context 'upload error' do
let(:attachment) { 'fake' }
it "should fail" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierEnvoyerMessage: {
errors: [{ message: "Lidentifiant du fichier téléversé est invalide" }],
message: nil
})
end
end
end
describe 'dossierPasserEnInstruction' do
let(:dossiers) { [dossier2, dossier1, dossier] }
let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
let(:instructeur_id) { instructeur.to_typed_id }
let(:disable_notification) { false }
let(:query) do
"mutation {
dossierPasserEnInstruction(input: {
dossierId: \"#{dossier.to_typed_id}\",
instructeurId: \"#{instructeur_id}\",
disableNotification: #{disable_notification}
}) {
dossier {
id
state
motivation
}
errors {
message
}
}
}"
end
context 'success' do
it "should passer en instruction dossier" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierPasserEnInstruction: {
dossier: {
id: dossier.to_typed_id,
state: "en_instruction",
motivation: nil
},
errors: nil
})
perform_enqueued_jobs except: [APIEntreprise::ServiceJob]
expect(ActionMailer::Base.deliveries.size).to eq(4)
end
end
context 'validation error' do
let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) }
it "should fail" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierPasserEnInstruction: {
errors: [{ message: "Le dossier est déjà en instruction" }],
dossier: nil
})
end
end
context 'instructeur error' do
let(:instructeur_id) { create(:instructeur).to_typed_id }
it "should fail" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierPasserEnInstruction: {
errors: [{ message: 'Linstructeur na pas les droits daccès à ce dossier' }],
dossier: nil
})
end
end
context 'disable notification' do
let(:disable_notification) { true }
it "should passer en instruction dossier without notification" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierPasserEnInstruction: {
dossier: {
id: dossier.to_typed_id,
state: "en_instruction",
motivation: nil
},
errors: nil
})
perform_enqueued_jobs except: [APIEntreprise::ServiceJob]
expect(ActionMailer::Base.deliveries.size).to eq(3)
end
end
end
describe 'dossierRepasserEnInstruction' do
let(:dossiers) { [dossier2, dossier1, dossier] }
let(:dossier) { create(:dossier, :refuse, :with_individual, procedure: procedure) }
let(:instructeur_id) { instructeur.to_typed_id }
let(:disable_notification) { false }
let(:query) do
"mutation {
dossierRepasserEnInstruction(input: {
dossierId: \"#{dossier.to_typed_id}\",
instructeurId: \"#{instructeur_id}\",
disableNotification: #{disable_notification}
}) {
dossier {
id
state
motivation
}
errors {
message
}
}
}"
end
context 'success' do
it "should repasser en instruction dossier" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierRepasserEnInstruction: {
dossier: {
id: dossier.to_typed_id,
state: "en_instruction",
motivation: nil
},
errors: nil
})
perform_enqueued_jobs except: [APIEntreprise::ServiceJob]
expect(ActionMailer::Base.deliveries.size).to eq(4)
end
end
end
describe 'dossierRepasserEnConstruction' do
let(:dossiers) { [dossier2, dossier1, dossier] }
let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) }
let(:instructeur_id) { instructeur.to_typed_id }
let(:disable_notification) { false }
let(:query) do
"mutation {
dossierRepasserEnConstruction(input: {
dossierId: \"#{dossier.to_typed_id}\",
instructeurId: \"#{instructeur_id}\",
disableNotification: #{disable_notification}
}) {
dossier {
id
state
motivation
}
errors {
message
}
}
}"
end
context 'success' do
it "should passer en instruction dossier" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierRepasserEnConstruction: {
dossier: {
id: dossier.to_typed_id,
state: "en_construction",
motivation: nil
},
errors: nil
})
perform_enqueued_jobs except: [APIEntreprise::ServiceJob]
expect(ActionMailer::Base.deliveries.size).to eq(3)
end
end
end
describe 'dossierClasserSansSuite' do
let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) }
let(:query) do
"mutation {
dossierClasserSansSuite(input: {
dossierId: \"#{dossier.to_typed_id}\",
instructeurId: \"#{instructeur.to_typed_id}\",
motivation: \"Parce que\",
justificatif: \"#{blob.signed_id}\"
}) {
dossier {
id
state
motivation
}
errors {
message
}
}
}"
end
context 'success' do
it "should classer sans suite dossier" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierClasserSansSuite: {
dossier: {
id: dossier.to_typed_id,
state: "sans_suite",
motivation: "Parce que"
},
errors: nil
})
end
end
context 'validation error' do
let(:dossier) { create(:dossier, :accepte, :with_individual, procedure: procedure) }
it "should fail" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierClasserSansSuite: {
errors: [{ message: "Le dossier est déjà accepté" }],
dossier: nil
})
end
end
end
describe 'dossierRefuser' do
let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) }
let(:query) do
"mutation {
dossierRefuser(input: {
dossierId: \"#{dossier.to_typed_id}\",
instructeurId: \"#{instructeur.to_typed_id}\",
motivation: \"Parce que\",
justificatif: \"#{blob.signed_id}\"
}) {
dossier {
id
state
motivation
}
errors {
message
}
}
}"
end
context 'success' do
it "should refuser dossier" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierRefuser: {
dossier: {
id: dossier.to_typed_id,
state: "refuse",
motivation: "Parce que"
},
errors: nil
})
end
end
context 'validation error' do
let(:dossier) { create(:dossier, :sans_suite, :with_individual, procedure: procedure) }
it "should fail" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierRefuser: {
errors: [{ message: "Le dossier est déjà classé sans suite" }],
dossier: nil
})
end
end
end
describe 'dossierAccepter' do
let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) }
let(:query) do
"mutation {
dossierAccepter(input: {
dossierId: \"#{dossier.to_typed_id}\",
instructeurId: \"#{instructeur.to_typed_id}\",
motivation: \"Parce que\",
justificatif: \"#{blob.signed_id}\"
}) {
dossier {
id
state
motivation
}
errors {
message
}
}
}"
end
context 'success' do
it "should accepter dossier" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierAccepter: {
dossier: {
id: dossier.to_typed_id,
state: "accepte",
motivation: "Parce que"
},
errors: nil
})
end
end
context 'success without motivation' do
let(:query) do
"mutation {
dossierAccepter(input: {
dossierId: \"#{dossier.to_typed_id}\",
instructeurId: \"#{instructeur.to_typed_id}\"
}) {
dossier {
id
state
motivation
}
errors {
message
}
}
}"
end
it "should accepter dossier" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierAccepter: {
dossier: {
id: dossier.to_typed_id,
state: "accepte",
motivation: nil
},
errors: nil
})
end
end
context 'validation error' do
let(:dossier) { create(:dossier, :refuse, :with_individual, procedure: procedure) }
it "should fail" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierAccepter: {
errors: [{ message: "Le dossier est déjà refusé" }],
dossier: nil
})
end
end
end
describe 'createDirectUpload' do
let(:query) do
"mutation {
createDirectUpload(input: {
dossierId: \"#{dossier.to_typed_id}\",
filename: \"#{blob_info[:filename]}\",
byteSize: #{blob_info[:byte_size]},
checksum: \"#{blob_info[:checksum]}\",
contentType: \"#{blob_info[:content_type]}\"
}) {
directUpload {
url
headers
blobId
signedBlobId
}
}
}"
end
let(:attach_query) do
"mutation {
dossierEnvoyerMessage(input: {
dossierId: \"#{dossier.to_typed_id}\",
instructeurId: \"#{instructeur.to_typed_id}\",
body: \"Hello world\",
attachment: \"#{direct_upload_blob_id}\"
}) {
message {
body
}
errors {
message
}
}
}"
end
let(:attach_query_exec) { post :execute, params: { query: attach_query } }
let(:attach_query_body) { JSON.parse(attach_query_exec.body, symbolize_names: true) }
let(:attach_query_data) { attach_query_body[:data] }
let(:direct_upload_data) { gql_data[:createDirectUpload][:directUpload] }
let(:direct_upload_blob_id) { direct_upload_data[:signedBlobId] }
it "should initiate a direct upload" do
expect(gql_errors).to eq(nil)
data = gql_data[:createDirectUpload][:directUpload]
expect(data[:url]).not_to be_nil
expect(data[:headers]).not_to be_nil
expect(data[:blobId]).not_to be_nil
expect(data[:signedBlobId]).not_to be_nil
end
it "wrong hash error" do
blob = ActiveStorage::Blob.find direct_upload_data[:blobId]
blob.service.upload blob.key, StringIO.new('toto')
expect(attach_query_data).to eq(dossierEnvoyerMessage: {
errors: [{ message: "Le hash du fichier téléversé est invalide" }],
message: nil
})
end
end
describe 'dossierChangerGroupeInstructeur' do
let(:query) do
"mutation {
dossierChangerGroupeInstructeur(input: {
dossierId: \"#{dossier.to_typed_id}\",
groupeInstructeurId: \"#{dossier.groupe_instructeur.to_typed_id}\"
}) {
errors {
message
}
}
}"
end
it "validation error" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierChangerGroupeInstructeur: {
errors: [{ message: "Le dossier est déjà avec le grope instructeur: 'défaut'" }]
})
end
context "should changer groupe instructeur" do
let!(:new_groupe_instructeur) { procedure.groupe_instructeurs.create(label: 'new groupe instructeur') }
let(:query) do
"mutation {
dossierChangerGroupeInstructeur(input: {
dossierId: \"#{dossier.to_typed_id}\",
groupeInstructeurId: \"#{new_groupe_instructeur.to_typed_id}\"
}) {
errors {
message
}
}
}"
end
it "change made" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierChangerGroupeInstructeur: {
errors: nil
})
end
end
end
describe 'dossierArchiver' do
let(:query) do
"mutation {
dossierArchiver(input: {
dossierId: \"#{dossier.to_typed_id}\",
instructeurId: \"#{instructeur.to_typed_id}\"
}) {
dossier {
archived
}
errors {
message
}
}
}"
end
it "validation error" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierArchiver: {
dossier: nil,
errors: [{ message: "Un dossier ne peut être archivé quune fois le traitement terminé" }]
})
end
context "should archive dossier" do
let(:dossier) { create(:dossier, :sans_suite, :with_individual, procedure: procedure) }
it "change made" do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierArchiver: {
dossier: {
archived: true
},
errors: nil
})
end
end
end
describe 'dossierModifierAnnotation' do
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, :with_all_annotations, administrateurs: [admin]) }
describe 'text' do
let(:query) do
"mutation {
dossierModifierAnnotationText(input: {
dossierId: \"#{dossier.to_typed_id}\",
annotationId: \"#{dossier.champs_private.first.to_typed_id}\",
instructeurId: \"#{instructeur.to_typed_id}\",
value: \"hello\"
}) {
annotation {
stringValue
}
errors {
message
}
}
}"
end
context "success" do
it 'should be a success' do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierModifierAnnotationText: {
annotation: {
stringValue: 'hello'
},
errors: nil
})
end
end
end
describe 'checkbox' do
let(:value) { 'true' }
let(:query) do
"mutation {
dossierModifierAnnotationCheckbox(input: {
dossierId: \"#{dossier.to_typed_id}\",
annotationId: \"#{dossier.champs_private.find { |c| c.type_champ == 'checkbox' }.to_typed_id}\",
instructeurId: \"#{instructeur.to_typed_id}\",
value: #{value}
}) {
annotation {
stringValue
}
errors {
message
}
}
}"
end
context "success when true" do
it 'should be a success' do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierModifierAnnotationCheckbox: {
annotation: {
stringValue: 'true'
},
errors: nil
})
end
end
context "success when false" do
let(:value) { 'false' }
it 'should be a success' do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierModifierAnnotationCheckbox: {
annotation: {
stringValue: 'false'
},
errors: nil
})
end
end
end
describe 'yes_no' do
let(:value) { 'true' }
let(:query) do
"mutation {
dossierModifierAnnotationCheckbox(input: {
dossierId: \"#{dossier.to_typed_id}\",
annotationId: \"#{dossier.champs_private.find { |c| c.type_champ == 'yes_no' }.to_typed_id}\",
instructeurId: \"#{instructeur.to_typed_id}\",
value: #{value}
}) {
annotation {
stringValue
}
errors {
message
}
}
}"
end
context "success when true" do
it 'should be a success' do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierModifierAnnotationCheckbox: {
annotation: {
stringValue: 'true'
},
errors: nil
})
end
end
context "success when false" do
let(:value) { 'false' }
it 'should be a success' do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierModifierAnnotationCheckbox: {
annotation: {
stringValue: 'false'
},
errors: nil
})
end
end
end
describe 'date' do
let(:query) do
"mutation {
dossierModifierAnnotationDate(input: {
dossierId: \"#{dossier.to_typed_id}\",
annotationId: \"#{dossier.champs_private.find { |c| c.type_champ == 'date' }.to_typed_id}\",
instructeurId: \"#{instructeur.to_typed_id}\",
value: \"#{1.day.from_now.to_date.iso8601}\"
}) {
annotation {
stringValue
}
errors {
message
}
}
}"
end
context "success" do
it 'should be a success' do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierModifierAnnotationDate: {
annotation: {
stringValue: dossier.reload.champs_private.find { |c| c.type_champ == 'date' }.to_s
},
errors: nil
})
end
end
end
describe 'datetime' do
let(:query) do
"mutation {
dossierModifierAnnotationDatetime(input: {
dossierId: \"#{dossier.to_typed_id}\",
annotationId: \"#{dossier.champs_private.find { |c| c.type_champ == 'datetime' }.to_typed_id}\",
instructeurId: \"#{instructeur.to_typed_id}\",
value: \"#{1.day.from_now.iso8601}\"
}) {
annotation {
stringValue
}
errors {
message
}
}
}"
end
context "success" do
it 'should be a success' do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierModifierAnnotationDatetime: {
annotation: {
stringValue: dossier.reload.champs_private.find { |c| c.type_champ == 'datetime' }.to_s
},
errors: nil
})
end
end
end
describe 'integer_number' do
let(:query) do
"mutation {
dossierModifierAnnotationIntegerNumber(input: {
dossierId: \"#{dossier.to_typed_id}\",
annotationId: \"#{dossier.champs_private.find { |c| c.type_champ == 'integer_number' }.to_typed_id}\",
instructeurId: \"#{instructeur.to_typed_id}\",
value: 42
}) {
annotation {
stringValue
}
errors {
message
}
}
}"
end
context "success" do
it 'should be a success' do
expect(gql_errors).to eq(nil)
expect(gql_data).to eq(dossierModifierAnnotationIntegerNumber: {
annotation: {
stringValue: '42'
},
errors: nil
})
end
end
end
end
end
end
context "when not authenticated" do
it "should return error" do
expect(gql_data).to eq(nil)
expect(gql_errors).not_to eq(nil)
end
describe "dossier" do
let(:query) { "{ dossier(number: #{dossier.id}) { id number usager { email } } }" }
it "should return error" do
expect(gql_data).to eq(nil)
expect(gql_errors).not_to eq(nil)
end
end
describe "mutation" do
let(:query) do
"mutation {
dossierEnvoyerMessage(input: {
dossierId: \"#{dossier.to_typed_id}\",
instructeurId: \"#{instructeur.to_typed_id}\",
body: \"Bonjour\"
}) {
message {
body
}
}
}"
end
it "should return error" do
expect(gql_data[:dossierEnvoyerMessage]).to eq(nil)
expect(gql_errors).not_to eq(nil)
end
end
end
end