require "test_helper" class DocumentChannelTest < ActionCable::Channel::TestCase setup do @anna = create_user("anna@example.com") @bob = create_user("bob@example.com") @doc = create_document(@anna) end def b64(s) = Base64.strict_encode64(s) def last = transmissions.last test "rejects without access" do stub_connection current_user: @bob subscribe id: @doc.id assert subscription.rejected? end test "init sends the JSON while there are no updates yet, then the updates" do stub_connection current_user: @anna subscribe id: @doc.id, key: "a" assert subscription.confirmed? assert_equal "owner", last["role"].to_s assert_equal DOC_JSON, last["content"] assert_empty last["updates"] @doc.updates.create!(data: "\x01\x02") subscribe id: @doc.id, key: "a" assert_nil last["content"] assert_equal [ b64("\x01\x02") ], last["updates"].map { _1["data"] } end test "an editor's update is stored and passed on to everyone" do stub_connection current_user: @anna subscribe id: @doc.id, key: "a" assert_broadcasts(DocumentChannel.broadcasting_for(@doc), 1) do perform :update, data: b64("abc") end assert_equal [ "abc" ], @doc.updates.pluck(:data) end test "a viewer (link) can't change anything but may send awareness" do link = @doc.share_links.create!(role: :viewer, created_by: @anna) stub_connection current_user: nil subscribe id: @doc.id, token: link.token, key: "v" assert subscription.confirmed? perform :update, data: b64("x") perform :compact, state: b64("x"), ids: [], snapshot: DOC_JSON assert_equal 0, @doc.updates.count assert_broadcasts(DocumentChannel.broadcasting_for(@doc), 1) do perform :awareness, data: b64("cursor") end end test "compact replaces the listed updates by one state and stores the snapshot" do a = @doc.updates.create!(data: "1") b = @doc.updates.create!(data: "2") keep = @doc.updates.create!(data: "3") stub_connection current_user: @anna subscribe id: @doc.id, key: "a" snapshot = { version: 2, sheets: [ { id: "s9", name: "Neu", objects: [] } ] }.to_json perform :compact, state: b64("12"), ids: [ a.id, b.id ], snapshot: snapshot assert_equal %w[3 12], @doc.updates.order(:id).pluck(:data) assert keep.reload assert_equal snapshot, @doc.reload.content end test "invalid snapshots are ignored" do stub_connection current_user: @anna subscribe id: @doc.id, key: "a" perform :snapshot, content: "[1]" assert_equal DOC_JSON, @doc.reload.content end test "losing access mid-session" do DocumentMembership.grant!(document: @doc, user: @bob, role: :editor, source: :invite) stub_connection current_user: @bob subscribe id: @doc.id, key: "b" @doc.document_memberships.delete_all subscription.send(:revalidate) assert_equal "revoked", last["type"] perform :update, data: b64("late") assert_equal 0, @doc.updates.count end test "role change is announced" do DocumentMembership.grant!(document: @doc, user: @bob, role: :editor, source: :invite) stub_connection current_user: @bob subscribe id: @doc.id, key: "b" @doc.document_memberships.update_all(role: DocumentMembership.roles[:viewer]) subscription.send(:revalidate) assert_equal({ "type" => "role", "role" => "viewer" }, last.slice("type", "role").transform_values(&:to_s)) end end