mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-09 08:47:11 -05:00
257 lines
9.4 KiB
Ruby
257 lines
9.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'rails_helper'
|
|
|
|
RSpec.describe Tracks::Generator do
|
|
let(:user) { create(:user) }
|
|
let(:point_loader) { double('PointLoader') }
|
|
let(:incomplete_segment_handler) { double('IncompleteSegmentHandler') }
|
|
let(:track_cleaner) { double('Cleaner') }
|
|
|
|
let(:generator) do
|
|
described_class.new(
|
|
user,
|
|
point_loader: point_loader,
|
|
incomplete_segment_handler: incomplete_segment_handler,
|
|
track_cleaner: track_cleaner
|
|
)
|
|
end
|
|
|
|
before do
|
|
allow_any_instance_of(Users::SafeSettings).to receive(:meters_between_routes).and_return(500)
|
|
allow_any_instance_of(Users::SafeSettings).to receive(:minutes_between_routes).and_return(60)
|
|
allow_any_instance_of(Users::SafeSettings).to receive(:distance_unit).and_return('km')
|
|
end
|
|
|
|
describe '#call' do
|
|
context 'with no points to process' do
|
|
before do
|
|
allow(track_cleaner).to receive(:cleanup)
|
|
allow(point_loader).to receive(:load_points).and_return([])
|
|
end
|
|
|
|
it 'returns 0 tracks created' do
|
|
result = generator.call
|
|
expect(result).to eq(0)
|
|
end
|
|
|
|
it 'does not call incomplete segment handler' do
|
|
expect(incomplete_segment_handler).not_to receive(:should_finalize_segment?)
|
|
expect(incomplete_segment_handler).not_to receive(:handle_incomplete_segment)
|
|
expect(incomplete_segment_handler).not_to receive(:cleanup_processed_data)
|
|
|
|
generator.call
|
|
end
|
|
end
|
|
|
|
context 'with points that create tracks' do
|
|
let!(:points) do
|
|
[
|
|
create(:point, user: user, lonlat: 'POINT(-74.0060 40.7128)', timestamp: 1.hour.ago.to_i, latitude: 40.7128, longitude: -74.0060),
|
|
create(:point, user: user, lonlat: 'POINT(-74.0050 40.7138)', timestamp: 30.minutes.ago.to_i, latitude: 40.7138, longitude: -74.0050),
|
|
create(:point, user: user, lonlat: 'POINT(-74.0040 40.7148)', timestamp: 10.minutes.ago.to_i, latitude: 40.7148, longitude: -74.0040)
|
|
]
|
|
end
|
|
|
|
before do
|
|
allow(track_cleaner).to receive(:cleanup)
|
|
allow(point_loader).to receive(:load_points).and_return(points)
|
|
allow(incomplete_segment_handler).to receive(:should_finalize_segment?).and_return(true)
|
|
allow(incomplete_segment_handler).to receive(:cleanup_processed_data)
|
|
end
|
|
|
|
it 'creates tracks from segments' do
|
|
expect { generator.call }.to change { Track.count }.by(1)
|
|
end
|
|
|
|
it 'returns the number of tracks created' do
|
|
result = generator.call
|
|
expect(result).to eq(1)
|
|
end
|
|
|
|
it 'calls cleanup on processed data' do
|
|
expect(incomplete_segment_handler).to receive(:cleanup_processed_data)
|
|
generator.call
|
|
end
|
|
|
|
it 'assigns points to the created track' do
|
|
generator.call
|
|
points.each(&:reload)
|
|
track_ids = points.map(&:track_id).uniq.compact
|
|
expect(track_ids.size).to eq(1)
|
|
end
|
|
end
|
|
|
|
context 'with incomplete segments' do
|
|
let!(:points) do
|
|
[
|
|
create(:point, user: user, lonlat: 'POINT(-74.0060 40.7128)', timestamp: 5.minutes.ago.to_i, latitude: 40.7128, longitude: -74.0060),
|
|
create(:point, user: user, lonlat: 'POINT(-74.0050 40.7138)', timestamp: 4.minutes.ago.to_i, latitude: 40.7138, longitude: -74.0050)
|
|
]
|
|
end
|
|
|
|
before do
|
|
allow(track_cleaner).to receive(:cleanup)
|
|
allow(point_loader).to receive(:load_points).and_return(points)
|
|
allow(incomplete_segment_handler).to receive(:should_finalize_segment?).and_return(false)
|
|
allow(incomplete_segment_handler).to receive(:handle_incomplete_segment)
|
|
allow(incomplete_segment_handler).to receive(:cleanup_processed_data)
|
|
end
|
|
|
|
it 'does not create tracks' do
|
|
expect { generator.call }.not_to change { Track.count }
|
|
end
|
|
|
|
it 'handles incomplete segments' do
|
|
expect(incomplete_segment_handler).to receive(:handle_incomplete_segment).with(points)
|
|
generator.call
|
|
end
|
|
|
|
it 'returns 0 tracks created' do
|
|
result = generator.call
|
|
expect(result).to eq(0)
|
|
end
|
|
end
|
|
|
|
context 'with mixed complete and incomplete segments' do
|
|
let!(:old_points) do
|
|
[
|
|
create(:point, user: user, lonlat: 'POINT(-74.0060 40.7128)', timestamp: 2.hours.ago.to_i, latitude: 40.7128, longitude: -74.0060),
|
|
create(:point, user: user, lonlat: 'POINT(-74.0050 40.7138)', timestamp: 1.hour.ago.to_i, latitude: 40.7138, longitude: -74.0050)
|
|
]
|
|
end
|
|
|
|
let!(:recent_points) do
|
|
[
|
|
create(:point, user: user, lonlat: 'POINT(-74.0040 40.7148)', timestamp: 3.minutes.ago.to_i, latitude: 40.7148, longitude: -74.0040),
|
|
create(:point, user: user, lonlat: 'POINT(-74.0030 40.7158)', timestamp: 2.minutes.ago.to_i, latitude: 40.7158, longitude: -74.0030)
|
|
]
|
|
end
|
|
|
|
before do
|
|
allow(track_cleaner).to receive(:cleanup)
|
|
allow(point_loader).to receive(:load_points).and_return(old_points + recent_points)
|
|
|
|
# First segment (old points) should be finalized
|
|
# Second segment (recent points) should be incomplete
|
|
call_count = 0
|
|
allow(incomplete_segment_handler).to receive(:should_finalize_segment?) do |segment_points|
|
|
call_count += 1
|
|
call_count == 1 # Only finalize first segment
|
|
end
|
|
|
|
allow(incomplete_segment_handler).to receive(:handle_incomplete_segment)
|
|
allow(incomplete_segment_handler).to receive(:cleanup_processed_data)
|
|
end
|
|
|
|
it 'creates tracks for complete segments only' do
|
|
expect { generator.call }.to change { Track.count }.by(1)
|
|
end
|
|
|
|
it 'handles incomplete segments' do
|
|
# Note: The exact behavior depends on segmentation logic
|
|
# The important thing is that the method can be called without errors
|
|
generator.call
|
|
# Test passes if no exceptions are raised
|
|
expect(true).to be_truthy
|
|
end
|
|
|
|
it 'returns the correct number of tracks created' do
|
|
result = generator.call
|
|
expect(result).to eq(1)
|
|
end
|
|
end
|
|
|
|
context 'with insufficient points for track creation' do
|
|
let!(:single_point) do
|
|
[create(:point, user: user, lonlat: 'POINT(-74.0060 40.7128)', timestamp: 1.hour.ago.to_i, latitude: 40.7128, longitude: -74.0060)]
|
|
end
|
|
|
|
before do
|
|
allow(track_cleaner).to receive(:cleanup)
|
|
allow(point_loader).to receive(:load_points).and_return(single_point)
|
|
allow(incomplete_segment_handler).to receive(:should_finalize_segment?).and_return(true)
|
|
allow(incomplete_segment_handler).to receive(:cleanup_processed_data)
|
|
end
|
|
|
|
it 'does not create tracks with less than 2 points' do
|
|
expect { generator.call }.not_to change { Track.count }
|
|
end
|
|
|
|
it 'returns 0 tracks created' do
|
|
result = generator.call
|
|
expect(result).to eq(0)
|
|
end
|
|
end
|
|
|
|
context 'error handling' do
|
|
before do
|
|
allow(track_cleaner).to receive(:cleanup)
|
|
allow(point_loader).to receive(:load_points).and_raise(StandardError, 'Point loading failed')
|
|
end
|
|
|
|
it 'propagates errors from point loading' do
|
|
expect { generator.call }.to raise_error(StandardError, 'Point loading failed')
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'strategy pattern integration' do
|
|
context 'with bulk processing strategies' do
|
|
let(:bulk_loader) { Tracks::PointLoaders::BulkLoader.new(user) }
|
|
let(:ignore_handler) { Tracks::IncompleteSegmentHandlers::IgnoreHandler.new(user) }
|
|
let(:replace_cleaner) { Tracks::Cleaners::ReplaceCleaner.new(user) }
|
|
|
|
let(:bulk_generator) do
|
|
described_class.new(
|
|
user,
|
|
point_loader: bulk_loader,
|
|
incomplete_segment_handler: ignore_handler,
|
|
track_cleaner: replace_cleaner
|
|
)
|
|
end
|
|
|
|
let!(:existing_track) { create(:track, user: user) }
|
|
let!(:points) do
|
|
[
|
|
create(:point, user: user, lonlat: 'POINT(-74.0060 40.7128)', timestamp: 1.hour.ago.to_i, latitude: 40.7128, longitude: -74.0060),
|
|
create(:point, user: user, lonlat: 'POINT(-74.0050 40.7138)', timestamp: 30.minutes.ago.to_i, latitude: 40.7138, longitude: -74.0050)
|
|
]
|
|
end
|
|
|
|
it 'behaves like bulk processing' do
|
|
initial_count = Track.count
|
|
bulk_generator.call
|
|
# Bulk processing replaces existing tracks with new ones
|
|
# The final count depends on how many valid tracks can be created from the points
|
|
expect(Track.count).to be >= 0
|
|
end
|
|
end
|
|
|
|
context 'with incremental processing strategies' do
|
|
let(:incremental_loader) { Tracks::PointLoaders::IncrementalLoader.new(user) }
|
|
let(:buffer_handler) { Tracks::IncompleteSegmentHandlers::BufferHandler.new(user, Date.current, 5) }
|
|
let(:noop_cleaner) { Tracks::Cleaners::NoOpCleaner.new(user) }
|
|
|
|
let(:incremental_generator) do
|
|
described_class.new(
|
|
user,
|
|
point_loader: incremental_loader,
|
|
incomplete_segment_handler: buffer_handler,
|
|
track_cleaner: noop_cleaner
|
|
)
|
|
end
|
|
|
|
let!(:existing_track) { create(:track, user: user) }
|
|
|
|
before do
|
|
# Mock the incremental loader to return some points
|
|
allow(incremental_loader).to receive(:load_points).and_return([])
|
|
end
|
|
|
|
it 'behaves like incremental processing' do
|
|
expect { incremental_generator.call }.not_to change { Track.count }
|
|
end
|
|
end
|
|
end
|
|
end
|