From 774ac473736cbf348827cf6d861e7fbbb72d7623 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 7 Oct 2018 23:44:58 +0200 Subject: Add conversations API (#8832) * Add conversations API * Add web UI for conversations * Add test for conversations API * Add tests for ConversationAccount * Improve web UI * Rename ConversationAccount to AccountConversation * Remove conversations on block and mute * Change last_status_id to be a denormalization of status_ids * Add optimistic locking --- .../api/v1/conversations_controller_spec.rb | 37 +++++++++++ .../fabricators/conversation_account_fabricator.rb | 6 ++ spec/models/account_conversation_spec.rb | 72 ++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 spec/controllers/api/v1/conversations_controller_spec.rb create mode 100644 spec/fabricators/conversation_account_fabricator.rb create mode 100644 spec/models/account_conversation_spec.rb (limited to 'spec') diff --git a/spec/controllers/api/v1/conversations_controller_spec.rb b/spec/controllers/api/v1/conversations_controller_spec.rb new file mode 100644 index 000000000..2e9525855 --- /dev/null +++ b/spec/controllers/api/v1/conversations_controller_spec.rb @@ -0,0 +1,37 @@ +require 'rails_helper' + +RSpec.describe Api::V1::ConversationsController, type: :controller do + render_views + + let!(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + let(:other) { Fabricate(:user, account: Fabricate(:account, username: 'bob')) } + + before do + allow(controller).to receive(:doorkeeper_token) { token } + end + + describe 'GET #index' do + let(:scopes) { 'read:statuses' } + + before do + PostStatusService.new.call(other.account, 'Hey @alice', nil, visibility: 'direct') + end + + it 'returns http success' do + get :index + expect(response).to have_http_status(200) + end + + it 'returns pagination headers' do + get :index, params: { limit: 1 } + expect(response.headers['Link'].links.size).to eq(2) + end + + it 'returns conversations' do + get :index + json = body_as_json + expect(json.size).to eq 1 + end + end +end diff --git a/spec/fabricators/conversation_account_fabricator.rb b/spec/fabricators/conversation_account_fabricator.rb new file mode 100644 index 000000000..f57ffd535 --- /dev/null +++ b/spec/fabricators/conversation_account_fabricator.rb @@ -0,0 +1,6 @@ +Fabricator(:conversation_account) do + account nil + conversation nil + participant_account_ids "" + last_status nil +end diff --git a/spec/models/account_conversation_spec.rb b/spec/models/account_conversation_spec.rb new file mode 100644 index 000000000..70a76281e --- /dev/null +++ b/spec/models/account_conversation_spec.rb @@ -0,0 +1,72 @@ +require 'rails_helper' + +RSpec.describe AccountConversation, type: :model do + let!(:alice) { Fabricate(:account, username: 'alice') } + let!(:bob) { Fabricate(:account, username: 'bob') } + let!(:mark) { Fabricate(:account, username: 'mark') } + + describe '.add_status' do + it 'creates new record when no others exist' do + status = Fabricate(:status, account: alice, visibility: :direct) + status.mentions.create(account: bob) + + conversation = AccountConversation.add_status(alice, status) + + expect(conversation.participant_accounts).to include(bob) + expect(conversation.last_status).to eq status + expect(conversation.status_ids).to eq [status.id] + end + + it 'appends to old record when there is a match' do + last_status = Fabricate(:status, account: alice, visibility: :direct) + conversation = AccountConversation.create!(account: alice, conversation: last_status.conversation, participant_account_ids: [bob.id], status_ids: [last_status.id]) + + status = Fabricate(:status, account: bob, visibility: :direct, thread: last_status) + status.mentions.create(account: alice) + + new_conversation = AccountConversation.add_status(alice, status) + + expect(new_conversation.id).to eq conversation.id + expect(new_conversation.participant_accounts).to include(bob) + expect(new_conversation.last_status).to eq status + expect(new_conversation.status_ids).to eq [last_status.id, status.id] + end + + it 'creates new record when new participants are added' do + last_status = Fabricate(:status, account: alice, visibility: :direct) + conversation = AccountConversation.create!(account: alice, conversation: last_status.conversation, participant_account_ids: [bob.id], status_ids: [last_status.id]) + + status = Fabricate(:status, account: bob, visibility: :direct, thread: last_status) + status.mentions.create(account: alice) + status.mentions.create(account: mark) + + new_conversation = AccountConversation.add_status(alice, status) + + expect(new_conversation.id).to_not eq conversation.id + expect(new_conversation.participant_accounts).to include(bob, mark) + expect(new_conversation.last_status).to eq status + expect(new_conversation.status_ids).to eq [status.id] + end + end + + describe '.remove_status' do + it 'updates last status to a previous value' do + last_status = Fabricate(:status, account: alice, visibility: :direct) + status = Fabricate(:status, account: alice, visibility: :direct) + conversation = AccountConversation.create!(account: alice, conversation: last_status.conversation, participant_account_ids: [bob.id], status_ids: [status.id, last_status.id]) + last_status.mentions.create(account: bob) + last_status.destroy! + conversation.reload + expect(conversation.last_status).to eq status + expect(conversation.status_ids).to eq [status.id] + end + + it 'removes the record if no other statuses are referenced' do + last_status = Fabricate(:status, account: alice, visibility: :direct) + conversation = AccountConversation.create!(account: alice, conversation: last_status.conversation, participant_account_ids: [bob.id], status_ids: [last_status.id]) + last_status.mentions.create(account: bob) + last_status.destroy! + expect(AccountConversation.where(id: conversation.id).count).to eq 0 + end + end +end -- cgit