dawarich/spec/services/location_search/spatial_matcher_spec.rb
2025-09-03 18:22:39 +02:00

223 lines
6.7 KiB
Ruby

# frozen_string_literal: true
require 'rails_helper'
RSpec.describe LocationSearch::SpatialMatcher do
let(:service) { described_class.new }
let(:user) { create(:user) }
let(:latitude) { 52.5200 }
let(:longitude) { 13.4050 }
let(:radius_meters) { 100 }
describe '#find_points_near' do
let!(:near_point) do
create(:point,
user: user,
lonlat: "POINT(13.4051 52.5201)",
timestamp: 1.hour.ago.to_i,
city: 'Berlin',
country: 'Germany',
altitude: 100,
accuracy: 5
)
end
let!(:far_point) do
create(:point,
user: user,
lonlat: "POINT(13.5000 52.6000)",
timestamp: 2.hours.ago.to_i
)
end
let!(:other_user_point) do
create(:point,
user: create(:user),
lonlat: "POINT(13.4051 52.5201)",
timestamp: 30.minutes.ago.to_i
)
end
context 'with points within radius' do
it 'returns points within the specified radius' do
results = service.find_points_near(user, latitude, longitude, radius_meters)
expect(results.length).to eq(1)
expect(results.first[:id]).to eq(near_point.id)
end
it 'excludes points outside the radius' do
results = service.find_points_near(user, latitude, longitude, radius_meters)
point_ids = results.map { |r| r[:id] }
expect(point_ids).not_to include(far_point.id)
end
it 'only includes points from the specified user' do
results = service.find_points_near(user, latitude, longitude, radius_meters)
point_ids = results.map { |r| r[:id] }
expect(point_ids).not_to include(other_user_point.id)
end
it 'includes calculated distance' do
results = service.find_points_near(user, latitude, longitude, radius_meters)
expect(results.first[:distance_meters]).to be_a(Float)
expect(results.first[:distance_meters]).to be < radius_meters
end
it 'includes point attributes' do
results = service.find_points_near(user, latitude, longitude, radius_meters)
point = results.first
expect(point).to include(
id: near_point.id,
timestamp: near_point.timestamp,
coordinates: [52.5201, 13.4051],
city: 'Berlin',
country: 'Germany',
altitude: 100,
accuracy: 5
)
end
it 'includes ISO8601 formatted date' do
results = service.find_points_near(user, latitude, longitude, radius_meters)
expect(results.first[:date]).to match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)
end
it 'orders results by timestamp descending (most recent first)' do
# Create another nearby point with older timestamp
older_point = create(:point,
user: user,
lonlat: "POINT(13.4049 52.5199)",
timestamp: 3.hours.ago.to_i
)
results = service.find_points_near(user, latitude, longitude, radius_meters)
expect(results.first[:id]).to eq(near_point.id) # More recent
expect(results.last[:id]).to eq(older_point.id) # Older
end
end
context 'with date filtering' do
let(:date_options) do
{
date_from: 2.days.ago.to_date,
date_to: Date.current
}
end
let!(:old_point) do
create(:point,
user: user,
lonlat: "POINT(13.4051 52.5201)",
timestamp: 1.week.ago.to_i
)
end
it 'filters points by date range' do
results = service.find_points_near(user, latitude, longitude, radius_meters, date_options)
point_ids = results.map { |r| r[:id] }
expect(point_ids).to include(near_point.id)
expect(point_ids).not_to include(old_point.id)
end
context 'with only date_from' do
let(:date_options) { { date_from: 2.hours.ago.to_date } }
it 'includes points after date_from' do
results = service.find_points_near(user, latitude, longitude, radius_meters, date_options)
point_ids = results.map { |r| r[:id] }
expect(point_ids).to include(near_point.id)
end
end
context 'with only date_to' do
let(:date_options) { { date_to: 2.days.ago.to_date } }
it 'includes points before date_to' do
results = service.find_points_near(user, latitude, longitude, radius_meters, date_options)
point_ids = results.map { |r| r[:id] }
expect(point_ids).to include(old_point.id)
expect(point_ids).not_to include(near_point.id)
end
end
end
context 'with no points within radius' do
it 'returns empty array' do
results = service.find_points_near(user, 60.0, 30.0, 100) # Far away coordinates
expect(results).to be_empty
end
end
context 'with edge cases' do
it 'handles points at the exact radius boundary' do
# This test would require creating a point at exactly 100m distance
# For simplicity, we'll test with a very small radius that should exclude our test point
results = service.find_points_near(user, latitude, longitude, 1) # 1 meter radius
expect(results).to be_empty
end
it 'handles negative coordinates' do
# Create point with negative coordinates
negative_point = create(:point,
user: user,
lonlat: "POINT(151.2093 -33.8688)",
timestamp: 1.hour.ago.to_i
)
results = service.find_points_near(user, -33.8688, 151.2093, 1000)
expect(results.length).to eq(1)
expect(results.first[:id]).to eq(negative_point.id)
end
it 'handles coordinates near poles' do
# Create point near north pole
polar_point = create(:point,
user: user,
lonlat: "POINT(0.0 89.0)",
timestamp: 1.hour.ago.to_i
)
results = service.find_points_near(user, 89.0, 0.0, 1000)
expect(results.length).to eq(1)
expect(results.first[:id]).to eq(polar_point.id)
end
end
context 'with large datasets' do
before do
# Create many points to test performance
50.times do |i|
create(:point,
user: user,
lonlat: "POINT(#{longitude + (i * 0.0001)} #{latitude + (i * 0.0001)})", # Spread points slightly
timestamp: i.hours.ago.to_i
)
end
end
it 'efficiently queries large datasets' do
start_time = Time.current
results = service.find_points_near(user, latitude, longitude, 1000)
query_time = Time.current - start_time
expect(query_time).to be < 1.0 # Should complete within 1 second
expect(results.length).to be > 40 # Should find most of the points
end
end
end
end