1114 lines
51 KiB
Ruby
1114 lines
51 KiB
Ruby
#
|
|
# Copyright (C) 2011 - present Instructure, Inc.
|
|
#
|
|
# This file is part of Canvas.
|
|
#
|
|
# Canvas is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU Affero General Public License as published by the Free
|
|
# Software Foundation, version 3 of the License.
|
|
#
|
|
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License along
|
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
require File.expand_path(File.dirname(__FILE__) + '/../sharding_spec_helper.rb')
|
|
|
|
describe Conversation do
|
|
let_once(:sender) { user_factory }
|
|
let_once(:recipient) { user_factory }
|
|
|
|
context "initiation" do
|
|
it "should set private_hash for private conversations" do
|
|
users = create_users(2, return_type: :record)
|
|
expect(Conversation.initiate(users, true).private_hash).not_to be_nil
|
|
end
|
|
|
|
it "should not set private_hash for group conversations" do
|
|
users = create_users(3, return_type: :record)
|
|
expect(Conversation.initiate(users, false).private_hash).to be_nil
|
|
end
|
|
|
|
it "should reuse private conversations" do
|
|
users = create_users(2, return_type: :record)
|
|
c1 = Conversation.initiate(users, true)
|
|
c2 = Conversation.initiate(users, true)
|
|
expect(c1).to eq c2
|
|
ActiveRecord::Base
|
|
end
|
|
|
|
it "should not reuse group conversations" do
|
|
users = create_users(2, return_type: :record)
|
|
expect(Conversation.initiate(users, false)).not_to eq(
|
|
Conversation.initiate(users, false)
|
|
)
|
|
end
|
|
|
|
it "should populate subject if provided" do
|
|
users = create_users(2, return_type: :record)
|
|
expect(Conversation.initiate(users, nil, :subject => 'lunch').subject).to eq 'lunch'
|
|
end
|
|
|
|
context "sharding" do
|
|
specs_require_sharding
|
|
|
|
it "should create the conversation on the appropriate shard" do
|
|
users = []
|
|
users << user_factory(:name => 'a')
|
|
@shard1.activate { users << user_factory(:name => 'b') }
|
|
@shard2.activate { users << user_factory(:name => 'c') }
|
|
Shard.with_each_shard([Shard.default, @shard1, @shard2]) do
|
|
conversation = Conversation.initiate(users, false)
|
|
expect(conversation.shard).to eq Shard.current
|
|
expect(conversation.conversation_participants.all? { |cp| cp.shard == Shard.current }).to be_truthy
|
|
expect(conversation.conversation_participants.length).to eq 3
|
|
expect(conversation.participants.map(&:id)).to eq users.map(&:id)
|
|
cp = users[0].all_conversations.last
|
|
expect(cp.shard).to eq Shard.default
|
|
cp = users[1].all_conversations.last
|
|
expect(cp.shard).to eq @shard1
|
|
cp = users[2].all_conversations.last
|
|
expect(cp.shard).to eq @shard2
|
|
end
|
|
end
|
|
|
|
it "should re-use a private conversation from any shard" do
|
|
users = [user_factory]
|
|
@shard1.activate { users << user_factory }
|
|
conversation = Conversation.initiate(users, true)
|
|
expect(Conversation.initiate(users, true)).to eq conversation
|
|
@shard1.activate do
|
|
expect(Conversation.initiate(users, true)).to eq conversation
|
|
end
|
|
@shard2.activate do
|
|
expect(Conversation.initiate(users, true)).to eq conversation
|
|
end
|
|
end
|
|
|
|
it "should re-use a private conversation from an unrelated shard" do
|
|
users = []
|
|
@shard1.activate { users << user_factory }
|
|
@shard2.activate { users << user_factory }
|
|
conversation = Conversation.initiate(users, true)
|
|
expect(Conversation.initiate(users, true)).to eq conversation
|
|
@shard1.activate do
|
|
expect(Conversation.initiate(users, true)).to eq conversation
|
|
end
|
|
@shard2.activate do
|
|
expect(Conversation.initiate(users, true)).to eq conversation
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context "adding participants" do
|
|
it "should not add participants to private conversations" do
|
|
root_convo = Conversation.initiate([sender, recipient], true)
|
|
expect{ root_convo.add_participants(sender, [user_factory]) }.to raise_error("can't add participants to a private conversation")
|
|
end
|
|
|
|
it "should add new participants to group conversations and give them all messages" do
|
|
root_convo = Conversation.initiate([sender, recipient], false)
|
|
root_convo.add_message(sender, 'test')
|
|
|
|
new_guy = user_factory
|
|
expect{ root_convo.add_participants(sender, [new_guy]) }.not_to raise_error
|
|
expect(root_convo.participants(true).size).to eq 3
|
|
|
|
convo = new_guy.conversations.first
|
|
expect(convo.unread?).to be_truthy
|
|
expect(convo.messages.size).to eq 2 # the test message plus a "user was added" message
|
|
expect(convo.participants.size).to eq 3 # includes the sender (though we don't show him in the ui)
|
|
end
|
|
|
|
it "should only add participants to messages the existing user has participants on" do
|
|
root_convo = Conversation.initiate([sender, recipient], false)
|
|
msgs = []
|
|
msgs << root_convo.add_message(sender, "first message body") <<
|
|
root_convo.add_message(sender, "second message body") <<
|
|
root_convo.add_message(sender, "third message body") <<
|
|
root_convo.add_message(sender, "fourth message body")
|
|
sender.conversations.first.remove_messages(msgs[0])
|
|
sender.conversations.first.delete_messages(msgs[1])
|
|
|
|
new_guy = user_factory
|
|
root_convo.add_participants(sender, [new_guy])
|
|
# -1 for hard delete msg, +1 for generated message. soft deleted should still be added.
|
|
expect(new_guy.conversations.first.messages.size).to eql(msgs.size - 1 + 1)
|
|
end
|
|
|
|
|
|
it "should not re-add existing participants to group conversations" do
|
|
root_convo = Conversation.initiate([sender, recipient], false)
|
|
expect{ root_convo.add_participants(sender, [recipient]) }.not_to raise_error
|
|
expect(root_convo.participants.size).to eq 2
|
|
end
|
|
|
|
it "should update the updated_at timestamp and clear the identity header cache of new participants" do
|
|
root_convo = Conversation.initiate([sender, recipient], false)
|
|
root_convo.add_message(sender, 'test')
|
|
|
|
new_guy = user_factory
|
|
old_updated_at = new_guy.updated_at
|
|
root_convo.add_participants(sender, [new_guy])
|
|
expect(new_guy.reload.updated_at).not_to eql old_updated_at
|
|
end
|
|
|
|
context "sharding" do
|
|
specs_require_sharding
|
|
|
|
it "should add participants to the proper shards" do
|
|
users = []
|
|
users << user_factory(:name => 'a')
|
|
users << user_factory(:name => 'b')
|
|
users << user_factory(:name => 'c')
|
|
conversation = Conversation.initiate(users, false)
|
|
conversation.add_message(users.first, 'test')
|
|
expect(conversation.conversation_participants.size).to eq 3
|
|
@shard1.activate do
|
|
users << user_factory(:name => 'd')
|
|
conversation.add_participants(users.first, [users.last])
|
|
expect(conversation.conversation_participants.reload.size).to eq 4
|
|
expect(conversation.conversation_participants.all? { |cp| cp.shard == Shard.default }).to be_truthy
|
|
expect(users.last.all_conversations.last.shard).to eq @shard1
|
|
expect(conversation.participants(true).map(&:id)).to eq users.map(&:id)
|
|
end
|
|
@shard2.activate do
|
|
users << user_factory(:name => 'e')
|
|
conversation.add_participants(users.first, users[-2..-1])
|
|
expect(conversation.conversation_participants.reload.size).to eq 5
|
|
expect(conversation.conversation_participants.all? { |cp| cp.shard == Shard.default }).to be_truthy
|
|
expect(users.last.all_conversations.last.shard).to eq @shard2
|
|
expect(conversation.participants(true).map(&:id)).to eq users.map(&:id)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context "message counts" do
|
|
shared_examples_for "message counts" do
|
|
before :once do
|
|
(@shard1 || Shard.default).activate do
|
|
@sender = user_factory
|
|
@recipient = user_factory
|
|
end
|
|
end
|
|
it "should increment when adding messages" do
|
|
Conversation.initiate([@sender, @recipient], false).add_message(@sender, 'test')
|
|
expect(@sender.conversations.first.message_count).to eql 1
|
|
expect(@recipient.conversations.first.message_count).to eql 1
|
|
end
|
|
|
|
it "should decrement when removing messages" do
|
|
root_convo = Conversation.initiate([@sender, @recipient], false)
|
|
root_convo.add_message(@sender, 'test')
|
|
msg = root_convo.add_message(@sender, 'test2')
|
|
expect(@sender.conversations.first.message_count).to eql 2
|
|
expect(@recipient.conversations.first.message_count).to eql 2
|
|
|
|
@sender.conversations.first.remove_messages(msg)
|
|
expect(@sender.conversations.first.reload.message_count).to eql 1
|
|
expect(@recipient.conversations.first.reload.message_count).to eql 2
|
|
end
|
|
|
|
it "should decrement when deleting messages" do
|
|
root_convo = Conversation.initiate([@sender, @recipient], false)
|
|
root_convo.add_message(@sender, 'test')
|
|
msg = root_convo.add_message(@sender, 'test2')
|
|
expect(@sender.conversations.first.message_count).to eql 2
|
|
expect(@recipient.conversations.first.message_count).to eql 2
|
|
|
|
@sender.conversations.first.delete_messages(msg)
|
|
expect(@sender.conversations.first.reload.message_count).to eql 1
|
|
expect(@recipient.conversations.first.reload.message_count).to eql 2
|
|
end
|
|
end
|
|
|
|
include_examples "message counts"
|
|
|
|
context "sharding" do
|
|
specs_require_sharding
|
|
include_examples "message counts"
|
|
end
|
|
end
|
|
|
|
context "unread counts" do
|
|
shared_examples_for "unread counts" do
|
|
before :once do
|
|
(@shard1 || Shard.default).activate do
|
|
@sender = user_factory
|
|
@unread_guy = @recipient = user_factory
|
|
@subscribed_guy = user_factory
|
|
@unsubscribed_guy = user_factory
|
|
end
|
|
end
|
|
|
|
it "should increment for recipients when sending the first message in a conversation" do
|
|
root_convo = Conversation.initiate([@sender, @recipient], false)
|
|
expect(ConversationParticipant.unread.size).to eql 0 # only once the first message is added
|
|
root_convo.add_message(@sender, 'test')
|
|
expect(@sender.reload.unread_conversations_count).to eql 0
|
|
expect(@sender.conversations.unread.size).to eql 0
|
|
expect(@recipient.reload.unread_conversations_count).to eql 1
|
|
expect(@recipient.conversations.unread.size).to eql 1
|
|
end
|
|
|
|
it "should increment for subscribed recipients when adding a message to a read conversation" do
|
|
root_convo = Conversation.initiate([@sender, @unread_guy, @subscribed_guy, @unsubscribed_guy], false)
|
|
root_convo.add_message(@sender, 'test')
|
|
|
|
expect(@unread_guy.reload.unread_conversations_count).to eql 1
|
|
expect(@unread_guy.conversations.unread.size).to eql 1
|
|
@subscribed_guy.conversations.first.update_attribute(:workflow_state, "read")
|
|
expect(@subscribed_guy.reload.unread_conversations_count).to eql 0
|
|
expect(@subscribed_guy.conversations.unread.size).to eql 0
|
|
@unsubscribed_guy.conversations.first.update_attributes(:subscribed => false)
|
|
expect(@unsubscribed_guy.reload.unread_conversations_count).to eql 0
|
|
expect(@unsubscribed_guy.conversations.unread.size).to eql 0
|
|
|
|
root_convo.add_message(@sender, 'test2')
|
|
|
|
expect(@unread_guy.reload.unread_conversations_count).to eql 1
|
|
expect(@unread_guy.conversations.unread.size).to eql 1
|
|
expect(@subscribed_guy.reload.unread_conversations_count).to eql 1
|
|
expect(@subscribed_guy.conversations.unread.size).to eql 1
|
|
expect(@unsubscribed_guy.reload.unread_conversations_count).to eql 0
|
|
expect(@unsubscribed_guy.conversations.unread.size).to eql 0
|
|
end
|
|
|
|
it "should increment only for message participants" do
|
|
root_convo = Conversation.initiate([@sender, @recipient, @subscribed_guy], false)
|
|
root_convo.add_message(@sender, 'test')
|
|
|
|
@subscribed_guy.conversations.first.update_attribute(:workflow_state, "read")
|
|
expect(@subscribed_guy.reload.unread_conversations_count).to eql 0
|
|
expect(@subscribed_guy.conversations.unread.size).to eql 0
|
|
|
|
root_convo.add_message(@sender, 'test2', :only_users => [@recipient])
|
|
|
|
expect(@subscribed_guy.reload.unread_conversations_count).to eql 0
|
|
expect(@subscribed_guy.conversations.unread.size).to eql 0
|
|
end
|
|
|
|
it "should decrement when deleting an unread conversation" do
|
|
root_convo = Conversation.initiate([@sender, @unread_guy], false)
|
|
root_convo.add_message(@sender, 'test')
|
|
|
|
expect(@unread_guy.reload.unread_conversations_count).to eql 1
|
|
expect(@unread_guy.conversations.unread.size).to eql 1
|
|
@unread_guy.conversations.first.remove_messages(:all)
|
|
expect(@unread_guy.reload.unread_conversations_count).to eql 0
|
|
expect(@unread_guy.conversations.unread.size).to eql 0
|
|
end
|
|
|
|
it "should decrement when marking as read" do
|
|
root_convo = Conversation.initiate([@sender, @unread_guy], false)
|
|
root_convo.add_message(@sender, 'test')
|
|
|
|
expect(@unread_guy.reload.unread_conversations_count).to eql 1
|
|
expect(@unread_guy.conversations.unread.size).to eql 1
|
|
@unread_guy.conversations.first.update_attribute(:workflow_state, "read")
|
|
expect(@unread_guy.reload.unread_conversations_count).to eql 0
|
|
expect(@unread_guy.conversations.unread.size).to eql 0
|
|
end
|
|
|
|
it "should indecrement when marking as unread" do
|
|
root_convo = Conversation.initiate([@sender, @unread_guy], false)
|
|
root_convo.add_message(@sender, 'test')
|
|
@unread_guy.conversations.first.update_attribute(:workflow_state, "read")
|
|
|
|
expect(@unread_guy.reload.unread_conversations_count).to eql 0
|
|
expect(@unread_guy.conversations.unread.size).to eql 0
|
|
@unread_guy.conversations.first.update_attribute(:workflow_state, "unread")
|
|
expect(@unread_guy.reload.unread_conversations_count).to eql 1
|
|
expect(@unread_guy.conversations.unread.size).to eql 1
|
|
end
|
|
end
|
|
|
|
include_examples "unread counts"
|
|
context "sharding" do
|
|
specs_require_sharding
|
|
include_examples "unread counts"
|
|
end
|
|
end
|
|
|
|
context "subscription" do
|
|
it "should mark-as-read when unsubscribing iff it was unread" do
|
|
subscription_guy = user_factory
|
|
archive_guy = user_factory
|
|
root_convo = Conversation.initiate([sender, archive_guy, subscription_guy], false)
|
|
root_convo.add_message(sender, 'test')
|
|
|
|
expect(subscription_guy.reload.unread_conversations_count).to eql 1
|
|
expect(subscription_guy.conversations.unread.size).to eql 1
|
|
|
|
subscription_guy.conversations.first.update_attributes(:subscribed => false)
|
|
expect(subscription_guy.reload.unread_conversations_count).to eql 0
|
|
expect(subscription_guy.conversations.unread.size).to eql 0
|
|
|
|
archive_guy.conversations.first.update_attributes(:workflow_state => "archived", :subscribed => false)
|
|
expect(archive_guy.conversations.archived.size).to eql 1
|
|
end
|
|
|
|
it "should mark-as-unread when re-subscribing iff there are newer messages" do
|
|
flip_flopper_guy = user_factory
|
|
subscription_guy = user_factory
|
|
archive_guy = user_factory
|
|
root_convo = Conversation.initiate([sender, flip_flopper_guy, archive_guy, subscription_guy], false)
|
|
root_convo.add_message(sender, 'test')
|
|
|
|
flip_flopper_guy.conversations.first.update_attributes(:subscribed => false)
|
|
expect(flip_flopper_guy.reload.unread_conversations_count).to eql 0
|
|
expect(flip_flopper_guy.conversations.unread.size).to eql 0
|
|
# no new messages in the interim, he should stay "marked-as-read"
|
|
flip_flopper_guy.conversations.first.update_attributes(:subscribed => true)
|
|
expect(flip_flopper_guy.reload.unread_conversations_count).to eql 0
|
|
expect(flip_flopper_guy.conversations.unread.size).to eql 0
|
|
|
|
subscription_guy.conversations.first.update_attributes(:subscribed => false)
|
|
archive_guy.conversations.first.update_attributes(:workflow_state => "archived", :subscribed => false)
|
|
|
|
message = root_convo.add_message(sender, 'you wish you were subscribed!')
|
|
message.update_attribute(:created_at, Time.now.utc + 1.minute)
|
|
last_message_at = message.reload.created_at
|
|
|
|
subscription_guy.conversations.first.update_attributes(:subscribed => true)
|
|
archive_guy.conversations.first.update_attributes(:subscribed => true)
|
|
|
|
expect(subscription_guy.reload.unread_conversations_count).to eql 1
|
|
expect(subscription_guy.conversations.unread.size).to eql 1
|
|
expect(subscription_guy.conversations.first.last_message_at.to_i).to eql last_message_at.to_i
|
|
|
|
expect(archive_guy.reload.unread_conversations_count).to eql 1
|
|
expect(archive_guy.conversations.unread.size).to eql 1
|
|
expect(subscription_guy.conversations.first.last_message_at.to_i).to eql last_message_at.to_i
|
|
end
|
|
|
|
it "should not toggle read/unread until the subscription change is saved" do
|
|
subscription_guy = user_factory
|
|
root_convo = Conversation.initiate([sender, user_factory, subscription_guy], false)
|
|
root_convo.add_message(sender, 'test')
|
|
|
|
expect(subscription_guy.reload.unread_conversations_count).to eql 1
|
|
expect(subscription_guy.conversations.unread.size).to eql 1
|
|
|
|
subscription_guy.conversations.first.subscribed = false
|
|
expect(subscription_guy.reload.unread_conversations_count).to eql 1
|
|
expect(subscription_guy.conversations.unread.size).to eql 1
|
|
|
|
subscription_guy.conversations.first.subscribed = true
|
|
expect(subscription_guy.reload.unread_conversations_count).to eql 1
|
|
expect(subscription_guy.conversations.unread.size).to eql 1
|
|
end
|
|
end
|
|
|
|
context "adding messages" do
|
|
it "should deliver the message to all participants" do
|
|
recipients = create_users(5, return_type: :record)
|
|
Conversation.initiate([sender] + recipients, false).add_message(sender, 'test')
|
|
convo = sender.conversations.first
|
|
expect(convo.reload.read?).to be_truthy # only for the sender, and then only on the first message
|
|
expect(convo.messages.size).to eq 1
|
|
expect(convo.messages.first.body).to eq 'test'
|
|
recipients.each do |recipient|
|
|
convo = recipient.conversations.first
|
|
expect(convo.read?).to be_falsey
|
|
expect(convo.messages.size).to eq 1
|
|
expect(convo.messages.first.body).to eq 'test'
|
|
end
|
|
end
|
|
|
|
it "should broadcast conversation created", priority: "1", test_id: 193163 do
|
|
|
|
n2 = Notification.create(:name => "Conversation Created", :category => "TestImmediately")
|
|
|
|
[sender].each do |user|
|
|
channel = user.communication_channels.create(:path => "test_channel_email_#{user.id}", :path_type => "email")
|
|
channel.confirm
|
|
|
|
NotificationPolicy.create(:notification => n2, :communication_channel => channel, :frequency => "immediately")
|
|
end
|
|
|
|
recipients = create_users(5, return_type: :record)
|
|
conversation = Conversation.initiate(recipients, false).add_message(sender, 'test', :cc_author => true);
|
|
|
|
# check that our sender recieved a conversation created notification
|
|
expect(conversation.messages_sent).to include("Conversation Created")
|
|
end
|
|
|
|
it "should only ever change the workflow_state for the sender if it's archived and it's a direct message (not bulk)" do
|
|
Conversation.initiate([sender, recipient], true).add_message(sender, 'test')
|
|
convo = sender.conversations.first
|
|
convo.update_attribute(:workflow_state, "unread")
|
|
convo.add_message('another test', :update_for_sender => false) # as if it were a bulk private message
|
|
expect(convo.reload.unread?).to be_truthy
|
|
|
|
convo.update_attribute(:workflow_state, "archived")
|
|
convo.add_message('one more test', :update_for_sender => false)
|
|
expect(convo.reload.archived?).to be_truthy
|
|
|
|
convo.update_attribute(:workflow_state, "unread")
|
|
convo.add_message('and another test') # overrides subscribed-ness and updates timestamps
|
|
expect(convo.reload.unread?).to be_truthy
|
|
|
|
convo.update_attribute(:workflow_state, "archived")
|
|
convo.add_message('last one')
|
|
expect(convo.reload.archived?).to be_falsey
|
|
expect(convo.reload.read?).to be_truthy
|
|
end
|
|
|
|
it "should not set last_message_at for the sender if the conversation is deleted and update_for_sender=false" do
|
|
rconvo = Conversation.initiate([sender, recipient], true)
|
|
message = rconvo.add_message(sender, 'test')
|
|
convo = sender.conversations.first
|
|
expect(convo.last_message_at).not_to be_nil
|
|
|
|
convo.remove_messages(message)
|
|
expect(convo.last_message_at).to be_nil
|
|
|
|
convo.add_message('bulk message', :update_for_sender => false)
|
|
convo.reload
|
|
expect(convo.last_message_at).to be_nil
|
|
end
|
|
|
|
it "should set last_authored_at and visible_last_authored_at on deleted conversations even if update_for_sender=false" do
|
|
expected_times = [Time.now.utc - 1.hours, Time.now.utc].map{ |t| Time.parse(t.to_s) }
|
|
ConversationMessage.any_instance.expects(:current_time_from_proper_timezone).twice.returns(*expected_times)
|
|
|
|
rconvo = Conversation.initiate([sender, recipient], true)
|
|
message = rconvo.add_message(sender, 'test')
|
|
convo = sender.conversations.first
|
|
expect(convo.last_authored_at).to eql expected_times.first
|
|
expect(convo.visible_last_authored_at).to eql expected_times.first
|
|
|
|
convo.remove_messages(message)
|
|
expect(convo.last_authored_at).to eql expected_times.first
|
|
expect(convo.visible_last_authored_at).to be_nil
|
|
|
|
convo.add_message('bulk message', :update_for_sender => false)
|
|
convo.reload
|
|
expect(convo.last_authored_at).to eql expected_times.last
|
|
expect(convo.visible_last_authored_at).to eql expected_times.last
|
|
end
|
|
|
|
it "should deliver the message to unsubscribed participants but not alert them" do
|
|
recipients = create_users(5, return_type: :record)
|
|
Conversation.initiate([sender] + recipients, false).add_message(sender, 'test')
|
|
|
|
recipient = recipients.last
|
|
rconvo = recipient.conversations.first
|
|
expect(rconvo.unread?).to be_truthy
|
|
rconvo.update_attributes(:subscribed => false)
|
|
expect(rconvo.unread?).to be_falsey
|
|
|
|
convo = sender.conversations.first
|
|
message = convo.add_message('another test')
|
|
message.update_attribute(:created_at, Time.now.utc + 1.minute)
|
|
|
|
expect(rconvo.reload.unread?).to be_falsey
|
|
rconvo.update_attributes(:subscribed => true)
|
|
expect(rconvo.unread?).to be_truthy
|
|
end
|
|
|
|
it "should only alert message participants" do
|
|
recipients = create_users(5, return_type: :record)
|
|
convo = Conversation.initiate([sender] + recipients, false)
|
|
convo.add_message(sender, 'test')
|
|
|
|
recipient = recipients.last
|
|
rconvo = recipient.conversations.first
|
|
expect(rconvo.unread?).to be_truthy
|
|
rconvo.update_attribute(:workflow_state, "read")
|
|
|
|
convo.add_message(sender, 'another test', :only_users => [recipients.first])
|
|
|
|
expect(rconvo.reload.unread?).to be_falsey
|
|
end
|
|
end
|
|
|
|
context "context tags" do
|
|
context "current_context_strings" do
|
|
it "should not double-count duplicate enrollments" do
|
|
u1 = student_in_course(:active_all => true).user
|
|
u2 = student_in_course(:active_all => true).user
|
|
course1 = @course
|
|
|
|
course_with_student(:active_all => true, :user => u1)
|
|
course2 = @course
|
|
other_section = course2.course_sections.create
|
|
course2.enroll_student(u1, :allow_multiple_enrollments => true, :section => other_section)
|
|
expect(u1.enrollments.size).to eql 3
|
|
|
|
conversation = Conversation.initiate([u1, u2], true)
|
|
|
|
expect(conversation.current_context_strings(1)).to eql [course1.asset_string]
|
|
expect(u1.conversation_context_codes.sort).to eql [course1.asset_string, course2.asset_string].sort # just once
|
|
end
|
|
end
|
|
|
|
context "initial tags" do
|
|
it "should save all valid tags on the conversation" do # NOTE: this will change if/when we allow arbitrary tags
|
|
u1 = student_in_course(:active_all => true).user
|
|
u2 = student_in_course(:active_all => true, :course => @course).user
|
|
conversation = Conversation.initiate([u1, u2], true)
|
|
conversation.add_message(u1, 'test', :tags => [@course.asset_string, "asdf", "lol"])
|
|
expect(conversation.tags).to eql [@course.asset_string]
|
|
end
|
|
|
|
it "should set initial empty tags on the conversation and conversation_participant" do
|
|
u1 = student_in_course.user
|
|
u2 = student_in_course(:course => @course).user
|
|
conversation = Conversation.initiate([u1, u2], true)
|
|
expect(conversation.read_attribute(:tags)).not_to be_nil
|
|
expect(conversation.tags).to eql []
|
|
expect(u1.all_conversations.first.read_attribute(:tags)).not_to be_nil
|
|
expect(u1.all_conversations.first.tags).to eql []
|
|
expect(u2.all_conversations.first.read_attribute(:tags)).not_to be_nil
|
|
expect(u2.all_conversations.first.tags).to eql []
|
|
end
|
|
|
|
it "should ignore explicit context tags not shared by at least two participants" do
|
|
u1 = student_in_course(:active_all => true).user
|
|
u2 = student_in_course(:active_all => true, :course => @course).user
|
|
u3 = user_factory
|
|
@course1 = @course
|
|
@course2 = course_factory(active_all: true)
|
|
@course2.enroll_student(u1).update_attribute(:workflow_state, 'active')
|
|
conversation = Conversation.initiate([u1, u2, u3], false)
|
|
conversation.add_message(u1, 'test', :tags => [@course1.asset_string, @course2.asset_string])
|
|
expect(conversation.tags).to eql [@course1.asset_string]
|
|
end
|
|
|
|
it "should save all visible tags on the conversation_participant" do
|
|
u1 = student_in_course(:active_all => true).user
|
|
u2 = student_in_course(:active_all => true, :course => @course).user
|
|
u3 = user_factory
|
|
conversation = Conversation.initiate([u1, u2, u3], false)
|
|
conversation.add_message(u1, 'test', :tags => [@course.asset_string])
|
|
expect(conversation.tags).to eql [@course.asset_string]
|
|
expect(u1.conversations.first.tags).to eql [@course.asset_string]
|
|
expect(u2.conversations.first.tags).to eql [@course.asset_string]
|
|
expect(u3.conversations.first.tags).to eql []
|
|
end
|
|
|
|
it "should default all tags to common ones over the 50% threshold if none are specified" do
|
|
u1 = student_in_course(:active_all => true).user
|
|
u2 = student_in_course(:active_all => true, :course => @course).user
|
|
@course1 = @course
|
|
@course2 = course_factory(active_all: true)
|
|
@course2.enroll_student(u2).update_attribute(:workflow_state, 'active')
|
|
u3 = student_in_course(:active_all => true, :course => @course2).user
|
|
conversation = Conversation.initiate([u1, u2, u3], false)
|
|
conversation.add_message(u1, 'test')
|
|
expect(conversation.tags.sort).to eql [@course1.asset_string, @course2.asset_string].sort
|
|
expect(u1.conversations.first.tags).to eql [@course1.asset_string]
|
|
expect(u2.conversations.first.tags.sort).to eql [@course1.asset_string, @course2.asset_string].sort
|
|
expect(u3.conversations.first.tags).to eql [@course2.asset_string]
|
|
end
|
|
|
|
it "should default the conversation_participant tags to common ones over the 50% threshold if no specified tags match" do
|
|
u1 = student_in_course(:active_all => true).user
|
|
u2 = student_in_course(:active_all => true, :course => @course).user
|
|
@course1 = @course
|
|
@course2 = course_factory(active_all: true)
|
|
@course2.enroll_student(u2).update_attribute(:workflow_state, 'active')
|
|
u3 = student_in_course(:active_all => true, :course => @course2).user
|
|
conversation = Conversation.initiate([u1, u2, u3], false)
|
|
conversation.add_message(u1, 'test', :tags => [@course1.asset_string])
|
|
expect(conversation.tags).to eql [@course1.asset_string]
|
|
expect(u1.conversations.first.tags).to eql [@course1.asset_string]
|
|
expect(u2.conversations.first.tags).to eql [@course1.asset_string] # just the one, since it was explicit
|
|
expect(u3.conversations.first.tags).to eql [@course2.asset_string] # not in course1, so fall back to common ones (i.e. course2)
|
|
end
|
|
|
|
context "sharding" do
|
|
specs_require_sharding
|
|
|
|
it "should set all tags on the other shard's participants" do
|
|
course1 = @shard1.activate{ course_factory(:account => Account.create!, :active_all => true) }
|
|
course2 = @shard2.activate{ course_factory(:account => Account.create!, :active_all => true) }
|
|
user1 = student_in_course(:course => course1, :active_all => true).user
|
|
user2 = student_in_course(:course => course2, :active_all => true).user
|
|
student_in_course(:course => course2, :user => user1, :active_all => true)
|
|
student_in_course(:course => course1, :user => user2, :active_all => true)
|
|
conversation = Conversation.initiate([user1, user2], false)
|
|
conversation.add_message(user1, 'test')
|
|
expect(user1.conversations.first.tags.sort).to eql [course1.asset_string, course2.asset_string].sort
|
|
expect(user2.conversations.first.tags.sort).to eql [course1.asset_string, course2.asset_string].sort
|
|
end
|
|
end
|
|
end
|
|
|
|
context "deletion" do
|
|
it "should remove tags when all messages are deleted" do
|
|
u1 = student_in_course(:active_all => true).user
|
|
u2 = student_in_course(:active_all => true, :course => @course).user
|
|
conversation = Conversation.initiate([u1, u2], true)
|
|
conversation.add_message(u1, 'test')
|
|
expect(conversation.tags).to eql [@course.asset_string]
|
|
cp1 = u1.conversations.first
|
|
expect(cp1.tags).to eql [@course.asset_string]
|
|
cp2 = u2.conversations.first
|
|
expect(cp2.tags).to eql [@course.asset_string]
|
|
|
|
cp2.remove_messages(:all)
|
|
expect(cp2.tags).to eql []
|
|
|
|
# no change here
|
|
expect(cp1.reload.tags).to eql [@course.asset_string]
|
|
expect(conversation.reload.tags).to eql [@course.asset_string]
|
|
end
|
|
end
|
|
|
|
context "subsequent tags" do
|
|
let_once(:course1) { @course1 = course_factory(active_all: true) }
|
|
let_once(:course2) { @course2 = course_factory(active_all: true) }
|
|
let_once(:u1) { student_in_course(active_all: true, course: course1).user }
|
|
let_once(:u2) { student_in_course(active_all: true, course: course1).user }
|
|
let_once(:u3) { student_in_course(active_all: true, course: course2).user }
|
|
let_once(:conversation) do
|
|
@course2.enroll_student(u2).update_attribute(:workflow_state, 'active')
|
|
conversation = Conversation.initiate([u1, u2, u3], false)
|
|
conversation.add_message(u1, 'test', :tags => [@course1.asset_string])
|
|
conversation
|
|
end
|
|
|
|
it "should add new tags to the conversation" do
|
|
expect(conversation.tags).to eql [@course1.asset_string]
|
|
|
|
conversation.add_message(u1, 'another', :tags => [@course2.asset_string])
|
|
expect(conversation.tags.sort).to eql [@course1.asset_string, @course2.asset_string].sort
|
|
end
|
|
|
|
it "should add new visible tags to the conversation_participant" do
|
|
expect(u1.conversations.first.tags).to eq [@course1.asset_string]
|
|
expect(u2.conversations.first.tags).to eq [@course1.asset_string]
|
|
expect(u3.conversations.first.tags).to eq [@course2.asset_string]
|
|
|
|
conversation.add_message(u1, 'another', :tags => [@course2.asset_string, "course_0"])
|
|
expect(u1.conversations.first.tags).to eq [@course1.asset_string]
|
|
expect(u2.conversations.first.tags.sort).to eq [@course1.asset_string, @course2.asset_string].sort
|
|
expect(u3.conversations.first.tags).to eq [@course2.asset_string]
|
|
end
|
|
|
|
it "should ignore conversation_participants without a valid user" do
|
|
expect(u1.conversations.first.tags).to eq [@course1.asset_string]
|
|
expect(u2.conversations.first.tags).to eq [@course1.asset_string]
|
|
expect(u3.conversations.first.tags).to eq [@course2.asset_string]
|
|
broken_one = u3.conversations.first
|
|
ConversationParticipant.where(id: broken_one).update_all(user_id: -1, tags: '')
|
|
|
|
conversation.reload
|
|
conversation.add_message(u1, 'another', :tags => [@course2.asset_string, "course_0"])
|
|
expect(u1.conversations.first.tags).to eq [@course1.asset_string]
|
|
expect(u2.conversations.first.tags.sort).to eq [@course1.asset_string]
|
|
expect(broken_one.reload.tags).to eq []
|
|
end
|
|
end
|
|
|
|
context "private conversations" do
|
|
let_once(:course1) { @course1 = course_factory(active_all: true) }
|
|
let_once(:course2) { @course2 = course_factory(active_all: true) }
|
|
let_once(:u1) { student_in_course(active_all: true, course: course1).user }
|
|
let_once(:u2) { student_in_course(active_all: true, course: course1).user }
|
|
|
|
it "should save new visible tags on the conversation_message_participant" do
|
|
@course2.enroll_student(u1).update_attribute(:workflow_state, 'active')
|
|
@course2.enroll_student(u2).update_attribute(:workflow_state, 'active')
|
|
conversation = Conversation.initiate([u1, u2], true)
|
|
conversation.add_message(u1, 'test', :tags => [@course1.asset_string])
|
|
cp = u2.conversations.first
|
|
expect(cp.messages.human.first.tags).to eql [@course1.asset_string]
|
|
|
|
conversation.add_message(u1, 'another', :tags => [@course2.asset_string, "course_0"])
|
|
expect(cp.messages.human.first.tags).to eql [@course2.asset_string]
|
|
end
|
|
|
|
it "should save the previous message tags on the conversation_message_participant if there are no new visible ones" do
|
|
conversation = Conversation.initiate([u1, u2], true)
|
|
conversation.add_message(u1, 'test', :tags => [@course1.asset_string])
|
|
cp = u2.conversations.first
|
|
expect(cp.messages.human.first.tags).to eql [@course1.asset_string]
|
|
|
|
conversation.add_message(u1, 'another', :tags => ["course_0"])
|
|
expect(cp.messages.human.first.tags).to eql [@course1.asset_string]
|
|
end
|
|
|
|
it "should recompute the conversation_participant's tags when removing messages" do
|
|
@course2.enroll_student(u1).update_attribute(:workflow_state, 'active')
|
|
@course2.enroll_student(u2).update_attribute(:workflow_state, 'active')
|
|
conversation = Conversation.initiate([u1, u2], true)
|
|
conversation.add_message(u1, 'test', :tags => [@course1.asset_string])
|
|
cp = u2.conversations.first
|
|
expect(cp.tags).to eql [@course1.asset_string]
|
|
expect(cp.messages.human.first.tags).to eql [@course1.asset_string]
|
|
|
|
conversation.add_message(u1, 'another', :tags => [@course2.asset_string])
|
|
expect(cp.reload.tags.sort).to eql [@course1.asset_string, @course2.asset_string].sort
|
|
expect(cp.messages.human.first.tags).to eql [@course2.asset_string]
|
|
|
|
cp.remove_messages(cp.messages.human.first)
|
|
expect(cp.reload.tags).to eql [@course1.asset_string]
|
|
end
|
|
end
|
|
|
|
context "group conversations" do
|
|
let_once(:course1) { @course1 = course_factory(active_all: true) }
|
|
let_once(:course2) { @course2 = course_factory(active_all: true) }
|
|
let_once(:u1) { student_in_course(active_all: true, course: course1).user }
|
|
let_once(:u2) { student_in_course(active_all: true, course: course1).user }
|
|
|
|
it "should not save tags on the conversation_message_participant" do
|
|
u3 = student_in_course(:active_all => true, :course => @course1).user
|
|
conversation = Conversation.initiate([u1, u2, u3], false)
|
|
conversation.add_message(u1, 'test', :tags => [@course1.asset_string])
|
|
expect(u1.conversations.first.messages.human.first.tags).to eql []
|
|
expect(u2.conversations.first.messages.human.first.tags).to eql []
|
|
expect(u3.conversations.first.messages.human.first.tags).to eql []
|
|
end
|
|
|
|
it "should not recompute the conversation_participant's tags when removing messages" do
|
|
@course2.enroll_student(u2).update_attribute(:workflow_state, 'active')
|
|
u3 = student_in_course(:active_all => true, :course => @course2).user
|
|
conversation = Conversation.initiate([u1, u2, u3], false)
|
|
conversation.add_message(u1, 'test', :tags => [@course1.asset_string])
|
|
cp = u2.conversations.first
|
|
expect(cp.tags).to eql [@course1.asset_string]
|
|
|
|
conversation.add_message(u1, 'another', :tags => [@course2.asset_string])
|
|
expect(cp.reload.tags.sort).to eql [@course1.asset_string, @course2.asset_string].sort
|
|
|
|
cp.remove_messages(cp.messages.human.first)
|
|
expect(cp.reload.tags.sort).to eql [@course1.asset_string, @course2.asset_string].sort
|
|
end
|
|
|
|
it "should add tags specified along with new recipients" do
|
|
@course2.enroll_student(u2).update_attribute(:workflow_state, 'active')
|
|
u3 = student_in_course(:active_all => true, :course => @course2).user
|
|
u4 = student_in_course(:active_all => true, :course => @course2).user
|
|
conversation = Conversation.initiate([u1, u2, u3], false)
|
|
conversation.add_message(u1, 'test', :tags => [@course1.asset_string])
|
|
expect(conversation.tags).to eql [@course1.asset_string]
|
|
expect(u1.conversations.first.tags).to eql [@course1.asset_string]
|
|
expect(u2.conversations.first.tags).to eql [@course1.asset_string]
|
|
expect(u3.conversations.first.tags).to eql [@course2.asset_string]
|
|
|
|
conversation.add_participants(u2, [u4], :tags => [@course2.asset_string])
|
|
expect(conversation.reload.tags.sort).to eql [@course1.asset_string, @course2.asset_string].sort
|
|
expect(u1.conversations.first.tags).to eql [@course1.asset_string]
|
|
expect(u2.conversations.first.tags.sort).to eql [@course1.asset_string, @course2.asset_string].sort
|
|
expect(u3.conversations.first.tags).to eql [@course2.asset_string]
|
|
expect(u4.conversations.first.tags).to eql [@course2.asset_string]
|
|
end
|
|
end
|
|
|
|
context "migration" do
|
|
before :once do
|
|
@u1 = student_in_course(:active_all => true).user
|
|
@u2 = student_in_course(:active_all => true, :course => @course).user
|
|
@course1 = @course
|
|
@course2 = course_factory(active_all: true)
|
|
@course2.enroll_student(@u2).update_attribute(:workflow_state, 'active')
|
|
@u3 = student_in_course(:active_all => true, :course => @course2).user
|
|
@conversation = Conversation.initiate([@u1, @u2, @u3], false)
|
|
@conversation.add_message(@u1, 'test', :tags => [@course1.asset_string])
|
|
Conversation.update_all "tags = NULL"
|
|
ConversationParticipant.update_all "tags = NULL"
|
|
ConversationMessageParticipant.update_all "tags = NULL"
|
|
|
|
@conversation = Conversation.find(@conversation.id)
|
|
end
|
|
|
|
it "should set the default tags when migrating" do
|
|
@conversation.migrate_context_tags!
|
|
|
|
expect(@conversation.tags.sort).to eql [@course1.asset_string, @course2.asset_string].sort
|
|
expect(@u1.conversations.first.tags).to eql [@course1.asset_string]
|
|
expect(@u2.conversations.first.tags.sort).to eql [@course1.asset_string, @course2.asset_string].sort
|
|
expect(@u3.conversations.first.tags).to eql [@course2.asset_string]
|
|
end
|
|
|
|
it "should ignore conversation_participants without a user" do
|
|
broken_one = @u3.conversations.first
|
|
ConversationParticipant.where(id: broken_one).update_all(user_id: 0)
|
|
|
|
@conversation.migrate_context_tags!
|
|
|
|
expect(@conversation.tags).to eql [@course1.asset_string] # no course2 since participant is broken
|
|
expect(@u1.conversations.first.tags).to eql [@course1.asset_string]
|
|
expect(@u2.conversations.first.tags).to eql [@course1.asset_string]
|
|
expect(broken_one.reload.tags).to eql [] # skipped
|
|
end
|
|
end
|
|
|
|
context 'tag updates' do
|
|
before :once do
|
|
@teacher = teacher_in_course(:active_all => true).user
|
|
@student = student_in_course(:active_all => true, :course => @course).user
|
|
@old_course = @course
|
|
end
|
|
|
|
let_once(:student1) { student_in_course(:active_all => true, :course => @old_course).user }
|
|
let_once(:student2) { student_in_course(:active_all => true, :course => @old_course).user }
|
|
|
|
it "should remove old tags and add new ones" do
|
|
conversation = Conversation.initiate([@teacher, @student], true)
|
|
conversation.add_message(@teacher, 'first message')
|
|
|
|
new_course = course_factory
|
|
new_course.offer!
|
|
new_course.enroll_teacher(@teacher).accept!
|
|
new_course.enroll_student(@student).accept!
|
|
|
|
@old_course.complete!
|
|
|
|
third_course = course_factory
|
|
third_course.offer!
|
|
third_course.enroll_teacher(@teacher).accept!
|
|
|
|
conversation.reload
|
|
conversation.add_message(@student, 'second message')
|
|
|
|
conversation.conversation_participants.each do |participant|
|
|
participant.reload
|
|
expect(participant.tags).to eq [new_course.asset_string]
|
|
end
|
|
end
|
|
|
|
it "should continue to use old tags if there are no current shared contexts" do
|
|
conversation = Conversation.initiate([@teacher, @student], true)
|
|
conversation.add_message(@teacher, 'first message')
|
|
|
|
@old_course.complete!
|
|
|
|
teacher_course = course_factory
|
|
teacher_course.offer!
|
|
teacher_course.enroll_teacher(@teacher).accept!
|
|
|
|
student_course = course_factory
|
|
student_course.offer!
|
|
student_course.enroll_student(@student).accept!
|
|
|
|
conversation.add_message(@student, 'second message')
|
|
|
|
conversation.conversation_participants.each do |participant|
|
|
participant.reload
|
|
expect(participant.tags).to eq [@old_course.asset_string]
|
|
end
|
|
end
|
|
|
|
it "should use concluded tags from multiple courses" do
|
|
old_course2 = course_factory
|
|
|
|
old_course2.offer!
|
|
old_course2.enroll_teacher(@teacher).accept!
|
|
old_course2.enroll_student(@student).accept!
|
|
|
|
conversation = Conversation.initiate([@teacher, @student], true)
|
|
conversation.add_message(@teacher, 'first message')
|
|
|
|
[@old_course, old_course2].each { |c| c.complete! }
|
|
|
|
teacher_course = course_factory
|
|
teacher_course.offer!
|
|
teacher_course.enroll_teacher(@teacher).accept!
|
|
|
|
student_course = course_factory
|
|
student_course.offer!
|
|
student_course.enroll_student(@student).accept!
|
|
|
|
conversation.add_message(@teacher, 'second message')
|
|
|
|
conversation.conversation_participants.each do |participant|
|
|
participant.reload
|
|
expect(participant.tags.sort).to eq [@old_course, old_course2].map(&:asset_string).sort
|
|
end
|
|
end
|
|
|
|
it "should include concluded group contexts when no active ones exist" do
|
|
group = Group.create!(:context => @old_course)
|
|
[student1, student2].each { |s| group.users << s }
|
|
|
|
conversation = Conversation.initiate([student1, student2], true)
|
|
conversation.add_message(student1, 'first message')
|
|
|
|
@old_course.complete!
|
|
|
|
conversation.add_message(student2, 'second message')
|
|
|
|
conversation.conversation_participants.each do |participant|
|
|
participant.reload
|
|
expect(participant.tags).to include(group.asset_string)
|
|
end
|
|
end
|
|
|
|
it "should replace concluded group contexts with active ones" do
|
|
old_group = Group.create!(:context => @old_course)
|
|
[student1, student2].each { |s| old_group.users << s }
|
|
|
|
conversation = Conversation.initiate([student1, student2], true)
|
|
conversation.add_message(student1, 'first message')
|
|
|
|
@old_course.complete!
|
|
old_group.destroy
|
|
|
|
new_course = course_factory
|
|
new_course.offer!
|
|
[student1, student2].each { |s| new_course.enroll_student(s).accept! }
|
|
new_group = Group.create!(:context => new_course)
|
|
new_group.users << student1
|
|
new_group.users << student2
|
|
|
|
conversation.reload
|
|
conversation.add_message(student2, 'second message')
|
|
|
|
conversation.conversation_participants.each do |participant|
|
|
participant.reload
|
|
expect(participant.tags.sort).to eq [new_group, new_course].map(&:asset_string).sort
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context "root_account_ids" do
|
|
it "should always be ordered" do
|
|
conversation = Conversation.create
|
|
conversation.update_attribute :root_account_ids, [3, 2, 1]
|
|
expect(conversation.root_account_ids).to eql [1, 2, 3]
|
|
end
|
|
|
|
it "should be saved on the conversation when adding a message" do
|
|
u1 = user_factory
|
|
u2 = user_factory
|
|
conversation = Conversation.initiate([u1, u2], true)
|
|
conversation.add_message(u1, 'ohai', :root_account_id => 1)
|
|
conversation.add_message(u2, 'ohai yourself', :root_account_id => 2)
|
|
expect(conversation.root_account_ids).to eql [1, 2]
|
|
end
|
|
|
|
it "includes the context's root account when initiating" do
|
|
new_course = course_factory
|
|
conversation = Conversation.initiate([], false, context_type: 'Course', context_id: new_course.id)
|
|
expect(conversation.root_account_ids).to eql [new_course.root_account_id]
|
|
end
|
|
|
|
context "sharding" do
|
|
specs_require_sharding
|
|
|
|
it "should use global ids" do
|
|
@shard1.activate do
|
|
@account = account_model
|
|
new_course = course_factory(:account => @account)
|
|
u1 = user_factory
|
|
u2 = user_factory
|
|
conversation = Conversation.initiate([u1, u2], false, context_type: 'Course', context_id: new_course.id)
|
|
expect(conversation.root_account_ids).to eql [@account.global_id]
|
|
|
|
conversation.add_message(u1, 'ohai')
|
|
admin = account_admin_user(:account => @account, :active_all => true)
|
|
expect(u1.conversations.for_masquerading_user(admin).first).to be_present
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def merge_and_check(sender, source, target, source_user, target_user)
|
|
raise "source_user and target_user must be the same" if source_user && target_user && source_user != target_user
|
|
source.add_participants(sender, [source_user]) if source_user
|
|
target.add_participants(sender, [target_user]) if target_user
|
|
target_user = source_user || target_user
|
|
message_count = source.shard.activate { ConversationMessageParticipant.joins(:conversation_message).where(:user_id => target_user, :conversation_messages => {:conversation_id => source}).count }
|
|
message_count += target.shard.activate { ConversationMessageParticipant.joins(:conversation_message).where(:user_id => target_user, :conversation_messages => {:conversation_id => target}).count }
|
|
|
|
source.merge_into(target)
|
|
|
|
expect { source.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
|
expect(ConversationParticipant.where(conversation_id: source)).to eq []
|
|
expect(ConversationMessage.where(conversation_id: source)).to eq []
|
|
|
|
target.reload
|
|
expect(target.participants(true).map(&:id)).to eq [sender.id, target_user.id]
|
|
expect(target_user.reload.all_conversations.map(&:conversation)).to eq [target]
|
|
cp = target_user.all_conversations.first
|
|
expect(cp.messages.length).to eq message_count
|
|
end
|
|
|
|
describe "merge_into" do
|
|
# non-sharding cases are covered by ConversationParticipant#move_to_user specs
|
|
|
|
context "sharding" do
|
|
specs_require_sharding
|
|
|
|
before :once do
|
|
@sender = User.create!(:name => 'a')
|
|
@conversation1 = Conversation.initiate([@sender], false)
|
|
@conversation2 = Conversation.initiate([@sender], false)
|
|
@conversation3 = @shard1.activate { Conversation.initiate([@sender], false) }
|
|
@user1 = User.create!(:name => 'b')
|
|
@user2 = @shard1.activate { User.create!(:name => 'c') }
|
|
@user3 = @shard2.activate { User.create!(:name => 'd') }
|
|
@conversation1.add_message(@sender, 'message1')
|
|
@conversation2.add_message(@sender, 'message2')
|
|
@conversation3.add_message(@sender, 'message3')
|
|
end
|
|
|
|
context "matching shards" do
|
|
it "user from another shard participating in both conversations" do
|
|
merge_and_check(@sender, @conversation1, @conversation2, @user2, @user2)
|
|
expect(@conversation2.associated_shards.sort_by(&:id)).to eq [Shard.default, @shard1].sort_by(&:id)
|
|
end
|
|
|
|
it "user from another shard participating in source conversation only" do
|
|
merge_and_check(@sender, @conversation1, @conversation2, @user2, nil)
|
|
expect(@conversation2.associated_shards.sort_by(&:id)).to eq [Shard.default, @shard1].sort_by(&:id)
|
|
end
|
|
end
|
|
|
|
context "differing shards" do
|
|
it "user from source shard participating in both conversations" do
|
|
merge_and_check(@sender, @conversation1, @conversation3, @user1, @user1)
|
|
expect(@conversation3.associated_shards.sort_by(&:id)).to eq [@shard1, Shard.default].sort_by(&:id)
|
|
end
|
|
|
|
it "user from destination shard participating in both conversations" do
|
|
merge_and_check(@sender, @conversation1, @conversation3, @user2, @user2)
|
|
expect(@conversation3.associated_shards.sort_by(&:id)).to eq [@shard1, Shard.default].sort_by(&:id)
|
|
end
|
|
|
|
it "user from third shard participating in both conversations" do
|
|
merge_and_check(@sender, @conversation1, @conversation3, @user3, @user3)
|
|
expect(@conversation3.associated_shards.sort_by(&:id)).to eq [Shard.default, @shard1, @shard2].sort_by(&:id)
|
|
end
|
|
|
|
it "user from source shard participating in source conversation only" do
|
|
merge_and_check(@sender, @conversation1, @conversation3, @user1, nil)
|
|
expect(@conversation3.associated_shards.sort_by(&:id)).to eq [@shard1, Shard.default].sort_by(&:id)
|
|
end
|
|
|
|
it "user from destination shard participating in source conversation only" do
|
|
merge_and_check(@sender, @conversation1, @conversation3, @user2, nil)
|
|
expect(@conversation3.associated_shards.sort_by(&:id)).to eq [@shard1, Shard.default].sort_by(&:id)
|
|
end
|
|
|
|
it "user from third shard participating in source conversation only" do
|
|
merge_and_check(@sender, @conversation1, @conversation3, @user3, nil)
|
|
expect(@conversation3.associated_shards.sort_by(&:id)).to eq [Shard.default, @shard1, @shard2].sort_by(&:id)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '.batch_regenerate_private_hashes!' do
|
|
it "doesn't asplode with a query error" do
|
|
# we don't even care if the conversation exists, or that it's correctly updated
|
|
# we just want to form the query and make sure it has a qualified name;
|
|
# so for this spec to be useful you need to have qualified names enabled
|
|
Conversation.batch_regenerate_private_hashes!(1)
|
|
end
|
|
end
|
|
end
|