# frozen_string_literal: true RSpec.describe PresenceController do fab!(:user) fab!(:group) { Fabricate(:group).tap { |g| g.add(user) } } let(:ch1) { PresenceChannel.new("/test/public1") } let(:ch2) { PresenceChannel.new("/test/public2") } let(:secure_user_channel) { PresenceChannel.new("/test/secureuser") } let(:secure_group_channel) { PresenceChannel.new("/test/securegroup") } let(:allowed_user_channel) { PresenceChannel.new("/test/alloweduser") } let(:allowed_group_channel) { PresenceChannel.new("/test/allowedgroup") } let(:count_only_channel) { PresenceChannel.new("/test/countonly") } before do PresenceChannel.clear_all! secure_user = Fabricate(:user) secure_group = Fabricate(:group) PresenceChannel.register_prefix("test") do |channel| case channel when %r{\A/test/public\d*\z} PresenceChannel::Config.new(public: true) when "/test/secureuser" PresenceChannel::Config.new(allowed_user_ids: [secure_user.id]) when "/test/securegroup" PresenceChannel::Config.new(allowed_group_ids: [secure_group.id]) when "/test/alloweduser" PresenceChannel::Config.new(allowed_user_ids: [user.id]) when "/test/allowedgroup" PresenceChannel::Config.new(allowed_group_ids: [group.id]) when "/test/countonly" PresenceChannel::Config.new(public: true, count_only: true) else nil end end end after do PresenceChannel.clear_all! PresenceChannel.unregister_prefix("test") end describe "#update" do context "in readonly mode" do use_redis_snapshotting before { Discourse.enable_readonly_mode } it "produces 503" do sign_in(user) client_id = SecureRandom.hex post "/presence/update.json", params: { client_id: client_id, present_channels: [ch1.name, ch2.name], } expect(response.status).to eq(503) end end it "works" do sign_in(user) client_id = SecureRandom.hex expect(ch1.user_ids).to eq([]) expect(ch2.user_ids).to eq([]) post "/presence/update.json", params: { client_id: client_id, present_channels: [ch1.name, ch2.name], } expect(response.status).to eq(200) expect(ch1.user_ids).to eq([user.id]) expect(ch2.user_ids).to eq([user.id]) post "/presence/update.json", params: { client_id: client_id, present_channels: [ch1.name], leave_channels: [ch2.name], } expect(response.status).to eq(200) expect(ch1.user_ids).to eq([user.id]) expect(ch2.user_ids).to eq([]) post "/presence/update.json", params: { client_id: client_id, present_channels: [], leave_channels: [ch1.name], } expect(response.status).to eq(200) expect(ch1.user_ids).to eq([]) expect(ch2.user_ids).to eq([]) end it "returns true/false based on channel existence/security" do sign_in(user) client_id = SecureRandom.hex expect(ch1.user_ids).to eq([]) expect(secure_user_channel.user_ids).to eq([]) expect(secure_group_channel.user_ids).to eq([]) post "/presence/update.json", params: { client_id: client_id, present_channels: [ ch1.name, secure_user_channel.name, secure_group_channel.name, allowed_user_channel.name, allowed_group_channel.name, "/test/nonexistent", ], } expect(response.status).to eq(200) expect(response.parsed_body).to eq( { ch1.name => true, secure_user_channel.name => false, secure_group_channel.name => false, allowed_user_channel.name => true, allowed_group_channel.name => true, "/test/nonexistent" => false, }, ) expect(ch1.user_ids).to eq([user.id]) expect(secure_user_channel.user_ids).to eq([]) expect(secure_group_channel.user_ids).to eq([]) expect(allowed_user_channel.user_ids).to eq([user.id]) expect(allowed_group_channel.user_ids).to eq([user.id]) end it "doesn't overwrite the session" do sign_in(user) session_cookie_name = "_forum_session" get "/session/csrf.json" expect(response.status).to eq(200) expect(response.cookies.keys).to include(session_cookie_name) client_id = SecureRandom.hex post "/presence/update.json", params: { client_id: client_id, present_channels: [ch1.name] } expect(response.status).to eq(200) expect(response.cookies.keys).not_to include(session_cookie_name) end end describe "#get" do let(:user2) { Fabricate(:user) } let(:user3) { Fabricate(:user) } it "works" do get "/presence/get", params: { channels: [ch1.name] } expect(response.status).to eq(200) expect(response.parsed_body).to eq( ch1.name => { "users" => [], "count" => 0, "last_message_id" => MessageBus.last_id(ch1.message_bus_channel_name), }, ) ch1.present(user_id: user.id, client_id: SecureRandom.hex) ch1.present(user_id: user2.id, client_id: SecureRandom.hex) ch1.present(user_id: user3.id, client_id: SecureRandom.hex) get "/presence/get", params: { channels: [ch1.name] } expect(response.status).to eq(200) state = response.parsed_body[ch1.name] expect(state["users"].map { |u| u["id"] }).to contain_exactly(user.id, user2.id, user3.id) expect(state["users"][0].keys).to contain_exactly("avatar_template", "id", "name", "username") expect(state["count"]).to eq(3) expect(state["last_message_id"]).to eq(MessageBus.last_id(ch1.message_bus_channel_name)) end it "respects the existence/security of the channel" do sign_in user get "/presence/get", params: { channels: [ ch1.name, allowed_user_channel.name, allowed_group_channel.name, secure_user_channel.name, secure_group_channel.name, "/test/nonexistent", ], } expect(response.status).to eq(200) expect(response.parsed_body).to include( ch1.name => be_truthy, allowed_user_channel.name => be_truthy, allowed_group_channel.name => be_truthy, secure_user_channel.name => be_nil, secure_group_channel.name => be_nil, "/test/nonexistent" => be_nil, ) end it "works for count_only channels" do get "/presence/get", params: { channels: [count_only_channel.name] } expect(response.status).to eq(200) state = response.parsed_body[count_only_channel.name] expect(state.keys).to contain_exactly("count", "last_message_id") expect(state["count"]).to eq(0) count_only_channel.present(user_id: user.id, client_id: "a") get "/presence/get", params: { channels: [count_only_channel.name] } expect(response.status).to eq(200) expect(response.parsed_body[count_only_channel.name]["count"]).to eq(1) end end end