Compare commits

...
Author SHA1 Message Date
Muhsin Keloth e7152ee04a feat: add activity messages for linear actions 2025-05-28 18:37:27 +05:30
15 changed files with 363 additions and 47 deletions
@@ -1,5 +1,5 @@
class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::BaseController class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::BaseController
before_action :fetch_conversation, only: [:link_issue, :linked_issues] before_action :fetch_conversation, only: [:create_issue, :link_issue, :unlink_issue, :linked_issues]
before_action :fetch_hook, only: [:destroy] before_action :fetch_hook, only: [:destroy]
def destroy def destroy
@@ -31,6 +31,7 @@ class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::Bas
if issue[:error] if issue[:error]
render json: { error: issue[:error] }, status: :unprocessable_entity render json: { error: issue[:error] }, status: :unprocessable_entity
else else
create_linear_activity_message(:issue_created, { id: issue[:data]['identifier'] })
render json: issue[:data], status: :ok render json: issue[:data], status: :ok
end end
end end
@@ -42,17 +43,20 @@ class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::Bas
if issue[:error] if issue[:error]
render json: { error: issue[:error] }, status: :unprocessable_entity render json: { error: issue[:error] }, status: :unprocessable_entity
else else
create_linear_activity_message(:issue_linked, { id: issue_id })
render json: issue[:data], status: :ok render json: issue[:data], status: :ok
end end
end end
def unlink_issue def unlink_issue
link_id = permitted_params[:link_id] link_id = permitted_params[:link_id]
issue_id = permitted_params[:issue_id]
issue = linear_processor_service.unlink_issue(link_id) issue = linear_processor_service.unlink_issue(link_id)
if issue[:error] if issue[:error]
render json: { error: issue[:error] }, status: :unprocessable_entity render json: { error: issue[:error] }, status: :unprocessable_entity
else else
create_linear_activity_message(:issue_unlinked, { id: issue_id })
render json: issue[:data], status: :ok render json: issue[:data], status: :ok
end end
end end
@@ -101,4 +105,17 @@ class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::Bas
def fetch_hook def fetch_hook
@hook = Integrations::Hook.where(account: Current.account).find_by(app_id: 'linear') @hook = Integrations::Hook.where(account: Current.account).find_by(app_id: 'linear')
end end
def create_linear_activity_message(action_type, issue_data = {})
return unless @conversation
case action_type
when :issue_created
@conversation.create_linear_issue_created_activity(issue_data)
when :issue_linked
@conversation.create_linear_issue_linked_activity(issue_data)
when :issue_unlinked
@conversation.create_linear_issue_unlinked_activity(issue_data)
end
end
end end
@@ -33,9 +33,11 @@ class LinearAPI extends ApiClient {
); );
} }
unlinkIssue(linkId) { unlinkIssue(linkId, issueIdentifier, conversationId) {
return axios.post(`${this.url}/unlink_issue`, { return axios.post(`${this.url}/unlink_issue`, {
link_id: linkId, link_id: linkId,
issue_id: issueIdentifier,
conversation_id: conversationId,
}); });
} }
@@ -91,6 +91,19 @@ describe('#linearAPI', () => {
issueData issueData
); );
}); });
it('creates a valid request with conversation_id', () => {
const issueData = {
title: 'New Issue',
description: 'Issue description',
conversation_id: 123,
};
LinearAPIClient.createIssue(issueData);
expect(axiosMock.post).toHaveBeenCalledWith(
'/api/v1/integrations/linear/create_issue',
issueData
);
});
}); });
describe('link_issue', () => { describe('link_issue', () => {
@@ -120,6 +133,18 @@ describe('#linearAPI', () => {
} }
); );
}); });
it('creates a valid request with title', () => {
LinearAPIClient.link_issue(1, 'ENG-123', 'Sample Issue');
expect(axiosMock.post).toHaveBeenCalledWith(
'/api/v1/integrations/linear/link_issue',
{
issue_id: 'ENG-123',
conversation_id: 1,
title: 'Sample Issue',
}
);
});
}); });
describe('getLinkedIssue', () => { describe('getLinkedIssue', () => {
@@ -164,12 +189,26 @@ describe('#linearAPI', () => {
window.axios = originalAxios; window.axios = originalAxios;
}); });
it('creates a valid request', () => { it('creates a valid request with link_id only', () => {
LinearAPIClient.unlinkIssue(1); LinearAPIClient.unlinkIssue('link123');
expect(axiosMock.post).toHaveBeenCalledWith( expect(axiosMock.post).toHaveBeenCalledWith(
'/api/v1/integrations/linear/unlink_issue', '/api/v1/integrations/linear/unlink_issue',
{ {
link_id: 1, link_id: 'link123',
issue_id: undefined,
conversation_id: undefined,
}
);
});
it('creates a valid request with all parameters', () => {
LinearAPIClient.unlinkIssue('link123', 'ENG-456', 789);
expect(axiosMock.post).toHaveBeenCalledWith(
'/api/v1/integrations/linear/unlink_issue',
{
link_id: 'link123',
issue_id: 'ENG-456',
conversation_id: 789,
} }
); );
}); });
@@ -183,13 +183,18 @@ const createIssue = async () => {
state_id: formState.stateId || undefined, state_id: formState.stateId || undefined,
priority: formState.priority || undefined, priority: formState.priority || undefined,
label_ids: formState.labelId ? [formState.labelId] : undefined, label_ids: formState.labelId ? [formState.labelId] : undefined,
conversation_id: props.conversationId,
}; };
try { try {
isCreating.value = true; isCreating.value = true;
const response = await LinearAPI.createIssue(payload); const response = await LinearAPI.createIssue(payload);
const { id: issueId } = response.data; const { identifier: issueIdentifier } = response.data;
await LinearAPI.link_issue(props.conversationId, issueId, props.title); await LinearAPI.link_issue(
props.conversationId,
issueIdentifier,
props.title
);
useAlert(t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.CREATE_SUCCESS')); useAlert(t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.CREATE_SUCCESS'));
useTrack(LINEAR_EVENTS.CREATE_ISSUE); useTrack(LINEAR_EVENTS.CREATE_ISSUE);
onClose(); onClose();
@@ -50,7 +50,7 @@ const priorityLabel = computed(() => {
}); });
const unlinkIssue = () => { const unlinkIssue = () => {
emit('unlinkIssue', props.linkId); emit('unlinkIssue', props.linkId, props.issue.identifier);
}; };
</script> </script>
@@ -63,7 +63,7 @@ const onSearch = async value => {
isFetching.value = true; isFetching.value = true;
const response = await LinearAPI.searchIssues(value); const response = await LinearAPI.searchIssues(value);
issues.value = response.data.map(issue => ({ issues.value = response.data.map(issue => ({
id: issue.id, id: issue.identifier,
name: `${issue.identifier} ${issue.title}`, name: `${issue.identifier} ${issue.title}`,
icon: 'status', icon: 'status',
iconColor: issue.state.color, iconColor: issue.state.color,
@@ -55,10 +55,10 @@ const loadLinkedIssue = async () => {
} }
}; };
const unlinkIssue = async linkId => { const unlinkIssue = async (linkId, issueIdentifier) => {
try { try {
isUnlinking.value = true; isUnlinking.value = true;
await LinearAPI.unlinkIssue(linkId); await LinearAPI.unlinkIssue(linkId, issueIdentifier, props.conversationId);
useTrack(LINEAR_EVENTS.UNLINK_ISSUE); useTrack(LINEAR_EVENTS.UNLINK_ISSUE);
linkedIssue.value = null; linkedIssue.value = null;
useAlert(t('INTEGRATION_SETTINGS.LINEAR.UNLINK.SUCCESS')); useAlert(t('INTEGRATION_SETTINGS.LINEAR.UNLINK.SUCCESS'));
@@ -5,6 +5,7 @@ module ActivityMessageHandler
include LabelActivityMessageHandler include LabelActivityMessageHandler
include SlaActivityMessageHandler include SlaActivityMessageHandler
include TeamActivityMessageHandler include TeamActivityMessageHandler
include LinearActivityMessageHandler
private private
@@ -0,0 +1,24 @@
module LinearActivityMessageHandler
extend ActiveSupport::Concern
def create_linear_issue_created_activity(issue_data)
return unless issue_data[:id]
content = I18n.t('conversations.activity.linear.issue_created', issue_id: issue_data[:id])
::Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
end
def create_linear_issue_linked_activity(issue_data)
return unless issue_data[:id]
content = I18n.t('conversations.activity.linear.issue_linked', issue_id: issue_data[:id])
::Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
end
def create_linear_issue_unlinked_activity(issue_data)
return unless issue_data[:id]
content = I18n.t('conversations.activity.linear.issue_unlinked', issue_id: issue_data[:id])
::Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
end
end
+4
View File
@@ -186,6 +186,10 @@ en:
sla: sla:
added: '%{user_name} added SLA policy %{sla_name}' added: '%{user_name} added SLA policy %{sla_name}'
removed: '%{user_name} removed SLA policy %{sla_name}' removed: '%{user_name} removed SLA policy %{sla_name}'
linear:
issue_created: 'New Linear issue %{issue_id} has been created'
issue_linked: 'Linear issue %{issue_id} is now linked'
issue_unlinked: 'Linear issue %{issue_id} has been unlinked'
muted: '%{user_name} has muted the conversation' muted: '%{user_name} has muted the conversation'
unmuted: '%{user_name} has unmuted the conversation' unmuted: '%{user_name} has unmuted the conversation'
auto_resolution_message: 'Resolving the conversation as it has been inactive for a while. Please start a new conversation if you need further assistance.' auto_resolution_message: 'Resolving the conversation as it has been inactive for a while. Please start a new conversation if you need further assistance.'
+2 -1
View File
@@ -28,7 +28,8 @@ class Integrations::Linear::ProcessorService
{ {
data: { id: response['issueCreate']['issue']['id'], data: { id: response['issueCreate']['issue']['id'],
title: response['issueCreate']['issue']['title'] } title: response['issueCreate']['issue']['title'],
identifier: response['issueCreate']['issue']['identifier'] }
} }
end end
+1
View File
@@ -25,6 +25,7 @@ module Linear::Mutations
issue { issue {
id id
title title
identifier
} }
} }
} }
@@ -93,6 +93,8 @@ RSpec.describe 'Linear Integration API', type: :request do
end end
describe 'POST /api/v1/accounts/:account_id/integrations/linear/create_issue' do describe 'POST /api/v1/accounts/:account_id/integrations/linear/create_issue' do
let(:inbox) { create(:inbox, account: account) }
let(:conversation) { create(:conversation, account: account, inbox: inbox) }
let(:issue_params) do let(:issue_params) do
{ {
team_id: 'team1', team_id: 'team1',
@@ -101,32 +103,56 @@ RSpec.describe 'Linear Integration API', type: :request do
assignee_id: 'user1', assignee_id: 'user1',
priority: 'high', priority: 'high',
state_id: 'state1', state_id: 'state1',
label_ids: ['label1'] label_ids: ['label1'],
conversation_id: conversation.display_id
} }
end end
context 'when it is an authenticated user' do context 'when it is an authenticated user' do
context 'when the issue is created successfully' do context 'when the issue is created successfully' do
let(:created_issue) { { data: { 'id' => 'issue1', 'title' => 'Sample Issue' } } } let(:created_issue) { { data: { 'identifier' => 'ENG-123', 'title' => 'Sample Issue' } } }
it 'returns the created issue' do it 'returns the created issue' do
allow(processor_service).to receive(:create_issue).with(issue_params.stringify_keys).and_return(created_issue) allow(processor_service).to receive(:create_issue).with(issue_params.stringify_keys).and_return(created_issue)
post "/api/v1/accounts/#{account.id}/integrations/linear/create_issue", post "/api/v1/accounts/#{account.id}/integrations/linear/create_issue",
params: issue_params, params: issue_params,
headers: agent.create_new_auth_token, headers: agent.create_new_auth_token,
as: :json as: :json
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(response.body).to include('Sample Issue') expect(response.body).to include('Sample Issue')
end end
it 'creates activity message when conversation is provided' do
allow(processor_service).to receive(:create_issue).with(issue_params.stringify_keys).and_return(created_issue)
expect do
post "/api/v1/accounts/#{account.id}/integrations/linear/create_issue",
params: issue_params,
headers: agent.create_new_auth_token,
as: :json
end.to have_enqueued_job(Conversations::ActivityMessageJob)
.with(conversation, {
account_id: conversation.account_id,
inbox_id: conversation.inbox_id,
message_type: :activity,
content: 'New Linear issue ENG-123 has been created'
})
end
end end
context 'when issue creation fails' do context 'when issue creation fails' do
it 'returns error message' do it 'returns error message and does not create activity message' do
allow(processor_service).to receive(:create_issue).with(issue_params.stringify_keys).and_return(error: 'error message') allow(processor_service).to receive(:create_issue).with(issue_params.stringify_keys).and_return(error: 'error message')
post "/api/v1/accounts/#{account.id}/integrations/linear/create_issue",
params: issue_params, expect do
headers: agent.create_new_auth_token, post "/api/v1/accounts/#{account.id}/integrations/linear/create_issue",
as: :json params: issue_params,
headers: agent.create_new_auth_token,
as: :json
end.not_to have_enqueued_job(Conversations::ActivityMessageJob)
expect(response).to have_http_status(:unprocessable_entity) expect(response).to have_http_status(:unprocessable_entity)
expect(response.body).to include('error message') expect(response.body).to include('error message')
end end
@@ -135,7 +161,7 @@ RSpec.describe 'Linear Integration API', type: :request do
end end
describe 'POST /api/v1/accounts/:account_id/integrations/linear/link_issue' do describe 'POST /api/v1/accounts/:account_id/integrations/linear/link_issue' do
let(:issue_id) { 'issue1' } let(:issue_id) { 'ENG-456' }
let(:conversation) { create(:conversation, account: account) } let(:conversation) { create(:conversation, account: account) }
let(:link) { "#{ENV.fetch('FRONTEND_URL', nil)}/app/accounts/#{account.id}/conversations/#{conversation.display_id}" } let(:link) { "#{ENV.fetch('FRONTEND_URL', nil)}/app/accounts/#{account.id}/conversations/#{conversation.display_id}" }
let(:title) { 'Sample Issue' } let(:title) { 'Sample Issue' }
@@ -144,24 +170,38 @@ RSpec.describe 'Linear Integration API', type: :request do
context 'when the issue is linked successfully' do context 'when the issue is linked successfully' do
let(:linked_issue) { { data: { 'id' => 'issue1', 'link' => 'https://linear.app/issue1' } } } let(:linked_issue) { { data: { 'id' => 'issue1', 'link' => 'https://linear.app/issue1' } } }
it 'returns the linked issue' do it 'returns the linked issue and creates activity message' do
allow(processor_service).to receive(:link_issue).with(link, issue_id, title).and_return(linked_issue) allow(processor_service).to receive(:link_issue).with(link, issue_id, title).and_return(linked_issue)
post "/api/v1/accounts/#{account.id}/integrations/linear/link_issue",
params: { conversation_id: conversation.display_id, issue_id: issue_id, title: title }, expect do
headers: agent.create_new_auth_token, post "/api/v1/accounts/#{account.id}/integrations/linear/link_issue",
as: :json params: { conversation_id: conversation.display_id, issue_id: issue_id, title: title },
headers: agent.create_new_auth_token,
as: :json
end.to have_enqueued_job(Conversations::ActivityMessageJob)
.with(conversation, {
account_id: conversation.account_id,
inbox_id: conversation.inbox_id,
message_type: :activity,
content: 'Linear issue ENG-456 is now linked'
})
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(response.body).to include('https://linear.app/issue1') expect(response.body).to include('https://linear.app/issue1')
end end
end end
context 'when issue linking fails' do context 'when issue linking fails' do
it 'returns error message' do it 'returns error message and does not create activity message' do
allow(processor_service).to receive(:link_issue).with(link, issue_id, title).and_return(error: 'error message') allow(processor_service).to receive(:link_issue).with(link, issue_id, title).and_return(error: 'error message')
post "/api/v1/accounts/#{account.id}/integrations/linear/link_issue",
params: { conversation_id: conversation.display_id, issue_id: issue_id, title: title }, expect do
headers: agent.create_new_auth_token, post "/api/v1/accounts/#{account.id}/integrations/linear/link_issue",
as: :json params: { conversation_id: conversation.display_id, issue_id: issue_id, title: title },
headers: agent.create_new_auth_token,
as: :json
end.not_to have_enqueued_job(Conversations::ActivityMessageJob)
expect(response).to have_http_status(:unprocessable_entity) expect(response).to have_http_status(:unprocessable_entity)
expect(response.body).to include('error message') expect(response.body).to include('error message')
end end
@@ -171,29 +211,45 @@ RSpec.describe 'Linear Integration API', type: :request do
describe 'POST /api/v1/accounts/:account_id/integrations/linear/unlink_issue' do describe 'POST /api/v1/accounts/:account_id/integrations/linear/unlink_issue' do
let(:link_id) { 'attachment1' } let(:link_id) { 'attachment1' }
let(:issue_id) { 'ENG-789' }
let(:conversation) { create(:conversation, account: account) }
context 'when it is an authenticated user' do context 'when it is an authenticated user' do
context 'when the issue is unlinked successfully' do context 'when the issue is unlinked successfully' do
let(:unlinked_issue) { { data: { 'id' => 'issue1', 'link' => 'https://linear.app/issue1' } } } let(:unlinked_issue) { { data: { 'id' => 'issue1', 'link' => 'https://linear.app/issue1' } } }
it 'returns the unlinked issue' do it 'returns the unlinked issue and creates activity message' do
allow(processor_service).to receive(:unlink_issue).with(link_id).and_return(unlinked_issue) allow(processor_service).to receive(:unlink_issue).with(link_id).and_return(unlinked_issue)
post "/api/v1/accounts/#{account.id}/integrations/linear/unlink_issue",
params: { link_id: link_id }, expect do
headers: agent.create_new_auth_token, post "/api/v1/accounts/#{account.id}/integrations/linear/unlink_issue",
as: :json params: { link_id: link_id, issue_id: issue_id, conversation_id: conversation.display_id },
headers: agent.create_new_auth_token,
as: :json
end.to have_enqueued_job(Conversations::ActivityMessageJob)
.with(conversation, {
account_id: conversation.account_id,
inbox_id: conversation.inbox_id,
message_type: :activity,
content: 'Linear issue ENG-789 has been unlinked'
})
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(response.body).to include('https://linear.app/issue1') expect(response.body).to include('https://linear.app/issue1')
end end
end end
context 'when issue unlinking fails' do context 'when issue unlinking fails' do
it 'returns error message' do it 'returns error message and does not create activity message' do
allow(processor_service).to receive(:unlink_issue).with(link_id).and_return(error: 'error message') allow(processor_service).to receive(:unlink_issue).with(link_id).and_return(error: 'error message')
post "/api/v1/accounts/#{account.id}/integrations/linear/unlink_issue",
params: { link_id: link_id }, expect do
headers: agent.create_new_auth_token, post "/api/v1/accounts/#{account.id}/integrations/linear/unlink_issue",
as: :json params: { link_id: link_id, issue_id: issue_id, conversation_id: conversation.display_id },
headers: agent.create_new_auth_token,
as: :json
end.not_to have_enqueued_job(Conversations::ActivityMessageJob)
expect(response).to have_http_status(:unprocessable_entity) expect(response).to have_http_status(:unprocessable_entity)
expect(response.body).to include('error message') expect(response.body).to include('error message')
end end
@@ -82,15 +82,27 @@ describe Integrations::Linear::ProcessorService do
end end
let(:issue_response) do let(:issue_response) do
{ {
'issueCreate' => { 'issue' => { 'id' => 'issue1', 'title' => 'Issue title' } } 'issueCreate' => {
'issue' => {
'id' => 'issue1',
'title' => 'Issue title',
'identifier' => 'ENG-123'
}
}
} }
end end
context 'when Linear client returns valid data' do context 'when Linear client returns valid data' do
it 'returns parsed issue data' do it 'returns parsed issue data with identifier' do
allow(linear_client).to receive(:create_issue).with(params).and_return(issue_response) allow(linear_client).to receive(:create_issue).with(params).and_return(issue_response)
result = service.create_issue(params) result = service.create_issue(params)
expect(result).to eq({ data: { id: 'issue1', title: 'Issue title' } }) expect(result).to eq({
data: {
id: 'issue1',
title: 'Issue title',
identifier: 'ENG-123'
}
})
end end
end end
@@ -133,13 +145,13 @@ describe Integrations::Linear::ProcessorService do
describe '#unlink_issue' do describe '#unlink_issue' do
let(:link_id) { 'attachment1' } let(:link_id) { 'attachment1' }
let(:unlink_response) { { data: { link_id: link_id } } } let(:linear_client_response) { { success: true } }
context 'when Linear client returns valid data' do context 'when Linear client returns valid data' do
it 'returns parsed unlink data' do it 'returns unlink data with link_id' do
allow(linear_client).to receive(:unlink_issue).with(link_id).and_return(unlink_response) allow(linear_client).to receive(:unlink_issue).with(link_id).and_return(linear_client_response)
result = service.unlink_issue(link_id) result = service.unlink_issue(link_id)
expect(result).to eq(unlink_response) expect(result).to eq({ data: { link_id: link_id } })
end end
end end
@@ -207,4 +219,61 @@ describe Integrations::Linear::ProcessorService do
end end
end end
end end
# Tests specifically for activity message integration
describe 'activity message data compatibility' do
let(:linear_client_response) { { success: true } }
describe '#create_issue' do
it 'includes identifier field needed for activity messages' do
params = { title: 'Test Issue', team_id: 'team1' }
response = {
'issueCreate' => {
'issue' => {
'id' => 'internal_id_123',
'title' => 'Test Issue',
'identifier' => 'ENG-456'
}
}
}
allow(linear_client).to receive(:create_issue).with(params).and_return(response)
result = service.create_issue(params)
expect(result[:data]).to have_key(:identifier)
expect(result[:data][:identifier]).to eq('ENG-456')
end
end
describe '#link_issue' do
it 'returns issue_id in response for activity messages' do
link = 'https://example.com'
issue_id = 'ENG-789'
title = 'Test Issue'
response = {
'attachmentLinkURL' => {
'attachment' => { 'id' => 'attachment123' }
}
}
allow(linear_client).to receive(:link_issue).with(link, issue_id, title).and_return(response)
result = service.link_issue(link, issue_id, title)
expect(result[:data][:id]).to eq(issue_id)
end
end
describe '#unlink_issue' do
it 'returns structured data for activity messages' do
link_id = 'attachment456'
allow(linear_client).to receive(:unlink_issue).with(link_id).and_return(linear_client_response)
result = service.unlink_issue(link_id)
expect(result).to have_key(:data)
expect(result[:data]).to have_key(:link_id)
expect(result[:data][:link_id]).to eq(link_id)
end
end
end
end end
@@ -0,0 +1,97 @@
require 'rails_helper'
RSpec.describe LinearActivityMessageHandler, type: :model do
let(:account) { create(:account) }
let(:inbox) { create(:inbox, account: account) }
let(:conversation) { create(:conversation, account: account, inbox: inbox) }
describe '#create_linear_issue_created_activity' do
it 'enqueues an activity message job when issue data contains id' do
issue_data = { id: 'ENG-123' }
expect do
conversation.create_linear_issue_created_activity(issue_data)
end.to have_enqueued_job(Conversations::ActivityMessageJob)
.with(conversation, {
account_id: conversation.account_id,
inbox_id: conversation.inbox_id,
message_type: :activity,
content: 'New Linear issue ENG-123 has been created'
})
end
it 'does not enqueue job when issue data lacks id' do
issue_data = { title: 'Some issue' }
expect do
conversation.create_linear_issue_created_activity(issue_data)
end.not_to have_enqueued_job(Conversations::ActivityMessageJob)
end
it 'does not enqueue job when issue_data is empty' do
expect do
conversation.create_linear_issue_created_activity({})
end.not_to have_enqueued_job(Conversations::ActivityMessageJob)
end
end
describe '#create_linear_issue_linked_activity' do
it 'enqueues an activity message job when issue data contains id' do
issue_data = { id: 'ENG-456' }
expect do
conversation.create_linear_issue_linked_activity(issue_data)
end.to have_enqueued_job(Conversations::ActivityMessageJob)
.with(conversation, {
account_id: conversation.account_id,
inbox_id: conversation.inbox_id,
message_type: :activity,
content: 'Linear issue ENG-456 is now linked'
})
end
it 'does not enqueue job when issue data lacks id' do
issue_data = { title: 'Some issue' }
expect do
conversation.create_linear_issue_linked_activity(issue_data)
end.not_to have_enqueued_job(Conversations::ActivityMessageJob)
end
it 'does not enqueue job when issue_data is empty' do
expect do
conversation.create_linear_issue_linked_activity({})
end.not_to have_enqueued_job(Conversations::ActivityMessageJob)
end
end
describe '#create_linear_issue_unlinked_activity' do
it 'enqueues an activity message job when issue data contains id' do
issue_data = { id: 'ENG-789' }
expect do
conversation.create_linear_issue_unlinked_activity(issue_data)
end.to have_enqueued_job(Conversations::ActivityMessageJob)
.with(conversation, {
account_id: conversation.account_id,
inbox_id: conversation.inbox_id,
message_type: :activity,
content: 'Linear issue ENG-789 has been unlinked'
})
end
it 'does not enqueue job when issue data lacks id' do
issue_data = { title: 'Some issue' }
expect do
conversation.create_linear_issue_unlinked_activity(issue_data)
end.not_to have_enqueued_job(Conversations::ActivityMessageJob)
end
it 'does not enqueue job when issue_data is empty' do
expect do
conversation.create_linear_issue_unlinked_activity({})
end.not_to have_enqueued_job(Conversations::ActivityMessageJob)
end
end
end