swgr2rb 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +69 -0
  4. data/assets/Gemfile +8 -0
  5. data/assets/README.md +94 -0
  6. data/assets/configs/conductor_sender_configs.yaml +4 -0
  7. data/assets/endpoint_object_models/config_loaders/base_config.rb +28 -0
  8. data/assets/endpoint_object_models/config_loaders/conductor_config_loader.rb +18 -0
  9. data/assets/endpoint_object_models/json_validator/json_data_validator.rb +44 -0
  10. data/assets/endpoint_object_models/json_validator/json_schema_validator.rb +52 -0
  11. data/assets/endpoint_object_models/json_validator/json_validator.rb +34 -0
  12. data/assets/endpoint_object_models/loader.rb +16 -0
  13. data/assets/endpoint_object_models/object_models/base_endpoint_object_model.rb +22 -0
  14. data/assets/endpoint_object_models/object_models/base_endpoint_object_model_constants.rb +2 -0
  15. data/assets/endpoint_object_models/object_models/base_endpoint_object_model_methods.rb +82 -0
  16. data/assets/endpoint_object_models/prototypes/json_schema_data_types.rb +3 -0
  17. data/assets/endpoint_object_models/prototypes/request.rb +31 -0
  18. data/assets/features/step_definitions/base_steps.rb +56 -0
  19. data/assets/features/support/env.rb +9 -0
  20. data/assets/features/support/instance_variables.rb +9 -0
  21. data/assets/features/support/world.rb +12 -0
  22. data/assets/request_sender/conductor_sender/conductor_sender.rb +53 -0
  23. data/assets/request_sender/conductor_sender/response.rb +105 -0
  24. data/assets/request_sender/loader.rb +16 -0
  25. data/bin/swgr2rb +8 -0
  26. data/lib/cli/cli_options_parser.rb +139 -0
  27. data/lib/endpoint_class_config_generator/endpoint_class_config.rb +25 -0
  28. data/lib/endpoint_class_config_generator/endpoint_class_config_generator.rb +91 -0
  29. data/lib/endpoint_class_config_generator/json_paths_parser_methods.rb +68 -0
  30. data/lib/endpoint_class_config_generator/json_schema_definitions_parser_methods.rb +59 -0
  31. data/lib/endpoint_class_generator/endpoint_class_generator.rb +168 -0
  32. data/lib/endpoint_class_generator/endpoint_classes_generator.rb +76 -0
  33. data/lib/endpoint_class_generator/ruby_file_generator.rb +52 -0
  34. data/lib/endpoint_class_generator/ruby_file_generator_constants.rb +106 -0
  35. data/lib/endpoint_class_generator/schema_module_generator.rb +49 -0
  36. data/lib/json_fetcher/swagger_json_fetcher.rb +45 -0
  37. data/lib/prototypes/json_schema_data_types.rb +11 -0
  38. data/lib/prototypes/swgr2rb_error.rb +13 -0
  39. data/lib/request_sender/conductor_sender.rb +28 -0
  40. data/lib/request_sender/request.rb +31 -0
  41. data/lib/request_sender/response.rb +115 -0
  42. data/lib/scaffold_generator/feature_file_generator.rb +62 -0
  43. data/lib/scaffold_generator/scaffold_generator.rb +51 -0
  44. data/lib/scaffold_generator/scaffold_generator_constants.rb +19 -0
  45. data/lib/swgr2rb.rb +53 -0
  46. metadata +206 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8fff27c262279ed16947b368dcf3eb09f6460a55d2edc069cffa79512139a525
4
+ data.tar.gz: 72da74531bffacfd10b0046d97b9a1c256f2d0c418cb06c93f1e6ab4a9d7a52e
5
+ SHA512:
6
+ metadata.gz: a5dabc64221fd3d03011bd2892770fc37880d89ff855329b2d63b081728e878633e5d59809ece1004981d146dd8695393e57d40a3ea214a4676b55b2917decbf
7
+ data.tar.gz: 8616aa2474134f98c6905435b2450d91e97b19f9ddaa6409d610df3e503554efe8a7f21bd0a14f912c173fb648279fa04e511a6e9d93dee0a84a2acbb49fb098
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2020 Polytech Software
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,69 @@
1
+ # Swgr2rb
2
+
3
+ Swgr2rb (Swagger to Ruby) is a tool that generates Ruby classes for JSON schema validation
4
+ based on Swagger-generated documentation.
5
+ The tool was developed by [Polytech Software](https://polytech.software/).
6
+
7
+ ## Installation
8
+
9
+ Swgr2rb can be installed with RubyGems:
10
+
11
+ ```shell script
12
+ $ gem install swgr2rb
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ Swgr2rb can be used in two ways: to generate a new testing framework, or to update an existing one.
18
+
19
+ In both cases, `swgr2rb` has one required argument and a number of options.
20
+ The required argument must be either a URL of Swagger (e.g. `localhost:8080/swagger`)
21
+ or a path to the JSON file returned by Swagger (e.g. `docs/swagger.json`).
22
+ To read more about the options, view help:
23
+
24
+ ```shell script
25
+ $ swgr2rb --help
26
+ ```
27
+
28
+ ### Generating a new testing framework
29
+
30
+ Swgr2rb can generate a scaffold of a testing framework:
31
+
32
+ ```shell script
33
+ $ swgr2rb <swagger_url|json_file_path> --from-scratch
34
+ ```
35
+
36
+ A directory named `harness` will be created, and the scaffold will be generated inside.
37
+
38
+ The endpoint object models generated from Swagger will be located in the
39
+ `endpoint_object_models/object_model/%component_name%`
40
+ folder (component's name can be specified with the `-c/--component` option).
41
+
42
+ More about the generated testing framework's structure can be read in [its README file](./assets/README.md).
43
+
44
+ ### Updating an existing testing framework
45
+
46
+ Swgr2rb can be used to update the endpoint object models of an existing testing framework:
47
+
48
+ ```shell script
49
+ $ swgr2rb <swagger_url|json_file_path> -c <component_name>
50
+ ```
51
+
52
+ The tool will update the endpoint object model schema modules (located in
53
+ `%target_dir%/%component_name%/object_model_schemas`) according to Swagger,
54
+ and create new object model classes and schemas if there are new (previously untested)
55
+ endpoints in the documentation.
56
+
57
+ The behavior can be modified with the following command line options
58
+ (check `swgr2rb --help` to read more):
59
+
60
+ | Option | Description | Default value |
61
+ | --- | --- | --- |
62
+ | `-t/--target-dir TARGET_DIR` | The target directory for endpoint object models. | `endpoint_object_models/object_model` |
63
+ | `-c/--component COMPONENT` | The name of the component. | `component1` |
64
+ | `--[no-]update-only` | Do not create new files (endpoint models and schemas), only update the existing ones. | `false` |
65
+ | `--[no-]rewrite-schemas` | Rewrite schema modules if they already exist. | `true` |
66
+
67
+ ## License
68
+
69
+ Swgr2rb is released under the [MIT license](./LICENSE).
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'activesupport'
6
+ gem 'cucumber', '~> 3.1', '>= 3.1.2'
7
+ gem 'httparty'
8
+ gem 'humanize', '~> 2.1', '>= 2.1.1'
@@ -0,0 +1,94 @@
1
+ # Harness service
2
+ ## Overview
3
+ The harness service includes tests and endpoint object models. The service's prime use is to perform testing.
4
+
5
+ ## Project directories overview
6
+ ```
7
+ .
8
+ ├── configs
9
+ ├── endpoint_object_models
10
+ │   ├── config_loaders
11
+ │   ├── json_validator
12
+ │   ├── object_models
13
+ | | └── %component_name%
14
+ | | └── object_model_schemas
15
+ │   └── prototypes
16
+ ├── features
17
+ │   ├── component
18
+ | | └── %component_name%
19
+ │   ├── step_definitions
20
+ │   └── support
21
+ └── request_sender
22
+ └── conductor_sender
23
+ ```
24
+
25
+ The `configs` folder contains service's configuration files.
26
+
27
+ The `endpoint_object_models` folder contains all things related to the endpoint models such as the `JSON validator`
28
+ component, the `Config loader` component, and prototypes like `JSON schema data types` and `Request object`.
29
+ The endpoint models themselves are located in `object_models/%component_name%`.
30
+ The modules that contain expected JSON schemas for schema validation testing are located in
31
+ `object_models/%component_name%/object_model_schemas`.
32
+
33
+ The `features` folder contains the Cucumber tests.
34
+ All tests are located in `component/%component_name%`.
35
+
36
+ The `request_sender` folder includes the `conductor_sender`, which is used for sending requests to the application.
37
+
38
+ ## Service configuration
39
+
40
+ The service is configurable. The configuration file is in the `configs` directory.
41
+ The configuration file includes info about the location of the application.
42
+ In other words, it includes info on how to have access to the application.
43
+
44
+ ```yaml
45
+ connection:
46
+ protocol: 'https://'
47
+ host: '<HOST>'
48
+ port: '<PORT>'
49
+ ```
50
+
51
+ ## Setting up a local environment and run tests locally
52
+
53
+ The Harness service was written on Ruby and that is why it is required to install RVM (Ruby Version Master)
54
+ and ruby 2.6.5 as well.
55
+
56
+ To install RVM complete the following steps:
57
+
58
+ * Add the GPG key to your system by performing the following command:
59
+
60
+ ```bash
61
+ gpg2 --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
62
+ ```
63
+
64
+ * Download and install RVM by performing the following command:
65
+
66
+ ```bash
67
+ curl -sSL https://get.rvm.io | bash -s stable
68
+ ```
69
+
70
+ * To install ruby perform the following command:
71
+
72
+ ```bash
73
+ rvm install ruby-2.6.5
74
+ rvm --default use ruby-2.6.5
75
+ ```
76
+
77
+ The Harness service uses different libs, that's why to perform tests locally it is required to install them as well.
78
+ To do this perform the following command in the project folder:
79
+
80
+ ```bash
81
+ bundle install
82
+ ```
83
+
84
+ To execute tests perform one of the following commands:
85
+
86
+ ```bash
87
+ cucumber
88
+ ```
89
+
90
+ To execute a specific test perform the following command:
91
+
92
+ ```bash
93
+ cucumber --tags '<testcase_tag>' --tags '<component_tag>'
94
+ ```
@@ -0,0 +1,4 @@
1
+ connection:
2
+ protocol: 'https://'
3
+ host: 'localhost'
4
+ port: '8080'
@@ -0,0 +1,28 @@
1
+ class BaseConfig
2
+ attr_accessor :config_file
3
+ def initialize(config_file)
4
+ @config_file = config_file
5
+ create_attribute_accessors
6
+ self
7
+ end
8
+
9
+ private
10
+
11
+ def create_attribute_accessors
12
+ config_file.each do |k, v|
13
+ create_attribute_reader(k, v)
14
+ end
15
+ end
16
+
17
+ def create_attribute_reader(key, value)
18
+ case value
19
+ when Hash
20
+ instance_variable_set("@#{key}", BaseConfig.new(value))
21
+ else
22
+ instance_variable_set("@#{key}", value)
23
+ end
24
+ self.class.send(:define_method, key) do
25
+ instance_variable_get("@#{key}")
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_config'
4
+
5
+ class ConductorConfigLoader
6
+ class << self
7
+ def load
8
+ current_dir = __FILE__.scan(%r{(.*/harness/)(.*)}m).flatten.first
9
+ file = YAML.load_file(File.join(current_dir,
10
+ '/configs',
11
+ 'conductor_sender_configs.yaml'))
12
+ ConductorConfig.new(file)
13
+ end
14
+ end
15
+ end
16
+
17
+ class ConductorConfig < BaseConfig
18
+ end
@@ -0,0 +1,44 @@
1
+ class JsonDataValidator
2
+ class << self
3
+ def validate(expected_data, actual_data)
4
+ begin
5
+ validate_recursively(expected_data, actual_data)
6
+ rescue RuntimeError => e
7
+ raise "#{e.message}\n\n"\
8
+ "Full expected response:\n#{expected_data}\n\n"\
9
+ "Full actual response:\n#{actual_data}"
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def validate_recursively(expected_data, actual_data, key_name = nil)
16
+ case expected_data
17
+ when Array
18
+ validate_response_size(expected_data, actual_data, key_name)
19
+ expected_data.zip(actual_data).each do |expected, actual|
20
+ validate_recursively(expected, actual)
21
+ end
22
+ when Hash
23
+ expected_data.each do |exp_key, exp_value|
24
+ validate_recursively(exp_value, actual_data[exp_key], exp_key)
25
+ end
26
+ else
27
+ unless expected_data.to_s == actual_data.to_s ||
28
+ (expected_data.is_a?(Regexp) && expected_data.match?(actual_data.to_s))
29
+ raise "Unexpected value in response body#{key_name ? " for key '#{key_name}'" : ''}\n"\
30
+ "Expected: #{expected_data}\n"\
31
+ "Actual: #{actual_data}"
32
+ end
33
+ end
34
+ end
35
+
36
+ def validate_response_size(expected_data, actual_data, key_name = nil)
37
+ unless expected_data.size == actual_data.size
38
+ raise "Unexpected number of objects in response body#{key_name ? " for key '#{key_name}'" : ''}\n"\
39
+ "Expected: #{expected_data.size}\n"\
40
+ "Actual: #{actual_data.size}"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../endpoint_object_models/prototypes/json_schema_data_types'
4
+
5
+ class JsonSchemaValidator
6
+ class << self
7
+ def validate(expected_schema, actual_schema)
8
+ unless actual_schema.is_a? Hash
9
+ compare_types(expected_schema, actual_schema, '')
10
+ return
11
+ end
12
+ validate_keys(expected_schema.keys, actual_schema.keys)
13
+ expected_schema.each do |key, type|
14
+ case type
15
+ when Hash
16
+ validate(expected_schema[key], actual_schema[key])
17
+ when Array
18
+ actual_schema[key].each do |e|
19
+ validate(expected_schema[key][0], e)
20
+ end
21
+ else
22
+ compare_types(type, actual_schema[key], key)
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def compare_types(expected_type, actual_value, key)
30
+ unless actual_value.is_a?(expected_type) || actual_value.is_a?(NilClass)
31
+ raise "Schema do not match to the expected schema\n"\
32
+ "key: #{key}\n"\
33
+ "expected type: #{expected_type}\n"\
34
+ "actual type: #{actual_value.class}"
35
+ end
36
+ end
37
+
38
+ def validate_keys(expected_keys, actual_keys)
39
+ expected_keys.sort!
40
+ actual_keys.sort!
41
+ unless expected_keys == actual_keys
42
+ raise "Schema do not match to the expected schema \n\n"\
43
+ "expected schema includes the following keys: \n"\
44
+ "#{expected_keys.join("\n")}\n\n"\
45
+ "actual schema includes the following keys: \n"\
46
+ "#{actual_keys.join("\n")}\n\n"\
47
+ "missing keys : #{(expected_keys - actual_keys).join(', ')}\n"\
48
+ "extra keys : #{(actual_keys - expected_keys).join(', ')}\n"
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,34 @@
1
+ class JsonValidator
2
+ class << self
3
+ def validate(expected_schema, actual_data, expected_data = nil)
4
+ preprocess_actual_data(actual_data).each do |data|
5
+ JsonSchemaValidator.validate(expected_schema, data)
6
+ end
7
+ if expected_data
8
+ expected = preprocess_expected_data(expected_schema, expected_data)
9
+ JsonDataValidator.validate(expected, actual_data)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def preprocess_actual_data(actual_data)
16
+ actual_data.is_a?(Array) ? actual_data : [actual_data]
17
+ end
18
+
19
+ def preprocess_expected_data(expected_schema, expected_data)
20
+ case expected_data
21
+ when Array
22
+ expected_data.map { |data_hash| filter_expected_data_hash(expected_schema, data_hash) }
23
+ when Hash
24
+ filter_expected_data_hash(expected_schema, expected_data)
25
+ else
26
+ expected_data
27
+ end
28
+ end
29
+
30
+ def filter_expected_data_hash(expected_schema, expected_data)
31
+ expected_data.select { |key, _value| expected_schema.key?(key) }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ puts "Loading Endpoint' object models component"
4
+
5
+ def recursive_require(dir)
6
+ Dir["#{File.dirname(__FILE__)}/#{dir}/**/*.rb"].each do |f|
7
+ require(f)
8
+ end
9
+ end
10
+
11
+ dirs = Dir.entries(File.dirname(__FILE__)).select { |entry| File.directory? File.join(File.dirname(__FILE__), entry) and !(entry == '.' || entry == '..') }
12
+ dirs.each do |dir|
13
+ recursive_require(dir)
14
+ end
15
+
16
+ puts "Done loading Endpoint' object models component"
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class BaseEndpointObjectModel
4
+ attr_accessor :request_options, :request, :response, :end_point_path,
5
+ :supportable_request_types, :subdomain, :credentials
6
+
7
+ def send_request(_type, _params)
8
+ raise 'This is abstract class'
9
+ end
10
+
11
+ def validate_response_schema
12
+ raise 'This is abstract class'
13
+ end
14
+
15
+ def validate_error_response(_error_code)
16
+ raise 'This is abstract class'
17
+ end
18
+
19
+ def results
20
+ raise 'This is abstract class'
21
+ end
22
+ end
@@ -0,0 +1,2 @@
1
+ module BaseEndpointObjectModelConstants
2
+ end
@@ -0,0 +1,82 @@
1
+ require_relative 'base_endpoint_object_model_constants'
2
+
3
+ module BaseEndpointObjectModelMethods
4
+ include BaseEndpointObjectModelConstants
5
+
6
+ def send_request(type, params, sub_results = nil)
7
+ if request_type_support?(type)
8
+ @request_options = {
9
+ type: type,
10
+ params: params,
11
+ sub_results: sub_results
12
+ }
13
+ @request = Request.new(end_point_path, type,
14
+ generate_headers, generate_body)
15
+ @response = ConductorSender.send_request(request)
16
+ else
17
+ raise "Harness setup error!\n "\
18
+ "#{type} do not supports by the #{end_point_path}\n"\
19
+ 'list of the supportable types: '\
20
+ "#{supportable_request_types.join(', ')}"
21
+ end
22
+ end
23
+
24
+ def validate_error_response(error_code)
25
+ unless error_code == response.code
26
+ raise "The #{response.code} is wrong for #{self.class} endpoint\n"\
27
+ "Expected : #{error_code}\n"\
28
+ "Actual : #{response.code}"
29
+ end
30
+ send("#{response.code.to_i.humanize.gsub(' ', '_')}_error_code_schema")
31
+ end
32
+
33
+ def results
34
+ response.body
35
+ end
36
+
37
+ private
38
+
39
+ def validate_response_code
40
+ unless response.code == expected_code
41
+ raise "Invalid response code\n"\
42
+ "Expected: #{expected_code}\n"\
43
+ "Actual: #{response.code}"
44
+ end
45
+ end
46
+
47
+ def compare_error_body(expected, actual)
48
+ expected_keys = expected.keys.sort
49
+ actual_keys = actual.keys.sort
50
+
51
+ unless expected_keys == actual_keys
52
+ raise "The body for error is wrong \n"\
53
+ "Expected:\n"\
54
+ "#{expected_keys}\n"\
55
+ "Actual:\n"\
56
+ "#{actual_keys}"
57
+ end
58
+
59
+ expected.each do |k, v|
60
+ case v
61
+ when Hash
62
+ compare_error_body(expected[k], actual[k])
63
+ else
64
+ unless expected[k] == actual[k]
65
+ raise "The value of body is wrong\n"\
66
+ "Expected:\n"\
67
+ "#{k}: #{expected[k]}\n"\
68
+ "Actual:\n"\
69
+ "#{k}: #{actual[k]}"
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ def generate_headers
76
+ { 'Content-Type': 'application/json' }
77
+ end
78
+
79
+ def request_type_support?(type)
80
+ supportable_request_types.include?(type)
81
+ end
82
+ end