dbtap 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8ff6f5b046d3324a54837496af2f0727b541a34f
4
+ data.tar.gz: 42f106382e26df6a255f32172f5e9f5c0423745a
5
+ SHA512:
6
+ metadata.gz: e464b3b5d0f57c32185d6029381c9cd6442037527854d7e75134c8ad240f62ab39b70dde72be71bebc0f4c46738d5c1c6a5977de4cbf714156ec4eefa2eae910
7
+ data.tar.gz: 8ee1dd7ca87dc7ced01251f65fa250dc06eea9f67db701ad93c9760c63e6a1baf7d5d553cfde7d982e8c1f9bf29e03fa62921542e9e2746ecc94651555da5bd5
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ ## 0.0.1 - 2014-08-18
5
+
6
+ ### Added
7
+ - The project itself
8
+
9
+ ### Deprecated
10
+ - Nothing.
11
+
12
+ ### Removed
13
+ - Nothing.
14
+
15
+ ### Fixed
16
+ - Nothing.
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dbtap.gemspec
4
+ gemspec
5
+ gem 'pg' # ADDED BY SEQUELIZER
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Outcomes Insights, Inc.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Dbtap
2
+
3
+ Dbtap aims to eventually be a database-agnostic replacement for the __amazing__ [pgTAP](http://pgtap.org/)
4
+
5
+ ## Why?
6
+
7
+ [One of my projects](https://github.com/outcomesinsights/conceptql) started off using PostgreSQL as its sole RDBMS. I had employed pgTAP to run the [tests for that project](https://github.com/outcomesinsights/test_conceptql) and was __very__ happy. But, sadly, other databases needed to be supported and so my tests would no longer run against those new RDBMSs.
8
+
9
+ But fortunately, [Sequel](http://sequel.jeremyevans.net/) exists and is great for writing database-agnostic SQL. I am now porting over any functions I liked in pgTAP over into Dbtap.
10
+
11
+ I'll probably never need the majority of functions that pgTAP provides, meaning I won't be porting them over myself, but I'll update this library as my needs arise.
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'dbtap'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install dbtap
26
+
27
+
28
+ ## Usage
29
+
30
+ 1. Define a set of tests in a ruby file (see the examples directory)
31
+ 2. Run that file using Dbtap's command line tool, `dbtap`
32
+ - e.g. `dbtap run_file my_test_file.rb`
33
+ 3. Marvel at the TAP-compatible output that issues forth
34
+
35
+
36
+ ## Contributing
37
+
38
+ 1. Fork it ( http://github.com/outcomesinsights/dbtap/fork )
39
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
40
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
41
+ 4. Push to the branch (`git push origin my-new-feature`)
42
+ 5. Create new Pull Request
43
+
44
+ ## Thanks
45
+
46
+ - [Outcomes Insights, Inc.](http://outins.com)
47
+ - Many thanks for allowing me to release a portion of my work as Open Source Software!
48
+ - David E. Wheeler
49
+ - For writing [pgTAP](http://pgtap.org/)!
50
+ - Jeremy Evans
51
+ - For writing [Sequel](http://sequel.jeremyevans.net/)!
52
+
53
+ ## License
54
+ Released under the MIT license, Copyright (c) 2014 Outcomes Insights, Inc.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/dbtap ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/dbtap/cli'
3
+
4
+ Dbtap::CLI.start(ARGV)
data/dbtap.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dbtap/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dbtap"
8
+ spec.version = Dbtap::VERSION
9
+ spec.authors = ["Ryan Duryea"]
10
+ spec.email = ["aguynamedryan@gmail.com"]
11
+ spec.summary = %q{Write database-agnostic, TAP-emitting unit tests for your database}
12
+ spec.description = %q{A database-agnostic reimplementation of my favorite methods from pgTAP}
13
+ spec.homepage = "https://github.com/outcomesinsights/dbtap"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake", "~> 10.3"
23
+ spec.add_dependency 'thor', '~> 0.19'
24
+ spec.add_dependency 'sequelizer', '~> 0.0', '>= 0.0.3'
25
+ end
@@ -0,0 +1,18 @@
1
+ # Use the `dbtap` command line utility to run this file, e.g.:
2
+ # bundle exec dbtap run_test examples/basic_example.rb
3
+
4
+ # Start defining a set of tests
5
+ define_tests do
6
+ # Test if these two queries return the same set of rows
7
+ set_eq(db[:person], db[:person], 'person should equal self')
8
+ # See if this query runs within 1.0 seconds (plus or minus 0.99999)
9
+ performs_within(db[:person], 1.0, 0.99999, 'person should be quick')
10
+ end
11
+
12
+ # Define another set of results
13
+ define_tests do
14
+ set_eq(db['SELECT * FROM "person"'], db[:person], 'person should equal text-based self')
15
+ set_eq(db['SELECT * FROM "person"'], db['SELECT * FROM "person"'], 'text-based person should equal text-based self')
16
+ set_eq(db[:person], db[:person].limit(1), 'person should equal self')
17
+ performs_within(db[:person], 1.0, 0.99999, 'person should be quick')
18
+ end
data/lib/dbtap/cli.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'thor'
2
+ require_relative '../dbtap'
3
+
4
+ module Dbtap
5
+ class CLI < Thor
6
+ include Dbtap
7
+
8
+ desc 'run_test file1 [file2 ...]', 'runs tests in the selected file(s)'
9
+ def run_test(*files)
10
+ files.flatten.each do |file|
11
+ instance_eval(File.read(file))
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,72 @@
1
+ require 'sequelizer'
2
+
3
+ module Dbtap
4
+ # The Tapper class provides the DSL for Dbtap#define_tests
5
+ #
6
+ # It also drives the evaluation of all the tests via #run
7
+ class Tapper
8
+ include Sequelizer
9
+
10
+ attr :tests
11
+ def initialize
12
+ @tests = []
13
+ end
14
+
15
+ # Drives the evaluation of each test, emitting TAP-compliant messages
16
+ # for each test
17
+ def run
18
+ puts (1..tests.length).to_s
19
+ tests.each_with_index do |test, i|
20
+ if test.is_ok?
21
+ ok(test, i)
22
+ else
23
+ not_ok(test, i)
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+ # Emits a TAP-compliant OK message
30
+ def ok(test, i)
31
+ message = "ok #{i + 1}"
32
+ message += ' - ' + test.name if test.name
33
+ puts message
34
+ end
35
+
36
+ # Emits a TAP-compliant NOT OK message
37
+ def not_ok(test, i)
38
+ message = "not ok #{i + 1}"
39
+ message += ' - ' + test.name if test.name
40
+ message += "\n " + test.errors.split.join("\n ") if test.errors
41
+ puts message
42
+ end
43
+
44
+ # When dbtap is gem, this will require testers from the proper
45
+ # relative path
46
+ def local_require(file)
47
+ path = base_dir + 'testers' + file.to_s
48
+ require_relative path
49
+ end
50
+
51
+ # When we install dbtap as a gem, it will need to refer to the testers
52
+ # directory, so we'll first find where the gem lives
53
+ def base_dir
54
+ Pathname.new(__FILE__).dirname
55
+ end
56
+
57
+ # The following methods are hard-coded, and used to instantiate
58
+ # each type of test. I imagine I can do some sort of thing where
59
+ # each Tester class can register with Tapper to get dynamically create
60
+ # a set of methods for Tapper to use, but for now, this is my approach
61
+ def set_eq(*args)
62
+ local_require(:set_eq)
63
+ tests << SetEq.new(*args)
64
+ end
65
+
66
+ def performs_within(*args)
67
+ local_require(:performs_within)
68
+ tests << PerformsWithin.new(*args)
69
+ end
70
+ end
71
+ end
72
+
@@ -0,0 +1,53 @@
1
+ require 'benchmark'
2
+ require_relative 'tester'
3
+
4
+ module Dbtap
5
+ # Tests if a query performs within a certain period of time.
6
+ #
7
+ # Each query is run ten times and
8
+ #
9
+ # This is useful for benchmarking, and will verify that a query ran
10
+ # neither too slow nor too fast.
11
+ class PerformsWithin < Tester
12
+ attr :expected_time, :query, :elapsed_time, :delta
13
+ # +query+ - The query to benchmark
14
+ # +expected_time+ - The average expected time to run the query once (in milliseconds)
15
+ # +delta+ - The amount of time + or - that the query is allowed to deviate (in milliseconds)
16
+ # +name+ - (optional) the name of the test
17
+ def initialize(query, expected_time, delta, name = nil)
18
+ @name = name
19
+ @query = query
20
+ @expected_time = expected_time
21
+ @delta = delta
22
+ end
23
+
24
+ def ok?
25
+ (expected_time - elapsed_time).abs.to_f / repetitions <= delta
26
+ end
27
+
28
+ def errors
29
+ output = []
30
+ output << " average runtime: #{elapsed_time} ms"
31
+ output << " desired average: #{expected_time} +/- #{delta} ms"
32
+
33
+ output.join("\n")
34
+ end
35
+
36
+ private
37
+ def elapsed_time
38
+ @elapsed_time ||= bench_it
39
+ end
40
+
41
+ def bench_it
42
+ Benchmark.realtime do
43
+ repetitions.times do
44
+ query.count
45
+ end
46
+ end
47
+ end
48
+
49
+ def repetitions
50
+ 10
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,52 @@
1
+ require_relative 'tester'
2
+
3
+ module Dbtap
4
+ # Tests to see if two sets of results are equal
5
+ #
6
+ # Sets is a bit of a misnomer in that if a query returns duplicate rows,
7
+ # those rows are NOT deduplicated.
8
+ #
9
+ # The order in which either query returns the results is ignored when testing
10
+ # for equality.
11
+ class SetEq < Tester
12
+ attr :actual, :expected
13
+
14
+ # +actual+ - The query being tested
15
+ # +expected+ - The set of results expected. Should be another query.
16
+ # +name+ - (optional) the name of the test
17
+ def initialize(actual, expected, name = nil)
18
+ @name = name
19
+ @actual = actual
20
+ @expected = expected
21
+ end
22
+
23
+ def ok?
24
+ missing.empty? && extras.empty?
25
+ end
26
+
27
+ def errors
28
+ output = []
29
+ unless missing.empty?
30
+ output << "Missing #{missing.count}/#{expected_count} Records:\n " + missing.first.inspect
31
+ end
32
+
33
+ unless extras.empty?
34
+ output << "Extra #{extras.count} over #{expected_count} Records:\n " + extras.first.inspect
35
+ end
36
+ output.join("\n")
37
+ end
38
+
39
+ private
40
+ def missing
41
+ @missing ||= expected.except(actual).all
42
+ end
43
+
44
+ def extras
45
+ @extras ||= actual.except(expected).all
46
+ end
47
+
48
+ def expected_count
49
+ @expected_count = expected.count
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,21 @@
1
+ module Dbtap
2
+ # Parent class to all classes that provide some sort of test
3
+ #
4
+ # Child classes must provide an <tt>ok?</tt> method which returns
5
+ # true if the test passes, or false otherwise
6
+ #
7
+ # Child classes can opt to provide an <tt>errors</tt> method with
8
+ # should return a string with any error information relavent to the test
9
+ class Tester
10
+ attr :name
11
+
12
+ def is_ok?
13
+ ok?
14
+ rescue
15
+ puts "Bail out! #{$!.message}"
16
+ puts $!.backtrace.join("\n")
17
+ raise
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,3 @@
1
+ module Dbtap
2
+ VERSION = "0.0.1"
3
+ end
data/lib/dbtap.rb ADDED
@@ -0,0 +1,23 @@
1
+ require "dbtap/version"
2
+ require "dbtap/tapper"
3
+
4
+ module Dbtap
5
+ # Starts a new set of tests. Use like so:
6
+ #
7
+ # require 'dbtap'
8
+ # include Dbtap
9
+ #
10
+ # define_tests do
11
+ # set_eq(db[:table], db['select * from table'],
12
+ # 'Sequel selects all from table')
13
+ # # Your other tests here
14
+ # end
15
+ #
16
+ # Within the block, `db` is defined as a Sequel-based connection
17
+ # to your database as defined by your Sequelizer configuration
18
+ def define_tests(&block)
19
+ t = Tapper.new
20
+ t.instance_eval(&block)
21
+ t.run
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dbtap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Duryea
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.19'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.19'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sequelizer
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.0'
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 0.0.3
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '0.0'
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 0.0.3
75
+ description: A database-agnostic reimplementation of my favorite methods from pgTAP
76
+ email:
77
+ - aguynamedryan@gmail.com
78
+ executables:
79
+ - dbtap
80
+ extensions: []
81
+ extra_rdoc_files: []
82
+ files:
83
+ - ".gitignore"
84
+ - CHANGELOG.md
85
+ - Gemfile
86
+ - LICENSE.txt
87
+ - README.md
88
+ - Rakefile
89
+ - bin/dbtap
90
+ - dbtap.gemspec
91
+ - examples/basic_example.rb
92
+ - lib/dbtap.rb
93
+ - lib/dbtap/cli.rb
94
+ - lib/dbtap/tapper.rb
95
+ - lib/dbtap/testers/performs_within.rb
96
+ - lib/dbtap/testers/set_eq.rb
97
+ - lib/dbtap/testers/tester.rb
98
+ - lib/dbtap/version.rb
99
+ homepage: https://github.com/outcomesinsights/dbtap
100
+ licenses:
101
+ - MIT
102
+ metadata: {}
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubyforge_project:
119
+ rubygems_version: 2.2.2
120
+ signing_key:
121
+ specification_version: 4
122
+ summary: Write database-agnostic, TAP-emitting unit tests for your database
123
+ test_files: []
124
+ has_rdoc: