Skip to content

Commit e784045

Browse files
authored
F2: Test Freefeed API wrapper (#595)
* Test coverage * Test coverage * More test coverage * Add expectations * More tests * Rubocop * Rubocop
1 parent 5f811cc commit e784045

File tree

11 files changed

+764
-10
lines changed

11 files changed

+764
-10
lines changed

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ gem "honeybadger", "~> 5.15", ">= 5.15.6"
1010
gem "http", "~> 5.2"
1111
gem "jsbundling-rails"
1212
gem "memo_wise", "~> 1.9"
13+
gem "mimemagic", "~> 0.4.3"
1314
gem "ostruct", "~> 0.6.0"
1415
gem "pg", "~> 1.5"
1516
gem "propshaft"

Gemfile.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ GEM
186186
marcel (1.0.4)
187187
memo_wise (1.10.0)
188188
method_source (1.1.0)
189+
mimemagic (0.4.3)
190+
nokogiri (~> 1)
191+
rake
189192
mini_mime (1.1.5)
190193
minitest (5.25.1)
191194
msgpack (1.7.3)
@@ -407,6 +410,7 @@ DEPENDENCIES
407410
http (~> 5.2)
408411
jsbundling-rails
409412
memo_wise (~> 1.9)
413+
mimemagic (~> 0.4.3)
410414
ostruct (~> 0.6.0)
411415
pg (~> 1.5)
412416
propshaft

spec/fixtures/files/image_1x1.jpg

286 Bytes
Loading

spec/lib/freefeed/downloader_spec.rb

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,134 @@
11
require "rails_helper"
2+
3+
RSpec.describe Freefeed::Downloader do
4+
let(:url) { "https://example.com/image.jpg" }
5+
let(:http_client) { HTTP }
6+
let(:downloader) { described_class.new(url: url, http_client: http_client) }
7+
let(:binary_content) { File.read("spec/fixtures/files/image_1x1.jpg", mode: "rb") }
8+
9+
describe "successful download" do
10+
before do
11+
stub_request(:get, url)
12+
.to_return(
13+
status: 200,
14+
body: binary_content,
15+
headers: {"Content-Type" => "image/jpeg"}
16+
)
17+
end
18+
19+
it "yields IO object and content type" do
20+
expect { |b| downloader.call(&b) }
21+
.to yield_with_args(instance_of(StringIO), "image/jpeg")
22+
end
23+
24+
it "downloads file with correct content" do
25+
downloader.call do |io, mime_type|
26+
expect(mime_type).to eq("image/jpeg")
27+
expect(io.read).to eq(binary_content)
28+
end
29+
end
30+
31+
it "rewinds IO object before yielding" do
32+
downloader.call do |io, _mime_type|
33+
expect(io.pos).to eq(0)
34+
end
35+
end
36+
37+
it "sets binary encoding for IO object" do
38+
downloader.call do |io, _mime_type|
39+
expect(io.external_encoding).to eq(Encoding::BINARY)
40+
end
41+
end
42+
end
43+
44+
describe "missing content type" do
45+
before do
46+
stub_request(:get, url)
47+
.to_return(
48+
status: 200,
49+
body: binary_content,
50+
headers: {}
51+
)
52+
end
53+
54+
it "yields IO object and nil content type" do
55+
downloader.call do |io, mime_type|
56+
expect(mime_type).to be_nil
57+
expect(io.read).to eq(binary_content)
58+
end
59+
end
60+
end
61+
62+
describe "network failures" do
63+
it "returns nil on connection error" do
64+
stub_request(:get, url).to_raise(HTTP::ConnectionError)
65+
66+
expect { |b| downloader.call(&b) }.not_to yield_control
67+
expect(downloader.call {}).to be_nil
68+
end
69+
70+
it "returns nil on timeout error" do
71+
stub_request(:get, url).to_raise(HTTP::TimeoutError)
72+
73+
expect { |b| downloader.call(&b) }.not_to yield_control
74+
expect(downloader.call {}).to be_nil
75+
end
76+
end
77+
78+
describe "error status codes" do
79+
[403, 404, 500].each do |status|
80+
it "returns nil for status #{status}" do
81+
stub_request(:get, url).to_return(status: status)
82+
83+
expect { |b| downloader.call(&b) }.not_to yield_control
84+
expect(downloader.call {}).to be_nil
85+
end
86+
end
87+
end
88+
89+
describe "content types" do
90+
{
91+
"image/png" => "image.png",
92+
"application/pdf" => "document.pdf",
93+
"text/plain" => "text.txt",
94+
"application/octet-stream" => "binary.dat"
95+
}.each do |mime_type, filename|
96+
it "handles #{mime_type} content type" do
97+
stub_request(:get, "https://example.com/#{filename}")
98+
.to_return(
99+
status: 200,
100+
body: binary_content,
101+
headers: {"Content-Type" => mime_type}
102+
)
103+
104+
downloader = described_class.new(
105+
url: "https://example.com/#{filename}",
106+
http_client: http_client
107+
)
108+
109+
downloader.call do |io, content_type|
110+
expect(content_type).to eq(mime_type)
111+
expect(io.read).to eq(binary_content)
112+
end
113+
end
114+
end
115+
end
116+
117+
describe "empty response" do
118+
before do
119+
stub_request(:get, url)
120+
.to_return(
121+
status: 200,
122+
body: "",
123+
headers: {"Content-Type" => "text/plain"}
124+
)
125+
end
126+
127+
it "handles empty response correctly" do
128+
downloader.call do |io, mime_type|
129+
expect(mime_type).to eq("text/plain")
130+
expect(io.read).to eq("")
131+
end
132+
end
133+
end
134+
end
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
require "rails_helper"
2+
3+
RSpec.describe Freefeed::V1::Attachments do
4+
let(:client) { Freefeed::Client.new(token: "token", base_url: "https://example.com") }
5+
6+
describe "#create_attachment" do
7+
let(:file_path) { file_fixture("image_1x1.jpg") }
8+
9+
it "uploads a file from path" do
10+
stub_request(:post, "https://example.com/v1/attachments")
11+
.with(
12+
headers: {
13+
"Authorization" => "Bearer token",
14+
"User-Agent" => "feeder"
15+
}
16+
)
17+
.to_return(status: 200, body: "{}")
18+
19+
response = client.create_attachment(file_path)
20+
21+
expect(response.status.code).to eq(200)
22+
end
23+
24+
it "uploads a file with explicit content type" do
25+
stub_request(:post, "https://example.com/v1/attachments")
26+
.with(
27+
headers: {
28+
"Authorization" => "Bearer token",
29+
"User-Agent" => "feeder"
30+
}
31+
)
32+
.to_return(status: 200, body: "{}")
33+
34+
response = client.create_attachment(file_path, content_type: "image/png")
35+
36+
expect(response.status.code).to eq(200)
37+
end
38+
39+
it "uploads from IO object" do
40+
stub_request(:post, "https://example.com/v1/attachments")
41+
.with(
42+
headers: {
43+
"Authorization" => "Bearer token",
44+
"User-Agent" => "feeder"
45+
}
46+
)
47+
.to_return(status: 200, body: "{}")
48+
49+
io = StringIO.new("fake image content")
50+
response = client.create_attachment(io)
51+
52+
expect(response.status.code).to eq(200)
53+
end
54+
end
55+
56+
describe "#create_attachment_from" do
57+
let(:remote_url) { "https://example.com/image.jpg" }
58+
let(:attachment_response) do
59+
{
60+
"attachments" => {
61+
"id" => "attachment-123"
62+
}
63+
}.to_json
64+
end
65+
66+
it "creates attachment from remote URL" do
67+
# Stub the remote image download
68+
stub_request(:get, remote_url)
69+
.to_return(
70+
status: 200,
71+
body: "fake image content",
72+
headers: {"Content-Type" => "image/jpeg"}
73+
)
74+
75+
# Stub the attachment creation
76+
stub_request(:post, "https://example.com/v1/attachments")
77+
.with(
78+
headers: {
79+
"Authorization" => "Bearer token",
80+
"User-Agent" => "feeder"
81+
}
82+
)
83+
.to_return(
84+
status: 200,
85+
body: attachment_response,
86+
headers: {"Content-Type" => "application/json"}
87+
)
88+
89+
result = client.create_attachment_from(url: remote_url)
90+
91+
expect(result).to eq("attachment-123")
92+
end
93+
end
94+
end

spec/lib/freefeed/v1/comments_spec.rb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
require "rails_helper"
2+
3+
RSpec.describe Freefeed::V1::Comments do
4+
let(:client) { Freefeed::Client.new(token: "token", base_url: "https://example.com") }
5+
6+
describe "#create_comment" do
7+
it "creates a comment" do
8+
stub_request(:post, "https://example.com/v1/comments")
9+
.with(
10+
body: {
11+
body: "Comment text",
12+
postId: "post-123"
13+
}.to_json,
14+
headers: {
15+
"Authorization" => "Bearer token",
16+
"Content-Type" => "application/json; charset=utf-8",
17+
"User-Agent" => "feeder"
18+
}
19+
)
20+
.to_return(status: 200, body: "{}")
21+
22+
response = client.create_comment(
23+
body: "Comment text",
24+
postId: "post-123"
25+
)
26+
expect(response.status.code).to eq(200)
27+
end
28+
end
29+
30+
describe "#update_comment" do
31+
it "updates a comment" do
32+
stub_request(:put, "https://example.com/v1/comments/comment-123")
33+
.with(
34+
body: {
35+
body: "Updated comment text"
36+
}.to_json,
37+
headers: {
38+
"Authorization" => "Bearer token",
39+
"Content-Type" => "application/json; charset=utf-8",
40+
"User-Agent" => "feeder"
41+
}
42+
)
43+
.to_return(status: 200, body: "{}")
44+
45+
response = client.update_comment(
46+
"comment-123",
47+
body: "Updated comment text"
48+
)
49+
expect(response.status.code).to eq(200)
50+
end
51+
end
52+
53+
describe "#delete_comment" do
54+
it "deletes a comment" do
55+
stub_request(:delete, "https://example.com/v1/comments/comment-123")
56+
.with(
57+
headers: {
58+
"Authorization" => "Bearer token",
59+
"User-Agent" => "feeder"
60+
}
61+
)
62+
.to_return(status: 200, body: "{}")
63+
64+
response = client.delete_comment("comment-123")
65+
expect(response.status.code).to eq(200)
66+
end
67+
end
68+
end

0 commit comments

Comments
 (0)