Skip to content

Commit

Permalink
Add more tests for friendships and fix some newly discovered bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
felixauringer committed Feb 4, 2021
1 parent 1decb7d commit e4d0ad4
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 6 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ gem 'devise-bootstrap-views', '~> 1.1' # https://github.com/hisea/devise-bootstr
gem 'activestorage-validator'
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] # https://github.com/tzinfo/tzinfo-data
gem 'composite_primary_keys'

#
# Packaged JS, CSS libraries and helpers
Expand Down
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ GEM
xpath (~> 3.2)
childprocess (3.0.0)
coderay (1.1.3)
composite_primary_keys (12.0.6)
activerecord (~> 6.0.0)
concurrent-ruby (1.1.7)
crass (1.0.6)
debug_inspector (1.0.0)
Expand Down Expand Up @@ -305,6 +307,7 @@ DEPENDENCIES
bootstrap (~> 4.5, >= 4.5.2)
byebug
capybara (~> 3.33)
composite_primary_keys
devise (~> 4.7, >= 4.7.3)
devise-bootstrap-views (~> 1.1)
devise-i18n (~> 1.9, >= 1.9.2)
Expand Down
11 changes: 8 additions & 3 deletions app/models/friendship.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
# A class for handling contacts which ensures that the association is always symmetrical
class Friendship < ApplicationRecord
self.table_name = 'users_users'
belongs_to :user, foreign_key: :contact_id, inverse_of: :friendships
self.primary_keys = :user_id, :contact_id

belongs_to :user, inverse_of: :friendships
belongs_to :contact, class_name: 'User', inverse_of: :friendships

after_create do |c|
Friendship.create!(user_id: c.contact_id, contact_id: c.user_id) unless Friendship.find_by(contact_id: c.user_id)
unless Friendship.find_by(user_id: c.contact_id, contact_id: c.user_id)
Friendship.create(user_id: c.contact_id, contact_id: c.user_id)
end
end

after_destroy do |c|
reciprocal = Friendship.find_by(contact_id: c.user_id)
reciprocal = Friendship.find_by(contact_id: c.user_id, user_id: c.contact_id)
reciprocal&.destroy
end
end
4 changes: 2 additions & 2 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class User < ApplicationRecord
has_many :activities, dependent: :delete_all
has_many :call_participants, dependent: :delete_all, inverse_of: :user
has_many :jitsi_calls, through: :call_participants
has_many :friendships, dependent: :destroy
has_many :contacts, through: :friendships, source: :user
has_many :friendships, dependent: :destroy, inverse_of: :user
has_many :contacts, through: :friendships

# as we do not need to work with the relationship models as independent entities, `has_and_belongs_to_many` is fine
# https://guides.rubyonrails.org/association_basics.html#choosing-between-has-many-through-and-has-and-belongs-to-many
Expand Down
33 changes: 33 additions & 0 deletions spec/models/friendship_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require 'rails_helper'

RSpec.describe Friendship, type: :model do
let(:users) { FactoryBot.create_list :user, 3 }

before { described_class.create(user_id: users[0].id, contact_id: users[1].id) }

it 'creates a symmetrical association' do
expect(described_class.find_by(user_id: users[0].id, contact_id: users[1].id)).to be_present
expect(described_class.find_by(user_id: users[1].id, contact_id: users[0].id)).to be_present
end

it 'deletes both parts of the association' do
described_class.find_by(user_id: users[0].id, contact_id: users[1].id).destroy!
expect(described_class.find_by(user_id: users[0].id, contact_id: users[1].id)).to be_blank
expect(described_class.find_by(user_id: users[1].id, contact_id: users[0].id)).to be_blank
end

describe 'multiple contacts' do
before { described_class.create(user_id: users[0].id, contact_id: users[2].id) }

it 'allows a user to have multiple contacts' do
expect(described_class.find_by(user_id: users[0].id, contact_id: users[1].id)).to be_present
expect(described_class.find_by(user_id: users[0].id, contact_id: users[2].id)).to be_present
end

it 'allows to only delete one of the contacts' do
described_class.find_by(user_id: users[0].id, contact_id: users[1].id).destroy!
expect(described_class.find_by(user_id: users[0].id, contact_id: users[1].id)).to be_blank
expect(described_class.find_by(user_id: users[0].id, contact_id: users[2].id)).to be_present
end
end
end
25 changes: 24 additions & 1 deletion spec/models/user_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,30 @@
end

it 'destroys all participants when destroyed' do
expect { user.destroy }.to change(CallParticipant, :count).from(jitsi_calls.size).to(0)
expect { user.destroy }.to change(CallParticipant, :count).by(-jitsi_calls.size)
end
end

context 'with contacts' do
let(:contacts) { FactoryBot.create_list :user, 2 }

before do
user.save
contacts.each { |contact| user.friendships.create(contact: contact) }
end

it 'has associated contacts' do
expect(user.contacts).to include(*contacts)
end

it 'has friendships' do
expect(user.friendships.map(&:contact_id)).to include(*contacts.map(&:id))
end

it 'destroys all friendships when destroyed' do
user.destroy
expect(Friendship.where(user_id: user.id)).to be_empty
expect(Friendship.where(contact_id: user.id)).to be_empty
end
end
end

0 comments on commit e4d0ad4

Please sign in to comment.