dbexpect 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +41 -0
  4. data/COPYING +674 -0
  5. data/Gemfile +20 -0
  6. data/Gemfile.lock +45 -0
  7. data/LICENSE.txt +2 -0
  8. data/README.md +106 -0
  9. data/Rakefile +65 -0
  10. data/VERSION +1 -0
  11. data/bin/dbexpect +33 -0
  12. data/bin/great-expectations +28 -0
  13. data/bin/ready-rig +27 -0
  14. data/database.yml +9 -0
  15. data/lib/dbexpect.rb +19 -0
  16. data/lib/dbexpect/command_runner.rb +21 -0
  17. data/lib/dbexpect/console_formatter.rb +40 -0
  18. data/lib/dbexpect/d_s_l_parser.rb +124 -0
  19. data/lib/dbexpect/database.rb +59 -0
  20. data/lib/dbexpect/db_null.rb +25 -0
  21. data/lib/dbexpect/db_sequence.rb +26 -0
  22. data/lib/dbexpect/db_string.rb +29 -0
  23. data/lib/dbexpect/dbexpect.rb +87 -0
  24. data/lib/dbexpect/defaulting_row_set.rb +68 -0
  25. data/lib/dbexpect/expectation_checker.rb +32 -0
  26. data/lib/dbexpect/expectation_tree_node.rb +67 -0
  27. data/lib/dbexpect/expectations/expectation.rb +45 -0
  28. data/lib/dbexpect/expectations/row_count_expectation.rb +32 -0
  29. data/lib/dbexpect/expectations/row_expectation.rb +32 -0
  30. data/lib/dbexpect/odbc_connection.rb +47 -0
  31. data/lib/dbexpect/row.rb +43 -0
  32. data/lib/dbexpect/table.rb +71 -0
  33. data/spec/dbexpect_integration_spec.rb +131 -0
  34. data/spec/defaulting_row_set_spec.rb +37 -0
  35. data/spec/expectation_checker_spec.rb +47 -0
  36. data/spec/expectations/row_expectation_spec.rb +51 -0
  37. data/spec/fixtures/basic_test_expected_inserts.sql +6 -0
  38. data/spec/fixtures/cleanup_db.sql +2 -0
  39. data/spec/fixtures/expected_output.txt +11 -0
  40. data/spec/fixtures/sample_db.sql +12 -0
  41. data/spec/fixtures/sample_project/database.yml +9 -0
  42. data/spec/fixtures/sample_project/defaults/defaults.rb +5 -0
  43. data/spec/fixtures/sample_project/tests/basic_test.rb +41 -0
  44. data/spec/fixtures/sample_project/tests/test2.rb +3 -0
  45. data/spec/spec_helper.rb +28 -0
  46. metadata +186 -0
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+
9
+
10
+ group :development do
11
+ gem 'ruby-odbc'
12
+ gem "rdoc", "~> 3.12"
13
+ gem "bundler"
14
+ gem "jeweler", "~> 1.8.3"
15
+ gem "rspec", "~> 2.8.0"
16
+ gem 'pg'
17
+ gem "pry"
18
+ gem "ZenTest"
19
+ end
20
+
data/Gemfile.lock ADDED
@@ -0,0 +1,45 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ ZenTest (4.8.2)
5
+ coderay (1.0.7)
6
+ diff-lcs (1.1.3)
7
+ git (1.2.5)
8
+ jeweler (1.8.4)
9
+ bundler (~> 1.0)
10
+ git (>= 1.2.5)
11
+ rake
12
+ rdoc
13
+ json (1.7.5)
14
+ method_source (0.8)
15
+ pg (0.14.0)
16
+ pry (0.9.10)
17
+ coderay (~> 1.0.5)
18
+ method_source (~> 0.8)
19
+ slop (~> 3.3.1)
20
+ rake (0.9.2.2)
21
+ rdoc (3.12)
22
+ json (~> 1.4)
23
+ rspec (2.8.0)
24
+ rspec-core (~> 2.8.0)
25
+ rspec-expectations (~> 2.8.0)
26
+ rspec-mocks (~> 2.8.0)
27
+ rspec-core (2.8.0)
28
+ rspec-expectations (2.8.0)
29
+ diff-lcs (~> 1.1.2)
30
+ rspec-mocks (2.8.0)
31
+ ruby-odbc (0.99994)
32
+ slop (3.3.3)
33
+
34
+ PLATFORMS
35
+ ruby
36
+
37
+ DEPENDENCIES
38
+ ZenTest
39
+ bundler
40
+ jeweler (~> 1.8.3)
41
+ pg
42
+ pry
43
+ rdoc (~> 3.12)
44
+ rspec (~> 2.8.0)
45
+ ruby-odbc
data/LICENSE.txt ADDED
@@ -0,0 +1,2 @@
1
+ Copyright (c) 2012 Beau Fabry
2
+
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ dbexpect
2
+ =======
3
+
4
+ dbexpect is a domain specific language written in ruby for testing ETL solutions.
5
+
6
+ Taking cues from Rspec in structure and usage, the point is to enable
7
+ the specification of unit test data, job running, and expected outcomes
8
+ for an entity/test cases in a ruby file using an internal DSL that is
9
+ targeted at this kind of testing.
10
+
11
+ dbexpect is ideally suited to creating automated unit tests for
12
+ individual ETL jobs in a data warehousing or data migration project.
13
+ Helping to ensure correctness of the job initially developed, and
14
+ catching problems later on when someone makes a change that could affect
15
+ existing functionality.
16
+
17
+ Sample test
18
+ ---------
19
+
20
+ describe "Moving customers from source to target" do
21
+ @src = table(:dbexpect_src,:dbexpect_src,:customers_src)
22
+ @tgt = table(:dbexpect_tgt,:dbexpect_tgt,:customers_tgt)
23
+
24
+ etl_run_command "ruby etl2.rb"
25
+
26
+ expect_total_rows @tgt, 1
27
+
28
+ describe "it should upcase customer names" do
29
+ insert_into @src,
30
+ [:id,:name],
31
+ [[1,"Fred"]]
32
+
33
+ expect_rows @tgt,
34
+ [:id,:name],
35
+ [[1,"FRED"]]
36
+ end
37
+
38
+ describe "it should not migrate smith (because screw that guy)" do
39
+ insert_into @src,
40
+ [:id,:name],
41
+ [[1,"Smith"]]
42
+
43
+ # expect no rows
44
+ end
45
+ end
46
+
47
+ Installation
48
+ ------------
49
+ gem install dbexpect
50
+
51
+ Create a database.yml file in a folder where you want to store your
52
+ tests, and set up connections for each of the databases you want dbexpect
53
+ to talk to. Each of the connections will need to have an ODBC connection
54
+ defined as well.
55
+
56
+ database.yml:
57
+
58
+ database1:
59
+ database: odbc_dsn
60
+ username: barry
61
+ password: secret
62
+
63
+ database2:
64
+ database: odbc_dsn2
65
+ username: shaz
66
+ password: secret
67
+
68
+ Usage
69
+ -----
70
+ Assuming a folder structure for your tests that looks like this:
71
+
72
+ /
73
+ |-database.yml
74
+ |
75
+ |- defaults/
76
+ | |- defaults_for_tablex.rb
77
+ |
78
+ |- tests/
79
+ |- test1.rb
80
+ |- test2.rb
81
+
82
+ To run the tests in test1.rb:
83
+
84
+ prompt:/$ dbexpect tests/test1.rb
85
+
86
+ There is a sample dbexpect project at
87
+ http://github.com/C3/dbexpect_example for more information.
88
+
89
+
90
+ License
91
+ -------
92
+
93
+ Copyright 2012 C3 Business Solutions. See COPYING for further details.
94
+
95
+ dbexpect is free software: you can redistribute it and/or modify
96
+ it under the terms of the GNU General Public License as published by
97
+ the Free Software Foundation, either version 3 of the License, or
98
+ (at your option) any later version.
99
+
100
+ dbexpect is distributed in the hope that it will be useful,
101
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
102
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
103
+ GNU General Public License for more details.
104
+
105
+ You should have received a copy of the GNU General Public License
106
+ along with dbexpect. If not, see <http://www.gnu.org/licenses/>.
data/Rakefile ADDED
@@ -0,0 +1,65 @@
1
+ # Copyright 2012 C3 Business Solutions
2
+ #
3
+ # This file is part of dbexpect.
4
+ #
5
+ # dbexpect is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # dbexpect is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with dbexpect. If not, see <http://www.gnu.org/licenses/>.
17
+ # encoding: utf-8
18
+
19
+ require 'rubygems'
20
+ require 'bundler'
21
+ begin
22
+ Bundler.setup(:default, :development)
23
+ rescue Bundler::BundlerError => e
24
+ $stderr.puts e.message
25
+ $stderr.puts "Run `bundle install` to install missing gems"
26
+ exit e.status_code
27
+ end
28
+ require 'rake'
29
+
30
+ require 'jeweler'
31
+ Jeweler::Tasks.new do |gem|
32
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
33
+ gem.name = "dbexpect"
34
+ gem.homepage = "http://github.com/bfabry/dbexpect"
35
+ gem.license = "PRIVATE"
36
+ gem.summary = %Q{Generates inserts for test data from DSL}
37
+ gem.description = %Q{As above}
38
+ gem.email = "beau.fabry@c3businesssolutions.com"
39
+ gem.authors = ["Beau Fabry"]
40
+ # dependencies defined in Gemfile
41
+ end
42
+ Jeweler::RubygemsDotOrgTasks.new
43
+
44
+ require 'rspec/core'
45
+ require 'rspec/core/rake_task'
46
+ RSpec::Core::RakeTask.new(:spec) do |spec|
47
+ spec.pattern = FileList['spec/**/*_spec.rb']
48
+ end
49
+
50
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
51
+ spec.pattern = 'spec/**/*_spec.rb'
52
+ spec.rcov = true
53
+ end
54
+
55
+ task :default => :spec
56
+
57
+ require 'rdoc/task'
58
+ Rake::RDocTask.new do |rdoc|
59
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
60
+
61
+ rdoc.rdoc_dir = 'rdoc'
62
+ rdoc.title = "dbexpect #{version}"
63
+ rdoc.rdoc_files.include('README*')
64
+ rdoc.rdoc_files.include('lib/**/*.rb')
65
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.11.0
data/bin/dbexpect ADDED
@@ -0,0 +1,33 @@
1
+ # Copyright 2012 C3 Business Solutions
2
+ #
3
+ # This file is part of dbexpect.
4
+ #
5
+ # dbexpect is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # dbexpect is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with dbexpect. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ #!/usr/bin/env ruby
19
+
20
+ puts "dbexpect Copyright 2012 C3 Business Solutions"
21
+ puts "This program comes with ABSOLUTELY NO WARRANTY"
22
+
23
+ begin
24
+ require 'dbexpect'
25
+ rescue LoadError
26
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
27
+ require 'dbexpect'
28
+ end
29
+
30
+ db = Database.hash_from_config
31
+ ret = Dbexpect.new().run_test(ARGV.shift,db,CommandRunner.new)
32
+
33
+ exit(ret)
@@ -0,0 +1,28 @@
1
+ # Copyright 2012 C3 Business Solutions
2
+ #
3
+ # This file is part of dbexpect.
4
+ #
5
+ # dbexpect is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # dbexpect is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with dbexpect. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ #!/usr/bin/env ruby
19
+
20
+ begin
21
+ require 'dbexpect'
22
+ rescue LoadError
23
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
24
+ require 'dbexpect'
25
+ end
26
+
27
+ target_db = Database.from_dsn(ARGV.shift)
28
+ exit(Dbexpect.new().great_expectations(ARGV.shift,target_db))
data/bin/ready-rig ADDED
@@ -0,0 +1,27 @@
1
+ # Copyright 2012 C3 Business Solutions
2
+ #
3
+ # This file is part of dbexpect.
4
+ #
5
+ # dbexpect is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # dbexpect is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with dbexpect. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ #!/usr/bin/env ruby
19
+
20
+ begin
21
+ require 'dbexpect'
22
+ rescue LoadError
23
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
24
+ require 'dbexpect'
25
+ end
26
+ db = Database.from_dsn(ARGV.shift)
27
+ exit(Dbexpect.new().setup_test(ARGV.shift,db))
data/database.yml ADDED
@@ -0,0 +1,9 @@
1
+ dbexpect_src:
2
+ database: testgen_src
3
+ username: testgen
4
+ password: testgen
5
+
6
+ dbexpect_tgt:
7
+ database: testgen_tgt
8
+ username: testgen
9
+ password: testgen
data/lib/dbexpect.rb ADDED
@@ -0,0 +1,19 @@
1
+ # Copyright 2012 C3 Business Solutions
2
+ #
3
+ # This file is part of dbexpect.
4
+ #
5
+ # dbexpect is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # dbexpect is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with dbexpect. If not, see <http://www.gnu.org/licenses/>.
17
+ require_relative 'dbexpect/dbexpect'
18
+ require_relative 'dbexpect/database'
19
+ require_relative 'dbexpect/command_runner'
@@ -0,0 +1,21 @@
1
+ # Copyright 2012 C3 Business Solutions
2
+ #
3
+ # This file is part of dbexpect.
4
+ #
5
+ # dbexpect is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # dbexpect is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with dbexpect. If not, see <http://www.gnu.org/licenses/>.
17
+ class CommandRunner
18
+ def run(command)
19
+ system(command)
20
+ end
21
+ end
@@ -0,0 +1,40 @@
1
+ # Copyright 2012 C3 Business Solutions
2
+ #
3
+ # This file is part of dbexpect.
4
+ #
5
+ # dbexpect is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # dbexpect is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with dbexpect. If not, see <http://www.gnu.org/licenses/>.
17
+ class ConsoleFormatter
18
+ def initialize(output)
19
+ @output = output
20
+ end
21
+
22
+ def notify_passed
23
+ @output.puts "Passed all expectations\n"
24
+ end
25
+
26
+ def notify_failed(failed_expectations)
27
+ failed_expectations.traverse do |depth, description, expectations|
28
+ @output.puts((' ' * depth) + description + ":\n")
29
+
30
+ expectations.collect(&:failure_message).each do |msg|
31
+ @output.puts((' ' * depth) + ' ' + msg + "\n")
32
+ end
33
+ end
34
+ @output.puts "Failed to meet expectations\n"
35
+ end
36
+
37
+ def format_sql(sql)
38
+ @output.puts sql
39
+ end
40
+ end
@@ -0,0 +1,124 @@
1
+ # Copyright 2012 C3 Business Solutions
2
+ #
3
+ # This file is part of dbexpect.
4
+ #
5
+ # dbexpect is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # dbexpect is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with dbexpect. If not, see <http://www.gnu.org/licenses/>.
17
+ require_relative 'expectation_tree_node'
18
+ require 'set'
19
+
20
+ class DSLParser
21
+
22
+ attr_accessor :commands
23
+
24
+ def initialize
25
+ @tables = {}
26
+ @tree_nodes = [ExpectationTreeNode.new('->')]
27
+ @files_loaded = Set.new
28
+ @commands = []
29
+ end
30
+
31
+ def expectation_tree
32
+ @tree_nodes.first
33
+ end
34
+
35
+ def parse(script)
36
+ instance_eval(script)
37
+ end
38
+
39
+ def tables
40
+ @tables.values
41
+ end
42
+
43
+ protected
44
+ def requires(file)
45
+ unless @files_loaded.include?(file)
46
+ instance_eval(File.read(file))
47
+ end
48
+ @files_loaded << file
49
+ end
50
+
51
+ def etl_run_command(command)
52
+ @commands << command
53
+ end
54
+
55
+ def dirty(table)
56
+ table.dirty = true
57
+ end
58
+
59
+ def expect_total_rows(table, count)
60
+ @tree_nodes.last.add([table.row_count_check(count)])
61
+ end
62
+
63
+ def describe(description,&block)
64
+ new_node = @tree_nodes.last.create_child(description)
65
+ @tree_nodes << new_node
66
+ instance_eval(&block)
67
+ @tree_nodes.pop
68
+ end
69
+
70
+ def defaults_for(table, columns)
71
+ __set_defaults(table,:set_default,columns)
72
+ end
73
+
74
+ def expected_defaults(table, columns)
75
+ __set_defaults(table,:set_expected_default,columns)
76
+ end
77
+
78
+ def __set_defaults(table, method, columns)
79
+ columns.each do |col, value|
80
+ table.send(method, col, wrap(value))
81
+ end
82
+ end
83
+
84
+ def insert_into(table,row_columns,rows)
85
+ __add_rows(table, :add_fixture_row, row_columns, rows)
86
+ end
87
+
88
+ def expect_rows(table, row_columns, rows)
89
+ @tree_nodes.last.add __add_rows(table, :add_expected_row, row_columns, rows)
90
+ end
91
+
92
+ def __add_rows(table, row_method, row_columns, rows)
93
+ rows.collect do |row_values|
94
+ wrapped = row_values.map {|v| wrap(v) }
95
+ table.send(row_method,Hash[ row_columns.zip(wrapped)])
96
+ end
97
+ end
98
+
99
+ def all_tables(col_values)
100
+ col_values.each do |col,value|
101
+ @tables.each {|x,t| t.set_default(col, wrap(value)) }
102
+ end
103
+ end
104
+
105
+ def table(db_name,schema,tablename)
106
+ @tables[db_name.to_s + schema.to_s + tablename.to_s] ||= Table.new(db_name,schema,tablename)
107
+ end
108
+
109
+ NULL = DbNull.new
110
+ def null; DbNull.new; end
111
+
112
+ def wrap(val)
113
+ case val
114
+ when DbSequence
115
+ val
116
+ when DbNull
117
+ val
118
+ when nil
119
+ NULL
120
+ else
121
+ DbString.new(val)
122
+ end
123
+ end
124
+ end