mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-10-09 08:10:08 +09:00
462eff10df
Deleting the memberships with delete_all skips Membership's after_destroy_commit, which resets a member's connections when one membership is revoked. Until the job ran, a member who had the room open kept its streams, and a message that still landed in the room (a request already past the membership check, a bot's reply) reached them. The request now reads the members' ids in the transaction that deletes their memberships, and the job resets their connections before it destroys the messages. It costs a Redis round trip per member, about 1.5 s for 10,000, so it's done in the job rather than in the request. User#grant_membership_to_open_rooms read the open rooms and inserted in a separate statement, so a user created while a room was being closed could read it as open and be granted it after the close. It's now a single insert ... select, which SQLite runs under the write lock, skipping duplicates as insert_all did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bj8KnxpTf9sj2Ysa8aLAVa
125 lines
4.6 KiB
Ruby
125 lines
4.6 KiB
Ruby
require "test_helper"
|
|
|
|
class RoomsControllerTest < ActionDispatch::IntegrationTest
|
|
setup do
|
|
sign_in :david
|
|
end
|
|
|
|
test "index redirects to the user's last room" do
|
|
get rooms_url
|
|
assert_redirected_to room_url(users(:david).rooms.last)
|
|
end
|
|
|
|
test "show" do
|
|
get room_url(users(:david).rooms.last)
|
|
assert_response :success
|
|
end
|
|
|
|
test "show renders notification help for each platform" do
|
|
{
|
|
"Firefox on Android" => "Mozilla/5.0 (Android 14; Mobile; rv:131.0) Gecko/131.0 Firefox/131.0",
|
|
"Chrome on Android" => "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Mobile Safari/537.36",
|
|
"Firefox on desktop" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0",
|
|
"Chrome on desktop" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36",
|
|
"Safari on macOS" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15"
|
|
}.each do |platform, user_agent|
|
|
get room_url(users(:david).rooms.last), headers: { "User-Agent" => user_agent }
|
|
|
|
assert_response :success, platform
|
|
assert_select ".notifications-help ol", { minimum: 1 }, platform
|
|
end
|
|
end
|
|
|
|
test "shows records the last room visited in a cookie" do
|
|
get room_url(users(:david).rooms.last)
|
|
assert response.cookies[:last_room] = users(:david).rooms.last.id
|
|
end
|
|
|
|
test "show renders a link preview written by hand without its off-scheme image and link" do
|
|
room = rooms(:watercooler)
|
|
post room_messages_url(room, format: :turbo_stream), params: { message: {
|
|
body: link_preview_body(href: "javascript:alert(1)", url: "data:image/svg+xml;base64,PHN2Zy8+"),
|
|
client_message_id: "hand-written-preview" } }
|
|
assert_response :success
|
|
|
|
get room_url(room)
|
|
|
|
assert_response :success
|
|
assert_no_match /javascript:alert/, response.body
|
|
assert_no_match /data:image\/svg/, response.body
|
|
assert_match "Free cookies", response.body
|
|
end
|
|
|
|
test "show renders a link preview written by hand without its image pointed at this Campfire" do
|
|
room = rooms(:watercooler)
|
|
own_url = room_url(room, host: "www.example.com")
|
|
post room_messages_url(room, format: :turbo_stream), params: { message: {
|
|
body: link_preview_body(href: own_url, url: own_url),
|
|
client_message_id: "same-host-preview" } }
|
|
assert_response :success
|
|
|
|
get room_url(room)
|
|
|
|
assert_response :success
|
|
assert_no_match %r{<img src="#{Regexp.escape(own_url)}"}, response.body
|
|
assert_no_match %r{<a rel="noreferrer" target="_blank" href="#{Regexp.escape(own_url)}"}, response.body
|
|
assert_match "Free cookies", response.body
|
|
end
|
|
|
|
test "show renders an unfurled link preview" do
|
|
room = rooms(:watercooler)
|
|
post room_messages_url(room, format: :turbo_stream), params: { message: {
|
|
body: link_preview_body(href: "https://example.com/page", url: "https://example.com/image.png"),
|
|
client_message_id: "unfurled-preview" } }
|
|
assert_response :success
|
|
|
|
get room_url(room)
|
|
|
|
assert_response :success
|
|
assert_match %r{<img src="https://example\.com/image\.png"}, response.body
|
|
assert_match %r{href="https://example\.com/page"}, response.body
|
|
end
|
|
|
|
test "destroy" do
|
|
assert_turbo_stream_broadcasts :rooms, count: 1 do
|
|
assert_difference -> { Room.count }, -1 do
|
|
perform_enqueued_jobs { delete room_url(rooms(:designers)) }
|
|
end
|
|
end
|
|
end
|
|
|
|
test "destroy takes the room away from its members at once and leaves its messages to a job" do
|
|
room = rooms(:designers)
|
|
member_ids = room.memberships.pluck(:user_id)
|
|
|
|
assert_enqueued_with(job: Room::DestroyJob, args: [ room, member_ids ]) do
|
|
assert_no_difference -> { Message.count } do
|
|
delete room_url(room)
|
|
assert_redirected_to root_url
|
|
end
|
|
end
|
|
assert_empty room.memberships.reload
|
|
end
|
|
|
|
test "destroy only allowed for creators or those who can administer" do
|
|
sign_in :jz
|
|
|
|
assert_no_difference -> { Room.count } do
|
|
delete room_url(rooms(:designers))
|
|
assert_response :forbidden
|
|
end
|
|
|
|
rooms(:designers).update! creator: users(:jz)
|
|
|
|
assert_difference -> { Room.count }, -1 do
|
|
perform_enqueued_jobs { delete room_url(rooms(:designers)) }
|
|
end
|
|
end
|
|
|
|
private
|
|
def link_preview_body(href:, url:)
|
|
%(<div><action-text-attachment content-type="application/vnd.actiontext.opengraph-embed" ) +
|
|
%(href="#{href}" url="#{url}" filename="Free cookies" caption="Cookies here"></action-text-attachment></div>)
|
|
end
|
|
end
|