مراية لـ
https://github.com/postalserver/postal.git
تم المزامنة 2025-12-01 05:43:04 +00:00
test: add tests for the legacy API
هذا الالتزام موجود في:
@@ -99,7 +99,7 @@ class OutgoingMessagePrototype
|
||||
{
|
||||
name: attachment[:name],
|
||||
content_type: attachment[:content_type] || "application/octet-stream",
|
||||
data: attachment[:base64] ? Base64.decode64(attachment[:data]) : attachment[:data]
|
||||
data: attachment[:base64] && attachment[:data] ? Base64.decode64(attachment[:data]) : attachment[:data]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
118
spec/apis/legacy_api/messages/deliveries_spec.rb
Normal file
118
spec/apis/legacy_api/messages/deliveries_spec.rb
Normal file
@@ -0,0 +1,118 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe "Legacy Messages API", type: :request do
|
||||
describe "/api/v1/messages/deliveries" do
|
||||
context "when no authentication is provided" do
|
||||
it "returns an error" do
|
||||
post "/api/v1/messages/deliveries"
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "AccessDenied"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the credential does not match anything" do
|
||||
it "returns an error" do
|
||||
post "/api/v1/messages/deliveries", headers: { "x-server-api-key" => "invalid" }
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "InvalidServerAPIKey"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the credential belongs to a suspended server" do
|
||||
it "returns an error" do
|
||||
server = create(:server, :suspended)
|
||||
credential = create(:credential, server: server)
|
||||
post "/api/v1/messages/deliveries", headers: { "x-server-api-key" => credential.key }
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "ServerSuspended"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the credential is valid" do
|
||||
let(:server) { create(:server) }
|
||||
let(:credential) { create(:credential, server: server) }
|
||||
|
||||
context "when no message ID is provided" do
|
||||
it "returns an error" do
|
||||
post "/api/v1/messages/deliveries", headers: { "x-server-api-key" => credential.key }
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "parameter-error"
|
||||
expect(parsed_body["data"]["message"]).to match(/`id` parameter is required but is missing/)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the message ID does not exist" do
|
||||
it "returns an error" do
|
||||
post "/api/v1/messages/deliveries",
|
||||
headers: { "x-server-api-key" => credential.key,
|
||||
"content-type" => "application/json" },
|
||||
params: { id: 123 }.to_json
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "MessageNotFound"
|
||||
expect(parsed_body["data"]["id"]).to eq 123
|
||||
end
|
||||
end
|
||||
|
||||
context "when the message ID exists" do
|
||||
let(:server) { create(:server) }
|
||||
let(:credential) { create(:credential, server: server) }
|
||||
let(:message) { MessageFactory.outgoing(server) }
|
||||
|
||||
before do
|
||||
message.create_delivery("SoftFail", details: "no server found",
|
||||
output: "404",
|
||||
sent_with_ssl: true,
|
||||
log_id: "1234",
|
||||
time: 1.2)
|
||||
message.create_delivery("Sent", details: "sent successfully",
|
||||
output: "200",
|
||||
sent_with_ssl: false,
|
||||
log_id: "5678",
|
||||
time: 2.2)
|
||||
end
|
||||
|
||||
before do
|
||||
post "/api/v1/messages/deliveries",
|
||||
headers: { "x-server-api-key" => credential.key,
|
||||
"content-type" => "application/json" },
|
||||
params: { id: message.id }.to_json
|
||||
end
|
||||
|
||||
it "returns an array of deliveries" do
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "success"
|
||||
expect(parsed_body["data"]).to match([
|
||||
{ "id" => kind_of(Integer),
|
||||
"status" => "SoftFail",
|
||||
"details" => "no server found",
|
||||
"output" => "404",
|
||||
"sent_with_ssl" => true,
|
||||
"log_id" => "1234",
|
||||
"time" => 1.2,
|
||||
"timestamp" => kind_of(Float) },
|
||||
{ "id" => kind_of(Integer),
|
||||
"status" => "Sent",
|
||||
"details" => "sent successfully",
|
||||
"output" => "200",
|
||||
"sent_with_ssl" => false,
|
||||
"log_id" => "5678",
|
||||
"time" => 2.2,
|
||||
"timestamp" => kind_of(Float) },
|
||||
])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
270
spec/apis/legacy_api/messages/message_spec.rb
Normal file
270
spec/apis/legacy_api/messages/message_spec.rb
Normal file
@@ -0,0 +1,270 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe "Legacy Messages API", type: :request do
|
||||
describe "/api/v1/messages/message" do
|
||||
context "when no authentication is provided" do
|
||||
it "returns an error" do
|
||||
post "/api/v1/messages/message"
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "AccessDenied"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the credential does not match anything" do
|
||||
it "returns an error" do
|
||||
post "/api/v1/messages/message", headers: { "x-server-api-key" => "invalid" }
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "InvalidServerAPIKey"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the credential belongs to a suspended server" do
|
||||
it "returns an error" do
|
||||
server = create(:server, :suspended)
|
||||
credential = create(:credential, server: server)
|
||||
post "/api/v1/messages/message", headers: { "x-server-api-key" => credential.key }
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "ServerSuspended"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the credential is valid" do
|
||||
let(:server) { create(:server) }
|
||||
let(:credential) { create(:credential, server: server) }
|
||||
|
||||
context "when no message ID is provided" do
|
||||
it "returns an error" do
|
||||
post "/api/v1/messages/message", headers: { "x-server-api-key" => credential.key }
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "parameter-error"
|
||||
expect(parsed_body["data"]["message"]).to match(/`id` parameter is required but is missing/)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the message ID does not exist" do
|
||||
it "returns an error" do
|
||||
post "/api/v1/messages/message",
|
||||
headers: { "x-server-api-key" => credential.key,
|
||||
"content-type" => "application/json" },
|
||||
params: { id: 123 }.to_json
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "MessageNotFound"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the message ID exists" do
|
||||
let(:server) { create(:server) }
|
||||
let(:credential) { create(:credential, server: server) }
|
||||
let(:message) { MessageFactory.outgoing(server) }
|
||||
let(:expansions) { [] }
|
||||
|
||||
before do
|
||||
post "/api/v1/messages/message",
|
||||
headers: { "x-server-api-key" => credential.key,
|
||||
"content-type" => "application/json" },
|
||||
params: { id: message.id, _expansions: expansions }.to_json
|
||||
end
|
||||
|
||||
context "when no expansions are requested" do
|
||||
it "returns details about the message" do
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "success"
|
||||
expect(parsed_body["data"]).to match({
|
||||
"id" => message.id,
|
||||
"token" => message.token
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
context "when the status expansion is requested" do
|
||||
let(:expansions) { ["status"] }
|
||||
|
||||
it "returns details about the message" do
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "success"
|
||||
expect(parsed_body["data"]).to match({
|
||||
"id" => message.id,
|
||||
"token" => message.token,
|
||||
"status" => { "held" => false,
|
||||
"hold_expiry" => nil,
|
||||
"last_delivery_attempt" => nil,
|
||||
"status" => "Pending" }
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
context "when the details expansion is requested" do
|
||||
let(:expansions) { ["details"] }
|
||||
|
||||
it "returns details about the message" do
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "success"
|
||||
expect(parsed_body["data"]).to match({
|
||||
"id" => message.id,
|
||||
"token" => message.token,
|
||||
"details" => { "bounce" => false,
|
||||
"bounce_for_id" => 0,
|
||||
"direction" => "outgoing",
|
||||
"mail_from" => "test@example.com",
|
||||
"message_id" => message.message_id,
|
||||
"rcpt_to" => "john@example.com",
|
||||
"received_with_ssl" => nil,
|
||||
"size" => kind_of(String),
|
||||
"subject" => "An example message",
|
||||
"tag" => nil,
|
||||
"timestamp" => kind_of(Float) }
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
context "when the details expansion is requested" do
|
||||
let(:expansions) { ["inspection"] }
|
||||
|
||||
it "returns details about the message" do
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "success"
|
||||
expect(parsed_body["data"]).to match({
|
||||
"id" => message.id,
|
||||
"token" => message.token,
|
||||
"inspection" => { "inspected" => false,
|
||||
"spam" => false,
|
||||
"spam_score" => 0.0,
|
||||
"threat" => false,
|
||||
"threat_details" => nil }
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
context "when the body expansions are requested" do
|
||||
let(:expansions) { %w[plain_body html_body] }
|
||||
|
||||
it "returns details about the message" do
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "success"
|
||||
expect(parsed_body["data"]).to match({
|
||||
"id" => message.id,
|
||||
"token" => message.token,
|
||||
"plain_body" => message.plain_body,
|
||||
"html_body" => message.html_body
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
context "when the attachments expansions is requested" do
|
||||
let(:message) do
|
||||
MessageFactory.outgoing(server) do |_, mail|
|
||||
mail.attachments["example.txt"] = "hello world!"
|
||||
end
|
||||
end
|
||||
let(:expansions) { ["attachments"] }
|
||||
|
||||
it "returns details about the message" do
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "success"
|
||||
expect(parsed_body["data"]).to match({
|
||||
"id" => message.id,
|
||||
"token" => message.token,
|
||||
"attachments" => [
|
||||
{
|
||||
"content_type" => "text/plain",
|
||||
"data" => Base64.encode64("hello world!"),
|
||||
"filename" => "example.txt",
|
||||
"hash" => Digest::SHA1.hexdigest("hello world!"),
|
||||
"size" => 12
|
||||
},
|
||||
]
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
context "when the headers expansions is requested" do
|
||||
let(:expansions) { ["headers"] }
|
||||
|
||||
it "returns details about the message" do
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "success"
|
||||
expect(parsed_body["data"]).to match({
|
||||
"id" => message.id,
|
||||
"token" => message.token,
|
||||
"headers" => message.headers
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
context "when the raw_message expansions is requested" do
|
||||
let(:expansions) { ["raw_message"] }
|
||||
|
||||
it "returns details about the message" do
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "success"
|
||||
expect(parsed_body["data"]).to match({
|
||||
"id" => message.id,
|
||||
"token" => message.token,
|
||||
"raw_message" => Base64.encode64(message.raw_message)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
context "when the activity_entries expansions is requested" do
|
||||
let(:message) do
|
||||
MessageFactory.outgoing(server) do |msg|
|
||||
msg.create_load(double("request", ip: "1.2.3.4", user_agent: "user agent"))
|
||||
link = msg.create_link("https://example.com")
|
||||
link_id = msg.database.select(:links, where: { token: link }).first["id"]
|
||||
msg.database.insert(:clicks, {
|
||||
message_id: msg.id,
|
||||
link_id: link_id,
|
||||
ip_address: "1.2.3.4",
|
||||
user_agent: "user agent",
|
||||
timestamp: Time.now.to_f
|
||||
})
|
||||
end
|
||||
end
|
||||
let(:expansions) { ["activity_entries"] }
|
||||
|
||||
it "returns details about the message" do
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "success"
|
||||
expect(parsed_body["data"]).to match({
|
||||
"id" => message.id,
|
||||
"token" => message.token,
|
||||
"activity_entries" => {
|
||||
"loads" => [{
|
||||
"ip_address" => "1.2.3.4",
|
||||
"user_agent" => "user agent",
|
||||
"timestamp" => match(/\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z\z/)
|
||||
}],
|
||||
"clicks" => [{
|
||||
"url" => "https://example.com",
|
||||
"ip_address" => "1.2.3.4",
|
||||
"user_agent" => "user agent",
|
||||
"timestamp" => match(/\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z\z/)
|
||||
}]
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
234
spec/apis/legacy_api/send/message_spec.rb
Normal file
234
spec/apis/legacy_api/send/message_spec.rb
Normal file
@@ -0,0 +1,234 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe "Legacy Send API", type: :request do
|
||||
describe "/api/v1/send/message" do
|
||||
context "when no authentication is provided" do
|
||||
it "returns an error" do
|
||||
post "/api/v1/send/message"
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "AccessDenied"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the credential does not match anything" do
|
||||
it "returns an error" do
|
||||
post "/api/v1/send/message", headers: { "x-server-api-key" => "invalid" }
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "InvalidServerAPIKey"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the credential belongs to a suspended server" do
|
||||
it "returns an error" do
|
||||
server = create(:server, :suspended)
|
||||
credential = create(:credential, server: server)
|
||||
post "/api/v1/send/message", headers: { "x-server-api-key" => credential.key }
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "ServerSuspended"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the credential is valid" do
|
||||
let(:server) { create(:server) }
|
||||
let(:credential) { create(:credential, server: server) }
|
||||
let(:domain) { create(:domain, owner: server) }
|
||||
let(:default_params) do
|
||||
{
|
||||
to: ["test@example.com"],
|
||||
cc: ["cc@example.com"],
|
||||
bcc: ["bcc@example.com"],
|
||||
from: "test@#{domain.name}",
|
||||
sender: "sender@#{domain.name}",
|
||||
tag: "test-tag",
|
||||
reply_to: "reply@example.com",
|
||||
plain_body: "plain text",
|
||||
html_body: "<p>html</p>",
|
||||
attachments: [{ name: "test1.txt", content_type: "text/plain", data: Base64.encode64("hello world 1") },
|
||||
{ name: "test2.txt", content_type: "text/plain", data: Base64.encode64("hello world 2") },],
|
||||
headers: { "x-test-header-1" => "111", "x-test-header-2" => "222" },
|
||||
bounce: false,
|
||||
subject: "Test"
|
||||
}
|
||||
end
|
||||
let(:params) { default_params }
|
||||
|
||||
before do
|
||||
post "/api/v1/send/message",
|
||||
headers: { "x-server-api-key" => credential.key,
|
||||
"content-type" => "application/json" },
|
||||
params: params.to_json
|
||||
end
|
||||
|
||||
context "when no recipients are provided" do
|
||||
let(:params) { default_params.merge(to: [], cc: [], bcc: []) }
|
||||
|
||||
it "returns an error" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "NoRecipients"
|
||||
expect(parsed_body["data"]["message"]).to match(/there are no recipients defined to receive this message/i)
|
||||
end
|
||||
end
|
||||
|
||||
context "when no content is provided" do
|
||||
let(:params) { default_params.merge(html_body: nil, plain_body: nil) }
|
||||
|
||||
it "returns an error" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "NoContent"
|
||||
expect(parsed_body["data"]["message"]).to match(/there is no content defined for this e-mail/i)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the number of 'To' recipients exceeds the maximum" do
|
||||
let(:params) { default_params.merge(to: ["a@a.com"] * 51) }
|
||||
|
||||
it "returns an error" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "TooManyToAddresses"
|
||||
expect(parsed_body["data"]["message"]).to match(/the maximum number of To addresses has been reached/i)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the number of 'CC' recipients exceeds the maximum" do
|
||||
let(:params) { default_params.merge(cc: ["a@a.com"] * 51) }
|
||||
|
||||
it "returns an error" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "TooManyCCAddresses"
|
||||
expect(parsed_body["data"]["message"]).to match(/the maximum number of CC addresses has been reached/i)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the number of 'BCC' recipients exceeds the maximum" do
|
||||
let(:params) { default_params.merge(bcc: ["a@a.com"] * 51) }
|
||||
|
||||
it "returns an error" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "TooManyBCCAddresses"
|
||||
expect(parsed_body["data"]["message"]).to match(/the maximum number of BCC addresses has been reached/i)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the 'From' address is missing" do
|
||||
let(:params) { default_params.merge(from: nil) }
|
||||
|
||||
it "returns an error" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "FromAddressMissing"
|
||||
expect(parsed_body["data"]["message"]).to match(/the from address is missing and is required/i)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the 'From' address is not authorised" do
|
||||
let(:params) { default_params.merge(from: "test@another.com") }
|
||||
|
||||
it "returns an error" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "UnauthenticatedFromAddress"
|
||||
expect(parsed_body["data"]["message"]).to match(/the from address is not authorised to send mail from this server/i)
|
||||
end
|
||||
end
|
||||
|
||||
context "when an attachment is missing a name" do
|
||||
let(:params) { default_params.merge(attachments: [{ name: nil, content_type: "text/plain", data: Base64.encode64("hello world 1") }]) }
|
||||
|
||||
it "returns an error" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "AttachmentMissingName"
|
||||
expect(parsed_body["data"]["message"]).to match(/an attachment is missing a name/i)
|
||||
end
|
||||
end
|
||||
|
||||
context "when an attachment is missing data" do
|
||||
let(:params) { default_params.merge(attachments: [{ name: "test1.txt", content_type: "text/plain", data: nil }]) }
|
||||
|
||||
it "returns an error" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "AttachmentMissingData"
|
||||
expect(parsed_body["data"]["message"]).to match(/an attachment is missing data/i)
|
||||
end
|
||||
end
|
||||
|
||||
context "when an attachment entry is not a hash" do
|
||||
let(:params) { default_params.merge(attachments: [123, "string"]) }
|
||||
|
||||
it "continues as if it wasn't there" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
["test@example.com", "cc@example.com", "bcc@example.com"].each do |rcpt_to|
|
||||
message_id = parsed_body["data"]["messages"][rcpt_to]["id"]
|
||||
message = server.message(message_id)
|
||||
expect(message.attachments).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when given a complete email to send" do
|
||||
it "returns details of the messages created" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "success"
|
||||
expect(parsed_body["data"]["messages"]).to match({
|
||||
"test@example.com" => { "id" => kind_of(Integer), "token" => /\A[a-zA-Z0-9]{16}\z/ },
|
||||
"cc@example.com" => { "id" => kind_of(Integer), "token" => /\A[a-zA-Z0-9]{16}\z/ },
|
||||
"bcc@example.com" => { "id" => kind_of(Integer), "token" => /\A[a-zA-Z0-9]{16}\z/ }
|
||||
})
|
||||
end
|
||||
|
||||
it "adds an appropriate received header" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
message_id = parsed_body["data"]["messages"]["test@example.com"]["id"]
|
||||
message = server.message(message_id)
|
||||
expect(message.headers["received"].first).to match(/\Afrom api/)
|
||||
end
|
||||
|
||||
it "creates appropriate message objects" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
["test@example.com", "cc@example.com", "bcc@example.com"].each do |rcpt_to|
|
||||
message_id = parsed_body["data"]["messages"][rcpt_to]["id"]
|
||||
message = server.message(message_id)
|
||||
expect(message).to have_attributes(
|
||||
server: server,
|
||||
rcpt_to: rcpt_to,
|
||||
mail_from: params[:from],
|
||||
subject: params[:subject],
|
||||
message_id: kind_of(String),
|
||||
timestamp: kind_of(Time),
|
||||
domain_id: domain.id,
|
||||
credential_id: credential.id,
|
||||
bounce: false,
|
||||
tag: params[:tag],
|
||||
headers: hash_including("x-test-header-1" => ["111"],
|
||||
"x-test-header-2" => ["222"],
|
||||
"sender" => [params[:sender]],
|
||||
"to" => ["test@example.com"],
|
||||
"cc" => ["cc@example.com"],
|
||||
"reply-to" => ["reply@example.com"]),
|
||||
plain_body: params[:plain_body],
|
||||
html_body: params[:html_body],
|
||||
attachments: [
|
||||
have_attributes(content_type: /\Atext\/plain/, filename: "test1.txt", body: have_attributes(to_s: "hello world 1")),
|
||||
have_attributes(content_type: /\Atext\/plain/, filename: "test2.txt", body: have_attributes(to_s: "hello world 2")),
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
152
spec/apis/legacy_api/send/raw_spec.rb
Normal file
152
spec/apis/legacy_api/send/raw_spec.rb
Normal file
@@ -0,0 +1,152 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe "Legacy Send API", type: :request do
|
||||
describe "/api/v1/send/raw" do
|
||||
context "when no authentication is provided" do
|
||||
it "returns an error" do
|
||||
post "/api/v1/send/raw"
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "AccessDenied"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the credential does not match anything" do
|
||||
it "returns an error" do
|
||||
post "/api/v1/send/raw", headers: { "x-server-api-key" => "invalid" }
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "InvalidServerAPIKey"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the credential belongs to a suspended server" do
|
||||
it "returns an error" do
|
||||
server = create(:server, :suspended)
|
||||
credential = create(:credential, server: server)
|
||||
post "/api/v1/send/raw", headers: { "x-server-api-key" => credential.key }
|
||||
expect(response.status).to eq 200
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "error"
|
||||
expect(parsed_body["data"]["code"]).to eq "ServerSuspended"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the credential is valid" do
|
||||
let(:server) { create(:server) }
|
||||
let(:credential) { create(:credential, server: server) }
|
||||
let(:domain) { create(:domain, owner: server) }
|
||||
let(:data) do
|
||||
mail = Mail.new
|
||||
mail.to = "test1@example.com"
|
||||
mail.from = "test@#{domain.name}"
|
||||
mail.subject = "test"
|
||||
mail.text_part = Mail::Part.new
|
||||
mail.text_part.body = "plain text"
|
||||
mail.html_part = Mail::Part.new
|
||||
mail.html_part.content_type = "text/html; charset=UTF-8"
|
||||
mail.html_part.body = "<p>html</p>"
|
||||
mail
|
||||
end
|
||||
let(:default_params) do
|
||||
{
|
||||
mail_from: "test@#{domain.name}",
|
||||
rcpt_to: ["test1@example.com", "test2@example.com"],
|
||||
data: Base64.encode64(data.to_s),
|
||||
bounce: false
|
||||
}
|
||||
end
|
||||
let(:params) { default_params }
|
||||
|
||||
before do
|
||||
post "/api/v1/send/raw",
|
||||
headers: { "x-server-api-key" => credential.key,
|
||||
"content-type" => "application/json" },
|
||||
params: params.to_json
|
||||
end
|
||||
|
||||
context "when rcpt_to is not provided" do
|
||||
let(:params) { default_params.except(:rcpt_to) }
|
||||
|
||||
it "returns an error" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "parameter-error"
|
||||
expect(parsed_body["data"]["message"]).to match(/`rcpt_to` parameter is required but is missing/i)
|
||||
end
|
||||
end
|
||||
|
||||
context "when mail_from is not provided" do
|
||||
let(:params) { default_params.except(:mail_from) }
|
||||
|
||||
it "returns an error" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "parameter-error"
|
||||
expect(parsed_body["data"]["message"]).to match(/`mail_from` parameter is required but is missing/i)
|
||||
end
|
||||
end
|
||||
|
||||
context "when data is not provided" do
|
||||
let(:params) { default_params.except(:data) }
|
||||
|
||||
it "returns an error" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "parameter-error"
|
||||
expect(parsed_body["data"]["message"]).to match(/`data` parameter is required but is missing/i)
|
||||
end
|
||||
end
|
||||
|
||||
context "when no recipients are provided" do
|
||||
let(:params) { default_params.merge(rcpt_to: []) }
|
||||
|
||||
it "returns success but with no messages" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["status"]).to eq "success"
|
||||
expect(parsed_body["data"]["messages"]).to eq({})
|
||||
expect(parsed_body["data"]["message_id"]).to be nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when a valid email is provided" do
|
||||
it "returns details of the messages created" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
expect(parsed_body["data"]["message_id"]).to be_a String
|
||||
expect(parsed_body["data"]["messages"]).to be_a Hash
|
||||
expect(parsed_body["data"]["messages"]).to match({
|
||||
"test1@example.com" => { "id" => kind_of(Integer), "token" => /\A[a-zA-Z0-9]{16}\z/ },
|
||||
"test2@example.com" => { "id" => kind_of(Integer), "token" => /\A[a-zA-Z0-9]{16}\z/ }
|
||||
})
|
||||
end
|
||||
|
||||
it "creates appropriate message objects" do
|
||||
parsed_body = JSON.parse(response.body)
|
||||
["test1@example.com", "test2@example.com"].each do |rcpt_to|
|
||||
message_id = parsed_body["data"]["messages"][rcpt_to]["id"]
|
||||
message = server.message(message_id)
|
||||
expect(message).to have_attributes(
|
||||
server: server,
|
||||
rcpt_to: rcpt_to,
|
||||
mail_from: "test@#{domain.name}",
|
||||
subject: "test",
|
||||
message_id: kind_of(String),
|
||||
timestamp: kind_of(Time),
|
||||
domain_id: domain.id,
|
||||
credential_id: credential.id,
|
||||
bounce: false,
|
||||
headers: hash_including("to" => ["test1@example.com"]),
|
||||
plain_body: "plain text",
|
||||
html_body: "<p>html</p>",
|
||||
attachments: [],
|
||||
received_with_ssl: true,
|
||||
scope: "outgoing",
|
||||
raw_message: data.to_s
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -37,9 +37,16 @@ RSpec.configure do |config|
|
||||
config.include FactoryBot::Syntax::Methods
|
||||
config.include GeneralHelpers
|
||||
|
||||
# Before all request specs, set the hostname to the web hostname for
|
||||
# Postal otherwise it'll be www.example.com which will fail host
|
||||
# authorization checks.
|
||||
config.before(:each, type: :request) do
|
||||
host! Postal::Config.postal.web_hostname
|
||||
end
|
||||
|
||||
# Test that the factories are working as they should and then clean up before getting started on
|
||||
# the rest of the suite.
|
||||
config.before(:suite) do
|
||||
# Test that the factories are working as they should and then clean up before getting started on
|
||||
# the rest of the suite.
|
||||
DatabaseCleaner.start
|
||||
FactoryBot.lint
|
||||
ensure
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم