data_task 0.0.2

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.
@@ -0,0 +1,249 @@
1
+ require_relative './helper.rb'
2
+ require 'data_task/adapters/postgres'
3
+
4
+ module Rake
5
+ module DataTask
6
+
7
+ describe Postgres do
8
+
9
+ right_schema = "test_schema_1"
10
+ wrong_schema = "test_schema_2"
11
+ test_table = "test_table"
12
+ test_view = "test_view"
13
+ test_role = "test_role"
14
+
15
+ test_table_right_schema = "#{right_schema}.#{test_table}"
16
+ test_table_wrong_schema = "#{wrong_schema}.#{test_table}"
17
+
18
+ test_view_right_schema = "#{right_schema}.#{test_view}"
19
+ test_view_wrong_schema = "#{wrong_schema}.#{test_view}"
20
+
21
+ around do |test|
22
+ @adapter = get_adapter
23
+ if !@adapter.kind_of?(Rake::DataTask::Postgres)
24
+ skip("Using adapter #{@adapter}, so skipping #{self.class} tests.")
25
+ end
26
+
27
+ @adapter.with_transaction_rollback do
28
+ @adapter.execute <<-EOSQL
29
+ create schema #{right_schema};
30
+ create schema #{wrong_schema};
31
+ EOSQL
32
+ test.call
33
+ @adapter.execute <<-EOSQL
34
+ drop schema #{right_schema} cascade;
35
+ drop schema #{wrong_schema} cascade;
36
+ EOSQL
37
+ end
38
+ end
39
+
40
+ it "returns the current user name when called to" do
41
+ @adapter.execute "create role #{test_role}"
42
+ @adapter.with_role(test_role) do
43
+ @adapter.send(:current_user).must_equal test_role
44
+ end
45
+ end
46
+
47
+ it "returns the current search path when called to" do
48
+ @adapter.execute "set search_path to #{right_schema}, public"
49
+ @adapter.send(:search_path).must_equal [right_schema, 'public']
50
+ end
51
+
52
+ it "resets the search path after exiting a with_search_path block" do
53
+ @adapter.execute "set search_path to #{right_schema}, public"
54
+ @adapter.with_search_path([wrong_schema,'public']) do
55
+ @adapter.send(:search_path).must_equal [wrong_schema, 'public']
56
+ end
57
+ @adapter.send(:search_path).must_equal [right_schema, 'public']
58
+ end
59
+
60
+ it "returns the first schema in the search path that contains a table when called to" do
61
+ @adapter.execute "create table #{right_schema}.#{test_table} (var1 integer)"
62
+ @adapter.execute "set search_path to #{wrong_schema}, #{right_schema}, 'public'"
63
+ @adapter.send(:first_schema_for, test_table).must_equal right_schema
64
+ end
65
+
66
+ it "finds a table when it exists in the right schema" do
67
+ @adapter.execute "create table #{test_table_right_schema} (var1 integer)"
68
+ @adapter.table_exists?(test_table_right_schema).must_equal true
69
+ end
70
+
71
+ it "does not find a table when it does not exist in the right schema" do
72
+ @adapter.table_exists?(test_table_right_schema).must_equal false
73
+ end
74
+
75
+ it "does not find a table when it exists in the wrong schema" do
76
+ @adapter.execute "create table #{test_table_wrong_schema} (var1 integer)"
77
+ @adapter.table_exists?(test_table_right_schema).must_equal false
78
+ end
79
+
80
+ it "creates a table in the right schema when called to" do
81
+ @adapter.with_search_path([right_schema,'public']) do
82
+ @adapter.with_tracking do
83
+ @adapter.create_table test_table_right_schema, nil, '(var1 text)'
84
+ @adapter.table_exists?(test_table_right_schema).must_equal true
85
+ end
86
+ end
87
+ end
88
+
89
+ it "drops a table in the right schema when called to" do
90
+ @adapter.with_search_path([right_schema,'public']) do
91
+ @adapter.with_tracking do
92
+
93
+ @adapter.execute "create table #{test_table_right_schema} (var1 text)"
94
+ @adapter.execute "create table #{test_table_wrong_schema} (var1 text)"
95
+ @adapter.drop_table test_table_right_schema
96
+ @adapter.table_exists?(test_table_right_schema).must_equal false
97
+ @adapter.table_exists?(test_table_wrong_schema).must_equal true
98
+
99
+ end
100
+ end
101
+ end
102
+
103
+ it "creates a view in the right schema when called to" do
104
+ @adapter.with_search_path([right_schema,'public']) do
105
+ @adapter.with_tracking do
106
+
107
+ @adapter.create_view test_view_right_schema, "select * from information_schema.tables limit 0"
108
+ @adapter.view_exists?(test_view_right_schema).must_equal true
109
+
110
+ end
111
+ end
112
+ end
113
+
114
+ it "drops a view in the right schema when called to" do
115
+ @adapter.with_search_path([right_schema,'public']) do
116
+ @adapter.with_tracking do
117
+
118
+ @adapter.create_view test_view_right_schema, "select * from information_schema.tables limit 0"
119
+ @adapter.drop_view test_view_right_schema
120
+ @adapter.view_exists?(test_view_right_schema).must_equal false
121
+
122
+ end
123
+ end
124
+ end
125
+
126
+ it "updates the tracking table in the right schema when it creates a table" do
127
+ @adapter.with_search_path([right_schema,'public']) do
128
+ @adapter.with_tracking do
129
+
130
+ @adapter.create_table test_table_right_schema, nil, '(var1 integer)'
131
+ tracked_create = Sql.get_single_int(
132
+ @adapter.execute <<-EOSQL
133
+ select 1 from #{right_schema}.#{Db::TABLE_TRACKER_NAME}
134
+ where
135
+ relation_name = '#{test_table}' and
136
+ relation_type = '#{@adapter.relation_type_values[:table]}' and
137
+ operation = '#{@adapter.operation_values[:create]}'
138
+ EOSQL
139
+ )
140
+ tracked_create.must_equal 1
141
+
142
+ end
143
+ end
144
+ end
145
+
146
+ it "updates the tracking table in the right schema when it drops a table" do
147
+ @adapter.with_search_path([right_schema,'public']) do
148
+ @adapter.with_tracking do
149
+
150
+ @adapter.create_table test_table_right_schema, nil, '(var1 text)'
151
+ @adapter.drop_table test_table_right_schema
152
+ still_tracking_table = Sql.get_single_int(
153
+ @adapter.execute <<-EOSQL
154
+ select 1 from #{right_schema}.#{Db::TABLE_TRACKER_NAME}
155
+ where
156
+ relation_name = '#{test_table}' and
157
+ relation_type = '#{@adapter.relation_type_values[:table]}'
158
+ EOSQL
159
+ )
160
+ still_tracking_table.must_be_nil
161
+
162
+ end
163
+ end
164
+ end
165
+
166
+ it "updates the tracking table in the right schema on insert to a tracked table" do
167
+ @adapter.with_search_path([right_schema,'public']) do
168
+ @adapter.with_tracking do
169
+
170
+ @adapter.create_table test_table_right_schema, nil, '(var1 text)'
171
+ @adapter.execute "insert into #{test_table_right_schema} values ('a')"
172
+ tracked_insert = Sql.get_single_int(
173
+ @adapter.execute <<-EOSQL
174
+ select 1 from #{right_schema}.#{Db::TABLE_TRACKER_NAME}
175
+ where
176
+ relation_name = '#{test_table}' and
177
+ relation_type = '#{@adapter.relation_type_values[:table]}' and
178
+ operation = '#{@adapter.operation_values[:insert]}'
179
+ EOSQL
180
+ )
181
+ tracked_insert.must_equal 1
182
+
183
+ end
184
+ end
185
+ end
186
+
187
+ it "updates the tracking table in the right schema on update on a tracked table" do
188
+ @adapter.with_search_path([right_schema,'public']) do
189
+ @adapter.with_tracking do
190
+
191
+ @adapter.create_table test_table_right_schema, nil, '(var1 text, var2 text)'
192
+ @adapter.execute "insert into #{test_table_right_schema} values ('a', 'a')"
193
+ @adapter.execute "update #{test_table_right_schema} set var2 = 'b' where var1 = 'a'"
194
+
195
+ tracked_insert = Sql.get_single_int(
196
+ @adapter.execute <<-EOSQL
197
+ select 1 from #{right_schema}.#{Db::TABLE_TRACKER_NAME}
198
+ where
199
+ relation_name = '#{test_table}' and
200
+ relation_type = '#{@adapter.relation_type_values[:table]}' and
201
+ operation = '#{@adapter.operation_values[:update]}'
202
+ EOSQL
203
+ )
204
+ tracked_insert.must_equal 1
205
+
206
+ end
207
+ end
208
+ end
209
+
210
+ it "updates the tracking table in the right schema on truncate of a tracked table" do
211
+ @adapter.with_search_path([right_schema,'public']) do
212
+ @adapter.with_tracking do
213
+
214
+ @adapter.create_table test_table_right_schema, nil, '(var1 text)'
215
+ @adapter.truncate_table test_table_right_schema
216
+ tracked_truncate = Sql.get_single_int(
217
+ @adapter.execute <<-EOSQL
218
+ select 1 from #{right_schema}.#{Db::TABLE_TRACKER_NAME}
219
+ where
220
+ relation_name = '#{test_table}' and
221
+ relation_type = '#{@adapter.relation_type_values[:table]}' and
222
+ operation = '#{@adapter.operation_values[:truncate]}'
223
+ EOSQL
224
+ )
225
+ tracked_truncate.must_equal 1
226
+
227
+ end
228
+ end
229
+ end
230
+
231
+ it "says it is tracking tables after tracking is set up in the right schema" do
232
+ @adapter.with_search_path([right_schema,'public']) do
233
+ @adapter.tear_down_tracking
234
+ @adapter.set_up_tracking
235
+ (@adapter.tracking_tables?).must_equal true
236
+ end
237
+ end
238
+
239
+ it "says it is not tracking tables after tracking is torn down in the right schema" do
240
+ @adapter.with_search_path([right_schema,'public']) do
241
+ @adapter.tear_down_tracking
242
+ (@adapter.tracking_tables?).must_equal false
243
+ end
244
+ end
245
+
246
+ end
247
+
248
+ end
249
+ end
data/test/sql_spec.rb ADDED
@@ -0,0 +1,46 @@
1
+ require_relative './helper.rb'
2
+
3
+ module Rake
4
+ module DataTask
5
+
6
+ describe Sql do
7
+
8
+ around do |test|
9
+ @adapter = get_adapter
10
+ @adapter.with_transaction_rollback do
11
+ test.call
12
+ end
13
+ end
14
+
15
+ context "when asked to parse a single value" do
16
+ it "raises an error if the results array contains more than one column" do
17
+ r = @adapter.execute('select 1,2')
18
+ lambda {Sql.parse_single_value(r)}.must_raise(TypeError)
19
+ end
20
+ it "raises an error if the results array contains more than one row" do
21
+ r = @adapter.execute('select 1 union all select 2')
22
+ lambda {Sql.parse_single_value(r)}.must_raise(TypeError)
23
+ end
24
+ it "returns nil if the results array contains no rows" do
25
+ r = @adapter.execute("select 1 where #{@adapter.falsey_value}")
26
+ Sql.parse_single_value(r).must_be_nil
27
+ end
28
+ it "returns nil if the results array contains a null value" do
29
+ r = @adapter.execute('select NULL')
30
+ Sql.parse_single_value(r).must_be_nil
31
+ end
32
+ end
33
+
34
+ context "when asked for a single integer" do
35
+ it "returns a single integer if the query result is a single value convertible to an integer" do
36
+ Sql.get_single_int(@adapter.execute('select 1')).must_be_kind_of Integer
37
+ end
38
+ it "raises an error if the query results in a single non-integer" do
39
+ lambda {Sql.get_single_int(@adapter.execute("select 'a'"))}.must_raise(ArgumentError)
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,34 @@
1
+ module Rake
2
+ module DataTask
3
+ module DataCreation
4
+ OLDDATA = "old_data"
5
+ NEWDATA = "new_data"
6
+
7
+ def create_timed_data(adapter, old_data_name, *new_data_names)
8
+ old_data = Data.new(old_data_name, adapter)
9
+ return if (old_data.exists? &&
10
+ new_data_names.all? do |new_data_name|
11
+ new_data = Data.new(new_data_name, adapter)
12
+ new_data.exists? && new_data.mtime > old_data.mtime
13
+ end)
14
+ now = Time.now
15
+
16
+ create_data(adapter, old_data_name)
17
+ sleep(1.0)
18
+
19
+ new_data_names.each do |new_data_name|
20
+ create_data(adapter, new_data_name)
21
+ end
22
+ end
23
+
24
+ def create_data(adapter, name)
25
+ adapter.create_data name, nil, '(var1 integer, var2 integer)' unless adapter.data_exists?(name)
26
+ adapter.data_mtime(name)
27
+ end
28
+
29
+ def drop_data(adapter, name)
30
+ adapter.drop_data(name) rescue nil
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,207 @@
1
+ require File.expand_path(
2
+ File.join(Gem::Specification.find_by_name('rake').gem_dir,'test/helper.rb'), __FILE__)
3
+ require 'minitest/around/unit'
4
+ require_relative './table_creation.rb'
5
+ require_relative './helper.rb'
6
+
7
+ module Rake
8
+ module DataTask
9
+
10
+ class TestRakeDataTask < Rake::TestCase
11
+ include Rake
12
+ include DataCreation
13
+
14
+ def around(&block)
15
+ @adapter = get_adapter
16
+ @adapter.with_transaction_rollback do
17
+ yield
18
+ end
19
+ end
20
+
21
+ def setup
22
+ super
23
+
24
+ Task.clear
25
+ @runs = Array.new
26
+ end
27
+
28
+ def test_data_need
29
+ @adapter.with_tracking do
30
+ name = "dummy"
31
+ data @adapter[name]
32
+ ttask = Task[name]
33
+
34
+ Data.drop(ttask.name) rescue nil
35
+ assert ttask.needed?, "data should be needed"
36
+
37
+ @adapter.create_data name, nil, '(var1 integer)'
38
+
39
+ assert_equal nil, ttask.prerequisites.collect{|n| Task[n].timestamp}.max
40
+ assert ! ttask.needed?, "data should not be needed"
41
+ end
42
+ end
43
+
44
+ def test_data_times_new_depends_on_old
45
+ @adapter.with_tracking do
46
+ create_timed_data(@adapter, OLDDATA, NEWDATA)
47
+
48
+ t1 = Rake.application.intern(DataTask, @adapter[NEWDATA]).enhance([@adapter[OLDDATA]])
49
+ t2 = Rake.application.intern(DataTask, @adapter[OLDDATA])
50
+ assert ! t2.needed?, "Should not need to build old data"
51
+ assert ! t1.needed?, "Should not need to rebuild new data because of old"
52
+ end
53
+ end
54
+
55
+ def test_data_times_new_depend_on_regular_task_timestamps
56
+ @adapter.with_tracking do
57
+ load_phony
58
+
59
+ name = "dummy"
60
+ task name
61
+
62
+ create_timed_data(@adapter, NEWDATA)
63
+
64
+ t1 = Rake.application.intern(DataTask, @adapter[NEWDATA]).enhance([name])
65
+
66
+ assert t1.needed?, "depending on non-data task uses Time.now"
67
+
68
+ task(name => :phony)
69
+
70
+ assert t1.needed?, "unless the non-data task has a timestamp"
71
+ end
72
+ end
73
+
74
+ def test_data_times_old_depends_on_new
75
+ @adapter.with_tracking do
76
+ create_timed_data(@adapter, OLDDATA, NEWDATA)
77
+
78
+ t1 = Rake.application.intern(DataTask, @adapter[OLDDATA]).enhance([@adapter[NEWDATA]])
79
+ t2 = Rake.application.intern(DataTask, @adapter[NEWDATA])
80
+ assert ! t2.needed?, "Should not need to build new data"
81
+ preq_stamp = t1.prerequisites.collect{|t| Task[t].timestamp}.max
82
+ assert_equal t2.timestamp, preq_stamp
83
+ assert t1.timestamp < preq_stamp, "T1 should be older"
84
+ assert t1.needed?, "Should need to rebuild old data because of new"
85
+ end
86
+ end
87
+
88
+ def test_data_depends_on_task_depend_on_data
89
+ @adapter.with_tracking do
90
+ create_timed_data(@adapter, OLDDATA, NEWDATA)
91
+
92
+ data @adapter[NEWDATA] => [:obj] do |t| @runs << t.name end
93
+ task :obj => [OLDDATA] do |t| @runs << t.name end
94
+ data @adapter[OLDDATA] do |t| @runs << t.name end
95
+
96
+ Task[:obj].invoke
97
+ Task[NEWDATA].invoke
98
+ assert @runs.include?(NEWDATA)
99
+ end
100
+ end
101
+
102
+ def test_existing_data_depends_on_non_existing_data
103
+ @adapter.with_tracking do
104
+ @ran = false
105
+
106
+ create_data(@adapter, OLDDATA)
107
+ drop_data(@adapter, NEWDATA)
108
+ data @adapter[NEWDATA] do
109
+ @ran = true
110
+ end
111
+
112
+ data @adapter[OLDDATA] => NEWDATA
113
+
114
+ Task[OLDDATA].invoke
115
+
116
+ assert @ran
117
+ end
118
+ end
119
+
120
+ def test_data_depends_on_new_file
121
+ @adapter.with_tracking do
122
+ create_timed_data(@adapter, OLDDATA, NEWDATA)
123
+ sleep(1)
124
+
125
+ file NEWFILE do
126
+ create_file(NEWFILE)
127
+ end
128
+ Task[NEWFILE].invoke
129
+
130
+ @ran = false
131
+ data NEWDATA => NEWFILE do
132
+ @ran = true
133
+ end
134
+
135
+ Task[NEWDATA].invoke
136
+ assert @ran, "Should have run the data task with an updated file dependency."
137
+ end
138
+ end
139
+
140
+ def test_data_depends_on_new_file
141
+ @adapter.with_tracking do
142
+ file NEWFILE do
143
+ create_file(NEWFILE)
144
+ end
145
+ Task[NEWFILE].invoke
146
+
147
+ sleep(1)
148
+ create_timed_data(@adapter, OLDDATA, NEWDATA)
149
+
150
+ @ran = false
151
+ data @adapter[NEWDATA] => NEWFILE do
152
+ @ran = true
153
+ end
154
+
155
+ Task[@adapter[NEWDATA]].invoke
156
+ assert !@ran, "Should not have run the data task with an old file dependency."
157
+ end
158
+ end
159
+
160
+ def test_file_depends_on_new_data
161
+ @adapter.with_tracking do
162
+ create_file(NEWFILE)
163
+ sleep(1)
164
+
165
+ data @adapter[NEWDATA] do
166
+ create_timed_data(@adapter, OLDDATA, NEWDATA)
167
+ end
168
+ Task[@adapter[NEWDATA]].invoke
169
+
170
+ @ran = false
171
+ file NEWFILE => @adapter[NEWDATA] do
172
+ @ran = true
173
+ end
174
+
175
+ Task[@adapter[NEWFILE]].invoke
176
+ assert @ran, "Should have run the file task with an updated data dependency."
177
+ end
178
+ end
179
+
180
+ def test_file_depends_on_old_data
181
+ @adapter.with_tracking do
182
+ data @adapter[NEWDATA] do
183
+ create_timed_data(@adapter, OLDDATA, NEWDATA)
184
+ end
185
+ Task[@adapter[NEWDATA]].invoke
186
+
187
+ sleep(1)
188
+ create_file(NEWFILE)
189
+
190
+ @ran = false
191
+ file NEWFILE => @adapter[NEWDATA] do
192
+ @ran = true
193
+ end
194
+
195
+ Task[@adapter[NEWFILE]].invoke
196
+ assert !@ran, "Should not have run the file task with an old data dependency."
197
+ end
198
+ end
199
+
200
+ def load_phony
201
+ load File.join(@rake_lib, "rake/phony.rb")
202
+ end
203
+
204
+ end
205
+
206
+ end
207
+ end