From 064756e88dea27caba0e4c0fb959b1e4ac4bb749 Mon Sep 17 00:00:00 2001 From: Alex Musayev Date: Sat, 2 Nov 2024 18:14:34 +0100 Subject: [PATCH] F2: Test coverage for the post publisher (#597) * Update gems (includes Rails 8.0.0.rc2) * Remove redundant requires * Test post publisher happy path * Disable Rubocop standard temporarily * Simplify downloader errors handling * Define default HTTP client * Fix class name * Bypass exceptions * Add a comment * Test coverage * Remove logging from the service * Test comments publication * Test errors handling * Rubocop * Fix a test --- .rubocop.yml | 2 +- Gemfile.lock | 147 +++++------ app/services/post_publisher.rb | 7 +- lib/freefeed/downloader.rb | 6 +- .../freefeed/authenticated_request_spec.rb | 2 - spec/lib/freefeed/client_spec.rb | 2 - spec/lib/freefeed/downloader_spec.rb | 12 +- spec/lib/freefeed/error_spec.rb | 2 - spec/lib/freefeed/request_spec.rb | 2 - spec/lib/freefeed/v1/attachments_spec.rb | 2 - spec/lib/freefeed/v1/comments_spec.rb | 2 - spec/lib/freefeed/v1/posts_spec.rb | 2 - spec/lib/freefeed/v2/notifications_spec.rb | 2 - spec/lib/freefeed/v2/posts_spec.rb | 2 - spec/lib/freefeed/v2/timelines_spec.rb | 2 - spec/lib/freefeed/v2/users_spec.rb | 2 - spec/models/feed_spec.rb | 1 - spec/services/post_publisher_spec.rb | 249 +++++++++++++++++- 18 files changed, 330 insertions(+), 116 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 4b2f6915..6c93f247 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,5 +1,5 @@ require: - - standard + # - standard - rubocop-performance - rubocop-rails - rubocop-rspec diff --git a/Gemfile.lock b/Gemfile.lock index f560523e..c91b5f97 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,29 +3,29 @@ GEM specs: aasm (5.5.0) concurrent-ruby (~> 1.0) - actioncable (8.0.0.beta1) - actionpack (= 8.0.0.beta1) - activesupport (= 8.0.0.beta1) + actioncable (8.0.0.rc2) + actionpack (= 8.0.0.rc2) + activesupport (= 8.0.0.rc2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (8.0.0.beta1) - actionpack (= 8.0.0.beta1) - activejob (= 8.0.0.beta1) - activerecord (= 8.0.0.beta1) - activestorage (= 8.0.0.beta1) - activesupport (= 8.0.0.beta1) + actionmailbox (8.0.0.rc2) + actionpack (= 8.0.0.rc2) + activejob (= 8.0.0.rc2) + activerecord (= 8.0.0.rc2) + activestorage (= 8.0.0.rc2) + activesupport (= 8.0.0.rc2) mail (>= 2.8.0) - actionmailer (8.0.0.beta1) - actionpack (= 8.0.0.beta1) - actionview (= 8.0.0.beta1) - activejob (= 8.0.0.beta1) - activesupport (= 8.0.0.beta1) + actionmailer (8.0.0.rc2) + actionpack (= 8.0.0.rc2) + actionview (= 8.0.0.rc2) + activejob (= 8.0.0.rc2) + activesupport (= 8.0.0.rc2) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.0.0.beta1) - actionview (= 8.0.0.beta1) - activesupport (= 8.0.0.beta1) + actionpack (8.0.0.rc2) + actionview (= 8.0.0.rc2) + activesupport (= 8.0.0.rc2) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -33,35 +33,35 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.0.0.beta1) - actionpack (= 8.0.0.beta1) - activerecord (= 8.0.0.beta1) - activestorage (= 8.0.0.beta1) - activesupport (= 8.0.0.beta1) + actiontext (8.0.0.rc2) + actionpack (= 8.0.0.rc2) + activerecord (= 8.0.0.rc2) + activestorage (= 8.0.0.rc2) + activesupport (= 8.0.0.rc2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.0.0.beta1) - activesupport (= 8.0.0.beta1) + actionview (8.0.0.rc2) + activesupport (= 8.0.0.rc2) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (8.0.0.beta1) - activesupport (= 8.0.0.beta1) + activejob (8.0.0.rc2) + activesupport (= 8.0.0.rc2) globalid (>= 0.3.6) - activemodel (8.0.0.beta1) - activesupport (= 8.0.0.beta1) - activerecord (8.0.0.beta1) - activemodel (= 8.0.0.beta1) - activesupport (= 8.0.0.beta1) + activemodel (8.0.0.rc2) + activesupport (= 8.0.0.rc2) + activerecord (8.0.0.rc2) + activemodel (= 8.0.0.rc2) + activesupport (= 8.0.0.rc2) timeout (>= 0.4.0) - activestorage (8.0.0.beta1) - actionpack (= 8.0.0.beta1) - activejob (= 8.0.0.beta1) - activerecord (= 8.0.0.beta1) - activesupport (= 8.0.0.beta1) + activestorage (8.0.0.rc2) + actionpack (= 8.0.0.rc2) + activejob (= 8.0.0.rc2) + activerecord (= 8.0.0.rc2) + activesupport (= 8.0.0.rc2) marcel (~> 1.0) - activesupport (8.0.0.beta1) + activesupport (8.0.0.rc2) base64 benchmark (>= 0.3) bigdecimal @@ -85,7 +85,7 @@ GEM bigdecimal (3.1.8) bootsnap (1.18.4) msgpack (~> 1.2) - brakeman (6.2.1) + brakeman (6.2.2) racc builder (3.3.0) bundler-audit (0.9.2) @@ -99,7 +99,7 @@ GEM bigdecimal rexml crass (1.0.6) - date (3.3.4) + date (3.4.0) diff-lcs (1.5.1) docile (1.4.1) domain_name (0.6.20240107) @@ -107,8 +107,9 @@ GEM dry-configurable (1.2.0) dry-core (~> 1.0, < 2) zeitwerk (~> 2.6) - dry-core (1.0.1) + dry-core (1.0.2) concurrent-ruby (~> 1.0) + logger zeitwerk (~> 2.6) dry-inflector (1.1.0) dry-initializer (3.1.1) @@ -134,8 +135,8 @@ GEM erubi (1.13.0) factory_bot (6.5.0) activesupport (>= 5.0.0) - factory_bot_rails (6.4.3) - factory_bot (~> 6.4) + factory_bot_rails (6.4.4) + factory_bot (~> 6.5) railties (>= 5.0.0) ffi (1.17.0-aarch64-linux-gnu) ffi (1.17.0-arm-linux-gnu) @@ -149,7 +150,7 @@ GEM globalid (1.2.1) activesupport (>= 6.1) hashdiff (1.1.1) - honeybadger (5.16.0) + honeybadger (5.19.1) logger http (5.2.0) addressable (~> 2.8) @@ -168,14 +169,14 @@ GEM reline (>= 0.4.2) jsbundling-rails (1.3.1) railties (>= 6.0.0) - json (2.7.2) + json (2.7.5) language_server-protocol (3.17.0.3) lint_roller (1.1.0) llhttp-ffi (0.5.0) ffi-compiler (~> 1.0) rake (~> 13.0) logger (1.6.1) - loofah (2.22.0) + loofah (2.23.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -201,7 +202,7 @@ GEM timeout net-smtp (0.5.0) net-protocol - nio4r (2.7.3) + nio4r (2.7.4) nokogiri (1.16.7-aarch64-linux) racc (~> 1.4) nokogiri (1.16.7-arm-linux) @@ -217,12 +218,12 @@ GEM optimist (3.1.0) ostruct (0.6.0) parallel (1.26.3) - parser (3.3.5.0) + parser (3.3.5.1) ast (~> 2.4.1) racc patience_diff (1.2.0) optimist (~> 3.0) - pg (1.5.8) + pg (1.5.9) propshaft (1.1.0) actionpack (>= 7.0.0) activesupport (>= 7.0.0) @@ -247,23 +248,22 @@ GEM rack (>= 3.0.0) rack-test (2.1.0) rack (>= 1.3) - rackup (2.1.0) + rackup (2.2.0) rack (>= 3) - webrick (~> 1.8) - rails (8.0.0.beta1) - actioncable (= 8.0.0.beta1) - actionmailbox (= 8.0.0.beta1) - actionmailer (= 8.0.0.beta1) - actionpack (= 8.0.0.beta1) - actiontext (= 8.0.0.beta1) - actionview (= 8.0.0.beta1) - activejob (= 8.0.0.beta1) - activemodel (= 8.0.0.beta1) - activerecord (= 8.0.0.beta1) - activestorage (= 8.0.0.beta1) - activesupport (= 8.0.0.beta1) + rails (8.0.0.rc2) + actioncable (= 8.0.0.rc2) + actionmailbox (= 8.0.0.rc2) + actionmailer (= 8.0.0.rc2) + actionpack (= 8.0.0.rc2) + actiontext (= 8.0.0.rc2) + actionview (= 8.0.0.rc2) + activejob (= 8.0.0.rc2) + activemodel (= 8.0.0.rc2) + activerecord (= 8.0.0.rc2) + activestorage (= 8.0.0.rc2) + activesupport (= 8.0.0.rc2) bundler (>= 1.15.0) - railties (= 8.0.0.beta1) + railties (= 8.0.0.rc2) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -271,9 +271,9 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (8.0.0.beta1) - actionpack (= 8.0.0.beta1) - activesupport (= 8.0.0.beta1) + railties (8.0.0.rc2) + actionpack (= 8.0.0.rc2) + activesupport (= 8.0.0.rc2) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -295,8 +295,8 @@ GEM regexp_parser (2.9.2) reline (0.5.10) io-console (~> 0.5) - rexml (3.3.8) - rspec-core (3.13.1) + rexml (3.3.9) + rspec-core (3.13.2) rspec-support (~> 3.13.0) rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) @@ -325,19 +325,19 @@ GEM rubocop-ast (>= 1.32.2, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.32.3) + rubocop-ast (1.33.0) parser (>= 3.3.1.0) rubocop-factory_bot (2.26.1) rubocop (~> 1.61) rubocop-performance (1.22.1) rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails (2.26.2) + rubocop-rails (2.27.0) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.52.0, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rspec (3.1.0) + rubocop-rspec (3.2.0) rubocop (~> 1.61) rubocop-rspec_rails (2.30.0) rubocop (~> 1.61) @@ -352,7 +352,7 @@ GEM simplecov_json_formatter (~> 0.1) simplecov-html (0.13.1) simplecov_json_formatter (0.1.4) - standard (1.41.0) + standard (1.41.1) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.0) rubocop (~> 1.66.0) @@ -383,12 +383,11 @@ GEM addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.8.2) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) yaml-lint (0.1.2) - zeitwerk (2.7.0) + zeitwerk (2.7.1) PLATFORMS aarch64-linux diff --git a/app/services/post_publisher.rb b/app/services/post_publisher.rb index acd6b361..fcb153b3 100644 --- a/app/services/post_publisher.rb +++ b/app/services/post_publisher.rb @@ -1,6 +1,4 @@ class PostPublisher - include Logging - attr_reader :post, :freefeed_client def initialize(post:, freefeed_client:) @@ -15,9 +13,9 @@ def publish post.update(freefeed_post_id: post_id) create_comments(post_id) post.success! - logger.info("---> new post URL: #{post.permalink}") rescue StandardError post.fail! + raise # TBD: Report error end @@ -52,8 +50,9 @@ def create_attachments post.attachments.map { |url| create_attachment(url) } end + # TBD: Move download process to the Freefeed client namespace def create_attachment(url) - Downloader.call(url) do |io, content_type| + Freefeed::Downloader.new(url: url).call do |io, content_type| response = freefeed_client.create_attachment(io, content_type: content_type) response.parse.fetch("attachments").fetch("id") end diff --git a/lib/freefeed/downloader.rb b/lib/freefeed/downloader.rb index 19fe53cb..1fd5d448 100644 --- a/lib/freefeed/downloader.rb +++ b/lib/freefeed/downloader.rb @@ -2,13 +2,12 @@ module Freefeed class Downloader attr_reader :url, :http_client - def initialize(url:, http_client:) + def initialize(url:, http_client: HTTP) @url = url @http_client = http_client end def call - # TBD: Honeybadger.context(downloader: {url: url}) response = fetch_url return unless response&.status&.success? yield build_io_from(response), response.content_type.mime_type @@ -26,9 +25,6 @@ def build_io_from(response) def fetch_url http_client.get(url) - rescue StandardError - # TBD: Report download error - nil end end end diff --git a/spec/lib/freefeed/authenticated_request_spec.rb b/spec/lib/freefeed/authenticated_request_spec.rb index a5109d35..c913606a 100644 --- a/spec/lib/freefeed/authenticated_request_spec.rb +++ b/spec/lib/freefeed/authenticated_request_spec.rb @@ -1,5 +1,3 @@ -require "rails_helper" - RSpec.describe Freefeed::AuthenticatedRequest do let(:client) do Freefeed::Client.new( diff --git a/spec/lib/freefeed/client_spec.rb b/spec/lib/freefeed/client_spec.rb index bb6f8a56..586e28a0 100644 --- a/spec/lib/freefeed/client_spec.rb +++ b/spec/lib/freefeed/client_spec.rb @@ -1,5 +1,3 @@ -require "rails_helper" - RSpec.describe Freefeed::Client do let(:token) { "TEST_TOKEN" } let(:base_url) { "https://example.com" } diff --git a/spec/lib/freefeed/downloader_spec.rb b/spec/lib/freefeed/downloader_spec.rb index 300a4a56..a8a60d4d 100644 --- a/spec/lib/freefeed/downloader_spec.rb +++ b/spec/lib/freefeed/downloader_spec.rb @@ -1,5 +1,3 @@ -require "rails_helper" - RSpec.describe Freefeed::Downloader do let(:url) { "https://example.com/image.jpg" } let(:http_client) { HTTP } @@ -60,18 +58,16 @@ end describe "network failures" do - it "returns nil on connection error" do + it "returns nil on connection error and not yield" do stub_request(:get, url).to_raise(HTTP::ConnectionError) - expect { |b| downloader.call(&b) }.not_to yield_control - expect(downloader.call {}).to be_nil + expect { downloader.call { raise } }.to raise_error(HTTP::ConnectionError) end - it "returns nil on timeout error" do + it "returns nil on timeout error and not yield" do stub_request(:get, url).to_raise(HTTP::TimeoutError) - expect { |b| downloader.call(&b) }.not_to yield_control - expect(downloader.call {}).to be_nil + expect { downloader.call { raise } }.to raise_error(HTTP::TimeoutError) end end diff --git a/spec/lib/freefeed/error_spec.rb b/spec/lib/freefeed/error_spec.rb index 09f94df1..4b2489a9 100644 --- a/spec/lib/freefeed/error_spec.rb +++ b/spec/lib/freefeed/error_spec.rb @@ -1,5 +1,3 @@ -require "rails_helper" - RSpec.describe Freefeed::Error do it "returns the correct error class for a response code" do response = HTTP::Response.new(status: 404, version: "1.1", body: "", request: nil) diff --git a/spec/lib/freefeed/request_spec.rb b/spec/lib/freefeed/request_spec.rb index bb86f0bb..8f06b05e 100644 --- a/spec/lib/freefeed/request_spec.rb +++ b/spec/lib/freefeed/request_spec.rb @@ -1,5 +1,3 @@ -require "rails_helper" - RSpec.describe Freefeed::Request do let(:client) do Freefeed::Client.new( diff --git a/spec/lib/freefeed/v1/attachments_spec.rb b/spec/lib/freefeed/v1/attachments_spec.rb index ef0b51cc..577d04d7 100644 --- a/spec/lib/freefeed/v1/attachments_spec.rb +++ b/spec/lib/freefeed/v1/attachments_spec.rb @@ -1,5 +1,3 @@ -require "rails_helper" - RSpec.describe Freefeed::V1::Attachments do let(:client) { Freefeed::Client.new(token: "token", base_url: "https://example.com") } diff --git a/spec/lib/freefeed/v1/comments_spec.rb b/spec/lib/freefeed/v1/comments_spec.rb index 4dbcbe24..1ee8661a 100644 --- a/spec/lib/freefeed/v1/comments_spec.rb +++ b/spec/lib/freefeed/v1/comments_spec.rb @@ -1,5 +1,3 @@ -require "rails_helper" - RSpec.describe Freefeed::V1::Comments do let(:client) { Freefeed::Client.new(token: "token", base_url: "https://example.com") } diff --git a/spec/lib/freefeed/v1/posts_spec.rb b/spec/lib/freefeed/v1/posts_spec.rb index 7fe32453..15d1653c 100644 --- a/spec/lib/freefeed/v1/posts_spec.rb +++ b/spec/lib/freefeed/v1/posts_spec.rb @@ -1,5 +1,3 @@ -require "rails_helper" - RSpec.describe Freefeed::V1::Posts do let(:client) { Freefeed::Client.new(token: "token", base_url: "https://example.com") } diff --git a/spec/lib/freefeed/v2/notifications_spec.rb b/spec/lib/freefeed/v2/notifications_spec.rb index 940982a6..d9c4ef7d 100644 --- a/spec/lib/freefeed/v2/notifications_spec.rb +++ b/spec/lib/freefeed/v2/notifications_spec.rb @@ -1,6 +1,4 @@ # spec/lib/freefeed/v2/notifications_spec.rb -require "rails_helper" - RSpec.describe Freefeed::V2::Notifications do let(:client) { Freefeed::Client.new(token: "token", base_url: "https://example.com") } diff --git a/spec/lib/freefeed/v2/posts_spec.rb b/spec/lib/freefeed/v2/posts_spec.rb index ff5ac51a..2f57e8f2 100644 --- a/spec/lib/freefeed/v2/posts_spec.rb +++ b/spec/lib/freefeed/v2/posts_spec.rb @@ -1,5 +1,3 @@ -require "rails_helper" - RSpec.describe Freefeed::V2::Posts do let(:client) { Freefeed::Client.new(token: "token", base_url: "https://example.com") } diff --git a/spec/lib/freefeed/v2/timelines_spec.rb b/spec/lib/freefeed/v2/timelines_spec.rb index 7d9d61b6..01d5d79f 100644 --- a/spec/lib/freefeed/v2/timelines_spec.rb +++ b/spec/lib/freefeed/v2/timelines_spec.rb @@ -1,5 +1,3 @@ -require "rails_helper" - RSpec.describe Freefeed::V2::Timelines do let(:client) { Freefeed::Client.new(token: "token", base_url: "https://example.com") } diff --git a/spec/lib/freefeed/v2/users_spec.rb b/spec/lib/freefeed/v2/users_spec.rb index eb8f2383..7799d697 100644 --- a/spec/lib/freefeed/v2/users_spec.rb +++ b/spec/lib/freefeed/v2/users_spec.rb @@ -1,5 +1,3 @@ -require "rails_helper" - RSpec.describe Freefeed::V2::Users do let(:client) { Freefeed::Client.new(token: "token", base_url: "https://example.com") } diff --git a/spec/models/feed_spec.rb b/spec/models/feed_spec.rb index 34586f4d..ad18f7a9 100644 --- a/spec/models/feed_spec.rb +++ b/spec/models/feed_spec.rb @@ -134,7 +134,6 @@ context "with missing timestamps" do it "returns true" do expect(build(:feed, updated_at: arbitrary_time, configured_at: nil)).to be_configurable - expect(build(:feed, updated_at: true, configured_at: arbitrary_time)).to be_configurable expect(build(:feed, updated_at: nil, configured_at: nil)).to be_configurable end end diff --git a/spec/services/post_publisher_spec.rb b/spec/services/post_publisher_spec.rb index 9b5a85b7..2c4d6aaa 100644 --- a/spec/services/post_publisher_spec.rb +++ b/spec/services/post_publisher_spec.rb @@ -1 +1,248 @@ -require "rails_helper" +RSpec.describe PostPublisher do + subject(:service) { described_class.new(post: post, freefeed_client: freefeed_client) } + + let(:freefeed_client) do + Freefeed::Client.new( + token: "TEST_TOKEN", + base_url: "https://freefeed.test" + ) + end + + let(:feed) { create(:feed, name: "sample_feed") } + + let(:json_headers_with_authentication_token) do + { + "Accept" => "*/*", + "Authorization" => "Bearer TEST_TOKEN", + "Connection" => "close", + "Content-Type" => "application/json; charset=utf-8", + "Host" => "freefeed.test", + "User-Agent" => "feeder" + } + end + + describe "#publish" do + it "publishes a post" do + post = create( + :post, + feed: feed, + state: "enqueued", + attachments: [], + comments: [] + ) + + # Create post + stub_request(:post, "https://freefeed.test/v1/posts") + .with( + body: { + "post" => { + "body" => "Sample post text", + "attachments" => [] + }, + "meta" => { + "feeds" => [feed.name] + } + }.to_json, + headers: json_headers_with_authentication_token + ) + .to_return( + status: 200, + body: { + "posts" => { + "id" => "TEST_POST_ID" + } + }.to_json, + headers: { + "Content-Type" => "application/json; charset=utf-8" + } + ) + + publisher = described_class.new(post: post, freefeed_client: freefeed_client) + + expect { publisher.publish }.to \ + change { post.reload.slice("state", "freefeed_post_id") } + .from("state" => "enqueued", "freefeed_post_id" => nil) + .to("state" => "published", "freefeed_post_id" => "TEST_POST_ID") + end + + it "publishes attachments" do + post = create( + :post, + feed: feed, + state: "enqueued", + attachments: ["https://example.com/image.jpg"], + comments: [] + ) + + # Download attached image + stub_request(:get, "https://example.com/image.jpg") + .to_return( + status: 200, + body: file_fixture("image_1x1.jpg").read, + headers: {"Content-Type" => "image/jpeg"} + ) + + # Create attachment + stub_request(:post, "https://freefeed.test/v1/attachments") + .to_return( + status: 200, + body: {"attachments" => {"id" => "TEST_ATTACHMENT_ID"}}.to_json, + headers: { + "Content-Type" => "application/json; charset=utf-8" + } + ) + + # Create post + stub_request(:post, "https://freefeed.test/v1/posts") + .with( + body: { + "post" => { + "body" => "Sample post text", + "attachments" => ["TEST_ATTACHMENT_ID"] + }, + "meta" => { + "feeds" => [feed.name] + } + }.to_json, + headers: json_headers_with_authentication_token + ) + .to_return( + status: 200, + body: { + "posts" => { + "id" => "TEST_POST_ID" + } + }.to_json, + headers: { + "Content-Type" => "application/json; charset=utf-8" + } + ) + + publisher = described_class.new(post: post, freefeed_client: freefeed_client) + + expect { publisher.publish }.to \ + change { post.reload.slice("state", "freefeed_post_id") } + .from("state" => "enqueued", "freefeed_post_id" => nil) + .to("state" => "published", "freefeed_post_id" => "TEST_POST_ID") + end + + it "publishes comments" do + post = create( + :post, + feed: feed, + state: "enqueued", + attachments: [], + comments: ["Sample comment"] + ) + + # Create post + stub_request(:post, "https://freefeed.test/v1/posts") + .with( + body: { + "post" => { + "body" => "Sample post text", + "attachments" => [] + }, + "meta" => { + "feeds" => [feed.name] + } + }.to_json, + headers: json_headers_with_authentication_token + ) + .to_return( + status: 200, + body: { + "posts" => { + "id" => "TEST_POST_ID" + } + }.to_json, + headers: { + "Content-Type" => "application/json; charset=utf-8" + } + ) + + # Create comment + stub_request(:post, "https://freefeed.test/v1/comments") + .with( + body: {comment: {body: "Sample comment", postId: "TEST_POST_ID"}}.to_json, + headers: json_headers_with_authentication_token + ) + .to_return(status: 200, body: "", headers: {}) + + publisher = described_class.new(post: post, freefeed_client: freefeed_client) + + expect { publisher.publish }.to \ + change { post.reload.slice("state", "freefeed_post_id") } + .from("state" => "enqueued", "freefeed_post_id" => nil) + .to("state" => "published", "freefeed_post_id" => "TEST_POST_ID") + end + + it "handles error response" do + post = create( + :post, + feed: feed, + state: "enqueued", + attachments: [], + comments: [] + ) + + # Create post + stub_request(:post, "https://freefeed.test/v1/posts") + .with( + body: { + "post" => { + "body" => "Sample post text", + "attachments" => [] + }, + "meta" => { + "feeds" => [feed.name] + } + }.to_json, + headers: json_headers_with_authentication_token + ) + .to_return(status: 500) + + publisher = described_class.new(post: post, freefeed_client: freefeed_client) + + expect { publisher.publish }.to \ + raise_error(Freefeed::Error::InternalServerError).and \ + change { post.reload.slice("state", "freefeed_post_id") } + .from("state" => "enqueued", "freefeed_post_id" => nil) + .to("state" => "failed", "freefeed_post_id" => nil) + end + + it "handles request timeout" do + post = create( + :post, + feed: feed, + state: "enqueued", + attachments: [], + comments: [] + ) + + # Create post + stub_request(:post, "https://freefeed.test/v1/posts") + .with( + body: { + "post" => { + "body" => "Sample post text", + "attachments" => [] + }, + "meta" => { + "feeds" => [feed.name] + } + }.to_json, + headers: json_headers_with_authentication_token + ) + .to_timeout + + publisher = described_class.new(post: post, freefeed_client: freefeed_client) + + expect { publisher.publish }.to \ + raise_error(HTTP::TimeoutError).and \ + change { post.reload.slice("state", "freefeed_post_id") } + .from("state" => "enqueued", "freefeed_post_id" => nil) + .to("state" => "failed", "freefeed_post_id" => nil) + end + end +end