mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 17:21:38 -05:00
Add specs for hexagon API and public sharing; remove debug logs
This commit is contained in:
parent
5ff35136f2
commit
34e71b5d17
8 changed files with 465 additions and 27 deletions
|
|
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||
|
||||
- A cron job to generate daily tracks for users with new points since their last track generation. Being run every 4 hours.
|
||||
- A new month stat page, featuring insights on how user's month went: distance traveled, active days, countries visited and more.
|
||||
- Month stat page can now be shared via public link. User can limit access to the page by sharing period: 1/12/24 hours or permanent.
|
||||
|
||||
## Changed
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@ class Api::V1::Maps::HexagonsController < ApiController
|
|||
before_action :set_user_and_dates
|
||||
|
||||
def index
|
||||
Rails.logger.debug "Hexagon API request params: #{params.inspect}"
|
||||
Rails.logger.debug "Hexagon params: #{hexagon_params}"
|
||||
|
||||
service = Maps::HexagonGrid.new(hexagon_params)
|
||||
result = service.call
|
||||
|
||||
|
|
@ -16,13 +13,11 @@ class Api::V1::Maps::HexagonsController < ApiController
|
|||
render json: result
|
||||
rescue Maps::HexagonGrid::BoundingBoxTooLargeError,
|
||||
Maps::HexagonGrid::InvalidCoordinatesError => e
|
||||
Rails.logger.error "Hexagon validation error: #{e.message}"
|
||||
render json: { error: e.message }, status: :bad_request
|
||||
rescue Maps::HexagonGrid::PostGISError => e
|
||||
Rails.logger.error "Hexagon PostGIS error: #{e.message}"
|
||||
render json: { error: e.message }, status: :internal_server_error
|
||||
rescue StandardError => e
|
||||
handle_service_error(e)
|
||||
rescue StandardError => _e
|
||||
handle_service_error
|
||||
end
|
||||
|
||||
def bounds
|
||||
|
|
@ -82,21 +77,17 @@ class Api::V1::Maps::HexagonsController < ApiController
|
|||
end
|
||||
|
||||
def set_public_sharing_context
|
||||
Rails.logger.debug "Public sharing request with UUID: #{params[:uuid]}"
|
||||
@stat = Stat.find_by(sharing_uuid: params[:uuid])
|
||||
|
||||
unless @stat&.public_accessible?
|
||||
Rails.logger.error "Stat not found or not public accessible for UUID: #{params[:uuid]}"
|
||||
return render json: {
|
||||
render json: {
|
||||
error: 'Shared stats not found or no longer available'
|
||||
}, status: :not_found
|
||||
}, status: :not_found and return
|
||||
end
|
||||
|
||||
@target_user = @stat.user
|
||||
@start_date = Date.new(@stat.year, @stat.month, 1).beginning_of_day
|
||||
@end_date = @start_date.end_of_month.end_of_day
|
||||
|
||||
Rails.logger.debug "Found stat for user #{@target_user.id}, date range: #{@start_date} to #{@end_date}"
|
||||
end
|
||||
|
||||
def set_authenticated_context
|
||||
|
|
@ -105,8 +96,7 @@ class Api::V1::Maps::HexagonsController < ApiController
|
|||
@end_date = params[:end_date]
|
||||
end
|
||||
|
||||
def handle_service_error(error)
|
||||
Rails.logger.error "Hexagon generation error: #{error.message}\n#{error.backtrace.join("\n")}"
|
||||
def handle_service_error
|
||||
render json: { error: 'Failed to generate hexagon grid' }, status: :internal_server_error
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@
|
|||
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
|
||||
</form>
|
||||
|
||||
<h3 class="font-bold text-lg mb-4">🔗 Sharing Settings</h3>
|
||||
<h3 class="font-bold text-lg mb-4 flex items-center gap-2">
|
||||
<%= icon 'link' %> Sharing Settings
|
||||
</h3>
|
||||
|
||||
<div data-controller="sharing-modal"
|
||||
data-sharing-modal-url-value="<%= sharing_stats_path(year: @year, month: @month) %>">
|
||||
|
|
@ -16,7 +18,7 @@
|
|||
<span class="label-text font-medium">Enable public access</span>
|
||||
<input type="checkbox"
|
||||
name="enabled"
|
||||
<%= 'checked' if @stat&.sharing_enabled? %>
|
||||
<%= 'checked' if @stat.sharing_enabled? %>
|
||||
class="toggle toggle-primary"
|
||||
data-action="change->sharing-modal#toggleSharing"
|
||||
data-sharing-modal-target="enableToggle" />
|
||||
|
|
@ -28,7 +30,7 @@
|
|||
|
||||
<!-- Expiration Settings (shown when enabled) -->
|
||||
<div data-sharing-modal-target="expirationSettings"
|
||||
class="<%= 'hidden' unless @stat&.sharing_enabled? %>">
|
||||
class="<%= 'hidden' unless @stat.sharing_enabled? %>">
|
||||
|
||||
<div class="form-control mb-4">
|
||||
<label class="label">
|
||||
|
|
@ -57,11 +59,11 @@
|
|||
readonly
|
||||
class="input input-bordered join-item flex-1"
|
||||
data-sharing-modal-target="sharingLink"
|
||||
value="<%= @stat&.sharing_enabled? ? public_stat_url(@stat.sharing_uuid) : '' %>" />
|
||||
value="<%= @stat.sharing_enabled? ? public_stat_url(@stat.sharing_uuid) : '' %>" />
|
||||
<button type="button"
|
||||
class="btn btn-outline join-item"
|
||||
data-action="click->sharing-modal#copyLink">
|
||||
📋 Copy
|
||||
<%= icon 'copy' %> Copy
|
||||
</button>
|
||||
</div>
|
||||
<div class="label">
|
||||
|
|
@ -73,14 +75,11 @@
|
|||
|
||||
<!-- Privacy Notice (always visible) -->
|
||||
<div class="alert alert-info mb-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
<%= icon 'info' %>
|
||||
<div>
|
||||
<h3 class="font-bold">Privacy Protection</h3>
|
||||
<div class="text-sm">
|
||||
• Exact coordinates are hidden (approximate locations only)<br>
|
||||
• Map interaction is disabled<br>
|
||||
• Exact coordinates are hidden<br>
|
||||
• Personal information is not included
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ FactoryBot.define do
|
|||
month { 1 }
|
||||
distance { 1000 } # 1 km
|
||||
user
|
||||
sharing_settings { {} }
|
||||
sharing_uuid { SecureRandom.uuid }
|
||||
toponyms do
|
||||
[
|
||||
{
|
||||
|
|
@ -16,5 +18,31 @@ FactoryBot.define do
|
|||
}, { 'cities' => [], 'country' => nil }
|
||||
]
|
||||
end
|
||||
|
||||
trait :with_sharing_enabled do
|
||||
after(:create) do |stat, evaluator|
|
||||
stat.enable_sharing!(expiration: 'permanent')
|
||||
end
|
||||
end
|
||||
|
||||
trait :with_sharing_disabled do
|
||||
sharing_settings do
|
||||
{
|
||||
'enabled' => false,
|
||||
'expiration' => nil,
|
||||
'expires_at' => nil
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
trait :with_sharing_expired do
|
||||
sharing_settings do
|
||||
{
|
||||
'enabled' => true,
|
||||
'expiration' => '1h',
|
||||
'expires_at' => 1.hour.ago.iso8601
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -111,9 +111,9 @@ RSpec.describe Users::MailerSendingJob, type: :job do
|
|||
it 'raises ActiveRecord::RecordNotFound' do
|
||||
user.destroy
|
||||
|
||||
expect {
|
||||
expect do
|
||||
described_class.perform_now(user.id, 'welcome')
|
||||
}.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -329,5 +329,11 @@ RSpec.describe User, type: :model do
|
|||
expect { user.export_data }.to have_enqueued_job(Users::ExportDataJob).with(user.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#timezone' do
|
||||
it 'returns the app timezone' do
|
||||
expect(user.timezone).to eq(Time.zone.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
267
spec/requests/api/v1/maps/hexagons_spec.rb
Normal file
267
spec/requests/api/v1/maps/hexagons_spec.rb
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Api::V1::Maps::Hexagons', type: :request do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_request(:any, 'https://api.github.com/repos/Freika/dawarich/tags')
|
||||
.to_return(status: 200, body: '[{"name": "1.0.0"}]', headers: {})
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/maps/hexagons' do
|
||||
let(:valid_params) do
|
||||
{
|
||||
min_lon: -74.1,
|
||||
min_lat: 40.6,
|
||||
max_lon: -73.9,
|
||||
max_lat: 40.8,
|
||||
hex_size: 1000,
|
||||
start_date: '2024-06-01T00:00:00Z',
|
||||
end_date: '2024-06-30T23:59:59Z'
|
||||
}
|
||||
end
|
||||
|
||||
context 'with valid API key authentication' do
|
||||
let(:headers) { { 'Authorization' => "Bearer #{user.api_key}" } }
|
||||
|
||||
before do
|
||||
# Create test points within the date range and bounding box
|
||||
10.times do |i|
|
||||
create(:point,
|
||||
user:,
|
||||
latitude: 40.7 + (i * 0.001), # Slightly different coordinates
|
||||
longitude: -74.0 + (i * 0.001),
|
||||
timestamp: Time.new(2024, 6, 15, 12, i).to_i) # Different times
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns hexagon data successfully' do
|
||||
get '/api/v1/maps/hexagons', params: valid_params, headers: headers
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
|
||||
json_response = JSON.parse(response.body)
|
||||
expect(json_response).to have_key('type')
|
||||
expect(json_response['type']).to eq('FeatureCollection')
|
||||
expect(json_response).to have_key('features')
|
||||
expect(json_response['features']).to be_an(Array)
|
||||
end
|
||||
|
||||
it 'requires all bbox parameters' do
|
||||
incomplete_params = valid_params.except(:min_lon)
|
||||
|
||||
get '/api/v1/maps/hexagons', params: incomplete_params, headers: headers
|
||||
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
|
||||
json_response = JSON.parse(response.body)
|
||||
expect(json_response['error']).to include('Missing required parameters')
|
||||
expect(json_response['error']).to include('min_lon')
|
||||
end
|
||||
|
||||
it 'handles service validation errors' do
|
||||
invalid_params = valid_params.merge(min_lon: 200) # Invalid longitude
|
||||
|
||||
get '/api/v1/maps/hexagons', params: invalid_params, headers: headers
|
||||
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
end
|
||||
|
||||
it 'uses custom hex_size when provided' do
|
||||
custom_params = valid_params.merge(hex_size: 500)
|
||||
|
||||
get '/api/v1/maps/hexagons', params: custom_params, headers: headers
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with public sharing UUID' do
|
||||
let(:stat) { create(:stat, :with_sharing_enabled, user:, year: 2024, month: 6) }
|
||||
let(:uuid_params) { valid_params.merge(uuid: stat.sharing_uuid) }
|
||||
|
||||
before do
|
||||
# Create test points within the stat's month
|
||||
15.times do |i|
|
||||
create(:point,
|
||||
user:,
|
||||
latitude: 40.7 + (i * 0.002),
|
||||
longitude: -74.0 + (i * 0.002),
|
||||
timestamp: Time.new(2024, 6, 20, 10, i).to_i)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns hexagon data without API key' do
|
||||
get '/api/v1/maps/hexagons', params: uuid_params
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
|
||||
json_response = JSON.parse(response.body)
|
||||
expect(json_response).to have_key('type')
|
||||
expect(json_response['type']).to eq('FeatureCollection')
|
||||
expect(json_response).to have_key('features')
|
||||
end
|
||||
|
||||
it 'uses stat date range automatically' do
|
||||
# Points outside the stat's month should not be included
|
||||
5.times do |i|
|
||||
create(:point,
|
||||
user:,
|
||||
latitude: 40.7 + (i * 0.003),
|
||||
longitude: -74.0 + (i * 0.003),
|
||||
timestamp: Time.new(2024, 7, 1, 8, i).to_i) # July points
|
||||
end
|
||||
|
||||
get '/api/v1/maps/hexagons', params: uuid_params
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
|
||||
context 'with invalid sharing UUID' do
|
||||
it 'returns not found' do
|
||||
invalid_uuid_params = valid_params.merge(uuid: 'invalid-uuid')
|
||||
|
||||
get '/api/v1/maps/hexagons', params: invalid_uuid_params
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
|
||||
json_response = JSON.parse(response.body)
|
||||
expect(json_response['error']).to eq('Shared stats not found or no longer available')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with expired sharing' do
|
||||
let(:stat) { create(:stat, :with_sharing_expired, user:, year: 2024, month: 6) }
|
||||
|
||||
it 'returns not found' do
|
||||
get '/api/v1/maps/hexagons', params: uuid_params
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
|
||||
json_response = JSON.parse(response.body)
|
||||
expect(json_response['error']).to eq('Shared stats not found or no longer available')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with disabled sharing' do
|
||||
let(:stat) { create(:stat, :with_sharing_disabled, user:, year: 2024, month: 6) }
|
||||
|
||||
it 'returns not found' do
|
||||
get '/api/v1/maps/hexagons', params: uuid_params
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
|
||||
json_response = JSON.parse(response.body)
|
||||
expect(json_response['error']).to eq('Shared stats not found or no longer available')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without authentication' do
|
||||
it 'returns unauthorized' do
|
||||
get '/api/v1/maps/hexagons', params: valid_params
|
||||
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid API key' do
|
||||
let(:headers) { { 'Authorization' => 'Bearer invalid-key' } }
|
||||
|
||||
it 'returns unauthorized' do
|
||||
get '/api/v1/maps/hexagons', params: valid_params, headers: headers
|
||||
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/maps/hexagons/bounds' do
|
||||
context 'with valid API key authentication' do
|
||||
let(:headers) { { 'Authorization' => "Bearer #{user.api_key}" } }
|
||||
let(:date_params) do
|
||||
{
|
||||
start_date: Time.new(2024, 6, 1).to_i,
|
||||
end_date: Time.new(2024, 6, 30, 23, 59, 59).to_i
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
# Create test points within the date range
|
||||
create(:point, user:, latitude: 40.6, longitude: -74.1, timestamp: Time.new(2024, 6, 1, 12, 0).to_i)
|
||||
create(:point, user:, latitude: 40.8, longitude: -73.9, timestamp: Time.new(2024, 6, 30, 15, 0).to_i)
|
||||
create(:point, user:, latitude: 40.7, longitude: -74.0, timestamp: Time.new(2024, 6, 15, 10, 0).to_i)
|
||||
end
|
||||
|
||||
it 'returns bounding box for user data' do
|
||||
get '/api/v1/maps/hexagons/bounds', params: date_params, headers: headers
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
|
||||
json_response = JSON.parse(response.body)
|
||||
expect(json_response).to include('min_lat', 'max_lat', 'min_lng', 'max_lng', 'point_count')
|
||||
expect(json_response['min_lat']).to eq(40.6)
|
||||
expect(json_response['max_lat']).to eq(40.8)
|
||||
expect(json_response['min_lng']).to eq(-74.1)
|
||||
expect(json_response['max_lng']).to eq(-73.9)
|
||||
expect(json_response['point_count']).to eq(3)
|
||||
end
|
||||
|
||||
it 'returns not found when no points exist in date range' do
|
||||
get '/api/v1/maps/hexagons/bounds',
|
||||
params: { start_date: '2023-01-01T00:00:00Z', end_date: '2023-01-31T23:59:59Z' },
|
||||
headers: headers
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
|
||||
json_response = JSON.parse(response.body)
|
||||
expect(json_response['error']).to eq('No data found for the specified date range')
|
||||
expect(json_response['point_count']).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with public sharing UUID' do
|
||||
let(:stat) { create(:stat, :with_sharing_enabled, user:, year: 2024, month: 6) }
|
||||
|
||||
before do
|
||||
# Create test points within the stat's month
|
||||
create(:point, user:, latitude: 41.0, longitude: -74.5, timestamp: Time.new(2024, 6, 5, 9, 0).to_i)
|
||||
create(:point, user:, latitude: 41.2, longitude: -74.2, timestamp: Time.new(2024, 6, 25, 14, 0).to_i)
|
||||
end
|
||||
|
||||
it 'returns bounds for the shared stat period' do
|
||||
get '/api/v1/maps/hexagons/bounds', params: { uuid: stat.sharing_uuid }
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
|
||||
json_response = JSON.parse(response.body)
|
||||
expect(json_response).to include('min_lat', 'max_lat', 'min_lng', 'max_lng', 'point_count')
|
||||
expect(json_response['min_lat']).to eq(41.0)
|
||||
expect(json_response['max_lat']).to eq(41.2)
|
||||
expect(json_response['point_count']).to eq(2)
|
||||
end
|
||||
|
||||
context 'with invalid sharing UUID' do
|
||||
it 'returns not found' do
|
||||
get '/api/v1/maps/hexagons/bounds', params: { uuid: 'invalid-uuid' }
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
|
||||
json_response = JSON.parse(response.body)
|
||||
expect(json_response['error']).to eq('Shared stats not found or no longer available')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without authentication' do
|
||||
it 'returns unauthorized' do
|
||||
get '/api/v1/maps/hexagons/bounds',
|
||||
params: { start_date: '2024-06-01T00:00:00Z', end_date: '2024-06-30T23:59:59Z' }
|
||||
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -111,4 +111,151 @@ RSpec.describe '/stats', type: :request do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'public sharing' do
|
||||
let(:user) { create(:user) }
|
||||
let(:stat) { create(:stat, :with_sharing_enabled, user:, year: 2024, month: 6) }
|
||||
|
||||
describe 'GET /public/:uuid' do
|
||||
context 'with valid sharing UUID' do
|
||||
before do
|
||||
# Create some test points for data bounds calculation
|
||||
create_list(:point, 5, user:, timestamp: Time.new(2024, 6, 15).to_i)
|
||||
end
|
||||
|
||||
it 'renders the public month view' do
|
||||
get public_stat_url(stat.sharing_uuid)
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.body).to include('Monthly Digest')
|
||||
expect(response.body).to include('June 2024')
|
||||
end
|
||||
|
||||
it 'includes required content in response' do
|
||||
get public_stat_url(stat.sharing_uuid)
|
||||
|
||||
expect(response.body).to include('June 2024')
|
||||
expect(response.body).to include('Monthly Digest')
|
||||
expect(response.body).to include('data-public-stat-map-uuid-value')
|
||||
expect(response.body).to include(stat.sharing_uuid)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid sharing UUID' do
|
||||
it 'redirects to root with alert' do
|
||||
get public_stat_url('invalid-uuid')
|
||||
|
||||
expect(response).to redirect_to(root_path)
|
||||
expect(flash[:alert]).to eq('Shared stats not found or no longer available')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with expired sharing' do
|
||||
let(:stat) { create(:stat, :with_sharing_expired, user:, year: 2024, month: 6) }
|
||||
|
||||
it 'redirects to root with alert' do
|
||||
get public_stat_url(stat.sharing_uuid)
|
||||
|
||||
expect(response).to redirect_to(root_path)
|
||||
expect(flash[:alert]).to eq('Shared stats not found or no longer available')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with disabled sharing' do
|
||||
let(:stat) { create(:stat, :with_sharing_disabled, user:, year: 2024, month: 6) }
|
||||
|
||||
it 'redirects to root with alert' do
|
||||
get public_stat_url(stat.sharing_uuid)
|
||||
|
||||
expect(response).to redirect_to(root_path)
|
||||
expect(flash[:alert]).to eq('Shared stats not found or no longer available')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when stat has no points' do
|
||||
it 'renders successfully' do
|
||||
get public_stat_url(stat.sharing_uuid)
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.body).to include('Monthly Digest')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH /update_sharing' do
|
||||
context 'when user is signed in' do
|
||||
let!(:stat_to_share) { create(:stat, user:, year: 2024, month: 6) }
|
||||
|
||||
before { sign_in user }
|
||||
|
||||
context 'enabling sharing' do
|
||||
it 'enables sharing and returns success' do
|
||||
patch sharing_stats_path(year: 2024, month: 6),
|
||||
params: { enabled: '1' },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
|
||||
json_response = JSON.parse(response.body)
|
||||
expect(json_response['success']).to be(true)
|
||||
expect(json_response['sharing_url']).to be_present
|
||||
expect(json_response['message']).to eq('Sharing enabled successfully')
|
||||
|
||||
stat_to_share.reload
|
||||
expect(stat_to_share.sharing_enabled?).to be(true)
|
||||
expect(stat_to_share.sharing_uuid).to be_present
|
||||
end
|
||||
|
||||
it 'sets custom expiration when provided' do
|
||||
patch sharing_stats_path(year: 2024, month: 6),
|
||||
params: { enabled: '1', expiration: '1_week' },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
stat_to_share.reload
|
||||
expect(stat_to_share.sharing_enabled?).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'disabling sharing' do
|
||||
let!(:enabled_stat) { create(:stat, :with_sharing_enabled, user:, year: 2024, month: 7) }
|
||||
|
||||
it 'disables sharing and returns success' do
|
||||
patch sharing_stats_path(year: 2024, month: 7),
|
||||
params: { enabled: '0' },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
|
||||
json_response = JSON.parse(response.body)
|
||||
expect(json_response['success']).to be(true)
|
||||
expect(json_response['message']).to eq('Sharing disabled successfully')
|
||||
|
||||
enabled_stat.reload
|
||||
expect(enabled_stat.sharing_enabled?).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when stat does not exist' do
|
||||
it 'returns not found' do
|
||||
patch sharing_stats_path(year: 2024, month: 12),
|
||||
params: { enabled: '1' },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not signed in' do
|
||||
it 'returns unauthorized' do
|
||||
patch sharing_stats_path(year: 2024, month: 6),
|
||||
params: { enabled: '1' },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue