discourse-data-explorer/spec/controllers/queries_controller_spec.rb

245 lines
7.2 KiB
Ruby
Raw Normal View History

2016-04-11 17:13:51 -04:00
require 'rails_helper'
2015-07-15 16:20:42 -04:00
describe DataExplorer::QueryController do
routes { ::DataExplorer::Engine.routes }
def response_json
MultiJson.load(response.body)
end
before do
SiteSetting.data_explorer_enabled = true
end
2017-08-02 01:42:49 -04:00
let!(:admin) { log_in_user(Fabricate(:admin)) }
2015-07-15 16:20:42 -04:00
2018-02-23 09:01:50 -05:00
def make_query(sql, opts = {})
2015-07-15 16:20:42 -04:00
q = DataExplorer::Query.new
q.id = Fabrication::Sequencer.sequence("query-id", 1)
2018-02-23 09:01:50 -05:00
q.name = opts[:name] || "Query number #{q.id}"
2015-07-15 16:20:42 -04:00
q.description = "A description for query number #{q.id}"
q.sql = sql
q.save
q
end
describe "when disabled" do
before do
SiteSetting.data_explorer_enabled = false
end
it 'denies every request' do
2017-09-04 02:07:22 -04:00
get :index
expect(response.body).to be_empty
2016-08-19 04:46:03 -04:00
2017-09-04 02:07:22 -04:00
get :index, format: :json
2016-08-19 04:46:03 -04:00
expect(response.status).to eq(404)
2017-09-04 02:07:22 -04:00
get :schema, format: :json
2016-08-19 04:46:03 -04:00
expect(response.status).to eq(404)
2017-09-04 02:07:22 -04:00
get :show, params: { id: 3 }, format: :json
2016-08-19 04:46:03 -04:00
expect(response.status).to eq(404)
2017-09-04 02:07:22 -04:00
post :create, params: { id: 3 }, format: :json
2016-08-19 04:46:03 -04:00
expect(response.status).to eq(404)
2017-09-04 02:07:22 -04:00
post :run, params: { id: 3 }, format: :json
2016-08-19 04:46:03 -04:00
expect(response.status).to eq(404)
2017-09-04 02:07:22 -04:00
put :update, params: { id: 3 }, format: :json
2016-08-19 04:46:03 -04:00
expect(response.status).to eq(404)
2017-09-04 02:07:22 -04:00
delete :destroy, params: { id: 3 }, format: :json
2016-08-19 04:46:03 -04:00
expect(response.status).to eq(404)
2015-07-15 16:20:42 -04:00
end
end
describe "#index" do
it "behaves nicely with no queries" do
DataExplorer::Query.destroy_all
2017-09-04 02:07:22 -04:00
get :index, format: :json
2015-07-15 16:20:42 -04:00
expect(response).to be_success
expect(response_json['queries']).to eq([])
end
2018-02-23 09:01:50 -05:00
it "shows all available queries in alphabetical order" do
2015-07-15 16:20:42 -04:00
DataExplorer::Query.destroy_all
2018-02-23 09:01:50 -05:00
make_query('SELECT 1 as value', name: 'B')
make_query('SELECT 1 as value', name: 'A')
2017-09-04 02:07:22 -04:00
get :index, format: :json
2015-07-15 16:20:42 -04:00
expect(response).to be_success
expect(response_json['queries'].length).to eq(2)
2018-02-23 09:01:50 -05:00
expect(response_json['queries'][0]['name']).to eq('A')
expect(response_json['queries'][1]['name']).to eq('B')
2015-07-15 16:20:42 -04:00
end
end
describe "#run" do
2017-08-02 01:42:49 -04:00
let!(:admin) { log_in(:admin) }
2015-07-15 16:20:42 -04:00
2017-08-02 01:42:49 -04:00
def run_query(id, params = {})
2015-07-15 16:20:42 -04:00
params = Hash[params.map { |a| [a[0], a[1].to_s] }]
2017-09-04 02:07:22 -04:00
post :run, params: { id: id, _params: MultiJson.dump(params) }, format: :json
2015-07-15 16:20:42 -04:00
end
it "can run queries" do
q = make_query('SELECT 23 as my_value')
run_query q.id
expect(response).to be_success
expect(response_json['success']).to eq(true)
expect(response_json['errors']).to eq([])
expect(response_json['columns']).to eq(['my_value'])
2017-09-04 02:07:22 -04:00
expect(response_json['rows']).to eq([[23]])
2015-07-15 16:20:42 -04:00
end
it "can process parameters" do
2017-09-04 02:07:22 -04:00
q = make_query <<~SQL
-- [params]
-- int :foo = 34
SELECT :foo as my_value
SQL
2017-08-02 01:42:49 -04:00
run_query q.id, foo: 23
2015-07-15 16:20:42 -04:00
expect(response).to be_success
expect(response_json['errors']).to eq([])
expect(response_json['success']).to eq(true)
expect(response_json['columns']).to eq(['my_value'])
2017-09-04 02:07:22 -04:00
expect(response_json['rows']).to eq([[23]])
2015-07-15 16:20:42 -04:00
run_query q.id
expect(response).to be_success
expect(response_json['errors']).to eq([])
expect(response_json['success']).to eq(true)
expect(response_json['columns']).to eq(['my_value'])
2017-09-04 02:07:22 -04:00
expect(response_json['rows']).to eq([[34]])
2015-07-15 16:20:42 -04:00
# 2.3 is not an integer
2017-08-02 01:42:49 -04:00
run_query q.id, foo: '2.3'
2015-07-15 16:20:42 -04:00
expect(response).to_not be_success
expect(response_json['errors']).to_not eq([])
expect(response_json['success']).to eq(false)
expect(response_json['errors'].first).to match(/ValidationError/)
end
it "doesn't allow you to modify the database #1" do
p = create_post
2017-09-04 02:07:22 -04:00
q = make_query <<~SQL
UPDATE posts SET cooked = '<p>you may already be a winner!</p>' WHERE id = #{p.id}
RETURNING id
SQL
2015-07-15 16:20:42 -04:00
run_query q.id
p.reload
# Manual Test - comment out the following lines:
# ActiveRecord::Base.exec_sql "SET TRANSACTION READ ONLY"
# raise ActiveRecord::Rollback
# This test should fail on the below check.
expect(p.cooked).to_not match(/winner/)
expect(response).to_not be_success
expect(response_json['errors']).to_not eq([])
expect(response_json['success']).to eq(false)
expect(response_json['errors'].first).to match(/read-only transaction/)
end
it "doesn't allow you to modify the database #2" do
p = create_post
2017-09-04 02:07:22 -04:00
q = make_query <<~SQL
SELECT 1
)
SELECT * FROM query;
RELEASE SAVEPOINT active_record_1;
SET TRANSACTION READ WRITE;
UPDATE posts SET cooked = '<p>you may already be a winner!</p>' WHERE id = #{p.id};
SAVEPOINT active_record_1;
SET TRANSACTION READ ONLY;
WITH query AS (
SELECT 1
SQL
2015-07-15 16:20:42 -04:00
run_query q.id
p.reload
# Manual Test - change out the following line:
#
# module ::DataExplorer
# def self.run_query(...)
# if query.sql =~ /;/
#
# to
#
# if false && query.sql =~ /;/
#
# Afterwards, this test should fail on the below check.
expect(p.cooked).to_not match(/winner/)
expect(response).to_not be_success
expect(response_json['errors']).to_not eq([])
expect(response_json['success']).to eq(false)
expect(response_json['errors'].first).to match(/semicolon/)
end
it "doesn't allow you to lock rows" do
2017-09-04 02:07:22 -04:00
q = make_query <<~SQL
SELECT id FROM posts FOR UPDATE
SQL
2015-07-15 16:20:42 -04:00
run_query q.id
expect(response).to_not be_success
expect(response_json['errors']).to_not eq([])
expect(response_json['success']).to eq(false)
expect(response_json['errors'].first).to match(/read-only transaction/)
end
it "doesn't allow you to create a table" do
2017-09-04 02:07:22 -04:00
q = make_query <<~SQL
CREATE TABLE mytable (id serial)
SQL
2015-07-15 16:20:42 -04:00
run_query q.id
expect(response).to_not be_success
expect(response_json['errors']).to_not eq([])
expect(response_json['success']).to eq(false)
expect(response_json['errors'].first).to match(/read-only transaction|syntax error/)
end
it "doesn't allow you to break the transaction" do
2017-09-04 02:07:22 -04:00
q = make_query <<~SQL
COMMIT
SQL
2015-07-15 16:20:42 -04:00
run_query q.id
expect(response).to_not be_success
expect(response_json['errors']).to_not eq([])
expect(response_json['success']).to eq(false)
expect(response_json['errors'].first).to match(/syntax error/)
2017-09-04 02:07:22 -04:00
q.sql = <<~SQL
)
SQL
2015-07-15 16:20:42 -04:00
run_query q.id
expect(response).to_not be_success
expect(response_json['errors']).to_not eq([])
expect(response_json['success']).to eq(false)
expect(response_json['errors'].first).to match(/syntax error/)
2017-09-04 02:07:22 -04:00
q.sql = <<~SQL
RELEASE SAVEPOINT active_record_1
SQL
2015-07-15 16:20:42 -04:00
run_query q.id
expect(response).to_not be_success
expect(response_json['errors']).to_not eq([])
expect(response_json['success']).to eq(false)
expect(response_json['errors'].first).to match(/syntax error/)
end
it "can export data in CSV format" do
q = make_query('SELECT 23 as my_value')
post :run, params: { id: q.id, download: 1 }, format: :csv
expect(response).to be_success
end
2015-07-15 16:20:42 -04:00
end
end