visual_query 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +59 -0
- data/Rakefile +23 -0
- data/app/assets/images/visual_query/ajax-loader.gif +0 -0
- data/app/assets/javascripts/visual_query/index.js +288 -0
- data/app/assets/stylesheets/visual_query/visual_query.css +161 -0
- data/app/controllers/queries_controller.rb +157 -0
- data/app/helpers/application_helper.rb +5 -0
- data/app/helpers/queries_helper.rb +43 -0
- data/app/views/queries/_columns.html.erb +5 -0
- data/app/views/queries/_command.html.erb +5 -0
- data/app/views/queries/_command_results_browser.html.erb +1 -0
- data/app/views/queries/_command_save.html.erb +1 -0
- data/app/views/queries/_commands_results.html.erb +2 -0
- data/app/views/queries/_filter.html.erb +6 -0
- data/app/views/queries/_list_all_relations.html.erb +8 -0
- data/app/views/queries/_list_joinable_relations.html.erb +10 -0
- data/app/views/queries/_list_relation.html.erb +8 -0
- data/app/views/queries/_name.html.erb +8 -0
- data/app/views/queries/_results.html.erb +14 -0
- data/app/views/queries/_results_column_filter.html.erb +3 -0
- data/app/views/queries/_results_column_hide.html.erb +3 -0
- data/app/views/queries/_results_column_human_name.html.erb +8 -0
- data/app/views/queries/_results_empty.html.erb +3 -0
- data/app/views/queries/_sort.html.erb +12 -0
- data/app/views/queries/_sort_condition.html.erb +18 -0
- data/app/views/queries/_url_root.html.erb +1 -0
- data/app/views/queries/_warning_large_result_set.html.erb +12 -0
- data/app/views/queries/filters/_boolean.html.erb +6 -0
- data/app/views/queries/filters/_date.html.erb +17 -0
- data/app/views/queries/filters/_datetime.html.erb +1 -0
- data/app/views/queries/filters/_decimal.html.erb +1 -0
- data/app/views/queries/filters/_integer.html.erb +1 -0
- data/app/views/queries/filters/_numeric.html.erb +6 -0
- data/app/views/queries/filters/_string.html.erb +1 -0
- data/app/views/queries/filters/_text.html.erb +6 -0
- data/app/views/queries/index.html.erb +39 -0
- data/app/views/queries/new.html.erb +27 -0
- data/app/views/queries/not_found.html.erb +1 -0
- data/app/views/queries/show.html.erb +40 -0
- data/app/views/queries/sql_form.html.erb +16 -0
- data/config/initializers/visual_query.rb +11 -0
- data/config/routes.rb +26 -0
- data/db/migrate/20130927090319_create_visual_query_schema.rb +9 -0
- data/db/migrate/20130927090400_create_visual_query_metadata_table.rb +16 -0
- data/lib/tasks/visual_query_tasks.rake +12 -0
- data/lib/tutuf/visual_query.rb +6 -0
- data/lib/tutuf/visual_query/base.rb +268 -0
- data/lib/tutuf/visual_query/common.rb +58 -0
- data/lib/tutuf/visual_query/metadata.rb +35 -0
- data/lib/tutuf/visual_query/single.rb +111 -0
- data/lib/tutuf/visual_query/sql.rb +20 -0
- data/lib/visual_query.rb +4 -0
- data/lib/visual_query/engine.rb +4 -0
- data/lib/visual_query/version.rb +3 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/account.rb +3 -0
- data/test/dummy/app/models/address.rb +3 -0
- data/test/dummy/app/models/category.rb +3 -0
- data/test/dummy/app/models/composite_pk.rb +3 -0
- data/test/dummy/app/models/customer.rb +4 -0
- data/test/dummy/app/models/order.rb +4 -0
- data/test/dummy/app/models/person.rb +4 -0
- data/test/dummy/app/models/post.rb +3 -0
- data/test/dummy/app/models/product.rb +4 -0
- data/test/dummy/app/models/product_in.rb +3 -0
- data/test/dummy/app/models/product_out.rb +3 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +53 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +12 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +29 -0
- data/test/dummy/config/environments/production.rb +65 -0
- data/test/dummy/config/environments/test.rb +33 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/visual_query.rb +1 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +3 -0
- data/test/dummy/db/migrate/20130927112446_create_tables.rb +77 -0
- data/test/dummy/db/structure.sql +651 -0
- data/test/dummy/log/development.log +1548 -0
- data/test/dummy/log/test.log +36575 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/-h/-hj2e_RSTanfbfrP0tso5Q7actRM6_clE5hetFlQ2y8.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/3B/3B_zqoNDfkO8wvAME66zxm9KzQaeDVSjnH0qC08yufM.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/5g/5g7dhxVp4YbZmFw_-T3aU2oYq2Z9Jgtps0CKneXYSS0.cache +2 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/6-/6-CtZO6uG0yfwU8-098Sdy2wnfO0W6DbFu6B6DKYuiw.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/8k/8kFIVN4cTS9KCQt_UIF5s_rcY-bMYlQpM489D98hvP4.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/Au/AukU7t3xLnyCh7qW4u45q9YFmjVcYujmIFbnaOhF4Mo.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/Bm/Bmsq0vYQqlrtfq5s-W8kcfLvwcsPT_6_5XxXt9J_QOw.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/CQ/CQLNg7a2TsUWgc7JXjDkjseMig_dPVm6AvqO2IWk5zg.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/FX/FX9ZXN3HEHR5hPzvxW8rakWEt2ot4IPJyDB67O7KPZk.cache +2 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/Hv/HvOStITEkFHlcJCgaDnND6wzPw4dMmdAdZB1Xm6JfSA.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/NW/NWCtuFzfIgixavqY71NIAa4ajbsXxRuiLNjceHgQ24M.cache +2 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/Ta/TaG641Ow168nkagg10mh6zuWS8RwWuawpHuMGakCVjU.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/VN/VNCapNKJLeponthNeFJhaBYs92UBT3P8PugENHP0474.cache +2 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/VR/VRi0Hz7tc62H5Of9XVjyAk7vSNmMr8xeYowo6lSBnZg.cache +2 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/Vg/Vgl_u6t3BvczgGi_ZJlyyo7xYSe-GgEshLofx-3QorI.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/Vq/VqGJMF3Cpvp3fw2IEIkE2tzdFo_OdcEmxN58BQwbVDY.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/W6/W6DnXCIMHJ2i5hUkEiNeDLroWxW3VU18nq292n5jlts.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/Z7/Z7fH8ST-O3GMnDUKvtKHHTSObfH2Nbs0J1QS79i80yE.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/Zh/ZhrmuPgfbHthzikN8QSHR0Q0bLtSYS1Bzl9HauWeDfU.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/bY/bYzYCY_bAGQGVGMbcxtKIhUYrgDQhmQVTmK50JrMNb8.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/eT/eTmCDSnc2m9ER5Cn85g84xyTkVLWKLbbwPFQo8eUAIg.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/h-/h-taE7cHlbq76GUb5kHenjih_y66mO0w0lIZs7vY-0s.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/ji/jikmiWyu-cXN_ZJ4hgLc3kuCAY-QJY2jmPeXS4_9vZY.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/kC/kCijps52gsNlkYgT2Qzdz9UcSaxhcMGNfNL7MIiWk54.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/kQ/kQt-OsUDJg_sl1be-FqJ6Vhw4XVguw9_msZEwXP0Nh0.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/nd/ndbe-ZZWBqU5gLx5nxauCFvv963Zm3xqVEwVYQ7X_X8.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/q6/q6BYa32YJF11eGVapO4ouNl6gayPIsARgMavlzZmoi0.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/sm/sm7AdmddDYbFx4-eo_y_kaZspanmc-jiJeM8j2DXX5k.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/ux/uxPH9lLOW42lEQxJXnBizEObZReD8qkz6Eb6fdS6Ur4.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/wn/wn0ayyM-chRdwEPI11SLkFT-7G2-GcOX8UIIC2kOWLY.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/x7/x7KkTV3ibfIEysLB_ug5bfmnn2VLV_BldukPR3EoPBk.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/xK/xKo0PfDcZuMh8oO-6Gr4j4S8eR2qUNY9Gau4kAxKIH0.cache +1 -0
- data/test/fixtures/accounts.yml +4 -0
- data/test/fixtures/categories.yml +3 -0
- data/test/fixtures/categories_posts.yml +3 -0
- data/test/fixtures/people.yml +8 -0
- data/test/fixtures/posts.yml +8 -0
- data/test/fixtures/product_ins.yml +15 -0
- data/test/fixtures/product_outs.yml +5 -0
- data/test/fixtures/products.yml +6 -0
- data/test/fixtures/saved_queries.rb +7 -0
- data/test/functional/queries_controller_test.rb +267 -0
- data/test/functional/routes_test.rb +111 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/test_helper.rb +15 -0
- data/test/unit/metadata_test.rb +10 -0
- data/test/unit/sql_test.rb +37 -0
- data/test/unit/visual_query_test.rb +648 -0
- metadata +381 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
3
|
+
|
|
4
|
+
class RoutesTest < ActionController::TestCase
|
|
5
|
+
def setup
|
|
6
|
+
super
|
|
7
|
+
@routes = VisualQuery::Engine.routes
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_root
|
|
11
|
+
assert_routing({:method => :get, :path => ''},
|
|
12
|
+
{:controller=>"queries", :action=>"index"})
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def test_new
|
|
16
|
+
assert_routing({:method => :get, :path => '/new'},
|
|
17
|
+
{:controller=>"queries", :action=>"new"})
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_create
|
|
21
|
+
assert_routing({:method => :post, :path => ''},
|
|
22
|
+
{:controller=>"queries", :action=>"create"})
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_show
|
|
26
|
+
assert_routing({:method => :get, :path => '/some_query'},
|
|
27
|
+
{:controller=>"queries", :action=>"show", :name=>'some_query'})
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_delete
|
|
31
|
+
assert_routing({:method => :delete, :path => '/some_query'},
|
|
32
|
+
{:controller=>"queries", :action=>"destroy", :name=>'some_query'})
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Rails routing does not escape percent character %
|
|
36
|
+
#
|
|
37
|
+
# def test_show_with_special_characters
|
|
38
|
+
# assert_routing({:method => :get, :path => '/show/Long%20name%20with%20spaces,%20dot.%20and%25'},
|
|
39
|
+
# {:controller=>"queries", :action=>"show", :name=>"Long name with spaces, dot. and%"})
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# def test_delete_with_special_characters
|
|
43
|
+
# assert_routing({:method => :delete, :path => '/delete/Long%20name%20with%20spaces,%20dot.%20and%25'},
|
|
44
|
+
# {:controller=>"queries", :action=>"destroy", :name=>"Long name with spaces, dot. and%"})
|
|
45
|
+
# end
|
|
46
|
+
#
|
|
47
|
+
# def test_edit_sql_with_special_characters
|
|
48
|
+
# assert_routing({:method => :get, :path => '/edit_sql/Long%20name%20with%20spaces%20and%20dot.'},
|
|
49
|
+
# {:controller=>"queries", :action=>"edit_sql", :name=>"Long name with spaces and dot."})
|
|
50
|
+
# end
|
|
51
|
+
#
|
|
52
|
+
# def test_update_sql_with_special_characters
|
|
53
|
+
# assert_routing({:method => :put, :path => '/update_sql/Long%20name%20with%20spaces%20and%20dot.'},
|
|
54
|
+
# {:controller=>"queries", :action=>"update_sql", :name=>"Long name with spaces and dot."})
|
|
55
|
+
# end
|
|
56
|
+
|
|
57
|
+
def test_columns
|
|
58
|
+
assert_routing({:method => :get, :path => '/columns/Person'},
|
|
59
|
+
{:controller => "queries", :action => 'columns', :klass => 'Person'})
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_list_joinable
|
|
63
|
+
assert_routing({:method => :get, :path => '/list_joinable/Person'},
|
|
64
|
+
{:controller => "queries", :action => 'list_joinable', :klass => 'Person'})
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def test_join_data
|
|
68
|
+
assert_routing({:method => :get, :path => '/join_data/Person'},
|
|
69
|
+
{:controller => "queries", :action => 'join_data', :klass => 'Person'})
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_filter
|
|
73
|
+
assert_routing({:method => :get, :path => '/filter'},
|
|
74
|
+
{:controller => "queries", :action => 'filter'})
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def test_remove_filter
|
|
78
|
+
assert_routing({:method => :get, :path => '/remove_filter'},
|
|
79
|
+
{:controller => "queries", :action => 'remove_filter'})
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_sort_condition
|
|
83
|
+
assert_routing({:method => :get, :path => '/sort_condition'},
|
|
84
|
+
{:controller => "queries", :action => 'sort_condition'})
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_new_sql
|
|
88
|
+
assert_routing({:method => :get, :path => '/new_sql'},
|
|
89
|
+
{:controller=>"queries", :action=>"new_sql"})
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def test_edit_sql
|
|
93
|
+
assert_routing({:method => :get, :path => '/edit_sql/some_query'},
|
|
94
|
+
{:controller=>"queries", :action=>"edit_sql", :name => 'some_query'})
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def test_update_sql
|
|
98
|
+
assert_routing({:method => :put, :path => '/update_sql/some_query'},
|
|
99
|
+
{:controller=>"queries", :action=>"update_sql", :name => 'some_query'})
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def test_results_get
|
|
103
|
+
assert_routing({:method => :get, :path => '/results/some_query'},
|
|
104
|
+
{:controller=>"queries", :action=>"results", :name => 'some_query'})
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def test_results_post
|
|
108
|
+
assert_routing({:method => :post, :path => '/results/some_query'},
|
|
109
|
+
{:controller=>"queries", :action=>"results", :name => 'some_query'})
|
|
110
|
+
end
|
|
111
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require File.expand_path("../dummy/config/environment.rb", __FILE__)
|
|
2
|
+
require "rails/test_help"
|
|
3
|
+
|
|
4
|
+
# Load test data into the database
|
|
5
|
+
|
|
6
|
+
require File.expand_path("../fixtures/saved_queries.rb", __FILE__) if ActiveRecord::Base.connection.select_one("SELECT count(id) FROM tutuf_visual_query_metadata")['count'].to_i == 0
|
|
7
|
+
|
|
8
|
+
class ActiveSupport::TestCase
|
|
9
|
+
include ActiveRecord::TestFixtures
|
|
10
|
+
self.fixture_path = File.expand_path("../fixtures", __FILE__)
|
|
11
|
+
self.use_transactional_fixtures = true
|
|
12
|
+
self.use_instantiated_fixtures = false
|
|
13
|
+
|
|
14
|
+
fixtures :all
|
|
15
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
3
|
+
|
|
4
|
+
class MetadataTest < ActiveSupport::TestCase
|
|
5
|
+
def test_params_serialize_to_json_without_escaping_utf8
|
|
6
|
+
Tutuf::VisualQuery::Base.new('sql' => %{SELECT AVG(age) AS "възраст" FROM people}, 'query' => {'name' => "params_serialization"}).save
|
|
7
|
+
assert_equal %q!{"sql":"SELECT AVG(age) AS \"възраст\" FROM people","query":{"name":"params_serialization"}}!,
|
|
8
|
+
ActiveRecord::Base.connection.select_all(%{SELECT params FROM tutuf_visual_query_metadata WHERE name='params_serialization'}).first['params']
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
3
|
+
|
|
4
|
+
class VisualQueryTest < ActiveSupport::TestCase
|
|
5
|
+
def test_save_valid_sql_query
|
|
6
|
+
vqb = Tutuf::VisualQuery::Base.new('sql' => "SELECT AVG(age) AS average_age FROM people", 'query' => {'name' => "new raw sql query"})
|
|
7
|
+
assert vqb.valid?, "Metadata in test not valid - fix the test"
|
|
8
|
+
assert vqb.save, "Could not save valid raw sql query"
|
|
9
|
+
assert_equal ["average_age"], ActiveRecord::Base.connection.columns(%("#{Tutuf::VisualQuery::Base.schema}"."new raw sql query")).map(&:name)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_filters
|
|
13
|
+
vqb = Tutuf::VisualQuery::Base.find_by_name("raw_sql")
|
|
14
|
+
assert_equal [], vqb.filters
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_results
|
|
18
|
+
vqb = Tutuf::VisualQuery::Base.new('sql' => "SELECT AVG(age) AS average_age FROM people", 'query' => {'name' => "new raw sql query"})
|
|
19
|
+
assert !vqb.to_a.empty?, "Results should not be empty"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_update_sql_valid_when_original_is_sql
|
|
23
|
+
vqb = Tutuf::VisualQuery::Base.find_by_name('raw_sql')
|
|
24
|
+
assert vqb.update_sql('sql' => "SELECT MAX(age) AS max_age FROM people")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_update_sql_valid_when_original_is_visual
|
|
28
|
+
vqb = Tutuf::VisualQuery::Base.find_by_name('first')
|
|
29
|
+
assert vqb.update_sql('sql' => "SELECT MAX(age) AS max_age FROM people")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_update_sql_invalid
|
|
33
|
+
vqb = Tutuf::VisualQuery::Base.find_by_name('raw_sql')
|
|
34
|
+
assert !vqb.update_sql('sql' => "SELECT age FROM")
|
|
35
|
+
assert !vqb.errors.blank?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
3
|
+
require 'csv'
|
|
4
|
+
|
|
5
|
+
class VisualQueryTest < ActiveSupport::TestCase
|
|
6
|
+
|
|
7
|
+
def test_klasses
|
|
8
|
+
assert_equal [Account, Address, Category, CompositePk, Customer, Order, Person, Post, Product, ProductIn, ProductOut],
|
|
9
|
+
Tutuf::VisualQuery::Base.klasses
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_joinable_relations_belongs_to
|
|
13
|
+
reflections = Tutuf::VisualQuery::Base.joinable_relations(Order)
|
|
14
|
+
assert_equal [ActiveRecord::Reflection::AssociationReflection,ActiveRecord::Reflection::AssociationReflection], reflections.collect{|r| r.class}
|
|
15
|
+
assert_equal [Customer, Customer], reflections.collect{|r| r.klass}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_joinable_relations_has_many
|
|
19
|
+
reflection = Tutuf::VisualQuery::Base.joinable_relations(Customer).first
|
|
20
|
+
assert_equal Order, reflection.klass
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_joinable_relations_has_one
|
|
24
|
+
reflections = Tutuf::VisualQuery::Base.joinable_relations(Person).select{|r| r.klass == Address}
|
|
25
|
+
assert !reflections.empty?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_joinable_relations_has_and_belongs_to_many
|
|
29
|
+
reflection = Tutuf::VisualQuery::Base.joinable_relations(Post).first
|
|
30
|
+
assert_equal Category, reflection.klass
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_columns_all_content
|
|
34
|
+
assert_equal %w(id title body), Tutuf::VisualQuery::Base.columns(Post)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_columns_not_all_content
|
|
38
|
+
assert_equal %w(id country city street addressable_type), Tutuf::VisualQuery::Base.columns(Address)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_columns_with_composite_primary_key
|
|
42
|
+
assert_equal(%W(first_id second_id name), Tutuf::VisualQuery::Base.columns(CompositePk))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_join_condition_on_belongs_to
|
|
46
|
+
assert_equal [[{'rel_name' => 'orders', 'col_name' => 'billing_customer_id'}, {'rel_name' => 'customers', 'col_name' => 'id'}]],
|
|
47
|
+
Tutuf::VisualQuery::Base.join_condition(Order.reflect_on_association(:billing_customer))
|
|
48
|
+
assert_equal [[{'rel_name' => 'orders', 'col_name' => 'shipping_customer_id'}, {'rel_name' => 'customers', 'col_name' => 'id'}]],
|
|
49
|
+
Tutuf::VisualQuery::Base.join_condition(Order.reflect_on_association(:shipping_customer))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_join_condition_on_belongs_to_with_composite_primary_key
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_join_condition_on_has_many
|
|
56
|
+
assert_equal [[{'rel_name' => 'customers', 'col_name' => 'id'}, {'rel_name' => 'orders', 'col_name' => 'billing_customer_id'}]],
|
|
57
|
+
Tutuf::VisualQuery::Base.join_condition(Customer.reflect_on_association(:billing_orders))
|
|
58
|
+
assert_equal [[{'rel_name' => 'customers', 'col_name' => 'id'}, {'rel_name' => 'orders', 'col_name' => 'shipping_customer_id'}]],
|
|
59
|
+
Tutuf::VisualQuery::Base.join_condition(Customer.reflect_on_association(:shipping_orders))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_join_condition_on_has_many_with_composite_primary_key
|
|
63
|
+
end
|
|
64
|
+
def test_join_condition_on_has_many_with_polymorphic_association
|
|
65
|
+
end
|
|
66
|
+
def test_join_condition_on_has_many_with_polymorphic_association_and_with_composite_primary_key
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_join_condition_on_has_one
|
|
70
|
+
assert_equal [[{'rel_name' => 'people', 'col_name' => 'id'}, {'rel_name' => 'accounts', 'col_name' => 'person_id'}]],
|
|
71
|
+
Tutuf::VisualQuery::Base.join_condition(Person.reflect_on_association(:account))
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_join_condition_on_has_one_with_composite_primary_key
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def test_join_condition_on_has_one_with_polymorphic_association
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_join_condition_on_has_one_with_polymorphic_association_and_with_composite_primary_key
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def test_join_condition_on_has_and_belongs_to_many
|
|
84
|
+
assert_equal [[{'rel_name' => 'posts', 'col_name' => 'id'}, {'rel_name' => 'categories_posts', 'col_name' => 'post_id'}],
|
|
85
|
+
[{'rel_name' => 'categories_posts', 'col_name' => 'category_id'}, {'rel_name' => 'categories', 'col_name' => 'id'}]],
|
|
86
|
+
Tutuf::VisualQuery::Base.join_condition(Post.reflect_on_association(:categories))
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_join_condition_on_has_and_belongs_to_many_with_composite_primary_key
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def test_join_condition_on_has_and_belongs_to_many_with_polymorphic_association
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def test_join_condition_on_has_and_belongs_to_many_with_polymorphic_association_and_with_composite_primary_key
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def test_results_one_relation
|
|
99
|
+
cols = [{'rel_name' => "people", 'col_name' => "id"},
|
|
100
|
+
{'rel_name' => "people", 'col_name' => "name"},
|
|
101
|
+
{'rel_name' => "people", 'col_name' => "age"}]
|
|
102
|
+
results = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
103
|
+
'all_columns' => cols,
|
|
104
|
+
'relations' => [{'rel_name' => "people"}]}).to_a
|
|
105
|
+
person = people(:john)
|
|
106
|
+
assert results.include?([person.id.to_s, person.name, person.age.to_s])
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def test_results_two_relations_has_one
|
|
110
|
+
cols = [{'rel_name' => "people", 'col_name' => "id"},
|
|
111
|
+
{'rel_name' => "people", 'col_name' => "name"},
|
|
112
|
+
{'rel_name' => "people", 'col_name' => "age"},
|
|
113
|
+
{'rel_name' => "accounts", 'col_name' => "id"},
|
|
114
|
+
{'rel_name' => "accounts", 'col_name' => "name"}]
|
|
115
|
+
results = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
116
|
+
'all_columns' => cols,
|
|
117
|
+
'relations' => [{'rel_name' => "people"}, {'rel_name' => "accounts"}],
|
|
118
|
+
'join_conditions' =>[{'left_rel_name' => "accounts",
|
|
119
|
+
'left_col_name' => "person_id",
|
|
120
|
+
'right_rel_name' => "people",
|
|
121
|
+
'right_col_name' => "id"}]}).to_a
|
|
122
|
+
person = people(:john)
|
|
123
|
+
account = accounts(:john)
|
|
124
|
+
assert_equal [person.id.to_s, person.name, person.age.to_s, account.id.to_s, account.name], results[0]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def test_results_two_relations_has_and_belongs_to_many
|
|
128
|
+
cols = [{'rel_name' => "posts", 'col_name' => "title"},
|
|
129
|
+
{'rel_name' => "categories", 'col_name' => "name"}]
|
|
130
|
+
results = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
131
|
+
'all_columns' => cols,
|
|
132
|
+
'relations' => [{'rel_name' => "posts"},
|
|
133
|
+
{'rel_name' => "categories"},
|
|
134
|
+
{'rel_name' => "categories_posts"}],
|
|
135
|
+
'join_conditions' => [{'left_rel_name' => "posts",
|
|
136
|
+
'left_col_name' => "id",
|
|
137
|
+
'right_rel_name' => "categories_posts",
|
|
138
|
+
'right_col_name' => "post_id"},
|
|
139
|
+
{'left_rel_name' => "categories_posts",
|
|
140
|
+
'left_col_name' => "category_id",
|
|
141
|
+
'right_rel_name' => "categories",
|
|
142
|
+
'right_col_name' => "id"}]}).to_a
|
|
143
|
+
category = categories(:first)
|
|
144
|
+
post = posts(:first)
|
|
145
|
+
assert_equal [post.title, category.name], results[0]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def test_results_two_relations_has_and_belongs_to_many_join_type_full
|
|
149
|
+
cols = [{'rel_name' => "posts", 'col_name' => "title"},
|
|
150
|
+
{'rel_name' => "categories", 'col_name' => "name"}]
|
|
151
|
+
results = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
152
|
+
'all_columns' => cols,
|
|
153
|
+
'relations' => [{'rel_name' => "posts"},
|
|
154
|
+
{'rel_name' => "categories"},
|
|
155
|
+
{'rel_name' => "categories_posts"}],
|
|
156
|
+
'join_conditions' => [{'left_rel_name' => "posts",
|
|
157
|
+
'left_col_name' => "id",
|
|
158
|
+
'right_rel_name' => "categories_posts",
|
|
159
|
+
'right_col_name' => "post_id",
|
|
160
|
+
'type' => "FULL"},
|
|
161
|
+
{'left_rel_name' => "categories_posts",
|
|
162
|
+
'left_col_name' => "category_id",
|
|
163
|
+
'right_rel_name' => "categories",
|
|
164
|
+
'right_col_name' => "id"}]}).to_a
|
|
165
|
+
category = categories(:first)
|
|
166
|
+
post = posts(:first)
|
|
167
|
+
assert_equal [post.title, category.name], results[0]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def test_results_two_relations_has_and_belongs_to_many_join_type_invalid
|
|
171
|
+
cols = [{'rel_name' => "posts", 'col_name' => "title"},
|
|
172
|
+
{'rel_name' => "categories", 'col_name' => "name"}]
|
|
173
|
+
results = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
174
|
+
'all_columns' => cols,
|
|
175
|
+
'relations' => [{'rel_name' => "posts"},
|
|
176
|
+
{'rel_name' => "categories"},
|
|
177
|
+
{'rel_name' => "categories_posts"}],
|
|
178
|
+
'join_conditions' => [{'left_rel_name' => "posts",
|
|
179
|
+
'left_col_name' => "id",
|
|
180
|
+
'right_rel_name' => "categories_posts",
|
|
181
|
+
'right_col_name' => "post_id",
|
|
182
|
+
'type' => "NO SUCH"},
|
|
183
|
+
{'left_rel_name' => "categories_posts",
|
|
184
|
+
'left_col_name' => "category_id",
|
|
185
|
+
'right_rel_name' => "categories",
|
|
186
|
+
'right_col_name' => "id"}]}).to_a
|
|
187
|
+
category = categories(:first)
|
|
188
|
+
post = posts(:first)
|
|
189
|
+
assert_equal [post.title, category.name], results[0]
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def test_results_one_relation_with_filter
|
|
193
|
+
cols = [{'rel_name' => "people", 'col_name' => "name"}]
|
|
194
|
+
results = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
195
|
+
'all_columns' => cols,
|
|
196
|
+
'relations' => [{'rel_name' => "people"}],
|
|
197
|
+
'filters' => [{'schema' => "public",
|
|
198
|
+
'rel_name' => "people",
|
|
199
|
+
'col_name' => "id",
|
|
200
|
+
'op' => "=",
|
|
201
|
+
'val' => people(:john).id}]}).to_a
|
|
202
|
+
assert_equal [[people(:john).name]], results
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def test_results_with_hidden_columns
|
|
206
|
+
cols = [{'rel_name' => "people", 'col_name' => "name"},
|
|
207
|
+
{'rel_name' => "people", 'col_name' => "age"}]
|
|
208
|
+
results = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
209
|
+
'all_columns' => [{'rel_name' => "people", 'col_name' => "id"}] + cols,
|
|
210
|
+
'relations' => [{'rel_name' => "people"}]}).to_a
|
|
211
|
+
person = people(:john)
|
|
212
|
+
assert results.include?([nil, person.name, person.age.to_s])
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def test_results_balanced_query
|
|
216
|
+
cols = [{'rel_name' => "products", 'col_name' => "name"},
|
|
217
|
+
{'rel_name' => "product_ins", 'col_name' => "quantity"},
|
|
218
|
+
{'rel_name' => "product_outs", 'col_name' => "quantity"}]
|
|
219
|
+
results = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
220
|
+
'all_columns' => cols,
|
|
221
|
+
'relations' => [{'row' => 0, 'rel_name' => 'products'}, {'row' => 0, 'rel_name' => 'product_ins'},
|
|
222
|
+
{'row' => 1, 'rel_name' => 'products'}, {'row' => 1, 'rel_name' => 'product_outs'}],
|
|
223
|
+
'join_conditions' =>[{'left_rel_name' => "products", 'left_col_name' => "id",
|
|
224
|
+
'right_rel_name' => "product_ins", 'right_col_name' => "product_id",
|
|
225
|
+
'row' => 0},
|
|
226
|
+
{'left_rel_name' => "products", 'left_col_name' => "id",
|
|
227
|
+
'right_rel_name' => "product_outs", 'right_col_name' => "product_id",
|
|
228
|
+
'row' => 1}],
|
|
229
|
+
'rows' => [0, 1]
|
|
230
|
+
}).to_a
|
|
231
|
+
assert results.include?([products(:pencil).name, product_ins(:red_pencil).quantity.to_s, nil])
|
|
232
|
+
assert results.include?([products(:pencil).name, nil, product_outs(:red_pencil).quantity.to_s])
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def test_results_balanced_query_with_hidden_columns
|
|
236
|
+
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def query_for_results
|
|
240
|
+
cols = [{'rel_name' => "people", 'col_name' => "name", 'save_name' => "name"},
|
|
241
|
+
{'rel_name' => "people", 'col_name' => "age", 'save_name' => "age"}]
|
|
242
|
+
query = Tutuf::VisualQuery::Base.new('columns' => cols,
|
|
243
|
+
'all_columns' => cols,
|
|
244
|
+
'relations' => [{'rel_name'=>"people", 'row' => 0}],
|
|
245
|
+
'rows' => [0],
|
|
246
|
+
'query' => {'name' => "query for results"},
|
|
247
|
+
'action' => "create")
|
|
248
|
+
query.save
|
|
249
|
+
query
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def test_results_saved_query
|
|
253
|
+
assert_equal [["John Atanasoff", "34"], ["Джон Атанасов", "34"]],
|
|
254
|
+
query_for_results.to_a
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def test_to_csv
|
|
258
|
+
assert_equal "name,age\nJohn Atanasoff,34\nДжон Атанасов,34\n",
|
|
259
|
+
query_for_results.to_csv
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def test_to_json_without_escaping
|
|
263
|
+
assert_equal %q([{"name":"John Atanasoff","age":"34"},{"name":"Джон Атанасов","age":"34"}]),
|
|
264
|
+
query_for_results.to_json
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def test_sort_no_direction
|
|
268
|
+
cols = [{'rel_name' => "people", 'col_name' => "id"}]
|
|
269
|
+
results = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
270
|
+
'all_columns' => cols,
|
|
271
|
+
'relations' => [{'rel_name' => "people"}],
|
|
272
|
+
'sort_conditions' => {"0"=> cols[0]}}).to_a
|
|
273
|
+
assert_equal [["1"], ["2"]], results[0..1]
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def test_sort_ascending
|
|
277
|
+
cols = [{'rel_name' => "people", 'col_name' => "id"}]
|
|
278
|
+
results = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
279
|
+
'all_columns' => cols,
|
|
280
|
+
'relations' => [{'rel_name' => "people"}],
|
|
281
|
+
'sort_conditions' => {"0" => cols[0].merge('direction' => 'ASC')}}).to_a
|
|
282
|
+
assert_equal [["1"], ["2"]], results[0..1]
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def test_sort_descending
|
|
286
|
+
cols = [{'rel_name' => "people", 'col_name' => "id"}]
|
|
287
|
+
results = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
288
|
+
'all_columns' => cols,
|
|
289
|
+
'relations' => [{'rel_name' => "people"}],
|
|
290
|
+
'sort_conditions' => {"0" => cols[0].merge('direction' => 'DESC')}}).to_a
|
|
291
|
+
assert_equal [["2"], ["1"]], [results[-2], results[-1]]
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def test_sort_with_empty_condition
|
|
295
|
+
cols = [{'rel_name' => "people", 'col_name' => "id"}]
|
|
296
|
+
results = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
297
|
+
'all_columns' => cols,
|
|
298
|
+
'relations' => [{'rel_name' => "people"}],
|
|
299
|
+
'sort_conditions' => {"0"=> cols[0],
|
|
300
|
+
"1"=>{'rel_name' => "", 'col_name' => ""}}}).to_a
|
|
301
|
+
assert_equal [["1"], ["2"]], results[0..1]
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def test_sort_sql_injection_in_relation_name
|
|
305
|
+
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def test_sort_sql_injection_in_column_name
|
|
309
|
+
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def test_sort_sql_injection_in_direction
|
|
313
|
+
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def test_sort_balanced_query
|
|
317
|
+
cols = [{'rel_name' => "products", 'col_name' => "id"},
|
|
318
|
+
{'rel_name' => "product_ins", 'col_name' => "buy_price"},
|
|
319
|
+
{'rel_name' => "product_outs", 'col_name' => "sale_price"}]
|
|
320
|
+
results = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
321
|
+
'all_columns' => cols,
|
|
322
|
+
'relations' => [{'row' => 0, 'rel_name' => 'products'}, {'row' => 0, "rel_name" => 'product_ins'},
|
|
323
|
+
{'row' => 1, 'rel_name' => 'products'}, {'row' => 1, 'rel_name' => 'product_outs'}],
|
|
324
|
+
'join_conditions' =>[{'left_rel_name' => "products", 'left_col_name' => "id",
|
|
325
|
+
'right_rel_name' => "product_ins", 'right_col_name' => "product_id",
|
|
326
|
+
'row' => 0},
|
|
327
|
+
{'left_rel_name' => "products", 'left_col_name' => "id",
|
|
328
|
+
'right_rel_name' => "product_outs", 'right_col_name' => "product_id",
|
|
329
|
+
'row' => 1}],
|
|
330
|
+
'sort_conditions' => {"0" => {'rel_name' => "products", 'col_name' => "id"},
|
|
331
|
+
"1" => {'rel_name' => "product_ins", 'col_name' => "id"}},
|
|
332
|
+
'rows' => [0, 1]
|
|
333
|
+
}).to_a
|
|
334
|
+
assert_equal [ [products(:pencil).id.to_s, product_ins(:red_pencil).buy_price.to_s, nil],
|
|
335
|
+
[products(:pencil).id.to_s, product_ins(:blue_pencil).buy_price.to_s, nil],
|
|
336
|
+
[products(:bottle).id.to_s, product_ins(:bottle_700_ml).buy_price.to_s, nil],
|
|
337
|
+
[products(:pencil).id.to_s, nil, product_outs(:red_pencil).sale_price.to_s ]],
|
|
338
|
+
results[0..3]
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def test_parsed_filters_with_empty_condition
|
|
342
|
+
assert_equal %("schema"."relation"."column" >= '2008-06-01'),
|
|
343
|
+
Tutuf::VisualQuery::Single.new({'filters' => [ { 'schema' => "schema",
|
|
344
|
+
'rel_name' => "relation",
|
|
345
|
+
'col_name' => "column",
|
|
346
|
+
'op' => "<=",
|
|
347
|
+
'val' => ""},
|
|
348
|
+
{ 'schema' => "schema",
|
|
349
|
+
'rel_name' => "relation",
|
|
350
|
+
'col_name' => "column",
|
|
351
|
+
'op' => ">=",
|
|
352
|
+
'val' => "2008-06-01"}]}).parsed_filters
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def test_parsed_filters_with_all_nonempty_conditions
|
|
356
|
+
assert_equal %("schema"."relation"."column" <= '2008-07-01' AND "schema"."relation"."column" >= '2008-06-01'),
|
|
357
|
+
Tutuf::VisualQuery::Single.new({'filters' => [{'schema' => "schema",
|
|
358
|
+
'rel_name' => "relation",
|
|
359
|
+
'col_name' => "column",
|
|
360
|
+
'op' => "<=",
|
|
361
|
+
'val' => "2008-07-01"},
|
|
362
|
+
{'schema' => "schema",
|
|
363
|
+
'rel_name' => "relation",
|
|
364
|
+
'col_name' => "column",
|
|
365
|
+
'op' => ">=",
|
|
366
|
+
'val' => "2008-06-01"}]}).parsed_filters
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def test_sql_injection_in_columns_semicolon
|
|
370
|
+
cols = [{'col_name' => %q(name; DELETE FROM schema_info;), 'rel_name' => "people"}]
|
|
371
|
+
vqb = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
372
|
+
'all_columns' => cols,
|
|
373
|
+
'relations' => ['rel_name' => "people"]})
|
|
374
|
+
assert_equal 'SELECT "people"."name; DELETE FROM schema_info;" FROM "people"', vqb.query
|
|
375
|
+
assert_raise(ActiveRecord::StatementInvalid){vqb.to_a}
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def test_sql_injection_in_columns_semicolon_and_double_quote
|
|
379
|
+
cols = [{'col_name' => %q(name"; DELETE FROM schema_info;), 'rel_name' => "people"}]
|
|
380
|
+
vqb = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
381
|
+
'all_columns' => cols,
|
|
382
|
+
'relations' => ['rel_name' => "people"]})
|
|
383
|
+
assert_equal 'SELECT "people"."name""; DELETE FROM schema_info;" FROM "people"', vqb.query
|
|
384
|
+
assert_raise(ActiveRecord::StatementInvalid){vqb.to_a}
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def test_sql_injection_in_columns_semicolon_and_backslashed_double_quote
|
|
388
|
+
cols = [{'col_name' => %q(name\"; DELETE FROM schema_info;), 'rel_name' => "people"}]
|
|
389
|
+
vqb = Tutuf::VisualQuery::Base.new({'columns' => cols,
|
|
390
|
+
'all_columns' => cols,
|
|
391
|
+
'relations' => ['rel_name' => "people"]})
|
|
392
|
+
assert_equal 'SELECT "people"."name\\""; DELETE FROM schema_info;" FROM "people"', vqb.query
|
|
393
|
+
assert_raise(ActiveRecord::StatementInvalid){vqb.to_a}
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def test_sql_injection_in_relations_semicolon
|
|
397
|
+
vqb = Tutuf::VisualQuery::Base.new({'relations' => ['rel_name' => %q(people; DELETE FROM schema_info;)]})
|
|
398
|
+
assert_equal 'SELECT FROM "people; DELETE FROM schema_info;"', vqb.query
|
|
399
|
+
assert_raise(ActiveRecord::StatementInvalid){vqb.to_a}
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def test_sql_injection_in_relations_semicolon_and_double_quote
|
|
403
|
+
vqb = Tutuf::VisualQuery::Base.new({'relations' => ['rel_name' => %q(people"; DELETE FROM schema_info;)]})
|
|
404
|
+
assert_equal 'SELECT FROM "people""; DELETE FROM schema_info;"', vqb.query
|
|
405
|
+
assert_raise(ActiveRecord::StatementInvalid){vqb.to_a}
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def test_sql_injection_in_relations_semicolon_and_backslashed_double_quote
|
|
409
|
+
vqb = Tutuf::VisualQuery::Base.new({'relations' => ['rel_name' => %q(people\"; DELETE FROM schema_info;)]})
|
|
410
|
+
assert_equal 'SELECT FROM "people\\""; DELETE FROM schema_info;"', vqb.query
|
|
411
|
+
assert_raise(ActiveRecord::StatementInvalid){vqb.to_a}
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
def test_sql_injection_in_join_conditions
|
|
415
|
+
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def test_sql_injection_in_filters_schema
|
|
419
|
+
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def test_sql_injection_in_filters_relation
|
|
423
|
+
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def test_sql_injection_in_filters_column
|
|
427
|
+
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def test_sql_injection_in_filters_operator
|
|
431
|
+
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def test_sql_injection_in_filters_value
|
|
435
|
+
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def test_columns_all_selected
|
|
439
|
+
assert_equal %q("rel"."col1","rel"."col2"),
|
|
440
|
+
Tutuf::VisualQuery::Single.new('columns' => [{'rel_name' => "rel", 'col_name'=>"col1"}, {'rel_name' => "rel", 'col_name'=>"col2"}],
|
|
441
|
+
'all_columns' => [{'rel_name' => "rel", 'col_name'=>"col1"}, {'rel_name' => "rel", 'col_name'=>"col2"}]).parsed_columns
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def test_columns_not_all_selected
|
|
445
|
+
assert_equal %q("rel"."col1",NULL),
|
|
446
|
+
Tutuf::VisualQuery::Single.new('columns' => [{'rel_name' => "rel", 'col_name'=>"col1"}],
|
|
447
|
+
'all_columns' => [{'rel_name' => "rel", 'col_name'=>"col1"}, {'rel_name' => "rel", 'col_name'=>"col2"}]).parsed_columns
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def test_columns_for_saving_selected_not_empty
|
|
451
|
+
assert_equal %q("rel"."col1" AS "rel:col1"),
|
|
452
|
+
Tutuf::VisualQuery::Single.new('columns' => [{'rel_name' => "rel", 'col_name'=>"col1", 'save_name' => "rel:col1"}],
|
|
453
|
+
'all_columns' => [{'rel_name' => "rel", 'col_name'=>"col1"}, {'rel_name' => "rel", 'col_name'=>"col2"}],
|
|
454
|
+
'action' => "create").parsed_columns
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def test_columns_for_saving_selected_empty
|
|
458
|
+
assert_nil Tutuf::VisualQuery::Single.new('action' => "create").parsed_columns
|
|
459
|
+
assert_nil Tutuf::VisualQuery::Single.new('columns' => [], 'action' => "create").parsed_columns
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
def test_validate_name_cannot_be_empty_string
|
|
463
|
+
vqb = Tutuf::VisualQuery::Base.new('query' => {'name' => ""})
|
|
464
|
+
assert !vqb.valid?
|
|
465
|
+
assert vqb.errors[:name].any?
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def test_validate_name_cannot_be_nil
|
|
469
|
+
vqb = Tutuf::VisualQuery::Base.new({})
|
|
470
|
+
assert !vqb.valid?
|
|
471
|
+
assert vqb.errors[:name].any?
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
def test_validate_name_cannot_be_more_than_63_chars
|
|
475
|
+
vqb = Tutuf::VisualQuery::Base.new('query' => {'name' => "z"*64})
|
|
476
|
+
assert !vqb.valid?
|
|
477
|
+
assert vqb.errors[:name].any?
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
def test_validate_name_must_be_unique
|
|
481
|
+
vqb = Tutuf::VisualQuery::Base.new('query' => {'name' => "first"})
|
|
482
|
+
assert !vqb.valid?
|
|
483
|
+
assert vqb.errors[:name].any?, "No errors when name is not unique"
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def test_name_on_new
|
|
487
|
+
assert_equal "new query", Tutuf::VisualQuery::Base.new('query' => {'name' => "new query"}).name
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
def test_name_on_loading_from_db
|
|
491
|
+
assert_equal("first", Tutuf::VisualQuery::Base.find_by_name("first").name)
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def test_is_new_on_new_object
|
|
495
|
+
assert Tutuf::VisualQuery::Base.new('query' => {'name' => "new query"}).new?
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
def test_is_new_on_existing_object
|
|
499
|
+
assert !Tutuf::VisualQuery::Base.find_by_name("first").new?
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
def test_save_valid_visual_query
|
|
503
|
+
vqb = Tutuf::VisualQuery::Base.new('columns' => [{'rel_name' => "addresses", 'col_name' => "country", 'save_name' => "cntr"}],
|
|
504
|
+
'relations' => [{'rel_name' => "addresses"}],
|
|
505
|
+
'action' => "create",
|
|
506
|
+
'query' => {'name' => "new query"})
|
|
507
|
+
assert vqb.valid?, "Metadata in test not valid - fix the test"
|
|
508
|
+
assert vqb.save, "Could not save valid query"
|
|
509
|
+
assert_equal ["cntr"], ActiveRecord::Base.connection.columns(%("#{Tutuf::VisualQuery::Base.schema}"."new query")).map(&:name)
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
def test_save_invalid_metadata_and_valid_sql_generated
|
|
513
|
+
vqb = Tutuf::VisualQuery::Base.new('relations' => ["people"])
|
|
514
|
+
assert !vqb.valid?, "Metadata in test valid - fix the test"
|
|
515
|
+
assert !vqb.save, "Saved although metadata is invalid"
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
def test_save_invalid_metadata_and_invalid_sql_generated
|
|
519
|
+
metadata_count = Tutuf::VisualQuery::Metadata.count
|
|
520
|
+
vqb = Tutuf::VisualQuery::Base.new('query' => {'name' => "new query"})
|
|
521
|
+
assert vqb.valid?, "Metadata in test not valid - fix the test"
|
|
522
|
+
assert_raises(ActiveRecord::StatementInvalid){vqb.save}
|
|
523
|
+
assert_equal metadata_count, Tutuf::VisualQuery::Metadata.count, "Metadata saved when query view not saved"
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
def test_save_balanced_query
|
|
527
|
+
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
def test_find_by_name_existing
|
|
531
|
+
assert Tutuf::VisualQuery::Base.find_by_name("first")
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
def test_find_by_name_not_existing
|
|
535
|
+
assert_nil Tutuf::VisualQuery::Base.find_by_name("new query")
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
def test_find_all
|
|
539
|
+
all_queries = Tutuf::VisualQuery::Base.find_all
|
|
540
|
+
assert_equal Tutuf::VisualQuery::Metadata.count, all_queries.size, "Number of queries found differs from number of saved metadata"
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
def test_columns_with_name_of_existing_query
|
|
544
|
+
columns = Tutuf::VisualQuery::Base.find_by_name("first").columns
|
|
545
|
+
assert_equal "first", columns.first['rel_name']
|
|
546
|
+
assert_equal "first", columns.first['rel_name']
|
|
547
|
+
assert_equal "people:age", columns.first['col_name']
|
|
548
|
+
assert_equal "people:age", columns.first['col_name']
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
def test_columns_with_name_of_nonexisting_query
|
|
552
|
+
vqb = Tutuf::VisualQuery::Base.new('query' => {'name' => "non existing"})
|
|
553
|
+
assert vqb.name
|
|
554
|
+
assert_raises(ActiveRecord::StatementInvalid){vqb.columns}
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
def test_columns_without_query_name
|
|
558
|
+
assert_nil Tutuf::VisualQuery::Base.new({}).columns
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
def test_schema
|
|
562
|
+
assert_equal(Tutuf::VisualQuery::Base.new({}).schema, Tutuf::VisualQuery::Base.schema)
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
def test_data_type_existing_column
|
|
566
|
+
assert_equal(:date, Tutuf::VisualQuery::Base.data_type('rel_name' => "customers", 'col_name' => "created_at"))
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
def test_data_type_exisitng_relation_nonexisting_column
|
|
570
|
+
assert_nil Tutuf::VisualQuery::Base.data_type('rel_name' => "people", 'col_name' => "non existing")
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
def test_data_type_nonexisitng_relation_nonexisting_column
|
|
574
|
+
assert_raise(ActiveRecord::StatementInvalid){Tutuf::VisualQuery::Base.data_type('rel_name' => "non existing", 'col_name' => "non existing")}
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
def test_quote_relation_name_with_schema
|
|
578
|
+
assert_equal(%q("schema"."relation"), Tutuf::VisualQuery::Base.quote_relation_name('schema' => "schema", 'rel_name' => "relation"))
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
def test_quote_relation_name_with_schema_nil
|
|
582
|
+
assert_equal(%q("relation"), Tutuf::VisualQuery::Base.quote_relation_name('rel_name' => "relation"))
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
def test_quote_relation_name_with_schema_empty
|
|
586
|
+
assert_equal(%q("relation"), Tutuf::VisualQuery::Base.quote_relation_name('schema' => "", 'rel_name' => "relation"))
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
def test_join_relations_has_and_belongs_to_many
|
|
590
|
+
assert_equal(["categories_posts", "categories"], Tutuf::VisualQuery::Base.join_relations(Post.reflect_on_association(:categories)))
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
def test_join_relations_has_many
|
|
594
|
+
assert_equal(["orders"], Tutuf::VisualQuery::Base.join_relations(Customer.reflect_on_association(:billing_orders)))
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def test_join_relations_has_one
|
|
598
|
+
assert_equal(["accounts"], Tutuf::VisualQuery::Base.join_relations(Person.reflect_on_association(:account)))
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
def test_join_relations_belongs_to
|
|
602
|
+
assert_equal(["people"], Tutuf::VisualQuery::Base.join_relations(Account.reflect_on_association(:person)))
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def test_large_result_set_really_large
|
|
606
|
+
vqb = Tutuf::VisualQuery::Base.new({})
|
|
607
|
+
vqb.instance_variable_set("@results", (1..vqb.max_result_length).map{|i| [1] } << [1] )
|
|
608
|
+
assert vqb.large_result_set?
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def test_large_result_set_not_really_large
|
|
612
|
+
vqb = Tutuf::VisualQuery::Base.new({})
|
|
613
|
+
vqb.instance_variable_set("@results", (1..(vqb.max_result_length - 1)).map{|i| [1] })
|
|
614
|
+
assert !vqb.large_result_set?
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def test_large_result_set_nil_results
|
|
618
|
+
vqb = Tutuf::VisualQuery::Base.new({})
|
|
619
|
+
assert !vqb.large_result_set?
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
def test_large_result_set_empty_results
|
|
623
|
+
vqb = Tutuf::VisualQuery::Base.new({})
|
|
624
|
+
vqb.instance_variable_set("@results", [])
|
|
625
|
+
assert !vqb.large_result_set?
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
def test_destroy
|
|
629
|
+
metadata_count = Tutuf::VisualQuery::Metadata.count
|
|
630
|
+
vqb = Tutuf::VisualQuery::Base.find_by_name("first")
|
|
631
|
+
assert vqb.destroy
|
|
632
|
+
assert_equal(metadata_count - 1, Tutuf::VisualQuery::Metadata.count)
|
|
633
|
+
assert_nil ActiveRecord::Base.connection.select_one("SELECT 1 FROM information_schema.views WHERE table_name='first' AND table_schema='#{Tutuf::VisualQuery::Base.schema}';")
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
def test_disable
|
|
637
|
+
metadata_count = Tutuf::VisualQuery::Metadata.count
|
|
638
|
+
Tutuf::VisualQuery::Base.disable
|
|
639
|
+
assert_equal metadata_count, Tutuf::VisualQuery::Metadata.count
|
|
640
|
+
assert_nil ActiveRecord::Base.connection.select_one("SELECT 1 FROM information_schema.views WHERE table_schema='#{Tutuf::VisualQuery::Base.schema}'")
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
def test_enable
|
|
644
|
+
Tutuf::VisualQuery::Base.disable
|
|
645
|
+
Tutuf::VisualQuery::Base.enable
|
|
646
|
+
assert ActiveRecord::Base.connection.select_one("SELECT 1 FROM information_schema.views WHERE table_name='first' AND table_schema='#{Tutuf::VisualQuery::Base.schema}';")
|
|
647
|
+
end
|
|
648
|
+
end
|