Fix failing specs

This commit is contained in:
Eugene Burmakin 2025-09-19 19:55:27 +02:00
parent 2bd0390d1a
commit 584daadb5c
10 changed files with 57 additions and 58 deletions

View file

@ -8,8 +8,10 @@ class Api::V1::Maps::HexagonsController < ApiController
result = Maps::HexagonRequestHandler.new(
params: params,
user: current_api_user,
context: context
user: context[:user] || current_api_user,
stat: context[:stat],
start_date: context[:start_date],
end_date: context[:end_date]
).call
render json: result

View file

@ -72,7 +72,11 @@ module Maps
if param.match?(/^\d+$/)
param.to_i
else
Time.zone.parse(param).to_i
parsed_time = Time.zone.parse(param)
if parsed_time.nil?
raise ArgumentError, "Invalid date format: #{param}"
end
parsed_time.to_i
end
when Integer
param

View file

@ -58,15 +58,11 @@ module Maps
{
'type' => 'Feature',
'id' => index + 1,
'geometry' => generate_hexagon_geometry_from_h3(h3_index),
'geometry' => Maps::HexagonPolygonGenerator.new(h3_index:).call,
'properties' => build_hexagon_properties(index, count, earliest, latest)
}
end
def generate_hexagon_geometry_from_h3(h3_index)
Maps::HexagonPolygonGenerator.new(h3_index: h3_index).call
end
def build_hexagon_properties(index, count, earliest, latest)
{
'hex_id' => index + 1,

View file

@ -2,30 +2,16 @@
module Maps
class HexagonPolygonGenerator
def initialize(center_lng: nil, center_lat: nil, h3_resolution: 5, h3_index: nil)
@center_lng = center_lng
@center_lat = center_lat
@h3_resolution = h3_resolution
def initialize(h3_index:)
@h3_index = h3_index
end
def call
generate_h3_hexagon_polygon
end
private
attr_reader :center_lng, :center_lat, :h3_resolution, :h3_index
def generate_h3_hexagon_polygon
# Convert coordinates to H3 format [lat, lng]
coordinates = [center_lat, center_lng]
# Get H3 index for these coordinates at specified resolution
h3_index = H3.from_geo_coordinates(coordinates, h3_resolution)
# Parse H3 index from hex string if needed
index = h3_index.is_a?(String) ? h3_index.to_i(16) : h3_index
# Get the boundary coordinates for this H3 hexagon
boundary_coordinates = H3.to_boundary(h3_index)
boundary_coordinates = H3.to_boundary(index)
# Convert to GeoJSON polygon format (lng, lat)
polygon_coordinates = boundary_coordinates.map { [_2, _1] }
@ -38,5 +24,9 @@ module Maps
'coordinates' => [polygon_coordinates]
}
end
private
attr_reader :h3_index
end
end

View file

@ -1,8 +1,12 @@
# frozen_string_literal: true
class AddH3HexIdsToStats < ActiveRecord::Migration[8.0]
disable_ddl_transaction!
def change
add_column :stats, :h3_hex_ids, :jsonb, default: {}
add_index :stats, :h3_hex_ids, using: :gin, where: "(h3_hex_ids IS NOT NULL AND h3_hex_ids != '{}'::jsonb)"
add_index :stats, :h3_hex_ids, using: :gin,
where: "(h3_hex_ids IS NOT NULL AND h3_hex_ids != '{}'::jsonb)",
algorithm: :concurrently
end
end

4
db/schema.rb generated
View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_09_10_224714) do
ActiveRecord::Schema[8.0].define(version: 2025_09_18_215512) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
enable_extension "postgis"
@ -222,7 +222,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_09_10_224714) do
t.jsonb "daily_distance", default: {}
t.jsonb "sharing_settings", default: {}
t.uuid "sharing_uuid"
t.jsonb "h3_hex_ids", default: {}
t.index ["distance"], name: "index_stats_on_distance"
t.index ["h3_hex_ids"], name: "index_stats_on_h3_hex_ids", where: "((h3_hex_ids IS NOT NULL) AND (h3_hex_ids <> '{}'::jsonb))", using: :gin
t.index ["month"], name: "index_stats_on_month"
t.index ["sharing_uuid"], name: "index_stats_on_sharing_uuid", unique: true
t.index ["user_id"], name: "index_stats_on_user_id"

View file

@ -172,14 +172,14 @@ RSpec.describe 'Api::V1::Maps::Hexagons', type: :request do
context 'with pre-calculated hexagon centers' do
let(:pre_calculated_centers) do
[
[-74.0, 40.7, 1_717_200_000, 1_717_203_600], # lng, lat, earliest, latest timestamps
[-74.01, 40.71, 1_717_210_000, 1_717_213_600],
[-74.02, 40.72, 1_717_220_000, 1_717_223_600]
]
{
'8a1fb46622dffff' => [5, 1_717_200_000, 1_717_203_600], # count, earliest, latest timestamps
'8a1fb46622e7fff' => [3, 1_717_210_000, 1_717_213_600],
'8a1fb46632dffff' => [8, 1_717_220_000, 1_717_223_600]
}
end
let(:stat) do
create(:stat, :with_sharing_enabled, user:, year: 2024, month: 6, hexagon_centers: pre_calculated_centers)
create(:stat, :with_sharing_enabled, user:, year: 2024, month: 6, h3_hex_ids: pre_calculated_centers)
end
it 'uses pre-calculated hexagon centers instead of on-the-fly calculation' do
@ -228,7 +228,7 @@ RSpec.describe 'Api::V1::Maps::Hexagons', type: :request do
context 'with legacy area_too_large hexagon data' do
let(:stat) do
create(:stat, :with_sharing_enabled, user:, year: 2024, month: 6,
hexagon_centers: { 'area_too_large' => true })
h3_hex_ids: { 'area_too_large' => true })
end
before do
@ -246,7 +246,7 @@ RSpec.describe 'Api::V1::Maps::Hexagons', type: :request do
get '/api/v1/maps/hexagons', params: uuid_params
# The endpoint should handle the legacy data gracefully and not crash
# We're primarily testing that the condition `@stat&.hexagon_centers&.dig('area_too_large')` is covered
# We're primarily testing that the condition `@stat&.h3_hex_ids&.dig('area_too_large')` is covered
expect([200, 400, 500]).to include(response.status)
end
end

View file

@ -11,13 +11,13 @@ RSpec.describe Maps::HexagonCenterManager do
context 'with pre-calculated hexagon centers' do
let(:pre_calculated_centers) do
[
[-74.0, 40.7, 1_717_200_000, 1_717_203_600], # lng, lat, earliest, latest timestamps
[-74.01, 40.71, 1_717_210_000, 1_717_213_600],
[-74.02, 40.72, 1_717_220_000, 1_717_223_600]
]
{
'8a1fb46622dffff' => [5, 1_717_200_000, 1_717_203_600], # count, earliest, latest timestamps
'8a1fb46622e7fff' => [3, 1_717_210_000, 1_717_213_600],
'8a1fb46632dffff' => [8, 1_717_220_000, 1_717_223_600]
}
end
let(:stat) { create(:stat, user:, year: 2024, month: 6, hexagon_centers: pre_calculated_centers) }
let(:stat) { create(:stat, user:, year: 2024, month: 6, h3_hex_ids: pre_calculated_centers) }
it 'returns success with pre-calculated data' do
result = manage_centers
@ -51,7 +51,7 @@ RSpec.describe Maps::HexagonCenterManager do
context 'with legacy area_too_large flag' do
let(:stat) do
create(:stat, user:, year: 2024, month: 6, hexagon_centers: { 'area_too_large' => true })
create(:stat, user:, year: 2024, month: 6, h3_hex_ids: { 'area_too_large' => true })
end
before do
@ -69,7 +69,7 @@ RSpec.describe Maps::HexagonCenterManager do
end
it 'recalculates and updates the stat' do
expect(stat).to receive(:update).with(hexagon_centers: new_centers)
expect(stat).to receive(:update).with(h3_hex_ids: new_centers)
result = manage_centers
@ -105,7 +105,7 @@ RSpec.describe Maps::HexagonCenterManager do
end
context 'with stat but no hexagon_centers' do
let(:stat) { create(:stat, user:, year: 2024, month: 6, hexagon_centers: nil) }
let(:stat) { create(:stat, user:, year: 2024, month: 6, h3_hex_ids: nil) }
it 'returns nil' do
expect(manage_centers).to be_nil
@ -113,7 +113,7 @@ RSpec.describe Maps::HexagonCenterManager do
end
context 'with empty hexagon_centers' do
let(:stat) { create(:stat, user:, year: 2024, month: 6, hexagon_centers: []) }
let(:stat) { create(:stat, user:, year: 2024, month: 6, h3_hex_ids: []) }
it 'returns nil' do
expect(manage_centers).to be_nil

View file

@ -43,14 +43,14 @@ RSpec.describe Maps::HexagonRequestHandler do
context 'with public sharing UUID and pre-calculated centers' do
let(:pre_calculated_centers) do
[
[-74.0, 40.7, 1_717_200_000, 1_717_203_600],
[-74.01, 40.71, 1_717_210_000, 1_717_213_600]
]
{
'8a1fb46622dffff' => [5, 1_717_200_000, 1_717_203_600],
'8a1fb46622e7fff' => [3, 1_717_210_000, 1_717_213_600]
}
end
let(:stat) do
create(:stat, :with_sharing_enabled, user:, year: 2024, month: 6,
hexagon_centers: pre_calculated_centers)
h3_hex_ids: pre_calculated_centers)
end
let(:params) do
ActionController::Parameters.new(
@ -101,7 +101,7 @@ RSpec.describe Maps::HexagonRequestHandler do
context 'with legacy area_too_large that can be recalculated' do
let(:stat) do
create(:stat, :with_sharing_enabled, user:, year: 2024, month: 6,
hexagon_centers: { 'area_too_large' => true })
h3_hex_ids: { 'area_too_large' => true })
end
let(:params) do
ActionController::Parameters.new(
@ -129,7 +129,7 @@ RSpec.describe Maps::HexagonRequestHandler do
expect(result['metadata']['pre_calculated']).to be true
# Verify that the stat was updated with new centers (reload to check persistence)
expect(stat.reload.hexagon_centers).to eq([[-74.0, 40.7, 1_717_200_000, 1_717_203_600]])
expect(stat.reload.h3_hex_ids).to eq([[-74.0, 40.7, 1_717_200_000, 1_717_203_600]])
end
end
end

View file

@ -162,14 +162,15 @@ RSpec.describe Stats::CalculateMonth do
expect(total_points).to eq(2)
end
context 'when H3 raises an error' do
before do
allow(H3).to receive(:from_geo_coordinates).and_raise(StandardError, 'H3 error')
end
it 'raises PostGISError' do
expect { calculate_hexagons }.to raise_error(Stats::CalculateMonth::PostGISError, /Failed to calculate H3 hexagon centers/)
expect do
calculate_hexagons
end.to raise_error(Stats::CalculateMonth::PostGISError, /Failed to calculate H3 hexagon centers/)
end
it 'reports the exception' do
@ -185,7 +186,7 @@ RSpec.describe Stats::CalculateMonth do
it 'handles string timestamps' do
result = service.send(:parse_date_parameter, '1640995200')
expect(result).to eq(1640995200)
expect(result).to eq(1_640_995_200)
end
it 'handles ISO date strings' do
@ -194,8 +195,8 @@ RSpec.describe Stats::CalculateMonth do
end
it 'handles integer timestamps' do
result = service.send(:parse_date_parameter, 1640995200)
expect(result).to eq(1640995200)
result = service.send(:parse_date_parameter, 1_640_995_200)
expect(result).to eq(1_640_995_200)
end
it 'handles edge case gracefully' do