Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 185 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Testing Guide

This guide explains how to test the IP geocoding configuration feature.

## Running Automated Tests

### Run All Tests
```bash
bundle exec rake test
```

### Run Specific Test Files
```bash
# Test the generator with the flag
bundle exec ruby -Itest test/lib/generators/rails_url_shortener/rails_url_shortener_generator_test.rb

# Test the Visit model (includes IP geocoding tests)
bundle exec ruby -Itest test/models/rails_url_shortener/visit_test.rb

# Test the IP crawler job
bundle exec ruby -Itest test/jobs/rails_url_shortener/ip_crawler_job_test.rb
```

## Manual Testing

### 1. Test Generator Without Flag (Default - IP Geocoding Disabled)

```bash
# In a temporary Rails app or the test dummy app
cd test/dummy

# Run generator without flag
rails generate rails_url_shortener

# Verify the initializer has save_ip_geocode = false
cat config/initializers/rails_url_shortener.rb | grep save_ip_geocode
# Should show: RailsUrlShortener.save_ip_geocode = false

# Check that migration was skipped (ipgeos table should not exist)
rails db:migrate:status | grep ipgeo
# Should show the migration was skipped or not run

# Try to access the ipgeos table
rails console
> RailsUrlShortener::Ipgeo.table_exists?
# Should return: false (if migration was skipped)
```

### 2. Test Generator With Flag (IP Geocoding Enabled)

```bash
# Run generator with the flag
rails generate rails_url_shortener --enable-ip-geocode

# Verify the initializer has save_ip_geocode = true
cat config/initializers/rails_url_shortener.rb | grep save_ip_geocode
# Should show: RailsUrlShortener.save_ip_geocode = true

# Check that migration ran
rails db:migrate:status | grep ipgeo
# Should show the migration was executed

# Verify the ipgeos table exists
rails console
> RailsUrlShortener::Ipgeo.table_exists?
# Should return: true

> ActiveRecord::Base.connection.column_exists?(:rails_url_shortener_visits, :ipgeo_id)
# Should return: true
```

### 3. Test Runtime Behavior - IP Geocoding Disabled (Default)

```bash
rails console

# Verify default configuration
> RailsUrlShortener.save_ip_geocode
# Should return: false

# Create a visit (simulating a URL redirect)
> url = RailsUrlShortener::Url.generate('https://example.com')
> request = ActionDispatch::TestRequest.create
> request.remote_addr = '192.168.1.1'
> visit = RailsUrlShortener::Visit.parse_and_save(url, request)

# Verify visit was created but IpCrawlerJob was NOT enqueued
> visit.persisted?
# Should return: true

> ActiveJob::Base.queue_adapter.enqueued_jobs.select { |j| j[:job] == RailsUrlShortener::IpCrawlerJob }.count
# Should return: 0 (no job enqueued)

> visit.ipgeo
# Should return: nil
```

### 4. Test Runtime Behavior - IP Geocoding Enabled

```bash
rails console

# Enable IP geocoding
> RailsUrlShortener.save_ip_geocode = true

# Create a visit
> url = RailsUrlShortener::Url.generate('https://example.com')
> request = ActionDispatch::TestRequest.create
> request.remote_addr = '192.168.1.1'
> request.user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'

# Use ActiveJob test adapter to catch enqueued jobs
> ActiveJob::Base.queue_adapter = ActiveJob::QueueAdapters::TestAdapter.new

> visit = RailsUrlShortener::Visit.parse_and_save(url, request)

# Verify visit was created AND IpCrawlerJob was enqueued
> visit.persisted?
# Should return: true

> ActiveJob::Base.queue_adapter.enqueued_jobs.select { |j| j[:job] == RailsUrlShortener::IpCrawlerJob }.count
# Should return: 1 (job was enqueued)

# Process the job (if testing in test environment with VCR cassettes)
# > RailsUrlShortener::IpCrawlerJob.perform_now(visit)
```

### 5. Test Migration Behavior Directly

```bash
# Test that migration skips by default
ENV['ENABLE_IP_GEOCODE'] = nil
rails db:migrate:down VERSION=20220418184647 2>/dev/null || true
rails db:migrate:up VERSION=20220418184647
# Should output: "Skipping IP geocode migration..."

# Test that migration runs when flag is set
ENV['ENABLE_IP_GEOCODE'] = 'true'
rails db:migrate:down VERSION=20220418184647 2>/dev/null || true
rails db:migrate:up VERSION=20220418184647
# Should create the tables

# Verify tables exist
rails console
> RailsUrlShortener::Ipgeo.table_exists?
> ActiveRecord::Base.connection.column_exists?(:rails_url_shortener_visits, :ipgeo_id)
```

### 6. Integration Test - Full Workflow

```bash
# 1. Generate with flag
rails generate rails_url_shortener --enable-ip-geocode

# 2. Verify initializer
grep "save_ip_geocode = true" config/initializers/rails_url_shortener.rb

# 3. Start Rails server
rails server

# 4. In another terminal, create a short URL
rails console
> url = RailsUrlShortener::Url.generate('https://example.com')
> puts url.to_short_url

# 5. Visit the short URL
curl http://localhost:3000/shortener/[KEY]

# 6. Check that visit was created and IpCrawlerJob was enqueued
rails console
> visit = RailsUrlShortener::Visit.last
> visit.ipgeo # Should have ipgeo if job completed
```

## Test Checklist

- [ ] Generator without flag creates initializer with `save_ip_geocode = false`
- [ ] Generator with `--enable-ip-geocode` creates initializer with `save_ip_geocode = true`
- [ ] Migration skips by default (no ipgeos table created)
- [ ] Migration runs when `ENABLE_IP_GEOCODE=true` is set
- [ ] Visit.parse_and_save does not enqueue IpCrawlerJob when disabled
- [ ] Visit.parse_and_save enqueues IpCrawlerJob when enabled
- [ ] All existing tests still pass
- [ ] New generator tests pass

2 changes: 1 addition & 1 deletion app/models/rails_url_shortener/visit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def self.parse_and_save(url, request)
referer: request.headers['Referer']
)

IpCrawlerJob.perform_later(visit)
IpCrawlerJob.perform_later(visit) if RailsUrlShortener.save_ip_geocode
visit
end
# rubocop:enable Metrics/AbcSize
Expand Down
69 changes: 48 additions & 21 deletions db/migrate/20220418184647_create_rails_url_shortener_ipgeos.rb
Original file line number Diff line number Diff line change
@@ -1,29 +1,56 @@
class CreateRailsUrlShortenerIpgeos < ActiveRecord::Migration[7.0]
def up
create_table :rails_url_shortener_ipgeos do |t|
t.string :ip
t.string :country
t.string :country_code
t.string :region
t.string :region_name
t.string :city
t.string :lat
t.string :lon
t.string :timezone
t.string :isp
t.string :org
t.string :as
t.boolean :mobile
t.boolean :proxy
t.boolean :hosting
t.timestamps
# Skip this migration by default - only run if IP geocoding is enabled via environment variable
# Note: Set ENABLE_IP_GEOCODE=true before running migrations to create IP geocode tables
unless ENV['ENABLE_IP_GEOCODE'] == 'true'
say 'Skipping IP geocode migration (IP geocoding is disabled by default. Set ENABLE_IP_GEOCODE=true to enable)'
return
end

# Check if table already exists (for idempotency)
unless table_exists?(:rails_url_shortener_ipgeos)
create_table :rails_url_shortener_ipgeos do |t|
t.string :ip
t.string :country
t.string :country_code
t.string :region
t.string :region_name
t.string :city
t.string :lat
t.string :lon
t.string :timezone
t.string :isp
t.string :org
t.string :as
t.boolean :mobile
t.boolean :proxy
t.boolean :hosting
t.timestamps
end
end

# Check if column already exists before adding it
unless column_exists?(:rails_url_shortener_visits, :ipgeo_id)
add_column :rails_url_shortener_visits, :ipgeo_id, :integer
end

# Add index if it doesn't exist
unless index_exists?(:rails_url_shortener_visits, :ipgeo_id)
add_index :rails_url_shortener_visits, :ipgeo_id
end
add_column :rails_url_shortener_visits, :ipgeo_id, :integer
add_index :rails_url_shortener_visits, :ipgeo_id
end

def down
remove_column :rails_url_shortener_visits, :ipgeo_id
drop_table :rails_url_shortener_ipgeos
# Skip this migration if IP geocoding was never enabled
return unless ENV['ENABLE_IP_GEOCODE'] == 'true'

# Only drop if tables/columns exist
if column_exists?(:rails_url_shortener_visits, :ipgeo_id)
remove_column :rails_url_shortener_visits, :ipgeo_id
end

if table_exists?(:rails_url_shortener_ipgeos)
drop_table :rails_url_shortener_ipgeos
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,24 @@
class RailsUrlShortenerGenerator < Rails::Generators::Base
source_root File.expand_path('templates', __dir__)

# Add class option for IP geocoding flag
class_option :enable_ip_geocode,
type: :boolean,
default: false,
desc: 'Enable IP geocoding functionality and create ipgeos migration'

def install_and_run_migrations
if Rails.env.test?
puts 'Skipping migrations in test environment'
else
# Set environment variable based on generator option
if options[:enable_ip_geocode]
ENV['ENABLE_IP_GEOCODE'] = 'true'
puts 'Note: IP geocode migration will be created (--enable-ip-geocode flag)'
else
ENV['ENABLE_IP_GEOCODE'] = 'false' unless ENV['ENABLE_IP_GEOCODE']
puts 'Note: IP geocode migration will be skipped by default. Use --enable-ip-geocode flag to enable IP geocoding.'
end
rake 'rails_url_shortener:install:migrations'
rake 'db:migrate'
end
Expand All @@ -23,5 +37,12 @@ def add_route_to_routes_file

def copy
copy_file 'initializer.rb', 'config/initializers/rails_url_shortener.rb'

# Update initializer if IP geocoding is enabled
if options[:enable_ip_geocode]
gsub_file 'config/initializers/rails_url_shortener.rb',
/RailsUrlShortener\.save_ip_geocode = false/,
'RailsUrlShortener.save_ip_geocode = true'
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
RailsUrlShortener.minimum_key_length = 3 # minimum permited for a key
RailsUrlShortener.save_bots_visits = false # if save bots visits
RailsUrlShortener.save_visits = true # if save visits
RailsUrlShortener.save_ip_geocode = false # if save IP geocode information (set to true to enable IpCrawlerJob)
6 changes: 6 additions & 0 deletions lib/rails_url_shortener.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ module RailsUrlShortener
mattr_accessor :save_bots_visits, default: true

mattr_accessor :save_visits, default: true

##
# if save ip geocode information on db, this will trigger IpCrawlerJob
# to fetch IP geolocation data from external API
# by default this is disabled - set to true to enable IP geocoding
mattr_accessor :save_ip_geocode, default: false
end

ActiveSupport.on_load(:active_record) do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,54 @@ class RailsUrlShortenerGeneratorTest < Rails::Generators::TestCase
assert_match(%r{mount RailsUrlShortener::Engine, at: '/}, content)
end
end

test 'generator sets save_ip_geocode to false by default' do
routes_file = File.join(destination_root, 'config', 'routes.rb')
FileUtils.mkdir_p(File.dirname(routes_file))
File.write(routes_file, "Rails.application.routes.draw do\nend")

run_generator ['arguments']

# Verify initializer has save_ip_geocode set to false
assert_file 'config/initializers/rails_url_shortener.rb' do |content|
assert_match(/RailsUrlShortener\.save_ip_geocode = false/, content)
end
end

test 'generator sets save_ip_geocode to true when --enable-ip-geocode flag is used' do
routes_file = File.join(destination_root, 'config', 'routes.rb')
FileUtils.mkdir_p(File.dirname(routes_file))
File.write(routes_file, "Rails.application.routes.draw do\nend")

run_generator ['arguments', '--enable-ip-geocode']

# Verify initializer has save_ip_geocode set to true
assert_file 'config/initializers/rails_url_shortener.rb' do |content|
assert_match(/RailsUrlShortener\.save_ip_geocode = true/, content)
assert_no_match(/RailsUrlShortener\.save_ip_geocode = false/, content)
end
end

test 'generator sets ENABLE_IP_GEOCODE environment variable when flag is used' do
routes_file = File.join(destination_root, 'config', 'routes.rb')
FileUtils.mkdir_p(File.dirname(routes_file))
File.write(routes_file, "Rails.application.routes.draw do\nend")

# Clear the env var first
original_env = ENV['ENABLE_IP_GEOCODE']
ENV.delete('ENABLE_IP_GEOCODE')

run_generator ['arguments', '--enable-ip-geocode']

# Verify environment variable was set
assert_equal 'true', ENV['ENABLE_IP_GEOCODE']

# Restore original value
if original_env
ENV['ENABLE_IP_GEOCODE'] = original_env
else
ENV.delete('ENABLE_IP_GEOCODE')
end
end
end
end
Loading
Loading