Testing Legacy Messages API Implementation in Postal Server
This test suite validates the Legacy Messages API endpoints in Postal Server, focusing on message retrieval and expansion functionality. It covers authentication, error handling, and various message data expansion scenarios.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
postalserver/postal
spec/apis/legacy_api/messages/message_spec.rb
# 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 all expansions are requested" do
let(:expansions) { true }
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" },
"details" => { "bounce" => false,
"bounce_for_id" => 0,
"direction" => "outgoing",
"mail_from" => "[email protected]",
"message_id" => message.message_id,
"rcpt_to" => "[email protected]",
"received_with_ssl" => nil,
"size" => kind_of(String),
"subject" => "An example message",
"tag" => nil,
"timestamp" => kind_of(Float) },
"inspection" => { "inspected" => false,
"spam" => false,
"spam_score" => 0.0,
"threat" => false,
"threat_details" => nil },
"plain_body" => message.plain_body,
"html_body" => message.html_body,
"attachments" => [],
"headers" => message.headers,
"raw_message" => Base64.encode64(message.raw_message),
"activity_entries" => {
"loads" => [],
"clicks" => []
}
})
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" => "[email protected]",
"message_id" => message.message_id,
"rcpt_to" => "[email protected]",
"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