From 2b9fe12079f77daffabf3841c8a2a6ba6000b043 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 14 May 2024 18:34:25 +0200 Subject: [PATCH] test(dolist): add some specs and remove unsupported email sent with attachments --- app/lib/dolist/api.rb | 72 ++++++++--------- spec/lib/dolist/api_spec.rb | 152 ++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+), 38 deletions(-) create mode 100644 spec/lib/dolist/api_spec.rb diff --git a/app/lib/dolist/api.rb b/app/lib/dolist/api.rb index c805e4848..8713e93bf 100644 --- a/app/lib/dolist/api.rb +++ b/app/lib/dolist/api.rb @@ -61,61 +61,57 @@ module Dolist end def send_email(mail) - if mail.attachments.any? { !_1.inline? } - return send_email_with_attachment(mail) - end - body = { "TransactionalSending": prepare_mail_body(mail) } url = format_url(EMAIL_SENDING_TRANSACTIONAL) post(url, body.to_json) end - def send_email_with_attachment(mail) - uri = URI(format_url(EMAIL_SENDING_TRANSACTIONAL_ATTACHMENT)) + # def send_email_with_attachment(mail) + # uri = URI(format_url(EMAIL_SENDING_TRANSACTIONAL_ATTACHMENT)) - request = Net::HTTP::Post.new(uri) + # request = Net::HTTP::Post.new(uri) - default_headers.each do |key, value| - next if key.to_s == "Content-Type" - request[key] = value - end + # default_headers.each do |key, value| + # next if key.to_s == "Content-Type" + # request[key] = value + # end - boundary = "---011000010111000001101001" # any random string not present in the body - request.content_type = "multipart/form-data; boundary=#{boundary}" + # boundary = "---011000010111000001101001" # any random string not present in the body + # request.content_type = "multipart/form-data; boundary=#{boundary}" - body = "--#{boundary}\r\n" + # body = "--#{boundary}\r\n" - base64_files(mail.attachments).each do |file| - body << "Content-Disposition: form-data; name=\"#{file.field_name}\"; filename=\"#{file.filename}\"\r\n" - body << "Content-Type: #{file.mime_type}\r\n" - body << "\r\n" - body << file.content - body << "\r\n" - end + # base64_files(mail.attachments).each do |file| + # body << "Content-Disposition: form-data; name=\"#{file.field_name}\"; filename=\"#{file.filename}\"\r\n" + # body << "Content-Type: #{file.mime_type}\r\n" + # body << "\r\n" + # body << file.content + # body << "\r\n" + # end - body << "\r\n--#{boundary}\r\n" - body << "Content-Disposition: form-data; name=\"TransactionalSending\"\r\n" - body << "Content-Type: text/plain; charset=utf-8\r\n" - body << "\r\n" - body << prepare_mail_body(mail).to_jsv + # body << "\r\n--#{boundary}\r\n" + # body << "Content-Disposition: form-data; name=\"TransactionalSending\"\r\n" + # body << "Content-Type: text/plain; charset=utf-8\r\n" + # body << "\r\n" + # body << prepare_mail_body(mail).to_jsv - body << "\r\n--#{boundary}--\r\n" - body << "\r\n" + # body << "\r\n--#{boundary}--\r\n" + # body << "\r\n" - request.body = body + # request.body = body - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true + # http = Net::HTTP.new(uri.host, uri.port) + # http.use_ssl = true - response = http.request(request) + # response = http.request(request) - if response.body.empty? - fail "Dolist API returned an empty response" - else - JSON.parse(response.body) - end - end + # if response.body.empty? + # fail "Dolist API returned an empty response" + # else + # JSON.parse(response.body) + # end + # end def sent_mails(email_address) contact_id = fetch_contact_id(email_address) diff --git a/spec/lib/dolist/api_spec.rb b/spec/lib/dolist/api_spec.rb new file mode 100644 index 000000000..d01b0232a --- /dev/null +++ b/spec/lib/dolist/api_spec.rb @@ -0,0 +1,152 @@ +# frozen_string_literal: true + +describe Dolist::API do + let(:headers) { { "X-Rate-Limit-Remaining" => "15", "X-Rate-Limit-Reset" => (Time.current + 3600).to_i.to_s } } + + let(:mail) do + Mail.new do + to 'test@example.com' + from 'sender@example.com' + subject 'Test' + body 'Test body' + header['X-Dolist-Message-Name'] = 'Test Message' + end + end + + describe ".save_rate_limit_headers" do + it "saves the rate limit headers" do + Dolist::API.save_rate_limit_headers(headers) + expect(Dolist::API.limit_remaining).to eq(15) + expect(Dolist::API.limit_reset_at).to be_within(1.second).of(Time.zone.at(headers["X-Rate-Limit-Reset"].to_i / 1_000)) + end + end + + describe ".near_rate_limit?" do + context "when limit_remaining is nil" do + it "returns nil" do + Dolist::API.limit_remaining = nil + expect(Dolist::API.near_rate_limit?).to be_nil + end + end + + context "when limit_remaining is less than 20" do + it "returns true" do + Dolist::API.limit_remaining = 15 + expect(Dolist::API.near_rate_limit?).to be true + end + end + + context "when limit_remaining is 20 or more" do + it "returns false" do + Dolist::API.limit_remaining = 25 + expect(Dolist::API.near_rate_limit?).to be false + end + end + end + + describe ".sendable?" do + context "when mail has no recipient" do + it "returns false" do + allow(mail).to receive(:to).and_return([]) + expect(Dolist::API.sendable?(mail)).to be false + end + end + + context "when mail has bcc" do + it "returns false" do + allow(mail).to receive(:bcc).and_return(["bcc@example.com"]) + expect(Dolist::API.sendable?(mail)).to be false + end + end + + context "when mail has attachments that are not inline" do + it "returns false" do + attachment = double("Attachment", inline?: false) + allow(mail).to receive(:attachments).and_return([attachment]) + expect(Dolist::API.sendable?(mail)).to be false + end + end + + context "when mail is valid" do + it "returns true" do + expect(Dolist::API.sendable?(mail)).to be true + end + end + end + + describe "#send_email" do + let(:api) { Dolist::API.new } + let(:url) { "https://api.dolist.com/email/send" } + let(:mail_body) do + { + "Type": "TransactionalService", + "Contact": { + "FieldList": [ + { + "ID" => Dolist::API::EMAIL_KEY, + "Value" => mail.to.first + } + ] + }, + "Message": { + "Name": mail['X-Dolist-Message-Name'].value, + "Subject": mail.subject, + "SenderID": api.send(:sender_id, mail.from_address.domain), + "ForceHttp": false, + "Format": "html", + "DisableOpenTracking": true, + "IsTrackingValidated": true + }, + "MessageContent": { + "SourceCode": mail.decoded, + "EncodingType": "UTF8", + "EnableTrackingDetection": false + } + } + end + + let(:expected_body) { { "TransactionalSending": mail_body }.to_json } + + it "sends an email using the API" do + stub_request(:post, "https://apiv9.dolist.net/v1/email/sendings/transactional?AccountID=#{ENV["DOLIST_ACCOUNT_ID"]}") + .with(body: expected_body) + .to_return(body: { "Result" => "success" }.to_json, headers: { "X-Rate-Limit-Remaining" => "15", "X-Rate-Limit-Reset" => "1234" }) + + result = api.send_email(mail) + expect(result).to eq({ "Result" => "success" }) + end + end + + describe "#sent_mails" do + let(:api) { Dolist::API.new } + let(:email_address) { "test@example.com" } + let(:contact_id) { "12345" } + let(:dolist_messages) { [{ "SendingName" => "Test Message", "SendDate" => Time.zone.now.to_s, "Status" => "Sent", "IsDelivered" => true }] } + + before do + allow(api).to receive(:fetch_contact_id).with(email_address).and_return(contact_id) + allow(api).to receive(:fetch_dolist_messages).with(contact_id).and_return(dolist_messages) + end + + it "returns a list of sent mails" do + sent_mails = api.sent_mails(email_address) + expect(sent_mails).not_to be_empty + expect(sent_mails.first.subject).to eq("Test Message") + end + + context "when contact_id is nil" do + it "returns an empty list" do + allow(api).to receive(:fetch_contact_id).with(email_address).and_return(nil) + expect(api.sent_mails(email_address)).to be_empty + end + end + + context "when an error occurs" do + it "returns an empty list and logs the error" do + allow(api).to receive(:fetch_contact_id).and_raise(StandardError.new("Test Error")) + expect(Rails.logger).to receive(:error).with("Test Error") + expect(api.sent_mails(email_address)).to be_empty + end + end + end +end