stormforge-ruby 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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