feat: add Captain::Scenario Model and API [CW-4597] (#11907)
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
@@ -57,6 +57,7 @@ Rails.application.routes.draw do
|
|||||||
post :playground
|
post :playground
|
||||||
end
|
end
|
||||||
resources :inboxes, only: [:index, :create, :destroy], param: :inbox_id
|
resources :inboxes, only: [:index, :create, :destroy], param: :inbox_id
|
||||||
|
resources :scenarios
|
||||||
end
|
end
|
||||||
resources :assistant_responses
|
resources :assistant_responses
|
||||||
resources :bulk_actions, only: [:create]
|
resources :bulk_actions, only: [:create]
|
||||||
|
|||||||
18
db/migrate/20250710145708_create_captain_scenarios.rb
Normal file
18
db/migrate/20250710145708_create_captain_scenarios.rb
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
class CreateCaptainScenarios < ActiveRecord::Migration[7.1]
|
||||||
|
def change
|
||||||
|
create_table :captain_scenarios do |t|
|
||||||
|
t.string :title
|
||||||
|
t.text :description
|
||||||
|
t.text :instruction
|
||||||
|
t.jsonb :tools, default: []
|
||||||
|
t.boolean :enabled, default: true, null: false
|
||||||
|
t.references :assistant, null: false
|
||||||
|
t.references :account, null: false
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :captain_scenarios, :enabled
|
||||||
|
add_index :captain_scenarios, [:assistant_id, :enabled]
|
||||||
|
end
|
||||||
|
end
|
||||||
18
db/schema.rb
18
db/schema.rb
@@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.1].define(version: 2025_06_27_195529) do
|
ActiveRecord::Schema[7.1].define(version: 2025_07_10_145708) do
|
||||||
# These extensions should be enabled to support this database
|
# These extensions should be enabled to support this database
|
||||||
enable_extension "pg_stat_statements"
|
enable_extension "pg_stat_statements"
|
||||||
enable_extension "pg_trgm"
|
enable_extension "pg_trgm"
|
||||||
@@ -305,6 +305,22 @@ ActiveRecord::Schema[7.1].define(version: 2025_06_27_195529) do
|
|||||||
t.index ["inbox_id"], name: "index_captain_inboxes_on_inbox_id"
|
t.index ["inbox_id"], name: "index_captain_inboxes_on_inbox_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "captain_scenarios", force: :cascade do |t|
|
||||||
|
t.string "title"
|
||||||
|
t.text "description"
|
||||||
|
t.text "instruction"
|
||||||
|
t.jsonb "tools", default: []
|
||||||
|
t.boolean "enabled", default: true, null: false
|
||||||
|
t.bigint "assistant_id", null: false
|
||||||
|
t.bigint "account_id", null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["account_id"], name: "index_captain_scenarios_on_account_id"
|
||||||
|
t.index ["assistant_id", "enabled"], name: "index_captain_scenarios_on_assistant_id_and_enabled"
|
||||||
|
t.index ["assistant_id"], name: "index_captain_scenarios_on_assistant_id"
|
||||||
|
t.index ["enabled"], name: "index_captain_scenarios_on_enabled"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "categories", force: :cascade do |t|
|
create_table "categories", force: :cascade do |t|
|
||||||
t.integer "account_id", null: false
|
t.integer "account_id", null: false
|
||||||
t.integer "portal_id", null: false
|
t.integer "portal_id", null: false
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
class Api::V1::Accounts::Captain::ScenariosController < Api::V1::Accounts::BaseController
|
||||||
|
before_action :current_account
|
||||||
|
before_action -> { check_authorization(Captain::Scenario) }
|
||||||
|
before_action :set_assistant
|
||||||
|
before_action :set_scenario, only: [:show, :update, :destroy]
|
||||||
|
|
||||||
|
def index
|
||||||
|
@scenarios = assistant_scenarios.enabled
|
||||||
|
end
|
||||||
|
|
||||||
|
def show; end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@scenario = assistant_scenarios.create!(scenario_params.merge(account: Current.account))
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@scenario.update!(scenario_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@scenario.destroy
|
||||||
|
head :no_content
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_assistant
|
||||||
|
@assistant = account_assistants.find(params[:assistant_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_assistants
|
||||||
|
@account_assistants ||= Current.account.captain_assistants
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_scenario
|
||||||
|
@scenario = assistant_scenarios.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def assistant_scenarios
|
||||||
|
@assistant.scenarios
|
||||||
|
end
|
||||||
|
|
||||||
|
def scenario_params
|
||||||
|
params.require(:scenario).permit(:title, :description, :instruction, :enabled, tools: [])
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -30,6 +30,7 @@ class Captain::Assistant < ApplicationRecord
|
|||||||
through: :captain_inboxes
|
through: :captain_inboxes
|
||||||
has_many :messages, as: :sender, dependent: :nullify
|
has_many :messages, as: :sender, dependent: :nullify
|
||||||
has_many :copilot_threads, dependent: :destroy_async
|
has_many :copilot_threads, dependent: :destroy_async
|
||||||
|
has_many :scenarios, class_name: 'Captain::Scenario', dependent: :destroy_async
|
||||||
|
|
||||||
validates :name, presence: true
|
validates :name, presence: true
|
||||||
validates :description, presence: true
|
validates :description, presence: true
|
||||||
|
|||||||
44
enterprise/app/models/captain/scenario.rb
Normal file
44
enterprise/app/models/captain/scenario.rb
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: captain_scenarios
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# description :text
|
||||||
|
# enabled :boolean default(TRUE), not null
|
||||||
|
# instruction :text
|
||||||
|
# title :string
|
||||||
|
# tools :jsonb
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# account_id :bigint not null
|
||||||
|
# assistant_id :bigint not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_captain_scenarios_on_account_id (account_id)
|
||||||
|
# index_captain_scenarios_on_assistant_id (assistant_id)
|
||||||
|
# index_captain_scenarios_on_assistant_id_and_enabled (assistant_id,enabled)
|
||||||
|
# index_captain_scenarios_on_enabled (enabled)
|
||||||
|
#
|
||||||
|
class Captain::Scenario < ApplicationRecord
|
||||||
|
self.table_name = 'captain_scenarios'
|
||||||
|
|
||||||
|
belongs_to :assistant, class_name: 'Captain::Assistant'
|
||||||
|
belongs_to :account
|
||||||
|
|
||||||
|
validates :title, presence: true
|
||||||
|
validates :description, presence: true
|
||||||
|
validates :instruction, presence: true
|
||||||
|
validates :assistant_id, presence: true
|
||||||
|
validates :account_id, presence: true
|
||||||
|
|
||||||
|
scope :enabled, -> { where(enabled: true) }
|
||||||
|
|
||||||
|
before_save :populate_tools
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def populate_tools
|
||||||
|
# TODO: Implement tools population logic
|
||||||
|
end
|
||||||
|
end
|
||||||
21
enterprise/app/policies/captain/scenario_policy.rb
Normal file
21
enterprise/app/policies/captain/scenario_policy.rb
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
class Captain::ScenarioPolicy < ApplicationPolicy
|
||||||
|
def index?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def show?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def create?
|
||||||
|
@account_user.administrator?
|
||||||
|
end
|
||||||
|
|
||||||
|
def update?
|
||||||
|
@account_user.administrator?
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy?
|
||||||
|
@account_user.administrator?
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
json.partial! 'api/v1/models/captain/scenario', scenario: @scenario
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
json.data do
|
||||||
|
json.array! @scenarios do |scenario|
|
||||||
|
json.partial! 'api/v1/models/captain/scenario', scenario: scenario
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
json.partial! 'api/v1/models/captain/scenario', scenario: @scenario
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
json.partial! 'api/v1/models/captain/scenario', scenario: @scenario
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
json.id scenario.id
|
||||||
|
json.title scenario.title
|
||||||
|
json.description scenario.description
|
||||||
|
json.instruction scenario.instruction
|
||||||
|
json.tools scenario.tools
|
||||||
|
json.enabled scenario.enabled
|
||||||
|
json.assistant_id scenario.assistant_id
|
||||||
|
json.account_id scenario.account_id
|
||||||
|
json.created_at scenario.created_at
|
||||||
|
json.updated_at scenario.updated_at
|
||||||
|
if scenario.assistant.present?
|
||||||
|
json.assistant do
|
||||||
|
json.id scenario.assistant.id
|
||||||
|
json.name scenario.assistant.name
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'Api::V1::Accounts::Captain::Scenarios', type: :request do
|
||||||
|
let(:account) { create(:account) }
|
||||||
|
let(:admin) { create(:user, account: account, role: :administrator) }
|
||||||
|
let(:agent) { create(:user, account: account, role: :agent) }
|
||||||
|
let(:assistant) { create(:captain_assistant, account: account) }
|
||||||
|
|
||||||
|
def json_response
|
||||||
|
JSON.parse(response.body, symbolize_names: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /api/v1/accounts/{account.id}/captain/assistants/{assistant.id}/scenarios' do
|
||||||
|
context 'when it is an un-authenticated user' do
|
||||||
|
it 'returns unauthorized status' do
|
||||||
|
get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios"
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an agent' do
|
||||||
|
it 'returns success status' do
|
||||||
|
create_list(:captain_scenario, 3, assistant: assistant, account: account)
|
||||||
|
get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios",
|
||||||
|
headers: agent.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(json_response[:data].length).to eq(3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an admin' do
|
||||||
|
it 'returns success status and scenarios' do
|
||||||
|
create_list(:captain_scenario, 5, assistant: assistant, account: account)
|
||||||
|
get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios",
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(json_response[:data].length).to eq(5)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns only enabled scenarios' do
|
||||||
|
create(:captain_scenario, assistant: assistant, account: account, enabled: true)
|
||||||
|
create(:captain_scenario, assistant: assistant, account: account, enabled: false)
|
||||||
|
get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios",
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(json_response[:data].length).to eq(1)
|
||||||
|
expect(json_response[:data].first[:enabled]).to be(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /api/v1/accounts/{account.id}/captain/assistants/{assistant.id}/scenarios/{id}' do
|
||||||
|
let(:scenario) { create(:captain_scenario, assistant: assistant, account: account) }
|
||||||
|
|
||||||
|
context 'when it is an un-authenticated user' do
|
||||||
|
it 'returns unauthorized status' do
|
||||||
|
get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.id}"
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an agent' do
|
||||||
|
it 'returns success status and scenario' do
|
||||||
|
get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.id}",
|
||||||
|
headers: agent.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(json_response[:id]).to eq(scenario.id)
|
||||||
|
expect(json_response[:title]).to eq(scenario.title)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when scenario does not exist' do
|
||||||
|
it 'returns not found status' do
|
||||||
|
get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/999999",
|
||||||
|
headers: agent.create_new_auth_token
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST /api/v1/accounts/{account.id}/captain/assistants/{assistant.id}/scenarios' do
|
||||||
|
let(:valid_attributes) do
|
||||||
|
{
|
||||||
|
scenario: {
|
||||||
|
title: 'Test Scenario',
|
||||||
|
description: 'Test description',
|
||||||
|
instruction: 'Test instruction',
|
||||||
|
enabled: true,
|
||||||
|
tools: %w[tool1 tool2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an un-authenticated user' do
|
||||||
|
it 'returns unauthorized status' do
|
||||||
|
post "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios",
|
||||||
|
params: valid_attributes
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an agent' do
|
||||||
|
it 'returns unauthorized status' do
|
||||||
|
post "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios",
|
||||||
|
params: valid_attributes,
|
||||||
|
headers: agent.create_new_auth_token
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an admin' do
|
||||||
|
it 'creates a new scenario and returns success status' do
|
||||||
|
expect do
|
||||||
|
post "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios",
|
||||||
|
params: valid_attributes,
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
end.to change(Captain::Scenario, :count).by(1)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(json_response[:title]).to eq('Test Scenario')
|
||||||
|
expect(json_response[:description]).to eq('Test description')
|
||||||
|
expect(json_response[:enabled]).to be(true)
|
||||||
|
expect(json_response[:assistant_id]).to eq(assistant.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with invalid parameters' do
|
||||||
|
let(:invalid_attributes) do
|
||||||
|
{
|
||||||
|
scenario: {
|
||||||
|
title: '',
|
||||||
|
description: '',
|
||||||
|
instruction: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns unprocessable entity status' do
|
||||||
|
post "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios",
|
||||||
|
params: invalid_attributes,
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'PATCH /api/v1/accounts/{account.id}/captain/assistants/{assistant.id}/scenarios/{id}' do
|
||||||
|
let(:scenario) { create(:captain_scenario, assistant: assistant, account: account) }
|
||||||
|
let(:update_attributes) do
|
||||||
|
{
|
||||||
|
scenario: {
|
||||||
|
title: 'Updated Scenario Title',
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an un-authenticated user' do
|
||||||
|
it 'returns unauthorized status' do
|
||||||
|
patch "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.id}",
|
||||||
|
params: update_attributes
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an agent' do
|
||||||
|
it 'returns unauthorized status' do
|
||||||
|
patch "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.id}",
|
||||||
|
params: update_attributes,
|
||||||
|
headers: agent.create_new_auth_token
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an admin' do
|
||||||
|
it 'updates the scenario and returns success status' do
|
||||||
|
patch "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.id}",
|
||||||
|
params: update_attributes,
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(json_response[:title]).to eq('Updated Scenario Title')
|
||||||
|
expect(json_response[:enabled]).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with invalid parameters' do
|
||||||
|
let(:invalid_attributes) do
|
||||||
|
{
|
||||||
|
scenario: {
|
||||||
|
title: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns unprocessable entity status' do
|
||||||
|
patch "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.id}",
|
||||||
|
params: invalid_attributes,
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'DELETE /api/v1/accounts/{account.id}/captain/assistants/{assistant.id}/scenarios/{id}' do
|
||||||
|
let!(:scenario) { create(:captain_scenario, assistant: assistant, account: account) }
|
||||||
|
|
||||||
|
context 'when it is an un-authenticated user' do
|
||||||
|
it 'returns unauthorized status' do
|
||||||
|
delete "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.id}"
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an agent' do
|
||||||
|
it 'returns unauthorized status' do
|
||||||
|
delete "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.id}",
|
||||||
|
headers: agent.create_new_auth_token
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an admin' do
|
||||||
|
it 'deletes the scenario and returns no content status' do
|
||||||
|
expect do
|
||||||
|
delete "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/#{scenario.id}",
|
||||||
|
headers: admin.create_new_auth_token
|
||||||
|
end.to change(Captain::Scenario, :count).by(-1)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:no_content)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when scenario does not exist' do
|
||||||
|
it 'returns not found status' do
|
||||||
|
delete "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/scenarios/999999",
|
||||||
|
headers: admin.create_new_auth_token
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
63
spec/enterprise/models/captain/scenario_spec.rb
Normal file
63
spec/enterprise/models/captain/scenario_spec.rb
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Captain::Scenario, type: :model do
|
||||||
|
describe 'associations' do
|
||||||
|
it { is_expected.to belong_to(:assistant).class_name('Captain::Assistant') }
|
||||||
|
it { is_expected.to belong_to(:account) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'validations' do
|
||||||
|
it { is_expected.to validate_presence_of(:title) }
|
||||||
|
it { is_expected.to validate_presence_of(:description) }
|
||||||
|
it { is_expected.to validate_presence_of(:instruction) }
|
||||||
|
it { is_expected.to validate_presence_of(:assistant_id) }
|
||||||
|
it { is_expected.to validate_presence_of(:account_id) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'scopes' do
|
||||||
|
let(:account) { create(:account) }
|
||||||
|
let(:assistant) { create(:captain_assistant, account: account) }
|
||||||
|
|
||||||
|
describe '.enabled' do
|
||||||
|
it 'returns only enabled scenarios' do
|
||||||
|
enabled_scenario = create(:captain_scenario, assistant: assistant, account: account, enabled: true)
|
||||||
|
disabled_scenario = create(:captain_scenario, assistant: assistant, account: account, enabled: false)
|
||||||
|
|
||||||
|
expect(described_class.enabled).to include(enabled_scenario)
|
||||||
|
expect(described_class.enabled).not_to include(disabled_scenario)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'callbacks' do
|
||||||
|
let(:account) { create(:account) }
|
||||||
|
let(:assistant) { create(:captain_assistant, account: account) }
|
||||||
|
|
||||||
|
describe 'before_save :populate_tools' do
|
||||||
|
it 'calls populate_tools before saving' do
|
||||||
|
scenario = build(:captain_scenario, assistant: assistant, account: account)
|
||||||
|
expect(scenario).to receive(:populate_tools)
|
||||||
|
scenario.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'factory' do
|
||||||
|
it 'creates a valid scenario with associations' do
|
||||||
|
account = create(:account)
|
||||||
|
assistant = create(:captain_assistant, account: account)
|
||||||
|
scenario = build(:captain_scenario, assistant: assistant, account: account)
|
||||||
|
expect(scenario).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a scenario with all required attributes' do
|
||||||
|
scenario = create(:captain_scenario)
|
||||||
|
expect(scenario.title).to be_present
|
||||||
|
expect(scenario.description).to be_present
|
||||||
|
expect(scenario.instruction).to be_present
|
||||||
|
expect(scenario.enabled).to be true
|
||||||
|
expect(scenario.assistant).to be_present
|
||||||
|
expect(scenario.account).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
11
spec/factories/captain/scenario.rb
Normal file
11
spec/factories/captain/scenario.rb
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
FactoryBot.define do
|
||||||
|
factory :captain_scenario, class: 'Captain::Scenario' do
|
||||||
|
sequence(:title) { |n| "Scenario #{n}" }
|
||||||
|
description { 'Test scenario description' }
|
||||||
|
instruction { 'Test scenario instruction for the assistant to follow' }
|
||||||
|
tools { [] }
|
||||||
|
enabled { true }
|
||||||
|
association :assistant, factory: :captain_assistant
|
||||||
|
association :account
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user