swgr2rb 1.0.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 (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