elastic_results 1.0.2

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: 99161c14e7dbbdf95b0b78a312fb3c2c4e1cc7de
4
+ data.tar.gz: 3db34b799da012ecc7912dbc3bdff3f4675d94a2
5
+ SHA512:
6
+ metadata.gz: b505765a9fd4d5600e2eb33b2808cc252d6b0badce330a6fe08178ff551446a7381ffa1435e5f3bf316b2b5679bb0b49818a72c00ec4905f93a7d29682c80899
7
+ data.tar.gz: 352701d01b84d4a9c6db7d79aba5f220f796f7bc2be5872f2934fb92ec59ef250f4c2cdd71507008f670d889ee4efa0484bba4930959a6a7de49fe7a7e625fb0
data/.gitignore ADDED
@@ -0,0 +1,86 @@
1
+ ### JetBrains template
2
+ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
3
+
4
+ *.iml
5
+
6
+ ## Directory-based project format:
7
+ .idea/
8
+ # if you remove the above rule, at least ignore the following:
9
+
10
+ # User-specific stuff:
11
+ # .idea/workspace.xml
12
+ # .idea/tasks.xml
13
+ # .idea/dictionaries
14
+
15
+ # Sensitive or high-churn files:
16
+ # .idea/dataSources.ids
17
+ # .idea/dataSources.xml
18
+ # .idea/sqlDataSources.xml
19
+ # .idea/dynamic.xml
20
+ # .idea/uiDesigner.xml
21
+
22
+ # Gradle:
23
+ # .idea/gradle.xml
24
+ # .idea/libraries
25
+
26
+ # Mongo Explorer plugin:
27
+ # .idea/mongoSettings.xml
28
+
29
+ ## File-based project format:
30
+ *.ipr
31
+ *.iws
32
+
33
+ ## Plugin-specific files:
34
+
35
+ # IntelliJ
36
+ /out/
37
+
38
+ # mpeltonen/sbt-idea plugin
39
+ .idea_modules/
40
+
41
+ # JIRA plugin
42
+ atlassian-ide-plugin.xml
43
+
44
+ # Crashlytics plugin (for Android Studio and IntelliJ)
45
+ com_crashlytics_export_strings.xml
46
+ crashlytics.properties
47
+ crashlytics-build.properties
48
+ ### Ruby template
49
+ *.gem
50
+ *.rbc
51
+ /.config
52
+ /coverage/
53
+ /InstalledFiles
54
+ /pkg/
55
+ /spec/reports/
56
+ /spec/examples.txt
57
+ /test/tmp/
58
+ /test/version_tmp/
59
+ /tmp/
60
+
61
+ ## Specific to RubyMotion:
62
+ .dat*
63
+ .repl_history
64
+ build/
65
+
66
+ ## Documentation cache and generated files:
67
+ /.yardoc/
68
+ /_yardoc/
69
+ /doc/
70
+ /rdoc/
71
+
72
+ ## Environment normalisation:
73
+ /.bundle/
74
+ /vendor/bundle
75
+ /lib/bundler/man/
76
+
77
+ # for a library or gem, you might want to ignore these files since the code is
78
+ # intended to run in multiple environments; otherwise, check them in:
79
+ # Gemfile.lock
80
+ # .ruby-version
81
+ # .ruby-gemset
82
+
83
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
84
+ .rvmrc
85
+
86
+ ru# Created by .ignore support plugin (hsz.mobi)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in elastic_results.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,33 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ elastic_results (0.1.0)
5
+ elasticsearch
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ elasticsearch (1.0.15)
11
+ elasticsearch-api (= 1.0.15)
12
+ elasticsearch-transport (= 1.0.15)
13
+ elasticsearch-api (1.0.15)
14
+ multi_json
15
+ elasticsearch-transport (1.0.15)
16
+ faraday
17
+ multi_json
18
+ faraday (0.9.1)
19
+ multipart-post (>= 1.2, < 3)
20
+ multi_json (1.11.2)
21
+ multipart-post (2.0.0)
22
+ rake (10.4.2)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ bundler (~> 1.10)
29
+ elastic_results!
30
+ rake (~> 10.0)
31
+
32
+ BUNDLED WITH
33
+ 1.10.6
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 CoverMyMeds
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # ElasticResults
2
+
3
+ Store the results of your test runs in Elasticsearch via custom formatters.
4
+
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'elastic_results'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install elastic_results
21
+
22
+ ## Cucumber Usage
23
+ Add this line to your features/support/env.rb file:
24
+ ```ruby
25
+ require 'elastic_results/cucumber'
26
+ ```
27
+
28
+ Tell cucumber to use elastic_results:
29
+ ```
30
+ bundle exec cucumber --format ElasticResults::Cucumber::Formatter
31
+ ```
32
+
33
+ Tell cucumber to use elastic_results but still give normal output:
34
+ ```
35
+ bundle exec cucumber --format pretty --format ElasticResults::Cucumber::Formatter -o /dev/null
36
+ ```
37
+
38
+ Make this the default by adding the following to config/cucumber.yml
39
+ ```
40
+ default: --format pretty --format ElasticResults::Cucumber::Formatter -o /dev/null
41
+ ```
42
+
43
+ ## RSpec Usage
44
+ Add this line to your spec_helper.rb file:
45
+ ```ruby
46
+ require 'elastic_results/rspec'
47
+ ```
48
+
49
+ Tell rspec to use elastic_results:
50
+ ```
51
+ bundle exec rspec -r 'elastic_results/rspec' --format ElasticResults::RSpec::Formatter
52
+ ```
53
+
54
+ Tell rspec to use elastic_results but still give normal output:
55
+ ```
56
+ bundle exec rspec -r 'elastic_results/rspec' --format ElasticResults::RSpec::Formatter --format progress
57
+ ```
58
+
59
+ Make this the default by adding the following to spec_helper.rb
60
+ ```ruby
61
+ RSpec.configure do |config|
62
+ config.add_formatter 'ElasticResults::RSpec::Formatter'
63
+ config.add_formatter 'progress'
64
+ # the rest of your rspec configuration
65
+ end
66
+ ```
67
+ Then run rspec as normal.
68
+
69
+
70
+ ## SimpleCov Usage
71
+ Add this lines to your spec_helper.rb file:
72
+ ```ruby
73
+ require 'elastic_results/simplecov'
74
+ SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, ElasticResults::SimpleCov::Formatter]
75
+ ```
76
+ Then run simplecov as normal.
77
+
78
+
79
+
80
+ ## Configuration
81
+ elastic_results exposes several configuration points that can be set via environment variables or by accssing them on the ElasticResults module:
82
+
83
+ | ENV | ElasticResults | Default | Notes |
84
+ |--------------------|-------------------|-----------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
85
+ | ES_URL | es_url | http://localhost | The URL where elasticsearch can be reached |
86
+ | ES_INDEX_RESULT | es_index_result | test_results-YYYY-MM-DD | The index to post test results to. The default uses a prefix of test_results and a suffix of today's date |
87
+ | ES_TYPE_RESULT | es_type_result | test_result | The data type to use for test results. |
88
+ | ES_INDEX_COVERAGE | es_index_coverage | coverage-YYYY-MM-DD | The index to post coverage data to. The default uses a prefix of coverage and a suffix of today's date |
89
+ | ES_TYPE_COVERAGE | es_type_coverage | simplecov | The data type to use for coverage data. |
90
+ | ES_LOG | es_log | ENV['DEBUG'] | Log calls to elasticsearch to STDOUT? (useful for debugging elastic_results) |
91
+ | SUITE_NAME | suite_name | Dir.pwd | The name of your suite. If not given, it will be guessed using the name of the current folder when you launch your tests. |
92
+ | SUITE_TYPE | suite_type | integration | The type of suite you're running. i.e. unit, integration, regression, etc |
93
+ | GIT_COMMIT | | Output of: git rev-parse HEAD | The git revision being tested. Jenkins will set this in CI, otherwise it's pulled from git. |
94
+ | GIT_URL | | Output of: git config --get remote.origin.url | The url to the git repo being used. Jenkins will set this in CI, otherwise it's pulled from git. |
95
+ | GIT_BRANCH | | Output of: git rev-parse --abbrev-ref HEAD | The git branch being used. Jenkins will set this in CI, otherwise it's pulled from git. |
96
+ | TEAM_NAME | team_name | ??? | The name of your team. This makes it possible to slice your results easier. |
97
+ | BUILD_URL | | | The url to your Jenkins build if any. Set by Jenkins. |
98
+ | NODE_NAME | | Socket.gethostname | The node that ran the test. Set by Jenkins, or pulled from the machine. |
99
+ | BUILD_NUMBER | | MMDDYYHHMMSSUU | The jenkins build number. If not present, a fake build number will be built using the current date/time down to the millisecond. |
100
+ | JOB_NAME | | | The jenkins job name. |
101
+ | BUILD_TAG | | | The jenkins build tag. |
102
+ | TEST_ENV/RAILS_ENV | | | The enivironment the tests were run against. Will use TEST_ENV if present, otherwise will use RAILS_ENV |
103
+ | | use_unsafe_index | true if WebMock is defined, otherwise false | If true, validation of the SSL cert for the elasticsearch host will be skipped. Useful when your testing tools break things. |
104
+
105
+
106
+ ## Example config block:
107
+ ```ruby
108
+ ElasticResults.team_name = 'MyTeam - MySubTeam'
109
+ ElasticResults.suite_type = 'unit'
110
+ ElasticResults.kibana_url = 'https://kibana.mycompany.com'
111
+ ElasticResults.es_url = %w(https://es1.mycompany.net:9200 https://es1.mycompany.net:9200).sample
112
+ ```
113
+
114
+
115
+ ## Development
116
+
117
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake false` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
118
+
119
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
120
+
121
+ ## Contributing
122
+
123
+ Bug reports and pull requests are welcome on GitHub at https://github.com/covermymeds/elastic_results
124
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'elastic_results'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'elastic_results/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'elastic_results'
8
+ spec.version = ElasticResults::VERSION
9
+ spec.authors = ['Donavan Stanley']
10
+ spec.email = ['dstanley@covermymeds.com']
11
+
12
+ spec.summary = 'Formatters to shove test results into elastic search'
13
+ spec.homepage = 'https://github.com/covermymeds/elastic_results'
14
+
15
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
16
+ # delete this section to allow pushing this gem to any host.
17
+ if spec.respond_to?(:metadata)
18
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
19
+ else
20
+ fail 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
21
+ end
22
+
23
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ spec.bindir = 'exe'
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ['lib']
27
+
28
+ spec.add_development_dependency 'bundler', '~> 1.10'
29
+ spec.add_development_dependency 'rake', '~> 10.0'
30
+ spec.add_development_dependency 'pry'
31
+ spec.add_dependency 'elasticsearch'
32
+ spec.add_dependency 'googl'
33
+ end
@@ -0,0 +1,70 @@
1
+ require 'cucumber/formatter/gherkin_formatter_adapter'
2
+ require 'gherkin/formatter/argument'
3
+ require 'gherkin/formatter/json_formatter'
4
+ require 'elastic_results'
5
+ require 'elasticsearch'
6
+ require 'time'
7
+ require 'socket'
8
+
9
+ module ElasticResults
10
+ # Namespace for cucumber related stuff
11
+ module Cucumber
12
+ # Replacement for the stock Json formatter that outputs to elasticsearch
13
+ class JSONFormatter < ::Gherkin::Formatter::JSONFormatter
14
+ def initialize
15
+ @feature_hashes = []
16
+ @current_step_or_hook = nil
17
+ end
18
+
19
+ def eof
20
+ feature_hash = @feature_hashes.last
21
+ feature_hash['elements'].each do |scenario_hash|
22
+ record_scenario feature_hash, scenario_hash
23
+ end
24
+ end
25
+
26
+ def done
27
+ ElasticResults.write_urls
28
+ end
29
+
30
+ private
31
+
32
+ def record_scenario(feature_hash, scenario_hash)
33
+ ElasticResults.index_result result_for(feature_hash, scenario_hash)
34
+ end
35
+
36
+ def result_for(feature_hash, scenario_hash)
37
+ Result.new.tap do |res|
38
+ res.id = scenario_hash['id']
39
+ res.feature_name = feature_hash['name']
40
+ res.scenario_name = scenario_hash['name']
41
+ res.uri = feature_hash['uri']
42
+
43
+
44
+ res.tags.append(feature_hash['tags'].map { |t| t['name'] }) unless feature_hash['tags'].nil?
45
+
46
+
47
+ res.tags.append(scenario_hash['tags'].map { |t| t['name'] }) unless scenario_hash['tags'].nil?
48
+
49
+ # Cucumber stores times in nanoseconds for each step, because that's SUPER useful!
50
+ res.runtime = scenario_hash['steps'].map { |s| s['result']['duration'] }.reduce { |a, e| a + e } / 1_000_000_000.0
51
+
52
+ failing_step = scenario_hash['steps'].find { |step| !step['result'].nil? && step['result']['status'] == :failed }
53
+ unless failing_step.nil?
54
+ res.exception_msg = failing_step['result']['error_message']
55
+ res.exception_class = res.exception_msg =~ /ExpectationNotMetError/ ? 'ExpectationNotMetError' : 'RuntimeError'
56
+ res.exception_stacktrace = "#{failing_step['name']}\n#{failing_step['match']['location']}"
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ # A format adapter that uses our special JSON formatter
63
+ class Formatter < ::Cucumber::Formatter::GherkinFormatterAdapter
64
+ def initialize(_runtime, _io, options)
65
+ options[:expand] = true
66
+ super(ElasticResults::Cucumber::JSONFormatter.new, false, options)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,50 @@
1
+ require 'time'
2
+ require 'socket'
3
+
4
+ module ElasticResults
5
+ # A common data format for test results. The Cucumber and Rspec formatters populate this class.
6
+ class Result
7
+ attr_accessor :outcome, :exception_class, :exception_msg, :exception_stacktrace, :runtime
8
+ attr_accessor :id, :uri, :feature_name, :scenario_name, :build_number
9
+
10
+ def to_hash
11
+ result_hash = base_info
12
+
13
+ [:outcome, :exception_class, :exception_msg, :exception_stacktrace, :runtime,
14
+ :id, :uri, :feature_name, :scenario_name, :tags].each do |field|
15
+ result_hash[field] = send field
16
+ end
17
+ result_hash
18
+ end
19
+
20
+ def tags
21
+ @tags ||= []
22
+ end
23
+
24
+ def outcome
25
+ @outcome ||= exception_class.nil? ? 'pass' : fail_type
26
+ end
27
+
28
+ def fail_type
29
+ exception_class =~ /ExpectationNotMet/ ? :fail : :broke
30
+ end
31
+
32
+ def base_info
33
+ {
34
+ '@timestamp' => Time.now.iso8601(3),
35
+ build_number: ENV['BUILD_NUMBER'] ? ENV['BUILD_NUMBER'] : ElasticResults.build_id,
36
+ build_url: ENV['BUILD_URL'],
37
+ node_name: ENV['NODE_NAME'] ? ENV['NODE_NAME'] : Socket.gethostname,
38
+ job_name: ENV['JOB_NAME'],
39
+ build_tag: ENV['BUILD_TAG'],
40
+ environment: ENV['TEST_ENV'] || ENV['RAILS_ENV'],
41
+ git_commit: ElasticResults.git_commit,
42
+ git_url: ElasticResults.git_url,
43
+ git_branch: ElasticResults.git_branch,
44
+ suite_name: ElasticResults.suite_name,
45
+ suite_type: ElasticResults.suite_type,
46
+ team: ElasticResults.team_name
47
+ }
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,52 @@
1
+ RSpec::Support.require_rspec_core 'formatters/base_formatter'
2
+ require 'elastic_results'
3
+ require 'json'
4
+ require 'elasticsearch'
5
+ require 'time'
6
+ require 'socket'
7
+
8
+ module ElasticResults
9
+ module RSpec
10
+ # @private
11
+ class Formatter < ::RSpec::Core::Formatters::BaseFormatter
12
+ ::RSpec::Core::Formatters.register self, :example_passed, :example_failed, :close
13
+
14
+ def initialize(output)
15
+ super
16
+ end
17
+
18
+ def example_passed(notification)
19
+ record_example notification.example
20
+ end
21
+
22
+ def example_failed(notification)
23
+ record_example notification.example
24
+ end
25
+
26
+ def record_example(example)
27
+ ElasticResults.index_result result_for(example)
28
+ end
29
+
30
+ def close(_notification)
31
+ ElasticResults.write_urls
32
+ end
33
+
34
+ private
35
+
36
+ def result_for(example)
37
+ Result.new.tap do |res|
38
+ res.id = example.id
39
+ res.feature_name = example.description
40
+ res.scenario_name = example.full_description
41
+ res.uri = example.location
42
+ res.runtime = example.metadata[:execution_result].run_time
43
+ unless example.exception.nil?
44
+ res.exception_class = example.exception.class.name
45
+ res.exception_msg = example.exception.message
46
+ res.exception_stacktrace = example.exception.backtrace
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,86 @@
1
+ {
2
+ "mappings": {
3
+ "simplecov": {
4
+ "_timestamp": {
5
+ "enabled": true
6
+ },
7
+ "properties": {
8
+ "@timestamp": {
9
+ "type": "date",
10
+ "format": "dateOptionalTime"
11
+ },
12
+ "environment":{
13
+ "type": "string",
14
+ "index" : "not_analyzed",
15
+ "null_value" : "na"
16
+ },
17
+ "suite_name":{
18
+ "type": "string",
19
+ "null_value" : "na"
20
+ },
21
+ "suite_type":{
22
+ "type": "string",
23
+ "index" : "not_analyzed",
24
+ "null_value" : "na"
25
+ },
26
+ "team":{
27
+ "type": "string",
28
+ "null_value" : "na"
29
+ },
30
+ "build_number": {
31
+ "type": "string",
32
+ "index" : "not_analyzed",
33
+ "null_value" : "na"
34
+ },
35
+ "build_url": {
36
+ "type": "string",
37
+ "index" : "not_analyzed",
38
+ "null_value" : "na"
39
+ },
40
+ "node_name": {
41
+ "type": "string",
42
+ "index" : "not_analyzed",
43
+ "null_value" : "na"
44
+ },
45
+ "job_name": {
46
+ "type": "string",
47
+ "index" : "not_analyzed",
48
+ "null_value" : "na"
49
+ },
50
+ "build_tag": {
51
+ "type": "string",
52
+ "index" : "not_analyzed",
53
+ "null_value" : "na"
54
+ },
55
+ "git_commit": {
56
+ "type": "string",
57
+ "index" : "not_analyzed",
58
+ "null_value" : "na"
59
+ },
60
+ "git_url": {
61
+ "type": "string",
62
+ "index" : "not_analyzed",
63
+ "null_value" : "na"
64
+ },
65
+ "git_branch": {
66
+ "type": "string",
67
+ "null_value" : "na"
68
+ },
69
+ "metrics": {
70
+ "properties": {
71
+ "covered_strength": {
72
+ "type": "float"
73
+ },
74
+ "covered_lines": {
75
+ "type": "integer"
76
+ },
77
+ "total_lines": {
78
+ "type": "integer"
79
+ }
80
+ }
81
+ }
82
+
83
+ }
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,95 @@
1
+ {
2
+ "mappings": {
3
+ "test_result": {
4
+ "_timestamp": {
5
+ "enabled": true
6
+ },
7
+ "properties": {
8
+ "@timestamp": {
9
+ "type": "date",
10
+ "format": "dateOptionalTime"
11
+ },
12
+ "environment":{
13
+ "type": "string",
14
+ "index" : "not_analyzed",
15
+ "null_value" : "na"
16
+ },
17
+ "suite_name":{
18
+ "type": "string",
19
+ "null_value" : "na"
20
+ },
21
+ "suite_type": {
22
+ "type": "string",
23
+ "index": "not_analyzed",
24
+ "null_value" : "na"
25
+ },
26
+ "team":{
27
+ "type": "string",
28
+ "null_value" : "na"
29
+ },
30
+ "outcome": {
31
+ "type": "string",
32
+ "index" : "not_analyzed",
33
+ "null_value" : "na"
34
+ },
35
+ "exception_msg": {
36
+ "type": "string"
37
+ },
38
+ "exception_class": {
39
+ "type": "string",
40
+ "null_value" : "na"
41
+ },
42
+ "exception_stacktrace": {
43
+ "type": "string",
44
+ "null_value" : "na"
45
+ },
46
+ "runtime" : {
47
+ "type": "float"
48
+ },
49
+ "build_number": {
50
+ "type": "string",
51
+ "index" : "not_analyzed",
52
+ "null_value" : "na"
53
+ },
54
+ "build_url": {
55
+ "type": "string",
56
+ "index" : "not_analyzed",
57
+ "null_value" : "na"
58
+ },
59
+ "node_name": {
60
+ "type": "string",
61
+ "index" : "not_analyzed",
62
+ "null_value" : "na"
63
+ },
64
+ "job_name": {
65
+ "type": "string",
66
+ "index" : "not_analyzed",
67
+ "null_value" : "na"
68
+ },
69
+ "build_tag": {
70
+ "type": "string",
71
+ "index" : "not_analyzed",
72
+ "null_value" : "na"
73
+ },
74
+ "git_commit": {
75
+ "type": "string",
76
+ "index" : "not_analyzed",
77
+ "null_value" : "na"
78
+ },
79
+ "git_url": {
80
+ "type": "string",
81
+ "index" : "not_analyzed",
82
+ "null_value" : "na"
83
+ },
84
+ "git_branch": {
85
+ "type": "string",
86
+ "index" : "not_analyzed",
87
+ "null_value" : "na"
88
+ },
89
+ "tags": {
90
+ "type": "string"
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,49 @@
1
+ require 'simplecov'
2
+ require 'elastic_results'
3
+
4
+ module ElasticResults
5
+ module SimpleCov
6
+ # A simple formatter that converts a SimpleCov Result to a hash to be stored in elastic search
7
+ class Formatter
8
+ def format(result)
9
+ data = base_info
10
+
11
+ data[:command_name] = result.command_name
12
+ data[:metrics] = {
13
+ covered_percent: result.covered_percent,
14
+ covered_strength: result.covered_strength.nan? ? 0.0 : result.covered_strength,
15
+ covered_lines: result.covered_lines,
16
+ total_lines: result.total_lines
17
+ }
18
+
19
+ ElasticResults.with_disabled_mocks {
20
+ if ElasticResults.use_unsafe_index
21
+ ElasticResults.unsafe_index ElasticResults.es_index_coverage, ElasticResults.es_type_coverage, data
22
+ else
23
+ ElasticResults.client.index index: ElasticResults.es_index_coverage, type: ElasticResults.es_type_coverage, body: data
24
+ end
25
+ }
26
+ end
27
+
28
+ private
29
+
30
+ def base_info
31
+ {
32
+ '@timestamp' => Time.now.iso8601(3),
33
+ build_number: ENV['BUILD_NUMBER'] ? ENV['BUILD_NUMBER'] : ElasticResults.build_id,
34
+ build_url: ENV['BUILD_URL'],
35
+ node_name: ENV['NODE_NAME'] ? ENV['NODE_NAME'] : Socket.gethostname,
36
+ job_name: ENV['JOB_NAME'],
37
+ build_tag: ENV['BUILD_TAG'],
38
+ environment: ENV['TEST_ENV'] || ENV['RAILS_ENV'],
39
+ git_commit: ElasticResults.git_commit,
40
+ git_url: ElasticResults.git_url,
41
+ git_branch: ElasticResults.git_branch,
42
+ suite_name: ElasticResults.suite_name,
43
+ suite_type: ElasticResults.suite_type,
44
+ team: ElasticResults.team_name
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ # Our root namespace for all objects
2
+ module ElasticResults
3
+ VERSION = '1.0.2'
4
+ end
@@ -0,0 +1,176 @@
1
+ require 'elastic_results/version'
2
+ require 'elastic_results/result'
3
+ require 'elasticsearch'
4
+ require 'json'
5
+
6
+ require "net/https"
7
+ require "uri"
8
+
9
+
10
+
11
+ # Our root namespace for all objects
12
+ module ElasticResults
13
+ class << self
14
+ attr_accessor :suite_name
15
+ attr_accessor :suite_type
16
+ attr_accessor :es_url
17
+ attr_accessor :es_index_result
18
+ attr_accessor :es_index_coverage
19
+ attr_accessor :es_type_result
20
+ attr_accessor :es_type_coverage
21
+ attr_accessor :es_log
22
+ attr_accessor :team_name
23
+ attr_accessor :build_id
24
+ attr_accessor :google_api_key
25
+ attr_accessor :kibana_url
26
+ attr_accessor :use_unsafe_index
27
+ end
28
+
29
+ # Return the mapping JSON for used in creating indicies
30
+ # the which paramter should map to to a filename in the setup folder
31
+ def self.mapping_for(which)
32
+ setup_folder = File.expand_path('./elastic_results/setup', File.dirname(__FILE__))
33
+ File.read("#{setup_folder}/#{which.to_s}.json")
34
+ end
35
+
36
+ # Memoized instance of the elasticsearch client
37
+ def self.client
38
+ @@client ||= Elasticsearch::Client.new url: ElasticResults.es_url, log: ElasticResults.es_log
39
+ end
40
+
41
+ # Given a payload store it as a test result
42
+ def self.index_result(payload)
43
+ index ElasticResults.es_index_result, ElasticResults.es_type_result, payload
44
+ end
45
+
46
+ # Given a payload store it as coverage data
47
+ def self.index_coverage(payload)
48
+ index ElasticResults.es_index_coverage, ElasticResults.es_type_coverage, payload
49
+ end
50
+
51
+ # Generic index function that knows when to use the elasticsearch API.
52
+ def self.index(index, type, payload)
53
+ begin
54
+ with_disabled_mocks {
55
+ if ElasticResults.use_unsafe_index
56
+ unsafe_index index, type, payload.to_hash
57
+ else
58
+ client.index index: index, type: type, body: payload.to_hash
59
+ end
60
+ }
61
+ rescue Exception => ex
62
+ puts "#{ex.message} while storing results in Elasticsearch"
63
+ end
64
+
65
+
66
+ end
67
+
68
+ # An "unsafe" index routine which uses Net:HTTP with SSL validation turned off. Needed for when WebMock is in use
69
+ # as it breaks SSL cert validations.
70
+ def self.unsafe_index(index, type, payload)
71
+ uri = URI.parse(ElasticResults.es_url)
72
+ http = Net::HTTP.new(uri.host, uri.port)
73
+
74
+ if ElasticResults.es_url =~ /https/
75
+ http.use_ssl = true
76
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
77
+ end
78
+
79
+ request = Net::HTTP::Post.new("/#{index}/#{type}")
80
+ request.body = payload.to_json
81
+ response = http.request(request)
82
+ end
83
+
84
+ # Write out a tab deliminated file containing links to the build information
85
+ def self.write_urls(filename = 'build_links.txt')
86
+ File.open(filename, 'w') { |file| file.write("View failures in Kibana\t#{ElasticResults.fails_url}\t\nView all results in Kibana\t#{ElasticResults.results_url}\t") }
87
+ end
88
+
89
+ # Returns a link to the results. shortened URL if possible otherwise the full URL from long_results_url.
90
+ def self.results_url
91
+ if ElasticResults.google_api_key.nil?
92
+ long_results_url
93
+ else
94
+ require 'googl'
95
+ Googl.shorten(long_results_url, '127.0.0.1', ElasticResults.google_api_key).short_url
96
+ end
97
+ end
98
+
99
+ # Returns a link to the failures. shortened URL if possible otherwise the full URL from long_fails_url.
100
+ def self.fails_url
101
+ if ElasticResults.google_api_key.nil?
102
+ long_fails_url
103
+ else
104
+ require 'googl'
105
+ Googl.shorten(long_fails_url, '127.0.0.1', ElasticResults.google_api_key).short_url
106
+ end
107
+ end
108
+
109
+
110
+ # Set up defaults for the meta data
111
+ ElasticResults.team_name ||= ENV['TEAM_NAME'].nil? ? '???' : ENV['TEAM_NAME']
112
+ ElasticResults.suite_name ||= ENV['SUITE_NAME'].nil? ? File.basename(Dir.pwd) : ENV['SUITE_NAME']
113
+ ElasticResults.suite_type ||= ENV['SUITE_TYPE'].nil? ? 'integration' : ENV['SUITE_TYPE']
114
+ ElasticResults.es_url ||= ENV['ES_HOST'].nil? ? 'http://localhost' : ENV['ES_HOST']
115
+ ElasticResults.es_index_result ||= ENV['ES_INDEX_RESULT'].nil? ? "test_results-#{DateTime.now.strftime('%F')}" : ENV['ES_INDEX_RESULT']
116
+ ElasticResults.es_type_result ||= ENV['ES_TYPE_RESULT'].nil? ? 'test_result' : ENV['ES_TYPE_RESULT']
117
+ ElasticResults.es_log ||= ENV['DEBUG'].nil? ? false : true
118
+ ElasticResults.build_id ||= DateTime.now.strftime('%Y%m%d%H%M%S%L')
119
+ ElasticResults.es_index_coverage ||= ENV['ES_INDEX_COVERAGE'].nil? ? "coverage-#{DateTime.now.strftime('%F')}" : ENV['ES_INDEX_COVERAGE']
120
+ ElasticResults.es_type_coverage ||= ENV['ES_TYPE_COVERAGE'].nil? ? 'simplecov' : ENV['ES_TYPE_COVERAGE']
121
+ ElasticResults.google_api_key ||= ENV['GOOGLE_API_KEY']
122
+ ElasticResults.kibana_url ||= ENV['KIBANA_URL'].nil? ? 'http://localhost:5601' : ENV['KIBANA_URL']
123
+ ElasticResults.use_unsafe_index ||= defined?(WebMock) ? true : false
124
+
125
+
126
+ private
127
+ # Returns a full link to the discover page showing the test results for the current build
128
+ def self.long_results_url
129
+ build_id = ENV['BUILD_NUMBER'] ? ENV['BUILD_NUMBER'] : ElasticResults.build_id
130
+ "#{ElasticResults.kibana_url}/#/discover/?_g=(refreshInterval:(display:Off,pause:!f,section:0,value:0),time:(from:now%2Fd,mode:quick,to:now%2Fd))&_a=(columns:!(team,suite_name,build_number,environment,runtime,outcome,feature_name,scenario_name,exception_class,exception_msg,exception_stacktrace),filters:!(),index:'test_results-*',interval:auto,query:(query_string:(analyze_wildcard:!t,query:'build_number:#{build_id}')),sort:!('@timestamp',desc))"
131
+ end
132
+
133
+ # Returns a full link to the discover page showing the test failures for the current build
134
+ def self.long_fails_url
135
+ build_id = ENV['BUILD_NUMBER'] ? ENV['BUILD_NUMBER'] : ElasticResults.build_id
136
+ "#{ElasticResults.kibana_url}/#/discover/?_g=(refreshInterval:(display:Off,pause:!f,section:0,value:0),time:(from:now%2Fd,mode:quick,to:now%2Fd))&_a=(columns:!(team,suite_name,build_number,environment,runtime,outcome,feature_name,scenario_name,exception_class,exception_msg,exception_stacktrace),filters:!(),index:'test_results-*',interval:auto,query:(query_string:(analyze_wildcard:!t,query:'build_number:#{build_id}%20AND%20(outcome:broke%20OR%20outcome:fail)')),sort:!('@timestamp',desc))"
137
+ end
138
+
139
+ # Memoize the git_commit to avoid hitting git for each result
140
+ def self.git_commit
141
+ @@git_commit ||= ENV['GIT_COMMIT'] ? ENV['GIT_COMMIT'] : `git rev-parse HEAD`.chomp
142
+ end
143
+
144
+ # Memoize the git_url to avoid hitting git for each result
145
+ def self.git_url
146
+ @@git_url ||= ENV['GIT_URL'] ? ENV['GIT_URL'] : `git config --get remote.origin.url`.chomp
147
+ end
148
+
149
+ # Memoize the git_branch to avoid hitting git for each result
150
+ def self.git_branch
151
+ @@git_branch ||= ENV['GIT_BRANCH'] ? ENV['GIT_BRANCH'] : `git rev-parse --abbrev-ref HEAD`.chomp
152
+ end
153
+
154
+ # Helper function to disable mocks before calling out to the web (and getting blocked)
155
+ def self.with_disabled_mocks
156
+ should_reenable_webmock = false
157
+ if defined? WebMock
158
+ if !WebMock.net_connect_allowed?
159
+ should_reenable_webmock = true
160
+ WebMock.disable!
161
+ end
162
+ end
163
+
164
+ if defined? VCR
165
+ VCR.turned_off {
166
+ yield
167
+ }
168
+ else
169
+ yield
170
+ end
171
+
172
+ if should_reenable_webmock
173
+ WebMock.enable!
174
+ end
175
+ end
176
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elastic_results
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Donavan Stanley
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-12-21 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.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
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.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: elasticsearch
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: googl
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email:
85
+ - dstanley@covermymeds.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - Gemfile.lock
93
+ - LICENSE
94
+ - README.md
95
+ - Rakefile
96
+ - bin/console
97
+ - bin/setup
98
+ - elastic_results.gemspec
99
+ - lib/elastic_results.rb
100
+ - lib/elastic_results/cucumber.rb
101
+ - lib/elastic_results/result.rb
102
+ - lib/elastic_results/rspec.rb
103
+ - lib/elastic_results/setup/coverage.json
104
+ - lib/elastic_results/setup/test_results.json
105
+ - lib/elastic_results/simplecov.rb
106
+ - lib/elastic_results/version.rb
107
+ homepage: https://github.com/covermymeds/elastic_results
108
+ licenses: []
109
+ metadata:
110
+ allowed_push_host: https://rubygems.org
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 2.0.14
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: Formatters to shove test results into elastic search
131
+ test_files: []