Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ghiculescu/a3a713134f18e351c7ba521377146014 to your computer and use it in GitHub Desktop.
Save ghiculescu/a3a713134f18e351c7ba521377146014 to your computer and use it in GitHub Desktop.
How to replicate https://github.com/CanCanCommunity/cancancan/pull/600 - switch to master, add this test file, then run the test command below. It should fail for you too!
# frozen_string_literal: true
require 'spec_helper'
if CanCan::ModelAdapters::ActiveRecordAdapter.version_greater_or_equal?('5.0.0')
describe CanCan::ModelAdapters::ActiveRecord5Adapter do
context 'with postgresql' do
before :each do
connect_db
ActiveRecord::Migration.verbose = false
ActiveRecord::Schema.define do
create_table(:blog_authors) do |t|
t.string :name, null: false
t.timestamps null: false
end
create_table(:blog_posts) do |t|
t.references :blog_author
t.string :title, null: false
t.timestamps null: false
end
create_table(:blog_post_comments) do |t|
t.references :blog_post
t.string :body, null: false
t.timestamps null: false
end
end
unless defined?(BlogAuthor)
class BlogAuthor < ActiveRecord::Base
has_many :blog_posts
has_many :blog_post_comments, through: :blog_posts
end
end
unless defined?(BlogPost)
class BlogPost < ActiveRecord::Base
has_many :blog_post_comments
belongs_to :blog_author
default_scope -> { order(:title) }
end
end
unless defined?(BlogPostComment)
class BlogPostComment < ActiveRecord::Base
belongs_to :blog_post
default_scope -> { order(created_at: :desc) }
end
end
end
subject(:ability) { Ability.new(nil) }
let(:alex) { BlogAuthor.create!(name: 'Alex') }
let(:josh) { BlogAuthor.create!(name: 'Josh') }
let(:p1) { josh.blog_posts.create!(title: 'p1') }
let(:p2) { alex.blog_posts.create!(title: 'p2') }
let(:p1c1) { p1.blog_post_comments.create!(body: 'p1c1', created_at: Time.new(2019, 8, 25, 1)) }
let(:p1c2) { p1.blog_post_comments.create!(body: 'p1c2', created_at: Time.new(2019, 8, 25, 2)) }
let(:p2c1) { p2.blog_post_comments.create!(body: 'p2c1', created_at: Time.new(2019, 8, 25, 3)) }
let(:p2c2) { p2.blog_post_comments.create!(body: 'p2c2', created_at: Time.new(2019, 8, 25, 4)) }
context 'when default scope sets an order, and abilities dont have extra checks' do
before do
ability.can :read, BlogPost
ability.can :read, BlogPostComment
end
it 'can get accessible records' do
expected_bodies_in_order = [p2c2, p2c1, p1c2, p1c1].map(&:body)
accessible = BlogPostComment.accessible_by(ability)
expect(accessible.map(&:body)).to eq(expected_bodies_in_order)
expect(accessible.count).to eq(4)
end
it 'can get accessible records from a has_many' do
expected_bodies_in_order = [p2c2, p2c1].map(&:body)
accessible = p2.blog_post_comments.accessible_by(ability)
expect(accessible.map(&:body)).to eq(expected_bodies_in_order)
expect(accessible.count).to eq(2)
end
it 'can get accessible records from a has_many - other post' do
expected_bodies_in_order = [p1c2, p1c1].map(&:body)
accessible = p1.blog_post_comments.accessible_by(ability)
expect(accessible.map(&:body)).to eq(expected_bodies_in_order)
expect(accessible.count).to eq(2)
end
it 'can get accessible records from a has_many :through' do
expected_bodies_in_order = [p2c2, p2c1].map(&:body)
accessible = alex.blog_post_comments.accessible_by(ability)
expect(accessible.map(&:body)).to eq(expected_bodies_in_order)
expect(accessible.count).to eq(2)
end
it 'can get accessible records from a has_many :through - other user' do
expected_bodies_in_order = [p1c2, p1c1].map(&:body)
accessible = josh.blog_post_comments.accessible_by(ability)
expect(accessible.map(&:body)).to eq(expected_bodies_in_order)
expect(accessible.count).to eq(2)
end
end
context 'when default scope sets an order, and abilities have extra checks' do
before do
ability.can :read, BlogPost
# this is the only change vs. the above context -- in this context we can *only* see alex's posts
ability.can :read, BlogPostComment, blog_post: { blog_author_id: alex.id }
end
it 'can get accessible records' do
expected_bodies_in_order = [p2c2, p2c1].map(&:body)
accessible = BlogPostComment.accessible_by(ability)
expect(accessible.map(&:body)).to eq(expected_bodies_in_order)
expect(accessible.count).to eq(2)
end
it 'can get accessible records from a has_many' do
expected_bodies_in_order = [p2c2, p2c1].map(&:body)
accessible = p2.blog_post_comments.accessible_by(ability)
expect(accessible.map(&:body)).to eq(expected_bodies_in_order)
expect(accessible.count).to eq(2)
end
it 'can get accessible records from a has_many - none returned' do
expected_bodies_in_order = []
accessible = p1.blog_post_comments.accessible_by(ability)
expect(accessible.map(&:body)).to eq(expected_bodies_in_order)
expect(accessible.count).to eq(0)
end
it 'can get accessible records from a has_many :through' do
expected_bodies_in_order = [p2c2, p2c1].map(&:body)
accessible = alex.blog_post_comments.accessible_by(ability)
expect(accessible.map(&:body)).to eq(expected_bodies_in_order)
expect(accessible.count).to eq(2)
end
it 'can get accessible records from a has_many :through - none returned' do
expected_bodies_in_order = []
accessible = josh.blog_post_comments.accessible_by(ability)
expect(accessible.map(&:body)).to eq(expected_bodies_in_order)
expect(accessible.count).to eq(0)
end
end
end
end
end
$ DB=postgres bundle exec appraisal activerecord_5.2.2 rspec spec/cancan/model_adapters/active_record_5_adapter_with_default_scopes_spec.rb
>> BUNDLE_GEMFILE=/Users/alex/code/cancancan/gemfiles/activerecord_5.2.2.gemfile bundle exec rspec spec/cancan/model_adapters/active_record_5_adapter_with_default_scopes_spec.rb
Run options: include {:focus=>true}
All examples were filtered out; ignoring {:focus=>true}
Randomized with seed 56631
.....F...F
Failures:
1) CanCan::ModelAdapters::ActiveRecord5Adapter with postgresql when default scope sets an order, and abilities have extra checks can get accessible records from a has_many :through
Failure/Error: expect(accessible.map(&:body)).to eq(expected_bodies_in_order)
ActiveRecord::StatementInvalid:
PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
LINE 1: ... ORDER BY "blog_post_comments"."created_at" DESC, "blog_post...
^
: SELECT DISTINCT "blog_post_comments".* FROM "blog_post_comments" INNER JOIN "blog_posts" ON "blog_post_comments"."blog_post_id" = "blog_posts"."id" LEFT OUTER JOIN "blog_posts" "blog_posts_blog_post_comments" ON "blog_posts_blog_post_comments"."id" = "blog_post_comments"."blog_post_id" WHERE "blog_posts"."blog_author_id" = $1 AND "blog_posts"."blog_author_id" = $2 ORDER BY "blog_post_comments"."created_at" DESC, "blog_posts"."title" ASC
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:677:in `prepare'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:677:in `block in prepare_statement'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:672:in `prepare_statement'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:617:in `exec_cache'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:600:in `execute_and_clear'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql/database_statements.rb:81:in `exec_query'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/abstract/database_statements.rb:482:in `select_prepared'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/abstract/database_statements.rb:68:in `select_all'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/abstract/query_cache.rb:106:in `select_all'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/querying.rb:41:in `find_by_sql'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/relation.rb:560:in `block in exec_queries'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/relation.rb:584:in `skip_query_cache_if_necessary'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/relation.rb:547:in `exec_queries'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/association_relation.rb:34:in `exec_queries'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/relation.rb:422:in `load'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/relation.rb:200:in `records'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/relation/delegation.rb:71:in `each'
# ./spec/cancan/model_adapters/active_record_5_adapter_with_default_scopes_spec.rb:143:in `map'
# ./spec/cancan/model_adapters/active_record_5_adapter_with_default_scopes_spec.rb:143:in `block (4 levels) in <top (required)>'
# ------------------
# --- Caused by: ---
# PG::InvalidColumnReference:
# ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
# LINE 1: ... ORDER BY "blog_post_comments"."created_at" DESC, "blog_post...
# ^
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:677:in `prepare'
2) CanCan::ModelAdapters::ActiveRecord5Adapter with postgresql when default scope sets an order, and abilities have extra checks can get accessible records from a has_many :through - none returned
Failure/Error: expect(accessible.map(&:body)).to eq(expected_bodies_in_order)
ActiveRecord::StatementInvalid:
PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
LINE 1: ... ORDER BY "blog_post_comments"."created_at" DESC, "blog_post...
^
: SELECT DISTINCT "blog_post_comments".* FROM "blog_post_comments" INNER JOIN "blog_posts" ON "blog_post_comments"."blog_post_id" = "blog_posts"."id" LEFT OUTER JOIN "blog_posts" "blog_posts_blog_post_comments" ON "blog_posts_blog_post_comments"."id" = "blog_post_comments"."blog_post_id" WHERE "blog_posts"."blog_author_id" = $1 AND "blog_posts"."blog_author_id" = $2 ORDER BY "blog_post_comments"."created_at" DESC, "blog_posts"."title" ASC
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:677:in `prepare'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:677:in `block in prepare_statement'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:672:in `prepare_statement'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:617:in `exec_cache'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:600:in `execute_and_clear'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql/database_statements.rb:81:in `exec_query'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/abstract/database_statements.rb:482:in `select_prepared'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/abstract/database_statements.rb:68:in `select_all'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/abstract/query_cache.rb:106:in `select_all'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/querying.rb:41:in `find_by_sql'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/relation.rb:560:in `block in exec_queries'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/relation.rb:584:in `skip_query_cache_if_necessary'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/relation.rb:547:in `exec_queries'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/association_relation.rb:34:in `exec_queries'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/relation.rb:422:in `load'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/relation.rb:200:in `records'
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/relation/delegation.rb:71:in `each'
# ./spec/cancan/model_adapters/active_record_5_adapter_with_default_scopes_spec.rb:150:in `map'
# ./spec/cancan/model_adapters/active_record_5_adapter_with_default_scopes_spec.rb:150:in `block (4 levels) in <top (required)>'
# ------------------
# --- Caused by: ---
# PG::InvalidColumnReference:
# ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
# LINE 1: ... ORDER BY "blog_post_comments"."created_at" DESC, "blog_post...
# ^
# /Users/alex/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:677:in `prepare'
Finished in 9.34 seconds (files took 0.63906 seconds to load)
10 examples, 2 failures
Failed examples:
rspec ./spec/cancan/model_adapters/active_record_5_adapter_with_default_scopes_spec.rb:140 # CanCan::ModelAdapters::ActiveRecord5Adapter with postgresql when default scope sets an order, and abilities have extra checks can get accessible records from a has_many :through
rspec ./spec/cancan/model_adapters/active_record_5_adapter_with_default_scopes_spec.rb:147 # CanCan::ModelAdapters::ActiveRecord5Adapter with postgresql when default scope sets an order, and abilities have extra checks can get accessible records from a has_many :through - none returned
Randomized with seed 56631
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment