db-factory 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ == 0.1.0 2013-04-15
2
+
3
+ * Initial release
4
+ * Known limitations
5
+ * No YAML file validation
6
+ * Missing comments
7
+ * Documentation
8
+ * Examples
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,12 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'db-factory'
3
+ s.version = '0.1.1'
4
+ s.date = '2013-04-16'
5
+ s.summary = "Prepare and compare test data for PL/SQL unit testing"
6
+ s.description = "db-factory uses definition files with YAML/ERB syntax to define test data (preconditions) for unit PL/SQL testing and can compare actual data with defined expected data."
7
+ s.authors = ["Stefan Cibiri"]
8
+ s.email = 'stefancibiri@yahoo.com'
9
+ s.files = ["History.txt", "VERSION", "db-factory.gemspec", "examples/simple_example_1/Readme.txt", "examples/simple_example_1/create_objects.sql", "examples/simple_example_1/simple_example_1.yml", "examples/simple_example_1/simple_example_1_spec.rb", "examples/simple_example_1/spec_helper.rb", "lib/config.yml", "lib/db-factory.rb", "lib/db_factory/db_factory.rb", "lib/db_factory/helpers.rb", "lib/db_factory/version.rb"]
10
+ s.homepage = "https://github.com/stci/db-factory"
11
+ s.add_dependency('ruby-plsql')
12
+ end
@@ -0,0 +1,23 @@
1
+ Simple Example 1 Readme
2
+ -----------------------
3
+
4
+ 1) run "create_objects.sql" under e.g. SCOTT schema (which normaly should be schema where your application and data is)
5
+
6
+ 2) create user TEST_USER with there privileges
7
+ grant connect to TEST_USER;
8
+ grant delete_catalog_role to TEST_USER;
9
+ grant execute_catalog_role to TEST_USER;
10
+ grant select_catalog_role to TEST_USER;
11
+ grant alter any table to TEST_USER;
12
+ grant create role to TEST_USER;
13
+ grant delete any table to TEST_USER;
14
+ grant execute any procedure to TEST_USER;
15
+ grant flashback any table to TEST_USER;
16
+ grant insert any table to TEST_USER;
17
+ grant select any dictionary to TEST_USER;
18
+ grant select any sequence to TEST_USER;
19
+ grant select any table to TEST_USER;
20
+ grant update any table to TEST_USER;
21
+
22
+ 3) execute in command line:
23
+ rspec simple_example_1_spec.rb
@@ -0,0 +1,35 @@
1
+ create table DEPARTEMENTS
2
+ (
3
+ departement_id NUMBER,
4
+ departement_name VARCHAR2(30),
5
+ coeficient number(5,2)
6
+ );
7
+
8
+ create table EMPLOYEES
9
+ (
10
+ employee_id NUMBER,
11
+ departement_id NUMBER,
12
+ first_name VARCHAR2(30),
13
+ last_name VARCHAR2(30),
14
+ hire_date DATE,
15
+ phone_number VARCHAR2(30),
16
+ salary NUMBER(12,2)
17
+ );
18
+
19
+ create or replace function salary_update(dep_id_from number,
20
+ dep_id_to number) return number is
21
+ begin
22
+ update employees e
23
+ set e.salary = e.salary *
24
+ (select coeficient
25
+ from departements d
26
+ where d.departement_id = e.departement_id)
27
+ where e.departement_id between dep_id_from and dep_id_to;
28
+
29
+ return 0;
30
+
31
+ exception
32
+ when others then
33
+ dbms_output.put_line(sqlerrm);
34
+ return 1;
35
+ end;
@@ -0,0 +1,63 @@
1
+ --- %FORMAT: 1.0
2
+
3
+ CONSTANTS:
4
+ - HIRE_DATE : &HD
5
+ <%= Date.today %>
6
+
7
+ DEFAULTS:
8
+ tables:
9
+
10
+ - "scott.employees": {
11
+ :hire_date : *HD,
12
+ :last_name : 'SMITH'
13
+ }
14
+
15
+ COMMON:
16
+ setup:
17
+ tables:
18
+
19
+ - "scott.departements":
20
+ delete:
21
+ condition: "1=1"
22
+ columns:
23
+ [:departement_id, :departement_name, :coeficient]
24
+ data:
25
+ - [1 , 'IT' , 1.15 ]
26
+ - [2 , 'Administration' , 1.10 ]
27
+ - [3 , 'Sales' , 0.95 ]
28
+ - [4 , 'Other' , ~ ]
29
+
30
+ SALARY:
31
+ setup:
32
+ tables:
33
+
34
+ - "scott.employees":
35
+ delete:
36
+ condition: "last_name = 'SMITH'"
37
+ columns:
38
+ [:employee_id, :departement_id, :first_name, :salary]
39
+ data:
40
+ - [1 , 1 , 'STEVE' , 100.00 ]
41
+ - [2 , 1 , 'BOB' , 120.00 ]
42
+ - [3 , 2 , 'COLIN' , 180.30 ]
43
+ - [4 , 3 , 'PHIL' , 280.00 ]
44
+ - [5 , 4 , 'JHON' , 88.00 ]
45
+
46
+ postconditions:
47
+ tables:
48
+
49
+ - "scott.employees":
50
+ filter: "last_name = 'SMITH' and departement_id <> 4"
51
+ connect keys: [:employee_id]
52
+ columns:
53
+ [:employee_id, :salary]
54
+ stage-1:
55
+ - [1 , 115.00 ]
56
+ - [2 , 138.00 ]
57
+ - [3 , 198.33 ]
58
+ - [4 , 280.00 ]
59
+ expected data:
60
+ - [1 , 115.00 ]
61
+ - [2 , 138.00 ]
62
+ - [3 , 198.33 ]
63
+ - [4 , 266.00 ]
@@ -0,0 +1,21 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Simple example 1" do
4
+
5
+ DBFactory.load('simple_example_1.yml')
6
+
7
+ before(:all) do
8
+ DBFactory.setup('SALARY')
9
+ end
10
+
11
+ it "should update salary of employees according coeficient defined on department" do
12
+
13
+ plsql.scott.salary_update(1, 2).should == 0
14
+ DBFactory.evaluate('stage-1').should == true
15
+
16
+ plsql.scott.salary_update(3, 3).should == 0
17
+ DBFactory.evaluate().should == true
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,52 @@
1
+ require "rspec"
2
+ require "ruby-plsql"
3
+ require "db-factory"
4
+
5
+ $LOAD_PATH << File.dirname(__FILE__) + '/factories'
6
+
7
+ # Establish connection to database where tests will be performed.
8
+ # Change according to your needs.
9
+ DATABASE_USER = "TEST_USER"
10
+ DATABASE_PASSWORD = "TEST_USER"
11
+ DATABASE_NAME = "DEV_DB" # TNS
12
+
13
+ plsql.connect! DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAME
14
+
15
+ # Set autocommit to false so that automatic commits after each statement are _not_ performed
16
+ plsql.connection.autocommit = false
17
+ # reduce network traffic in case of large resultsets
18
+ plsql.connection.prefetch_rows = 100
19
+ # uncomment to log DBMS_OUTPUT to standard output
20
+ # plsql.dbms_output_stream = STDOUT
21
+
22
+ # Do logoff when exiting to ensure that session temporary tables
23
+ # (used when calling procedures with table types defined in packages)
24
+ at_exit do
25
+ plsql.logoff
26
+ end
27
+
28
+ RSpec.configure do |config|
29
+ config.before(:each) do |test|
30
+ plsql.savepoint "before_each"
31
+
32
+ plsql.sys.dbms_application_info.set_module(:module_name => test_filename, :action_name => test.example.metadata[:example_group][:full_description]);
33
+ end
34
+ config.after(:each) do
35
+ # Always perform rollback to savepoint after each test
36
+ plsql.rollback_to "before_each"
37
+ end
38
+ config.after(:all) do
39
+ # Always perform rollback after each describe block
40
+ plsql.rollback
41
+ end
42
+ end
43
+
44
+ # require all helper methods which are located in any helpers subdirectories
45
+ Dir[File.dirname(__FILE__) + '/**/helpers/*.rb'].each {|f| require f}
46
+
47
+ # require all factory modules which are located in any factories subdirectories
48
+ Dir[File.dirname(__FILE__) + '/**/factories/*.rb'].each {|f| require f}
49
+
50
+ # Add source directory to load path where PL/SQL example procedures are defined.
51
+ # It is not required if PL/SQL procedures are already loaded in test database in some other way.
52
+ $:.push File.dirname(__FILE__) + '/../../source'
@@ -0,0 +1,15 @@
1
+ # configuration file
2
+
3
+ GENERAL:
4
+ supported format:
5
+ - 1.0
6
+ validation schema: validator.yml
7
+
8
+ LOG:
9
+ path : .
10
+ filename : <%= "DBFactory.#{Date.today().to_s()}.log" %>
11
+ date format: "%Y-%m-%d %H:%M:%S"
12
+ level : <%= Logger::DEBUG %>
13
+
14
+ DIFF:
15
+ filename_tpl: "diff-#CASE#.yml"
@@ -0,0 +1,5 @@
1
+ ROOT = File.expand_path(File.dirname(__FILE__))
2
+
3
+ %w(db_factory version helpers).each do |file|
4
+ require "db_factory/#{file}"
5
+ end
@@ -0,0 +1,532 @@
1
+ require "logger"
2
+ require "bigdecimal/util"
3
+ require "yaml"
4
+ require "erb"
5
+
6
+ class DBFactoryClass
7
+
8
+ @cfg
9
+ @log
10
+ @definition
11
+ @case
12
+ @scn
13
+
14
+ def initialize()
15
+ l_erb = ERB.new(File.read("#{ROOT}/config.yml"))
16
+ @cfg = YAML.load(l_erb.result(binding).to_s)
17
+
18
+ @log = Logger.new("#{@cfg['LOG']['path']}/#{@cfg['LOG']['filename']}")
19
+ @log.info "================ Initializing DBFactory ================"
20
+ @log.debug "[ DBFactory::initialize(...) ]"
21
+
22
+ @log.datetime_format = @cfg['LOG']['date format']
23
+ @log.level = @cfg['LOG']['level']
24
+
25
+ return self
26
+ end
27
+
28
+ def load(definition_file)
29
+ @log.debug "[ DBFactory::load(...) ]"
30
+ @log.debug " > definition_file = <#{definition_file}>"
31
+
32
+ begin
33
+ @log.debug "Reading constants ..."
34
+ constants = YAML.load_file(definition_file)['CONSTANTS'].reduce(Hash.new, :merge)
35
+ rescue Exception => e
36
+ @log.error "[ DBFactory::load(...) ] ERROR: <#{e.to_s}>"
37
+ @log.error "Error occurred during load of definition file <#{definition_file}>"
38
+ raise "Check defintion file <#{definition_file}>: #{e.to_s}"
39
+ end
40
+
41
+ @log.debug "Injecting constants ..."
42
+ l_erb = ERB.new(File.read(definition_file).strip.gsub(/^\s*#.*/,'').gsub(/\n^\s*$/,''))
43
+
44
+ @log.debug "Parsing defintion file ..."
45
+ @definition = YAML.load(l_erb.result(binding).to_s)
46
+ @case = 'COMMON'
47
+
48
+ # puts @cfg['GENERAL']['supported_file_format_versions'].to_s
49
+ # puts @definition['FORMAT VERSION']
50
+ # puts @definition.inspect
51
+ raise "Unsupported version of YAML file. Supported file versions: #{@cfg['GENERAL']['supported format'].to_s}" unless @cfg['GENERAL']['supported
52
+ format'].include?(@definition['%FORMAT'])
53
+ end
54
+
55
+ def setup(casename = 'COMMON')
56
+ @log.debug "[ DBFactory::setup(...) ]"
57
+ @log.debug " > casename = <#{casename}>"
58
+
59
+ if casename != 'COMMON'
60
+ setup()
61
+ else
62
+ @scn = plsql.select("select current_scn from v$database")[0][:current_scn]
63
+ @log.info("CURRENT SCN from DB = <#{@scn}>")
64
+ end
65
+
66
+ @case = casename
67
+
68
+ if @definition[@case].include?('cleanup')
69
+ @definition[@case]['cleanup']['tables'].each { |t|
70
+ cleanup_table(t)
71
+ }
72
+ end
73
+
74
+ # puts @definition[@case] unless casename == 'COMMON'
75
+ @definition[@case]['setup']['tables'].each { |t|
76
+ prepare_table(t)
77
+ }
78
+
79
+ end
80
+
81
+ def check_postconditions(stage = nil)
82
+ @log.debug "[ DBFactory::check_postconditions(...) ]"
83
+ l_result = true
84
+ raise "Use stage name (must contain string 'stage-[stage name]'" unless stage.nil? || stage.include?('stage')
85
+ # l_stage = "stage-#{stage}" unless stage == nil
86
+ l_diff_file = @cfg['DIFF']['filename_tpl'].gsub('#CASE#',@case)
87
+ File.delete(l_diff_file) unless not File.exist?(l_diff_file)
88
+ @definition[@case]['postconditions']['tables'].each { |t|
89
+ if stage.nil? || t.values[0].include?(stage)
90
+ l_result_tmp = check_postconditions_for_table(t, stage)
91
+ l_result = l_result_tmp unless l_result == false
92
+ end
93
+ }
94
+
95
+ if l_result
96
+ @log.info("> DBFactory::evaluate(...) ... PASS")
97
+ else
98
+ @log.warn("> DBFactory::evaluate(...) ... FAIL")
99
+ end
100
+
101
+ return l_result
102
+ end
103
+
104
+ def flashback()
105
+ @log.debug "[ DBFactory::flashback(...) ]"
106
+ l_result = true
107
+ @definition[@case]['flashback']['tables'].each { |t|
108
+ table_owner = t.split('.')[0].upcase
109
+ table_name = t.split('.')[1].upcase
110
+ @log.debug " > table_owner = <#{table_owner}>"
111
+ @log.debug " > table_name = <#{table_name}>"
112
+ @log.debug " > scn = <#{@scn}>"
113
+ l_result = plsql.flashback_table(table_owner, table_name, @scn) unless l_result == false
114
+ @log.debug "...DONE"
115
+ }
116
+ @scn = nil
117
+
118
+ return l_result
119
+ end
120
+
121
+ private
122
+
123
+ def execute_sql(sql_command)
124
+ @log.debug "[ DBFactory::sql_command(...) ]"
125
+ @log.debug " > sql_command = <#{sql_command}>"
126
+
127
+ begin
128
+ eval("plsql.execute('#{sql_command}')")
129
+ rescue Exception => e
130
+ @log.error "[ DBFactory::execute_sql(...) ] ERROR: <#{e.to_s}>"
131
+ @log.error "Error occurred during execution of SQL command <#{sql_command}>"
132
+ raise
133
+ end
134
+ end
135
+
136
+ def cleanup_table(table)
137
+ @log.debug "[ DBFactory::cleanup_table(...) ]"
138
+
139
+ begin
140
+ # l_delete = @definition[@case]['cleanup']['tables'][tablename]['delete']['condition']
141
+ l_delete = table.values[0]['delete']['condition']
142
+ @log.debug "Using delete condition: <#{l_delete}>"
143
+ l_object_id = plsql.find_table(table.keys[0])
144
+ l_object_id.delete("WHERE #{l_delete}")
145
+ rescue Exception => e
146
+ @log.error "[ DBFactory::cleanup(...) ] ERROR: <#{e.to_s}>"
147
+ # @log.warn "Error occured during cleanup procedure on table <#{tablename}> failed in <#{@case}> block"
148
+ # raise "Error occured during cleanup procedutre on table <#{tablename}> failed in <#{@case}> block"
149
+ raise "Error occured during cleanup procedutre"
150
+ end
151
+ end
152
+
153
+ def delete(table)
154
+ @log.debug "[ DBFactory::delete(...) ]"
155
+
156
+ begin
157
+ if table.values[0].include?('delete')
158
+ l_delete = table.values[0]['delete']['condition']
159
+ else
160
+ l_delete = '1=0'
161
+ end
162
+ l_object_id = plsql.find_table(table.keys[0])
163
+ l_object_id.delete("WHERE #{l_delete}") unless l_delete.nil?
164
+ rescue Exception => e
165
+ @log.error " >> ERROR: <#{e.to_s}>"
166
+ raise "Failed on delete statement."
167
+ end
168
+ end
169
+
170
+ def insert(table)
171
+ @log.debug "[ DBFactory::insert(...) ]"
172
+
173
+ begin
174
+ tablename = table.keys[0]
175
+ l_data = data(table)
176
+ l_object_id = plsql.find_table(tablename)
177
+ l_object_id.insert(l_data)
178
+ @log.info("#{l_data.size} records were inserted into <#{tablename}> table")
179
+ rescue Exception => e
180
+ raise "[ DBFactory::insert ]: Failed insert data. #{e.to_s}"
181
+ end
182
+ end
183
+
184
+ def sql_statement(table)
185
+ @log.debug "[ DBFactory::sql_statement(...) ]"
186
+
187
+ begin
188
+ l_statement = table.values[0]['sql statement']
189
+ execute_sql(l_statement) unless l_statement.nil?
190
+ rescue Exception => e
191
+ @log.error " >> ERROR: <#{e.to_s}>"
192
+ raise "Failed on SQL statement"
193
+ end
194
+ end
195
+
196
+ def other_column_data(table)
197
+ @log.debug "[ DBFactory::other_column_data(...) ]"
198
+
199
+ tablename = table.keys[0]
200
+ begin
201
+ l_object_id = plsql.find_table(tablename)
202
+ l_other_column_data = l_object_id.first || {}
203
+ rescue Exception => e
204
+ raise "[ DBFactory::other_column_data ]: #{e.to_s}"
205
+ end
206
+
207
+ return l_other_column_data
208
+ end
209
+
210
+ def defaults(table)
211
+ @log.debug "[ DBFactory::defaults(...) ]"
212
+
213
+ l_other_column_data = other_column_data(table)
214
+
215
+ tablename = table.keys[0]
216
+ l_defaults1 = {}
217
+ begin
218
+ @definition['DEFAULTS']['tables'].each { |t|
219
+ if t.keys[0] == table.keys[0]
220
+ l_defaults1 = t.values[0] || {}
221
+ break
222
+ end
223
+ }
224
+ # if l_defaults1 != nil
225
+ # @log.debug "Loading of defaults for table <#{tablename}> was succesfull in <DEFAULTS> block"
226
+ # else
227
+ # l_defaults1 = {}
228
+ # end
229
+ rescue Exception => e
230
+ @log.error "Error: <#{e.to_s}>"
231
+ @log.warn "Loading of defaults for table <#{tablename}> failed in <DEFAULTS> block"
232
+ end
233
+
234
+ l_defaults2 = {}
235
+ begin
236
+ l_defaults2 = table.values[0]['defaults'] || {}
237
+ # if l_defaults2 != nil
238
+ # @log.debug "Loading of defaults for table <#{tablename}> was succesfull in <#{@case}> block"
239
+ # else
240
+ # l_defaults2 = {}
241
+ # end
242
+ rescue Exception => e
243
+ @log.error "[ DBFactory::defaults(...) ] ERROR: <#{e.to_s}>"
244
+ @log.warn "Loading of defaults for table <#{tablename}> failed in <#{@case}> block"
245
+ end
246
+
247
+ return(l_other_column_data.merge(l_defaults1).merge(l_defaults2))
248
+ end
249
+
250
+ def data(table)
251
+ @log.debug "[ DBFactory::data(...) ]"
252
+
253
+ table_def = table[table.keys[0]]
254
+ begin
255
+ l_data = []
256
+ l_defaults = defaults(table)
257
+ l_data_tmp = table_def['data']
258
+
259
+ case
260
+ when l_data_tmp.nil?
261
+ l_data = []
262
+ when l_data_tmp[0].instance_of?(Hash)
263
+ l_data_tmp.collect! { |values|
264
+ values = l_defaults.merge(values)
265
+ }
266
+ l_data = l_data_tmp
267
+ when l_data_tmp[0].instance_of?(Array)
268
+ l_columns = table_def['columns']
269
+ l_data_tmp.each_index { |i|
270
+ l_data[i] = {}
271
+ l_data_tmp[i].each_index { |k|
272
+ l_data[i] = l_data[i].merge({l_columns[k] => l_data_tmp[i][k]})
273
+ }
274
+ }
275
+ l_data.collect! { |values|
276
+ values = l_defaults.merge(values)
277
+ }
278
+ end
279
+ rescue Exception => e
280
+ raise "[ DBFactory::data ]: Failed loading of data. #{e.to_s}"
281
+ end
282
+
283
+ # convert FLOAT - otherwise OCI error will be rasied
284
+ l_data.each_index {|i|
285
+ l_data[i].each { |k,v|
286
+ l_data[i][k] = v.instance_of?(Float) ? v.to_d : v
287
+ }
288
+ }
289
+ return l_data
290
+ end
291
+
292
+ def prepare_table(table)
293
+ @log.debug "[ DBFactory::prepare_table(...) ]"
294
+ @log.debug ">> table: #{table.keys[0]}"
295
+
296
+ begin
297
+ tablename = table.keys[0]
298
+ l_object_id = plsql.find_table(tablename)
299
+ rescue Exception => e
300
+ raise "Failed to find table <#{tablename}> in DB"
301
+ end
302
+
303
+ # delete data according to delete condition in file
304
+ delete(table)
305
+
306
+ # execute SQL statement
307
+ sql_statement(table)
308
+
309
+ # insert defined data
310
+ insert(table)
311
+
312
+ end
313
+
314
+ def expected_columns(table)
315
+ l_data_tmp = table.values[0]['expected data']
316
+ case
317
+ when l_data_tmp[0].instance_of?(Hash)
318
+ l_expected_columns = l_data_tmp[0].keys
319
+ when l_data_tmp[0].instance_of?(Array)
320
+ l_expected_columns = table.values[0]['columns']
321
+ end
322
+ l_expected_columns
323
+ end
324
+
325
+ def expected_data(table, stage = nil)
326
+ @log.debug "[ DBFactory::expected_data(...) ]"
327
+ @log.debug " > stage = <#{stage}>"
328
+
329
+ stage ||= 'expected data'
330
+
331
+ l_data = []
332
+ begin
333
+ l_data_tmp = table.values[0][stage]
334
+ case
335
+ when l_data_tmp[0].instance_of?(Hash)
336
+ l_data = l_data_tmp
337
+ when l_data_tmp[0].instance_of?(Array)
338
+ l_columns = table.values[0]['columns']
339
+ # l_columns = expected_columns(tablename)
340
+ l_data_tmp.each_index { |i|
341
+ l_data[i] = {}
342
+ l_data_tmp[i].each_index { |k|
343
+ l_data[i] = l_data[i].merge({l_columns[k] => l_data_tmp[i][k]})
344
+ }
345
+ }
346
+ end
347
+ rescue Exception => e
348
+ @log.error "[ DBFactory::expected_data(...) ] ERROR: <#{e.to_s}>"
349
+ raise "Failed to get expected data."
350
+ end
351
+
352
+ # @log.info("There are currently #{l_data.size} records expected in <#{tablename}> table")
353
+ return l_data
354
+ end
355
+
356
+ def actual_data(table)
357
+ @log.debug "[ DBFactory::actual_data(...) ]"
358
+
359
+ l_object_id = plsql.find_table(table.keys[0])
360
+ begin
361
+ l_filter = table.values[0]['filter'] || '1=1'
362
+ @log.debug("filter: <#{l_filter}>")
363
+ l_data = l_object_id.all("WHERE #{l_filter}")
364
+ l_columns = expected_columns(table)
365
+ @log.debug "columns: <#{l_columns}>"
366
+ l_data.each_index { |i|
367
+ l_data[i].delete_if { |k,v| not l_columns.include?(k) }
368
+ }
369
+ l_data.each_index { |i|
370
+ l_data[i].each { |k,v|
371
+ if v.instance_of?(String)
372
+ l_data[i][k] = v.rstrip
373
+ end
374
+ if v.instance_of?(BigDecimal)
375
+ l_data[i][k] = v.to_f
376
+ end
377
+ }
378
+ }
379
+ rescue Exception => e
380
+ @log.error "[ DBFactory::actual_data(...) ] ERROR: <#{e.to_s}>"
381
+ # @log.warn "Loading actual data for table <#{tablename}> failed in <#{@case}> block"
382
+ raise "Failed to load actual data from DB." #or table <#{tablename}> failed in <#{@case}> block"
383
+ end
384
+
385
+ # @log.info("There are currently #{l_data.size} records in <#{tablename}> table")
386
+ return l_data
387
+ end
388
+
389
+ def pk_data_split(data, pk)
390
+ l_split_data = []
391
+ data.each { |values|
392
+ l_pk = {}
393
+ pk.each { |k|
394
+ l_pk[k] = values[k]
395
+ values.delete(k)
396
+ }
397
+ l_split_data.push({"PK" => l_pk, "DATA" => values})
398
+ }
399
+ # puts l_split_data.inspect
400
+ return l_split_data
401
+ end
402
+
403
+ def get_all_pk(data1, data2)
404
+ l_all_pks = []
405
+ data1.each { |v|
406
+ l_tmp = {"PK" => v["PK"]}
407
+ l_all_pks.push(l_tmp) unless l_all_pks.include?(l_tmp)
408
+ }
409
+ data2.each { |v|
410
+ l_tmp = {"PK" => v["PK"]}
411
+ l_all_pks.push(l_tmp) unless l_all_pks.include?(l_tmp)
412
+ }
413
+ return l_all_pks #.sort_by { |k| k["PK"] }
414
+ end
415
+
416
+ def check_postconditions_for_table(table, stage = nil)
417
+ @log.debug "[ DBFactory::check_postconditions_for_table(...) ]"
418
+ @log.debug " > stage = <#{stage}>"
419
+
420
+ tablename = table.keys[0]
421
+ l_data = nil
422
+ begin
423
+ l_expected_data = expected_data(table, stage)
424
+ l_actual_data = actual_data(table)
425
+
426
+ if l_expected_data == l_actual_data
427
+ @log.info("> DBFactory::compare_table(#{tablename}, #{stage}) ... PASS")
428
+ return true
429
+ end
430
+
431
+ @log.warn "Seems that some differences found when comparing actual and expected data in table <#{tablename}>"
432
+
433
+ # l_pk = @definition[@case]['postconditions']['tables'][tablename]['connect keys']
434
+ l_pk = table.values[0]['connect keys']
435
+ @log.debug "connect keys: <#{l_pk}>"
436
+ if l_pk == nil
437
+ raise "Missing <connect keys> definition"
438
+ end
439
+
440
+ te = pk_data_split(l_expected_data, l_pk) #.sort_by { |v| v.values_at(0) }
441
+ ta = pk_data_split(l_actual_data, l_pk) #.sort_by { |v| v.values_at(0) }
442
+ # puts te.inspect
443
+ # puts ta.inspect
444
+ d = []
445
+ get_all_pk(te, ta).each { |k|
446
+ vei = te.index { |v| v["PK"] == k["PK"] }
447
+ if vei != nil
448
+ ve = te[vei]
449
+ te.delete_at(vei)
450
+ else
451
+ ve = {"PK" => {}, "DATA" => {}}
452
+ end
453
+
454
+ # ve = te.detect { |v| v["PK"] == k["PK"] }
455
+ # if ve == nil
456
+ # ve = {"PK" => {}, "DATA" => {}}
457
+ # end
458
+ vai = ta.index { |v| v["PK"] == k["PK"] }
459
+ if vai != nil
460
+ va = ta[vai]
461
+ ta.delete_at(vai)
462
+ else
463
+ va = {"PK" => {}, "DATA" => {}}
464
+ end
465
+ # va = ta.detect { |v| v["PK"] == k["PK"] }
466
+ # if va == nil
467
+ # va = {"PK" => {}, "DATA" => {}}
468
+ # end
469
+ diff = va["DATA"].diff(ve["DATA"])
470
+ # puts diff.inspect
471
+ # d.push({"PK" => k["PK"], "DATA" => diff}) unless diff == {}
472
+ d.push(k["PK"].merge(diff)) unless diff == {}
473
+ }
474
+
475
+ if d.empty?
476
+ # puts "l_expected_datasize = <#{l_expected_data.size}>"
477
+ # puts l_expected_data.inspect
478
+ # puts "l_actual_data.size = <#{l_actual_data.size}>"
479
+ @log.info("> DBFactory::compare_table(#{tablename}, #{stage}) ... PASS")
480
+ return true
481
+ # return false
482
+ else
483
+ if stage.nil?
484
+ d = {"#{@case}" => {"#{tablename}" => d}}.to_yaml
485
+ else
486
+ d = {"#{@case}" => {"#{stage}" => {"#{tablename}" => d}}}.to_yaml
487
+ end
488
+ l_diff_file = @cfg['DIFF']['filename_tpl'].gsub('#CASE#',@case)
489
+ @log.info "Writing differences for table <#{tablename}> for <#{@case}> block into <#{l_diff_file}>"
490
+ l_fw = File.open(l_diff_file, "a")
491
+ l_fw.write(d)
492
+ l_fw.close
493
+ end
494
+
495
+ rescue Exception => e
496
+ @log.error "[ DBFactory::compare_table(...) ] ERROR: <#{e.to_s}>"
497
+ @log.warn "Comparison of actual and expected data for table <#{tablename}> failed in <#{@case}> block"
498
+ raise "Error occurred during comparison if data for table <#{tablename}> in <#{@case}> block"
499
+ end
500
+
501
+ @log.warn("> DBFactory::compare_table(#{tablename}, #{stage}) ... FAIL")
502
+
503
+ return false
504
+ end
505
+
506
+ end
507
+
508
+ module DBFactory
509
+
510
+ LOG = "#{ROOT}/log"
511
+ DEFAULTS = "#{ROOT}/defaults"
512
+
513
+ @instance
514
+
515
+ def self.load(*args)
516
+ @instance = DBFactoryClass.new
517
+ @instance.load(*args)
518
+ end
519
+
520
+ def self.setup(*args)
521
+ @instance.setup(*args)
522
+ end
523
+
524
+ def self.evaluate(*args)
525
+ @instance.check_postconditions(*args)
526
+ end
527
+
528
+ def self.flashback(*args)
529
+ @instance.flashback(*args)
530
+ end
531
+
532
+ end
@@ -0,0 +1,32 @@
1
+ module PLSQL
2
+ class Schema
3
+ # Returns object ID of table object. Examples:
4
+ # plsql.find_table('scott.my_table')
5
+ # plsql.find_table('scott', 'my_table')
6
+ def find_table(*args)
7
+ if args.size == 1
8
+ table_owner = args[0].split('.')[0].upcase
9
+ table_name = args[0].split('.')[1].upcase
10
+ else
11
+ table_name = args[0]
12
+ table_owner = args[1]
13
+ end
14
+ find_database_object(table_name, table_owner)
15
+ end
16
+ end
17
+ end
18
+
19
+ class Hash
20
+ # Returns difference between 2 hashes (actual values and expecyted values)
21
+ # as Hash with "expected" and "actual" keys
22
+ def diff(other)
23
+ l_keys = self.keys.concat(other.keys).uniq
24
+ l_keys.inject({}) do |memo, key|
25
+ l_expected = other[key] rescue {}
26
+ unless self[key] == l_expected
27
+ memo[key] = {"actual" => self[key], "expected" => l_expected}
28
+ end
29
+ memo
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module DBFactory #:nodoc:
2
+ VERSION = File.read(File.dirname(__FILE__)+'/../../VERSION').chomp
3
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: db-factory
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Stefan Cibiri
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ruby-plsql
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: db-factory uses definition files with YAML/ERB syntax to define test
31
+ data (preconditions) for unit PL/SQL testing and can compare actual data with defined
32
+ expected data.
33
+ email: stefancibiri@yahoo.com
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - History.txt
39
+ - VERSION
40
+ - db-factory.gemspec
41
+ - examples/simple_example_1/Readme.txt
42
+ - examples/simple_example_1/create_objects.sql
43
+ - examples/simple_example_1/simple_example_1.yml
44
+ - examples/simple_example_1/simple_example_1_spec.rb
45
+ - examples/simple_example_1/spec_helper.rb
46
+ - lib/config.yml
47
+ - lib/db-factory.rb
48
+ - lib/db_factory/db_factory.rb
49
+ - lib/db_factory/helpers.rb
50
+ - lib/db_factory/version.rb
51
+ homepage: https://github.com/stci/db-factory
52
+ licenses: []
53
+ post_install_message:
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project:
71
+ rubygems_version: 1.8.23
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: Prepare and compare test data for PL/SQL unit testing
75
+ test_files: []