From 74950d5a3ef4781cf33425515548781cc57f7b0c Mon Sep 17 00:00:00 2001 From: Raphael Date: Sat, 27 Jun 2026 11:11:05 +0200 Subject: [PATCH 1/2] fix: started to work on definition related velorum bugs --- app/graphql/subscription_triggers.rb | 4 +-- app/graphql/subscriptions/ai/generate_flow.rb | 6 ++++- app/jobs/velorum_generate_flow_job.rb | 17 +++++++++--- app/services/error_code.rb | 1 + app/services/velorum/generate_flow_service.rb | 12 +++++++++ spec/jobs/velorum_generate_flow_job_spec.rb | 26 ++++++++++++++++--- .../subscription/ai/generate_flow_spec.rb | 22 ++++++++++++++++ .../velorum/generate_flow_service_spec.rb | 20 ++++++++++++++ 8 files changed, 98 insertions(+), 10 deletions(-) diff --git a/app/graphql/subscription_triggers.rb b/app/graphql/subscription_triggers.rb index 37c6f4af9..638e46073 100644 --- a/app/graphql/subscription_triggers.rb +++ b/app/graphql/subscription_triggers.rb @@ -10,11 +10,11 @@ def self.execution_result(execution_result) ) end - def self.ai_generate_flow(execution_identifier, flow) + def self.ai_generate_flow(execution_identifier, flow, errors: []) SagittariusSchema.subscriptions.trigger( :ai_generate_flow, { execution_identifier: execution_identifier }, - flow, + { flow: flow, errors: errors }, context: { visibility_profile: :execution } ) end diff --git a/app/graphql/subscriptions/ai/generate_flow.rb b/app/graphql/subscriptions/ai/generate_flow.rb index 4967dd592..bb836f03b 100644 --- a/app/graphql/subscriptions/ai/generate_flow.rb +++ b/app/graphql/subscriptions/ai/generate_flow.rb @@ -10,6 +10,10 @@ class GenerateFlow < BaseSubscription required: true, description: 'AI generation request identifier returned by the mutation' + field :errors, + type: [Types::Errors::ErrorType], + null: false, + description: 'Errors encountered while generating the flow' field :flow, type: Types::Ai::GenerationFlowType, null: true, @@ -20,7 +24,7 @@ def subscribe(**) end def update(*) - unsubscribe(flow: object) + unsubscribe(object) end end end diff --git a/app/jobs/velorum_generate_flow_job.rb b/app/jobs/velorum_generate_flow_job.rb index 1cfdc60f9..6db23a1ad 100644 --- a/app/jobs/velorum_generate_flow_job.rb +++ b/app/jobs/velorum_generate_flow_job.rb @@ -3,10 +3,10 @@ class VelorumGenerateFlowJob < ApplicationJob def perform(execution_identifier, project_id, prompt, model_identifier, flow_id = nil) project = NamespaceProject.find_by(id: project_id) - return if project.nil? + return trigger_error(execution_identifier, :project_not_found, 'Project does not exist') if project.nil? flow = flow_id.present? ? Flow.find_by(id: flow_id) : nil - return if flow_id.present? && flow.nil? + return trigger_error(execution_identifier, :flow_not_found, 'Flow does not exist') if flow_id.present? && flow.nil? response = Velorum::GenerateFlowService.new( nil, @@ -17,6 +17,17 @@ def perform(execution_identifier, project_id, prompt, model_identifier, flow_id authorize: false ).execute - SubscriptionTriggers.ai_generate_flow(execution_identifier, response.success? ? response.payload[:flow] : nil) + if response.success? + SubscriptionTriggers.ai_generate_flow(execution_identifier, response.payload[:flow]) + else + trigger_error(execution_identifier, response.payload[:error_code], response.message) + end + end + + private + + def trigger_error(execution_identifier, error_code, message) + error = { error_code: error_code, details: [{ message: message }] } + SubscriptionTriggers.ai_generate_flow(execution_identifier, nil, errors: [error]) end end diff --git a/app/services/error_code.rb b/app/services/error_code.rb index 06711c737..d33a3c94b 100644 --- a/app/services/error_code.rb +++ b/app/services/error_code.rb @@ -59,6 +59,7 @@ def self.error_codes invalid_attachment: { description: 'The attachment is invalid because of active model errors' }, invalid_flow: { description: 'The flow is invalid because of active model errors' }, flow_generation_failed: { description: 'Flow generation failed' }, + no_definitions: { description: 'No definitions are available to generate a flow' }, project_not_found: { description: 'The namespace project with the given identifier was not found' }, runtime_not_found: { description: 'The runtime with the given identifier was not found' }, runtime_not_assigned: { description: 'The runtime is not assigned to the project' }, diff --git a/app/services/velorum/generate_flow_service.rb b/app/services/velorum/generate_flow_service.rb index 632572ec5..f7ed05d0e 100644 --- a/app/services/velorum/generate_flow_service.rb +++ b/app/services/velorum/generate_flow_service.rb @@ -34,6 +34,7 @@ def execute return flow_project_mismatch_response if flow.present? && flow.project != project return missing_permission_response if authorize? && !allowed? return no_primary_runtime_response if runtime.nil? + return no_definitions_response unless definitions? response = flow.present? ? client.flow(flow_request) : client.prompt(prompt_request) logger.debug( @@ -123,6 +124,10 @@ def runtime project.primary_runtime end + def definitions? + runtime.function_definitions.any? || runtime.data_types.any? || runtime.flow_types.any? + end + def client @client ||= Sagittarius::Velorum::Client.new end @@ -154,6 +159,13 @@ def no_primary_runtime_response ServiceResponse.error(message: 'Project has no primary runtime', error_code: :no_primary_runtime) end + def no_definitions_response + ServiceResponse.error( + message: 'No definitions are available to generate a flow', + error_code: :no_definitions + ) + end + def flow_generation_failed_response(error) ServiceResponse.error( message: 'Flow generation failed', diff --git a/spec/jobs/velorum_generate_flow_job_spec.rb b/spec/jobs/velorum_generate_flow_job_spec.rb index b2663b177..5770e5caa 100644 --- a/spec/jobs/velorum_generate_flow_job_spec.rb +++ b/spec/jobs/velorum_generate_flow_job_spec.rb @@ -32,13 +32,22 @@ expect(SubscriptionTriggers).to have_received(:ai_generate_flow).with(execution_identifier, flow) end - it 'does not trigger the subscription when the project is gone' do + it 'closes the subscription with an error when the project is gone' do perform_enqueued_jobs do described_class.perform_later(execution_identifier, -1, 'Generate a flow', 'gpt-5') end expect(Velorum::GenerateFlowService).not_to have_received(:new) - expect(SubscriptionTriggers).not_to have_received(:ai_generate_flow) + expect(SubscriptionTriggers).to have_received(:ai_generate_flow).with( + execution_identifier, + nil, + errors: [ + a_hash_including( + error_code: :project_not_found, + details: [{ message: 'Project does not exist' }] + ) + ] + ) end context 'when flow generation fails' do @@ -46,12 +55,21 @@ ServiceResponse.error(message: 'Flow generation failed', error_code: :flow_generation_failed) end - it 'triggers a nil subscription response to close the stream' do + it 'triggers an error subscription response to close the stream' do perform_enqueued_jobs do described_class.perform_later(execution_identifier, project.id, 'Generate a flow', 'gpt-5') end - expect(SubscriptionTriggers).to have_received(:ai_generate_flow).with(execution_identifier, nil) + expect(SubscriptionTriggers).to have_received(:ai_generate_flow).with( + execution_identifier, + nil, + errors: [ + a_hash_including( + error_code: :flow_generation_failed, + details: [{ message: 'Flow generation failed' }] + ) + ] + ) end end end diff --git a/spec/requests/graphql/subscription/ai/generate_flow_spec.rb b/spec/requests/graphql/subscription/ai/generate_flow_spec.rb index b859ef019..7c1a45d3b 100644 --- a/spec/requests/graphql/subscription/ai/generate_flow_spec.rb +++ b/spec/requests/graphql/subscription/ai/generate_flow_spec.rb @@ -118,6 +118,9 @@ <<~GQL subscription($executionIdentifier: String!) { aiGenerateFlow(executionIdentifier: $executionIdentifier) { + errors { + errorCode + } flow { name type { @@ -196,6 +199,7 @@ expect(transmissions.last).to include('more' => false) expect(transmissions.last.dig('result', 'data', 'aiGenerateFlow')).to eq( + 'errors' => [], 'flow' => { 'name' => 'Generated flow', 'type' => { @@ -282,4 +286,22 @@ } ) end + + it 'delivers an error and closes the subscription when generation cannot start' do + perform :execute, + query: subscription_query, + variables: { executionIdentifier: execution_identifier } + + error = { + error_code: :no_definitions, + details: [{ message: 'No definitions are available to generate a flow' }], + } + SubscriptionTriggers.ai_generate_flow(execution_identifier, nil, errors: [error]) + + expect(transmissions.last).to include('more' => false) + expect(transmissions.last.dig('result', 'data', 'aiGenerateFlow')).to eq( + 'errors' => [{ 'errorCode' => 'NO_DEFINITIONS' }], + 'flow' => nil + ) + end end diff --git a/spec/services/velorum/generate_flow_service_spec.rb b/spec/services/velorum/generate_flow_service_spec.rb index da29dc7c6..6ce0efd7f 100644 --- a/spec/services/velorum/generate_flow_service_spec.rb +++ b/spec/services/velorum/generate_flow_service_spec.rb @@ -179,6 +179,26 @@ end end + context 'when the runtime has no definitions' do + let(:runtime) do + instance_double( + Runtime, + id: 9, + function_definitions: [], + data_types: [], + flow_types: [] + ) + end + + it 'returns an error without calling Velorum' do + expect(service_response).to be_error + expect(service_response.message).to eq('No definitions are available to generate a flow') + expect(service_response.payload[:error_code]).to eq(:no_definitions) + expect(client).not_to have_received(:prompt) + expect(client).not_to have_received(:flow) + end + end + context 'when Velorum returns a gRPC error' do before do allow(client).to receive(:prompt).and_raise( From 1bf0f0c5e308d389b7e62db8cdf33e76aabac266 Mon Sep 17 00:00:00 2001 From: Raphael Date: Sun, 28 Jun 2026 21:07:09 +0200 Subject: [PATCH 2/2] feat: added more error handling for velorum --- app/graphql/mutations/ai/generate_flow.rb | 9 +++ app/graphql/subscription_triggers.rb | 4 +- app/graphql/subscriptions/ai/generate_flow.rb | 6 +- app/jobs/velorum_generate_flow_job.rb | 17 +---- app/services/velorum/generate_flow_service.rb | 4 +- docs/graphql/enum/errorcodeenum.md | 1 + spec/jobs/velorum_generate_flow_job_spec.rb | 31 +-------- .../graphql/mutation/ai/generate_flow_spec.rb | 69 +++++++++++++++++++ .../subscription/ai/generate_flow_spec.rb | 22 ------ .../velorum/generate_flow_service_spec.rb | 28 +++++++- 10 files changed, 115 insertions(+), 76 deletions(-) diff --git a/app/graphql/mutations/ai/generate_flow.rb b/app/graphql/mutations/ai/generate_flow.rb index d4d9af59a..016755824 100644 --- a/app/graphql/mutations/ai/generate_flow.rb +++ b/app/graphql/mutations/ai/generate_flow.rb @@ -29,6 +29,8 @@ class GenerateFlow < BaseMutation def resolve(project_id:, prompt:, model_identifier:, flow_id: nil) return error_response(:invalid_setting, 'AI is disabled') unless ai_enabled? + return error_response(:missing_parameter, 'Prompt cannot be empty') if prompt.blank? + return error_response(:missing_parameter, 'Model identifier cannot be empty') if model_identifier.blank? project = SagittariusSchema.object_from_id(project_id) return error_response(:project_not_found, 'Invalid project id') if project.nil? @@ -41,6 +43,9 @@ def resolve(project_id:, prompt:, model_identifier:, flow_id: nil) return error_response(:no_primary_runtime, 'Project has no primary runtime') if project.primary_runtime.nil? return error_response(:missing_permission, 'Missing permission') unless allowed?(project, flow) + unless definitions_available?(project.primary_runtime) + return error_response(:no_definitions, 'The primary runtime must provide functions and flow types') + end execution_identifier = SecureRandom.uuid VelorumGenerateFlowJob.perform_later(execution_identifier, project.id, prompt, model_identifier, flow&.id) @@ -63,6 +68,10 @@ def allowed?(project, flow) Ability.allowed?(current_authentication, ability, subject) end + def definitions_available?(runtime) + runtime.function_definitions.exists? && runtime.flow_types.exists? + end + def error_response(error_code, message) { execution_identifier: nil, diff --git a/app/graphql/subscription_triggers.rb b/app/graphql/subscription_triggers.rb index 638e46073..37c6f4af9 100644 --- a/app/graphql/subscription_triggers.rb +++ b/app/graphql/subscription_triggers.rb @@ -10,11 +10,11 @@ def self.execution_result(execution_result) ) end - def self.ai_generate_flow(execution_identifier, flow, errors: []) + def self.ai_generate_flow(execution_identifier, flow) SagittariusSchema.subscriptions.trigger( :ai_generate_flow, { execution_identifier: execution_identifier }, - { flow: flow, errors: errors }, + flow, context: { visibility_profile: :execution } ) end diff --git a/app/graphql/subscriptions/ai/generate_flow.rb b/app/graphql/subscriptions/ai/generate_flow.rb index bb836f03b..4967dd592 100644 --- a/app/graphql/subscriptions/ai/generate_flow.rb +++ b/app/graphql/subscriptions/ai/generate_flow.rb @@ -10,10 +10,6 @@ class GenerateFlow < BaseSubscription required: true, description: 'AI generation request identifier returned by the mutation' - field :errors, - type: [Types::Errors::ErrorType], - null: false, - description: 'Errors encountered while generating the flow' field :flow, type: Types::Ai::GenerationFlowType, null: true, @@ -24,7 +20,7 @@ def subscribe(**) end def update(*) - unsubscribe(object) + unsubscribe(flow: object) end end end diff --git a/app/jobs/velorum_generate_flow_job.rb b/app/jobs/velorum_generate_flow_job.rb index 6db23a1ad..1cfdc60f9 100644 --- a/app/jobs/velorum_generate_flow_job.rb +++ b/app/jobs/velorum_generate_flow_job.rb @@ -3,10 +3,10 @@ class VelorumGenerateFlowJob < ApplicationJob def perform(execution_identifier, project_id, prompt, model_identifier, flow_id = nil) project = NamespaceProject.find_by(id: project_id) - return trigger_error(execution_identifier, :project_not_found, 'Project does not exist') if project.nil? + return if project.nil? flow = flow_id.present? ? Flow.find_by(id: flow_id) : nil - return trigger_error(execution_identifier, :flow_not_found, 'Flow does not exist') if flow_id.present? && flow.nil? + return if flow_id.present? && flow.nil? response = Velorum::GenerateFlowService.new( nil, @@ -17,17 +17,6 @@ def perform(execution_identifier, project_id, prompt, model_identifier, flow_id authorize: false ).execute - if response.success? - SubscriptionTriggers.ai_generate_flow(execution_identifier, response.payload[:flow]) - else - trigger_error(execution_identifier, response.payload[:error_code], response.message) - end - end - - private - - def trigger_error(execution_identifier, error_code, message) - error = { error_code: error_code, details: [{ message: message }] } - SubscriptionTriggers.ai_generate_flow(execution_identifier, nil, errors: [error]) + SubscriptionTriggers.ai_generate_flow(execution_identifier, response.success? ? response.payload[:flow] : nil) end end diff --git a/app/services/velorum/generate_flow_service.rb b/app/services/velorum/generate_flow_service.rb index f7ed05d0e..7b11d0590 100644 --- a/app/services/velorum/generate_flow_service.rb +++ b/app/services/velorum/generate_flow_service.rb @@ -125,7 +125,7 @@ def runtime end def definitions? - runtime.function_definitions.any? || runtime.data_types.any? || runtime.flow_types.any? + runtime.function_definitions.any? && runtime.flow_types.any? end def client @@ -161,7 +161,7 @@ def no_primary_runtime_response def no_definitions_response ServiceResponse.error( - message: 'No definitions are available to generate a flow', + message: 'The primary runtime must provide functions and flow types', error_code: :no_definitions ) end diff --git a/docs/graphql/enum/errorcodeenum.md b/docs/graphql/enum/errorcodeenum.md index 65bd1d665..ae73ded15 100644 --- a/docs/graphql/enum/errorcodeenum.md +++ b/docs/graphql/enum/errorcodeenum.md @@ -80,6 +80,7 @@ Represents the available error responses | `NAMESPACE_ROLE_NOT_FOUND` | The namespace role with the given identifier was not found | | `NODE_NOT_FOUND` | The node with this id does not exist | | `NO_DATA_TYPE_FOR_IDENTIFIER` | No data type could be found for the given identifier | +| `NO_DEFINITIONS` | No definitions are available to generate a flow | | `NO_FREE_LICENSE_SEATS` | There are no free license seats to complete this operation | | `NO_PRIMARY_RUNTIME` | The project does not have a primary runtime | | `ORGANIZATION_NOT_FOUND` | The organization with the given identifier was not found | diff --git a/spec/jobs/velorum_generate_flow_job_spec.rb b/spec/jobs/velorum_generate_flow_job_spec.rb index 5770e5caa..38c3145d2 100644 --- a/spec/jobs/velorum_generate_flow_job_spec.rb +++ b/spec/jobs/velorum_generate_flow_job_spec.rb @@ -32,44 +32,17 @@ expect(SubscriptionTriggers).to have_received(:ai_generate_flow).with(execution_identifier, flow) end - it 'closes the subscription with an error when the project is gone' do - perform_enqueued_jobs do - described_class.perform_later(execution_identifier, -1, 'Generate a flow', 'gpt-5') - end - - expect(Velorum::GenerateFlowService).not_to have_received(:new) - expect(SubscriptionTriggers).to have_received(:ai_generate_flow).with( - execution_identifier, - nil, - errors: [ - a_hash_including( - error_code: :project_not_found, - details: [{ message: 'Project does not exist' }] - ) - ] - ) - end - context 'when flow generation fails' do let(:service_response) do ServiceResponse.error(message: 'Flow generation failed', error_code: :flow_generation_failed) end - it 'triggers an error subscription response to close the stream' do + it 'closes the subscription without a flow' do perform_enqueued_jobs do described_class.perform_later(execution_identifier, project.id, 'Generate a flow', 'gpt-5') end - expect(SubscriptionTriggers).to have_received(:ai_generate_flow).with( - execution_identifier, - nil, - errors: [ - a_hash_including( - error_code: :flow_generation_failed, - details: [{ message: 'Flow generation failed' }] - ) - ] - ) + expect(SubscriptionTriggers).to have_received(:ai_generate_flow).with(execution_identifier, nil) end end end diff --git a/spec/requests/graphql/mutation/ai/generate_flow_spec.rb b/spec/requests/graphql/mutation/ai/generate_flow_spec.rb index d070484db..1e9bd36fc 100644 --- a/spec/requests/graphql/mutation/ai/generate_flow_spec.rb +++ b/spec/requests/graphql/mutation/ai/generate_flow_spec.rb @@ -20,6 +20,27 @@ let(:current_user) { create(:user) } let(:runtime) { create(:runtime) } + let(:runtime_module) { create(:runtime_module, runtime: runtime) } + let(:runtime_function_definition) do + create(:runtime_function_definition, runtime: runtime, runtime_module: runtime_module) + end + let(:runtime_flow_type) { create(:runtime_flow_type, runtime: runtime, runtime_module: runtime_module) } + let!(:function_definition) do + create( + :function_definition, + runtime: runtime, + runtime_module: runtime_module, + runtime_function_definition: runtime_function_definition + ) + end + let!(:flow_type) do + create( + :flow_type, + runtime: runtime, + runtime_module: runtime_module, + runtime_flow_type: runtime_flow_type + ) + end let(:project) { create(:namespace_project, primary_runtime: runtime) } let(:variables) do { @@ -69,4 +90,52 @@ expect(VelorumGenerateFlowJob).not_to have_received(:perform_later) end end + + context 'when the prompt is blank' do + before { variables[:input][:prompt] = ' ' } + + it 'returns an error and does not enqueue a job' do + mutate! + + expect(graphql_data_at(:ai_generate_flow, :execution_identifier)).to be_nil + expect(graphql_data_at(:ai_generate_flow, :errors, 0, :error_code)).to eq('MISSING_PARAMETER') + expect(VelorumGenerateFlowJob).not_to have_received(:perform_later) + end + end + + context 'when the model identifier is blank' do + before { variables[:input][:modelIdentifier] = ' ' } + + it 'returns an error and does not enqueue a job' do + mutate! + + expect(graphql_data_at(:ai_generate_flow, :execution_identifier)).to be_nil + expect(graphql_data_at(:ai_generate_flow, :errors, 0, :error_code)).to eq('MISSING_PARAMETER') + expect(VelorumGenerateFlowJob).not_to have_received(:perform_later) + end + end + + context 'when the primary runtime has no functions' do + before { function_definition.destroy! } + + it 'returns an error and does not enqueue a job' do + mutate! + + expect(graphql_data_at(:ai_generate_flow, :execution_identifier)).to be_nil + expect(graphql_data_at(:ai_generate_flow, :errors, 0, :error_code)).to eq('NO_DEFINITIONS') + expect(VelorumGenerateFlowJob).not_to have_received(:perform_later) + end + end + + context 'when the primary runtime has no flow types' do + before { flow_type.destroy! } + + it 'returns an error and does not enqueue a job' do + mutate! + + expect(graphql_data_at(:ai_generate_flow, :execution_identifier)).to be_nil + expect(graphql_data_at(:ai_generate_flow, :errors, 0, :error_code)).to eq('NO_DEFINITIONS') + expect(VelorumGenerateFlowJob).not_to have_received(:perform_later) + end + end end diff --git a/spec/requests/graphql/subscription/ai/generate_flow_spec.rb b/spec/requests/graphql/subscription/ai/generate_flow_spec.rb index 7c1a45d3b..b859ef019 100644 --- a/spec/requests/graphql/subscription/ai/generate_flow_spec.rb +++ b/spec/requests/graphql/subscription/ai/generate_flow_spec.rb @@ -118,9 +118,6 @@ <<~GQL subscription($executionIdentifier: String!) { aiGenerateFlow(executionIdentifier: $executionIdentifier) { - errors { - errorCode - } flow { name type { @@ -199,7 +196,6 @@ expect(transmissions.last).to include('more' => false) expect(transmissions.last.dig('result', 'data', 'aiGenerateFlow')).to eq( - 'errors' => [], 'flow' => { 'name' => 'Generated flow', 'type' => { @@ -286,22 +282,4 @@ } ) end - - it 'delivers an error and closes the subscription when generation cannot start' do - perform :execute, - query: subscription_query, - variables: { executionIdentifier: execution_identifier } - - error = { - error_code: :no_definitions, - details: [{ message: 'No definitions are available to generate a flow' }], - } - SubscriptionTriggers.ai_generate_flow(execution_identifier, nil, errors: [error]) - - expect(transmissions.last).to include('more' => false) - expect(transmissions.last.dig('result', 'data', 'aiGenerateFlow')).to eq( - 'errors' => [{ 'errorCode' => 'NO_DEFINITIONS' }], - 'flow' => nil - ) - end end diff --git a/spec/services/velorum/generate_flow_service_spec.rb b/spec/services/velorum/generate_flow_service_spec.rb index 6ce0efd7f..c73683c12 100644 --- a/spec/services/velorum/generate_flow_service_spec.rb +++ b/spec/services/velorum/generate_flow_service_spec.rb @@ -179,7 +179,7 @@ end end - context 'when the runtime has no definitions' do + context 'when the runtime does not have functions and flow types' do let(:runtime) do instance_double( Runtime, @@ -192,13 +192,37 @@ it 'returns an error without calling Velorum' do expect(service_response).to be_error - expect(service_response.message).to eq('No definitions are available to generate a flow') + expect(service_response.message).to eq('The primary runtime must provide functions and flow types') expect(service_response.payload[:error_code]).to eq(:no_definitions) expect(client).not_to have_received(:prompt) expect(client).not_to have_received(:flow) end end + context 'when the runtime only has functions' do + let(:runtime) do + instance_double(Runtime, id: 9, function_definitions: [function_definition], data_types: [], flow_types: []) + end + + it 'returns an error without calling Velorum' do + expect(service_response).to be_error + expect(service_response.payload[:error_code]).to eq(:no_definitions) + expect(client).not_to have_received(:prompt) + end + end + + context 'when the runtime only has flow types' do + let(:runtime) do + instance_double(Runtime, id: 9, function_definitions: [], data_types: [], flow_types: [flow_type]) + end + + it 'returns an error without calling Velorum' do + expect(service_response).to be_error + expect(service_response.payload[:error_code]).to eq(:no_definitions) + expect(client).not_to have_received(:prompt) + end + end + context 'when Velorum returns a gRPC error' do before do allow(client).to receive(:prompt).and_raise(