defra_ruby_mocks 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +8 -0
  3. data/README.md +149 -0
  4. data/Rakefile +38 -0
  5. data/app/controllers/defra_ruby_mocks/application_controller.rb +17 -0
  6. data/app/controllers/defra_ruby_mocks/company_controller.rb +23 -0
  7. data/app/services/defra_ruby_mocks/base_service.rb +9 -0
  8. data/app/services/defra_ruby_mocks/companies_house_service.rb +54 -0
  9. data/app/views/defra_ruby_mocks/company/not_found.json.erb +8 -0
  10. data/app/views/defra_ruby_mocks/company/show.json.erb +3 -0
  11. data/config/routes.rb +8 -0
  12. data/lib/defra_ruby_mocks.rb +26 -0
  13. data/lib/defra_ruby_mocks/configuration.rb +39 -0
  14. data/lib/defra_ruby_mocks/engine.rb +13 -0
  15. data/lib/defra_ruby_mocks/version.rb +5 -0
  16. data/lib/tasks/changelog.rake +8 -0
  17. data/lib/tasks/defra_ruby_mocks_tasks.rake +8 -0
  18. data/spec/defra_ruby_mocks_spec.rb +44 -0
  19. data/spec/dummy/README.rdoc +28 -0
  20. data/spec/dummy/Rakefile +6 -0
  21. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  22. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  23. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  24. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  25. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  26. data/spec/dummy/bin/bundle +3 -0
  27. data/spec/dummy/bin/rails +4 -0
  28. data/spec/dummy/bin/rake +4 -0
  29. data/spec/dummy/bin/setup +29 -0
  30. data/spec/dummy/config.ru +4 -0
  31. data/spec/dummy/config/application.rb +27 -0
  32. data/spec/dummy/config/boot.rb +5 -0
  33. data/spec/dummy/config/environment.rb +5 -0
  34. data/spec/dummy/config/environments/development.rb +38 -0
  35. data/spec/dummy/config/environments/production.rb +76 -0
  36. data/spec/dummy/config/environments/test.rb +42 -0
  37. data/spec/dummy/config/initializers/assets.rb +11 -0
  38. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  39. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  40. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  41. data/spec/dummy/config/initializers/inflections.rb +16 -0
  42. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  43. data/spec/dummy/config/initializers/session_store.rb +3 -0
  44. data/spec/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
  45. data/spec/dummy/config/initializers/wrap_parameters.rb +9 -0
  46. data/spec/dummy/config/locales/en.yml +23 -0
  47. data/spec/dummy/config/routes.rb +4 -0
  48. data/spec/dummy/config/secrets.yml +22 -0
  49. data/spec/dummy/log/development.log +0 -0
  50. data/spec/dummy/log/test.log +147 -0
  51. data/spec/dummy/public/404.html +67 -0
  52. data/spec/dummy/public/422.html +67 -0
  53. data/spec/dummy/public/500.html +66 -0
  54. data/spec/dummy/public/favicon.ico +0 -0
  55. data/spec/examples.txt +30 -0
  56. data/spec/lib/configuration_spec.rb +75 -0
  57. data/spec/rails_helper.rb +46 -0
  58. data/spec/requests/company_spec.rb +80 -0
  59. data/spec/services/companies_house_service_spec.rb +47 -0
  60. data/spec/spec_helper.rb +82 -0
  61. data/spec/support/helpers/configuration.rb +16 -0
  62. data/spec/support/pry.rb +7 -0
  63. data/spec/support/simplecov.rb +17 -0
  64. metadata +237 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a51d8ad9a6aacfc79ec4354df4b6ac22a95e4d3e
4
+ data.tar.gz: f97be7da287ba2987204dea1cc6ae72b188aa454
5
+ SHA512:
6
+ metadata.gz: 4251aedd24958f9cb6bac0f0fc6378f741f8fbf798e87f07e4cce3379c2c276bbd5ce28ec6a89b8d8cef378eb8a9182679c4f53d823763df38c0d7b49ed45b87
7
+ data.tar.gz: d9ea74e870ab67ad6cca27b328a41ac7ddb0e132f435c718a596b91f1f0f9161067364497fc43b546b2fbff94c8a59a25a597976d65385d9fb3e166d4b7a1e07
data/LICENSE ADDED
@@ -0,0 +1,8 @@
1
+ The Open Government Licence (OGL) Version 3
2
+
3
+ Copyright (c) 2019 Environment Agency
4
+
5
+ This source code is licensed under the Open Government Licence v3.0. To view this
6
+ licence, visit www.nationalarchives.gov.uk/doc/open-government-licence/version/3
7
+ or write to the Information Policy Team, The National Archives, Kew, Richmond,
8
+ Surrey, TW9 4DU.
@@ -0,0 +1,149 @@
1
+ # Defra Ruby Mocks
2
+
3
+ [![Build Status](https://travis-ci.com/DEFRA/defra-ruby-mocks.svg?branch=master)](https://travis-ci.com/DEFRA/defra-ruby-mocks)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/8b14cc1e0e1c1d6a33cc/maintainability)](https://codeclimate.com/github/DEFRA/defra-ruby-mocks/maintainability)
5
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/8b14cc1e0e1c1d6a33cc/test_coverage)](https://codeclimate.com/github/DEFRA/defra-ruby-mocks/test_coverage)
6
+ [![security](https://hakiri.io/github/DEFRA/defra-ruby-mocks/master.svg)](https://hakiri.io/github/DEFRA/defra-ruby-mocks/master)
7
+ [![Licence](https://img.shields.io/badge/Licence-OGLv3-blue.svg)](http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3)
8
+
9
+ A Rails Engine used by the [Ruby services team](https://github.com/DEFRA/ruby-services-team) in their digital services.
10
+
11
+ We use it to mock external services such as Companies House, so we can performance test our services and not get in trouble!
12
+
13
+ When mounted in an app, it will add additional endpoints which when called will 'mock' the functionality of the external service.
14
+
15
+ Things to note
16
+
17
+ - We have gone with an engine rather than an additional service, to simplify management of mocking in our environments
18
+ - The mocks do not replicate all functionality an external service provides, only the features we use in our services
19
+
20
+ ## Prerequisites
21
+
22
+ Make sure you already have:
23
+
24
+ - Ruby 2.4.2
25
+ - [Bundler](http://bundler.io/) – for installing Ruby gems
26
+
27
+ ## Mounting the engine
28
+
29
+ Add the engine to your Gemfile:
30
+
31
+ ```ruby
32
+ gem "defra_ruby_mocks",
33
+ git: "https://github.com/DEFRA/defra-ruby-mocks"
34
+ ```
35
+
36
+ Install it with `bundle install`.
37
+
38
+ Then mount the engine in your routes.rb file:
39
+
40
+ ```ruby
41
+ Rails.application.routes.draw do
42
+ mount DefraRubyMocks::Engine => "/mocks"
43
+ end
44
+ ```
45
+
46
+ The engine should now be mounted at `/mocks` of your project. You can change `"/mocks"` to a different route if you'd prefer it to be elsewhere.
47
+
48
+ ## Configuration
49
+
50
+ For the mock routes to be accessible you'll also need to enable them.
51
+
52
+ ```ruby
53
+ # config/initializers/defra_ruby_mocks.rb
54
+ require "defra_ruby_mocks"
55
+
56
+ DefraRubyMocks.configure do |config|
57
+ config.enable = true
58
+ config.delay = 1000
59
+ end
60
+ ```
61
+
62
+ To protect against having them enabled when in production, by default the engine will not allow access unless they have been enabled in the config.
63
+
64
+ We also provide an option to control how long the mock should delay before responding. The default is 1000ms (1 second).
65
+
66
+ ## Mocks
67
+
68
+ The project currently mocks the following services.
69
+
70
+ ### Companies House
71
+
72
+ When mounted into an app you can make requests to `/mocks/company/[company number]` to get a response that matches what our apps expect.
73
+
74
+ This is an important distinction to note. When our apps like the [Waste Exemptions front office](https://github.com/DEFRA/waste-exemptions-front-office) make a real request to Companies House, they get a lot more information back in the JSON reponse. However the only thing they are interested in is the value of `"company_status"`.
75
+
76
+ So rather than maintain a lot of unused JSON data, the mock just returns that bit of the JSON.
77
+
78
+ ```bash
79
+ curl http://localhost:3000/mocks/company/SC123456
80
+ {
81
+ "company_status": "active"
82
+ }
83
+ ```
84
+
85
+ #### Company numbers
86
+
87
+ As long as the request is for a valid number the mock will return the status as `"active"`. (see <https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/426891/uniformResourceIdentifiersCustomerGuide.pdf> for details of valid number formats).
88
+
89
+ The exceptions to this are the 'special' numbers listed below. Use them if you are looking for alternate responses.
90
+
91
+ - `05868270` will return `"dissolved"`
92
+ - `04270505` will return `"administration"`
93
+ - `99999999` will mock a not found result and return a 404 error
94
+ - `88888888` will return `"liquidation"`
95
+ - `77777777` will return `"receivership"`
96
+ - `66666666` will return `"converted-closed"`
97
+ - `55555555` will return `"voluntary-arrangement"`
98
+ - `44444444` will return `"insolvency-proceedings"`
99
+ - `33333333` will return `"open"`
100
+ - `22222222` will return `"closed"`
101
+
102
+ The list of possible statuses was taken from
103
+
104
+ - [Companies House API](https://developer.companieshouse.gov.uk/api/docs/company/company_number/companyProfile-resource.html)
105
+ - [Companies House API enumerations](https://github.com/companieshouse/api-enumerations/blob/master/constants.yml)
106
+
107
+ ## Installation
108
+
109
+ You don't need to do this if you're just mounting the engine without making any changes.
110
+
111
+ However, if you want to edit the engine, you'll have to install it locally.
112
+
113
+ Clone the repo and drop into the project:
114
+
115
+ ```bash
116
+ git clone https://github.com/DEFRA/defra-ruby-mocks.git && cd defra-ruby-mocks
117
+ ```
118
+
119
+ Then install the dependencies with `bundle install`.
120
+
121
+ ## Testing the engine
122
+
123
+ The engine is mounted in a dummy Rails 4 app (in /spec/dummy) so we can properly test its behaviour.
124
+
125
+ The test suite is written in RSpec.
126
+
127
+ To run all the tests, use `bundle exec rspec`.
128
+
129
+ ## Contributing to this project
130
+
131
+ If you have an idea you'd like to contribute please log an issue.
132
+
133
+ All contributions should be submitted via a pull request.
134
+
135
+ ## License
136
+
137
+ THIS INFORMATION IS LICENSED UNDER THE CONDITIONS OF THE OPEN GOVERNMENT LICENCE found at:
138
+
139
+ <http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3>
140
+
141
+ The following attribution statement MUST be cited in your products and applications when using this information.
142
+
143
+ > Contains public sector information licensed under the Open Government license v3
144
+
145
+ ### About the license
146
+
147
+ The Open Government Licence (OGL) was developed by the Controller of Her Majesty's Stationery Office (HMSO) to enable information providers in the public sector to license the use and re-use of their information under a common open licence.
148
+
149
+ It is designed to encourage use and re-use of information freely and flexibly, with only a few conditions.
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "bundler/setup"
5
+ rescue LoadError
6
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
7
+ end
8
+
9
+ require "rdoc/task"
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = "rdoc"
13
+ rdoc.title = "DefraRubyMocks"
14
+ rdoc.options << "--line-numbers"
15
+ rdoc.rdoc_files.include("README.rdoc")
16
+ rdoc.rdoc_files.include("lib/**/*.rb")
17
+ end
18
+
19
+ load "rails/tasks/statistics.rake"
20
+
21
+ Dir[File.join(File.dirname(__FILE__), "lib/tasks/**/*.rake")].each { |f| load f }
22
+
23
+ Bundler::GemHelper.install_tasks
24
+
25
+ # This is wrapped to prevent an error when rake is called in environments where
26
+ # rspec may not be available, e.g. production. As such we don't need to handle
27
+ # the error.
28
+ # rubocop:disable Lint/HandleExceptions
29
+ begin
30
+ require "rspec/core/rake_task"
31
+
32
+ RSpec::Core::RakeTask.new(:spec)
33
+
34
+ task default: :spec
35
+ rescue LoadError
36
+ # no rspec available
37
+ end
38
+ # rubocop:enable Lint/HandleExceptions
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRubyMocks
4
+ class ApplicationController < ActionController::Base
5
+ protect_from_forgery with: :exception
6
+ before_action :delay_response
7
+
8
+ private
9
+
10
+ def delay_response
11
+ # sleep() expects the time to be specified in seconds, but we allow delay
12
+ # to be specified in milliseconds. Dividing by 1000 converts milliseconds
13
+ # to seconds.
14
+ sleep(DefraRubyMocks.configuration.delay / 1000)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRubyMocks
4
+ class CompanyController < ApplicationController
5
+
6
+ before_action :set_default_response_format
7
+
8
+ def show
9
+ @status = CompaniesHouseService.run(params[:id])
10
+
11
+ respond_to :json
12
+ rescue NotFoundError
13
+ render "not_found", status: 404
14
+ end
15
+
16
+ private
17
+
18
+ def set_default_response_format
19
+ request.format = :json
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRubyMocks
4
+ class BaseService
5
+ def self.run(attrs = nil)
6
+ new.run(attrs)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRubyMocks
4
+ class NotFoundError < StandardError
5
+ end
6
+
7
+ class CompaniesHouseService < BaseService
8
+
9
+ # Examples we need to validate are
10
+ # 10997904, 09764739
11
+ # SC534714, CE000958
12
+ # IP00141R, IP27702R, SP02252R
13
+ # https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/426891/uniformResourceIdentifiersCustomerGuide.pdf
14
+ VALID_COMPANIES_HOUSE_REGISTRATION_NUMBER_REGEX = Regexp.new(
15
+ /\A(\d{8,8}$)|([a-zA-Z]{2}\d{6}$)|([a-zA-Z]{2}\d{5}[a-zA-Z]{1}$)\z/i
16
+ ).freeze
17
+
18
+ NOT_FOUND = "99999999"
19
+
20
+ def self.special_company_numbers
21
+ {
22
+ "05868270" => "dissolved",
23
+ "04270505" => "administration",
24
+ "88888888" => "liquidation",
25
+ "77777777" => "receivership",
26
+ "66666666" => "converted-closed",
27
+ "55555555" => "voluntary-arrangement",
28
+ "44444444" => "insolvency-proceedings",
29
+ "33333333" => "open",
30
+ "22222222" => "closed"
31
+ }
32
+ end
33
+
34
+ def run(company_number)
35
+ raise NotFoundError unless valid_company_number?(company_number)
36
+ raise NotFoundError if company_number == NOT_FOUND
37
+
38
+ return specials[company_number] if specials.key?(company_number)
39
+
40
+ "active"
41
+ end
42
+
43
+ private
44
+
45
+ def valid_company_number?(company_number)
46
+ company_number.match?(VALID_COMPANIES_HOUSE_REGISTRATION_NUMBER_REGEX)
47
+ end
48
+
49
+ def specials
50
+ self.class.special_company_numbers
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,8 @@
1
+ {
2
+ "errors": [
3
+ {
4
+ "type": "ch:service",
5
+ "error": "company-profile-not-found"
6
+ }
7
+ ]
8
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "company_status": "<%= @status %>"
3
+ }
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ DefraRubyMocks::Engine.routes.draw do
4
+ get "/company/:id",
5
+ to: "company#show",
6
+ as: "company",
7
+ constraints: ->(_request) { DefraRubyMocks.configuration.enabled? }
8
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "defra_ruby_mocks/engine"
4
+
5
+ module DefraRubyMocks
6
+ # Enable the ability to configure the gem from its host app, rather than
7
+ # reading directly from env vars. Derived from
8
+ # https://robots.thoughtbot.com/mygem-configure-block
9
+ class << self
10
+ attr_writer :configuration
11
+
12
+ def configuration
13
+ @configuration ||= Configuration.new
14
+ end
15
+
16
+ # Added for testing. Without we cannot test both a config object with and
17
+ # with set values in the same rspec session
18
+ def reset_configuration
19
+ @configuration = nil
20
+ end
21
+ end
22
+
23
+ def self.configure
24
+ yield(configuration)
25
+ end
26
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRubyMocks
4
+ class Configuration
5
+
6
+ DEFAULT_DELAY = 1000
7
+
8
+ attr_reader :delay
9
+
10
+ def initialize
11
+ @enable = false
12
+ @delay = DEFAULT_DELAY
13
+ end
14
+
15
+ # Controls whether the mocks are enabled. Only if set to true will the mock
16
+ # pages be accessible
17
+ def enable=(arg)
18
+ # We implement our own setter to handle values being passed in as strings
19
+ # rather than booleans
20
+ parsed = arg.to_s.downcase
21
+
22
+ @enable = parsed == "true"
23
+ end
24
+
25
+ def enabled?
26
+ @enable
27
+ end
28
+
29
+ # Set a delay in milliseconds for the mocks to respond.
30
+ # Defaults to 1000 (1 sec)
31
+ def delay=(arg)
32
+ # We implement our own setter to handle values being passed in as strings
33
+ # rather than integers
34
+ @delay = arg.to_i
35
+
36
+ @delay = DEFAULT_DELAY if @delay.zero?
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "configuration"
4
+
5
+ module DefraRubyMocks
6
+ class Engine < ::Rails::Engine
7
+ isolate_namespace DefraRubyMocks
8
+
9
+ config.generators do |g|
10
+ g.test_framework :rspec
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRubyMocks
4
+ VERSION = "1.0.0"
5
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "github_changelog_generator/task"
4
+
5
+ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
6
+ config.user = "DEFRA"
7
+ config.project = "defra-ruby-mocks"
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # namespace :defra_ruby_mocks do
4
+ # desc "Explaining what the task does"
5
+ # task :test do
6
+ # # Task goes here
7
+ # end
8
+ # end