diff --git a/spec/factories/channel.rb b/spec/factories/channel.rb index 625f09504..e12e4776d 100644 --- a/spec/factories/channel.rb +++ b/spec/factories/channel.rb @@ -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 diff --git a/spec/factories/ticket.rb b/spec/factories/ticket.rb index 35b03275c..917784ae7 100644 --- a/spec/factories/ticket.rb +++ b/spec/factories/ticket.rb @@ -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 diff --git a/spec/factories/ticket/article.rb b/spec/factories/ticket/article.rb index efcd7f845..f7e18d416 100644 --- a/spec/factories/ticket/article.rb +++ b/spec/factories/ticket/article.rb @@ -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 diff --git a/spec/lib/twitter_sync_spec.rb b/spec/lib/twitter_sync_spec.rb new file mode 100644 index 000000000..f504ed244 --- /dev/null +++ b/spec/lib/twitter_sync_spec.rb @@ -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 diff --git a/spec/models/channel/driver/twitter_spec.rb b/spec/models/channel/driver/twitter_spec.rb deleted file mode 100644 index c17820a4f..000000000 --- a/spec/models/channel/driver/twitter_spec.rb +++ /dev/null @@ -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: #') - 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: 'Tweetbot for Mac', - 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: 'Tweetbot for Mac', - 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: 'Tweetbot for Mac', - 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: 'Tweetbot for Mac', - 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 diff --git a/spec/models/channel_spec.rb b/spec/models/channel_spec.rb new file mode 100644 index 000000000..986dbb434 --- /dev/null +++ b/spec/models/channel_spec.rb @@ -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: " \ + '#', + '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 diff --git a/spec/models/ticket/article_spec.rb b/spec/models/ticket/article_spec.rb new file mode 100644 index 000000000..20a1319e6 --- /dev/null +++ b/spec/models/ticket/article_spec.rb @@ -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/)' 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 diff --git a/spec/support/vcr.rb b/spec/support/vcr.rb index 4010a35cd..d0693331f 100644 --- a/spec/support/vcr.rb +++ b/spec/support/vcr.rb @@ -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 diff --git a/test/data/vcr_cassettes/models/channel/driver/twitter/article_to_tweet.yml b/test/data/vcr_cassettes/models/channel/driver/twitter/article_to_tweet.yml index c78783b1b..bcb400879 100644 --- a/test/data/vcr_cassettes/models/channel/driver/twitter/article_to_tweet.yml +++ b/test/data/vcr_cassettes/models/channel/driver/twitter/article_to_tweet.yml @@ -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 diff --git a/test/data/vcr_cassettes/models/channel/driver/twitter/article_to_tweet_channel_replace.yml b/test/data/vcr_cassettes/models/channel/driver/twitter/article_to_tweet_channel_replace.yml index 0b2ad99ad..66b357a35 100644 --- a/test/data/vcr_cassettes/models/channel/driver/twitter/article_to_tweet_channel_replace.yml +++ b/test/data/vcr_cassettes/models/channel/driver/twitter/article_to_tweet_channel_replace.yml @@ -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 diff --git a/test/data/vcr_cassettes/models/channel/driver/twitter/fetch_channel_valid.yml b/test/data/vcr_cassettes/models/channel/driver/twitter/fetch_channel_valid.yml index de11be33f..493c1b8f7 100644 --- a/test/data/vcr_cassettes/models/channel/driver/twitter/fetch_channel_valid.yml +++ b/test/data/vcr_cassettes/models/channel/driver/twitter/fetch_channel_valid.yml @@ -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