diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e629eb..c5dc0fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,9 @@ jobs: fi echo "Couchbase configuration validated successfully" + - name: Setup Couchbase indexes + run: RAILS_ENV=test bundle exec rake couchbase:setup_indexes + - name: Run integration tests run: bundle exec rspec spec/requests/api/v1 diff --git a/Gemfile b/Gemfile index f6f2bd3..3ffe191 100644 --- a/Gemfile +++ b/Gemfile @@ -5,24 +5,9 @@ ruby '3.4.1' # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" gem 'rails', '~> 7.1.3', '>= 7.1.3.2' -# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails] -gem 'sprockets-rails' - -# Use sqlite3 as the database for Active Record -gem 'sqlite3', '~> 1.4' - # Use the Puma web server [https://github.com/puma/puma] gem 'puma', '>= 5.0' -# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails] -gem 'importmap-rails' - -# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev] -gem 'turbo-rails' - -# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev] -gem 'stimulus-rails' - # Build JSON APIs with ease [https://github.com/rails/jbuilder] gem 'jbuilder' @@ -72,7 +57,5 @@ group :development do end group :test do - # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing] - gem 'capybara' - gem 'selenium-webdriver' + # API-only testing with RSpec (no system/browser testing needed) end diff --git a/README.md b/README.md index b821cb7..1784c25 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,91 @@ production: > Note: The connection string expects the `couchbases://` or `couchbase://` part. +## Couchbase Index Management + +This application requires specific N1QL indexes on the `travel-sample` bucket to function correctly. These indexes optimize the SQL++ queries used by the application for filtering and joining documents. + +### Automatic Index Setup + +Indexes are automatically created when you: + +- Run `bin/setup` for local development setup +- Run tests in CI/CD (GitHub Actions automatically creates indexes before running tests) + +The application uses idempotent index creation (using `CREATE INDEX IF NOT EXISTS`), so it's safe to run the setup multiple times. + +### Required Indexes + +The application requires the following indexes on the `travel-sample` bucket: + +1. **`idx_type`** - General index on the `type` field for all document queries +2. **`idx_type_country`** - Index for airline queries filtered by country (`Airline.list_by_country_or_all`) +3. **`idx_type_destinationairport`** - Index for route queries by destination airport (`Airline.to_airport`) +4. **`idx_type_sourceairport_stops`** - Index for route queries by source airport and stops (`Route.direct_connections`) +5. **`idx_type_airlineid`** - Index for airline queries by airline ID (used in joins with routes) + +### Manual Index Management + +You can manually manage indexes using the following Rake tasks: + +#### Create All Required Indexes + +```sh +bundle exec rake couchbase:setup_indexes +``` + +This command creates all required indexes on the `travel-sample` bucket. It's idempotent and safe to run multiple times. + +#### List All Indexes + +```sh +bundle exec rake couchbase:list_indexes +``` + +This command lists all indexes currently present in the `travel-sample` bucket, including their state, type, and indexed fields. + +#### Drop Application Indexes + +```sh +bundle exec rake couchbase:drop_indexes +``` + +This command drops all application-managed indexes. It requires confirmation before executing. For automated scripts, you can force the operation: + +```sh +FORCE_DROP=true bundle exec rake couchbase:drop_indexes +``` + +> **Warning**: Use with caution! Dropping indexes will cause queries to fail until indexes are recreated. + +### Troubleshooting Index Issues + +If you encounter index-related errors: + +1. **Verify indexes exist**: + ```sh + bundle exec rake couchbase:list_indexes + ``` + +2. **Check index state**: Indexes should be in "online" state. If they're "building" or "pending", wait for them to complete. + +3. **Recreate indexes**: + ```sh + bundle exec rake couchbase:drop_indexes + bundle exec rake couchbase:setup_indexes + ``` + +4. **Check permissions**: Ensure your Couchbase user has "Query Manage Index" permission to create and drop indexes. + +### Index Creation in CI/CD + +The GitHub Actions workflow automatically creates indexes before running tests. If index creation fails in CI: + +1. Check the "Setup Couchbase indexes" step in the GitHub Actions log +2. Verify that `DB_CONN_STR`, `DB_USERNAME`, and `DB_PASSWORD` secrets/variables are correctly set +3. Ensure the Couchbase user has "Query Manage Index" permission +4. Check that the Couchbase cluster is accessible from GitHub Actions runners + ## Running The Application ### Directly on machine diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb deleted file mode 100644 index d672697..0000000 --- a/app/channels/application_cable/channel.rb +++ /dev/null @@ -1,4 +0,0 @@ -module ApplicationCable - class Channel < ActionCable::Channel::Base - end -end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb deleted file mode 100644 index 0ff5442..0000000 --- a/app/channels/application_cable/connection.rb +++ /dev/null @@ -1,4 +0,0 @@ -module ApplicationCable - class Connection < ActionCable::Connection::Base - end -end diff --git a/app/javascript/application.js b/app/javascript/application.js deleted file mode 100644 index 0d7b494..0000000 --- a/app/javascript/application.js +++ /dev/null @@ -1,3 +0,0 @@ -// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails -import "@hotwired/turbo-rails" -import "controllers" diff --git a/app/javascript/controllers/application.js b/app/javascript/controllers/application.js deleted file mode 100644 index 1213e85..0000000 --- a/app/javascript/controllers/application.js +++ /dev/null @@ -1,9 +0,0 @@ -import { Application } from "@hotwired/stimulus" - -const application = Application.start() - -// Configure Stimulus development experience -application.debug = false -window.Stimulus = application - -export { application } diff --git a/app/javascript/controllers/hello_controller.js b/app/javascript/controllers/hello_controller.js deleted file mode 100644 index 5975c07..0000000 --- a/app/javascript/controllers/hello_controller.js +++ /dev/null @@ -1,7 +0,0 @@ -import { Controller } from "@hotwired/stimulus" - -export default class extends Controller { - connect() { - this.element.textContent = "Hello World!" - } -} diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js deleted file mode 100644 index 54ad4ca..0000000 --- a/app/javascript/controllers/index.js +++ /dev/null @@ -1,11 +0,0 @@ -// Import and register all your controllers from the importmap under controllers/* - -import { application } from "controllers/application" - -// Eager load all controllers defined in the import map under controllers/**/*_controller -import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading" -eagerLoadControllersFrom("controllers", application) - -// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!) -// import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading" -// lazyLoadControllersFrom("controllers", application) diff --git a/app/models/airline.rb b/app/models/airline.rb index 33cfcbd..3dcc8ed 100644 --- a/app/models/airline.rb +++ b/app/models/airline.rb @@ -29,6 +29,6 @@ class Airline < CouchbaseOrm::Base } n1ql :to_airport, query_fn: proc { |bucket, values, options| - cluster.query("SELECT raw META(air).id FROM (SELECT DISTINCT META(airline).id AS airlineId FROM `#{bucket.name}` AS route JOIN `#{bucket.name}` AS airline ON route.airlineid = META(airline).id WHERE route.destinationairport = #{quote(values[0])}) AS subquery JOIN `#{bucket.name}` AS air ON META(air).id = subquery.airlineId LIMIT #{values[1]} OFFSET #{values[2]}", options) + cluster.query("SELECT raw META(air).id FROM (SELECT DISTINCT META(airline).id AS airlineId FROM `#{bucket.name}` AS route JOIN `#{bucket.name}` AS airline ON route.airlineid = META(airline).id AND airline.type = 'airline' WHERE route.type = 'route' AND route.destinationairport = #{quote(values[0])} ORDER BY META(airline).id) AS subquery JOIN `#{bucket.name}` AS air ON META(air).id = subquery.airlineId AND air.type = 'airline' LIMIT #{values[1]} OFFSET #{values[2]}", options) } end diff --git a/app/models/route.rb b/app/models/route.rb index 674e003..4f5327d 100644 --- a/app/models/route.rb +++ b/app/models/route.rb @@ -28,6 +28,6 @@ class Route < CouchbaseOrm::Base validates :distance, presence: true, numericality: { greater_than_or_equal_to: 0 } n1ql :direct_connections, query_fn: proc { |bucket, values, options| - cluster.query("SELECT distinct raw meta(route).id FROM `#{bucket.name}` AS airport JOIN `#{bucket.name}` AS route ON route.sourceairport = airport.faa WHERE airport.faa = #{quote(values[0])} AND route.stops = 0 LIMIT #{values[1]} OFFSET #{values[2]}", options) + cluster.query("SELECT distinct raw meta(route).id FROM `#{bucket.name}` AS airport JOIN `#{bucket.name}` AS route ON route.sourceairport = airport.faa AND route.type = 'route' WHERE airport.type = 'airport' AND airport.faa = #{quote(values[0])} AND route.stops = 0 LIMIT #{values[1]} OFFSET #{values[2]}", options) } end diff --git a/bin/setup b/bin/setup index 3cd5a9d..fcdc779 100755 --- a/bin/setup +++ b/bin/setup @@ -25,6 +25,9 @@ FileUtils.chdir APP_ROOT do puts "\n== Preparing database ==" system! "bin/rails db:prepare" + puts "\n== Setting up Couchbase indexes ==" + system! "bin/rails couchbase:setup_indexes" + puts "\n== Removing old logs and tempfiles ==" system! "bin/rails log:clear tmp:clear" diff --git a/config/application.rb b/config/application.rb index 9a7a7f7..d2ece23 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,6 +1,16 @@ require_relative 'boot' -require 'rails/all' +# Load Rails components individually (excluding activerecord since we use Couchbase via couchbase-orm) +require 'rails' +require 'action_controller/railtie' +require 'action_view/railtie' +require 'action_mailer/railtie' +require 'active_job/railtie' + +# Excluded components (using Couchbase instead of ActiveRecord): +# - active_record/railtie - Using Couchbase via couchbase-orm +# - active_storage/engine - Not needed for API-only app +# - action_cable/railtie - Not using WebSockets # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. diff --git a/config/database.yml b/config/database.yml index 796466b..c6939b9 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,25 +1,19 @@ -# SQLite. Versions 3.8.0 and up are supported. -# gem install sqlite3 -# -# Ensure the SQLite 3 gem is defined in your Gemfile -# gem "sqlite3" +# NOTE: This file is not used by the application. +# This application uses Couchbase as its database through the couchbase-orm gem. +# Database connections are configured in config/initializers/couchbase.rb # +# The configuration below is kept for Rails compatibility but is not actively used. +# No SQL database (SQLite, PostgreSQL, MySQL) is required for this application. + +# Minimal configuration to prevent Rails from attempting database connections default: &default - adapter: sqlite3 - pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - timeout: 5000 + adapter: nulldb development: <<: *default - database: storage/development.sqlite3 -# Warning: The database defined as "test" will be erased and -# re-generated from your development database when you run "rake". -# Do not set this db to the same as development or production. test: <<: *default - database: storage/test.sqlite3 production: <<: *default - database: storage/production.sqlite3 diff --git a/config/environments/development.rb b/config/environments/development.rb index 90591fb..71d81f7 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -34,7 +34,7 @@ end # Store uploaded files on the local file system (see config/storage.yml for options). - config.active_storage.service = :local + # config.active_storage.service = :local # Commented out - not using ActiveStorage # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false @@ -51,16 +51,16 @@ config.active_support.disallowed_deprecation_warnings = [] # Raise an error on page load if there are pending migrations. - config.active_record.migration_error = :page_load + # config.active_record.migration_error = :page_load # Commented out - not using ActiveRecord # Highlight code that triggered database queries in logs. - config.active_record.verbose_query_logs = true + # config.active_record.verbose_query_logs = true # Commented out - not using ActiveRecord # Highlight code that enqueued background job in logs. config.active_job.verbose_enqueue_logs = true # Suppress logger output for asset requests. - config.assets.quiet = true + # config.assets.quiet = true # Commented out - not using asset pipeline # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true diff --git a/config/environments/production.rb b/config/environments/production.rb index ae5ee3b..6b2609f 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -27,7 +27,7 @@ # config.assets.css_compressor = :sass # Do not fall back to assets pipeline if a precompiled asset is missed. - config.assets.compile = false + # config.assets.compile = false # Commented out - not using asset pipeline # Enable serving of images, stylesheets, and JavaScripts from an asset server. # config.asset_host = "http://assets.example.com" @@ -37,7 +37,7 @@ # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX # Store uploaded files on the local file system (see config/storage.yml for options). - config.active_storage.service = :local + # config.active_storage.service = :local # Commented out - not using ActiveStorage # Mount Action Cable outside main process or domain. # config.action_cable.mount_path = nil @@ -85,7 +85,7 @@ config.active_support.report_deprecations = false # Do not dump schema after migrations. - config.active_record.dump_schema_after_migration = false + # config.active_record.dump_schema_after_migration = false # Commented out - not using ActiveRecord # Enable DNS rebinding protection and other `Host` header attacks. # config.hosts = [ diff --git a/config/environments/test.rb b/config/environments/test.rb index adbb4a6..98f5b1f 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -35,7 +35,7 @@ config.action_controller.allow_forgery_protection = false # Store uploaded files on the local file system in a temporary directory. - config.active_storage.service = :test + # config.active_storage.service = :test # Commented out - not using ActiveStorage config.action_mailer.perform_caching = false diff --git a/config/importmap.rb b/config/importmap.rb deleted file mode 100644 index e7724df..0000000 --- a/config/importmap.rb +++ /dev/null @@ -1,7 +0,0 @@ -# Pin npm packages by running ./bin/importmap - -pin 'application' -pin '@hotwired/turbo-rails', to: 'turbo.min.js' -pin '@hotwired/stimulus', to: 'stimulus.min.js' -pin '@hotwired/stimulus-loading', to: 'stimulus-loading.js' -pin_all_from 'app/javascript/controllers', under: 'controllers' diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb deleted file mode 100644 index 2eeef96..0000000 --- a/config/initializers/assets.rb +++ /dev/null @@ -1,12 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Version of your assets, change this if you want to expire all your assets. -Rails.application.config.assets.version = "1.0" - -# Add additional assets to the asset load path. -# Rails.application.config.assets.paths << Emoji.images_path - -# Precompile additional assets. -# application.js, application.css, and all non-JS/CSS in the app/assets -# folder are already added. -# Rails.application.config.assets.precompile += %w( admin.js admin.css ) diff --git a/db/seeds.rb b/db/seeds.rb deleted file mode 100644 index 4fbd6ed..0000000 --- a/db/seeds.rb +++ /dev/null @@ -1,9 +0,0 @@ -# This file should ensure the existence of records required to run the application in every environment (production, -# development, test). The code here should be idempotent so that it can be executed at any point in every environment. -# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup). -# -# Example: -# -# ["Action", "Comedy", "Drama", "Horror"].each do |genre_name| -# MovieGenre.find_or_create_by!(name: genre_name) -# end diff --git a/lib/tasks/couchbase.rake b/lib/tasks/couchbase.rake new file mode 100644 index 0000000..e003337 --- /dev/null +++ b/lib/tasks/couchbase.rake @@ -0,0 +1,208 @@ +# frozen_string_literal: true + +namespace :couchbase do + desc 'Setup required Couchbase indexes for the application' + task setup_indexes: :environment do + puts '=== Setting up Couchbase indexes ===' + + # Get cluster connection from any model (they all share the same cluster) + cluster = Airline.cluster + bucket_name = Airline.bucket.name + + puts "Target bucket: #{bucket_name}" + + # Define all required indexes based on N1QL queries in models + indexes = [ + { + name: 'idx_type', + fields: ['type'], + description: 'Index on type field for all document queries' + }, + { + name: 'idx_type_country', + fields: ['type', 'country'], + where: "type = 'airline'", + description: 'Index for airline queries by country (Airline.list_by_country_or_all)' + }, + { + name: 'idx_type_destinationairport', + fields: ['type', 'destinationairport'], + where: "type = 'route'", + description: 'Index for route queries by destination airport (Airline.to_airport)' + }, + { + name: 'idx_type_sourceairport_stops', + fields: ['type', 'sourceairport', 'stops'], + where: "type = 'route'", + description: 'Index for route queries by source airport and stops (Route.direct_connections)' + }, + { + name: 'idx_type_airlineid', + fields: ['type', 'airlineid'], + where: "type = 'airline'", + description: 'Index for airline queries by airline ID (Airline.to_airport join)' + } + ] + + created_count = 0 + skipped_count = 0 + failed_indexes = [] + + indexes.each do |index_def| + begin + puts "\nCreating index: #{index_def[:name]}" + puts " Description: #{index_def[:description]}" + + # Build the CREATE INDEX query with IF NOT EXISTS for idempotency + query = build_create_index_query(bucket_name, index_def) + puts " Query: #{query}" + + # Execute the query + cluster.query(query) + + # If no error, index is ready + created_count += 1 + puts " \u2713 Index created or already exists" + + rescue Couchbase::Error::IndexExists => e + # This shouldn't happen with IF NOT EXISTS, but handle it anyway + puts " \u2299 Index already exists (skipped)" + skipped_count += 1 + rescue Couchbase::Error::CouchbaseError => e + puts " \u2717 Failed: #{e.message}" + failed_indexes << { name: index_def[:name], error: e.message } + rescue StandardError => e + puts " \u2717 Unexpected error: #{e.message}" + failed_indexes << { name: index_def[:name], error: e.message } + end + end + + # Print summary + puts "\n=== Index Setup Summary ===" + puts "Total indexes: #{indexes.count}" + puts "Successfully processed: #{created_count}" + puts "Skipped (already existed): #{skipped_count}" + puts "Failed: #{failed_indexes.count}" + + if failed_indexes.any? + puts "\n=== Failed Indexes ===" + failed_indexes.each do |failed| + puts " - #{failed[:name]}: #{failed[:error]}" + end + + # Exit with error code for CI + exit 1 + else + puts "\n\u2713 All indexes are ready!" + end + rescue Couchbase::Error::AuthenticationFailure => e + puts "\n\u2717 Authentication failed: #{e.message}" + puts "Please verify your Couchbase credentials:" + puts " - DB_USERNAME environment variable" + puts " - DB_PASSWORD environment variable" + exit 1 + rescue Couchbase::Error::CouchbaseError => e + puts "\n\u2717 Couchbase connection error: #{e.message}" + puts "Please check your connection settings:" + puts " - DB_CONN_STR environment variable" + puts " - DB_USERNAME environment variable" + puts " - DB_PASSWORD environment variable" + puts " - Network connectivity to Couchbase cluster" + exit 1 + rescue StandardError => e + puts "\n\u2717 Unexpected error: #{e.class} - #{e.message}" + puts e.backtrace.first(5).join("\n") + exit 1 + end + + desc 'Drop all application indexes (use with caution!)' + task drop_indexes: :environment do + puts '=== Dropping Couchbase indexes ===' + puts 'WARNING: This will drop all application indexes!' + + unless ENV['FORCE_DROP'] == 'true' + print 'Are you sure? (yes/no): ' + confirmation = $stdin.gets.chomp + unless confirmation.downcase == 'yes' + puts 'Aborted.' + exit 0 + end + end + + cluster = Airline.cluster + bucket_name = Airline.bucket.name + + index_names = [ + 'idx_type', + 'idx_type_country', + 'idx_type_destinationairport', + 'idx_type_sourceairport_stops', + 'idx_type_airlineid' + ] + + dropped_count = 0 + not_found_count = 0 + + index_names.each do |index_name| + begin + query = "DROP INDEX `#{bucket_name}`.`#{index_name}`" + cluster.query(query) + puts " \u2713 Dropped index: #{index_name}" + dropped_count += 1 + rescue Couchbase::Error::IndexNotFound + puts " \u2299 Index not found (already dropped): #{index_name}" + not_found_count += 1 + rescue StandardError => e + puts " \u2717 Failed to drop #{index_name}: #{e.message}" + end + end + + puts "\n=== Drop Summary ===" + puts "Dropped: #{dropped_count}" + puts "Not found: #{not_found_count}" + puts "\n\u2713 Index cleanup complete!" + end + + desc 'List all indexes in the bucket' + task list_indexes: :environment do + puts '=== Couchbase Indexes ===' + + cluster = Airline.cluster + bucket_name = Airline.bucket.name + + query = "SELECT idx.* FROM system:indexes AS idx WHERE idx.keyspace_id = '#{bucket_name}' ORDER BY idx.name" + result = cluster.query(query) + + if result.rows.empty? + puts "No indexes found in bucket '#{bucket_name}'" + else + puts "Indexes in bucket '#{bucket_name}':\n" + result.rows.each do |row| + puts " Name: #{row['name']}" + puts " State: #{row['state']}" + puts " Type: #{row['using']}" + puts " Keys: #{row['index_key']}" + puts " Condition: #{row['condition']}" if row['condition'] + puts "" + end + puts "Total: #{result.rows.count} indexes" + end + rescue StandardError => e + puts "\u2717 Error listing indexes: #{e.message}" + exit 1 + end + + # Private helper method to build CREATE INDEX query + def build_create_index_query(bucket_name, index_def) + query = "CREATE INDEX IF NOT EXISTS `#{index_def[:name]}` ON `#{bucket_name}`" + + # Add fields + fields = index_def[:fields].map { |f| "`#{f}`" }.join(', ') + query += "(#{fields})" + + # Add WHERE clause if present + query += " WHERE #{index_def[:where]}" if index_def[:where] + + query + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index a15455f..3db7137 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -22,26 +22,16 @@ # # Rails.root.glob('spec/support/**/*.rb').sort.each { |f| require f } -# Checks for pending migrations and applies them before tests are run. -# If you are not using ActiveRecord, you can remove these lines. -begin - ActiveRecord::Migration.maintain_test_schema! -rescue ActiveRecord::PendingMigrationError => e - abort e.to_s.strip -end -RSpec.configure do |config| - # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures - config.fixture_paths = [ - Rails.root.join('spec/fixtures') - ] +# NOTE: ActiveRecord is not used in this application - it uses Couchbase via couchbase-orm +# The following ActiveRecord-specific configurations have been removed: +# - ActiveRecord::Migration.maintain_test_schema! +# - config.fixture_paths +# - config.use_transactional_fixtures - # If you're not using ActiveRecord, or you'd prefer not to run each of your - # examples within a transaction, remove the following line or assign false - # instead of true. - config.use_transactional_fixtures = true +RSpec.configure do |config| # You can uncomment this line to turn off ActiveRecord support entirely. - # config.use_active_record = false + config.use_active_record = false # RSpec Rails can automatically mix in different behaviours to your tests # based on their file location, for example enabling you to call `get` and diff --git a/spec/requests/api/v1/airlines_spec.rb b/spec/requests/api/v1/airlines_spec.rb index 64809ba..9070ba0 100644 --- a/spec/requests/api/v1/airlines_spec.rb +++ b/spec/requests/api/v1/airlines_spec.rb @@ -191,20 +191,20 @@ let(:offset) { '0' } let(:expected_airlines) do [ - { 'callsign' => 'JETBLUE', 'country' => 'United States', 'iata' => 'B6', 'icao' => 'JBU', - 'name' => 'JetBlue Airways' }, { 'callsign' => 'SPEEDBIRD', 'country' => 'United Kingdom', 'iata' => 'BA', 'icao' => 'BAW', 'name' => 'British Airways' }, + { 'callsign' => 'AIRFRANS', 'country' => 'France', 'iata' => 'AF', 'icao' => 'AFR', + 'name' => 'Air France' }, { 'callsign' => 'DELTA', 'country' => 'United States', 'iata' => 'DL', 'icao' => 'DAL', 'name' => 'Delta Air Lines' }, + { 'callsign' => 'AMERICAN', 'country' => 'United States', 'iata' => 'AA', 'icao' => 'AAL', + 'name' => 'American Airlines' }, { 'callsign' => 'HAWAIIAN', 'country' => 'United States', 'iata' => 'HA', 'icao' => 'HAL', 'name' => 'Hawaiian Airlines' }, + { 'callsign' => 'JETBLUE', 'country' => 'United States', 'iata' => 'B6', 'icao' => 'JBU', + 'name' => 'JetBlue Airways' }, { 'callsign' => 'FLAGSHIP', 'country' => 'United States', 'iata' => '9E', 'icao' => 'FLG', 'name' => 'Pinnacle Airlines' }, - { 'callsign' => 'AMERICAN', 'country' => 'United States', 'iata' => 'AA', 'icao' => 'AAL', - 'name' => 'American Airlines' }, - { 'callsign' => 'STARWAY', 'country' => 'France', 'iata' => 'SE', 'icao' => 'SEU', - 'name' => 'XL Airways France' }, { 'callsign' => 'SUN COUNTRY', 'country' => 'United States', 'iata' => 'SY', 'icao' => 'SCX', 'name' => 'Sun Country Airlines' }, { 'callsign' => 'UNITED', 'country' => 'United States', 'iata' => 'UA', 'icao' => 'UAL',