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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 85d68c903e5f1bf29d11c3f1ecb0a3a7c850cd9e
4
- data.tar.gz: 4a43dfe929d8e04456ff17d792ec2eb2ca2b3f7a
3
+ metadata.gz: a71f19f3000d41004864b28c10f41b3f147f7868
4
+ data.tar.gz: fa3dcaeda42ddb19d05bcfc0b308cbf8bef70327
5
5
  SHA512:
6
- metadata.gz: a0c11e4d88f89b3b8f49c30d76493103c053f9a8ecd0015b42dd0ad12bf3ce80e43176c3295a02c5bf2d23863873e2c78585031d9e480c3a1f9f2e347d2d9b70
7
- data.tar.gz: 83465c018ce7ee7c1ea6c446a61d52b67a677e8edbfbb838bb40d8eccc3fb67b9b0395a2dd7d1fcfb2bb32050ccff087eb39fbe83e15c2c54cf0fdad1c9d4a56
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 which contains an engine to easily create smoke tests.
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.2'
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
- Kapnismology::Result.new(true, {connection: 'good'}, "Connected!")
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 Kapnismology::Result object
37
+ - Have an instance method `result` returning a Result or Success object
39
38
 
40
- Any class created this way will be called and its result will be added to a resulting hash.
41
- In this case the result of this class would be added to the result as:
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 inside `lib/smoke_test`.
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 of the test, define `self.name` in your
55
- smoke test class.
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 `Kapnismology::NullResult` instead of a `Kapnismology::Result`. Like:
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?('that file'))
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.categories` method in your smoke test class.
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.categories
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=integration_
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,NeitherCallThis_
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 = SmokeTest.evaluations(allowed_tags, blacklist)
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
@@ -5,3 +5,4 @@ require 'kapnismology/evaluation'
5
5
  require 'kapnismology/evaluation_collection'
6
6
  require 'kapnismology/rake_task'
7
7
  require 'kapnismology/smoke_test'
8
+ require 'kapnismology/smoke_test_collection'
@@ -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 |app|
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.result || unavailable_result
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
- <<-eos
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
@@ -20,7 +20,7 @@ module Kapnismology
20
20
  end
21
21
 
22
22
  def to_json
23
- evaluations.delete_if{ |evaluation| evaluation.null_result? }.to_json
23
+ evaluations.delete_if(&:null_result?).to_json
24
24
  end
25
25
 
26
26
  private
@@ -2,7 +2,7 @@ require 'kapnismology/terminal'
2
2
  module Kapnismology
3
3
  class RakeTask
4
4
  def output
5
- evaluations = SmokeTest.evaluations
5
+ evaluations = SmokeTestCollection.evaluations
6
6
  puts
7
7
  puts
8
8
  evaluations.each do |evaluation|
@@ -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
- def passed?
19
- !!@passed
20
- end
21
-
22
- def to_hash
23
- { passed: passed?, data: @data, message: @message }
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 NullResult
30
- attr_reader :message
31
- def initialize(message = 'The result could not be determined')
69
+ class Success < BaseResult
70
+ def initialize(data, message)
71
+ @passed = true
72
+ @data = data
32
73
  @message = message
33
- end
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/evaluation_collection'
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
- def result
15
- raise 'this method has to be implemented in inherited classes'
14
+ # Default constructor may be overwritten by child class
15
+ def initialize
16
+ @all_result_messages = []
16
17
  end
17
18
 
18
- class << self
19
- def inherited(klass)
20
- smoke_tests << klass
21
- end
19
+ def result
20
+
21
+ end
22
22
 
23
- def smoke_tests
24
- @smoke_tests ||= []
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
- def evaluations(allowed_tags=[RUNTIME_TAG], blacklist=[])
28
- # We will run any class which categories are in the allowed list
29
- # and not blacklisted
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
- private
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
@@ -3,8 +3,13 @@ module Kapnismology
3
3
  def self.green(msg)
4
4
  "\e[32m\e[1m#{msg}\e[0m"
5
5
  end
6
+
6
7
  def self.red(msg)
7
8
  "\e[31m\e[1m#{msg}\e[0m"
8
9
  end
10
+
11
+ def self.bold(msg)
12
+ "\e[1m#{msg}\e[0m"
13
+ end
9
14
  end
10
15
  end
@@ -1,3 +1,3 @@
1
1
  module Kapnismology
2
- VERSION = '1.6.0'.freeze
2
+ VERSION = '1.7.0'.freeze
3
3
  end
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.6.0
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-04 00:00:00.000000000 Z
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.2'
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.2'
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