diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 050be3e..4cfd977 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: persist-credentials: false - name: Set up Ruby - uses: ruby/setup-ruby@a0102e0972be65f351c307e2d64b9314a57c8073 # v1.324.0 + uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0 with: ruby-version: .ruby-version bundler-cache: true @@ -40,7 +40,7 @@ jobs: persist-credentials: false - name: Set up Ruby - uses: ruby/setup-ruby@a0102e0972be65f351c307e2d64b9314a57c8073 # v1.324.0 + uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0 with: ruby-version: .ruby-version bundler-cache: true @@ -87,7 +87,7 @@ jobs: persist-credentials: false - name: Set up Ruby - uses: ruby/setup-ruby@a0102e0972be65f351c307e2d64b9314a57c8073 # v1.324.0 + uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0 env: REDIS_URL: redis://localhost:6379/0 with: @@ -117,7 +117,7 @@ jobs: persist-credentials: false - name: Set up Ruby - uses: ruby/setup-ruby@a0102e0972be65f351c307e2d64b9314a57c8073 # v1.324.0 + uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0 env: REDIS_URL: redis://localhost:6379/0 with: diff --git a/Gemfile.lock b/Gemfile.lock index e361a18..3d02eff 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -23,9 +23,9 @@ GIT GIT remote: https://github.com/rails/propshaft.git - revision: e49a9de659ff27462015e54dd832e86e762a6ddc + revision: dc979db89cd07c72ee4d11d415ae1cb4fd072623 specs: - propshaft (1.2.1) + propshaft (1.3.2) actionpack (>= 7.0.0) activesupport (>= 7.0.0) rack @@ -142,7 +142,7 @@ GEM base64 (0.3.0) bcrypt (3.1.22) benchmark (0.5.0) - bigdecimal (3.3.1) + bigdecimal (4.1.3) brakeman (8.0.6) racc builder (3.3.0) @@ -159,7 +159,7 @@ GEM regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) chunky_png (1.4.0) - concurrent-ruby (1.3.7) + concurrent-ruby (1.3.8) connection_pool (2.5.5) crack (1.0.0) bigdecimal @@ -172,7 +172,7 @@ GEM drb (2.2.3) erb (6.0.4) erubi (1.13.1) - faker (3.5.2) + faker (3.8.0) i18n (>= 1.8.11, < 2) ffi (1.17.2) ffi (1.17.2-aarch64-linux-gnu) @@ -185,7 +185,7 @@ GEM globalid (1.3.0) activesupport (>= 6.1) hashdiff (1.2.0) - i18n (1.14.7) + i18n (1.15.2) concurrent-ruby (~> 1.0) image_processing (1.14.0) mini_magick (>= 4.9.5, < 6) @@ -224,7 +224,9 @@ GEM mini_magick (5.3.1) logger mini_mime (1.1.5) - minitest (5.26.2) + minitest (6.0.6) + drb (~> 2.0) + prism (~> 1.5) mocha (3.1.0) ruby2_keywords (>= 0.0.5) mono_logger (1.1.2) @@ -263,7 +265,7 @@ GEM pp (0.6.3) prettyprint prettyprint (0.2.0) - prism (1.4.0) + prism (1.9.0) psych (5.2.6) date stringio @@ -271,7 +273,7 @@ GEM puma (7.2.1) nio4r (~> 2.0) racc (1.8.1) - rack (3.2.6) + rack (3.2.7) rack-protection (4.2.1) base64 (>= 0.1.0) logger (>= 1.6.0) @@ -355,9 +357,9 @@ GEM ffi (~> 1.12) logger ruby2_keywords (0.0.5) - rubyzip (3.4.0) + rubyzip (3.7.0) securerandom (0.4.1) - selenium-webdriver (4.35.0) + selenium-webdriver (4.49.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) diff --git a/app/controllers/searches_controller.rb b/app/controllers/searches_controller.rb index 221218d..735da0c 100644 --- a/app/controllers/searches_controller.rb +++ b/app/controllers/searches_controller.rb @@ -20,7 +20,7 @@ class SearchesController < ApplicationController private def set_messages if query.present? - @messages = Current.user.reachable_messages.search(query).with_presentation.last_page_of(100) + @messages = Current.user.reachable_messages.search(query).with_presentation.last_page_of_matches(100) else @messages = Message.none end diff --git a/app/models/message/searchable.rb b/app/models/message/searchable.rb index b792cd0..9245ead 100644 --- a/app/models/message/searchable.rb +++ b/app/models/message/searchable.rb @@ -17,6 +17,14 @@ module Message::Searchable end end + class_methods do + # Orders by the index's rowid, which is the message id, so SQLite walks the full-text index + # newest first and stops at the page. Ordering by created_at sorted every match before paging. + def last_page_of_matches(size) + Message::Pagination::Page.load(reorder("idx.rowid"), :last, size) + end + end + private def create_in_index execute_sql_with_binds "insert into message_search_index(rowid, body) values (?, ?)", id, plain_text_body diff --git a/app/models/webhook.rb b/app/models/webhook.rb index 5c307e7..f386e5f 100644 --- a/app/models/webhook.rb +++ b/app/models/webhook.rb @@ -63,7 +63,7 @@ class Webhook < ApplicationRecord end def extract_attachment_from(response) - if response.content_type && mime_type = Mime::Type.lookup(response.content_type) + if response.code == "200" && response.content_type && mime_type = Mime::Type.lookup(response.content_type) ActiveStorage::Blob.create_and_upload! \ io: StringIO.new(response.body), filename: "attachment.#{mime_type.symbol}", content_type: mime_type.to_s end diff --git a/app/views/pwa/_install_instructions.html.erb b/app/views/pwa/_install_instructions.html.erb index d956053..b82ddb2 100644 --- a/app/views/pwa/_install_instructions.html.erb +++ b/app/views/pwa/_install_instructions.html.erb @@ -9,7 +9,7 @@ <% case when platform.edge? %>
    -
  1. Click <%= image_tag "install-edge.svg", alt: "the app available - install Campfire chat button", size: 16 %>in the address bar.
  2. +
  3. Click <%= image_tag "external/install-edge.svg", alt: "the app available - install Campfire chat button", size: 16 %>in the address bar.
  4. Click Install.
<% when platform.chrome? && platform.android? %> diff --git a/test/controllers/users/profiles_controller_test.rb b/test/controllers/users/profiles_controller_test.rb index 1f14953..0331aaf 100644 --- a/test/controllers/users/profiles_controller_test.rb +++ b/test/controllers/users/profiles_controller_test.rb @@ -11,6 +11,13 @@ class Users::ProfilesControllerTest < ActionDispatch::IntegrationTest assert_response :success end + test "show includes install instructions for Edge" do + get user_profile_url, headers: { "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18363" } + + assert_response :success + assert_select ".pwa__instructions img[src*='install-edge']" + end + test "update" do put user_profile_url, params: { user: { name: "John Doe", bio: "Acrobat" } } diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml index 39f71a5..a2f8804 100644 --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -1,4 +1,4 @@ -<% password_digest = BCrypt::Password.create("secret123456") %> +<% password_digest = BCrypt::Password.create("secret123456", cost: BCrypt::Engine::MIN_COST) %> david: name: David diff --git a/test/models/message/searchable_test.rb b/test/models/message/searchable_test.rb index d55bedf..6a8f4ab 100644 --- a/test/models/message/searchable_test.rb +++ b/test/models/message/searchable_test.rb @@ -20,6 +20,21 @@ class Message::SearchableTest < ActiveSupport::TestCase assert_equal messages, rooms(:designers).messages.search("cat") end + test "the last page of matches holds the newest ones, read off the index without sorting every match" do + messages = [ "first cat", "second cat", "third cat" ].map do |body| + rooms(:designers).messages.create! body: body, client_message_id: body, creator: users(:david) + end + + queries = [] + collect = ->(*, payload) { queries << payload[:sql] if payload[:sql].include?("message_search_index") } + page = ActiveSupport::Notifications.subscribed(collect, "sql.active_record") do + rooms(:designers).messages.search("cat").last_page_of_matches(2) + end + + assert_equal messages.last(2), page + assert_no_match(/TEMP B-TREE/, Message.connection.select_rows("EXPLAIN QUERY PLAN #{queries.sole}").map(&:last).join(" | ")) + end + test "rich text body is converted to plain text for indexing" do message = rooms(:designers).messages.create! body: "My hovercraft is full of eels", client_message_id: "earth", creator: users(:david) diff --git a/test/models/webhook_test.rb b/test/models/webhook_test.rb index 4f4e3bc..d37192a 100644 --- a/test/models/webhook_test.rb +++ b/test/models/webhook_test.rb @@ -41,7 +41,7 @@ class WebhookTest < ActiveSupport::TestCase test "delivery with error reply" do assert_no_difference -> { Message.count } do - WebMock.stub_request(:post, webhooks(:bender).url).to_return(status: 500, body: "Internal Error!", headers: {}) + WebMock.stub_request(:post, webhooks(:bender).url).to_return(status: 500, body: "

Internal Error!

", headers: { "Content-Type" => "text/html" }) response = webhooks(:bender).deliver(messages(:first)) end end