mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-08-12 10:00:42 +09:00
3ca1dcbf77
Bots can only create. A lifecycle notification — an alert that fires and then resolves, a deploy that starts and finishes, a backup that runs — therefore has to post a second message, and the room becomes an append-only log of states rather than a view of the current one. Adds PATCH and DELETE inside the existing bot_key scope, routed to Messages::ByBotsController. The body is read the way create reads it, so updating a message is the same request shape as posting one. No new authorization: both actions already run through ensure_can_administer, and can_administer? grants access only to a record the user created, so a bot key reaches that bot's own messages and no others. set_room narrows it again by looking the room up through the bot's own memberships. A leaked bot key gains what it could already do by posting: write to rooms that bot belongs to. update answers head :ok rather than the redirect, which meant extracting the update and its broadcast into update_message — calling super and then head would double render, since the parent redirects inside the action. destroy needs no split, because the parent renders implicitly like create does.
229 lines
7.6 KiB
Ruby
229 lines
7.6 KiB
Ruby
require "test_helper"
|
|
|
|
class Messages::ByBotsControllerTest < ActionDispatch::IntegrationTest
|
|
setup do
|
|
@room = rooms(:watercooler)
|
|
end
|
|
|
|
test "create" do
|
|
assert_difference -> { Message.count }, +1 do
|
|
post room_bot_messages_url(@room, users(:bender).bot_key), params: +"Hello Bot World!"
|
|
assert_equal "Hello Bot World!", Message.last.plain_text_body
|
|
end
|
|
end
|
|
|
|
test "create with UTF-8 content" do
|
|
assert_difference -> { Message.count }, +1 do
|
|
post room_bot_messages_url(@room, users(:bender).bot_key), params: +"Hello 👋!"
|
|
assert_equal "Hello 👋!", Message.last.plain_text_body
|
|
end
|
|
end
|
|
|
|
test "create file" do
|
|
assert_difference -> { Message.count }, +1 do
|
|
post room_bot_messages_url(@room, users(:bender).bot_key), params: { attachment: fixture_file_upload("moon.jpg", "image/jpeg") }
|
|
assert Message.last.attachment.present?
|
|
end
|
|
end
|
|
|
|
test "create does not trigger a webhook to the sending bot if it mentions itself" do
|
|
body = "<div>Hey #{mention_attachment_for(:bender)}</div>"
|
|
|
|
assert_no_enqueued_jobs only: Bot::WebhookJob do
|
|
post room_bot_messages_url(@room, users(:bender).bot_key), params: body
|
|
end
|
|
end
|
|
|
|
test "create does not trigger a webhook to the sending bot in a direct room" do
|
|
assert_no_enqueued_jobs only: Bot::WebhookJob do
|
|
post room_bot_messages_url(rooms(:bender_and_kevin), users(:bender).bot_key), params: +"Talking to myself again!"
|
|
end
|
|
end
|
|
|
|
test "create without a body or attachment" do
|
|
assert_no_difference -> { Message.count } do
|
|
post room_bot_messages_url(@room, users(:bender).bot_key)
|
|
assert_response :unprocessable_content
|
|
|
|
post room_bot_messages_url(@room, users(:bender).bot_key), params: +" "
|
|
assert_response :unprocessable_content
|
|
end
|
|
end
|
|
|
|
test "create can't be abused to post messages as any user" do
|
|
user = users(:kevin)
|
|
bot_key = "#{user.id}-"
|
|
|
|
assert_no_difference -> { Message.count } do
|
|
post room_bot_messages_url(rooms(:bender_and_kevin), bot_key), params: "Hello 👋!"
|
|
end
|
|
|
|
assert_response :redirect
|
|
end
|
|
|
|
test "index returns the room's messages in the order they were sent" do
|
|
get room_bot_messages_url(@room, users(:bender).bot_key)
|
|
assert_response :success
|
|
|
|
json = JSON.parse(response.body)
|
|
assert_equal @room.messages.ordered.map(&:id), json.map { it["id"] }
|
|
end
|
|
|
|
test "index includes message details" do
|
|
post room_bot_messages_url(@room, users(:bender).bot_key), params: +"Hello from Bender!"
|
|
|
|
get room_bot_messages_url(@room, users(:bender).bot_key)
|
|
assert_response :success
|
|
|
|
message = Message.last
|
|
json_message = JSON.parse(response.body).last
|
|
assert_equal message.id, json_message["id"]
|
|
assert_equal "Hello from Bender!", json_message["body"]["plain_text"]
|
|
assert_includes json_message["body"]["html"], "Hello from Bender!"
|
|
assert_equal message.created_at.utc.iso8601(3), json_message["created_at"]
|
|
assert_equal users(:bender).id, json_message["creator"]["id"]
|
|
assert_equal "Bender Bot", json_message["creator"]["name"]
|
|
assert_equal "bot", json_message["creator"]["role"]
|
|
assert_equal @room.id, json_message["room"]["id"]
|
|
assert_equal room_message_url(@room, message), json_message["url"]
|
|
end
|
|
|
|
test "index pages through older messages with the Link header" do
|
|
(Message::PAGE_SIZE - @room.messages.count + 1).times do |i|
|
|
@room.messages.create!(body: "Filler #{i}", creator: users(:jason), client_message_id: "filler-#{i}")
|
|
end
|
|
|
|
get room_bot_messages_url(@room, users(:bender).bot_key)
|
|
assert_response :success
|
|
|
|
json = JSON.parse(response.body)
|
|
assert_equal Message::PAGE_SIZE, json.size
|
|
assert_equal "41", response.headers["X-Total-Count"]
|
|
assert_not_includes json.map { it["id"] }, messages(:fourth).id
|
|
|
|
get response.headers["Link"][/<(.*)>/, 1]
|
|
assert_response :success
|
|
|
|
json = JSON.parse(response.body)
|
|
assert_equal [ messages(:fourth).id ], json.map { it["id"] }
|
|
assert_nil response.headers["Link"]
|
|
end
|
|
|
|
test "index pages newer messages with after" do
|
|
get room_bot_messages_url(@room, users(:bender).bot_key, after: messages(:tenth).id)
|
|
assert_response :success
|
|
|
|
json = JSON.parse(response.body)
|
|
assert_equal %i[ eleventh twelfth thirteenth ].map { messages(it).id }, json.map { it["id"] }
|
|
assert_nil response.headers["Link"]
|
|
end
|
|
|
|
test "index in a room with no messages" do
|
|
get room_bot_messages_url(rooms(:bender_and_kevin), users(:bender).bot_key)
|
|
assert_response :success
|
|
|
|
assert_equal [], JSON.parse(response.body)
|
|
assert_equal "0", response.headers["X-Total-Count"]
|
|
assert_nil response.headers["Link"]
|
|
end
|
|
|
|
test "index requires a valid bot key" do
|
|
get room_bot_messages_url(@room, "invalid-bot-key")
|
|
assert_response :redirect
|
|
end
|
|
|
|
test "index is not found for a room the bot is not a member of" do
|
|
get room_bot_messages_url(rooms(:designers), users(:bender).bot_key)
|
|
assert_response :not_found
|
|
end
|
|
|
|
test "create is not found for a room the bot is not a member of" do
|
|
assert_no_difference -> { Message.count } do
|
|
post room_bot_messages_url(rooms(:designers), users(:bender).bot_key), params: +"Hello!"
|
|
end
|
|
assert_response :not_found
|
|
end
|
|
|
|
test "regular messages index remains denied for bots" do
|
|
get room_messages_url(@room, bot_key: users(:bender).bot_key)
|
|
assert_response :forbidden
|
|
end
|
|
|
|
test "update" do
|
|
message = post_bot_message "Deploying..."
|
|
|
|
assert_no_difference -> { Message.count } do
|
|
patch room_bot_message_url(@room, users(:bender).bot_key, message), params: +"Deployed."
|
|
end
|
|
|
|
assert_response :ok
|
|
assert_equal "Deployed.", message.reload.plain_text_body
|
|
end
|
|
|
|
test "update with UTF-8 content" do
|
|
message = post_bot_message "Deploying..."
|
|
|
|
patch room_bot_message_url(@room, users(:bender).bot_key, message), params: +"Deployed 🚀!"
|
|
|
|
assert_response :ok
|
|
assert_equal "Deployed 🚀!", message.reload.plain_text_body
|
|
end
|
|
|
|
test "update can't touch a message the bot did not create" do
|
|
message = messages(:fourth)
|
|
original = message.plain_text_body
|
|
|
|
patch room_bot_message_url(@room, users(:bender).bot_key, message), params: +"Hijacked!"
|
|
|
|
assert_response :forbidden
|
|
assert_equal original, message.reload.plain_text_body
|
|
end
|
|
|
|
test "update is not found for a room the bot is not a member of" do
|
|
message = messages(:first)
|
|
original = message.plain_text_body
|
|
|
|
patch room_bot_message_url(rooms(:designers), users(:bender).bot_key, message), params: +"Hijacked!"
|
|
|
|
assert_response :not_found
|
|
assert_equal original, message.reload.plain_text_body
|
|
end
|
|
|
|
test "update can't be abused to edit messages as any user" do
|
|
message = messages(:fourth)
|
|
bot_key = "#{users(:jz).id}-"
|
|
original = message.plain_text_body
|
|
|
|
patch room_bot_message_url(@room, bot_key, message), params: +"Hijacked!"
|
|
|
|
assert_response :redirect
|
|
assert_equal original, message.reload.plain_text_body
|
|
end
|
|
|
|
test "destroy" do
|
|
message = post_bot_message "Deploying..."
|
|
|
|
assert_difference -> { Message.count }, -1 do
|
|
delete room_bot_message_url(@room, users(:bender).bot_key, message)
|
|
end
|
|
|
|
assert_response :no_content
|
|
end
|
|
|
|
test "destroy can't touch a message the bot did not create" do
|
|
message = messages(:fourth)
|
|
|
|
assert_no_difference -> { Message.count } do
|
|
delete room_bot_message_url(@room, users(:bender).bot_key, message)
|
|
end
|
|
|
|
assert_response :forbidden
|
|
end
|
|
|
|
private
|
|
def post_bot_message(body)
|
|
post room_bot_messages_url(@room, users(:bender).bot_key), params: +body
|
|
Message.last
|
|
end
|
|
end
|