From 29670cd05aaee86477aab95b4a3bda95470b4501 Mon Sep 17 00:00:00 2001 From: Ryan Lue Date: Thu, 23 Apr 2020 16:16:55 +0800 Subject: [PATCH] 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. --- spec/models/channel/driver/twitter_spec.rb | 37 ++++---- ...condition_handling_for_incoming_tweets.yml | 84 +++++++++++++++++++ 2 files changed, 107 insertions(+), 14 deletions(-) create mode 100644 test/data/vcr_cassettes/models/channel/driver/twitter/race_condition_when_fetch_finds_a_half-processed_outgoing_tweet_skips_race_condition_handling_for_incoming_tweets.yml diff --git a/spec/models/channel/driver/twitter_spec.rb b/spec/models/channel/driver/twitter_spec.rb index 0b63fe5e8..407f98438 100644 --- a/spec/models/channel/driver/twitter_spec.rb +++ b/spec/models/channel/driver/twitter_spec.rb @@ -934,12 +934,25 @@ RSpec.describe Channel::Driver::Twitter do search_term: 'zammadzammadzammad', custom_options: { user: { - # Must match outgoing tweet author Twitter user ID + # "outgoing" tweets = authored by this Twitter user ID id: '1205290247124217856', }, }) 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') } 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 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 @@ -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 expect { channel.fetch } .to not_change(Ticket::Article, :count) + .and change(Time, :current).by_at_least(5) 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 diff --git a/test/data/vcr_cassettes/models/channel/driver/twitter/race_condition_when_fetch_finds_a_half-processed_outgoing_tweet_skips_race_condition_handling_for_incoming_tweets.yml b/test/data/vcr_cassettes/models/channel/driver/twitter/race_condition_when_fetch_finds_a_half-processed_outgoing_tweet_skips_race_condition_handling_for_incoming_tweets.yml new file mode 100644 index 000000000..459c451e7 --- /dev/null +++ b/test/data/vcr_cassettes/models/channel/driver/twitter/race_condition_when_fetch_finds_a_half-processed_outgoing_tweet_skips_race_condition_handling_for_incoming_tweets.yml @@ -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