feat(ee): Add Captain features (#10665)

Migration Guide: https://chwt.app/v4/migration

This PR imports all the work related to Captain into the EE codebase. Captain represents the AI-based features in Chatwoot and includes the following key components:

- Assistant: An assistant has a persona, the product it would be trained on. At the moment, the data at which it is trained is from websites. Future integrations on Notion documents, PDF etc. This PR enables connecting an assistant to an inbox. The assistant would run the conversation every time before transferring it to an agent.
- Copilot for Agents: When an agent is supporting a customer, we will be able to offer additional help to lookup some data or fetch information from integrations etc via copilot.
- Conversation FAQ generator: When a conversation is resolved, the Captain integration would identify questions which were not in the knowledge base.
- CRM memory: Learns from the conversations and identifies important information about the contact.

---------

Co-authored-by: Vishnu Narayanan <vishnu@chatwoot.com>
Co-authored-by: Sojan <sojan@pepalo.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
Pranav
2025-01-14 16:15:47 -08:00
committed by GitHub
parent 7b31b5ad6e
commit d070743383
184 changed files with 6666 additions and 2242 deletions

View File

@@ -0,0 +1,223 @@
require 'rails_helper'
RSpec.describe 'Api::V1::Accounts::Captain::AssistantResponses', type: :request do
let(:account) { create(:account) }
let(:assistant) { create(:captain_assistant, account: account) }
let(:document) { create(:captain_document, assistant: assistant, account: account) }
let(:admin) { create(:user, account: account, role: :administrator) }
let(:agent) { create(:user, account: account, role: :agent) }
let(:another_assistant) { create(:captain_assistant, account: account) }
let(:another_document) { create(:captain_document, account: account, assistant: assistant) }
def json_response
JSON.parse(response.body, symbolize_names: true)
end
describe 'GET /api/v1/accounts/:account_id/captain/assistant_responses' do
context 'when no filters are applied' do
before do
create_list(:captain_assistant_response, 30,
account: account,
assistant: assistant,
document: document)
end
it 'returns first page of responses with default pagination' do
get "/api/v1/accounts/#{account.id}/captain/assistant_responses",
headers: agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:ok)
expect(json_response[:payload].length).to eq(25)
end
it 'returns second page of responses' do
get "/api/v1/accounts/#{account.id}/captain/assistant_responses",
params: { page: 2 },
headers: agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:ok)
expect(json_response[:payload].length).to eq(5)
expect(json_response[:meta]).to eq({ page: 2, total_count: 30 })
end
end
context 'when filtering by assistant_id' do
before do
create_list(:captain_assistant_response, 3,
account: account,
assistant: assistant,
document: document)
create_list(:captain_assistant_response, 2,
account: account,
assistant: another_assistant,
document: document)
end
it 'returns only responses for the specified assistant' do
get "/api/v1/accounts/#{account.id}/captain/assistant_responses",
params: { assistant_id: assistant.id },
headers: agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:ok)
expect(json_response[:payload].length).to eq(3)
expect(json_response[:payload][0][:assistant][:id]).to eq(assistant.id)
end
end
context 'when filtering by document_id' do
before do
create_list(:captain_assistant_response, 3,
account: account,
assistant: assistant,
document: document)
create_list(:captain_assistant_response, 2,
account: account,
assistant: assistant,
document: another_document)
end
it 'returns only responses for the specified document' do
get "/api/v1/accounts/#{account.id}/captain/assistant_responses",
params: { document_id: document.id },
headers: agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:ok)
expect(json_response[:payload].length).to eq(3)
expect(json_response[:payload][0][:document][:id]).to eq(document.id)
end
end
end
describe 'GET /api/v1/accounts/:account_id/captain/assistant_responses/:id' do
let!(:response_record) { create(:captain_assistant_response, assistant: assistant, account: account) }
it 'returns the requested response if the user is agent or admin' do
get "/api/v1/accounts/#{account.id}/captain/assistant_responses/#{response_record.id}",
headers: agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:ok)
expect(json_response[:id]).to eq(response_record.id)
expect(json_response[:question]).to eq(response_record.question)
expect(json_response[:answer]).to eq(response_record.answer)
end
end
describe 'POST /api/v1/accounts/:account_id/captain/assistant_responses' do
let(:valid_params) do
{
assistant_response: {
question: 'Test question?',
answer: 'Test answer',
assistant_id: assistant.id
}
}
end
it 'creates a new response if the user is an admin' do
expect do
post "/api/v1/accounts/#{account.id}/captain/assistant_responses",
params: valid_params,
headers: admin.create_new_auth_token,
as: :json
end.to change(Captain::AssistantResponse, :count).by(1)
expect(response).to have_http_status(:success)
expect(json_response[:question]).to eq('Test question?')
expect(json_response[:answer]).to eq('Test answer')
end
context 'with invalid params' do
let(:invalid_params) do
{
assistant_response: {
question: 'Test',
answer: 'Test'
}
}
end
it 'returns unprocessable entity status' do
post "/api/v1/accounts/#{account.id}/captain/assistant_responses",
params: invalid_params,
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:unprocessable_entity)
end
end
end
describe 'PATCH /api/v1/accounts/:account_id/captain/assistant_responses/:id' do
let!(:response_record) { create(:captain_assistant_response, assistant: assistant) }
let(:update_params) do
{
assistant_response: {
question: 'Updated question?',
answer: 'Updated answer'
}
}
end
it 'updates the response if the user is an admin' do
patch "/api/v1/accounts/#{account.id}/captain/assistant_responses/#{response_record.id}",
params: update_params,
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:ok)
expect(json_response[:question]).to eq('Updated question?')
expect(json_response[:answer]).to eq('Updated answer')
end
context 'with invalid params' do
let(:invalid_params) do
{
assistant_response: {
question: '',
answer: ''
}
}
end
it 'returns unprocessable entity status' do
patch "/api/v1/accounts/#{account.id}/captain/assistant_responses/#{response_record.id}",
params: invalid_params,
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:unprocessable_entity)
end
end
end
describe 'DELETE /api/v1/accounts/:account_id/captain/assistant_responses/:id' do
let!(:response_record) { create(:captain_assistant_response, assistant: assistant) }
it 'deletes the response' do
expect do
delete "/api/v1/accounts/#{account.id}/captain/assistant_responses/#{response_record.id}",
headers: admin.create_new_auth_token,
as: :json
end.to change(Captain::AssistantResponse, :count).by(-1)
expect(response).to have_http_status(:no_content)
end
context 'with invalid id' do
it 'returns not found' do
delete "/api/v1/accounts/#{account.id}/captain/assistant_responses/0",
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:not_found)
end
end
end
end

View File

@@ -0,0 +1,178 @@
require 'rails_helper'
RSpec.describe 'Api::V1::Accounts::Captain::Assistants', type: :request do
let(:account) { create(:account) }
let(:admin) { create(:user, account: account, role: :administrator) }
let(:agent) { create(:user, account: account, role: :agent) }
def json_response
JSON.parse(response.body, symbolize_names: true)
end
describe 'GET /api/v1/accounts/{account.id}/captain/assistants' do
context 'when it is an un-authenticated user' do
it 'does not fetch assistants' do
get "/api/v1/accounts/#{account.id}/captain/assistants",
as: :json
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an agent' do
it 'fetches assistants for the account' do
create_list(:captain_assistant, 3, account: account)
get "/api/v1/accounts/#{account.id}/captain/assistants",
headers: agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
expect(json_response[:payload].length).to eq(3)
expect(json_response[:meta]).to eq(
{ total_count: 3, page: 1 }
)
end
end
end
describe 'GET /api/v1/accounts/{account.id}/captain/assistants/{id}' do
let(:assistant) { create(:captain_assistant, account: account) }
context 'when it is an un-authenticated user' do
it 'does not fetch the assistant' do
get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}",
as: :json
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an agent' do
it 'fetches the assistant' do
get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}",
headers: agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
expect(json_response[:id]).to eq(assistant.id)
end
end
end
describe 'POST /api/v1/accounts/{account.id}/captain/assistants' do
let(:valid_attributes) do
{
assistant: {
name: 'New Assistant',
description: 'Assistant Description'
}
}
end
context 'when it is an un-authenticated user' do
it 'does not create an assistant' do
post "/api/v1/accounts/#{account.id}/captain/assistants",
params: valid_attributes,
as: :json
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an agent' do
it 'does not create an assistant' do
post "/api/v1/accounts/#{account.id}/captain/assistants",
params: valid_attributes,
headers: agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an admin' do
it 'creates a new assistant' do
expect do
post "/api/v1/accounts/#{account.id}/captain/assistants",
params: valid_attributes,
headers: admin.create_new_auth_token,
as: :json
end.to change(Captain::Assistant, :count).by(1)
expect(json_response[:name]).to eq('New Assistant')
expect(response).to have_http_status(:success)
end
end
end
describe 'PATCH /api/v1/accounts/{account.id}/captain/assistants/{id}' do
let(:assistant) { create(:captain_assistant, account: account) }
let(:update_attributes) do
{
assistant: {
name: 'Updated Assistant'
}
}
end
context 'when it is an un-authenticated user' do
it 'does not update the assistant' do
patch "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}",
params: update_attributes,
as: :json
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an agent' do
it 'does not update the assistant' do
patch "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}",
params: update_attributes,
headers: agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an admin' do
it 'updates the assistant' do
patch "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}",
params: update_attributes,
headers: admin.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
expect(json_response[:name]).to eq('Updated Assistant')
end
end
end
describe 'DELETE /api/v1/accounts/{account.id}/captain/assistants/{id}' do
let!(:assistant) { create(:captain_assistant, account: account) }
context 'when it is an un-authenticated user' do
it 'does not delete the assistant' do
delete "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}",
as: :json
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an agent' do
it 'delete the assistant' do
delete "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}",
headers: agent.create_new_auth_token,
as: :json
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an admin' do
it 'deletes the assistant' do
expect do
delete "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}",
headers: admin.create_new_auth_token,
as: :json
end.to change(Captain::Assistant, :count).by(-1)
expect(response).to have_http_status(:no_content)
end
end
end
end

View File

@@ -0,0 +1,271 @@
require 'rails_helper'
RSpec.describe 'Api::V1::Accounts::Captain::Documents', 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) }
let(:assistant2) { create(:captain_assistant, account: account) }
let(:document) { create(:captain_document, assistant: assistant, account: account) }
def json_response
JSON.parse(response.body, symbolize_names: true)
end
describe 'GET /api/v1/accounts/:account_id/captain/documents' do
context 'when it is an un-authenticated user' do
before do
get "/api/v1/accounts/#{account.id}/captain/documents"
end
it 'returns unauthorized status' do
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an agent' do
context 'when no filters are applied' do
before do
create_list(:captain_document, 30, assistant: assistant, account: account)
end
it 'returns the first page of documents' do
get "/api/v1/accounts/#{account.id}/captain/documents", headers: agent.create_new_auth_token, as: :json
expect(response).to have_http_status(:ok)
expect(json_response[:payload].length).to eq(25)
expect(json_response[:meta]).to eq({ page: 1, total_count: 30 })
end
it 'returns the second page of documents' do
get "/api/v1/accounts/#{account.id}/captain/documents",
params: { page: 2 },
headers: agent.create_new_auth_token, as: :json
expect(response).to have_http_status(:ok)
expect(json_response[:payload].length).to eq(5)
expect(json_response[:meta]).to eq({ page: 2, total_count: 30 })
end
end
context 'when filtering by assistant_id' do
before do
create_list(:captain_document, 3, assistant: assistant, account: account)
create_list(:captain_document, 2, assistant: assistant2, account: account)
end
it 'returns only documents for the specified assistant' do
get "/api/v1/accounts/#{account.id}/captain/documents",
params: { assistant_id: assistant.id },
headers: agent.create_new_auth_token, as: :json
expect(response).to have_http_status(:ok)
expect(json_response[:payload].length).to eq(3)
expect(json_response[:payload][0][:assistant][:id]).to eq(assistant.id)
end
it 'returns empty array when assistant has no documents' do
new_assistant = create(:captain_assistant, account: account)
get "/api/v1/accounts/#{account.id}/captain/documents",
params: { assistant_id: new_assistant.id },
headers: agent.create_new_auth_token, as: :json
expect(response).to have_http_status(:ok)
expect(json_response[:payload]).to be_empty
end
end
context 'when documents belong to different accounts' do
let(:other_account) { create(:account) }
before do
create_list(:captain_document, 3, assistant: assistant, account: account)
create_list(:captain_document, 2, account: other_account)
end
it 'only returns documents for the current account' do
get "/api/v1/accounts/#{account.id}/captain/documents",
headers: agent.create_new_auth_token, as: :json
expect(response).to have_http_status(:ok)
expect(json_response[:payload].length).to eq(3)
document_account_ids = json_response[:payload].pluck(:account_id).uniq
expect(document_account_ids).to eq([account.id])
end
end
context 'with pagination and assistant filter combined' do
before do
create_list(:captain_document, 30, assistant: assistant, account: account)
create_list(:captain_document, 10, assistant: assistant2, account: account)
end
it 'returns paginated results for specific assistant' do
get "/api/v1/accounts/#{account.id}/captain/documents",
params: { assistant_id: assistant.id, page: 2 },
headers: agent.create_new_auth_token, as: :json
expect(response).to have_http_status(:ok)
expect(json_response[:payload].length).to eq(5)
expect(json_response[:payload][0][:assistant][:id]).to eq(assistant.id)
expect(json_response[:meta]).to eq({ page: 2, total_count: 30 })
end
end
end
end
describe 'GET /api/v1/accounts/:account_id/captain/documents/:id' do
context 'when it is an un-authenticated user' do
before do
get "/api/v1/accounts/#{account.id}/captain/documents/#{document.id}"
end
it 'returns unauthorized status' do
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an agent' do
before do
get "/api/v1/accounts/#{account.id}/captain/documents/#{document.id}",
headers: agent.create_new_auth_token, as: :json
end
it 'returns success status' do
expect(response).to have_http_status(:success)
end
it 'returns the requested document' do
expect(json_response[:id]).to eq(document.id)
expect(json_response[:name]).to eq(document.name)
expect(json_response[:external_link]).to eq(document.external_link)
end
end
end
describe 'POST /api/v1/accounts/:account_id/captain/documents' do
let(:valid_attributes) do
{
document: {
name: 'Test Document',
external_link: 'https://example.com/doc',
assistant_id: assistant.id
}
}
end
let(:invalid_attributes) do
{
document: {
name: 'Test Document',
external_link: 'https://example.com/doc'
}
}
end
context 'when it is an un-authenticated user' do
before do
post "/api/v1/accounts/#{account.id}/captain/documents",
params: valid_attributes, as: :json
end
it 'returns unauthorized status' do
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an agent' do
it 'returns unauthorized' do
post "/api/v1/accounts/#{account.id}/captain/documents",
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
context 'with valid parameters' do
it 'creates a new document' do
expect do
post "/api/v1/accounts/#{account.id}/captain/documents",
params: valid_attributes,
headers: admin.create_new_auth_token
end.to change(Captain::Document, :count).by(1)
end
it 'returns success status and the created document' do
post "/api/v1/accounts/#{account.id}/captain/documents",
params: valid_attributes,
headers: admin.create_new_auth_token, as: :json
expect(response).to have_http_status(:success)
expect(json_response[:name]).to eq('Test Document')
expect(json_response[:external_link]).to eq('https://example.com/doc')
end
end
context 'with invalid parameters' do
before do
post "/api/v1/accounts/#{account.id}/captain/documents",
params: invalid_attributes,
headers: admin.create_new_auth_token
end
it 'returns unprocessable entity status' do
expect(response).to have_http_status(:unprocessable_entity)
end
end
end
end
describe 'DELETE /api/v1/accounts/:account_id/captain/documents/:id' do
context 'when it is an un-authenticated user' do
before do
delete "/api/v1/accounts/#{account.id}/captain/documents/#{document.id}"
end
it 'returns unauthorized status' do
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an agent' do
let!(:document_to_delete) { create(:captain_document, assistant: assistant) }
it 'deletes the document' do
delete "/api/v1/accounts/#{account.id}/captain/documents/#{document_to_delete.id}",
headers: agent.create_new_auth_token
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an admin' do
context 'when document exists' do
let!(:document_to_delete) { create(:captain_document, assistant: assistant) }
it 'deletes the document' do
expect do
delete "/api/v1/accounts/#{account.id}/captain/documents/#{document_to_delete.id}",
headers: admin.create_new_auth_token
end.to change(Captain::Document, :count).by(-1)
end
it 'returns no content status' do
delete "/api/v1/accounts/#{account.id}/captain/documents/#{document_to_delete.id}",
headers: admin.create_new_auth_token
expect(response).to have_http_status(:no_content)
end
end
context 'when document does not exist' do
before do
delete "/api/v1/accounts/#{account.id}/captain/documents/invalid_id",
headers: admin.create_new_auth_token
end
it 'returns not found status' do
expect(response).to have_http_status(:not_found)
end
end
end
end
end

View File

@@ -0,0 +1,119 @@
require 'rails_helper'
RSpec.describe 'Api::V1::Accounts::Captain::Inboxes', type: :request do
let(:account) { create(:account) }
let(:assistant) { create(:captain_assistant, account: account) }
let(:inbox) { create(:inbox, account: account) }
let(:inbox2) { create(:inbox, account: account) }
let!(:captain_inbox) { create(:captain_inbox, captain_assistant: assistant, inbox: inbox) }
let(:admin) { create(:user, account: account, role: :administrator) }
let(:agent) { create(:user, account: account, role: :agent) }
def json_response
JSON.parse(response.body, symbolize_names: true)
end
describe 'GET /api/v1/accounts/:account_id/captain/assistants/:assistant_id/inboxes' do
context 'when user is authorized' do
it 'returns a list of inboxes for the assistant' do
get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/inboxes",
headers: agent.create_new_auth_token
expect(response).to have_http_status(:ok)
expect(json_response[:payload].first[:id]).to eq(captain_inbox.inbox.id)
end
end
context 'when user is unauthorized' do
it 'returns unauthorized status' do
get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/inboxes"
expect(response).to have_http_status(:unauthorized)
end
end
context 'when assistant does not exist' do
it 'returns not found status' do
get "/api/v1/accounts/#{account.id}/captain/assistants/999999/inboxes",
headers: agent.create_new_auth_token
expect(response).to have_http_status(:not_found)
end
end
end
describe 'POST /api/v1/accounts/:account/captain/assistants/:assistant_id/inboxes' do
let(:valid_params) do
{
inbox: {
inbox_id: inbox2.id
}
}
end
context 'when user is authorized' do
it 'creates a new captain inbox' do
expect do
post "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/inboxes",
params: valid_params,
headers: admin.create_new_auth_token
end.to change(CaptainInbox, :count).by(1)
expect(response).to have_http_status(:success)
expect(json_response[:id]).to eq(inbox2.id)
end
context 'when inbox does not exist' do
it 'returns not found status' do
post "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/inboxes",
params: { inbox: { inbox_id: 999_999 } },
headers: admin.create_new_auth_token
expect(response).to have_http_status(:not_found)
end
end
context 'when params are invalid' do
it 'returns unprocessable entity status' do
post "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/inboxes",
params: {},
headers: admin.create_new_auth_token
expect(response).to have_http_status(:unprocessable_entity)
end
end
end
context 'when user is agent' do
it 'returns unauthorized status' do
post "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/inboxes",
params: valid_params,
headers: agent.create_new_auth_token
expect(response).to have_http_status(:unauthorized)
end
end
end
describe 'DELETE /api/v1/accounts/captain/assistants/:assistant_id/inboxes/:inbox_id' do
context 'when user is authorized' do
it 'deletes the captain inbox' do
expect do
delete "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/inboxes/#{inbox.id}",
headers: admin.create_new_auth_token
end.to change(CaptainInbox, :count).by(-1)
expect(response).to have_http_status(:no_content)
end
context 'when captain inbox does not exist' do
it 'returns not found status' do
delete "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/inboxes/999999",
headers: admin.create_new_auth_token
expect(response).to have_http_status(:not_found)
end
end
end
end
end

View File

@@ -1,133 +0,0 @@
require 'rails_helper'
RSpec.describe 'Response Sources API', type: :request do
let!(:account) { create(:account) }
let!(:admin) { create(:user, account: account, role: :administrator) }
before do
skip_unless_response_bot_enabled_test_environment
end
describe 'POST /api/v1/accounts/{account.id}/response_sources/parse' do
let(:valid_params) do
{
link: 'http://test.test'
}
end
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
post "/api/v1/accounts/#{account.id}/response_sources/parse", params: valid_params
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated user' do
it 'returns links in the webpage' do
crawler = double
allow(PageCrawlerService).to receive(:new).and_return(crawler)
allow(crawler).to receive(:page_links).and_return(['http://test.test'])
post "/api/v1/accounts/#{account.id}/response_sources/parse", headers: admin.create_new_auth_token,
params: valid_params
expect(response).to have_http_status(:success)
expect(response.parsed_body['links']).to eq(['http://test.test'])
end
end
end
describe 'POST /api/v1/accounts/{account.id}/response_sources' do
let(:valid_params) do
{
response_source: {
name: 'Test',
source_link: 'http://test.test',
response_documents_attributes: [
{ document_link: 'http://test1.test' },
{ document_link: 'http://test2.test' }
]
}
}
end
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
expect { post "/api/v1/accounts/#{account.id}/response_sources", params: valid_params }.not_to change(ResponseSource, :count)
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated user' do
it 'creates the response sources and documents' do
expect do
post "/api/v1/accounts/#{account.id}/response_sources", headers: admin.create_new_auth_token,
params: valid_params
end.to change(ResponseSource, :count).by(1)
expect(ResponseDocument.count).to eq(2)
expect(response).to have_http_status(:success)
end
end
end
describe 'POST /api/v1/accounts/{account.id}/response_sources/{response_source.id}/add_document' do
let!(:response_source) { create(:response_source, account: account) }
let(:valid_params) do
{ document_link: 'http://test.test' }
end
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
expect do
post "/api/v1/accounts/#{account.id}/response_sources/#{response_source.id}/add_document",
params: valid_params
end.not_to change(ResponseDocument, :count)
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated user' do
it 'creates the response sources and documents' do
expect do
post "/api/v1/accounts/#{account.id}/response_sources/#{response_source.id}/add_document", headers: admin.create_new_auth_token,
params: valid_params
end.to change(ResponseDocument, :count).by(1)
expect(response).to have_http_status(:success)
end
end
end
describe 'POST /api/v1/accounts/{account.id}/response_sources/{response_source.id}/remove_document' do
let!(:response_source) { create(:response_source, account: account) }
let!(:response_document) { response_source.response_documents.create!(document_link: 'http://test.test') }
let(:valid_params) do
{ document_id: response_document.id }
end
context 'when it is an unauthenticated user' do
it 'returns unauthorized' do
expect do
post "/api/v1/accounts/#{account.id}/response_sources/#{response_source.id}/remove_document",
params: valid_params
end.not_to change(ResponseDocument, :count)
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated user' do
it 'creates the response sources and documents' do
expect do
post "/api/v1/accounts/#{account.id}/response_sources/#{response_source.id}/remove_document", headers: admin.create_new_auth_token,
params: valid_params
end.to change(ResponseDocument, :count).by(-1)
expect(response).to have_http_status(:success)
expect { response_document.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end