diff --git a/.travis.yml b/.travis.yml index 8ec07511e..679787e7d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,26 +1,31 @@ language: ruby sudo: false +services: + - postgresql env: - - "RAILS_VERSION=4.2.11" - - "RAILS_VERSION=5.0.7.2" - - "RAILS_VERSION=5.1.7" - - "RAILS_VERSION=5.2.3" - - "RAILS_VERSION=6.0.0" -# - "RAILS_VERSION=master" + - RAILS_VERSION=6.0.0 DATABASE_URL=postgres://postgres@localhost/jr_test + - RAILS_VERSION=6.0.0 + - RAILS_VERSION=5.2.3 DATABASE_URL=postgres://postgres@localhost/jr_test + - RAILS_VERSION=5.2.3 + - RAILS_VERSION=5.1.7 + - RAILS_VERSION=5.0.7.2 + - RAILS_VERSION=4.2.11 rvm: - - 2.3.8 - - 2.4.7 - - 2.5.6 - - 2.6.4 + - 2.4.9 + - 2.5.7 + - 2.6.5 matrix: - allow_failures: - - env: "RAILS_VERSION=master" exclude: - - rvm: 2.6.4 + - rvm: 2.6.5 env: "RAILS_VERSION=4.2.11" - - rvm: 2.3.8 - env: "RAILS_VERSION=6.0.0" - - rvm: 2.4.7 + - rvm: 2.4.9 env: "RAILS_VERSION=6.0.0" + - rvm: 2.4.9 + env: "RAILS_VERSION=6.0.0 DATABASE_URL=postgres://postgres@localhost/jr_test" + - rvm: 2.4.9 + env: "RAILS_VERSION=5.2.3 DATABASE_URL=postgres://postgres@localhost/jr_test" before_install: - - gem install bundler --version 1.17.3 \ No newline at end of file + - gem install bundler --version 1.17.3 +before_script: + - sh -c "if [ '$DATABASE_URL' = 'postgres://postgres@localhost/jr_test' ]; then psql -c 'DROP DATABASE IF EXISTS jr_test;' -U postgres; fi" + - sh -c "if [ '$DATABASE_URL' = 'postgres://postgres@localhost/jr_test' ]; then psql -c 'CREATE DATABASE jr_test;' -U postgres; fi" \ No newline at end of file diff --git a/Gemfile b/Gemfile index 754aad900..2535d0200 100644 --- a/Gemfile +++ b/Gemfile @@ -9,6 +9,8 @@ end version = ENV['RAILS_VERSION'] || 'default' platforms :ruby do + gem 'pg' + if version.start_with?('4.2', '5.0') gem 'sqlite3', '~> 1.3.13' else diff --git a/test/config/database.yml b/test/config/database.yml deleted file mode 100644 index 97abfd13b..000000000 --- a/test/config/database.yml +++ /dev/null @@ -1,6 +0,0 @@ -test: - adapter: sqlite3 - database: test_db -# database: ":memory:" - pool: 5 - timeout: 5000 diff --git a/test/controllers/controller_test.rb b/test/controllers/controller_test.rb index f71370322..adcac5a94 100644 --- a/test/controllers/controller_test.rb +++ b/test/controllers/controller_test.rb @@ -6,6 +6,7 @@ def set_content_type_header! class PostsControllerTest < ActionController::TestCase def setup + super JSONAPI.configuration.raise_if_parameters_not_allowed = true JSONAPI.configuration.always_include_to_one_linkage_data = false end @@ -445,7 +446,7 @@ def test_sorting_asc assert_cacheable_get :index, params: {sort: 'title'} assert_response :success - assert_equal "A First Post", json_response['data'][0]['attributes']['title'] + assert_equal "A 1ST Post", json_response['data'][0]['attributes']['title'] end def test_sorting_desc @@ -459,7 +460,7 @@ def test_sorting_by_multiple_fields assert_cacheable_get :index, params: {sort: 'title,body'} assert_response :success - assert_equal '14', json_response['data'][0]['id'] + assert_equal '15', json_response['data'][0]['id'] end def create_alphabetically_first_user_and_post @@ -473,8 +474,15 @@ def test_sorting_by_relationship_field assert_response :success assert json_response['data'].length > 10, 'there are enough records to show sort' - assert_equal '17', json_response['data'][0]['id'], 'nil is at the top' - assert_equal post.id.to_s, json_response['data'][1]['id'], 'alphabetically first user is second' + + # Postgres sorts nulls last, whereas sqlite and mysql sort nulls first + if ENV['DATABASE_URL'].starts_with?('postgres') + assert_equal '17', json_response['data'][-1]['id'], 'nil is at the start' + assert_equal post.id.to_s, json_response['data'][0]['id'], 'alphabetically first user is not first' + else + assert_equal '17', json_response['data'][0]['id'], 'nil is at the end' + assert_equal post.id.to_s, json_response['data'][1]['id'], 'alphabetically first user is second' + end end def test_desc_sorting_by_relationship_field @@ -483,8 +491,15 @@ def test_desc_sorting_by_relationship_field assert_response :success assert json_response['data'].length > 10, 'there are enough records to show sort' - assert_equal '17', json_response['data'][-1]['id'], 'nil is at the bottom' - assert_equal post.id.to_s, json_response['data'][-2]['id'], 'alphabetically first user is second last' + + # Postgres sorts nulls last, whereas sqlite and mysql sort nulls first + if ENV['DATABASE_URL'].starts_with?('postgres') + assert_equal '17', json_response['data'][0]['id'], 'nil is at the start' + assert_equal post.id.to_s, json_response['data'][-1]['id'] + else + assert_equal '17', json_response['data'][-1]['id'], 'nil is at the end' + assert_equal post.id.to_s, json_response['data'][-2]['id'], 'alphabetically first user is second last' + end end def test_sorting_by_relationship_field_include @@ -493,8 +508,14 @@ def test_sorting_by_relationship_field_include assert_response :success assert json_response['data'].length > 10, 'there are enough records to show sort' - assert_equal '17', json_response['data'][0]['id'], 'nil is at the top' - assert_equal post.id.to_s, json_response['data'][1]['id'], 'alphabetically first user is second' + + if ENV['DATABASE_URL'].starts_with?('postgres') + assert_equal '17', json_response['data'][-1]['id'], 'nil is at the top' + assert_equal post.id.to_s, json_response['data'][0]['id'] + else + assert_equal '17', json_response['data'][0]['id'], 'nil is at the top' + assert_equal post.id.to_s, json_response['data'][1]['id'], 'alphabetically first user is second' + end end def test_invalid_sort_param @@ -3107,7 +3128,7 @@ def test_type_formatting assert json_response['data'].is_a?(Hash) assert_equal 'Jane Author', json_response['data']['attributes']['spouseName'] assert_equal 'First man to run across Antartica.', json_response['data']['attributes']['bio'] - assert_equal 23.89/45.6, json_response['data']['attributes']['qualityRating'] + assert_equal (23.89/45.6).round(5), json_response['data']['attributes']['qualityRating'].round(5) assert_equal '47000.56', json_response['data']['attributes']['salary'] assert_equal '2013-08-07T20:25:00.000Z', json_response['data']['attributes']['dateTimeJoined'] assert_equal '1965-06-30', json_response['data']['attributes']['birthday'] @@ -4707,7 +4728,12 @@ def test_fetch_robots_with_sort_by_name Robot.create! name: 'jane', version: 1 assert_cacheable_get :index, params: {sort: 'name'} assert_response :success - assert_equal 'John', json_response['data'].first['attributes']['name'] + + if ENV['DATABASE_URL'].starts_with?('postgres') + assert_equal 'jane', json_response['data'].first['attributes']['name'] + else + assert_equal 'John', json_response['data'].first['attributes']['name'] + end end def test_fetch_robots_with_sort_by_lower_name diff --git a/test/fixtures/posts.yml b/test/fixtures/posts.yml index 491a627b6..02d94ef3f 100644 --- a/test/fixtures/posts.yml +++ b/test/fixtures/posts.yml @@ -85,7 +85,7 @@ post_14: post_15: id: 15 - title: AAAA First Post + title: A 1ST Post body: First!!!!!!!!! author_id: 1003 diff --git a/test/integration/requests/request_test.rb b/test/integration/requests/request_test.rb index 64f988767..2cd84a0c9 100644 --- a/test/integration/requests/request_test.rb +++ b/test/integration/requests/request_test.rb @@ -1438,13 +1438,16 @@ def test_sort_primary_attribute end def test_sort_included_attribute + # Postgres sorts nulls last, whereas sqlite and mysql sort nulls first + pg = ENV['DATABASE_URL'].starts_with?('postgres') + get '/api/v6/authors?sort=author_detail.author_stuff', headers: { 'Accept' => JSONAPI::MEDIA_TYPE } assert_jsonapi_response 200 - assert_equal '1000', json_response['data'][0]['id'] + assert_equal pg ? '1001' : '1000', json_response['data'][0]['id'] get '/api/v6/authors?sort=-author_detail.author_stuff', headers: { 'Accept' => JSONAPI::MEDIA_TYPE } assert_jsonapi_response 200 - assert_equal '1002', json_response['data'][0]['id'] + assert_equal pg ? '1000' : '1002', json_response['data'][0]['id'] end def test_include_parameter_quoted diff --git a/test/test_helper.rb b/test/test_helper.rb index b8e6acc40..97e51fe7d 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -21,6 +21,8 @@ end end +ENV['DATABASE_URL'] ||= "sqlite3:test_db" + require 'active_record/railtie' require 'rails/test_help' require 'minitest/mock' @@ -68,6 +70,9 @@ class TestApp < Rails::Application end end +DatabaseCleaner.allow_remote_database_url = true +DatabaseCleaner.strategy = :transaction + module MyEngine class Engine < ::Rails::Engine isolate_namespace MyEngine @@ -477,8 +482,6 @@ class CatResource < JSONAPI::Resource jsonapi_resources :people end -DatabaseCleaner.strategy = :transaction - # Ensure backward compatibility with Minitest 4 Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test) diff --git a/test/unit/active_relation_resource_finder/join_manager_test.rb b/test/unit/active_relation_resource_finder/join_manager_test.rb index f8526ff30..9fb13a82b 100644 --- a/test/unit/active_relation_resource_finder/join_manager_test.rb +++ b/test/unit/active_relation_resource_finder/join_manager_test.rb @@ -3,6 +3,19 @@ class JoinTreeTest < ActiveSupport::TestCase + def db_true + case ActiveRecord::Base.connection.adapter_name + when 'SQLite' + if Rails::VERSION::MAJOR >= 6 || (Rails::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR >= 2) + "1" + else + "'t'" + end + when 'PostgreSQL' + 'TRUE' + end + end + def test_no_added_joins join_manager = JSONAPI::ActiveRelation::JoinManager.new(resource_klass: PostResource) @@ -79,11 +92,9 @@ def test_add_joins_source_relationship_with_custom_apply records = Api::V10::PostResource.records({}) records = join_manager.join(records, {}) - if Rails::VERSION::MAJOR >= 6 || (Rails::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR >= 2) - assert_equal 'SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" WHERE "comments"."approved" = 1', records.to_sql - else - assert_equal 'SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" WHERE "comments"."approved" = \'t\'', records.to_sql - end + sql = 'SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" WHERE "comments"."approved" = ' + db_true + + assert_equal sql, records.to_sql assert_hash_equals({alias: 'comments', join_type: :inner}, join_manager.source_join_details) end @@ -99,11 +110,9 @@ def test_add_nested_scoped_joins records = Api::V10::PostResource.records({}) records = join_manager.join(records, {}) - if Rails::VERSION::MAJOR >= 6 || (Rails::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR >= 2) - assert_equal 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" ON "people"."id" = "posts"."author_id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = 1 AND "author"."special" = 1', records.to_sql - else - assert_equal 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" ON "people"."id" = "posts"."author_id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = \'t\' AND "author"."special" = \'t\'', records.to_sql - end + sql = 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" ON "people"."id" = "posts"."author_id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true + + assert_equal sql, records.to_sql assert_hash_equals({alias: 'posts', join_type: :root}, join_manager.source_join_details) assert_hash_equals({alias: 'comments', join_type: :left}, join_manager.join_details_by_relationship(Api::V10::PostResource._relationship(:comments))) @@ -123,11 +132,10 @@ def test_add_nested_scoped_joins records = join_manager.join(records, {}) # Note sql is in different order, but aliases should still be right - if Rails::VERSION::MAJOR >= 6 || (Rails::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR >= 2) - assert_equal 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "people" ON "people"."id" = "posts"."author_id" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = 1 AND "author"."special" = 1', records.to_sql - else - assert_equal 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "people" ON "people"."id" = "posts"."author_id" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = \'t\' AND "author"."special" = \'t\'', records.to_sql - end + sql = 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "people" ON "people"."id" = "posts"."author_id" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true + + assert_equal sql, records.to_sql + assert_hash_equals({alias: 'posts', join_type: :root}, join_manager.source_join_details) assert_hash_equals({alias: 'comments', join_type: :left}, join_manager.join_details_by_relationship(Api::V10::PostResource._relationship(:comments))) assert_hash_equals({alias: 'authors_comments', join_type: :left}, join_manager.join_details_by_relationship(Api::V10::CommentResource._relationship(:author))) @@ -163,11 +171,9 @@ def test_add_nested_joins_with_fields records = Api::V10::PostResource.records({}) records = join_manager.join(records, {}) - if Rails::VERSION::MAJOR >= 6 || (Rails::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR >= 2) - assert_equal 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" ON "people"."id" = "posts"."author_id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = 1 AND "author"."special" = 1', records.to_sql - else - assert_equal 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" ON "people"."id" = "posts"."author_id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = \'t\' AND "author"."special" = \'t\'', records.to_sql - end + sql = 'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" ON "people"."id" = "posts"."author_id" LEFT OUTER JOIN "people" "authors_comments" ON "authors_comments"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true + + assert_equal sql, records.to_sql assert_hash_equals({alias: 'posts', join_type: :root}, join_manager.source_join_details) assert_hash_equals({alias: 'comments', join_type: :left}, join_manager.join_details_by_relationship(Api::V10::PostResource._relationship(:comments))) @@ -184,11 +190,9 @@ def test_add_joins_with_sub_relationship records = Api::V10::PostResource.records({}) records = join_manager.join(records, {}) - if Rails::VERSION::MAJOR >= 6 || (Rails::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR >= 2) - assert_equal 'SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" ON "people"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" LEFT OUTER JOIN "comments" "comments_people" ON "comments_people"."author_id" = "people"."id" WHERE "comments"."approved" = 1 AND "author"."special" = 1', records.to_sql - else - assert_equal 'SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" ON "people"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" LEFT OUTER JOIN "comments" "comments_people" ON "comments_people"."author_id" = "people"."id" WHERE "comments"."approved" = \'t\' AND "author"."special" = \'t\'', records.to_sql - end + sql = 'SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" LEFT OUTER JOIN "people" ON "people"."id" = "comments"."author_id" LEFT OUTER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "comments_tags"."tag_id" LEFT OUTER JOIN "comments" "comments_people" ON "comments_people"."author_id" = "people"."id" WHERE "comments"."approved" = ' + db_true + ' AND "author"."special" = ' + db_true + + assert_equal sql, records.to_sql assert_hash_equals({alias: 'comments', join_type: :inner}, join_manager.source_join_details) assert_hash_equals({alias: 'comments', join_type: :inner}, join_manager.join_details_by_relationship(Api::V10::PostResource._relationship(:comments)))