mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-11 01:31:39 -05:00
Add some unit tests
This commit is contained in:
parent
5544bcd5ff
commit
48962e87e8
7 changed files with 286 additions and 17 deletions
|
|
@ -5,20 +5,16 @@ class Stat < ApplicationRecord
|
||||||
|
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
|
||||||
def timespan
|
|
||||||
DateTime.new(year, month).beginning_of_month..DateTime.new(year, month).end_of_month
|
|
||||||
end
|
|
||||||
|
|
||||||
def distance_by_day
|
def distance_by_day
|
||||||
timespan.to_a.map.with_index(1) do |day, index|
|
timespan.to_a.map.with_index(1) do |day, index|
|
||||||
beginning_of_day = day.beginning_of_day.to_i
|
beginning_of_day = day.beginning_of_day.to_i
|
||||||
end_of_day = day.end_of_day.to_i
|
end_of_day = day.end_of_day.to_i
|
||||||
|
|
||||||
data = { day: index, distance: 0 }
|
|
||||||
|
|
||||||
# We have to filter by user as well
|
# We have to filter by user as well
|
||||||
points = Point.where(timestamp: beginning_of_day..end_of_day)
|
points = Point.where(timestamp: beginning_of_day..end_of_day)
|
||||||
|
|
||||||
|
data = { day: index, distance: 0 }
|
||||||
|
|
||||||
points.each_cons(2) do |point1, point2|
|
points.each_cons(2) do |point1, point2|
|
||||||
distance = Geocoder::Calculations.distance_between(
|
distance = Geocoder::Calculations.distance_between(
|
||||||
[point1.latitude, point1.longitude], [point2.latitude, point2.longitude]
|
[point1.latitude, point1.longitude], [point2.latitude, point2.longitude]
|
||||||
|
|
@ -49,12 +45,18 @@ class Stat < ApplicationRecord
|
||||||
|
|
||||||
data = CountriesAndCities.new(points).call
|
data = CountriesAndCities.new(points).call
|
||||||
|
|
||||||
{ countries: data.count, cities: data.sum { |country| country[:cities].count } }
|
{ countries: data.map { _1[:country] }.uniq.count, cities: data.sum { |country| country[:cities].count } }
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.years
|
def self.years
|
||||||
starting_year = pluck(:year).uniq.min || Time.current.year
|
starting_year = select(:year).min&.year || Time.current.year
|
||||||
|
|
||||||
(starting_year..Time.current.year).to_a.reverse
|
(starting_year..Time.current.year).to_a.reverse
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def timespan
|
||||||
|
DateTime.new(year, month).beginning_of_month..DateTime.new(year, month).end_of_month
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :import do
|
factory :import do
|
||||||
user_id { "" }
|
user
|
||||||
|
name { 'APRIL_2013.json' }
|
||||||
source { 1 }
|
source { 1 }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ FactoryBot.define do
|
||||||
raw_data { "" }
|
raw_data { "" }
|
||||||
tracker_id { "MyString" }
|
tracker_id { "MyString" }
|
||||||
import_id { "" }
|
import_id { "" }
|
||||||
city { "MyString" }
|
city { nil }
|
||||||
country { "MyString" }
|
country { nil }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
61
spec/jobs/reverse_geocoding_job_spec.rb
Normal file
61
spec/jobs/reverse_geocoding_job_spec.rb
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe ReverseGeocodingJob, type: :job do
|
||||||
|
describe '#perform' do
|
||||||
|
subject(:perform) { described_class.new.perform(point.id) }
|
||||||
|
|
||||||
|
let(:point) { create(:point) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Geocoder).to receive(:search).and_return([double(city: 'City', country: 'Country')])
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when REVERSE_GEOCODING_ENABLED is false' do
|
||||||
|
before { stub_const('REVERSE_GEOCODING_ENABLED', false) }
|
||||||
|
|
||||||
|
it 'does not update point' do
|
||||||
|
expect { perform }.not_to change { point.reload.city }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not call Geocoder' do
|
||||||
|
perform
|
||||||
|
|
||||||
|
expect(Geocoder).not_to have_received(:search)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when REVERSE_GEOCODING_ENABLED is true' do
|
||||||
|
before { stub_const('REVERSE_GEOCODING_ENABLED', true) }
|
||||||
|
|
||||||
|
it 'updates point with city and country' do
|
||||||
|
expect { perform }.to change { point.reload.city }.from(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calls Geocoder' do
|
||||||
|
perform
|
||||||
|
|
||||||
|
expect(Geocoder).to have_received(:search).with([point.latitude, point.longitude])
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when point has city and country' do
|
||||||
|
let(:point) { create(:point, city: 'City', country: 'Country') }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Geocoder).to receive(:search).and_return(
|
||||||
|
[double(city: 'Another city', country: 'Some country')]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not update point' do
|
||||||
|
expect { perform }.not_to change { point.reload.city }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not call Geocoder' do
|
||||||
|
perform
|
||||||
|
|
||||||
|
expect(Geocoder).not_to have_received(:search)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,5 +1,20 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe StatCreatingJob, type: :job do
|
RSpec.describe StatCreatingJob, type: :job do
|
||||||
pending "add some examples to (or delete) #{__FILE__}"
|
describe '#perform' do
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
|
||||||
|
subject { described_class.perform_now([user.id]) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(CreateStats).to receive(:new).and_call_original
|
||||||
|
allow_any_instance_of(CreateStats).to receive(:call)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a stat' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(CreateStats).to have_received(:new).with([user.id])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,135 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Stat, type: :model do
|
RSpec.describe Stat, type: :model do
|
||||||
it { is_expected.to validate_presence_of(:year) }
|
describe 'associations' do
|
||||||
it { is_expected.to validate_presence_of(:month) }
|
it { is_expected.to belong_to(:user) }
|
||||||
|
it { is_expected.to validate_presence_of(:year) }
|
||||||
|
it { is_expected.to validate_presence_of(:month) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'methods' do
|
||||||
|
let(:year) { 2021 }
|
||||||
|
|
||||||
|
describe '.year_cities_and_countries' do
|
||||||
|
subject { described_class.year_cities_and_countries(year) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_const('MINIMUM_POINTS_IN_CITY', 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are points' do
|
||||||
|
let!(:points) do
|
||||||
|
create_list(:point, 3, city: 'City', country: 'Country', timestamp: DateTime.new(year, 1))
|
||||||
|
create_list(:point, 2, city: 'Some City', country: 'Another country', timestamp: DateTime.new(year, 2))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
it 'returns countries and cities' do
|
||||||
|
expect(subject).to eq(countries: 2, cities: 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are no points' do
|
||||||
|
it 'returns countries and cities' do
|
||||||
|
expect(subject).to eq(countries: 0, cities: 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.years' do
|
||||||
|
subject { described_class.years }
|
||||||
|
|
||||||
|
context 'when there are no stats' do
|
||||||
|
it 'returns years' do
|
||||||
|
expect(subject).to eq([Time.current.year])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are stats' do
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let(:expected_years) { (year..Time.current.year).to_a.reverse }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:stat, year: 2021, user: user)
|
||||||
|
create(:stat, year: 2020, user: user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns years' do
|
||||||
|
expect(subject).to eq(expected_years)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#distance_by_day' do
|
||||||
|
subject { stat.distance_by_day }
|
||||||
|
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let(:stat) { create(:stat, year: year, month: 1, user: user) }
|
||||||
|
let(:expected_distance) do
|
||||||
|
# 31 day of January
|
||||||
|
(1..31).map { |day| [day, 0] }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are points' do
|
||||||
|
let!(:points) do
|
||||||
|
create(:point, latitude: 1, longitude: 1, timestamp: DateTime.new(year, 1, 1, 1))
|
||||||
|
create(:point, latitude: 2, longitude: 2, timestamp: DateTime.new(year, 1, 1, 2))
|
||||||
|
end
|
||||||
|
|
||||||
|
before { expected_distance[0][1] = 157.23 }
|
||||||
|
|
||||||
|
it 'returns distance by day' do
|
||||||
|
expect(subject).to eq(expected_distance)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are no points' do
|
||||||
|
it 'returns distance by day' do
|
||||||
|
expect(subject).to eq(expected_distance)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#timespan' do
|
||||||
|
subject { stat.send(:timespan) }
|
||||||
|
|
||||||
|
let(:stat) { build(:stat, year: year, month: 1) }
|
||||||
|
let(:expected_timespan) { DateTime.new(year, 1).beginning_of_month..DateTime.new(year, 1).end_of_month }
|
||||||
|
|
||||||
|
it 'returns timespan' do
|
||||||
|
expect(subject).to eq(expected_timespan)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#self.year_distance' do
|
||||||
|
subject { described_class.year_distance(year) }
|
||||||
|
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let(:expected_distance) do
|
||||||
|
(1..12).map { |month| [Date::MONTHNAMES[month], 0] }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are stats' do
|
||||||
|
let!(:stats) do
|
||||||
|
create(:stat, year: year, month: 1, distance: 100, user: user)
|
||||||
|
create(:stat, year: year, month: 2, distance: 200, user: user)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
expected_distance[0][1] = 100
|
||||||
|
expected_distance[1][1] = 200
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns year distance' do
|
||||||
|
expect(subject).to eq(expected_distance)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are no stats' do
|
||||||
|
it 'returns year distance' do
|
||||||
|
expect(subject).to eq(expected_distance)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,68 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe User, type: :model do
|
RSpec.describe User, type: :model do
|
||||||
it { is_expected.to have_many(:imports).dependent(:destroy) }
|
describe 'associations' do
|
||||||
it { is_expected.to have_many(:points).through(:imports) }
|
it { is_expected.to have_many(:imports).dependent(:destroy) }
|
||||||
it { is_expected.to have_many(:stats) }
|
it { is_expected.to have_many(:points).through(:imports) }
|
||||||
|
it { is_expected.to have_many(:stats) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'methods' do
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
|
||||||
|
xdescribe '#export_data' do
|
||||||
|
subject { user.export_data }
|
||||||
|
|
||||||
|
let(:import) { create(:import, user: user) }
|
||||||
|
let(:point) { create(:point, import: import) }
|
||||||
|
|
||||||
|
it 'returns json' do
|
||||||
|
expect(subject).to include(user.email)
|
||||||
|
expect(subject).to include('dawarich-export')
|
||||||
|
expect(subject).to include(point.attributes.except('raw_data', 'id', 'created_at', 'updated_at', 'country', 'city', 'import_id').to_json)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#total_km' do
|
||||||
|
subject { user.total_km }
|
||||||
|
|
||||||
|
let!(:stat_1) { create(:stat, user: user, distance: 10) }
|
||||||
|
let!(:stat_2) { create(:stat, user: user, distance: 20) }
|
||||||
|
|
||||||
|
it 'returns sum of distances' do
|
||||||
|
expect(subject).to eq(30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#total_countries' do
|
||||||
|
subject { user.total_countries }
|
||||||
|
|
||||||
|
let!(:stat) { create(:stat, user: user, toponyms: [{ 'country' => 'Country' }]) }
|
||||||
|
|
||||||
|
it 'returns number of countries' do
|
||||||
|
expect(subject).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#total_cities' do
|
||||||
|
subject { user.total_cities }
|
||||||
|
|
||||||
|
let!(:stat) { create(:stat, user: user, toponyms: [{ 'city' => 'City' }]) }
|
||||||
|
|
||||||
|
it 'returns number of cities' do
|
||||||
|
expect(subject).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#total_reverse_geocoded' do
|
||||||
|
subject { user.total_reverse_geocoded }
|
||||||
|
|
||||||
|
let(:import) { create(:import, user: user) }
|
||||||
|
let!(:point) { create(:point, country: 'Country', city: 'City', import: import) }
|
||||||
|
|
||||||
|
it 'returns number of reverse geocoded points' do
|
||||||
|
expect(subject).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue