lavin 0.3.0 → 0.5.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: 52a0a48c405e0aff4b816ff7bf91b27e8407929a838db819f8777d222b4f7674
4
- data.tar.gz: 4acb3c0a928acd45ecbd8137089433e69cdc8235919a0db6fefefb40418bfd82
3
+ metadata.gz: f992df4f0b4f330811a5c440e4f2cc0f2d514c6adc93520851b5a3f37c1807d0
4
+ data.tar.gz: 1ab740ad5a07113568023f7a46e2d36a722422ad4c3ea75dbcd7974dd79db570
5
5
  SHA512:
6
- metadata.gz: e2fe666e13f22d13dc4f085e8ab7e78c2242a973bd9b0d58054e0b4ada2cc17d1ca924e4103a3c55408bd88c4d1cd3efa8b3449cee7fd8a5d36794392218779c
7
- data.tar.gz: b1f7f478a288492082a4f02462d345ce42703316638b3d6aee7848a92a539b22b750105e5e70fdf2ffe662ac26ed68a4f6a32c63154b18ba4faf96dee917b16b
6
+ metadata.gz: '07319fe5d1f1cdfbd5cd86519ce6b273f773397ff11707cef85bda11c65a1b77a27fc5a739c7c1aa3c61f20726103a08d9d45afd0d1d811849c30820717d0d1b'
7
+ data.tar.gz: 3049f57b36562110c1bc09ffe0a7bfe7648cb8134c9cff73f9d74c6e66590749177158999d2c4f7f41cd0d8ad7e712d90410a05b56dc36222d878ab3a8879f0e
checksums.yaml.gz.sig CHANGED
@@ -1,3 +1,2 @@
1
- >�����r$
2
- b���{
3
- �l[GLfT�9��=y����ӄ�$�i�q���}%����N�B»�
1
+ 0S���Q��dUw�ל��\�<,��� �����ϸ�;ߤ"*��CU�#̬�D�
2
+ �i������tNG70,$����#{Ɠ`d��M�8��ȁ�g4nk����c.ljV�<�;W�;56߫��[a���@]���͜T"��qH����pT��ng���t�{@�ؚQ��/�ُr:����_:`���#�U�x�\Yb�j�����
data/lib/lavin/error.rb CHANGED
@@ -6,4 +6,8 @@ module Lavin
6
6
  class RecoverableError < Error; end
7
7
 
8
8
  class IrrecoverableError < Error; end
9
+
10
+ class SuccessfulStep < Error; end
11
+
12
+ class SuccessfulUser < Error; end
9
13
  end
data/lib/lavin/hook.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'lavin/error'
4
+
3
5
  module Lavin
4
6
  class Hook
5
7
  attr_reader :user, :block, :type
@@ -19,11 +21,11 @@ module Lavin
19
21
  report_statistics = context.client.report_statistics
20
22
  context.client.report_statistics = false
21
23
  context.instance_exec(&block)
22
- rescue RecoverableError
24
+ rescue SuccessfulStep, RecoverableError
25
+ rescue SuccessfulUser, IrrecoverableError
26
+ throw :stop_user
23
27
  rescue => error
24
- puts "Caught #{error.class} in #{type} hook: #{error.message}"
25
- puts error.backtrace unless error.is_a? IrrecoverableError
26
- throw :failure
28
+ puts "Exception in #{user.name}.#{type} block - #{error.class}: #{error.message}"
27
29
  ensure
28
30
  context.client.report_statistics = report_statistics
29
31
  end
data/lib/lavin/step.rb CHANGED
@@ -23,10 +23,14 @@ module Lavin
23
23
  def call(context:)
24
24
  context.instance_exec(&block)
25
25
  Statistics.register_step(user: user.name, step_name: name)
26
+ rescue SuccessfulStep, SuccessfulUser => error
27
+ Statistics.register_step(user: user.name, step_name: name)
28
+ throw :stop_user if error.is_a? SuccessfulUser
26
29
  rescue IrrecoverableError => error
27
30
  Statistics.register_step(user: user.name, step_name: name, failure: error.message)
28
- throw :failure
31
+ throw :stop_user
29
32
  rescue => error
33
+ puts "Exception in #{user.name}.#{name} - #{error.class}: #{error.message}"
30
34
  Statistics.register_step(user: user.name, step_name: name, failure: error.message)
31
35
  end
32
36
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lavin/error'
4
+
5
+ module Lavin
6
+ module Success
7
+ def success
8
+ raise SuccessfulStep
9
+ end
10
+
11
+ def success!
12
+ raise SuccessfulUser
13
+ end
14
+ end
15
+ end
data/lib/lavin/user.rb CHANGED
@@ -5,6 +5,7 @@ require "lavin/user_config"
5
5
  require "lavin/worker"
6
6
  require "lavin/http_client"
7
7
  require "lavin/failure"
8
+ require "lavin/success"
8
9
 
9
10
  module Lavin
10
11
  class User
@@ -14,6 +15,7 @@ module Lavin
14
15
  subclass.include Worker
15
16
  subclass.include HttpClient
16
17
  subclass.include Failure
18
+ subclass.include Success
17
19
  all_personas << subclass
18
20
  end
19
21
 
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.3.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/lavin/worker.rb CHANGED
@@ -40,13 +40,13 @@ module Lavin
40
40
  end
41
41
 
42
42
  def run
43
- catch(:failure) do
43
+ catch(:stop_user) do
44
44
  self.class.before.run(context: self).then { Runner.yield } if self.class.before
45
45
 
46
46
  run_step until finished?
47
47
  end
48
48
 
49
- catch(:failure) do
49
+ catch(:stop_user) do
50
50
  self.class.after.run(context: self).then { Runner.yield } if self.class.after
51
51
  end
52
52
  end
data/views/edit.erb ADDED
@@ -0,0 +1,25 @@
1
+ <div class="flex flex-col items-center">
2
+ <h1 class="mt-32 text-center text-2xl">Edit <%= persona.name %></h1>
3
+ <div class="w-1/5 bg-white dark:bg-slate-800 rounded-lg mt-8 px-6 py-4 shadow-slate-900 shadow-lg">
4
+ <form action="/update_config" method="post" class="mt-16">
5
+ <% persona.config.each do |key, value| %>
6
+ <input type="hidden" name="persona" value="<%= persona.name %>">
7
+ <% if value == true || value == false %>
8
+ <div class="my-4">
9
+ <label for="<%= key %>" class="text-slate-500 dark:text-slate-300"><%= key.to_s.capitalize %></label>
10
+ <input type="checkbox" id="<%= key %>" name="<%= key %>" class="float-right" <%= "checked" if value %>></br>
11
+ </div>
12
+ <% else %>
13
+ <div class="mt-2">
14
+ <label for="<%= key %>" class="text-slate-500 dark:text-slate-300"><%= key.to_s.capitalize %></label></br>
15
+ <input type="<%= input_type_for(value) %>" id="<%= key %>" name="<%= key %>" class="rounded p-2 w-full" value="<%= value %>"></br>
16
+ </div>
17
+ <% end %>
18
+ <% end %>
19
+ <div class="mt-8 flex flex-row gap-x-4">
20
+ <a href="/" class="p-2 grow bg-slate-300 hover:bg-slate-400 dark:bg-slate-500 dark:hover:bg-slate-600 shadow-slate-900 shadow-lg rounded text-center">Cancel</a>
21
+ <input type=submit value="Update" class="p-2 grow bg-indigo-400 hover:bg-indigo-500 dark:bg-indigo-500 dark:hover:bg-indigo-600 shadow-slate-900 shadow-lg rounded text-center">
22
+ </div>
23
+ </form>
24
+ </div>
25
+ </div>
@@ -0,0 +1,62 @@
1
+ <div class="mt-16 h-full flex-grow flex flex-col items-center">
2
+ <div class="flex flex-col w-full h-full bg-white dark:bg-slate-800 px-16 pt-16 pb-8 text-slate-500 dark:text-slate-300">
3
+ <div class="flex flex-row items-center">
4
+ <div class="flex-grow">
5
+ <p>Duration: <%= format("%.3fs", stats.duration) %></p>
6
+ <p>Total requests: <%= stats.total_requests%></p>
7
+ <p>Rate: <%= stats.rate%> rps</p>
8
+ </div>
9
+ <% if running %>
10
+ <div class="">
11
+ <span class="text-amber-700 text-xl animate-pulse">Running</span>
12
+ </div>
13
+ <div class="ml-8 w-1/12">
14
+ <form action="/stop" method="post" class="mb-0">
15
+ <input type=submit value="Stop" class="px-4 py-2 shadow-slate-900 shadow-lg rounded bg-amber-700 hover:bg-amber-800 text-xl text-center w-full">
16
+ </form>
17
+ </div>
18
+ <% end %>
19
+ </div>
20
+ <div class="w-full h-full overflow-auto mt-8">
21
+ <table class="w-1/4 table-auto text-slate-500 dark:text-slate-300">
22
+ <thead>
23
+ <tr>
24
+ <th class="text-left">Steps</th>
25
+ <th class="text-right">Success</th>
26
+ <th class="text-right">Failure</th>
27
+ </tr>
28
+ </thead>
29
+ <% stats.each_step do |(user, step), step_stats| %>
30
+ <tr class="border-b border-dashed hover:border-solid border-slate-700">
31
+ <td class="text-left text-sm"><%= "#{user} - #{step}" %></td>
32
+ <td class="text-right text-sm"><%= step_stats[:success] %></td>
33
+ <td class="text-right text-sm"><%= step_stats[:failure] %></td>
34
+ </tr>
35
+ <% end %>
36
+ </table>
37
+ <table class="mt-8 w-1/2 table-auto text-slate-500 dark:text-slate-300">
38
+ <thead>
39
+ <tr>
40
+ <th class="text-left">Failure</th>
41
+ <th class="text-left">Count</th>
42
+ </tr>
43
+ </thead>
44
+ <% stats.each_failure do |message, count| %>
45
+ <tr class="border-b border-dashed hover:border-solid border-slate-700">
46
+ <td class="text-left text-sm"><%= message %></td>
47
+ <td class="text-left text-sm"><%= count%></td>
48
+ </tr>
49
+ <% end %>
50
+ </table>
51
+ </div>
52
+ </div>
53
+ <div class="w-full h-1/6 text-center py-10 object-center">
54
+ <a href="/" class="bg-slate-500 p-2 shadow-slate-900 shadow-lg rounded hover:bg-slate-600">Back to start</a>
55
+ <a href="/statistics" class="ml-4 bg-slate-500 p-2 shadow-slate-900 shadow-lg rounded hover:bg-slate-600">Statistics</a>
56
+ </div>
57
+ </div>
58
+ <% if running %>
59
+ <script>
60
+ window.setTimeout(function() {window.location.reload();}, 2000);
61
+ </script>
62
+ <% end %>
data/views/index.erb ADDED
@@ -0,0 +1,19 @@
1
+ <div class="mt-32 flex flex-col items-center">
2
+ <% if personas.empty? %>
3
+ <p class="text-lg">No configration found please restart the server with a path to a directory containing the configuration!</p>
4
+ <% else %>
5
+ <div class="w-1/5">
6
+ <form action="/start" method="post">
7
+ <input type=submit value="Start" class="py-3 shadow-slate-900 shadow-lg rounded bg-amber-700 hover:bg-amber-800 text-xl text-center w-full">
8
+ </form>
9
+ </div>
10
+ <h2 class="mt-16 text-2xl">Configuration</h2>
11
+ <div class="w-5/6 flex flex-row flex-wrap gap-x-8 gap-y-4 justify-center">
12
+ <% personas.each do |persona| %>
13
+ <div class="w-1/4">
14
+ <%= erb :user_config, locals: {persona: persona} %>
15
+ </div>
16
+ <% end %>
17
+ </div>
18
+ <% end %>
19
+ </div>
data/views/layout.erb ADDED
@@ -0,0 +1,14 @@
1
+ <head>
2
+ <meta charset="UTF-8">
3
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
4
+ <title>Lavin load testing</title>
5
+ <script src="https://cdn.tailwindcss.com"></script>
6
+ </head>
7
+ <body class="bg-cyan-800">
8
+ <div class="h-screen flex flex-col">
9
+ <div>
10
+ <h1 class="mt-16 text-center text-4xl"><strong class="text-6xl">Lavin</strong> load testing</h1>
11
+ </div>
12
+ <%= yield %>
13
+ </div>
14
+ </body>
@@ -0,0 +1,4 @@
1
+ <div class="flex flex-col items-center">
2
+ <h1 class="mt-16 text-2xl text-center">Sorry, this page does not exist!</h1>
3
+ <a href="/" class="mt-8 bg-slate-300 hover:bg-slate-400 dark:bg-slate-500 dark:hover:bg-slate-600 p-2 shadow-slate-900 shadow-lg rounded">Back to start</a>
4
+ </div>
@@ -0,0 +1,72 @@
1
+ <div class="mt-16 h-full flex-grow flex flex-col items-center">
2
+ <div class="flex flex-col w-full h-full bg-white dark:bg-slate-800 px-16 pt-16 pb-8 text-slate-500 dark:text-slate-300">
3
+ <div class="flex flex-row items-center">
4
+ <div class="flex-grow">
5
+ <p>Duration: <%= format("%.3fs", stats.duration) %></p>
6
+ <p>Total requests: <%= stats.total_requests%></p>
7
+ <p>Rate: <%= stats.rate%> rps</p>
8
+ </div>
9
+ <% if running %>
10
+ <div class="">
11
+ <span class="text-amber-700 text-xl animate-pulse">Running</span>
12
+ </div>
13
+ <div class="ml-8 w-1/12">
14
+ <form action="/stop" method="post" class="mb-0">
15
+ <input type=submit value="Stop" class="px-4 py-2 shadow-slate-900 shadow-lg rounded bg-amber-700 hover:bg-amber-800 text-xl text-center w-full">
16
+ </form>
17
+ </div>
18
+ <% end %>
19
+ </div>
20
+ <div class="w-full h-full overflow-auto mt-8">
21
+ <table class="w-1/4 table-auto text-slate-500 dark:text-slate-300">
22
+ <thead>
23
+ <tr>
24
+ <th class="text-left">Steps</th>
25
+ <th class="text-right">Success</th>
26
+ <th class="text-right">Failure</th>
27
+ </tr>
28
+ </thead>
29
+ <% stats.each_step do |(user, step), step_stats| %>
30
+ <tr class="border-b border-dashed hover:border-solid border-slate-700">
31
+ <td class="text-left text-sm"><%= "#{user} - #{step}" %></td>
32
+ <td class="text-right text-sm"><%= step_stats[:success] %></td>
33
+ <td class="text-right text-sm"><%= step_stats[:failure] %></td>
34
+ </tr>
35
+ <% end %>
36
+ </table>
37
+ <table class="mt-8 w-full table-auto text-slate-500 dark:text-slate-300">
38
+ <thead>
39
+ <tr>
40
+ <th class="text-left">Method</th>
41
+ <th class="text-left">URL</th>
42
+ <th class="text-right">Requests</th>
43
+ <th class="text-right">Failed requests</th>
44
+ <th class="text-right">Avg duration</th>
45
+ <th class="text-right">Min duration</th>
46
+ <th class="text-right">Max duration</th>
47
+ </tr>
48
+ </thead>
49
+ <% stats.each_request do |request_stats| %>
50
+ <tr class="border-b border-dashed hover:border-solid border-slate-700">
51
+ <td class="text-left text-sm"><%= request_stats[:method] %></td>
52
+ <td class="text-left text-sm"><%= request_stats[:url]%></td>
53
+ <td class="text-right text-sm"><%= request_stats[:requests]%></td>
54
+ <td class="text-right text-sm"><%= request_stats[:failed_requests]%></td>
55
+ <td class="text-right text-sm"><%= format("%8fs", request_stats[:avg_duration]) %></td>
56
+ <td class="text-right text-sm"><%= format("%8fs", request_stats[:min_duration]) %></td>
57
+ <td class="text-right text-sm"><%= format("%8fs", request_stats[:max_duration]) %></td>
58
+ </tr>
59
+ <% end %>
60
+ </table>
61
+ </div>
62
+ </div>
63
+ <div class="w-full h-1/6 text-center py-10 object-center">
64
+ <a href="/" class="bg-slate-500 p-2 shadow-slate-900 shadow-lg rounded hover:bg-slate-600">Back to start</a>
65
+ <a href="/failures" class="ml-4 bg-slate-500 p-2 shadow-slate-900 shadow-lg rounded hover:bg-slate-600">Failures</a>
66
+ </div>
67
+ </div>
68
+ <% if running %>
69
+ <script>
70
+ window.setTimeout(function() {window.location.reload();}, 2000);
71
+ </script>
72
+ <% end %>
@@ -0,0 +1,15 @@
1
+ <div class="mt-8 px-6 py-8 bg-white dark:bg-slate-800 rounded-lg shadow-slate-900 shadow-lg">
2
+ <h3 class="text-center text-slate-900 dark:text-white text-2xl"><%= persona.name %></h3>
3
+ <p class="text-center truncate text-slate-500 dark:text-slate-400 mt-2 text-sm"><%= persona.description %></p>
4
+ <ul class="mt-8">
5
+ <% persona.config.each do |key, value| %>
6
+ <li>
7
+ <span class="text-slate-500 dark:text-slate-300"><%= key %></span>
8
+ <span class="text-slate-500 dark:text-slate-300 float-right"><%= value%></span>
9
+ </li>
10
+ <% end %>
11
+ </ul>
12
+ <div class="mt-8 flex flex-row justify-end">
13
+ <a href="/edit?persona=<%= persona.name %>" class="mt-4 text-sm bg-slate-300 hover:bg-slate-400 dark:bg-slate-500 dark:hover:bg-slate-600 p-2 shadow-slate-900 shadow-4xl rounded">Edit</a>
14
+ </div>
15
+ </div>
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.3.0
4
+ version: 0.5.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-11-02 00:00:00.000000000 Z
32
+ date: 2022-11-04 00:00:00.000000000 Z
33
33
  dependencies:
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: async
@@ -120,11 +120,19 @@ files:
120
120
  - lib/lavin/statistics.rb
121
121
  - lib/lavin/stats.rb
122
122
  - lib/lavin/step.rb
123
+ - lib/lavin/success.rb
123
124
  - lib/lavin/user.rb
124
125
  - lib/lavin/user_config.rb
125
126
  - lib/lavin/version.rb
126
127
  - lib/lavin/web_server.rb
127
128
  - lib/lavin/worker.rb
129
+ - views/edit.erb
130
+ - views/failures.erb
131
+ - views/index.erb
132
+ - views/layout.erb
133
+ - views/not_found.erb
134
+ - views/statistics.erb
135
+ - views/user_config.erb
128
136
  homepage: https://github.com/sammyhenningsson/lavin
129
137
  licenses:
130
138
  - MIT
metadata.gz.sig CHANGED
Binary file