startback-websocket 0.14.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.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/README.md +13 -0
  4. data/Rakefile +18 -0
  5. data/lib/startback/audit/prometheus.rb +87 -0
  6. data/lib/startback/audit/shared.rb +17 -0
  7. data/lib/startback/audit/trailer.rb +129 -0
  8. data/lib/startback/audit.rb +3 -0
  9. data/lib/startback/caching/entity_cache.rb +157 -0
  10. data/lib/startback/caching/no_store.rb +28 -0
  11. data/lib/startback/caching/store.rb +34 -0
  12. data/lib/startback/context/h_factory.rb +43 -0
  13. data/lib/startback/context/middleware.rb +53 -0
  14. data/lib/startback/context.rb +122 -0
  15. data/lib/startback/errors.rb +197 -0
  16. data/lib/startback/event/agent.rb +84 -0
  17. data/lib/startback/event/bus/bunny/async.rb +162 -0
  18. data/lib/startback/event/bus/bunny.rb +1 -0
  19. data/lib/startback/event/bus/memory/async.rb +45 -0
  20. data/lib/startback/event/bus/memory/sync.rb +35 -0
  21. data/lib/startback/event/bus/memory.rb +2 -0
  22. data/lib/startback/event/bus.rb +100 -0
  23. data/lib/startback/event/engine.rb +94 -0
  24. data/lib/startback/event/ext/context.rb +5 -0
  25. data/lib/startback/event/ext/operation.rb +13 -0
  26. data/lib/startback/event.rb +47 -0
  27. data/lib/startback/ext/date_time.rb +9 -0
  28. data/lib/startback/ext/time.rb +9 -0
  29. data/lib/startback/ext.rb +2 -0
  30. data/lib/startback/model.rb +6 -0
  31. data/lib/startback/operation/error_operation.rb +19 -0
  32. data/lib/startback/operation/multi_operation.rb +28 -0
  33. data/lib/startback/operation.rb +78 -0
  34. data/lib/startback/services.rb +11 -0
  35. data/lib/startback/support/data_object.rb +71 -0
  36. data/lib/startback/support/env.rb +41 -0
  37. data/lib/startback/support/fake_logger.rb +18 -0
  38. data/lib/startback/support/hooks.rb +48 -0
  39. data/lib/startback/support/log_formatter.rb +34 -0
  40. data/lib/startback/support/logger.rb +34 -0
  41. data/lib/startback/support/operation_runner.rb +150 -0
  42. data/lib/startback/support/robustness.rb +157 -0
  43. data/lib/startback/support/transaction_manager.rb +25 -0
  44. data/lib/startback/support/transaction_policy.rb +33 -0
  45. data/lib/startback/support/world.rb +54 -0
  46. data/lib/startback/support.rb +26 -0
  47. data/lib/startback/version.rb +8 -0
  48. data/lib/startback/web/api.rb +99 -0
  49. data/lib/startback/web/auto_caching.rb +85 -0
  50. data/lib/startback/web/catch_all.rb +52 -0
  51. data/lib/startback/web/cors_headers.rb +80 -0
  52. data/lib/startback/web/health_check.rb +49 -0
  53. data/lib/startback/web/magic_assets/ng_html_transformer.rb +80 -0
  54. data/lib/startback/web/magic_assets/rake_tasks.rb +64 -0
  55. data/lib/startback/web/magic_assets.rb +98 -0
  56. data/lib/startback/web/middleware.rb +13 -0
  57. data/lib/startback/web/prometheus.rb +16 -0
  58. data/lib/startback/web/shield.rb +58 -0
  59. data/lib/startback.rb +43 -0
  60. data/spec/spec_helper.rb +49 -0
  61. data/spec/unit/audit/test_prometheus.rb +72 -0
  62. data/spec/unit/audit/test_trailer.rb +105 -0
  63. data/spec/unit/caching/test_entity_cache.rb +136 -0
  64. data/spec/unit/context/test_abstraction_factory.rb +64 -0
  65. data/spec/unit/context/test_dup.rb +42 -0
  66. data/spec/unit/context/test_fork.rb +37 -0
  67. data/spec/unit/context/test_h_factory.rb +31 -0
  68. data/spec/unit/context/test_middleware.rb +45 -0
  69. data/spec/unit/context/test_with_world.rb +20 -0
  70. data/spec/unit/context/test_world.rb +17 -0
  71. data/spec/unit/event/bus/memory/test_async.rb +43 -0
  72. data/spec/unit/event/bus/memory/test_sync.rb +43 -0
  73. data/spec/unit/support/hooks/test_after_hook.rb +54 -0
  74. data/spec/unit/support/hooks/test_before_hook.rb +54 -0
  75. data/spec/unit/support/operation_runner/test_around_run.rb +156 -0
  76. data/spec/unit/support/operation_runner/test_before_after_call.rb +48 -0
  77. data/spec/unit/support/test_data_object.rb +156 -0
  78. data/spec/unit/support/test_env.rb +75 -0
  79. data/spec/unit/support/test_robusteness.rb +229 -0
  80. data/spec/unit/support/test_transaction_manager.rb +64 -0
  81. data/spec/unit/support/test_world.rb +72 -0
  82. data/spec/unit/test_event.rb +62 -0
  83. data/spec/unit/test_operation.rb +55 -0
  84. data/spec/unit/test_support.rb +40 -0
  85. data/spec/unit/web/fixtures/assets/app/hello.es6 +4 -0
  86. data/spec/unit/web/fixtures/assets/app/hello.html +1 -0
  87. data/spec/unit/web/fixtures/assets/index.es6 +1 -0
  88. data/spec/unit/web/test_api.rb +82 -0
  89. data/spec/unit/web/test_auto_caching.rb +81 -0
  90. data/spec/unit/web/test_catch_all.rb +77 -0
  91. data/spec/unit/web/test_cors_headers.rb +88 -0
  92. data/spec/unit/web/test_healthcheck.rb +59 -0
  93. data/spec/unit/web/test_magic_assets.rb +82 -0
  94. data/tasks/test.rake +14 -0
  95. metadata +237 -0
@@ -0,0 +1,80 @@
1
+ module Startback
2
+ module Web
3
+ #
4
+ # Sets Cross-Origin Response Headers on requests specifying an Origin
5
+ # HTTP header, according configuration passed at construction and/or
6
+ # environment variables.
7
+ #
8
+ # Example:
9
+ #
10
+ # # Default configuration, using environment variables when set
11
+ # use CorsHeaders
12
+ #
13
+ # # Force a bouncing of the origin, using the Origin request header
14
+ # # as Access-Control-Allow-Origin response header
15
+ # use CorsHeaders, bounce: true
16
+ #
17
+ # # Overrides a specific header
18
+ # use CorsHeaders, headers: { 'Access-Control-Allow-Methods' => 'POST' }
19
+ #
20
+ class CorsHeaders
21
+
22
+ ALLOW_ORIGIN = ENV['STARTBACK_CORS_ALLOW_ORIGIN'] || '*'
23
+
24
+ ALLOW_METHODS = ENV['STARTBACK_CORS_ALLOW_METHODS'] || 'OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE'
25
+
26
+ ALLOW_CREDENTIALS = ENV['STARTBACK_CORS_ALLOW_CREDENTIALS'] || 'true'
27
+
28
+ MAX_AGE = ENV['STARTBACK_CORS_MAX_AGE'] || '1728000'
29
+
30
+ ALLOW_HEADERS = ENV['STARTBACK_CORS_ALLOW_HEADERS'] || 'Authorization, Content-Type, Origin, Accept, If-Modified-Since, If-Match, If-None-Match'
31
+
32
+ EXPOSE_HEADERS = ENV['STARTBACK_CORS_EXPOSE_HEADERS'] || 'Location, ETag, Last-Modified, Content-Type'
33
+
34
+ DEFAULT_CORS_HEADERS = {
35
+ 'Access-Control-Allow-Origin' => ALLOW_ORIGIN,
36
+ 'Access-Control-Allow-Methods' => ALLOW_METHODS,
37
+ 'Access-Control-Allow-Credentials' => ALLOW_CREDENTIALS,
38
+ 'Access-Control-Max-Age' => MAX_AGE,
39
+ 'Access-Control-Allow-Headers' => ALLOW_HEADERS,
40
+ 'Access-Control-Expose-Headers' => EXPOSE_HEADERS
41
+ }
42
+
43
+ DEFAULT_OPTIONS = {
44
+ :headers => DEFAULT_CORS_HEADERS
45
+ }
46
+
47
+ def initialize(app, options = {})
48
+ @app = app
49
+ @options = Startback::Support.deep_merge(DEFAULT_OPTIONS, options)
50
+ end
51
+
52
+ def call(env)
53
+ status, headers, body = @app.call(env)
54
+ if origin = env['HTTP_ORIGIN']
55
+ headers = cors_headers(origin).merge(headers)
56
+ end
57
+ if env['REQUEST_METHOD'] == 'OPTIONS'
58
+ headers['Content-Length'] = '0'
59
+ status, headers, body = [204, headers, []]
60
+ end
61
+ [status, headers, body]
62
+ end
63
+
64
+ private
65
+
66
+ def cors_headers(origin)
67
+ headers = @options[:headers].dup
68
+ if bounce?
69
+ headers['Access-Control-Allow-Origin'] = origin
70
+ end
71
+ headers
72
+ end
73
+
74
+ def bounce?
75
+ @options[:bounce]
76
+ end
77
+
78
+ end # class AllowCors
79
+ end # class CorsHeaders
80
+ end # module Samback
@@ -0,0 +1,49 @@
1
+ module Startback
2
+ module Web
3
+ #
4
+ # Can be used to easily implement a HealthCheck web service inside a Startback
5
+ # application.
6
+ #
7
+ # Examples:
8
+ #
9
+ # # Returns a 204 with no body
10
+ # run Startback::Web::HealthCheck.new
11
+ #
12
+ # # Returns a 204 with no body
13
+ # run Startback::Web::HealthCheck.new { nil }
14
+ #
15
+ # # Returns a 200 with Ok in plain text
16
+ # run Startback::Web::HealthCheck.new { "Ok" }
17
+ #
18
+ # # Re-raises the exception
19
+ # run Startback::Web::HealthCheck.new { raise "Something bad" }
20
+ #
21
+ # Please note that this rack app is not 100% Rack compliant, since it raises
22
+ # any error that the block itself raises. This class aims at being backed up
23
+ # by a Shield and/or CatchAll middleware.
24
+ #
25
+ # This class is not aimed at being subclassed.
26
+ #
27
+ class HealthCheck
28
+
29
+ def initialize(&bl)
30
+ @checker = bl
31
+ end
32
+
33
+ def call(env)
34
+ if debug_msg = check!(env)
35
+ [ 200, { "Content-Type" => "text/plain" }, Array(debug_msg) ]
36
+ else
37
+ [ 204, {}, [] ]
38
+ end
39
+ end
40
+
41
+ protected
42
+
43
+ def check!(env)
44
+ @checker.call if @checker
45
+ end
46
+
47
+ end # class HealthCheck
48
+ end # module Web
49
+ end # module Startback
@@ -0,0 +1,80 @@
1
+ module Startback
2
+ module Web
3
+ class MagicAssets
4
+ #
5
+ # Plugin for MagicAssets that compiles .html angular templates in the
6
+ # assets structure to javascript files filling angular's template cache.
7
+ #
8
+ # Heavily inspired, yet over-simplified version, of angular-rails-templates
9
+ # See https://github.com/pitr/angular-rails-templates, licensed under MIT
10
+ #
11
+ # Example:
12
+ #
13
+ # use Startback::Web::MagicAssets, {
14
+ # plugins: [Startback::Web::MagicAssets::NgHtmlTransfomer.new]
15
+ # }
16
+ #
17
+ class NgHtmlTransformer
18
+
19
+ DEFAULT_OPTIONS = {
20
+ :path => '/assets',
21
+ :ng_module => 'templates',
22
+ :mime_type => 'text/ng-html',
23
+ :extensions => [".html"]
24
+ }
25
+
26
+ def initialize(options = {})
27
+ @options = DEFAULT_OPTIONS.merge(options)
28
+ end
29
+ attr_reader :options
30
+
31
+ def install(sprockets)
32
+ sprockets.register_mime_type options[:mime_type], extensions: options[:extensions]
33
+ sprockets.register_transformer options[:mime_type], 'application/javascript', self
34
+ end
35
+
36
+ TPL = <<-EOF
37
+ angular.module("<%= ng_module %>").run(["$templateCache", function($templateCache) {
38
+ $templateCache.put("<%= angular_template_name %>", <%= html %>)
39
+ }]);
40
+ EOF
41
+
42
+ # inspired by Rails' action_view/helpers/javascript_helper.rb
43
+ JS_ESCAPE_MAP = {
44
+ '\\' => '\\\\',
45
+ "\r\n" => '\n',
46
+ "\n" => '\n',
47
+ "\r" => '\n',
48
+ '"' => '\\"',
49
+ "'" => "\\'"
50
+ }
51
+
52
+ # We want to deliver the shortist valid javascript escaped string
53
+ # Count the number of " vs '
54
+ # If more ', escape "
55
+ # If more ", escape '
56
+ # If equal, prefer to escape "
57
+
58
+ def escape_javascript(raw)
59
+ if raw
60
+ quote = raw.count(%{'}) >= raw.count(%{"}) ? %{"} : %{'}
61
+ escaped = raw.gsub(/(\\|\r\n|[\n\r#{quote}])/u) {|match| JS_ESCAPE_MAP[match] }
62
+ "#{quote}#{escaped}#{quote}"
63
+ else
64
+ '""'
65
+ end
66
+ end
67
+
68
+ def call(input)
69
+ file_path = input[:filename]
70
+ angular_template_name = "#{options[:path]}/#{input[:name]}.html"
71
+ source_file = file_path
72
+ ng_module = options[:ng_module]
73
+ html = escape_javascript(input[:data].chomp)
74
+ ERB.new(TPL).result(binding)
75
+ end
76
+
77
+ end # class NgHtmlTransformer
78
+ end # class MagicAssets
79
+ end # module Web
80
+ end # module Startback
@@ -0,0 +1,64 @@
1
+ module Startback
2
+ module Web
3
+ class MagicAssets
4
+ class RakeTasks
5
+
6
+ DEFAULT_OPTIONS = {
7
+ :namespace => :assets
8
+ }
9
+
10
+ def initialize(rake, options)
11
+ @rake = rake
12
+ @options = DEFAULT_OPTIONS.merge(options)
13
+ install
14
+ end
15
+ attr_reader :rake, :options
16
+
17
+ private
18
+
19
+ def install
20
+ require 'securerandom'
21
+
22
+ ns = options[:namespace]
23
+ target_folder = options[:target]
24
+ assets = options[:assets]
25
+ assets = MagicAssets.new(assets) if assets.is_a?(Hash)
26
+ version = SecureRandom.urlsafe_base64
27
+
28
+ rake.instance_exec do
29
+ namespace(ns) do
30
+
31
+ desc 'Cleans generated assets'
32
+ task :clean do
33
+ FileUtils.rm_rf target_folder
34
+ end
35
+
36
+ task :prepare do
37
+ FileUtils.mkdir_p target_folder
38
+ (target_folder/"VERSION").write(version)
39
+ end
40
+
41
+ desc 'compile javascript assets'
42
+ task :compile_js do
43
+ assets['vendor.js'].write_to(target_folder/"vendor-#{version}.min.js")
44
+ assets['app.js'].write_to(target_folder/"app-#{version}.min.js")
45
+ puts "successfully compiled js assets"
46
+ end
47
+
48
+ desc 'compile css assets'
49
+ task :compile_css do
50
+ assets['vendor.css'].write_to(target_folder/"vendor-#{version}.min.css")
51
+ assets['app.css'].write_to(target_folder/"app-#{version}.min.css")
52
+ puts "successfully compiled css assets"
53
+ end
54
+
55
+ desc 'compile assets'
56
+ task :compile => [:clean, :prepare, :compile_js, :compile_css]
57
+ end
58
+ end
59
+ end
60
+
61
+ end
62
+ end # class MagicAssets
63
+ end # module Web
64
+ end # module Startback
@@ -0,0 +1,98 @@
1
+ require 'sprockets'
2
+ module Startback
3
+ module Web
4
+ #
5
+ # Rack application & middleware that can be used to simplify javascript
6
+ # and css assets management, using Sprockets.
7
+ #
8
+ # Example:
9
+ #
10
+ # # Used as rack app, typically under a path
11
+ # Rack::Builder.new do
12
+ # map '/assets' do
13
+ # run Startback::Web::MagicAssets.new({
14
+ # folder: "/path/to/assets/src"
15
+ # })
16
+ # end
17
+ # run MyApp
18
+ # end
19
+ #
20
+ # # Used as a rack middleware, e.g. in a Sinatra application
21
+ # use Startback::Web::MagicAssets, {
22
+ # folder: "/path/to/assets/src",
23
+ # path: "/assets"
24
+ # }
25
+ #
26
+ # Sprocket configuration can be done through the `:sprocket` option:
27
+ #
28
+ # use Startback::Web::MagicAssets, {
29
+ # sprockets: {
30
+ # :css_compressor => :scss
31
+ # }
32
+ # }
33
+ #
34
+ class MagicAssets
35
+
36
+ DEFAULT_OPTIONS = {
37
+ sprockets: {},
38
+ plugins: {}
39
+ }
40
+
41
+ def initialize(app, options = {})
42
+ app, options = nil, app if app.is_a?(Hash)
43
+ @app = app
44
+ @options = DEFAULT_OPTIONS.merge(options)
45
+ @sprockets = build_sprockets
46
+ end
47
+ attr_reader :sprockets
48
+
49
+ def call(env)
50
+ if new_env = is_match?(env)
51
+ @sprockets.call(new_env)
52
+ else
53
+ @app.call(env)
54
+ end
55
+ end
56
+
57
+ def [](*args, &bl)
58
+ @sprockets.[](*args, &bl)
59
+ end
60
+
61
+ private
62
+
63
+ def path
64
+ @options[:path]
65
+ end
66
+
67
+ def is_match?(env)
68
+ if @app.nil?
69
+ # Not used as a middleware, use this env and match
70
+ env
71
+ elsif env['PATH_INFO'].start_with?(path)
72
+ # Used as a middleware, and PATH_INFO starts with the
73
+ # assets path => strip it for sprockets
74
+ env.merge("PATH_INFO" => env["PATH_INFO"].sub(path, ""))
75
+ else
76
+ # No match, let @app execute with the untouched environment
77
+ nil
78
+ end
79
+ end
80
+
81
+ def build_sprockets
82
+ Sprockets::Environment.new.tap{|s|
83
+ Array(@options[:folder]).each do |folder|
84
+ s.append_path(folder)
85
+ end
86
+ @options[:sprockets].each_pair do |k,v|
87
+ s.public_send(:"#{k}=", v)
88
+ end
89
+ @options[:plugins].each do |p|
90
+ p.install(s)
91
+ end
92
+ }
93
+ end
94
+
95
+ end # class MagicAssets
96
+ end # module Web
97
+ end # module Startback
98
+ require_relative 'magic_assets/rake_tasks'
@@ -0,0 +1,13 @@
1
+ module Startback
2
+ module Web
3
+ module Middleware
4
+
5
+ protected
6
+
7
+ def context(env = @env)
8
+ ::Startback::Context::Middleware.context(env) || Errors.server_error!("Unable to find context!!")
9
+ end
10
+
11
+ end # module Middleware
12
+ end # module Web
13
+ end # module Startback
@@ -0,0 +1,16 @@
1
+ require 'prometheus/middleware/exporter'
2
+ module Startback
3
+ module Web
4
+ #
5
+ # Can be used to expose the prometheus metrics inside a Startback
6
+ # application.
7
+ #
8
+ # Example:
9
+ #
10
+ # use Startback::Web::Prometheus
11
+ #
12
+ class Prometheus < Prometheus::Middleware::Exporter
13
+
14
+ end # class Prometheus
15
+ end # module Web
16
+ end # module Startback
@@ -0,0 +1,58 @@
1
+ module Startback
2
+ module Web
3
+ #
4
+ # This Rack middleware catches all known exceptions raised by sublayers
5
+ # in the Rack chain. Those exceptions are converted to proper HTTP error
6
+ # codes and friendly error messages encoded in json.
7
+ #
8
+ # Please check the Errors module about status codes used for each Startback
9
+ # error.
10
+ #
11
+ # This class aims at being used as top level of a Rack chain.
12
+ #
13
+ # Examples:
14
+ #
15
+ # Rack::Builder.new do
16
+ # use Startback::Web::Shield
17
+ # end
18
+ #
19
+ class Shield < Rack::Robustness
20
+ include Errors
21
+
22
+ self.no_catch_all
23
+ self.content_type 'application/json'
24
+
25
+ # Decoding errors from json and csv are considered user's fault
26
+ self.on(Finitio::TypeError){ 400 }
27
+ self.on(::NotImplementedError){ 501 }
28
+
29
+ # Various other codes for the framework specific error classes
30
+ self.on(Startback::Errors::Error) {|ex|
31
+ ex.class.status
32
+ }
33
+
34
+ # A bit of logic to choose the best error message for the user
35
+ # according to the error class
36
+ self.body{|ex|
37
+ body_for(ex).to_json
38
+ }
39
+
40
+ def body_for(ex)
41
+ ex = ex.root_cause if ex.is_a?(Finitio::TypeError)
42
+ body = { code: ex.class.name, description: ex.message }
43
+ return body unless ex.is_a?(Startback::Errors::Error)
44
+ return body unless ex.has_causes?
45
+
46
+ body[:causes] = ex.causes
47
+ .filter{|cause|
48
+ cause.is_a?(Startback::Errors::Error)
49
+ }
50
+ .map{|cause|
51
+ body_for(cause)
52
+ }
53
+ body
54
+ end
55
+
56
+ end # class Shield
57
+ end # module Web
58
+ end # module Startback
data/lib/startback.rb ADDED
@@ -0,0 +1,43 @@
1
+ require 'sinatra'
2
+ require 'rack/robustness'
3
+ require 'finitio'
4
+ require 'logger'
5
+ require 'path'
6
+ require 'ostruct'
7
+ require 'benchmark'
8
+
9
+ # Provides a reusable backend framework for backend components written
10
+ # in ruby.
11
+ #
12
+ # The framework installs conventions regarding:
13
+ #
14
+ # - The exposition of web service APIs (Framework::Api, on top of Sinatra)
15
+ # - Operations (Framework::Operation)
16
+ # - Error handling (Framework::Errors) and their handling in web APIs
17
+ # (based on Rack::Robustness)
18
+ # - General code support (Framework::Support modules & classes).
19
+ #
20
+ # Please refer to the documentation of those main abstractions for details.
21
+ #
22
+ module Startback
23
+
24
+ # Simply checks that a path exists of raise an error
25
+ def self._!(path)
26
+ Path(path).tap do |p|
27
+ raise "Missing #{p.basename}." unless p.exists?
28
+ end
29
+ end
30
+
31
+ require_relative 'startback/version'
32
+ require_relative 'startback/ext'
33
+ require_relative 'startback/errors'
34
+ require_relative 'startback/support'
35
+ require_relative 'startback/model'
36
+ require_relative 'startback/context'
37
+ require_relative 'startback/operation'
38
+ require_relative 'startback/services'
39
+
40
+ # Logger instance to use for the application
41
+ LOGGER = ::Startback::Support::Logger.new
42
+
43
+ end # module Startback
@@ -0,0 +1,49 @@
1
+ require 'startback'
2
+ require 'startback/event'
3
+ require 'startback/support/fake_logger'
4
+ require 'rack/test'
5
+ require 'ostruct'
6
+
7
+ module SpecHelpers
8
+ end
9
+
10
+ RSpec.configure do |c|
11
+ c.include SpecHelpers
12
+ end
13
+
14
+ class SubContext < Startback::Context
15
+
16
+ attr_accessor :foo
17
+
18
+ h_factory do |c,h|
19
+ c.foo = h["foo"]
20
+ end
21
+
22
+ h_dump do |h|
23
+ h.merge!("foo" => foo)
24
+ end
25
+
26
+ world(:partner) do
27
+ Object.new
28
+ end
29
+
30
+ end
31
+
32
+ class SubContext
33
+
34
+ attr_accessor :bar
35
+
36
+ h_factory do |c,h|
37
+ c.bar = h["bar"]
38
+ end
39
+
40
+ h_dump do |h|
41
+ h.merge!("bar" => bar)
42
+ end
43
+
44
+ end
45
+
46
+ class User
47
+ class Changed < Startback::Event
48
+ end
49
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'startback/audit'
3
+ module Startback
4
+ module Audit
5
+ describe Prometheus do
6
+
7
+ EXPORTER = Prometheus.new({
8
+ prefix: "hello",
9
+ labels: {
10
+ app_version: "1.0"
11
+ }
12
+ })
13
+
14
+ class Runner
15
+ include Startback::Support::OperationRunner
16
+
17
+ class IdealOp < Startback::Operation
18
+ def call
19
+ 42
20
+ end
21
+ end
22
+
23
+ class ExceptionalOp < Startback::Operation
24
+ def call
25
+ raise "Oops"
26
+ end
27
+ end
28
+
29
+ around_run(EXPORTER)
30
+ def test
31
+ run IdealOp.new
32
+ end
33
+ def test_exp
34
+ run ExceptionalOp.new
35
+ end
36
+ end
37
+
38
+ describe 'The ideal case' do
39
+ before do
40
+ expect(EXPORTER.calls).to receive(:observe).with(
41
+ kind_of(Numeric),
42
+ hash_including(labels: {
43
+ operation: "Startback::Audit::Runner::IdealOp",
44
+ startback_version: Startback::VERSION,
45
+ app_version: "1.0"
46
+ }))
47
+ expect(EXPORTER.errors).not_to receive(:increment)
48
+ end
49
+ it 'runs the operation' do
50
+ expect(Runner.new.test).to eql(42)
51
+ end
52
+ end
53
+
54
+ describe 'The exceptional case' do
55
+ before do
56
+ expect(EXPORTER.errors).to receive(:increment).with(
57
+ hash_including(labels: {
58
+ operation: "Startback::Audit::Runner::ExceptionalOp",
59
+ startback_version: Startback::VERSION,
60
+ app_version: "1.0"
61
+ })
62
+ )
63
+ expect(EXPORTER.calls).not_to receive(:observe)
64
+ end
65
+ it 'let errors bubble up' do
66
+ expect { Runner.new.test_exp }.to raise_error(/Oops/)
67
+ end
68
+ end
69
+
70
+ end
71
+ end
72
+ end