dawarich/spec/services/visits/create_spec.rb
2025-10-07 22:00:11 +02:00

196 lines
5.7 KiB
Ruby

# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Visits::Create do
let(:user) { create(:user) }
let(:valid_params) do
{
name: 'Test Visit',
latitude: 52.52,
longitude: 13.405,
started_at: '2023-12-01T10:00:00Z',
ended_at: '2023-12-01T12:00:00Z'
}
end
describe '#call' do
context 'when all parameters are valid' do
subject(:service) { described_class.new(user, valid_params) }
it 'creates a visit successfully' do
expect { service.call }.to change { user.visits.count }.by(1)
expect(service.call).to be_truthy
expect(service.visit).to be_persisted
end
it 'creates a visit with correct attributes' do
service.call
visit = service.visit
expect(visit.name).to eq('Test Visit')
expect(visit.user).to eq(user)
expect(visit.status).to eq('confirmed')
expect(visit.started_at).to eq(DateTime.parse('2023-12-01T10:00:00Z'))
expect(visit.ended_at).to eq(DateTime.parse('2023-12-01T12:00:00Z'))
expect(visit.duration).to eq(120) # 2 hours in minutes
end
it 'creates a place with correct coordinates' do
service.call
place = service.visit.place
expect(place).to be_persisted
expect(place.name).to eq('Test Visit')
expect(place.latitude).to eq(52.52)
expect(place.longitude).to eq(13.405)
expect(place.source).to eq('manual')
end
end
context 'when reusing existing place' do
let!(:existing_place) do
create(:place,
latitude: 52.52,
longitude: 13.405,
lonlat: 'POINT(13.405 52.52)')
end
let!(:existing_visit) { create(:visit, user: user, place: existing_place) }
subject(:service) { described_class.new(user, valid_params) }
it 'reuses the existing place' do
expect { service.call }.not_to change { Place.count }
expect(service.visit.place).to eq(existing_place)
end
it 'creates a new visit with the existing place' do
expect { service.call }.to change { user.visits.count }.by(1)
expect(service.visit.place).to eq(existing_place)
end
end
context 'when place creation fails' do
subject(:service) { described_class.new(user, valid_params) }
before do
allow(Place).to receive(:create!).and_raise(ActiveRecord::RecordInvalid.new(Place.new))
end
it 'returns false' do
expect(service.call).to be(false)
end
it 'calls ExceptionReporter' do
expect(ExceptionReporter).to receive(:call)
service.call
end
it 'does not create a visit' do
expect { service.call }.not_to change { Visit.count }
end
end
context 'when visit creation fails' do
subject(:service) { described_class.new(user, valid_params) }
before do
allow_any_instance_of(User).to receive_message_chain(:visits, :create!).and_raise(ActiveRecord::RecordInvalid.new(Visit.new))
end
it 'returns false' do
expect(service.call).to be(false)
end
it 'calls ExceptionReporter' do
expect(ExceptionReporter).to receive(:call)
service.call
end
end
context 'edge cases' do
context 'when name is not provided but defaults are used' do
let(:params) { valid_params.merge(name: '') }
subject(:service) { described_class.new(user, params) }
it 'returns false due to validation' do
expect(service.call).to be(false)
end
end
context 'when coordinates are strings' do
let(:params) do
valid_params.merge(
latitude: '52.52',
longitude: '13.405'
)
end
subject(:service) { described_class.new(user, params) }
it 'converts them to floats and creates visit successfully' do
expect(service.call).to be_truthy
place = service.visit.place
expect(place.latitude).to eq(52.52)
expect(place.longitude).to eq(13.405)
end
end
context 'when visit duration is very short' do
let(:params) do
valid_params.merge(
started_at: '2023-12-01T12:00:00Z',
ended_at: '2023-12-01T12:01:00Z' # 1 minute
)
end
subject(:service) { described_class.new(user, params) }
it 'creates visit with correct duration' do
service.call
expect(service.visit.duration).to eq(1)
end
end
context 'when visit duration is very long' do
let(:params) do
valid_params.merge(
started_at: '2023-12-01T08:00:00Z',
ended_at: '2023-12-02T20:00:00Z' # 36 hours
)
end
subject(:service) { described_class.new(user, params) }
it 'creates visit with correct duration' do
service.call
expect(service.visit.duration).to eq(36 * 60) # 36 hours in minutes
end
end
context 'when datetime-local input is provided without timezone' do
let(:params) do
valid_params.merge(
started_at: '2023-12-01T19:54',
ended_at: '2023-12-01T20:54'
)
end
subject(:service) { described_class.new(user, params) }
it 'parses the datetime in the application timezone' do
service.call
visit = service.visit
expect(visit.started_at.hour).to eq(19)
expect(visit.started_at.min).to eq(54)
expect(visit.ended_at.hour).to eq(20)
expect(visit.ended_at.min).to eq(54)
end
it 'calculates correct duration' do
service.call
expect(service.visit.duration).to eq(60) # 1 hour in minutes
end
end
end
end
end