kapnismology 1.6.0 → 1.7.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 +4 -4
- data/README.md +68 -16
- data/app/controllers/kapnismology/smoke_tests_controller.rb +1 -1
- data/lib/kapnismology.rb +1 -0
- data/lib/kapnismology/engine.rb +2 -2
- data/lib/kapnismology/evaluation.rb +3 -11
- data/lib/kapnismology/evaluation_collection.rb +1 -1
- data/lib/kapnismology/rake_task.rb +1 -1
- data/lib/kapnismology/result.rb +54 -15
- data/lib/kapnismology/smoke_test.rb +40 -19
- data/lib/kapnismology/smoke_test_collection.rb +28 -0
- data/lib/kapnismology/terminal.rb +5 -0
- data/lib/kapnismology/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a71f19f3000d41004864b28c10f41b3f147f7868
|
4
|
+
data.tar.gz: fa3dcaeda42ddb19d05bcfc0b308cbf8bef70327
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05350329b7cbfac2f6dc72a30a44dc6f168cb0f23b64324d6d85c87b1353144b886945d7c4608e6f2fc08fe27da7aa9d7b944f78a14b53447452f47dc32ddff1
|
7
|
+
data.tar.gz: f6c7a55968f13f3ca421696cfaa1ac45bfd34d20a4345e582a50ce8f7dc76ba755aabd9de9779d1506036672938b09ac8abd163acde779fd77e19caa5c94717f
|
data/README.md
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
# Kapnismology
|
2
2
|
|
3
|
-
Kapnismology 'the study of smoke', is a gem
|
3
|
+
Kapnismology 'the study of smoke', is a gem containing a Rails engine to easily create smoke tests.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
-
Kapnismology only supports Rails.
|
8
7
|
In the Gemfile write:
|
9
8
|
```
|
10
|
-
gem 'kapnismology', '~> 1.
|
9
|
+
gem 'kapnismology', '~> 1.7'
|
11
10
|
```
|
12
11
|
|
13
12
|
In your config/routes write:
|
@@ -28,17 +27,26 @@ Create a class like this:
|
|
28
27
|
class MySmokeTest < Kapnismology::SmokeTest
|
29
28
|
|
30
29
|
def result
|
31
|
-
|
30
|
+
Success.new({connection: 'good'}, "Connected!")
|
32
31
|
end
|
33
32
|
end
|
34
33
|
```
|
35
34
|
|
36
35
|
The class must:
|
37
36
|
- Inherit from `Kapnismology::SmokeTest`
|
38
|
-
- Have an instance method `result` returning a
|
37
|
+
- Have an instance method `result` returning a Result or Success object
|
39
38
|
|
40
|
-
|
41
|
-
|
39
|
+
A test passes if it returns:
|
40
|
+
`Result.new(true, data, message)` or
|
41
|
+
`Success.new(data, message)`
|
42
|
+
|
43
|
+
A test fails if it returns:
|
44
|
+
`Result.new(false, data, message)` or
|
45
|
+
`raise SmokeTestFailed.new(data, message)`
|
46
|
+
|
47
|
+
|
48
|
+
Any class created this way will be called and its result will be merged with other results.
|
49
|
+
In the example above the result of this class would be added to the results as:
|
42
50
|
```
|
43
51
|
{'MySmokeTest': { passed: true, data: { connection: 'good' }, message: 'Connected!' }}
|
44
52
|
```
|
@@ -46,19 +54,35 @@ In this case the result of this class would be added to the result as:
|
|
46
54
|
## Loading tests
|
47
55
|
|
48
56
|
Kapnismology will find any class which inherits from `Kapnismology::SmokeTest` in memory.
|
49
|
-
Kapnismology will require all the files
|
57
|
+
Kapnismology will require all the files in your project in the path `lib/smoke_test`.
|
50
58
|
If for any reason you want to have your test in any other location, then you will need to make sure they are properly required.
|
51
59
|
|
52
60
|
## Naming
|
53
61
|
|
54
|
-
If you want to change the name
|
55
|
-
|
62
|
+
If you want to change the name tests are reported in the final result, define `self.name` in your smoke test class.
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
class MySmokeTest < Kapnismology::SmokeTest
|
66
|
+
def self.name
|
67
|
+
'Database smoke test'
|
68
|
+
end
|
69
|
+
def result
|
70
|
+
Success.new({connection: 'good'}, "Connected!")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
Will produce:
|
76
|
+
```ruby
|
77
|
+
{'Database smoke test': { passed: true, data: { connection: 'good' }, message: 'Connected!' }}
|
78
|
+
|
79
|
+
```
|
56
80
|
|
57
81
|
## Not runnable tests
|
58
82
|
|
59
|
-
If your test find a situation when it does not make sense, then you can return a `
|
83
|
+
If your test find a situation when it does not make sense, then you can return a `NullResult` instead of a `Result`. Like:
|
60
84
|
```
|
61
|
-
if (File.exist?('
|
85
|
+
if (File.exist?('necessary file'))
|
62
86
|
Result.new(....)
|
63
87
|
else
|
64
88
|
NullResult.new
|
@@ -71,14 +95,14 @@ NullResult do not need any parameter but you can optionally pass a message.
|
|
71
95
|
## Tagging and running tags
|
72
96
|
|
73
97
|
All smoke tests are tagged by default with 'deployment' and 'runtime'.
|
74
|
-
If you want to tag your test with any one of the above or any other tag, just overwrite the `self.
|
98
|
+
If you want to tag your test with any one of the above or any other tag, just overwrite the `self.tags` method in your smoke test class.
|
75
99
|
The following example creates a smoke test tagged with the tags 'slow' and 'integration'.
|
76
100
|
|
77
101
|
```Ruby
|
78
102
|
class ExpensiveTest < Kapnismology::SmokeTest
|
79
103
|
def result
|
80
104
|
end
|
81
|
-
def self.
|
105
|
+
def self.tags
|
82
106
|
['slow', 'integration']
|
83
107
|
end
|
84
108
|
end
|
@@ -88,7 +112,7 @@ When you call the URL, all smoke tests marked with 'runtime' will be run. As by
|
|
88
112
|
|
89
113
|
The above smoke test as it has not the 'runtime' category, it will not be run. To run it you should call your service like this:
|
90
114
|
```
|
91
|
-
wget http://myservice.com/smoke_test?tags=
|
115
|
+
wget http://myservice.com/smoke_test?tags=integration
|
92
116
|
```
|
93
117
|
|
94
118
|
It will run all your integration tests. You can run it together with your runtime tests also:
|
@@ -102,9 +126,37 @@ It will run all your integration tests. You can run it together with your runtim
|
|
102
126
|
If you want to skip some smoke test when you call the URL then you can add the 'skip' query parameter to the URL indicating the tests you want to skip.
|
103
127
|
For instance:
|
104
128
|
```
|
105
|
-
wget http://myservice.com/smoke_test?skip=ToNotBeCalled,
|
129
|
+
wget http://myservice.com/smoke_test?skip=ToNotBeCalled,NeitherCallThis
|
130
|
+
```
|
131
|
+
|
132
|
+
## Recommended coding style
|
133
|
+
|
134
|
+
Hopefully Kapnismology is flexible enough so you can code your smoke test as you prefer, our recommended style is this:
|
135
|
+
|
136
|
+
```
|
137
|
+
def result
|
138
|
+
user = user_from_remote
|
139
|
+
puts_to_result('User successfully retrieved')
|
140
|
+
|
141
|
+
role = role_from_remote(user)
|
142
|
+
puts_to_result('Role for the user retrieved')
|
143
|
+
|
144
|
+
if check_permissions(user, role)
|
145
|
+
Result.new(true, {user: user, role: role}, "Permissions are properly set")
|
146
|
+
else
|
147
|
+
Result.new(false, {user: user, role: role}, "Permissions failed")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def user_from_remote
|
152
|
+
get_user
|
153
|
+
rescue => e
|
154
|
+
raise SmokeTestFailed.new({class: e.class, message: e.message}, "Error raised when accessing user")
|
155
|
+
end
|
156
|
+
...
|
106
157
|
```
|
107
158
|
|
159
|
+
Using small methods that raise SmokeTestFailed when failed will help you write an easy to read `result` method.
|
108
160
|
|
109
161
|
|
110
162
|
## TODO
|
@@ -3,7 +3,7 @@ module Kapnismology
|
|
3
3
|
# smoke tests registered in the application and gather the results
|
4
4
|
class SmokeTestsController < ApplicationController
|
5
5
|
def index
|
6
|
-
evaluations =
|
6
|
+
evaluations = SmokeTestCollection.evaluations(allowed_tags, blacklist)
|
7
7
|
render text: evaluations.to_json, status: status(evaluations)
|
8
8
|
end
|
9
9
|
|
data/lib/kapnismology.rb
CHANGED
data/lib/kapnismology/engine.rb
CHANGED
@@ -3,10 +3,10 @@ module Kapnismology
|
|
3
3
|
begin
|
4
4
|
# Rails engine to automatically load our code and smoke tests libraries
|
5
5
|
class Engine < ::Rails::Engine
|
6
|
-
initializer 'kapnismology.add_autoload_paths', before: :set_autoload_paths do |
|
6
|
+
initializer 'kapnismology.add_autoload_paths', before: :set_autoload_paths do |_app|
|
7
7
|
smoketest_dir = Rails.root.join('lib', 'smoke_test')
|
8
8
|
if smoketest_dir.exist?
|
9
|
-
Dir[File.join(smoketest_dir, '*.rb')].each {|file| require file }
|
9
|
+
Dir[File.join(smoketest_dir, '*.rb')].each { |file| require file }
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
@@ -2,11 +2,11 @@ require 'json'
|
|
2
2
|
require 'kapnismology/terminal'
|
3
3
|
|
4
4
|
module Kapnismology
|
5
|
-
# Mapping of test_name => result for each smoke test
|
5
|
+
# Mapping of test_name => returned result for each smoke test
|
6
6
|
class Evaluation
|
7
7
|
def initialize(test_class)
|
8
8
|
@name = test_class.name.split('::').last
|
9
|
-
@result = test_class.new.
|
9
|
+
@result = test_class.new.__result__ || unavailable_result
|
10
10
|
end
|
11
11
|
|
12
12
|
def passed?
|
@@ -22,11 +22,7 @@ module Kapnismology
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def to_s
|
25
|
-
|
26
|
-
The smoke test #{@name} #{passed_or_failed_text}
|
27
|
-
#{@result.message}
|
28
|
-
#{@result.to_hash[:data]}
|
29
|
-
eos
|
25
|
+
@result.to_s(@name)
|
30
26
|
end
|
31
27
|
|
32
28
|
private
|
@@ -34,9 +30,5 @@ eos
|
|
34
30
|
def unavailable_result
|
35
31
|
Result.new(false, {}, 'This test has not returned any result.')
|
36
32
|
end
|
37
|
-
|
38
|
-
def passed_or_failed_text
|
39
|
-
passed? ? Terminal.green('passed') : Terminal.red('failed')
|
40
|
-
end
|
41
33
|
end
|
42
34
|
end
|
data/lib/kapnismology/result.rb
CHANGED
@@ -1,38 +1,77 @@
|
|
1
1
|
module Kapnismology
|
2
|
+
# This is the base class for all types of results.
|
3
|
+
# It is useful to be able to test if the object is of a correct result type.
|
4
|
+
# It also have methods to add information and serialize it.
|
5
|
+
class BaseResult
|
6
|
+
def to_hash
|
7
|
+
{ passed: passed?, data: @data, message: @message, extra_messages: @extra_messages }
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s(name)
|
11
|
+
<<-eos
|
12
|
+
#{format_passed(passed?)}: #{name}
|
13
|
+
#{format_extra_messages(@extra_messages)}#{Terminal.bold(@message)}
|
14
|
+
#{@data}
|
15
|
+
eos
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_extra_messages(messages)
|
19
|
+
@extra_messages = (messages || []).compact.flatten
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def passed?
|
24
|
+
!!@passed
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def format_extra_messages(extra_messages)
|
30
|
+
if extra_messages.empty?
|
31
|
+
''
|
32
|
+
else
|
33
|
+
extra_messages.join("\n") + "\n"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def format_passed(passed)
|
38
|
+
passed ? Terminal.green('passed') : Terminal.red('failed')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
2
42
|
# This is the result of each smoke test.
|
3
43
|
# This class makes sense to enforce smoke test to return something known
|
4
44
|
# Params of the constructor:
|
5
45
|
# * passed : Boolean: true -> test passed, false -> test failed
|
6
46
|
# * data : Typically Array or Hash representing the result of the test
|
7
47
|
# * message: String with an extra message to provide human readable information
|
8
|
-
class Result
|
9
|
-
attr_reader :data, :message
|
10
|
-
|
48
|
+
class Result < BaseResult
|
11
49
|
def initialize(passed, data, message)
|
12
50
|
raise ArgumentError, 'passed must be true or false' unless !!passed == passed
|
13
51
|
@passed = passed
|
14
52
|
@data = data
|
15
53
|
@message = message
|
54
|
+
@extra_messages = []
|
16
55
|
end
|
56
|
+
end
|
17
57
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
58
|
+
class NullResult < BaseResult
|
59
|
+
def initialize(data, message = 'The result could not be determined')
|
60
|
+
@passed = true
|
61
|
+
@data = data
|
62
|
+
@message = message
|
63
|
+
@extra_messages = []
|
24
64
|
end
|
25
65
|
end
|
26
66
|
|
27
67
|
# Use this class when your test is not valid in the current situation
|
28
68
|
# For instance when you have a test for deployments that have not happen, etc.
|
29
|
-
class
|
30
|
-
|
31
|
-
|
69
|
+
class Success < BaseResult
|
70
|
+
def initialize(data, message)
|
71
|
+
@passed = true
|
72
|
+
@data = data
|
32
73
|
@message = message
|
33
|
-
|
34
|
-
def passed?
|
35
|
-
true
|
74
|
+
@extra_messages = []
|
36
75
|
end
|
37
76
|
end
|
38
77
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'kapnismology/result'
|
2
|
-
require 'kapnismology/
|
2
|
+
require 'kapnismology/smoke_test_collection'
|
3
3
|
|
4
4
|
module Kapnismology
|
5
5
|
#
|
@@ -11,28 +11,33 @@ module Kapnismology
|
|
11
11
|
RUNTIME_TAG = 'runtime'.freeze
|
12
12
|
DEFAULT_TAGS = [DEPLOYMENT_TAG, RUNTIME_TAG].freeze
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
# Default constructor may be overwritten by child class
|
15
|
+
def initialize
|
16
|
+
@all_result_messages = []
|
16
17
|
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
19
|
+
def result
|
20
|
+
|
21
|
+
end
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
def __result__
|
24
|
+
result_object = result
|
25
|
+
unless result_object.is_a?(Kapnismology::BaseResult)
|
26
|
+
message = "Smoke test #{self.class}, returned #{result_object.class} instead of a Result"
|
27
|
+
result_object = Result.new(false, { returned_class: result_object.class }, message)
|
25
28
|
end
|
29
|
+
rescue SmokeTestFailed => e
|
30
|
+
result_object = e.result
|
31
|
+
rescue => e
|
32
|
+
message = "Unrescued error happened in #{self.class}"
|
33
|
+
result_object = Result.new(false, { exception: e.class, message: e.message }, message)
|
34
|
+
ensure
|
35
|
+
return result_object.add_extra_messages(@all_result_messages)
|
36
|
+
end
|
26
37
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
runable_tests = smoke_tests.select do |test|
|
31
|
-
klass_name = test.name.split('::').last
|
32
|
-
!blacklist.include?(klass_name) &&
|
33
|
-
!(allowed_tags & test.tags).empty?
|
34
|
-
end
|
35
|
-
EvaluationCollection.new(runable_tests)
|
38
|
+
class << self
|
39
|
+
def inherited(klass)
|
40
|
+
SmokeTestCollection.add_smoke_test(klass)
|
36
41
|
end
|
37
42
|
|
38
43
|
def tags
|
@@ -40,13 +45,29 @@ module Kapnismology
|
|
40
45
|
end
|
41
46
|
end
|
42
47
|
|
43
|
-
|
48
|
+
protected
|
49
|
+
|
50
|
+
def puts_to_result(message)
|
51
|
+
@all_result_messages ||= []
|
52
|
+
@all_result_messages.push(message)
|
53
|
+
end
|
44
54
|
|
45
55
|
# These classes makes it very simple to implementors of results to use them without the module name
|
46
56
|
class Result < Kapnismology::Result
|
47
57
|
end
|
48
58
|
class NullResult < Kapnismology::NullResult
|
49
59
|
end
|
60
|
+
class Success < Kapnismology::Success
|
61
|
+
end
|
50
62
|
|
63
|
+
class SmokeTestFailed < StandardError
|
64
|
+
def initialize(data, message)
|
65
|
+
@data = data
|
66
|
+
@message = message
|
67
|
+
end
|
68
|
+
def result
|
69
|
+
Kapnismology::Result.new(false, @data, @message)
|
70
|
+
end
|
71
|
+
end
|
51
72
|
end
|
52
73
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'kapnismology/evaluation_collection'
|
2
|
+
|
3
|
+
module Kapnismology
|
4
|
+
|
5
|
+
# This class maintains the collection of all the smoke tests found in the system
|
6
|
+
class SmokeTestCollection
|
7
|
+
class << self
|
8
|
+
def add_smoke_test(klass)
|
9
|
+
smoke_tests << klass
|
10
|
+
end
|
11
|
+
|
12
|
+
def smoke_tests
|
13
|
+
@smoke_tests ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
def evaluations(allowed_tags = [SmokeTest::RUNTIME_TAG], blacklist = [])
|
17
|
+
# We will run any class which categories are in the allowed list
|
18
|
+
# and not blacklisted
|
19
|
+
runable_tests = smoke_tests.select do |test|
|
20
|
+
klass_name = test.name.split('::').last
|
21
|
+
!blacklist.include?(klass_name) &&
|
22
|
+
!(allowed_tags & test.tags).empty?
|
23
|
+
end
|
24
|
+
EvaluationCollection.new(runable_tests)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/kapnismology/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kapnismology
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jordi Polo Carres
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '3.
|
33
|
+
version: '3.4'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '3.
|
40
|
+
version: '3.4'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: byebug
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -154,6 +154,7 @@ files:
|
|
154
154
|
- lib/kapnismology/rake_task.rb
|
155
155
|
- lib/kapnismology/result.rb
|
156
156
|
- lib/kapnismology/smoke_test.rb
|
157
|
+
- lib/kapnismology/smoke_test_collection.rb
|
157
158
|
- lib/kapnismology/terminal.rb
|
158
159
|
- lib/kapnismology/version.rb
|
159
160
|
homepage: https://www.github.com/JordiPolo/kapnismology
|