rest-assured 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.gitignore +7 -0
  2. data/Gemfile +17 -0
  3. data/LICENSE +22 -0
  4. data/README.markdown +78 -0
  5. data/Rakefile +4 -0
  6. data/bin/console +12 -0
  7. data/bin/rest-assured +28 -0
  8. data/cucumber.yml +3 -0
  9. data/db/migrate/20110620161740_add_fixtures.rb +12 -0
  10. data/db/migrate/20110625155332_add_redirects_table.rb +12 -0
  11. data/db/migrate/20110709150645_add_description_to_fixtures.rb +9 -0
  12. data/db/migrate/20110807222522_add_active_bit_to_fixtures.rb +9 -0
  13. data/db/migrate/20110818155555_add_position_to_redirects.rb +9 -0
  14. data/db/migrate/20110823132023_add_method_to_fixtures.rb +9 -0
  15. data/db/migrate/20110912162948_rename_url_to_fullpath.rb +9 -0
  16. data/db/migrate/20110912163705_rename_fixtures_to_doubles.rb +9 -0
  17. data/features/doubles_via_api.feature +50 -0
  18. data/features/doubles_via_ui.feature +66 -0
  19. data/features/persistence.feature +24 -0
  20. data/features/redirect_rules_via_api.feature +29 -0
  21. data/features/redirect_rules_via_ui.feature +68 -0
  22. data/features/step_definitions/doubles_steps.rb +143 -0
  23. data/features/step_definitions/persistence_steps.rb +13 -0
  24. data/features/step_definitions/redirect_rules_steps.rb +64 -0
  25. data/features/step_definitions/support/numeric_transforms.rb +3 -0
  26. data/features/support/env.rb +71 -0
  27. data/lib/rest-assured.rb +66 -0
  28. data/lib/rest-assured/config.rb +11 -0
  29. data/lib/rest-assured/init.rb +12 -0
  30. data/lib/rest-assured/models/double.rb +30 -0
  31. data/lib/rest-assured/models/redirect.rb +37 -0
  32. data/lib/rest-assured/routes/double.rb +77 -0
  33. data/lib/rest-assured/routes/redirect.rb +71 -0
  34. data/lib/rest-assured/version.rb +3 -0
  35. data/lib/sinatra/partials.rb +18 -0
  36. data/public/css/grid.inuit.css +76 -0
  37. data/public/css/inuit.css +904 -0
  38. data/public/css/jquery.jgrowl.css +132 -0
  39. data/public/css/style.css +88 -0
  40. data/public/img/css/12-grid-720.png +0 -0
  41. data/public/img/css/12-grid.png +0 -0
  42. data/public/img/css/baseline.gif +0 -0
  43. data/public/img/css/grid-720.png +0 -0
  44. data/public/img/css/grid.png +0 -0
  45. data/public/img/css/icons/error.png +0 -0
  46. data/public/img/css/icons/info.png +0 -0
  47. data/public/img/css/icons/success.png +0 -0
  48. data/public/img/css/icons/warning.png +0 -0
  49. data/public/javascript/application.js +12 -0
  50. data/public/javascript/jquery.jgrowl_minimized.js +11 -0
  51. data/rest-assured.gemspec +36 -0
  52. data/spec/functional/double_routes_spec.rb +117 -0
  53. data/spec/functional/redirect_routes_spec.rb +108 -0
  54. data/spec/models/double_spec.rb +73 -0
  55. data/spec/models/redirect_spec.rb +38 -0
  56. data/spec/spec_helper.rb +47 -0
  57. data/views/base.scss +11 -0
  58. data/views/doubles/_form.haml +16 -0
  59. data/views/doubles/edit.haml +4 -0
  60. data/views/doubles/index.haml +41 -0
  61. data/views/doubles/new.haml +3 -0
  62. data/views/layout.haml +25 -0
  63. data/views/redirects/_form.haml +11 -0
  64. data/views/redirects/edit.haml +4 -0
  65. data/views/redirects/index.haml +33 -0
  66. data/views/redirects/new.haml +3 -0
  67. metadata +250 -0
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ bundle_bin
6
+ *.db
7
+ .sass-cache
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rest-assured.gemspec
4
+ gemspec
5
+
6
+ gem 'cucumber'
7
+ gem 'database_cleaner'
8
+ gem 'rspec'
9
+ gem 'shoulda-matchers'
10
+ gem 'rack-test'
11
+ gem 'capybara'
12
+ gem 'capybara-firebug'
13
+ gem RUBY_VERSION =~ /^1\.8/ ? 'ruby-debug' : 'ruby-debug19'
14
+ gem 'awesome_print'
15
+ gem 'interactive_editor'
16
+ gem 'launchy'
17
+ #gem 'akephalos', git: 'https://github.com/Nerian/akephalos.git'
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011, Artem Avetisyan, British Broadcasting Corporation
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,78 @@
1
+ # REST assured
2
+
3
+ ## Overview
4
+
5
+ A tool for stubbing/mocking external http based services that app under test is talking to. This is useful for blackbox testing or in cases where it is not possible to access application objects directly from test code.
6
+ There are three main use cases:
7
+
8
+ * stubbing out external data sources with predefined data, so that test code has known data to assert against
9
+ * setting expectations on messages to external services (currently not yet implemented)
10
+ * mimic different responses from external services during development. For that purpose there is web UI
11
+
12
+ ## Usage
13
+
14
+ You are going to need ruby >= 1.8.7. Install gem and run:
15
+
16
+ sudo gem install rest-assured # omit sudo if using rvm
17
+ rest-assured &
18
+
19
+ Or clone from github and run:
20
+
21
+ git clone git@github.com:BBC/rest-assured.git
22
+ ./rest-assured/bin/rest-assured &
23
+
24
+ This starts an instance of rest-assured on port 4578 (changable with --port option) and creates rest-assured.db (changable with --database option) in the current directory. You can now access it via REST or web interfaces on http://localhost:4578
25
+
26
+ ### Doubles
27
+
28
+ Double is a stub/mock of a particular external call. There is the following rest API for setting up doubles:
29
+
30
+ * `POST '/doubles', { request_fullpath: path, content: content, method: method }`
31
+ Creates double with the following parameters:
32
+
33
+ - __request_fullpath__ - e.g., `/some/api/object`, or with parameters in query string (useful for doubling GETs) - `/some/other/api/object?a=2&b=c`. Mandatory.
34
+ - __content__ - whatever you want this double to respond with. Mandatory.
35
+ - __method__ - one of http the following http verbs: GET, POST, PUT, DELETE. Optional. GET is default.
36
+
37
+ Example (using ruby RestClient):
38
+
39
+ RestClient.post 'http://localhost:4578:/doubles', { request_fullpath: '/api/v2/products?type=fresh', method: 'GET', content: 'this is list of products' }
40
+
41
+ Now GETting http://localhost:4578/api/v2/products?type=fresh (in browser for instance) should return "this is list of products".
42
+
43
+ If there is more than one double for the same request\_fullpath and method, the last created one gets served. In UI you can manually control which double is 'active' (gets served).
44
+
45
+ * `DELETE '/doubles/all'`
46
+ Deletes all doubles.
47
+
48
+ ### Redirects
49
+
50
+ It is sometimes desirable to only double certain calls while letting others through to the 'real' services. Meet Redirects. Kind of "rewrite rules" for requests that didn't match any double. Here is the resp API for managing redirects:
51
+
52
+ * `POST '/redirects', { pattern: pattern, to: uri }` Creates redirect with the following parameters:
53
+
54
+ - __pattern__ - regex (perl5 style) tested against request fullpath. Mandatory
55
+ - __to__ - url base e.g., `https://myserver:8787/api`. Mandatory
56
+
57
+ Example (using ruby RestClient):
58
+
59
+ RestClient.post 'http://localhost:4578/redirects', { pattern: '^/auth', to: 'https://myserver.com/api' }
60
+
61
+ Now request (any method) to http://localhost:4578/auth/services/1 will get redirected to https://myserver.com/api/auth/services/1. Provided of course there is no double matched for that fullpath and method.
62
+ Much like rewrite rules, redirects are evaluated in order (of creation). In UI you can manually rearrange the order.
63
+
64
+ ### Storage
65
+
66
+ By default when you start rest-assured it creates (unless already exists) sqlite database and stores it into file in the current directory. This is good for using it for development - when you want doubles/redirects to persist across restarts - but may not be so desirable for using with tests, where you want each test run to start from blank slate. For that reason, you can specify `--database :memory:` so that database is kept in memory.
67
+
68
+ ## TODO
69
+
70
+ * Implement expectations
71
+ * Support headers (extends previous point)
72
+ * Ruby client library
73
+ * Support methods in UI (at the moment it is always GET)
74
+ * Don't allow to double internal routes. Just in case
75
+
76
+ ## Author
77
+
78
+ [Artem Avetisyan](https://github.com/artemave)
@@ -0,0 +1,4 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'sinatra/activerecord/rake'
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ require "irb"
3
+ require 'bundler/setup'
4
+
5
+ $:.push File.expand_path('../../lib', __FILE__)
6
+
7
+ require 'rest-assured/config'
8
+ AppConfig[:database] = ENV['FRS_DB'] || File.expand_path('../../db/development.db', __FILE__)
9
+
10
+ require 'rest-assured'
11
+
12
+ IRB.start(__FILE__)
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.push File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'rubygems'
6
+ require 'optparse'
7
+ require 'rest-assured/config'
8
+
9
+ OptionParser.new do |opts|
10
+ opts.banner = "Usage: rest-assured [options]"
11
+
12
+ opts.on('-d', '--database FILENAME', "Path to database file. Defaults to ./rest-assured.db. There is a special value ':memory:' for in memory database.") do |fname|
13
+ AppConfig[:database] = fname
14
+ end
15
+
16
+ opts.on('-p', '--port PORT', Integer, "Server port. Defaults to 4578") do |port|
17
+ AppConfig[:port] = port
18
+ end
19
+
20
+ opts.on_tail("-h", "--help", "Show this message") do
21
+ puts opts
22
+ exit
23
+ end
24
+ end.parse!
25
+
26
+ require 'rest-assured'
27
+
28
+ RestAssured::Application.run!
@@ -0,0 +1,3 @@
1
+ default: --tags ~@long
2
+ long: --tags @long
3
+ wip: --tags @wip
@@ -0,0 +1,12 @@
1
+ class AddFixtures < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :fixtures do |t|
4
+ t.string :url
5
+ t.text :content
6
+ end
7
+ end
8
+
9
+ def self.down
10
+ drop_table :fixtures
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ class AddRedirectsTable < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :redirects do |t|
4
+ t.string :pattern
5
+ t.string :to
6
+ end
7
+ end
8
+
9
+ def self.down
10
+ drop_table :redirects
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ class AddDescriptionToFixtures < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :fixtures, :description, :text
4
+ end
5
+
6
+ def self.down
7
+ remove_column :fixtures, :description
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class AddActiveBitToFixtures < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :fixtures, :active, :boolean, :default => true
4
+ end
5
+
6
+ def self.down
7
+ remove_column :fixtures, :active
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class AddPositionToRedirects < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :redirects, :position, :integer
4
+ end
5
+
6
+ def self.down
7
+ remove_column :redirects, :position
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class AddMethodToFixtures < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :fixtures, :method, :string
4
+ end
5
+
6
+ def self.down
7
+ remove_column :fixtures, :method
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class RenameUrlToFullpath < ActiveRecord::Migration
2
+ def self.up
3
+ rename_column :fixtures, :url, :fullpath
4
+ end
5
+
6
+ def self.down
7
+ rename_column :fixtures, :fullpath, :url
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class RenameFixturesToDoubles < ActiveRecord::Migration
2
+ def self.up
3
+ rename_table :fixtures, :doubles
4
+ end
5
+
6
+ def self.down
7
+ rename_table :doubles, :fixtures
8
+ end
9
+ end
@@ -0,0 +1,50 @@
1
+ Feature: use doubles via api
2
+ In order to use double data in integration tests
3
+ As a developer
4
+ I want to mock rest services my app is consuming from
5
+
6
+ Scenario Outline: create double
7
+ When I create a double with "<fullpath>" as fullpath, "<content>" as response content and "<method>" as request method
8
+ Then there should be 1 double with "<fullpath>" as fullpath, "<content>" as response content and "<result_method>" as request method
9
+
10
+ Examples:
11
+ | fullpath | content | method | result_method |
12
+ | /api/something | created | POST | POST |
13
+ | /api/sss | changed | PUT | PUT |
14
+ | /api/asdfsf | removed | DELETE | DELETE |
15
+ | /api/some | text content | GET | GET |
16
+ | /api/some?a=3&b=dd | more content | | GET |
17
+
18
+ Scenario Outline: request fullpath that matches double
19
+ Given there is double with "<fullpath>" as fullpath, "<content>" as response content and "<method>" as request method
20
+ When I "<method>" "<fullpath>"
21
+ Then I should get "<content>" in response content
22
+
23
+ Examples:
24
+ | fullpath | content | method |
25
+ | /api/something | created | POST |
26
+ | /api/sss | changed | PUT |
27
+ | /api/asdfsf | removed | DELETE |
28
+ | /api/some?a=3&b=dd | more content | GET |
29
+
30
+ # current rule: last added double gets picked
31
+ Scenario Outline: request fullpath that matches multiple doubles
32
+ Given there is double with "<fullpath>" as fullpath and "<content>" as response content
33
+ And there is double with "<fullpath>" as fullpath and "<content2>" as response content
34
+ When I "GET" "<fullpath>"
35
+ Then I should get "<content2>" in response content
36
+
37
+ Examples:
38
+ | fullpath | content | content2 |
39
+ | /api/something | test content | another content |
40
+ | /api/some?a=3&b=dd | more content | some text |
41
+
42
+ Scenario: request fullpath that does not match any double
43
+ Given there are no doubles
44
+ When I "GET" "/api/something"
45
+ Then I should get 404 in response status
46
+
47
+ Scenario: clear doubles
48
+ Given there are some doubles
49
+ When I delete all doubles
50
+ Then there should be no doubles
@@ -0,0 +1,66 @@
1
+ @ui
2
+ Feature: manage doubles via ui
3
+ In order to use double data in development
4
+ As a developer
5
+ I want to have a manual interface for managing doubles
6
+
7
+ Scenario: view existing doubles
8
+ Given the following doubles exist:
9
+ | fullpath | description | content |
10
+ | /url1/aaa | twitter | test content |
11
+ | /url2/bbb | geo location | more content |
12
+ | /u/b?c=1 | wikipedia | article |
13
+ When I visit "doubles" page
14
+ Then I should see that I am on "doubles" page
15
+ And I should see existing doubles:
16
+ | fullpath | description |
17
+ | /url1/aaa | twitter |
18
+ | /url2/bbb | geo location |
19
+ | /u/b?c=1 | wikipedia |
20
+
21
+ Scenario: add new double
22
+ Given I am on "doubles" page
23
+ When I choose to create a double
24
+ And I enter double details:
25
+ | fullpath | description | content |
26
+ | /url2/bb?a=b5 | google api | test content |
27
+ And I save it
28
+ Then I should see "Double created"
29
+ And I should see existing doubles:
30
+ | fullpath | description |
31
+ | /url2/bb?a=b5 | google api |
32
+
33
+ @javascript
34
+ Scenario: choose active double
35
+ Given there are two doubles for the same fullpath
36
+ When I visit "doubles" page
37
+ And I make first double active
38
+ Then first double should be served
39
+ When I make second double active
40
+ Then second double should be served
41
+
42
+ Scenario: edit double
43
+ Given the following doubles exist:
44
+ | fullpath | description | content |
45
+ | /url1/aaa | twitter | test content |
46
+ And I visit "doubles" page
47
+ And I choose to edit double
48
+ When I change "double" "description" to "google"
49
+ And I save it
50
+ Then I should see that I am on "doubles" page
51
+ And I should see existing doubles:
52
+ | fullpath | description |
53
+ | /url1/aaa | google |
54
+
55
+ @javascript
56
+ Scenario: delete double
57
+ Given the following doubles exist:
58
+ | fullpath | description | content |
59
+ | /url1/aaa | twitter | test content |
60
+ | /url/cc/bb | google | other content |
61
+ And I visit "doubles" page
62
+ And I choose to delete double with fullpath "/url1/aaa"
63
+ Then I should be asked to confirm delete
64
+ And I should see "Double deleted"
65
+ And I should not see "/url1/aaa"
66
+ And I should see "/url/cc/bb"
@@ -0,0 +1,24 @@
1
+ Feature: Persistence
2
+ In order to persist fixtrures/redirects between service restarts
3
+ As a developer
4
+ I want to be able to specify persistent storage
5
+
6
+ Scenario: default storage
7
+ Given I start service without --database option
8
+ And I register "/api/something" as fullpath and "content" as response content
9
+ And I restart service without --database option
10
+ When I request "/api/something"
11
+ Then I should get 404 in response status
12
+
13
+ Scenario Outline: specify storage
14
+ Given I start service with --database "<db>" option
15
+ And I register "/api/something" as fullpath and "content" as response content
16
+ And I restart service with --database "<db2>" option
17
+ When I request "/api/something"
18
+ Then I should get <status> in response status
19
+
20
+ Examples:
21
+ | db | db2 | status |
22
+ | database.db | database.db | 200 |
23
+ | /tmp/database.db | /tmp/database.db | 200 |
24
+ | database.db | database2.db | 404 |
@@ -0,0 +1,29 @@
1
+ Feature: manage redirect rules
2
+ In order to be able to mock only part of api
3
+ As a developer
4
+ I want to redirect to real api if there are no doubles for requested fullpath
5
+
6
+ Background:
7
+ Given there are no redirect rules
8
+ And there are no doubles
9
+
10
+ Scenario: no redirect rules
11
+ When I request "/api/something"
12
+ Then I should get 404
13
+
14
+ Scenario: add redirect rule
15
+ When I register redirect with pattern "^/api" and uri "http://real.api.co.uk"
16
+ And I request "/api/something"
17
+ Then it should redirect to "http://real.api.co.uk/api/something"
18
+
19
+ Scenario: add second redirect that match the same request
20
+ When I register redirect with pattern "/api/something" and uri "http://real.api.co.uk"
21
+ And I register redirect with pattern "/api/some.*" and uri "http://real.com"
22
+ And I request "/api/something"
23
+ Then it should redirect to "http://real.api.co.uk/api/something"
24
+
25
+ Scenario: add second redirect that does not match the same request
26
+ When I register redirect with pattern "/api/something" and uri "http://real.api.co.uk"
27
+ And I register redirect with pattern "/api/some" and uri "http://real.com"
28
+ And I request "/api/someth"
29
+ Then it should redirect to "http://real.com/api/someth"