Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
require 'rails_helper'
|
|
|
|
|
|
|
|
|
|
RSpec.describe Channel::Driver::Twitter do
|
|
|
|
|
subject(:channel) { create(:twitter_channel) }
|
|
|
|
|
|
2020-02-21 04:43:51 +00:00
|
|
|
|
let(:external_credential) { ExternalCredential.find(channel.options[:auth][:external_credential_id]) }
|
|
|
|
|
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
describe '#process', current_user_id: 1 do
|
|
|
|
|
# Twitter channels must be configured to know whose account they're monitoring.
|
|
|
|
|
subject(:channel) do
|
|
|
|
|
create(:twitter_channel, custom_options: { user: { id: payload[:for_user_id] } })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:payload) { YAML.safe_load(File.read(payload_file), [ActiveSupport::HashWithIndifferentAccess]) }
|
|
|
|
|
|
|
|
|
|
# https://git.znuny.com/zammad/zammad/-/issues/305
|
|
|
|
|
shared_examples 'for user processing' do
|
|
|
|
|
let(:sender_attributes) do
|
|
|
|
|
{
|
|
|
|
|
'login' => sender_profile[:screen_name],
|
|
|
|
|
'firstname' => sender_profile[:name].capitalize,
|
|
|
|
|
'web' => sender_profile[:url],
|
|
|
|
|
'note' => sender_profile[:description],
|
|
|
|
|
'address' => sender_profile[:location],
|
|
|
|
|
'image_source' => sender_profile[:profile_image_url],
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:avatar_attributes) do
|
|
|
|
|
{
|
|
|
|
|
'object_lookup_id' => ObjectLookup.by_name('User'),
|
|
|
|
|
'deletable' => true,
|
|
|
|
|
'source' => 'twitter',
|
|
|
|
|
'source_url' => sender_profile[:profile_image_url],
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:authorization_attributes) do
|
|
|
|
|
{
|
|
|
|
|
'uid' => sender_profile[:id],
|
|
|
|
|
'username' => sender_profile[:screen_name],
|
|
|
|
|
'provider' => 'twitter',
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'from unknown user' do
|
|
|
|
|
it 'creates a User record for the sender' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(User, :count).by(1)
|
|
|
|
|
.and change { User.exists?(sender_attributes) }.to(true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'creates an Avatar record for the sender', :use_vcr do
|
|
|
|
|
# Why 2, and not 1? Avatar.add auto-generates a default (source: 'init') record
|
|
|
|
|
# before actually adding the specified (source: 'twitter') one.
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Avatar, :count).by_at_least(1)
|
|
|
|
|
.and change { Avatar.exists?(avatar_attributes) }.to(true)
|
|
|
|
|
|
|
|
|
|
expect(User.last.image).to eq(Avatar.last.store_hash)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'creates an Authorization record for the sender' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Authorization, :count).by(1)
|
|
|
|
|
.and change { Authorization.exists?(authorization_attributes) }.to(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'from known user' do
|
|
|
|
|
let!(:user) { create(:user) }
|
|
|
|
|
|
|
|
|
|
let!(:avatar) { create(:avatar, o_id: user.id, object_lookup_id: ObjectLookup.by_name('User'), source: 'twitter') }
|
|
|
|
|
|
|
|
|
|
let!(:authorization) do
|
|
|
|
|
Authorization.create(user_id: user.id, uid: sender_profile[:id], provider: 'twitter')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'updates the sender’s existing User record' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to not_change(User, :count)
|
|
|
|
|
.and not_change { user.reload.attributes.slice('login', 'firstname') }
|
|
|
|
|
.and change { User.exists?(sender_attributes.except('login', 'firstname')) }.to(true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'updates the sender’s existing Avatar record', :use_vcr do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to not_change(Avatar, :count)
|
|
|
|
|
.and change { Avatar.exists?(avatar_attributes) }.to(true)
|
|
|
|
|
|
|
|
|
|
expect(user.reload.image).to eq(avatar.reload.store_hash)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'updates the sender’s existing Authorization record' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to not_change(Authorization, :count)
|
|
|
|
|
.and change { Authorization.exists?(authorization_attributes) }.to(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for incoming DM' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
let(:payload_file) { Rails.root.join('test/data/twitter/webhook_events/direct_message-incoming.yml') }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
include_examples 'for user processing' do
|
|
|
|
|
# Payload sent by Twitter is { ..., users: [{ <uid>: <sender> }, { <uid>: <receiver> }] }
|
|
|
|
|
let(:sender_profile) { payload[:users].values.first }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe 'ticket creation' do
|
|
|
|
|
let(:ticket_attributes) do
|
|
|
|
|
# NOTE: missing "customer_id" (because the value is generated as part of the #process method)
|
|
|
|
|
{
|
|
|
|
|
'title' => title,
|
|
|
|
|
'group_id' => channel.options[:sync][:direct_messages][:group_id],
|
|
|
|
|
'state' => Ticket::State.find_by(default_create: true),
|
|
|
|
|
'priority' => Ticket::Priority.find_by(default_create: true),
|
|
|
|
|
'preferences' => {
|
|
|
|
|
'channel_id' => channel.id,
|
|
|
|
|
'channel_screen_name' => channel.options[:user][:screen_name],
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:title) { payload[:direct_message_events].first[:message_create][:message_data][:text] }
|
|
|
|
|
|
|
|
|
|
it 'creates a new ticket' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Ticket, :count).by(1)
|
|
|
|
|
.and change { Ticket.exists?(ticket_attributes) }.to(true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for duplicate messages' do
|
|
|
|
|
before do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
channel.process(
|
|
|
|
|
YAML.safe_load(File.read(payload_file), [ActiveSupport::HashWithIndifferentAccess])
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'does not create duplicate ticket' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to not_change(Ticket, :count)
|
|
|
|
|
.and not_change(Ticket::Article, :count)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for message longer than 80 chars' do
|
|
|
|
|
before { payload[:direct_message_events].first[:message_create][:message_data][:text] = 'a' * 81 }
|
|
|
|
|
|
|
|
|
|
let(:title) { "#{'a' * 80}..." }
|
|
|
|
|
|
|
|
|
|
it 'creates ticket with truncated title' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Ticket, :count).by(1)
|
|
|
|
|
.and change { Ticket.exists?(ticket_attributes) }.to(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'in reply to existing thread/ticket' do
|
|
|
|
|
# import parent DM
|
|
|
|
|
before do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
channel.process(
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
YAML.safe_load(
|
2020-01-06 08:20:59 +00:00
|
|
|
|
File.read(Rails.root.join('test/data/twitter/webhook_events/direct_message-incoming.yml')),
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
[ActiveSupport::HashWithIndifferentAccess]
|
2020-01-06 08:20:59 +00:00
|
|
|
|
)
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
2020-01-06 08:20:59 +00:00
|
|
|
|
let(:payload_file) { Rails.root.join('test/data/twitter/webhook_events/direct_message-incoming_2.yml') }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
it 'uses existing ticket' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to not_change(Ticket, :count)
|
|
|
|
|
.and not_change { Ticket.last.state }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'marked "closed" / "merged" / "removed"' do
|
|
|
|
|
before { Ticket.last.update(state: Ticket::State.find_by(name: 'closed')) }
|
|
|
|
|
|
|
|
|
|
it 'creates a new ticket' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Ticket, :count).by(1)
|
|
|
|
|
.and change { Ticket.exists?(ticket_attributes) }.to(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'marked "pending reminder" / "pending close"' do
|
|
|
|
|
before { Ticket.last.update(state: Ticket::State.find_by(name: 'pending reminder')) }
|
|
|
|
|
|
|
|
|
|
it 'sets existing ticket to "open"' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to not_change(Ticket, :count)
|
|
|
|
|
.and change { Ticket.last.state.name }.to('open')
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe 'article creation' do
|
|
|
|
|
let(:article_attributes) do
|
|
|
|
|
# NOTE: missing "ticket_id" (because the value is generated as part of the #process method)
|
|
|
|
|
{
|
|
|
|
|
'from' => "@#{payload[:users].values.first[:screen_name]}",
|
|
|
|
|
'to' => "@#{payload[:users].values.second[:screen_name]}",
|
|
|
|
|
'body' => payload[:direct_message_events].first[:message_create][:message_data][:text],
|
|
|
|
|
'message_id' => payload[:direct_message_events].first[:id],
|
|
|
|
|
'in_reply_to' => nil,
|
|
|
|
|
'type_id' => Ticket::Article::Type.find_by(name: 'twitter direct-message').id,
|
|
|
|
|
'sender_id' => Ticket::Article::Sender.find_by(name: 'Customer').id,
|
|
|
|
|
'internal' => false,
|
|
|
|
|
'preferences' => { 'twitter' => twitter_prefs, 'links' => link_array }
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:twitter_prefs) do
|
|
|
|
|
{
|
|
|
|
|
'created_at' => payload[:direct_message_events].first[:created_timestamp],
|
|
|
|
|
'recipient_id' => payload[:direct_message_events].first[:message_create][:target][:recipient_id],
|
|
|
|
|
'recipient_screen_name' => payload[:users].values.second[:screen_name],
|
|
|
|
|
'sender_id' => payload[:direct_message_events].first[:message_create][:sender_id],
|
|
|
|
|
'sender_screen_name' => payload[:users].values.first[:screen_name],
|
|
|
|
|
'app_id' => payload[:apps]&.values&.first&.dig(:app_id),
|
|
|
|
|
'app_name' => payload[:apps]&.values&.first&.dig(:app_name),
|
|
|
|
|
'geo' => {},
|
|
|
|
|
'place' => {},
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:link_array) do
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
'url' => "https://twitter.com/messages/#{user_ids.map(&:to_i).sort.join('-')}",
|
|
|
|
|
'target' => '_blank',
|
|
|
|
|
'name' => 'on Twitter',
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:user_ids) { payload[:users].values.map { |u| u[:id] } }
|
|
|
|
|
|
|
|
|
|
it 'creates a new article' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Ticket::Article, :count).by(1)
|
|
|
|
|
.and change { Ticket::Article.exists?(article_attributes) }.to(true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for duplicate messages' do
|
|
|
|
|
before do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
channel.process(
|
|
|
|
|
YAML.safe_load(File.read(payload_file), [ActiveSupport::HashWithIndifferentAccess])
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'does not create duplicate article' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to not_change(Ticket::Article, :count)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when message contains shortened (t.co) url' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
let(:payload_file) { Rails.root.join('test/data/twitter/webhook_events/direct_message-incoming_with_url.yml') }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
it 'replaces the t.co url for the original' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change { Ticket::Article.exists?(body: <<~BODY.chomp) }.to(true)
|
|
|
|
|
Did you know about this? https://en.wikipedia.org/wiki/Frankenstein#Composition
|
|
|
|
|
BODY
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for outgoing DM' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
let(:payload_file) { Rails.root.join('test/data/twitter/webhook_events/direct_message-outgoing.yml') }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
describe 'ticket creation' do
|
|
|
|
|
let(:ticket_attributes) do
|
|
|
|
|
# NOTE: missing "customer_id" (because User.last changes before and after the method is called)
|
|
|
|
|
{
|
|
|
|
|
'title' => payload[:direct_message_events].first[:message_create][:message_data][:text],
|
|
|
|
|
'group_id' => channel.options[:sync][:direct_messages][:group_id],
|
|
|
|
|
'state' => Ticket::State.find_by(name: 'closed'),
|
|
|
|
|
'priority' => Ticket::Priority.find_by(default_create: true),
|
|
|
|
|
'preferences' => {
|
|
|
|
|
'channel_id' => channel.id,
|
|
|
|
|
'channel_screen_name' => channel.options[:user][:screen_name],
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'creates a closed ticket' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Ticket, :count).by(1)
|
|
|
|
|
.and change { Ticket.exists?(ticket_attributes) }.to(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe 'article creation' do
|
|
|
|
|
let(:article_attributes) do
|
|
|
|
|
# NOTE: missing "ticket_id" (because the value is generated as part of the #process method)
|
|
|
|
|
{
|
|
|
|
|
'from' => "@#{payload[:users].values.first[:screen_name]}",
|
|
|
|
|
'to' => "@#{payload[:users].values.second[:screen_name]}",
|
|
|
|
|
'body' => payload[:direct_message_events].first[:message_create][:message_data][:text],
|
|
|
|
|
'message_id' => payload[:direct_message_events].first[:id],
|
|
|
|
|
'in_reply_to' => nil,
|
|
|
|
|
'type_id' => Ticket::Article::Type.find_by(name: 'twitter direct-message').id,
|
|
|
|
|
'sender_id' => Ticket::Article::Sender.find_by(name: 'Customer').id,
|
|
|
|
|
'internal' => false,
|
|
|
|
|
'preferences' => { 'twitter' => twitter_prefs, 'links' => link_array }
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:twitter_prefs) do
|
|
|
|
|
{
|
|
|
|
|
'created_at' => payload[:direct_message_events].first[:created_timestamp],
|
|
|
|
|
'recipient_id' => payload[:direct_message_events].first[:message_create][:target][:recipient_id],
|
|
|
|
|
'recipient_screen_name' => payload[:users].values.second[:screen_name],
|
|
|
|
|
'sender_id' => payload[:direct_message_events].first[:message_create][:sender_id],
|
|
|
|
|
'sender_screen_name' => payload[:users].values.first[:screen_name],
|
|
|
|
|
'app_id' => payload[:apps]&.values&.first&.dig(:app_id),
|
|
|
|
|
'app_name' => payload[:apps]&.values&.first&.dig(:app_name),
|
|
|
|
|
'geo' => {},
|
|
|
|
|
'place' => {},
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:link_array) do
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
'url' => "https://twitter.com/messages/#{user_ids.map(&:to_i).sort.join('-')}",
|
|
|
|
|
'target' => '_blank',
|
|
|
|
|
'name' => 'on Twitter',
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:user_ids) { payload[:users].values.map { |u| u[:id] } }
|
|
|
|
|
|
|
|
|
|
it 'creates a new article' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Ticket::Article, :count).by(1)
|
|
|
|
|
.and change { Ticket::Article.exists?(article_attributes) }.to(true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when message contains shortened (t.co) url' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
let(:payload_file) { Rails.root.join('test/data/twitter/webhook_events/direct_message-incoming_with_url.yml') }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
it 'replaces the t.co url for the original' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change { Ticket::Article.exists?(body: <<~BODY.chomp) }.to(true)
|
|
|
|
|
Did you know about this? https://en.wikipedia.org/wiki/Frankenstein#Composition
|
|
|
|
|
BODY
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when message contains a media attachment (e.g., JPG)' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
let(:payload_file) { Rails.root.join('test/data/twitter/webhook_events/direct_message-incoming_with_media.yml') }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
it 'does not store it as an attachment on the article' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
channel.process(payload)
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
expect(Ticket::Article.last.attachments).to be_empty
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for incoming tweet' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
let(:payload_file) { Rails.root.join('test/data/twitter/webhook_events/tweet_create-user_mention.yml') }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
include_examples 'for user processing' do
|
|
|
|
|
# Payload sent by Twitter is { ..., tweet_create_events: [{ ..., user: <author> }] }
|
|
|
|
|
let(:sender_profile) { payload[:tweet_create_events].first[:user] }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe 'ticket creation' do
|
|
|
|
|
let(:ticket_attributes) do
|
|
|
|
|
# NOTE: missing "customer_id" (because User.last changes before and after the method is called)
|
|
|
|
|
{
|
|
|
|
|
'title' => payload[:tweet_create_events].first[:text],
|
|
|
|
|
'group_id' => channel.options[:sync][:direct_messages][:group_id],
|
|
|
|
|
'state' => Ticket::State.find_by(default_create: true),
|
|
|
|
|
'priority' => Ticket::Priority.find_by(default_create: true),
|
|
|
|
|
'preferences' => {
|
|
|
|
|
'channel_id' => channel.id,
|
|
|
|
|
'channel_screen_name' => channel.options[:user][:screen_name],
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'creates a new ticket' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Ticket, :count).by(1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for duplicate tweets' do
|
|
|
|
|
before do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
channel.process(
|
|
|
|
|
YAML.safe_load(File.read(payload_file), [ActiveSupport::HashWithIndifferentAccess])
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'does not create duplicate ticket' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to not_change(Ticket, :count)
|
|
|
|
|
.and not_change(Ticket::Article, :count)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'in response to existing tweet thread' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
let(:payload_file) { Rails.root.join('test/data/twitter/webhook_events/tweet_create-response.yml') }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
let(:parent_tweet_payload) do
|
|
|
|
|
YAML.safe_load(
|
2020-01-06 08:20:59 +00:00
|
|
|
|
File.read(Rails.root.join('test/data/twitter/webhook_events/tweet_create-user_mention.yml')),
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
[ActiveSupport::HashWithIndifferentAccess]
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'that hasn’t been imported yet', :use_vcr do
|
|
|
|
|
it 'creates a new ticket' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Ticket, :count).by(1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'retrieves the parent tweet via the Twitter API' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Ticket::Article, :count).by(2)
|
|
|
|
|
|
|
|
|
|
expect(Ticket::Article.second_to_last.body).to eq(parent_tweet_payload[:tweet_create_events].first[:text])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'after parent tweet has been deleted' do
|
|
|
|
|
before do
|
|
|
|
|
payload[:tweet_create_events].first[:in_reply_to_status_id] = 1207610954160037890 # rubocop:disable Style/NumericLiterals
|
|
|
|
|
payload[:tweet_create_events].first[:in_reply_to_status_id_str] = '1207610954160037890'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'creates a new ticket' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Ticket, :count).by(1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'silently ignores error when retrieving parent tweet' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }.to not_raise_error
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'that was previously imported' do
|
|
|
|
|
# import parent tweet
|
2020-01-06 08:20:59 +00:00
|
|
|
|
before { channel.process(parent_tweet_payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
it 'uses existing ticket' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to not_change(Ticket, :count)
|
|
|
|
|
.and not_change { Ticket.last.state }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'and marked "closed" / "merged" / "removed" / "pending reminder" / "pending close"' do
|
|
|
|
|
before { Ticket.last.update(state: Ticket::State.find_by(name: 'closed')) }
|
|
|
|
|
|
|
|
|
|
it 'sets existing ticket to "open"' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to not_change(Ticket, :count)
|
|
|
|
|
.and change { Ticket.last.state.name }.to('open')
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe 'article creation' do
|
|
|
|
|
let(:article_attributes) do
|
|
|
|
|
# NOTE: missing "ticket_id" (because the value is generated as part of the #process method)
|
|
|
|
|
{
|
|
|
|
|
'from' => "@#{payload[:tweet_create_events].first[:user][:screen_name]}",
|
|
|
|
|
'to' => "@#{payload[:tweet_create_events].first[:entities][:user_mentions].first[:screen_name]}",
|
|
|
|
|
'body' => payload[:tweet_create_events].first[:text],
|
|
|
|
|
'message_id' => payload[:tweet_create_events].first[:id_str],
|
|
|
|
|
'in_reply_to' => payload[:tweet_create_events].first[:in_reply_to_status_id],
|
|
|
|
|
'type_id' => Ticket::Article::Type.find_by(name: 'twitter status').id,
|
|
|
|
|
'sender_id' => Ticket::Article::Sender.find_by(name: 'Customer').id,
|
|
|
|
|
'internal' => false,
|
|
|
|
|
'preferences' => { 'twitter' => twitter_prefs, 'links' => link_array }
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:twitter_prefs) do
|
|
|
|
|
{
|
|
|
|
|
'mention_ids' => payload[:tweet_create_events].first[:entities][:user_mentions].map { |um| um[:id] },
|
|
|
|
|
'geo' => payload[:tweet_create_events].first[:geo].to_h,
|
|
|
|
|
'retweeted' => payload[:tweet_create_events].first[:retweeted],
|
|
|
|
|
'possibly_sensitive' => payload[:tweet_create_events].first[:possibly_sensitive],
|
|
|
|
|
'in_reply_to_user_id' => payload[:tweet_create_events].first[:in_reply_to_user_id],
|
|
|
|
|
'place' => payload[:tweet_create_events].first[:place].to_h,
|
|
|
|
|
'retweet_count' => payload[:tweet_create_events].first[:retweet_count],
|
|
|
|
|
'source' => payload[:tweet_create_events].first[:source],
|
|
|
|
|
'favorited' => payload[:tweet_create_events].first[:favorited],
|
|
|
|
|
'truncated' => payload[:tweet_create_events].first[:truncated],
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:link_array) do
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
'url' => "https://twitter.com/_/status/#{payload[:tweet_create_events].first[:id]}",
|
|
|
|
|
'target' => '_blank',
|
|
|
|
|
'name' => 'on Twitter',
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'creates a new article' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Ticket::Article, :count).by(1)
|
|
|
|
|
.and change { Ticket::Article.exists?(article_attributes) }.to(true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when message mentions multiple users' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
let(:payload_file) { Rails.root.join('test/data/twitter/webhook_events/tweet_create-user_mention_multiple.yml') }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
let(:mentionees) { "@#{payload[:tweet_create_events].first[:entities][:user_mentions].map { |um| um[:screen_name] }.join(', @')}" }
|
|
|
|
|
|
|
|
|
|
it 'records all mentionees in comma-separated "to" attribute' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change { Ticket::Article.exists?(to: mentionees) }.to(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when message exceeds 140 characters' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
let(:payload_file) { Rails.root.join('test/data/twitter/webhook_events/tweet_create-user_mention_extended.yml') }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
let(:full_body) { payload[:tweet_create_events].first[:extended_tweet][:full_text] }
|
|
|
|
|
|
|
|
|
|
it 'records the full (extended) tweet body' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change { Ticket::Article.exists?(body: full_body) }.to(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when message contains shortened (t.co) url' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
let(:payload_file) { Rails.root.join('test/data/twitter/webhook_events/tweet_create-user_mention_with_url.yml') }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
it 'replaces the t.co url for the original' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change { Ticket::Article.exists?(body: <<~BODY.chomp) }.to(true)
|
|
|
|
|
@ScruffyMcG https://zammad.org/
|
|
|
|
|
BODY
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when message contains a media attachment (e.g., JPG)' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
let(:payload_file) { Rails.root.join('test/data/twitter/webhook_events/tweet_create-user_mention_with_media.yml') }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
it 'replaces the t.co url for the original' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change { Ticket::Article.exists?(body: <<~BODY.chomp) }.to(true)
|
|
|
|
|
@ScruffyMcG https://twitter.com/pennbrooke1/status/1209101446706122752/photo/1
|
|
|
|
|
BODY
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'stores it as an attachment on the article', :use_vcr do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
channel.process(payload)
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
expect(Ticket::Article.last.attachments).to be_one
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for outgoing tweet' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
let(:payload_file) { Rails.root.join('test/data/twitter/webhook_events/tweet_create-user_mention_outgoing.yml') }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
|
|
|
|
|
describe 'ticket creation' do
|
|
|
|
|
let(:ticket_attributes) do
|
|
|
|
|
# NOTE: missing "customer_id" (because User.last changes before and after the method is called)
|
|
|
|
|
{
|
|
|
|
|
'title' => payload[:tweet_create_events].first[:text],
|
|
|
|
|
'group_id' => channel.options[:sync][:direct_messages][:group_id],
|
|
|
|
|
'state' => Ticket::State.find_by(name: 'closed'),
|
|
|
|
|
'priority' => Ticket::Priority.find_by(default_create: true),
|
|
|
|
|
'preferences' => {
|
|
|
|
|
'channel_id' => channel.id,
|
|
|
|
|
'channel_screen_name' => channel.options[:user][:screen_name],
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'creates a closed ticket' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Ticket, :count).by(1)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe 'article creation' do
|
|
|
|
|
let(:article_attributes) do
|
|
|
|
|
# NOTE: missing "ticket_id" (because the value is generated as part of the #process method)
|
|
|
|
|
{
|
|
|
|
|
'from' => "@#{payload[:tweet_create_events].first[:user][:screen_name]}",
|
|
|
|
|
'to' => "@#{payload[:tweet_create_events].first[:entities][:user_mentions].first[:screen_name]}",
|
|
|
|
|
'body' => payload[:tweet_create_events].first[:text],
|
|
|
|
|
'message_id' => payload[:tweet_create_events].first[:id_str],
|
|
|
|
|
'in_reply_to' => payload[:tweet_create_events].first[:in_reply_to_status_id],
|
|
|
|
|
'type_id' => Ticket::Article::Type.find_by(name: 'twitter status').id,
|
|
|
|
|
'sender_id' => Ticket::Article::Sender.find_by(name: 'Customer').id,
|
|
|
|
|
'internal' => false,
|
|
|
|
|
'preferences' => { 'twitter' => twitter_prefs, 'links' => link_array }
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:twitter_prefs) do
|
|
|
|
|
{
|
|
|
|
|
'mention_ids' => payload[:tweet_create_events].first[:entities][:user_mentions].map { |um| um[:id] },
|
|
|
|
|
'geo' => payload[:tweet_create_events].first[:geo].to_h,
|
|
|
|
|
'retweeted' => payload[:tweet_create_events].first[:retweeted],
|
|
|
|
|
'possibly_sensitive' => payload[:tweet_create_events].first[:possibly_sensitive],
|
|
|
|
|
'in_reply_to_user_id' => payload[:tweet_create_events].first[:in_reply_to_user_id],
|
|
|
|
|
'place' => payload[:tweet_create_events].first[:place].to_h,
|
|
|
|
|
'retweet_count' => payload[:tweet_create_events].first[:retweet_count],
|
|
|
|
|
'source' => payload[:tweet_create_events].first[:source],
|
|
|
|
|
'favorited' => payload[:tweet_create_events].first[:favorited],
|
|
|
|
|
'truncated' => payload[:tweet_create_events].first[:truncated],
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:link_array) do
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
'url' => "https://twitter.com/_/status/#{payload[:tweet_create_events].first[:id]}",
|
|
|
|
|
'target' => '_blank',
|
|
|
|
|
'name' => 'on Twitter',
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'creates a new article' do
|
2020-01-06 08:20:59 +00:00
|
|
|
|
expect { channel.process(payload) }
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
.to change(Ticket::Article, :count).by(1)
|
|
|
|
|
.and change { Ticket::Article.exists?(article_attributes) }.to(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-02-21 04:43:51 +00:00
|
|
|
|
|
|
|
|
|
describe '#send', :use_vcr do
|
|
|
|
|
shared_examples 'for #send' do
|
|
|
|
|
# Channel#deliver takes a hash in the following format
|
|
|
|
|
# (see Observer::Ticket::Article::CommunicateTwitter::BackgroundJob#perform)
|
|
|
|
|
#
|
|
|
|
|
# Why not just accept the whole article?
|
|
|
|
|
# Presumably so all channels have a consistent interface...
|
|
|
|
|
# but it might be a good idea to let it accept both one day
|
|
|
|
|
# (the "robustness principle")
|
|
|
|
|
let(:delivery_payload) do
|
|
|
|
|
{
|
|
|
|
|
type: outgoing_tweet.type.name,
|
|
|
|
|
to: outgoing_tweet.to,
|
|
|
|
|
body: outgoing_tweet.body,
|
|
|
|
|
in_reply_to: outgoing_tweet.in_reply_to
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe 'Import Mode behavior' do
|
|
|
|
|
before { Setting.set('import_mode', true) }
|
|
|
|
|
|
|
|
|
|
it 'is a no-op' do
|
|
|
|
|
expect(Twitter::REST::Client).not_to receive(:new)
|
|
|
|
|
|
|
|
|
|
channel.deliver(delivery_payload)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe 'Twitter API authentication' do
|
|
|
|
|
let(:consumer_credentials) do
|
|
|
|
|
{
|
|
|
|
|
consumer_key: external_credential.credentials[:consumer_key],
|
|
|
|
|
consumer_secret: external_credential.credentials[:consumer_secret],
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:oauth_credentials) do
|
|
|
|
|
{
|
|
|
|
|
access_token: channel.options[:auth][:oauth_token],
|
|
|
|
|
access_token_secret: channel.options[:auth][:oauth_token_secret],
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'uses consumer key/secret stored on ExternalCredential' do
|
|
|
|
|
expect(Twitter::REST::Client)
|
|
|
|
|
.to receive(:new).with(hash_including(consumer_credentials))
|
|
|
|
|
.and_call_original
|
|
|
|
|
|
|
|
|
|
channel.deliver(delivery_payload)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'uses OAuth token/secret stored on #options hash' do
|
|
|
|
|
expect(Twitter::REST::Client)
|
|
|
|
|
.to receive(:new).with(hash_including(oauth_credentials))
|
|
|
|
|
.and_call_original
|
|
|
|
|
|
|
|
|
|
channel.deliver(delivery_payload)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe 'Twitter API activity' do
|
|
|
|
|
it 'creates a tweet/DM via the API' do
|
|
|
|
|
channel.deliver(delivery_payload)
|
|
|
|
|
|
|
|
|
|
expect(WebMock)
|
|
|
|
|
.to have_requested(:post, "https://api.twitter.com/1.1#{endpoint}")
|
|
|
|
|
.with(body: request_body)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'returns the created tweet/DM' do
|
|
|
|
|
expect(channel.deliver(delivery_payload)).to match(return_value)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for tweets' do
|
|
|
|
|
let!(:outgoing_tweet) { create(:twitter_article) }
|
|
|
|
|
let(:endpoint) { '/statuses/update.json' }
|
|
|
|
|
let(:request_body) { <<~BODY.chomp }
|
|
|
|
|
in_reply_to_status_id&status=#{URI.encode_www_form_component(outgoing_tweet.body)}
|
|
|
|
|
BODY
|
|
|
|
|
let(:return_value) { Twitter::Tweet }
|
|
|
|
|
|
|
|
|
|
include_examples 'for #send'
|
|
|
|
|
|
|
|
|
|
context 'in a thread' do
|
|
|
|
|
let!(:outgoing_tweet) { create(:twitter_article, :reply) }
|
|
|
|
|
let(:request_body) { <<~BODY.chomp }
|
|
|
|
|
in_reply_to_status_id=#{outgoing_tweet.in_reply_to}&status=#{URI.encode_www_form_component(outgoing_tweet.body)}
|
|
|
|
|
BODY
|
|
|
|
|
|
|
|
|
|
it 'creates a tweet via the API' do
|
|
|
|
|
channel.deliver(delivery_payload)
|
|
|
|
|
|
|
|
|
|
expect(WebMock)
|
|
|
|
|
.to have_requested(:post, "https://api.twitter.com/1.1#{endpoint}")
|
|
|
|
|
.with(body: request_body)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'containing an asterisk (workaround for sferik/twitter #677)' do
|
|
|
|
|
let!(:outgoing_tweet) { create(:twitter_article, body: 'foo * bar') }
|
|
|
|
|
let(:request_body) { <<~BODY.chomp }
|
|
|
|
|
in_reply_to_status_id&status=#{URI.encode_www_form_component('foo * bar')}
|
|
|
|
|
BODY
|
|
|
|
|
|
|
|
|
|
it 'converts it to a full-width asterisk (U+FF0A)' do
|
|
|
|
|
channel.deliver(delivery_payload)
|
|
|
|
|
|
|
|
|
|
expect(WebMock)
|
|
|
|
|
.to have_requested(:post, "https://api.twitter.com/1.1#{endpoint}")
|
|
|
|
|
.with(body: request_body)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for DMs' do
|
|
|
|
|
let!(:outgoing_tweet) { create(:twitter_dm_article, :pending_delivery) }
|
|
|
|
|
let(:endpoint) { '/direct_messages/events/new.json' }
|
|
|
|
|
let(:request_body) { <<~BODY.chomp }
|
|
|
|
|
{"event":{"type":"message_create","message_create":{"target":{"recipient_id":"#{Authorization.last.uid}"},"message_data":{"text":"#{outgoing_tweet.body}"}}}}
|
|
|
|
|
BODY
|
|
|
|
|
let(:return_value) { { event: hash_including(type: 'message_create') } }
|
|
|
|
|
|
|
|
|
|
include_examples 'for #send'
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-02-25 08:44:32 +00:00
|
|
|
|
|
Testing: Prevent synchronization issues with VCR
=== Background: What is VCR?
VCR caches HTTP network traffic for tests
that call out to third-party services (e.g., the Twitter REST API).
It "records" to a YAML file the first time a test is run,
and we save those files to the repo so they can be "replayed" later.
=== The problem & the naive approach
The ways Zammad uses third-party services can be time-sensitive.
For instance, Zammad assumes that when it fetches new tweets,
it's getting an up-to-date response from the Twitter API,
and ignores all tweets 15+ days old.
Of course, if we're using VCR, that's not the case--
in the test, Zammad receives a listing of tweets that's frozen in time.
Thus, 15 days after the test was first run,
it won't import any tweets at all.
Initially, the fix was to add the following to the spec file:
before { travel_to '2020-02-06 13:37 +0100' }
This hardcodes the date when the tests were first written,
and it has significant drawbacks:
1. It leads to hard-to-diagnose bugs when adding new test cases.
Your new test case will not have a VCR cassette associated with it;
the first time you run it, it will travel way back in time
and try to interact with the Twitter REST API.
The Twitter REST API requires an OAuth signature
based on a current timestamp,
and will reject the one generated during the test:
Twitter::Error::Unauthorized: Timestamp out of bounds.
Because RSpec, VCR, and Channel#fetch all suppress error output in
different ways, this error message takes some work to find.
2. If you succeed in adding new test cases,
they won't match the timestamp above.
=== A better solution
This commit adds a new option to the :use_vcr metadata tag:
it 'does something', use_vcr: :time_sensitive
Examples tagged in this way will automatically travel back to exactly
when a VCR cassette was recorded prior to using it.
=== Discussion
You may notice that the VCR auto-record logic was moved
from a helper module to a simple block.
The helper module was intended to improve organization and clean up the
config code, but due to context/binding issues, it was not possible to
call `travel_to` from inside the helper module.
2020-03-06 10:44:59 +00:00
|
|
|
|
describe '#fetch', use_vcr: :time_sensitive do
|
2020-02-25 08:44:32 +00:00
|
|
|
|
describe 'Twitter API authentication' do
|
|
|
|
|
let(:consumer_credentials) do
|
|
|
|
|
{
|
|
|
|
|
consumer_key: external_credential.credentials[:consumer_key],
|
|
|
|
|
consumer_secret: external_credential.credentials[:consumer_secret],
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:oauth_credentials) do
|
|
|
|
|
{
|
|
|
|
|
access_token: channel.options[:auth][:oauth_token],
|
|
|
|
|
access_token_secret: channel.options[:auth][:oauth_token_secret],
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'uses consumer key/secret stored on ExternalCredential' do
|
|
|
|
|
expect(Twitter::REST::Client)
|
|
|
|
|
.to receive(:new).with(hash_including(consumer_credentials))
|
|
|
|
|
.and_call_original
|
|
|
|
|
|
|
|
|
|
channel.fetch
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'uses OAuth token/secret stored on #options hash' do
|
|
|
|
|
expect(Twitter::REST::Client)
|
|
|
|
|
.to receive(:new).with(hash_including(oauth_credentials))
|
|
|
|
|
.and_call_original
|
|
|
|
|
|
|
|
|
|
channel.fetch
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe 'Twitter API activity' do
|
|
|
|
|
it 'sets successful status attributes' do
|
2020-02-25 08:55:34 +00:00
|
|
|
|
expect { channel.fetch }
|
2020-02-25 08:44:32 +00:00
|
|
|
|
.to change { channel.reload.attributes }
|
|
|
|
|
.to hash_including(
|
|
|
|
|
'status_in' => 'ok',
|
|
|
|
|
'last_log_in' => '',
|
|
|
|
|
'status_out' => nil,
|
|
|
|
|
'last_log_out' => nil
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
2020-02-25 08:55:34 +00:00
|
|
|
|
context 'with search term configured (at .options[:sync][:search])' do
|
|
|
|
|
it 'creates an article for each recent tweet' do
|
|
|
|
|
expect { channel.fetch }
|
2020-03-12 17:37:05 +00:00
|
|
|
|
.to change(Ticket, :count).by(2)
|
2020-02-25 08:44:32 +00:00
|
|
|
|
|
|
|
|
|
expect(Ticket.last.attributes).to include(
|
2020-02-25 08:55:34 +00:00
|
|
|
|
'title' => "Come and join our team to bring Zammad even further forward! It's gonna be ama...",
|
2020-02-25 08:44:32 +00:00
|
|
|
|
'preferences' => { 'channel_id' => channel.id,
|
|
|
|
|
'channel_screen_name' => channel.options[:user][:screen_name] },
|
2020-02-25 08:55:34 +00:00
|
|
|
|
'customer_id' => User.find_by(firstname: 'Mr.Generation', lastname: '').id
|
2020-02-25 08:44:32 +00:00
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
2020-03-12 17:37:05 +00:00
|
|
|
|
context 'for responses to other tweets' do
|
|
|
|
|
let(:thread) do
|
|
|
|
|
Ticket.joins(articles: :type).where(ticket_article_types: { name: 'twitter status' })
|
|
|
|
|
.group('tickets.id').having(
|
|
|
|
|
case ActiveRecord::Base.connection_config[:adapter]
|
|
|
|
|
when 'mysql2'
|
|
|
|
|
'COUNT("ticket_articles.*") > 1'
|
|
|
|
|
when 'postgresql'
|
|
|
|
|
'COUNT(ticket_articles.*) > 1'
|
|
|
|
|
end
|
|
|
|
|
).first
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'creates articles for parent tweets as well' do
|
|
|
|
|
channel.fetch
|
|
|
|
|
|
|
|
|
|
expect(thread.articles.last.body).to match(/zammad/i) # search result
|
|
|
|
|
expect(thread.articles.first.body).not_to match(/zammad/i) # parent tweet
|
|
|
|
|
end
|
2020-02-25 08:55:34 +00:00
|
|
|
|
end
|
2020-02-25 08:44:32 +00:00
|
|
|
|
|
2020-03-12 17:37:05 +00:00
|
|
|
|
context 'and "track_retweets" option' do
|
|
|
|
|
context 'is false (default)' do
|
|
|
|
|
it 'skips retweets' do
|
|
|
|
|
expect { channel.fetch }
|
|
|
|
|
.not_to change { Ticket.where('title LIKE ?', 'RT @%').count }.from(0)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'is true' do
|
|
|
|
|
subject(:channel) { create(:twitter_channel, custom_options: { sync: { track_retweets: true } }) }
|
|
|
|
|
|
|
|
|
|
it 'creates an article for each recent tweet/retweet' do
|
|
|
|
|
expect { channel.fetch }
|
|
|
|
|
.to change { Ticket.where('title LIKE ?', 'RT @%').count }.by(1)
|
|
|
|
|
.and change(Ticket, :count).by(3)
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-02-25 08:55:34 +00:00
|
|
|
|
end
|
2020-02-25 08:44:32 +00:00
|
|
|
|
|
2020-03-12 17:37:05 +00:00
|
|
|
|
context 'and "import_older_tweets" option (legacy)' do
|
|
|
|
|
context 'is false (default)' do
|
|
|
|
|
it 'skips tweets 15+ days older than channel itself' do
|
|
|
|
|
expect { channel.fetch }
|
|
|
|
|
.not_to change { Ticket.where('title LIKE ?', 'GitHub Trending Archive, 29 Nov 2018, Ruby. %').count }.from(0)
|
|
|
|
|
end
|
2020-02-25 08:55:34 +00:00
|
|
|
|
end
|
|
|
|
|
|
2020-03-12 17:37:05 +00:00
|
|
|
|
context 'is true' do
|
|
|
|
|
subject(:channel) { create(:twitter_channel, :legacy) }
|
|
|
|
|
|
|
|
|
|
it 'creates an article for each tweet' do
|
|
|
|
|
expect { channel.fetch }
|
|
|
|
|
.to change { Ticket.where('title LIKE ?', 'GitHub Trending Archive, 29 Nov 2018, Ruby. %').count }.by(1)
|
|
|
|
|
.and change(Ticket, :count).by(3)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe 'duplicate handling' do
|
|
|
|
|
context 'when fetched tweets have already been imported' do
|
|
|
|
|
before do
|
|
|
|
|
tweet_ids.each { |tweet_id| create(:ticket_article, message_id: tweet_id) }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:tweet_ids) { [1222126386334388225, 1222109934923460608] } # rubocop:disable Style/NumericLiterals
|
|
|
|
|
|
|
|
|
|
it 'does not import duplicates' do
|
|
|
|
|
expect { channel.fetch }.not_to change(Ticket::Article, :count)
|
|
|
|
|
end
|
2020-02-25 08:55:34 +00:00
|
|
|
|
end
|
|
|
|
|
|
2020-03-12 17:37:05 +00:00
|
|
|
|
describe 'Race condition: when #fetch finds a half-processed, outgoing tweet' do
|
2020-03-12 18:28:50 +00:00
|
|
|
|
subject!(:channel) do
|
|
|
|
|
create(:twitter_channel,
|
|
|
|
|
search_term: 'zammadzammadzammad',
|
|
|
|
|
custom_options: {
|
|
|
|
|
user: {
|
|
|
|
|
# Must match outgoing tweet author Twitter user ID
|
|
|
|
|
id: '1205290247124217856',
|
|
|
|
|
},
|
|
|
|
|
})
|
2020-03-12 17:37:05 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let!(:tweet) { create(:twitter_article, body: 'zammadzammadzammad') }
|
|
|
|
|
|
|
|
|
|
context '(i.e., after the BG job has posted the article to Twitter…' do
|
|
|
|
|
# NOTE: This context block cannot be set up programmatically.
|
|
|
|
|
# Instead, the tweet was posted, fetched, recorded into a VCR cassette,
|
|
|
|
|
# and then manually copied into the existing VCR cassette for this example.
|
|
|
|
|
|
|
|
|
|
context '…but before the BG job has "synced" article.message_id with tweet.id)' do
|
|
|
|
|
let(:twitter_job) { Delayed::Job.find_by(handler: <<~YML) }
|
|
|
|
|
--- !ruby/object:Observer::Ticket::Article::CommunicateTwitter::BackgroundJob
|
|
|
|
|
article_id: #{tweet.id}
|
|
|
|
|
YML
|
|
|
|
|
|
|
|
|
|
around do |example|
|
|
|
|
|
# This test case requires the use_vcr: :time_sensitive option
|
|
|
|
|
# to travel_to(when the VCR cassette was recorded).
|
|
|
|
|
#
|
|
|
|
|
# This ensures that #fetch doesn't ignore
|
|
|
|
|
# the "older" tweets stored in the VCR cassette,
|
|
|
|
|
# but it also freezes time,
|
|
|
|
|
# which breaks this race condition handling logic:
|
|
|
|
|
#
|
|
|
|
|
# break if Delayed::Job.where('created_at < ?', Time.current).none?
|
|
|
|
|
#
|
|
|
|
|
# So, we unfreeze time here.
|
|
|
|
|
travel_back
|
|
|
|
|
|
|
|
|
|
# Run BG job (Why not use Scheduler.worker?
|
|
|
|
|
# It led to hangs & failures elsewhere in test suite.)
|
|
|
|
|
Thread.new do
|
|
|
|
|
sleep 5 # simulate other bg jobs holding up the queue
|
|
|
|
|
twitter_job.invoke_job
|
|
|
|
|
end.tap { example.run }.join
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'does not import the duplicate tweet (waits up to 60s for BG job to finish)' do
|
|
|
|
|
expect { channel.fetch }
|
|
|
|
|
.to not_change(Ticket::Article, :count)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-02-25 08:55:34 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'for very common search terms' do
|
2020-03-12 18:28:50 +00:00
|
|
|
|
subject(:channel) { create(:twitter_channel, search_term: 'coronavirus') }
|
2020-02-25 08:55:34 +00:00
|
|
|
|
|
|
|
|
|
let(:twitter_articles) { Ticket::Article.joins(:type).where(ticket_article_types: { name: 'twitter status' }) }
|
|
|
|
|
|
2020-03-12 17:37:05 +00:00
|
|
|
|
# NOTE: Ordinarily, RSpec examples should be kept as small as possible.
|
|
|
|
|
# In this case, we bundle these examples together because
|
|
|
|
|
# separating them would duplicate expensive setup:
|
|
|
|
|
# even with HTTP caching, this single example takes nearly a minute.
|
|
|
|
|
it 'imports max. ~120 articles every 15 minutes' do
|
2020-02-25 08:55:34 +00:00
|
|
|
|
channel.fetch
|
|
|
|
|
|
2020-03-12 17:37:05 +00:00
|
|
|
|
expect((twitter_articles - Ticket.last.articles).count).to be <= 120
|
|
|
|
|
expect(twitter_articles.count).to be > 120
|
2020-02-25 08:55:34 +00:00
|
|
|
|
|
|
|
|
|
travel(14.minutes)
|
|
|
|
|
|
|
|
|
|
expect { create(:twitter_channel).fetch }
|
|
|
|
|
.not_to change(Ticket::Article, :count)
|
|
|
|
|
|
2020-03-12 17:37:05 +00:00
|
|
|
|
travel(1.minute)
|
2020-02-25 08:55:34 +00:00
|
|
|
|
|
|
|
|
|
expect { create(:twitter_channel).fetch }
|
|
|
|
|
.to change(Ticket::Article, :count)
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-02-25 08:44:32 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
Refactoring: Prep refactoring of Twitter webhook processing logic
This commit rescopes test coverage added in 9678b1857
from TwitterSync#process_webhook to Channel::Driver::Twitter#process.
When Zammad processes an incoming webhook event from Twitter, it enters:
* ChannelsTwitterController#webhook_incoming, which thinly wraps
* Channel::Driver::Twitter#process, which thinly wraps
* TwitterSync#process_webhook
The core logic for webhook processing is contained in this last method,
so that's the method that test coverage was written around.
After examining the classes more carefully,
it seems that this logic doesn't quite belong in the TwitterSync class,
which is principally for outbound communication to the Twitter API.
Rather, it is more analogous to Channel::EmailParser#process
(which takes an incoming email and converts it into the appropriate
Zammad assets; i.e., users, tickets, and articles),
and so will be migrated upstream into Channel::Driver::Twitter#process.
This refactoring will be undertaken in subsequent commits.
2020-01-06 06:36:29 +00:00
|
|
|
|
end
|