Testing: Fix false positive and cover edge case for Channel::Driver::Twitter#fetch

Originally, a spec for race condition handling
in Channel::Driver::Twitter#fetch
used a `travel_back` call in the wrong place:
it needed to be called _after_ `subject!(:channel)` was created,
but since it was placed in an `around` block,
it ran before all preceding setup blocks.

Moving it to a `before` block changed the order in which it was called,
thus fixing a false positive in the test.

An additional expectation was added
to prevent this false positive in the future,
and a new test case was added to capture yet another edge case.
This commit is contained in:
Ryan Lue 2020-04-23 16:16:55 +08:00 committed by Thorsten Eckel
parent 3f28c5f54e
commit 29670cd05a
2 changed files with 107 additions and 14 deletions

View file

@ -934,12 +934,25 @@ RSpec.describe Channel::Driver::Twitter do
search_term: 'zammadzammadzammad', search_term: 'zammadzammadzammad',
custom_options: { custom_options: {
user: { user: {
# Must match outgoing tweet author Twitter user ID # "outgoing" tweets = authored by this Twitter user ID
id: '1205290247124217856', id: '1205290247124217856',
}, },
}) })
end end
# 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 test expectation logic:
#
# expect { channel.fetch }.to change(Time, :current).by_at_least(5)
#
# So, we unfreeze time here.
before { travel_back }
let!(:tweet) { create(:twitter_article, body: 'zammadzammadzammad') } let!(:tweet) { create(:twitter_article, body: 'zammadzammadzammad') }
context '(i.e., after the BG job has posted the article to Twitter…' do context '(i.e., after the BG job has posted the article to Twitter…' do
@ -954,19 +967,6 @@ RSpec.describe Channel::Driver::Twitter do
YML YML
around do |example| 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? # Run BG job (Why not use Scheduler.worker?
# It led to hangs & failures elsewhere in test suite.) # It led to hangs & failures elsewhere in test suite.)
Thread.new do Thread.new do
@ -978,9 +978,18 @@ RSpec.describe Channel::Driver::Twitter do
it 'does not import the duplicate tweet (waits up to 60s for BG job to finish)' do it 'does not import the duplicate tweet (waits up to 60s for BG job to finish)' do
expect { channel.fetch } expect { channel.fetch }
.to not_change(Ticket::Article, :count) .to not_change(Ticket::Article, :count)
.and change(Time, :current).by_at_least(5)
end end
end end
end end
# To reproduce this test case, the VCR cassette has been modified
# so that the fetched tweet has a different ("incoming") author user ID.
it 'skips race condition handling for incoming tweets' do
expect { channel.fetch }
.to change(Ticket::Article, :count)
.and change(Time, :current).by_at_most(1)
end
end end
end end

View file

@ -0,0 +1,84 @@
---
http_interactions:
- request:
method: get
uri: https://api.twitter.com/1.1/search/tweets.json?count=100&q=zammadzammadzammad&result_type=mixed
body:
encoding: UTF-8
string: ''
headers:
User-Agent:
- TwitterRubyGem/6.2.0
Authorization:
- OAuth oauth_consumer_key="REDACTED", oauth_nonce="37876a49fa0c474bd7732dea70083056",
oauth_signature="f159trPoyOipHcp%2BPlL33Kh2nO4%3D", oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1583840887", oauth_token="REDACTED",
oauth_version="1.0"
Connection:
- close
Host:
- api.twitter.com
response:
status:
code: 200
message: OK
headers:
Cache-Control:
- no-cache, no-store, must-revalidate, pre-check=0, post-check=0
Connection:
- close
Content-Disposition:
- attachment; filename=json.json
Content-Length:
- '2346'
Content-Type:
- application/json;charset=utf-8
Date:
- Tue, 10 Mar 2020 11:48:07 GMT
Expires:
- Tue, 31 Mar 1981 05:00:00 GMT
Last-Modified:
- Tue, 10 Mar 2020 11:48:07 GMT
Pragma:
- no-cache
Server:
- tsa_m
Set-Cookie:
- guest_id=v1%3A158384088754038008; Max-Age=63072000; Expires=Thu, 10 Mar 2022
11:48:07 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
- lang=en; Path=/
- personalization_id="v1_N5oIKIuBSmkhNAVvuM7dWQ=="; Max-Age=63072000; Expires=Thu,
10 Mar 2022 11:48:07 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
Status:
- 200 OK
Strict-Transport-Security:
- max-age=631138519
X-Access-Level:
- read-write-directmessages
X-Connection-Hash:
- a615db05b45fdf48368de9e967c599c2
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- SAMEORIGIN
X-Rate-Limit-Limit:
- '180'
X-Rate-Limit-Remaining:
- '177'
X-Rate-Limit-Reset:
- '1583841131'
X-Response-Time:
- '134'
X-Transaction:
- '004926c000957504'
X-Twitter-Response-Tags:
- BouncerCompliant
X-Xss-Protection:
- '0'
body:
encoding: UTF-8
string: '{"statuses":[{"created_at":"Tue Mar 10 11:48:05 +0000 2020","id":1237344473199153152,"id_str":"1237344473199153152","text":"zammadzammadzammad","truncated":false,"entities":{"hashtags":[],"symbols":[],"user_mentions":[],"urls":[]},"metadata":{"iso_language_code":"lv","result_type":"recent"},"source":"\u003ca
href=\"https:\/\/zammad.com\/\" rel=\"nofollow\"\u003ezammad\u003c\/a\u003e","in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":1205290247124217857,"id_str":"1205290247124217857","name":"pennbrooke","screen_name":"pennbrooke1","location":"","description":"","url":null,"entities":{"description":{"urls":[]}},"protected":false,"followers_count":0,"friends_count":1,"listed_count":0,"created_at":"Fri
Dec 13 00:56:10 +0000 2019","favourites_count":0,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":19,"lang":null,"contributors_enabled":false,"is_translator":false,"is_translation_enabled":false,"profile_background_color":"F5F8FA","profile_background_image_url":null,"profile_background_image_url_https":null,"profile_background_tile":false,"profile_image_url":"http:\/\/abs.twimg.com\/sticky\/default_profile_images\/default_profile_normal.png","profile_image_url_https":"https:\/\/abs.twimg.com\/sticky\/default_profile_images\/default_profile_normal.png","profile_link_color":"1DA1F2","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"has_extended_profile":false,"default_profile":true,"default_profile_image":true,"following":false,"follow_request_sent":false,"notifications":false,"translator_type":"none"},"geo":null,"coordinates":null,"place":null,"contributors":null,"is_quote_status":false,"retweet_count":0,"favorite_count":0,"favorited":false,"retweeted":false,"lang":"lv"}],"search_metadata":{"completed_in":0.017,"max_id":1237344473199153152,"max_id_str":"1237344473199153152","next_results":"?max_id=1237344473199153151&q=zammadzammadzammad&count=100&include_entities=1&result_type=mixed","query":"zammadzammadzammad","refresh_url":"?since_id=1237344473199153152&q=zammadzammadzammad&result_type=mixed&include_entities=1","count":100,"since_id":0,"since_id_str":"0"}}'
http_version:
recorded_at: Tue, 10 Mar 2020 11:48:07 GMT