lavin 0.1.0 → 0.3.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
  SHA256:
3
- metadata.gz: ed47d43b5e5f91e1af2c3e319fa03d3588aee9826215394038359e559773a173
4
- data.tar.gz: 17261c42f46a7c4e1ecff28adbd92434c5ec66ac28d4c208d6195a55bf49f69d
3
+ metadata.gz: 52a0a48c405e0aff4b816ff7bf91b27e8407929a838db819f8777d222b4f7674
4
+ data.tar.gz: 4acb3c0a928acd45ecbd8137089433e69cdc8235919a0db6fefefb40418bfd82
5
5
  SHA512:
6
- metadata.gz: 747a493c85dd8fe99c5da92d9fc8380248f80a7b0483bfe42ba3f48fc8a06c89ecc45dfa02f424304b75d28709dfd7c35bc9137a658b22cb449dab91a4bb13ff
7
- data.tar.gz: 304de7aa069cb83a61f3530bec34df37349e4572cdc4eb23ec20b86daa4a75606f207248f7b4efe5f3338d8f4166ba94c71ee33e856bc22e6cb36720585627cf
6
+ metadata.gz: e2fe666e13f22d13dc4f085e8ab7e78c2242a973bd9b0d58054e0b4ada2cc17d1ca924e4103a3c55408bd88c4d1cd3efa8b3449cee7fd8a5d36794392218779c
7
+ data.tar.gz: b1f7f478a288492082a4f02462d345ce42703316638b3d6aee7848a92a539b22b750105e5e70fdf2ffe662ac26ed68a4f6a32c63154b18ba4faf96dee917b16b
checksums.yaml.gz.sig CHANGED
Binary file
data/lib/lavin/client.rb CHANGED
@@ -5,6 +5,13 @@ require 'async/http/internet'
5
5
  module Lavin
6
6
  class Client
7
7
  class Error < Lavin::Error; end
8
+
9
+ class ServerError < Error
10
+ def initialize(status)
11
+ super("Server responded with status: #{status}")
12
+ end
13
+ end
14
+
8
15
  class NoCurrentAsyncTaskError < Error
9
16
  def initialize(msg = nil)
10
17
  super(msg || "Trying to create a client outside of an Async task")
@@ -17,7 +24,7 @@ module Lavin
17
24
  }.freeze
18
25
 
19
26
  attr_reader :internet, :base_url
20
- attr_accessor :request_count, :cookie
27
+ attr_accessor :request_count, :cookie, :report_statistics
21
28
 
22
29
  def initialize(base_url = nil)
23
30
  raise NoCurrentAsyncTaskError unless Async::Task.current?
@@ -26,6 +33,7 @@ module Lavin
26
33
  @base_url = base_url
27
34
  @request_count = 0
28
35
  @cookie = nil
36
+ @report_statistics = true
29
37
  end
30
38
 
31
39
  def close
@@ -41,7 +49,7 @@ module Lavin
41
49
 
42
50
  status, headers, body = process(response)
43
51
 
44
- Statistics.register_request(method:, url:, status:, duration:)
52
+ Statistics.register_request(method:, url:, status:, duration:) if report_statistics
45
53
 
46
54
  {status:, headers:, body: body}
47
55
  end
data/lib/lavin/error.rb CHANGED
@@ -2,4 +2,8 @@
2
2
 
3
3
  module Lavin
4
4
  class Error < StandardError; end
5
+
6
+ class RecoverableError < Error; end
7
+
8
+ class IrrecoverableError < Error; end
5
9
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lavin/error'
4
+
5
+ module Lavin
6
+ module Failure
7
+ def failure(msg)
8
+ raise RecoverableError, msg
9
+ end
10
+
11
+ def failure!(msg)
12
+ raise IrrecoverableError, msg
13
+ end
14
+ end
15
+ end
data/lib/lavin/hook.rb ADDED
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lavin
4
+ class Hook
5
+ attr_reader :user, :block, :type
6
+
7
+ def initialize(user:, type: :before, &block)
8
+ @user = user
9
+ @block = block
10
+ @type = type
11
+ end
12
+
13
+ def run(context: nil)
14
+ call(context:)
15
+ Runner.yield
16
+ end
17
+
18
+ def call(context:)
19
+ report_statistics = context.client.report_statistics
20
+ context.client.report_statistics = false
21
+ context.instance_exec(&block)
22
+ rescue RecoverableError
23
+ rescue => error
24
+ puts "Caught #{error.class} in #{type} hook: #{error.message}"
25
+ puts error.backtrace unless error.is_a? IrrecoverableError
26
+ throw :failure
27
+ ensure
28
+ context.client.report_statistics = report_statistics
29
+ end
30
+ end
31
+ end
@@ -9,7 +9,7 @@ module Lavin
9
9
 
10
10
  def initialize(**kwargs)
11
11
  super(**kwargs)
12
- @client = Client.new(config[:base_url])
12
+ @client = kwargs.fetch(:client) { Client.new(config[:base_url]) }
13
13
  end
14
14
 
15
15
  def cleanup
@@ -17,27 +17,35 @@ module Lavin
17
17
  end
18
18
 
19
19
  def get(url, headers: {})
20
- client.request(:get, url:, headers:)
20
+ request(:get, url:, headers:)
21
21
  end
22
22
 
23
23
  def head(url, headers: {})
24
- client.request(:head, url:, headers:)
24
+ request(:head, url:, headers:)
25
25
  end
26
26
 
27
27
  def post(url, headers: {}, body: nil)
28
- client.request(:post, url:, headers:, body:)
28
+ request(:post, url:, headers:, body:)
29
29
  end
30
30
 
31
31
  def put(url, headers: {}, body: nil)
32
- client.request(:put, url:, headers:, body:)
32
+ request(:put, url:, headers:, body:)
33
33
  end
34
34
 
35
35
  def patch(url, headers: {}, body: nil)
36
- client.request(:patch, url:, headers:, body:)
36
+ request(:patch, url:, headers:, body:)
37
37
  end
38
38
 
39
39
  def delete(url, headers: {})
40
- client.request(:delete, url:, headers:)
40
+ request(:delete, url:, headers:)
41
+ end
42
+
43
+ private
44
+
45
+ def request(method, url:, headers:, body: nil)
46
+ client.request(method, url:, headers:, body:).tap do |response|
47
+ raise Lavin::Client::ServerError, response[:status] if response[:status] > 499
48
+ end
41
49
  end
42
50
  end
43
51
  end
data/lib/lavin/runner.rb CHANGED
@@ -57,14 +57,14 @@ module Lavin
57
57
  rescue StandardError => error
58
58
  puts "Failed to run in thread: #{error.message}"
59
59
  thread.join
60
- thread = nil
60
+ self.thread = nil
61
61
  raise
62
62
  end
63
63
 
64
64
  def stop
65
- Statistics.stop
66
65
  thread&.kill
67
66
  self.thread = nil
67
+ Statistics.stop
68
68
  end
69
69
 
70
70
  def running?
@@ -28,6 +28,10 @@ module Lavin
28
28
  data[:total_requests]
29
29
  end
30
30
 
31
+ def total_steps
32
+ data[:step_summary][:count]
33
+ end
34
+
31
35
  def duration
32
36
  return data[:duration] if data[:duration]
33
37
 
@@ -42,8 +46,14 @@ module Lavin
42
46
  data[:requests][key] << result
43
47
  end
44
48
 
45
- def register_step
46
- # TODO
49
+ def register_step(user:, step_name:, failure: nil)
50
+ data[:step_summary][:count] += 1
51
+ data[:step_summary][:success] += 1 unless failure
52
+ data[:step_summary][:failure] += 1 if failure
53
+ key = [user, step_name]
54
+ data[:steps][key][:success] += 1 unless failure
55
+ data[:steps][key][:failure] += 1 if failure
56
+ data[:failures][failure.to_s] += 1 if failure
47
57
  end
48
58
 
49
59
  def stats
@@ -53,9 +63,11 @@ module Lavin
53
63
  requests = data[:requests].map do |(method, url), requests|
54
64
  durations = []
55
65
  statuses = []
66
+ failed_requests = 0
56
67
  requests.each do |request|
57
68
  durations << request[:duration]
58
69
  statuses << request[:status]
70
+ failed_requests += 1 if request[:failure]
59
71
  end
60
72
  min_duration = durations.min
61
73
  max_duration = durations.max
@@ -66,9 +78,10 @@ module Lavin
66
78
  url: url,
67
79
  requests: requests.size,
68
80
  statuses: statuses.tally,
69
- avg_duration: avg_duration,
70
- min_duration: min_duration,
71
- max_duration: max_duration,
81
+ failed_requests:,
82
+ avg_duration:,
83
+ min_duration:,
84
+ max_duration:
72
85
  }
73
86
  end
74
87
 
@@ -76,7 +89,10 @@ module Lavin
76
89
  duration: duration,
77
90
  total_requests: total_requests,
78
91
  rate: duration ? format("%.2f", total_requests / duration) : 0,
79
- requests: requests
92
+ step_summary: data[:step_summary],
93
+ steps: data[:steps],
94
+ requests: requests,
95
+ failures: data[:failures]
80
96
  ).tap do |stats|
81
97
  # FIXME remove!
82
98
  puts "Calculated stats in #{Time.now - time}s"
@@ -88,12 +104,24 @@ module Lavin
88
104
 
89
105
  show_summary(values)
90
106
 
107
+ show_steps(values) do |(user, step), hash|
108
+ format(
109
+ "%-48<user_step>s %8<success>d %8<failure>d",
110
+ user_step: "#{user}.#{step}",
111
+ **hash
112
+ )
113
+ end
114
+
91
115
  show_table(values) do |request_values|
92
116
  format(
93
117
  "%-6<method>s %-100<url>s %6<requests>d %12<avg_duration>fs %12<min_duration>fs %12<max_duration>fs",
94
118
  **request_values
95
119
  )
96
120
  end
121
+
122
+ show_failures(values) do |message, count|
123
+ format("%-64<message>s %6<count>d", message:, count:)
124
+ end
97
125
  end
98
126
 
99
127
  private
@@ -116,8 +144,14 @@ module Lavin
116
144
  start: nil,
117
145
  duration: nil,
118
146
  total_requests: 0,
119
- steps: [],
120
- requests: Hash.new { |h, k| h[k] = [] }
147
+ step_summary: {
148
+ count: 0,
149
+ success: 0,
150
+ failure: 0
151
+ },
152
+ steps: Hash.new { |h, k| h[k] = {success: 0, failure: 0} },
153
+ requests: Hash.new { |h, k| h[k] = [] },
154
+ failures: Hash.new { |h, k| h[k] = 0 }
121
155
  }
122
156
  end
123
157
 
@@ -127,14 +161,29 @@ module Lavin
127
161
  Lavin results:
128
162
  Test ran for #{values.duration}s
129
163
  Total number of requests: #{values.total_requests}
130
- Rate: #{format("%.2f", values.total_requests/values.duration)} rps
164
+ Rate: #{format("%.2f", values.total_requests / values.duration)} rps
131
165
 
166
+ Total number of steps: #{values.total_steps}
167
+ Step success rate: #{format("%.2f %%", 100 * values.successful_steps.to_f / values.total_steps)}
132
168
  RESULT
133
169
  end
134
170
 
171
+ def show_steps(values)
172
+ puts format(
173
+ "\n%-48<user_step>s %8<success>s %8<failure>s",
174
+ user_step: "Steps",
175
+ success: "Success",
176
+ failure: "Failure"
177
+ )
178
+ divider = "-" * 66
179
+ puts divider
180
+ values.each_step { |step_values| puts yield step_values }
181
+ puts divider
182
+ end
183
+
135
184
  def show_table(values)
136
185
  puts format(
137
- "%-6<method>s %-100<url>s %-6<requests>s %12<avg_duration>s %12<min_duration>s %12<max_duration>s",
186
+ "\n%-6<method>s %-100<url>s %-6<requests>s %12<avg_duration>s %12<min_duration>s %12<max_duration>s",
138
187
  method: "Method",
139
188
  url: "URL",
140
189
  requests: "Requests",
@@ -148,6 +197,14 @@ module Lavin
148
197
  values.each_request { |request_values| puts yield request_values }
149
198
  puts divider
150
199
  end
200
+
201
+ def show_failures(values)
202
+ puts format("\n%-64<message>s %6<count>s", message: "Failures", count: "Count")
203
+ divider = "-" * 71
204
+ puts divider
205
+ values.each_failure { |failures| puts yield failures }
206
+ puts divider
207
+ end
151
208
  end
152
209
  end
153
210
  end
data/lib/lavin/stats.rb CHANGED
@@ -2,13 +2,16 @@
2
2
 
3
3
  module Lavin
4
4
  class Stats
5
- attr_reader :duration, :total_requests, :rate, :requests
5
+ attr_reader :duration, :total_requests, :rate, :requests, :step_summary, :steps, :failures
6
6
 
7
- def initialize(duration:, total_requests:, rate:, requests: [])
7
+ def initialize(duration:, total_requests:, rate:, requests: [], step_summary: {}, steps: [], failures: [])
8
8
  @duration = duration
9
9
  @total_requests = total_requests
10
10
  @rate = rate
11
11
  @requests = requests
12
+ @step_summary = step_summary
13
+ @steps = steps
14
+ @failures = failures
12
15
  end
13
16
 
14
17
  def empty?
@@ -20,12 +23,35 @@ module Lavin
20
23
  duration:,
21
24
  total_requests:,
22
25
  rate:,
23
- requests:
26
+ requests:,
27
+ step_summary:,
28
+ steps:,
29
+ failures:
24
30
  }
25
31
  end
26
32
 
33
+ def total_steps
34
+ @step_summary[:count]
35
+ end
36
+
37
+ def successful_steps
38
+ @step_summary[:success]
39
+ end
40
+
41
+ def failed_steps
42
+ @step_summary[:failure]
43
+ end
44
+
45
+ def each_step(&block)
46
+ steps.each(&block)
47
+ end
48
+
27
49
  def each_request(&block)
28
50
  requests.each(&block)
29
51
  end
52
+
53
+ def each_failure(&block)
54
+ failures.each(&block)
55
+ end
30
56
  end
31
57
  end
data/lib/lavin/step.rb CHANGED
@@ -1,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'lavin/error'
4
+
3
5
  module Lavin
4
6
  class Step
5
- attr_reader :user, :block, :repeat
7
+ attr_reader :name, :user, :block, :repeat
6
8
 
7
- def initialize(repeat: 1, &block)
9
+ def initialize(name:, user:, repeat: 1, &block)
10
+ @name = name
11
+ @user = user
8
12
  @repeat = repeat
9
13
  @block = block
10
14
  end
@@ -18,11 +22,12 @@ module Lavin
18
22
 
19
23
  def call(context:)
20
24
  context.instance_exec(&block)
21
- # Report Success!
25
+ Statistics.register_step(user: user.name, step_name: name)
26
+ rescue IrrecoverableError => error
27
+ Statistics.register_step(user: user.name, step_name: name, failure: error.message)
28
+ throw :failure
22
29
  rescue => error
23
- puts "Caught an error - #{error.class}: #{error.message}"
24
- puts error.backtrace
25
- # Report Failure!
30
+ Statistics.register_step(user: user.name, step_name: name, failure: error.message)
26
31
  end
27
32
  end
28
33
  end
data/lib/lavin/user.rb CHANGED
@@ -4,6 +4,7 @@ require "set"
4
4
  require "lavin/user_config"
5
5
  require "lavin/worker"
6
6
  require "lavin/http_client"
7
+ require "lavin/failure"
7
8
 
8
9
  module Lavin
9
10
  class User
@@ -12,6 +13,7 @@ module Lavin
12
13
  subclass.include UserConfig
13
14
  subclass.include Worker
14
15
  subclass.include HttpClient
16
+ subclass.include Failure
15
17
  all_personas << subclass
16
18
  end
17
19
 
data/lib/lavin/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lavin
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -40,6 +40,11 @@ module Lavin
40
40
  redirect to('/statistics')
41
41
  end
42
42
 
43
+ post '/stop' do
44
+ Lavin::Runner.stop
45
+ redirect to('/statistics')
46
+ end
47
+
43
48
  get '/statistics' do
44
49
  stats = Statistics.stats
45
50
  running = Lavin::Runner.running?
@@ -50,6 +55,16 @@ module Lavin
50
55
  end
51
56
  end
52
57
 
58
+ get '/failures' do
59
+ stats = Statistics.stats
60
+ running = Lavin::Runner.running?
61
+ if stats.empty? && !running
62
+ redirect to('/')
63
+ else
64
+ erb :failures, locals: {stats:, running:}
65
+ end
66
+ end
67
+
53
68
  get '/edit' do
54
69
  persona = find_persona
55
70
  raise Sinatra::NotFound unless persona
data/lib/lavin/worker.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'lavin/step'
4
+ require 'lavin/hook'
4
5
 
5
6
  module Lavin
6
7
  module Worker
@@ -8,21 +9,22 @@ module Lavin
8
9
  def before(&block)
9
10
  return @before unless block
10
11
 
11
- @before = block
12
+ @before = Hook.new(user: self, type: :before, &block)
12
13
  end
13
14
 
14
15
  def after(&block)
15
16
  return @after unless block
16
17
 
17
- @after = block
18
+ @after = Hook.new(user: self, type: :after, &block)
18
19
  end
19
20
 
20
21
  def steps
21
22
  @steps ||= []
22
23
  end
23
24
 
24
- def step(**options, &block)
25
- steps << Step.new(**options, &block)
25
+ def step(name: nil, **options, &block)
26
+ name ||= "Step##{steps.size + 1}"
27
+ steps << Step.new(user: self, name:, **options, &block)
26
28
  end
27
29
  end
28
30
 
@@ -38,11 +40,15 @@ module Lavin
38
40
  end
39
41
 
40
42
  def run
41
- self.class.before.call.then { Runner.yield } if self.class.before
43
+ catch(:failure) do
44
+ self.class.before.run(context: self).then { Runner.yield } if self.class.before
42
45
 
43
- run_step until finished?
46
+ run_step until finished?
47
+ end
44
48
 
45
- self.class.after.call.then { Runner.yield } if self.class.after
49
+ catch(:failure) do
50
+ self.class.after.run(context: self).then { Runner.yield } if self.class.after
51
+ end
46
52
  end
47
53
 
48
54
  private
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lavin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sammy Henningsson
@@ -29,7 +29,7 @@ cert_chain:
29
29
  DVzXaUnsmwP+jQ1PkDa5q8ibBzMd2c6Hmm87UDqPxZtML0bF9SjrpbyLMjwtXaMA
30
30
  WDPp0ajpdUZ9GPHsrVNYXiOfQIqcmlmpYVsH1o7vuneUIcIDMrnMDChh
31
31
  -----END CERTIFICATE-----
32
- date: 2022-10-30 00:00:00.000000000 Z
32
+ date: 2022-11-02 00:00:00.000000000 Z
33
33
  dependencies:
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: async
@@ -113,6 +113,8 @@ files:
113
113
  - lib/lavin.rb
114
114
  - lib/lavin/client.rb
115
115
  - lib/lavin/error.rb
116
+ - lib/lavin/failure.rb
117
+ - lib/lavin/hook.rb
116
118
  - lib/lavin/http_client.rb
117
119
  - lib/lavin/runner.rb
118
120
  - lib/lavin/statistics.rb
metadata.gz.sig CHANGED
Binary file