Rewrite twitter_spec.rb
This commit is contained in:
parent
eed5fe1459
commit
d056feae94
11 changed files with 351 additions and 417 deletions
|
@ -7,5 +7,44 @@ FactoryBot.define do
|
|||
preferences {}
|
||||
updated_by_id 1
|
||||
created_by_id 1
|
||||
|
||||
factory :twitter_channel do
|
||||
area 'Twitter::Account'
|
||||
options do
|
||||
{
|
||||
adapter: 'twitter',
|
||||
auth: {
|
||||
consumer_key: 'some',
|
||||
consumer_secret: 'some',
|
||||
oauth_token: 'key',
|
||||
oauth_token_secret: 'secret',
|
||||
},
|
||||
user: {
|
||||
screen_name: 'system_login',
|
||||
id: 'system_id',
|
||||
},
|
||||
sync: {
|
||||
import_older_tweets: true,
|
||||
track_retweets: true,
|
||||
search: [
|
||||
{
|
||||
term: 'zammad',
|
||||
group_id: Group.first.id,
|
||||
},
|
||||
{
|
||||
term: 'hash_tag1',
|
||||
group_id: Group.first.id,
|
||||
},
|
||||
],
|
||||
mentions: {
|
||||
group_id: Group.first.id,
|
||||
},
|
||||
direct_messages: {
|
||||
group_id: Group.first.id,
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,5 +7,18 @@ FactoryBot.define do
|
|||
priority { Ticket::Priority.lookup(name: '2 normal') }
|
||||
updated_by_id 1
|
||||
created_by_id 1
|
||||
|
||||
factory :twitter_ticket do
|
||||
transient do
|
||||
channel { create(:twitter_channel) }
|
||||
end
|
||||
|
||||
preferences do
|
||||
{
|
||||
channel_id: channel.id,
|
||||
channel_screen_name: channel.options[:user][:screen_name]
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,14 +1,28 @@
|
|||
FactoryBot.define do
|
||||
factory :ticket_article, class: Ticket::Article do
|
||||
transient do
|
||||
type_name 'email'
|
||||
sender_name 'Customer'
|
||||
end
|
||||
|
||||
from 'factory-customer-1@example.com'
|
||||
to 'factory-customer-1@example.com'
|
||||
subject 'factory article'
|
||||
message_id 'factory@id_com_1'
|
||||
body 'some message 123'
|
||||
internal false
|
||||
sender { Ticket::Article::Sender.find_by(name: 'Customer') }
|
||||
type { Ticket::Article::Type.find_by(name: 'email') }
|
||||
sender { Ticket::Article::Sender.find_by(name: sender_name) }
|
||||
type { Ticket::Article::Type.find_by(name: type_name) }
|
||||
updated_by_id 1
|
||||
created_by_id 1
|
||||
|
||||
factory :twitter_article do
|
||||
transient do
|
||||
type_name 'twitter status'
|
||||
end
|
||||
|
||||
association :ticket, factory: :twitter_ticket
|
||||
body { Faker::Lorem.sentence }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
75
spec/lib/twitter_sync_spec.rb
Normal file
75
spec/lib/twitter_sync_spec.rb
Normal file
|
@ -0,0 +1,75 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe TwitterSync do
|
||||
describe '.preferences_cleanup' do
|
||||
describe 'sanitizing Twitter preferences' do
|
||||
context 'when given as a bare hash' do
|
||||
it 'automatically adds empty hashes at :geo and :place' do
|
||||
expect(described_class.preferences_cleanup({}))
|
||||
.to eq({ geo: {}, place: {} })
|
||||
end
|
||||
|
||||
it 'does not modify values at :mention_ids' do
|
||||
expect(described_class.preferences_cleanup({ mention_ids: [1_234_567_890] }))
|
||||
.to include({ mention_ids: [1_234_567_890] })
|
||||
end
|
||||
|
||||
it 'converts geo: instance_of(Twitter::NullOjbect) to empty hash' do
|
||||
expect(described_class.preferences_cleanup({ geo: Twitter::NullObject.new }))
|
||||
.to include(geo: {})
|
||||
end
|
||||
|
||||
it 'converts geo: instance_of(Twitter::Geo.new) to matching hash' do
|
||||
expect(described_class.preferences_cleanup({ geo: Twitter::Geo.new(coordinates: [1, 1]) }))
|
||||
.to include(geo: { coordinates: [1, 1] })
|
||||
end
|
||||
|
||||
it 'converts place: instance_of(Twitter::NullOjbect) to empty hash' do
|
||||
expect(described_class.preferences_cleanup({ place: Twitter::NullObject.new }))
|
||||
.to include(place: {})
|
||||
end
|
||||
|
||||
it 'converts place: instance_of(Twitter::Place.new) to matching hash' do
|
||||
place_data = { country: 'da', name: 'do', woeid: 1, id: 1 }
|
||||
|
||||
expect(described_class.preferences_cleanup({ place: Twitter::Place.new(place_data) }))
|
||||
.to include(place: place_data)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given nested in an article preferences hash' do
|
||||
it 'automatically adds empty hashes at :geo and :place' do
|
||||
expect(described_class.preferences_cleanup({ twitter: {} }))
|
||||
.to eq(twitter: { geo: {}, place: {} })
|
||||
end
|
||||
|
||||
it 'does not modify values at :mention_ids' do
|
||||
expect(described_class.preferences_cleanup({ twitter: { mention_ids: [1_234_567_890] } }))
|
||||
.to include(twitter: hash_including(mention_ids: [1_234_567_890]))
|
||||
end
|
||||
|
||||
it 'converts geo: instance_of(Twitter::NullOjbect) to empty hash' do
|
||||
expect(described_class.preferences_cleanup({ twitter: { geo: Twitter::NullObject.new } }))
|
||||
.to include(twitter: hash_including(geo: {}))
|
||||
end
|
||||
|
||||
it 'converts geo: instance_of(Twitter::Geo.new) to matching hash' do
|
||||
expect(described_class.preferences_cleanup({ twitter: { geo: Twitter::Geo.new(coordinates: [1, 1]) } }))
|
||||
.to include(twitter: hash_including(geo: { coordinates: [1, 1] }))
|
||||
end
|
||||
|
||||
it 'converts place: instance_of(Twitter::NullOjbect) to empty hash' do
|
||||
expect(described_class.preferences_cleanup({ twitter: { place: Twitter::NullObject.new } }))
|
||||
.to include(twitter: hash_including(place: {}))
|
||||
end
|
||||
|
||||
it 'converts place: instance_of(Twitter::Place.new) to matching hash' do
|
||||
place_data = { country: 'da', name: 'do', woeid: 1, id: 1 }
|
||||
|
||||
expect(described_class.preferences_cleanup({ twitter: { place: Twitter::Place.new(place_data) } }))
|
||||
.to include(twitter: hash_including(place: place_data))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,395 +0,0 @@
|
|||
require 'rails_helper'
|
||||
|
||||
require_dependency 'channel/driver/twitter'
|
||||
|
||||
RSpec.describe ::Channel::Driver::Twitter do
|
||||
|
||||
let(:channel) do
|
||||
create(
|
||||
:channel,
|
||||
area: 'Twitter::Account',
|
||||
options: {
|
||||
adapter: 'twitter',
|
||||
auth: {
|
||||
consumer_key: 'some',
|
||||
consumer_secret: 'some',
|
||||
oauth_token: 'key',
|
||||
oauth_token_secret: 'secret',
|
||||
},
|
||||
user: {
|
||||
screen_name: 'system_login',
|
||||
id: 'system_id',
|
||||
},
|
||||
sync: {
|
||||
import_older_tweets: true,
|
||||
track_retweets: true,
|
||||
search: [
|
||||
{
|
||||
term: 'zammad',
|
||||
group_id: Group.first.id,
|
||||
},
|
||||
{
|
||||
term: 'hash_tag1',
|
||||
group_id: Group.first.id,
|
||||
},
|
||||
],
|
||||
mentions: {
|
||||
group_id: Group.first.id,
|
||||
},
|
||||
direct_messages: {
|
||||
group_id: Group.first.id,
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
active: true,
|
||||
created_by_id: 1,
|
||||
updated_by_id: 1
|
||||
)
|
||||
end
|
||||
|
||||
it 'fetch channel with invalid token' do
|
||||
VCR.use_cassette('models/channel/driver/twitter/fetch_channel_invalid') do
|
||||
expect(channel.fetch(true)).to be false
|
||||
end
|
||||
|
||||
channel.reload
|
||||
expect(channel.status_in).to eq('error')
|
||||
expect(channel.last_log_in).to eq('Can\'t use Channel::Driver::Twitter: #<Twitter::Error::Unauthorized: Invalid or expired token.>')
|
||||
expect(channel.status_out).to be nil
|
||||
expect(channel.last_log_out).to be nil
|
||||
end
|
||||
|
||||
it 'fetch channel with valid token' do
|
||||
expect(Ticket.count).to eq(1)
|
||||
VCR.use_cassette('models/channel/driver/twitter/fetch_channel_valid') do
|
||||
expect(channel.fetch(true)).to be true
|
||||
end
|
||||
|
||||
expect(Ticket.count).to eq(27)
|
||||
|
||||
ticket = Ticket.last
|
||||
expect(ticket.title).to eq('Wir haben unsere DMs deaktiviert. Leider können wir dank der neuen Twitter API k...')
|
||||
expect(ticket.preferences[:channel_id]).to eq(channel.id)
|
||||
expect(ticket.preferences[:channel_screen_name]).to eq(channel.options[:user][:screen_name])
|
||||
expect(ticket.customer.firstname).to eq('Ccc')
|
||||
expect(ticket.customer.lastname).to eq('Event Logistics')
|
||||
|
||||
channel.reload
|
||||
expect(channel.status_in).to eq('ok')
|
||||
expect(channel.last_log_in).to eq('')
|
||||
expect(channel.status_out).to be nil
|
||||
expect(channel.last_log_out).to be nil
|
||||
end
|
||||
|
||||
it 'send tweet based on article - outbound' do
|
||||
user = User.find(2)
|
||||
text = 'Today the weather is really...'
|
||||
ticket = Ticket.create!(
|
||||
title: text[0, 40],
|
||||
customer_id: user.id,
|
||||
group_id: Group.first.id,
|
||||
state: Ticket::State.find_by(name: 'new'),
|
||||
priority: Ticket::Priority.find_by(name: '2 normal'),
|
||||
preferences: {
|
||||
channel_id: channel.id,
|
||||
channel_screen_name: 'system_login',
|
||||
},
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
assert(ticket, "outbound ticket created, text: #{text}")
|
||||
article = Ticket::Article.create!(
|
||||
ticket_id: ticket.id,
|
||||
body: text,
|
||||
type: Ticket::Article::Type.find_by(name: 'twitter status'),
|
||||
sender: Ticket::Article::Sender.find_by(name: 'Agent'),
|
||||
internal: false,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
|
||||
VCR.use_cassette('models/channel/driver/twitter/article_to_tweet') do
|
||||
Scheduler.worker(true)
|
||||
end
|
||||
|
||||
ticket.reload
|
||||
expect(ticket.state.name).to eq('open')
|
||||
expect(ticket.group.name).to eq(Group.first.name)
|
||||
expect(ticket.title).to eq('Today the weather is really...')
|
||||
|
||||
article.reload
|
||||
expect(article.from).to eq('@example')
|
||||
expect(article.to).to eq('')
|
||||
expect(article.cc).to be nil
|
||||
expect(article.subject).to be nil
|
||||
expect(article.sender.name).to eq('Agent')
|
||||
expect(article.type.name).to eq('twitter status')
|
||||
expect(article.message_id).to eq('1069382411899817990')
|
||||
expect(article.content_type).to eq('text/plain')
|
||||
expect(article.body).to eq('Today the weather is really...')
|
||||
expect(article.preferences[:links][0][:url]).to eq('https://twitter.com/statuses/1069382411899817990')
|
||||
expect(article.preferences[:links][0][:target]).to eq('_blank')
|
||||
expect(article.preferences[:links][0][:name]).to eq('on Twitter')
|
||||
|
||||
channel.reload
|
||||
expect(channel.status_in).to be nil
|
||||
expect(channel.last_log_in).to be nil
|
||||
expect(channel.status_out).to eq('ok')
|
||||
expect(channel.last_log_out).to eq('')
|
||||
end
|
||||
|
||||
it 'send tweet based on article - with replaced channel' do
|
||||
user = User.find(2)
|
||||
text = 'Today and tomorrow the weather is really...'
|
||||
ticket = Ticket.create!(
|
||||
title: text[0, 40],
|
||||
customer_id: user.id,
|
||||
group_id: Group.first.id,
|
||||
state: Ticket::State.find_by(name: 'new'),
|
||||
priority: Ticket::Priority.find_by(name: '2 normal'),
|
||||
preferences: {
|
||||
channel_id: 'some_other_id',
|
||||
channel_screen_name: 'system_login',
|
||||
},
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
assert(ticket, "outbound ticket created, text: #{text}")
|
||||
article = Ticket::Article.create!(
|
||||
ticket_id: ticket.id,
|
||||
body: text,
|
||||
type: Ticket::Article::Type.find_by(name: 'twitter status'),
|
||||
sender: Ticket::Article::Sender.find_by(name: 'Agent'),
|
||||
internal: false,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
|
||||
channel.reload
|
||||
expect(channel.options[:user][:screen_name]).not_to be ticket.preferences[:channel_screen_name]
|
||||
expect(channel.status_in).to be nil
|
||||
expect(channel.last_log_in).to be nil
|
||||
expect(channel.status_out).to be nil
|
||||
expect(channel.last_log_out).to be nil
|
||||
|
||||
VCR.use_cassette('models/channel/driver/twitter/article_to_tweet_channel_replace') do
|
||||
Scheduler.worker(true)
|
||||
end
|
||||
|
||||
ticket.reload
|
||||
expect(ticket.state.name).to eq('open')
|
||||
expect(ticket.group.name).to eq(Group.first.name)
|
||||
expect(ticket.title).to eq('Today and tomorrow the weather is really')
|
||||
|
||||
article.reload
|
||||
expect(article.from).to eq('@example')
|
||||
expect(article.to).to eq('')
|
||||
expect(article.cc).to be nil
|
||||
expect(article.subject).to be nil
|
||||
expect(article.sender.name).to eq('Agent')
|
||||
expect(article.type.name).to eq('twitter status')
|
||||
expect(article.message_id).to eq('1069382411899817991')
|
||||
expect(article.content_type).to eq('text/plain')
|
||||
expect(article.body).to eq('Today and tomorrow the weather is really...')
|
||||
expect(article.preferences[:links][0][:url]).to eq('https://twitter.com/statuses/1069382411899817991')
|
||||
expect(article.preferences[:links][0][:target]).to eq('_blank')
|
||||
expect(article.preferences[:links][0][:name]).to eq('on Twitter')
|
||||
|
||||
channel.reload
|
||||
expect(channel.status_in).to be nil
|
||||
expect(channel.last_log_in).to be nil
|
||||
expect(channel.status_out).to eq('ok')
|
||||
expect(channel.last_log_out).to eq('')
|
||||
end
|
||||
|
||||
it 'article preferences' do
|
||||
|
||||
org_community = Organization.create_if_not_exists(
|
||||
name: 'Zammad Foundation',
|
||||
)
|
||||
user_community = User.create_or_update(
|
||||
login: 'article.twitter@example.org',
|
||||
firstname: 'Article',
|
||||
lastname: 'Twitter',
|
||||
email: 'article.twitter@example.org',
|
||||
password: '',
|
||||
active: true,
|
||||
roles: [ Role.find_by(name: 'Customer') ],
|
||||
organization_id: org_community.id,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
|
||||
ticket1 = Ticket.create!(
|
||||
group_id: Group.first.id,
|
||||
customer_id: user_community.id,
|
||||
title: 'Tweet 1!',
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
twitter_preferences = {
|
||||
mention_ids: [1_234_567_890],
|
||||
geo: Twitter::NullObject.new,
|
||||
retweeted: false,
|
||||
possibly_sensitive: false,
|
||||
in_reply_to_user_id: 1_234_567_890,
|
||||
place: Twitter::NullObject.new,
|
||||
retweet_count: 0,
|
||||
source: '<a href="http://example.com/software/tweetbot/mac" rel="nofollow">Tweetbot for Mac</a>',
|
||||
favorited: false,
|
||||
truncated: false
|
||||
}
|
||||
preferences = {
|
||||
twitter: TwitterSync.preferences_cleanup(twitter_preferences),
|
||||
links: [
|
||||
{
|
||||
url: 'https://twitter.com/statuses/123',
|
||||
target: '_blank',
|
||||
name: 'on Twitter',
|
||||
},
|
||||
],
|
||||
}
|
||||
article1 = Ticket::Article.create!(
|
||||
ticket_id: ticket1.id,
|
||||
type_id: Ticket::Article::Type.find_by(name: 'twitter status').id,
|
||||
sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id,
|
||||
from: '@example',
|
||||
body: 'some tweet',
|
||||
internal: false,
|
||||
preferences: TwitterSync.preferences_cleanup(preferences),
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
expect(article1.preferences[:twitter]).to be_truthy
|
||||
expect(article1.preferences[:twitter][:mention_ids][0]).to eq(1_234_567_890)
|
||||
expect(article1.preferences[:twitter][:geo].class).to be ActiveSupport::HashWithIndifferentAccess
|
||||
expect(article1.preferences[:twitter][:geo].blank?).to be true
|
||||
expect(article1.preferences[:twitter][:place].class).to be ActiveSupport::HashWithIndifferentAccess
|
||||
expect(article1.preferences[:twitter][:place].blank?).to be true
|
||||
|
||||
twitter_preferences = {
|
||||
mention_ids: [1_234_567_890],
|
||||
geo: Twitter::NullObject.new,
|
||||
retweeted: false,
|
||||
possibly_sensitive: false,
|
||||
in_reply_to_user_id: 1_234_567_890,
|
||||
place: Twitter::NullObject.new,
|
||||
retweet_count: 0,
|
||||
source: '<a href="http://example.com/software/tweetbot/mac" rel="nofollow">Tweetbot for Mac</a>',
|
||||
favorited: false,
|
||||
truncated: false
|
||||
}
|
||||
preferences = TwitterSync.preferences_cleanup(
|
||||
twitter: twitter_preferences,
|
||||
links: [
|
||||
{
|
||||
url: 'https://twitter.com/statuses/123',
|
||||
target: '_blank',
|
||||
name: 'on Twitter',
|
||||
},
|
||||
],
|
||||
)
|
||||
article2 = Ticket::Article.create!(
|
||||
ticket_id: ticket1.id,
|
||||
type_id: Ticket::Article::Type.find_by(name: 'twitter status').id,
|
||||
sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id,
|
||||
from: '@example',
|
||||
body: 'some tweet',
|
||||
internal: false,
|
||||
preferences: TwitterSync.preferences_cleanup(preferences),
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
expect(article2.preferences[:twitter]).to be_truthy
|
||||
expect(article2.preferences[:twitter][:mention_ids][0]).to eq(1_234_567_890)
|
||||
expect(article1.preferences[:twitter][:geo].class).to be ActiveSupport::HashWithIndifferentAccess
|
||||
expect(article1.preferences[:twitter][:geo].blank?).to be true
|
||||
expect(article1.preferences[:twitter][:place].class).to be ActiveSupport::HashWithIndifferentAccess
|
||||
expect(article1.preferences[:twitter][:place].blank?).to be true
|
||||
|
||||
twitter_preferences = {
|
||||
mention_ids: [1_234_567_890],
|
||||
geo: Twitter::Geo.new(coordinates: [1, 1]),
|
||||
retweeted: false,
|
||||
possibly_sensitive: false,
|
||||
in_reply_to_user_id: 1_234_567_890,
|
||||
place: Twitter::Place.new(country: 'da', name: 'do', woeid: 1, id: 1),
|
||||
retweet_count: 0,
|
||||
source: '<a href="http://example.com/software/tweetbot/mac" rel="nofollow">Tweetbot for Mac</a>',
|
||||
favorited: false,
|
||||
truncated: false
|
||||
}
|
||||
preferences = {
|
||||
twitter: TwitterSync.preferences_cleanup(twitter_preferences),
|
||||
links: [
|
||||
{
|
||||
url: 'https://twitter.com/statuses/123',
|
||||
target: '_blank',
|
||||
name: 'on Twitter',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
article3 = Ticket::Article.create!(
|
||||
ticket_id: ticket1.id,
|
||||
type_id: Ticket::Article::Type.find_by(name: 'twitter status').id,
|
||||
sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id,
|
||||
from: '@example',
|
||||
body: 'some tweet',
|
||||
internal: false,
|
||||
preferences: preferences,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
|
||||
expect(article3.preferences[:twitter]).to be_truthy
|
||||
expect(article3.preferences[:twitter][:mention_ids][0]).to eq(1_234_567_890)
|
||||
expect(article3.preferences[:twitter][:geo].class).to be ActiveSupport::HashWithIndifferentAccess
|
||||
expect(article3.preferences[:twitter][:geo]).to eq({ 'coordinates' => [1, 1] })
|
||||
expect(article3.preferences[:twitter][:place].class).to be ActiveSupport::HashWithIndifferentAccess
|
||||
expect(article3.preferences[:twitter][:place]).to eq({ 'country' => 'da', 'name' => 'do', 'woeid' => 1, 'id' => 1 })
|
||||
|
||||
twitter_preferences = {
|
||||
mention_ids: [1_234_567_890],
|
||||
geo: Twitter::Geo.new(coordinates: [1, 1]),
|
||||
retweeted: false,
|
||||
possibly_sensitive: false,
|
||||
in_reply_to_user_id: 1_234_567_890,
|
||||
place: Twitter::Place.new(country: 'da', name: 'do', woeid: 1, id: 1),
|
||||
retweet_count: 0,
|
||||
source: '<a href="http://example.com/software/tweetbot/mac" rel="nofollow">Tweetbot for Mac</a>',
|
||||
favorited: false,
|
||||
truncated: false
|
||||
}
|
||||
preferences = TwitterSync.preferences_cleanup(
|
||||
twitter: twitter_preferences,
|
||||
links: [
|
||||
{
|
||||
url: 'https://twitter.com/statuses/123',
|
||||
target: '_blank',
|
||||
name: 'on Twitter',
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
article4 = Ticket::Article.create!(
|
||||
ticket_id: ticket1.id,
|
||||
type_id: Ticket::Article::Type.find_by(name: 'twitter status').id,
|
||||
sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id,
|
||||
from: '@example',
|
||||
body: 'some tweet',
|
||||
internal: false,
|
||||
preferences: preferences,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
expect(article4.preferences[:twitter]).to be_truthy
|
||||
expect(article4.preferences[:twitter]).to be_truthy
|
||||
expect(article4.preferences[:twitter][:mention_ids][0]).to eq(1_234_567_890)
|
||||
expect(article4.preferences[:twitter][:geo].class).to be ActiveSupport::HashWithIndifferentAccess
|
||||
expect(article4.preferences[:twitter][:geo]).to eq({ 'coordinates' => [1, 1] })
|
||||
expect(article4.preferences[:twitter][:place].class).to be ActiveSupport::HashWithIndifferentAccess
|
||||
expect(article4.preferences[:twitter][:place]).to eq({ 'country' => 'da', 'name' => 'do', 'woeid' => 1, 'id' => 1 })
|
||||
end
|
||||
end
|
65
spec/models/channel_spec.rb
Normal file
65
spec/models/channel_spec.rb
Normal file
|
@ -0,0 +1,65 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Channel do
|
||||
describe '#fetch' do
|
||||
around do |example|
|
||||
VCR.use_cassette(cassette, match_requests_on: %i[method uri oauth_headers]) { example.run }
|
||||
end
|
||||
|
||||
context 'for Twitter driver' do
|
||||
subject(:twitter_channel) { create(:twitter_channel) }
|
||||
|
||||
context 'with invalid token' do
|
||||
let(:cassette) { 'models/channel/driver/twitter/fetch_channel_invalid' }
|
||||
|
||||
it 'returns false' do
|
||||
expect(twitter_channel.fetch(true)).to be(false)
|
||||
end
|
||||
|
||||
it 'sets error/nil status attributes' do
|
||||
expect { twitter_channel.fetch(true) }
|
||||
.to change { twitter_channel.reload.attributes }
|
||||
.to hash_including(
|
||||
'status_in' => 'error',
|
||||
'last_log_in' => "Can't use Channel::Driver::Twitter: " \
|
||||
'#<Twitter::Error::Unauthorized: Invalid or expired token.>',
|
||||
'status_out' => nil,
|
||||
'last_log_out' => nil
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with valid token' do
|
||||
let(:cassette) { 'models/channel/driver/twitter/fetch_channel_valid' }
|
||||
|
||||
it 'returns true' do
|
||||
expect(twitter_channel.fetch(true)).to be(true)
|
||||
end
|
||||
|
||||
it 'sets successful status attributes' do
|
||||
expect { twitter_channel.fetch(true) }
|
||||
.to change { twitter_channel.reload.attributes }
|
||||
.to hash_including(
|
||||
'status_in' => 'ok',
|
||||
'last_log_in' => '',
|
||||
'status_out' => nil,
|
||||
'last_log_out' => nil
|
||||
)
|
||||
end
|
||||
|
||||
it 'adds tickets as appropriate' do
|
||||
expect { twitter_channel.fetch(true) }
|
||||
.to change { Ticket.count }.by(26)
|
||||
|
||||
expect(Ticket.last.attributes).to include(
|
||||
'title' => 'Wir haben unsere DMs deaktiviert. ' \
|
||||
'Leider können wir dank der neuen Twitter API k...',
|
||||
'preferences' => { 'channel_id' => twitter_channel.id,
|
||||
'channel_screen_name' => twitter_channel.options[:user][:screen_name] },
|
||||
'customer_id' => User.find_by(firstname: 'Ccc', lastname: 'Event Logistics').id
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
115
spec/models/ticket/article_spec.rb
Normal file
115
spec/models/ticket/article_spec.rb
Normal file
|
@ -0,0 +1,115 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Ticket::Article do
|
||||
describe 'hooks on creation' do
|
||||
context 'of outgoing article' do
|
||||
context 'over Twitter' do
|
||||
subject!(:twitter_article) { create(:twitter_article, sender_name: 'Agent') }
|
||||
let(:channel) { Channel.find(twitter_article.ticket.preferences[:channel_id]) }
|
||||
|
||||
describe 'background job actions' do
|
||||
let(:run_bg_jobs) do
|
||||
lambda do
|
||||
VCR.use_cassette(cassette, match_requests_on: %i[method uri oauth_headers]) do
|
||||
Scheduler.worker(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
let(:cassette) { 'models/channel/driver/twitter/article_to_tweet' }
|
||||
|
||||
it 'sets #from attribute to sender’s Twitter handle' do
|
||||
expect(&run_bg_jobs)
|
||||
.to change { twitter_article.reload.from }
|
||||
.to('@example')
|
||||
end
|
||||
|
||||
it 'sets #to attribute to recipient’s Twitter handle' do
|
||||
expect(&run_bg_jobs)
|
||||
.to change { twitter_article.reload.to }
|
||||
.to('') # Tweet in VCR cassette is addressed to no one
|
||||
end
|
||||
|
||||
it 'sets #message_id attribute to tweet ID (https://twitter.com/statuses/<id>)' do
|
||||
expect(&run_bg_jobs)
|
||||
.to change { twitter_article.reload.message_id }
|
||||
.to('1069382411899817990')
|
||||
end
|
||||
|
||||
it 'sets #preferences hash with tweet metadata' do
|
||||
expect(&run_bg_jobs)
|
||||
.to change { twitter_article.reload.preferences }
|
||||
.to(hash_including('twitter', 'links'))
|
||||
|
||||
expect(twitter_article.preferences[:links].first)
|
||||
.to include(
|
||||
'name' => 'on Twitter',
|
||||
'target' => '_blank',
|
||||
'url' => "https://twitter.com/statuses/#{twitter_article.message_id}"
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not change #cc attribute' do
|
||||
expect(&run_bg_jobs).not_to change { twitter_article.reload.cc }
|
||||
end
|
||||
|
||||
it 'does not change #subject attribute' do
|
||||
expect(&run_bg_jobs).not_to change { twitter_article.reload.subject }
|
||||
end
|
||||
|
||||
it 'does not change #content_type attribute' do
|
||||
expect(&run_bg_jobs).not_to change { twitter_article.reload.content_type }
|
||||
end
|
||||
|
||||
it 'does not change #body attribute' do
|
||||
expect(&run_bg_jobs).not_to change { twitter_article.reload.body }
|
||||
end
|
||||
|
||||
it 'does not change #sender association' do
|
||||
expect(&run_bg_jobs).not_to change { twitter_article.reload.sender }
|
||||
end
|
||||
|
||||
it 'does not change #type association' do
|
||||
expect(&run_bg_jobs).not_to change { twitter_article.reload.type }
|
||||
end
|
||||
|
||||
it 'sets appropriate status attributes on the ticket’s channel' do
|
||||
expect(&run_bg_jobs)
|
||||
.to change { channel.reload.attributes }
|
||||
.to hash_including(
|
||||
'status_in' => nil,
|
||||
'last_log_in' => nil,
|
||||
'status_out' => 'ok',
|
||||
'last_log_out' => ''
|
||||
)
|
||||
end
|
||||
|
||||
context 'when the original channel (specified in ticket.preferences) was deleted' do
|
||||
context 'but a new one with the same screen_name exists' do
|
||||
let(:cassette) { 'models/channel/driver/twitter/article_to_tweet_channel_replace' }
|
||||
let(:new_channel) { create(:twitter_channel) }
|
||||
|
||||
before do
|
||||
channel.destroy
|
||||
|
||||
expect(new_channel.options[:user][:screen_name])
|
||||
.to eq(channel.options[:user][:screen_name])
|
||||
end
|
||||
|
||||
it 'sets appropriate status attributes on the new channel' do
|
||||
expect(&run_bg_jobs)
|
||||
.to change { new_channel.reload.attributes }
|
||||
.to hash_including(
|
||||
'status_in' => nil,
|
||||
'last_log_in' => nil,
|
||||
'status_out' => 'ok',
|
||||
'last_log_out' => ''
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -10,4 +10,12 @@ VCR.configure do |config|
|
|||
uri.host.include?(site)
|
||||
end
|
||||
end
|
||||
|
||||
config.register_request_matcher(:oauth_headers) do |r1, r2|
|
||||
without_onetime_oauth_params = ->(params) { params.gsub(/oauth_(nonce|signature|timestamp)="[^"]+", /, '') }
|
||||
|
||||
r1.headers.except('Authorization') == r2.headers.except('Authorization') &&
|
||||
r1.headers['Authorization']&.map(&without_onetime_oauth_params) ==
|
||||
r2.headers['Authorization']&.map(&without_onetime_oauth_params)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="some",
|
||||
oauth_signature="some%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795610", oauth_token="some",
|
||||
oauth_timestamp="1543795610", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
|
|
@ -12,7 +12,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="some",
|
||||
oauth_signature="some%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795610", oauth_token="some",
|
||||
oauth_timestamp="1543795610", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
|
|
@ -12,7 +12,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="8efb0d12349b48e6acaa2ec6ff224cc2",
|
||||
oauth_signature="uABvZoC5sN%2F68E4oxp6Qk6SxO2Y%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795852", oauth_token="some",
|
||||
oauth_timestamp="1543795852", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -526,7 +526,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="83003a1356235c21998dbe47bd20e034",
|
||||
oauth_signature="KtBt4mbxUM9pQeEHXi%2BywugYuqk%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795853", oauth_token="some",
|
||||
oauth_timestamp="1543795853", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -771,7 +771,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="bb5275ca035610773ca6172601e35be6",
|
||||
oauth_signature="yk2RDPjsEnljxqdWfmCOjS01ylg%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795854", oauth_token="some",
|
||||
oauth_timestamp="1543795854", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -1229,7 +1229,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="756b2ebc88106059e7afdec3979455ed",
|
||||
oauth_signature="KYRdmzJiODGS%2BYqqmYUPhwq2Fwc%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795855", oauth_token="some",
|
||||
oauth_timestamp="1543795855", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -1421,7 +1421,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="de1e719ccd92c862ad99062c09332301",
|
||||
oauth_signature="ueto3kSV%2BcRxu%2FXOod5N4CqW%2BNk%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795855", oauth_token="some",
|
||||
oauth_timestamp="1543795855", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -1666,7 +1666,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="4b3ceecc83d55c1b720580fb1e3d36db",
|
||||
oauth_signature="sYaGT3aj%2BUSwTMuTWouWuQti3BQ%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795856", oauth_token="some",
|
||||
oauth_timestamp="1543795856", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -1966,7 +1966,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="9778307d17972ae3edc5b5d2a5530bb4",
|
||||
oauth_signature="qf8Acv3oYLzER%2BF53HL%2F7xQfbvM%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795857", oauth_token="some",
|
||||
oauth_timestamp="1543795857", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -2215,7 +2215,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="7ff1412c085625c63956f13fee4a0066",
|
||||
oauth_signature="XTJJazMUukMF7V0QJysnt0RIbV0%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795857", oauth_token="some",
|
||||
oauth_timestamp="1543795857", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -3371,7 +3371,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="db0fdd3eedd9c9a4c4a7dea0f6dd3cc9",
|
||||
oauth_signature="5OrIGadCeuoU%2BpDkNkUYviu0awo%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795861", oauth_token="some",
|
||||
oauth_timestamp="1543795861", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -3511,7 +3511,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="12ed1f52923c07c7a35ea78d2d177e7d",
|
||||
oauth_signature="UEKkeqCiwZnRNZK2xL9yFh2jYLA%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795862", oauth_token="some",
|
||||
oauth_timestamp="1543795862", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -4498,7 +4498,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="df1d4b0020304d719b581ad2a89ac722",
|
||||
oauth_signature="ZVjS8VWcWdYXZpr2I3ZX56DOWTE%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795865", oauth_token="some",
|
||||
oauth_timestamp="1543795865", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -4746,7 +4746,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="2dbf57fffac364cfd3d3bb63b01ca49f",
|
||||
oauth_signature="DoKz2xUY3qPs%2Bnscylkyemx7acY%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795866", oauth_token="some",
|
||||
oauth_timestamp="1543795866", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -5046,7 +5046,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="43788c0bf65af0ea0e0e59b029a98a58",
|
||||
oauth_signature="8H%2Bw00OP0m9rjOpLRdQia%2BOjtIo%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795866", oauth_token="some",
|
||||
oauth_timestamp="1543795866", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -5298,7 +5298,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="813d8700f25ff78f741b2c778b312cf7",
|
||||
oauth_signature="Ha0OSsfG5yFKzMdNbRHC2Fn9tKo%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795867", oauth_token="some",
|
||||
oauth_timestamp="1543795867", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -5861,7 +5861,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="6ba7598ee7171aa89d5172a75b5e7634",
|
||||
oauth_signature="RrL7Yjl6GE4OJ4IJX4H5GiYLFJU%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795868", oauth_token="some",
|
||||
oauth_timestamp="1543795868", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -6107,7 +6107,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="3a2fab4325efb72a9fe7f466fdb96910",
|
||||
oauth_signature="%2BwYKPEqhsVvUK7IDnOSZpB4J1h0%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795869", oauth_token="some",
|
||||
oauth_timestamp="1543795869", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -6301,7 +6301,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="bab0a69fdb7b4718244cb9e3a86f28d4",
|
||||
oauth_signature="WrbLQ6TcP7153IxnujSFyoJ462w%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795870", oauth_token="some",
|
||||
oauth_timestamp="1543795870", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
@ -6495,7 +6495,7 @@ http_interactions:
|
|||
Authorization:
|
||||
- OAuth oauth_consumer_key="some", oauth_nonce="b88d83169c5ad58749312e180ccb580a",
|
||||
oauth_signature="mwtfaPm2G4x5pRnOr79wWj%2F4UOc%3D", oauth_signature_method="HMAC-SHA1",
|
||||
oauth_timestamp="1543795870", oauth_token="some",
|
||||
oauth_timestamp="1543795870", oauth_token="key",
|
||||
oauth_version="1.0"
|
||||
Connection:
|
||||
- close
|
||||
|
|
Loading…
Reference in a new issue