mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-09 08:47:11 -05:00
177 lines
5.5 KiB
Ruby
177 lines
5.5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'rails_helper'
|
|
|
|
RSpec.describe Family::Invitation, type: :model do
|
|
describe 'associations' do
|
|
it { is_expected.to belong_to(:family) }
|
|
it { is_expected.to belong_to(:invited_by).class_name('User') }
|
|
end
|
|
|
|
describe 'validations' do
|
|
subject { build(:family_invitation) }
|
|
|
|
it { is_expected.to validate_presence_of(:email) }
|
|
it { is_expected.to allow_value('test@example.com').for(:email) }
|
|
it { is_expected.not_to allow_value('invalid-email').for(:email) }
|
|
it { is_expected.to validate_uniqueness_of(:token) }
|
|
it { is_expected.to validate_presence_of(:status) }
|
|
|
|
it 'validates token presence after creation' do
|
|
invitation = build(:family_invitation, token: nil)
|
|
invitation.save
|
|
expect(invitation.token).to be_present
|
|
end
|
|
|
|
it 'validates expires_at presence after creation' do
|
|
invitation = build(:family_invitation, expires_at: nil)
|
|
invitation.save
|
|
expect(invitation.expires_at).to be_present
|
|
end
|
|
end
|
|
|
|
describe 'enums' do
|
|
it { is_expected.to define_enum_for(:status).with_values(pending: 0, accepted: 1, expired: 2, cancelled: 3) }
|
|
end
|
|
|
|
describe 'scopes' do
|
|
let(:family) { create(:family) }
|
|
let(:pending_invitation) do
|
|
create(:family_invitation, family: family, status: :pending, expires_at: 1.day.from_now)
|
|
end
|
|
let(:expired_invitation) { create(:family_invitation, family: family, status: :pending, expires_at: 1.day.ago) }
|
|
let(:accepted_invitation) { create(:family_invitation, :accepted, family: family) }
|
|
|
|
describe '.active' do
|
|
it 'returns only pending and non-expired invitations' do
|
|
expect(Family::Invitation.active).to include(pending_invitation)
|
|
expect(Family::Invitation.active).not_to include(expired_invitation)
|
|
expect(Family::Invitation.active).not_to include(accepted_invitation)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'callbacks' do
|
|
describe 'before_validation on create' do
|
|
let(:invitation) { build(:family_invitation, token: nil, expires_at: nil) }
|
|
|
|
it 'generates a token' do
|
|
invitation.save
|
|
expect(invitation.token).to be_present
|
|
expect(invitation.token.length).to be > 20
|
|
end
|
|
|
|
it 'sets expiry date' do
|
|
invitation.save
|
|
expect(invitation.expires_at).to be_within(1.minute).of(Family::Invitation::EXPIRY_DAYS.days.from_now)
|
|
end
|
|
|
|
it 'does not override existing token' do
|
|
custom_token = 'custom-token'
|
|
invitation.token = custom_token
|
|
invitation.save
|
|
expect(invitation.token).to eq(custom_token)
|
|
end
|
|
|
|
it 'does not override existing expiry date' do
|
|
custom_expiry = 3.days.from_now
|
|
invitation.expires_at = custom_expiry
|
|
invitation.save
|
|
expect(invitation.expires_at).to be_within(1.second).of(custom_expiry)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#expired?' do
|
|
context 'when expires_at is in the future' do
|
|
let(:invitation) { create(:family_invitation, expires_at: 1.day.from_now) }
|
|
|
|
it 'returns false' do
|
|
expect(invitation.expired?).to be false
|
|
end
|
|
end
|
|
|
|
context 'when expires_at is in the past' do
|
|
let(:invitation) { create(:family_invitation, expires_at: 1.day.ago) }
|
|
|
|
it 'returns true' do
|
|
expect(invitation.expired?).to be true
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#can_be_accepted?' do
|
|
context 'when invitation is pending and not expired' do
|
|
let(:invitation) { create(:family_invitation, status: :pending, expires_at: 1.day.from_now) }
|
|
|
|
it 'returns true' do
|
|
expect(invitation.can_be_accepted?).to be true
|
|
end
|
|
end
|
|
|
|
context 'when invitation is pending but expired' do
|
|
let(:invitation) { create(:family_invitation, status: :pending, expires_at: 1.day.ago) }
|
|
|
|
it 'returns false' do
|
|
expect(invitation.can_be_accepted?).to be false
|
|
end
|
|
end
|
|
|
|
context 'when invitation is accepted' do
|
|
let(:invitation) { create(:family_invitation, :accepted, expires_at: 1.day.from_now) }
|
|
|
|
it 'returns false' do
|
|
expect(invitation.can_be_accepted?).to be false
|
|
end
|
|
end
|
|
|
|
context 'when invitation is cancelled' do
|
|
let(:invitation) { create(:family_invitation, :cancelled, expires_at: 1.day.from_now) }
|
|
|
|
it 'returns false' do
|
|
expect(invitation.can_be_accepted?).to be false
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'constants' do
|
|
it 'defines EXPIRY_DAYS' do
|
|
expect(Family::Invitation::EXPIRY_DAYS).to eq(7)
|
|
end
|
|
end
|
|
|
|
describe 'token uniqueness' do
|
|
let(:family) { create(:family) }
|
|
let(:user) { create(:user) }
|
|
|
|
it 'ensures each invitation has a unique token' do
|
|
invitation1 = create(:family_invitation, family: family, invited_by: user)
|
|
invitation2 = create(:family_invitation, family: family, invited_by: user)
|
|
|
|
expect(invitation1.token).not_to eq(invitation2.token)
|
|
end
|
|
end
|
|
|
|
describe 'email format validation' do
|
|
let(:invitation) { build(:family_invitation) }
|
|
|
|
it 'accepts valid email formats' do
|
|
valid_emails = ['test@example.com', 'user.name@domain.co.uk', 'user+tag@example.org']
|
|
|
|
valid_emails.each do |email|
|
|
invitation.email = email
|
|
expect(invitation).to be_valid
|
|
end
|
|
end
|
|
|
|
it 'rejects invalid email formats' do
|
|
invalid_emails = ['invalid-email', '@example.com', 'user@', 'user space@example.com']
|
|
|
|
invalid_emails.each do |email|
|
|
invitation.email = email
|
|
expect(invitation).not_to be_valid
|
|
expect(invitation.errors[:email]).to be_present
|
|
end
|
|
end
|
|
end
|
|
end
|