stormforge-ruby 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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +17 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +6 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +72 -0
  9. data/Rakefile +6 -0
  10. data/bin/stormforge +18 -0
  11. data/examples/README.md +27 -0
  12. data/examples/Stormfile +1 -0
  13. data/examples/demo_case.rb +12 -0
  14. data/examples/file_upload.rb +28 -0
  15. data/examples/fixtures/requests.csv +2 -0
  16. data/examples/fixtures/users.csv +2 -0
  17. data/examples/simple_and_long.rb +12 -0
  18. data/examples/simple_and_short.rb +12 -0
  19. data/examples/test_case_definition_v1.rb +206 -0
  20. data/lib/core_ext/fixnum.rb +20 -0
  21. data/lib/stormforge.rb +39 -0
  22. data/lib/stormforge/client.rb +227 -0
  23. data/lib/stormforge/dsl.rb +4 -0
  24. data/lib/stormforge/dsl/test_case.rb +9 -0
  25. data/lib/stormforge/dsl/test_case/attribute_access.rb +19 -0
  26. data/lib/stormforge/dsl/test_case/cloud.rb +33 -0
  27. data/lib/stormforge/dsl/test_case/data_source.rb +49 -0
  28. data/lib/stormforge/dsl/test_case/data_source/file_fixture.rb +104 -0
  29. data/lib/stormforge/dsl/test_case/data_source/random_number.rb +64 -0
  30. data/lib/stormforge/dsl/test_case/data_source/random_string.rb +52 -0
  31. data/lib/stormforge/dsl/test_case/definition.rb +128 -0
  32. data/lib/stormforge/dsl/test_case/session.rb +77 -0
  33. data/lib/stormforge/registry.rb +23 -0
  34. data/lib/stormforge/version.rb +3 -0
  35. data/lib/thor/generators/init.rb +14 -0
  36. data/lib/thor/generators/templates/Stormfile +15 -0
  37. data/lib/thor/main.rb +79 -0
  38. data/lib/thor/storm_forge_base.rb +46 -0
  39. data/lib/thor/testcase.rb +23 -0
  40. data/lib/thor/testrun.rb +75 -0
  41. data/spec/client_spec.rb +4 -0
  42. data/spec/dsl/test_case/attribute_access_spec.rb +46 -0
  43. data/spec/dsl/test_case/cloud_spec.rb +15 -0
  44. data/spec/dsl/test_case/data_source/file_fixture_spec.rb +101 -0
  45. data/spec/dsl/test_case/data_source/random_number_spec.rb +51 -0
  46. data/spec/dsl/test_case/data_source/random_string_spec.rb +33 -0
  47. data/spec/dsl/test_case/data_source_spec.rb +12 -0
  48. data/spec/dsl/test_case/definition_spec.rb +107 -0
  49. data/spec/dsl/test_case/session_spec.rb +102 -0
  50. data/spec/dsl/test_case_integration_spec.rb +148 -0
  51. data/spec/dsl/test_case_spec.rb +8 -0
  52. data/spec/fixtures/ip_addresses.csv +1 -0
  53. data/spec/fixtures/slug.csv +1 -0
  54. data/spec/fixtures/users.csv +2 -0
  55. data/spec/registry_spec.rb +34 -0
  56. data/spec/spec_helper.rb +33 -0
  57. data/spec/stormforger_spec.rb +13 -0
  58. data/stormforge_ruby.gemspec +38 -0
  59. metadata +344 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 246410660bb62a59942c33d6e258f0205248793b
4
+ data.tar.gz: 727e4fb96552319e584f48f08859536198c73ab4
5
+ SHA512:
6
+ metadata.gz: 715e40725762bfcbd01fc85af424ceca102b3746ce81c6d4ea3ce9f7c8967eb64cb7aff516d862e9d0c1ffa3bfc9129a605747025a726ef7cb6d631c094e2b94
7
+ data.tar.gz: 47fe2145b5a12446211e74935c228a71a0f71f86e8aafb85cd5ac1bfebfd932e67265a143c6dec5979ef2d6443ae8a102f937c7356033928afb6ef9ef206d347
@@ -0,0 +1,5 @@
1
+ coverage
2
+ tmp
3
+ Gemfile.lock
4
+ examples/fixtures/tmp
5
+ pkg
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format Fuubar
@@ -0,0 +1,17 @@
1
+ language: ruby
2
+ bundler_args: #--without development
3
+ rvm:
4
+ - ruby-head
5
+ - 2.0.0
6
+ - 1.9.3
7
+ - jruby-19mode
8
+ - rbx-2.2.1
9
+ notifications:
10
+ recipients:
11
+ - travis-ci.org@tisba.de
12
+ matrix:
13
+ allow_failures:
14
+ - rvm: ruby-head
15
+ - rvm: jruby-19mode
16
+ - rvm: rbx-2.2.1
17
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in stormforge_ruby.gemspec
4
+ gemspec
@@ -0,0 +1,6 @@
1
+ guard :rspec do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
6
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Sebastian Cohnen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,72 @@
1
+ [![Build Status](https://travis-ci.org/tisba/stormforge-ruby.png)](https://travis-ci.org/tisba/stormforge-ruby)
2
+
3
+ # StormForge
4
+
5
+ `stormforge` is the command line client tool to use StormForger from
6
+ your command line. It can also be used as a ruby gem for deeper
7
+ integration with your tools.
8
+
9
+ **Please note** that StormForger is in closed beta right now and you
10
+ need an account to use it. Visit https://stormforger.com/ to get more
11
+ information.
12
+
13
+
14
+ ## Dependencies
15
+
16
+ stormorge-ruby supports
17
+
18
+ * Ruby 1.9.3
19
+ * Ruby 2.0.0
20
+ * JRuby 1.7.8 (1.9 mode only)
21
+
22
+
23
+ ## Installation
24
+
25
+ Add this line to your application's Gemfile:
26
+
27
+ gem "stormforge-ruby", require: "stormforge"
28
+
29
+ And then execute:
30
+
31
+ $ bundle
32
+
33
+ Or install it yourself as:
34
+
35
+ $ gem install stormforge-ruby
36
+
37
+
38
+ ## CLI Usage
39
+
40
+ To get started, run
41
+
42
+ stormforge auth
43
+
44
+ This will ask you for your StormForger credentials, aquires your API
45
+ token and stores it in `~/.stormforger`. Those credentials will be used
46
+ for all other requests afterwards.
47
+
48
+ To set up StormForger for your project, run
49
+
50
+ stormforge init
51
+
52
+ This will create a `Stormfile` with an example test case so you can get
53
+ started.
54
+
55
+ To get more usage information, run
56
+
57
+ stormforge [SUBCOMMAND] help
58
+
59
+
60
+ ## GEM Usage
61
+
62
+ This gem will be usable as a library for closer integration to your
63
+ projects. Right now this isn't officially supported.
64
+
65
+
66
+ ## Contributing
67
+
68
+ 1. Fork it
69
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
70
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
71
+ 4. Push to the branch (`git push origin my-new-feature`)
72
+ 5. Create new Pull Request
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "thor"
4
+ require "pp"
5
+
6
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
7
+
8
+ require "stormforge"
9
+
10
+ module StormForge::Thor; end
11
+
12
+ require "thor/generators/init"
13
+ require "thor/storm_forge_base"
14
+ require "thor/testrun"
15
+ require "thor/testcase"
16
+ require "thor/main"
17
+
18
+ StormForge::Thor::Main.start(ARGV)
@@ -0,0 +1,27 @@
1
+ # StormForge Test Case Examples
2
+
3
+ Test Cases are definied in a custom Ruby DSL. They basically look like this:
4
+
5
+ ```ruby
6
+ StormForge.define_case :simple_test_case do
7
+ title "Very simple test case"
8
+ version "0.1"
9
+ description "Simple and short test to examine test run process."
10
+ targets "app.example.com"
11
+
12
+ arrival_phase duration: 5.minutes, rate: 1.per_second
13
+
14
+ session "hit start page", 100.percent do
15
+ get "/"
16
+ end
17
+ end
18
+ ```
19
+
20
+ You can specify test cases directly in your `Stormfile` (run `stormforge
21
+ init` to generate one). For more advanced usage, or if you have many
22
+ test cases to manage, you can put them in separate files which you can
23
+ load in your `Stormfile`. Take a look at a fresh generated `Stormfile`
24
+ for further examples and documentation.
25
+
26
+ A comprehensive example for a test case can be found in
27
+ https://github.com/tisba/stormforge-ruby/blob/master/examples/test_case_definition_v1.rb.
@@ -0,0 +1 @@
1
+ Dir[File.join(File.dirname(__FILE__), "*.rb")].each { |test_case| load test_case }
@@ -0,0 +1,12 @@
1
+ StormForge.define_case :demo do
2
+ title "StormForger Demo"
3
+ version "0.1"
4
+ description "Simple and short test to examine test run process."
5
+ targets "testapp.stormforger.com"
6
+
7
+ arrival_phase duration: 5.minutes, rate: 1.per_second
8
+
9
+ session "hit start page", 100.percent do
10
+ get "/knock-knock"
11
+ end
12
+ end
@@ -0,0 +1,28 @@
1
+ StormForge.define_case :file_upload do
2
+ def create_test_file(file_name, size)
3
+ FileUtils.mkdir_p(File.dirname(file_name))
4
+ File.open(file_name, "w") do |file|
5
+ file.write "x" * size
6
+ end
7
+ end
8
+ create_test_file("fixtures/tmp/large_file_1mb.dat", 1.megabyte)
9
+
10
+ title "Upload Test"
11
+ version "0.1"
12
+ description "Testing Uploads."
13
+ targets "testapp.stormforger.com"
14
+
15
+ arrival_phase duration: 5.minutes, rate: 1.per_second
16
+
17
+ data_sources do
18
+ file :large_file_1mb, source: "fixtures/large_file_1mb.dat", simple_file: true
19
+ end
20
+
21
+ session "hit start page", 100.percent do
22
+ put "/file-upload", {
23
+ payload_from_file: :large_file_1mb
24
+ }
25
+
26
+ delete "/files/delete-this"
27
+ end
28
+ end
@@ -0,0 +1,2 @@
1
+ /ad/1
2
+ /ad/2
@@ -0,0 +1,2 @@
1
+ 1;joe@example.com;password1
2
+ 2;marry@example.com;anotherpassword
@@ -0,0 +1,12 @@
1
+ StormForge.define_case :simple_and_long do
2
+ title "Simple and Long :)"
3
+ version "0.1"
4
+ description "Simple and short test to examine test run process."
5
+ targets "testapp.stormforger.com"
6
+
7
+ arrival_phase duration: 50.minutes, rate: 50.per_second
8
+
9
+ session "hit start page", 100.percent do
10
+ get "/"
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ StormForge.define_case :simple_and_short do
2
+ title "Simple and Short"
3
+ version "0.1"
4
+ description "Simple and short test to examine test run process."
5
+ targets "testapp.stormforger.com"
6
+
7
+ arrival_phase duration: 5.minutes, rate: 1.per_second
8
+
9
+ session "hit start page", 100.percent do
10
+ get "/"
11
+ end
12
+ end
@@ -0,0 +1,206 @@
1
+ StormForge.define_case :test_case_dsl_draft do
2
+ def create_test_file(file_name, size)
3
+ FileUtils.mkdir_p(File.dirname(file_name))
4
+ File.open(file_name, "w") do |file|
5
+ file.write "x" * size
6
+ end
7
+ end
8
+
9
+ create_test_file("fixtures/tmp/large_file_1mb.dat", 1.megabyte)
10
+
11
+
12
+ # Test case title
13
+ #
14
+ # You can specify a title or summary of what your test case does. The title will be used to
15
+ # identify a test case and therefore it should be unique.
16
+ #
17
+ # title is mandatory
18
+ title "My first Test Case"
19
+
20
+ # Test case version
21
+ #
22
+ # version specifies **your** version of the test specification. You can use this to differentiate
23
+ # between evolving test cases.
24
+ #
25
+ # Currently this information is purely informational and will be visible in your StormForger
26
+ # account.
27
+ #
28
+ # version is mandatory.
29
+ version "0.4"
30
+
31
+ # Description of the test case
32
+ #
33
+ # The given description is purely informal and will be visible in your
34
+ # account at StormForger.
35
+ description <<-EOS.undent
36
+ This is an example of how a possbile TestCase DSL could look like.
37
+
38
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
39
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
40
+ ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
41
+ aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit
42
+ in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
43
+ Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
44
+ officia deserunt mollit anim id est laborum.
45
+ EOS
46
+
47
+ # Targets for load test
48
+ #
49
+ # Specify a list of target hosts (FQDN is required). Each node is hit with requests in a round
50
+ # robin fashion.
51
+ #
52
+ # All specified requests in sessions will have the specified host.
53
+ #
54
+ # This option is mandatory, at least one node must be specified.
55
+ targets "_1.api.example.com", "_2.api.example.com"
56
+
57
+ # Cloud provider for traffic generation
58
+ #
59
+ # Specify a cloud provider to use to generate traffic. Currently, only :aws is available.
60
+ #
61
+ # This setting is optional. It will default to use Amazon Web Services (:aws) and no specific
62
+ # security group.
63
+ cloud :aws do
64
+ # Security Group of load generating instances
65
+ #
66
+ # The given SG is used on launched instances. You can add this SG to allow incoming traffic to
67
+ # your instances.
68
+ security_group "sg-XXXXXXXX"
69
+ end
70
+
71
+
72
+ # Arrival Phases
73
+ #
74
+ # Arrival phases describe how many users are launched for a specified time. You need at least one
75
+ # phase.
76
+ #
77
+ # You can flag a phase as a "warm up" phase. This is purely informational currently.
78
+ arrival_phase warmup: true, duration: 10.minutes, rate: 10.per_second
79
+ arrival_phase duration: 30.minutes, rate: 5.per_minute
80
+ arrival_phase duration: 1.hour, rate: 10.per_minute
81
+
82
+ # Data Sources
83
+ #
84
+ # data_sources specifies one or more data source to be used in sessions. For usage of the sources,
85
+ # take a look at the session definition.
86
+ data_sources do
87
+ # There are two kinds of test data providers right now:
88
+ #
89
+ # * file based and
90
+ # * random generators
91
+ #
92
+ # File based data sources are expected to contain one line per entity. If you have multiple
93
+ # fields per line, you can name them using the `fields` options. If you have multiple fields per
94
+ # line and don't specify the fields, only the first field is going to be used.
95
+ #
96
+ # Available options:
97
+ #
98
+ # * `source` Specifies the relative or absolute location on the local file system of the
99
+ # fixture. This is mandatory.
100
+ # * `fields` Specifies a list of the available fields per row in the source file. This is
101
+ # optional.
102
+ # * `delimiter` Specifies the field delimiter, defaults to ";". This is optional.
103
+ # * `order` Specifies if the file should be use :sequentially or :randomly. Defaults
104
+ # to :sequentially.
105
+ #
106
+ # fixtures/users.csv could look like this:
107
+ #
108
+ # 1;joe@example.com;password1
109
+ # 2;marry@example.com;anotherpassword
110
+ #
111
+ # fixtures/requests.csv could look like this:
112
+ #
113
+ # /path/to/endpoint
114
+ # /path/to/endpoint?with=query&debug=1
115
+ #
116
+ file :ad_request, source: "fixtures/requests.csv"
117
+ file :user, source: "fixtures/users.csv", fields: [:id, :email, :password]
118
+
119
+ # FIXME simple_file is really a bad name. Other "file" data sources are
120
+ # translated to tsung's file_servers. But in some cases, we just want to have
121
+ # a file available, e.g. to be used for uploads
122
+ file :large_file_1mb, source: "fixtures/tmp/large_file_1mb.dat", simple_file: true
123
+
124
+ # In addition to file based data sources, you can also use random generators:
125
+ #
126
+ # * random_number: Generates a random number (integer) within a given range
127
+ # * random_string: Generates a random, alpha-numeric string with the given length
128
+ #
129
+ random_number :phone, range: 1..45000000
130
+ random_number :campaign_id, range: 10000..999999
131
+ random_string :referrer_token, length: 10
132
+ random_string :user_uid, length: 32
133
+ end
134
+
135
+
136
+ # Sessions
137
+ #
138
+ # A session represents a user or clients's interaction with your service. You have to specify
139
+ # at least one session.
140
+ #
141
+ # A session consists of a name and a likelyhood of a session beeing executed (probability). The
142
+ # summed up probability of all sessions has to be 100.
143
+ #
144
+ # You need at least one session.
145
+ #
146
+ # To access data sources within your session, you need to use the the `pick` method. Dependening
147
+ # on specified data source, you get back a item from a fixture file, or a generated value (see
148
+ # "Data Sources"). Notice, that you have to assign the result of `pick` to a variable, in case you
149
+ # need to use the same value multiple times. Every invocation of `pick` with the same data source,
150
+ # yields in a new value when running a test.
151
+ #
152
+ session "login only", 20.percent do
153
+ # You can access a data source by using `pick`. In case of a file source with multiple fields,
154
+ # you get back an object, where you can use the specified field names as methods:
155
+ user = pick(:user)
156
+
157
+ # To make an HTTP POST request, you can do this:
158
+ post "/users/login", payload: { email: user.email, password: user.password }
159
+ end
160
+
161
+ session "sign up with referrer token and login", 5.percent do
162
+ # Hit the status endpoint with a simple GET request
163
+ get "/status"
164
+
165
+ # Wait a bit
166
+ wait 2.seconds
167
+
168
+ user = pick(:user)
169
+ # Using `pick` with a generated data source, like :token (random string) or :phone (random
170
+ # number):
171
+ post "/users/sign_up", {
172
+ payload: {
173
+ email: user.email,
174
+ password: user.password,
175
+ ref_token: pick(:referrer_token),
176
+ phone: "+4917#{pick(:phone)}"
177
+ }
178
+ }
179
+
180
+ wait 1.minute
181
+
182
+ # Notice, that you can use `user`multiple times.
183
+ post "/users/login", payload: { email: user.email, password: user.password }
184
+ end
185
+
186
+ session "upload something", 5.percent do
187
+ # FIXME the API for all HTTP requests is quite sub optimal right now. The 'payload_from_file' is
188
+ # particular ugly. How could a saner API look like for options like this?
189
+ # FIXME 'payload_from_file' cannot accept a dynamic value right now
190
+ put "/upload/my-file", {
191
+ payload_from_file: :large_file_1mb
192
+ }
193
+ end
194
+
195
+ # The follow session demos how to make GET requests
196
+ session "get ads", 70.percent do
197
+ get "#{pick(:ad_request)}/#{pick(:campaign_id)}",
198
+ gzip: true,
199
+ cookies: {
200
+ "user_uid" => pick(:user_uid)
201
+ },
202
+ headers: {
203
+ "X-Forwared-For" => "1.2.3.4"
204
+ }
205
+ end
206
+ end