dragnet 5.2.1
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.
- checksums.yaml +7 -0
- data/.github/workflows/release.yml +45 -0
- data/.github/workflows/ruby-linters.yml +41 -0
- data/.github/workflows/ruby-tests.yml +38 -0
- data/.github/workflows/sphinx-doc.yml +79 -0
- data/.gitignore +13 -0
- data/.reek.yml +15 -0
- data/.rspec +3 -0
- data/.rubocop.yml +14 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +178 -0
- data/Gemfile +18 -0
- data/README.md +119 -0
- data/Rakefile +8 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/default.reek +7 -0
- data/dragnet.gemspec +39 -0
- data/exe/dragnet +9 -0
- data/lib/dragnet/base_repository.rb +69 -0
- data/lib/dragnet/cli/base.rb +72 -0
- data/lib/dragnet/cli/logger.rb +72 -0
- data/lib/dragnet/cli/master.rb +173 -0
- data/lib/dragnet/cli.rb +8 -0
- data/lib/dragnet/errors/error.rb +8 -0
- data/lib/dragnet/errors/file_not_found_error.rb +11 -0
- data/lib/dragnet/errors/incompatible_repository_error.rb +10 -0
- data/lib/dragnet/errors/missing_timestamp_attribute_error.rb +11 -0
- data/lib/dragnet/errors/no_mtr_files_found_error.rb +11 -0
- data/lib/dragnet/errors/not_a_repository_error.rb +11 -0
- data/lib/dragnet/errors/repo_path_not_found_error.rb +10 -0
- data/lib/dragnet/errors/unable_to_write_report_error.rb +11 -0
- data/lib/dragnet/errors/unknown_export_format_error.rb +11 -0
- data/lib/dragnet/errors/validation_error.rb +11 -0
- data/lib/dragnet/errors/yaml_format_error.rb +9 -0
- data/lib/dragnet/errors.rb +9 -0
- data/lib/dragnet/explorer.rb +103 -0
- data/lib/dragnet/exporter.rb +130 -0
- data/lib/dragnet/exporters/exporter.rb +29 -0
- data/lib/dragnet/exporters/html_exporter.rb +158 -0
- data/lib/dragnet/exporters/id_generator.rb +35 -0
- data/lib/dragnet/exporters/json_exporter.rb +34 -0
- data/lib/dragnet/exporters/serializers/repo_serializer.rb +40 -0
- data/lib/dragnet/exporters/serializers/test_record_serializer.rb +101 -0
- data/lib/dragnet/exporters/serializers/verification_result_serializer.rb +34 -0
- data/lib/dragnet/exporters/serializers.rb +12 -0
- data/lib/dragnet/exporters/templates/template.html.erb +518 -0
- data/lib/dragnet/exporters.rb +13 -0
- data/lib/dragnet/helpers/repository_helper.rb +27 -0
- data/lib/dragnet/multi_repository.rb +48 -0
- data/lib/dragnet/repo.rb +31 -0
- data/lib/dragnet/repository.rb +77 -0
- data/lib/dragnet/test_record.rb +91 -0
- data/lib/dragnet/validator.rb +79 -0
- data/lib/dragnet/validators/data_validator.rb +75 -0
- data/lib/dragnet/validators/entities/repo_validator.rb +31 -0
- data/lib/dragnet/validators/entities/test_record_validator.rb +93 -0
- data/lib/dragnet/validators/entities.rb +12 -0
- data/lib/dragnet/validators/fields/description_validator.rb +24 -0
- data/lib/dragnet/validators/fields/field_validator.rb +69 -0
- data/lib/dragnet/validators/fields/files_validator.rb +29 -0
- data/lib/dragnet/validators/fields/id_validator.rb +36 -0
- data/lib/dragnet/validators/fields/meta_data_field_validator.rb +36 -0
- data/lib/dragnet/validators/fields/path_validator.rb +22 -0
- data/lib/dragnet/validators/fields/repos_validator.rb +62 -0
- data/lib/dragnet/validators/fields/result_validator.rb +33 -0
- data/lib/dragnet/validators/fields/sha1_validator.rb +42 -0
- data/lib/dragnet/validators/fields.rb +17 -0
- data/lib/dragnet/validators/files_validator.rb +85 -0
- data/lib/dragnet/validators/repos_validator.rb +74 -0
- data/lib/dragnet/validators/validator.rb +20 -0
- data/lib/dragnet/validators.rb +12 -0
- data/lib/dragnet/verification_result.rb +125 -0
- data/lib/dragnet/verifier.rb +64 -0
- data/lib/dragnet/verifiers/changes_verifier.rb +70 -0
- data/lib/dragnet/verifiers/files_verifier.rb +51 -0
- data/lib/dragnet/verifiers/repos_verifier.rb +92 -0
- data/lib/dragnet/verifiers/repository_verifier.rb +27 -0
- data/lib/dragnet/verifiers/result_verifier.rb +32 -0
- data/lib/dragnet/verifiers/test_record_verifier.rb +85 -0
- data/lib/dragnet/verifiers/verifier.rb +37 -0
- data/lib/dragnet/verifiers.rb +8 -0
- data/lib/dragnet/version.rb +5 -0
- data/lib/dragnet.rb +18 -0
- metadata +190 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
require_relative 'base_repository'
|
6
|
+
|
7
|
+
module Dragnet
|
8
|
+
# A small wrapper around a Git Repository object. It provides some useful
|
9
|
+
# methods needed during the verify process as well as for reporting.
|
10
|
+
class Repository < Dragnet::BaseRepository
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
attr_reader :git
|
14
|
+
|
15
|
+
def_delegators :@git, :branch, :branches, :diff
|
16
|
+
|
17
|
+
# Creates a new instance of the class. Tries to open the given path as a Git
|
18
|
+
# repository.
|
19
|
+
# @param [Pathname] path The path where the root of the repository is located.
|
20
|
+
# @raise [ArgumentError] If the given path is not a valid git repository.
|
21
|
+
def initialize(path:)
|
22
|
+
super
|
23
|
+
@git = Git.open(path)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Git::Object::Commit] The +Commit+ object at the +HEAD+ of the
|
27
|
+
# repository.
|
28
|
+
def head
|
29
|
+
@head ||= git.object('HEAD')
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the URI path of the repository (extracted from its first remote
|
33
|
+
# [assumed to be the origin]). Example:
|
34
|
+
#
|
35
|
+
# ssh://jenkins@gerrit.int.esrlabs.com:29418/tools/dragnet -> /tools/dragnet
|
36
|
+
#
|
37
|
+
# @return [String] The URI path of the repository
|
38
|
+
def remote_uri_path
|
39
|
+
URI.parse(git.remotes.first.url).path
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [FalseClass] It always returns false
|
43
|
+
def multi?
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns an array of all the branches that include the given commit.
|
48
|
+
# @param [String] commit The SHA1 of the commit to look for.
|
49
|
+
# @return [Array<Git::Branch>] An array with all the branches that contain
|
50
|
+
# the given commit.
|
51
|
+
def branches_with(commit)
|
52
|
+
branches.select { |branch| branch.contains?(commit) }
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns an array of all the branches that include the current HEAD.
|
56
|
+
# @return [Array<Git::Branch>] An array with all the branches that contain
|
57
|
+
# the current HEAD.
|
58
|
+
def branches_with_head
|
59
|
+
@branches_with_head ||= branches_with(head.sha)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# @param [Symbol] method_name The name of the method that was invoked.
|
65
|
+
# @raise [Dragnet::Errors::IncompatibleRepositoryError] Is always raised
|
66
|
+
# with a description of the method that was invoked and a possible cause
|
67
|
+
# for the failure.
|
68
|
+
def incompatible_repository(method_name)
|
69
|
+
super(
|
70
|
+
"Failed to perform the action '#{method_name}' on '#{path}'."\
|
71
|
+
' The path was not set-up as a multi-repo path. If you are running'\
|
72
|
+
' without the --multi-repo command line switch make sure that none of'\
|
73
|
+
" your MTRs have a 'repos' attribute or run with the --multi-repo switch"
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'validators/entities/test_record_validator'
|
4
|
+
|
5
|
+
module Dragnet
|
6
|
+
# Represents a Manual Test Record loaded from a MTR file.
|
7
|
+
class TestRecord
|
8
|
+
PASSED_RESULT = 'passed'
|
9
|
+
REVIEWED_STATUS = 'reviewed'
|
10
|
+
NO_FINDINGS = 'no findings'
|
11
|
+
|
12
|
+
# :reek:Attribute (This is an entity class)
|
13
|
+
|
14
|
+
attr_accessor :id, :result, :sha1, :name, :description, :files, :repos,
|
15
|
+
:review_status, :review_comments, :findings, :test_method,
|
16
|
+
:tc_derivation_method, :source_file, :verification_result
|
17
|
+
|
18
|
+
# rubocop:disable Metrics/AbcSize (There isn't much that can be done here,
|
19
|
+
# those are the attributes an MTR has).
|
20
|
+
# :reek:FeatureEnvy (Refers to args as much as it refers to itself)
|
21
|
+
|
22
|
+
# Creates a new instance of the class.
|
23
|
+
# @param [Hash] args The data for the Manual Test Record
|
24
|
+
# @option args [String] :id The ID of the MTR
|
25
|
+
# @option args [String] :result The result of the Manual Test.
|
26
|
+
# @option args [String] :sha1 The SHA1 of the commit in which the Manual
|
27
|
+
# Test was performed.
|
28
|
+
# @option args [String, Array<String>, nil] :name The name of the person who
|
29
|
+
# performed the Manual Test.
|
30
|
+
# @option args [String, nil] :description The description of the Manual
|
31
|
+
# Test, normally which actions were performed and what it was mean to
|
32
|
+
# test.
|
33
|
+
# @option args [String, Array<String>, nil] :files The files involved in the
|
34
|
+
# MTR, these are the files which will be checked for changes when
|
35
|
+
# evaluating the validity of the MTR.
|
36
|
+
# @option args [Array<Hash>, nil] :repos An array of +Hash+es with the
|
37
|
+
# information about the repositories that are involved in the MTR, these
|
38
|
+
# repositories will be checked for changes during the evaluation of the
|
39
|
+
# MTR.
|
40
|
+
# @option args [String, nil] :review_status or :reviewstatus The review
|
41
|
+
# status of the MTR. (Normally changed when someone other than the tester
|
42
|
+
# verifies the result of the Manual Test)
|
43
|
+
# @option args [String, nil] :review_comments or :reviewcomments The
|
44
|
+
# comments left by the person who performed the review of the Manual Test.
|
45
|
+
# @option args [String, nil] :findings The findings that the reviewer
|
46
|
+
# collected during the review process (if any).
|
47
|
+
# @option args [String, Array<String>, nil] :test_method The method(s) used
|
48
|
+
# to carry out the test.
|
49
|
+
# @option args [String, Array<String>, nil] :tc_derivation_method: The
|
50
|
+
# method(s) used to derive the test case,
|
51
|
+
# @note Either +:files+ or +:repos+ should be present, not both.
|
52
|
+
def initialize(args)
|
53
|
+
@id = args[:id]
|
54
|
+
@result = args[:result]
|
55
|
+
@sha1 = args[:sha1]
|
56
|
+
@name = args[:name]
|
57
|
+
@description = args[:description]
|
58
|
+
@files = args[:files]
|
59
|
+
@repos = args[:repos]
|
60
|
+
@review_status = args[:review_status] || args[:reviewstatus]
|
61
|
+
@review_comments = args[:review_comments] || args[:reviewcomments]
|
62
|
+
@findings = args[:findings]
|
63
|
+
@test_method = args[:test_method]
|
64
|
+
@tc_derivation_method = args[:tc_derivation_method]
|
65
|
+
end
|
66
|
+
# rubocop:enable Metrics/AbcSize
|
67
|
+
|
68
|
+
# Validates the MTR's fields
|
69
|
+
# @raise [Dragnet::Errors::ValidationError] If the validation fails.
|
70
|
+
def validate
|
71
|
+
Dragnet::Validators::Entities::TestRecordValidator.new(self).validate
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [Boolean] True if the Manual Test passed, false otherwise.
|
75
|
+
def passed?
|
76
|
+
result == PASSED_RESULT
|
77
|
+
end
|
78
|
+
|
79
|
+
# @return [Boolean] True if the Manual Test Record has been reviewed, false
|
80
|
+
# otherwise.
|
81
|
+
def reviewed?
|
82
|
+
review_status == REVIEWED_STATUS
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [Boolean] True if the Manual Test Record has findings (problems
|
86
|
+
# annotated during the review), false otherwise.
|
87
|
+
def findings?
|
88
|
+
!(findings.nil? || findings.strip.empty? || findings.downcase == NO_FINDINGS)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'colorize'
|
4
|
+
|
5
|
+
require_relative 'validators/data_validator'
|
6
|
+
require_relative 'validators/files_validator'
|
7
|
+
require_relative 'validators/repos_validator'
|
8
|
+
|
9
|
+
module Dragnet
|
10
|
+
# Validates a set of Manual Test Record files. That means, checking that they
|
11
|
+
# can be read, that they are valid YAML files, that they have the expected
|
12
|
+
# keys and that these keys have sensible values.
|
13
|
+
class Validator
|
14
|
+
attr_reader :files, :path, :logger, :errors, :valid_files
|
15
|
+
|
16
|
+
# Creates a new instance of the class.
|
17
|
+
# @param [Array<Pathname>] files An array with the MTR files to validate.
|
18
|
+
# @param [Pathname] path The path where the MTR files are located.
|
19
|
+
# @param [#info, #error] logger A logger object to use for output.
|
20
|
+
def initialize(files:, path:, logger:)
|
21
|
+
@files = files
|
22
|
+
@path = path
|
23
|
+
@logger = logger
|
24
|
+
end
|
25
|
+
|
26
|
+
# Validates the given files.
|
27
|
+
# @return [Array<Dragnet::TestRecord>] An array of +TestRecord+s, one for
|
28
|
+
# each valid MTR file (invalid files will be added to the +errors+ array).
|
29
|
+
# The returned hash has the following structure:
|
30
|
+
def validate
|
31
|
+
logger.info('Validating MTR Files...')
|
32
|
+
|
33
|
+
@errors = []
|
34
|
+
@valid_files = files.map { |file| validate_file(file) }.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# Validates the given file
|
40
|
+
# @param [Pathname] file The file to be validated.
|
41
|
+
# @return [Dragnet::TestRecord, nil] A +TestRecord+ object or +nil+ if the
|
42
|
+
# file is invalid.
|
43
|
+
# rubocop:disable Metrics/AbcSize (because of logging).
|
44
|
+
def validate_file(file)
|
45
|
+
logger.info "Validating #{file}..."
|
46
|
+
data = YAML.safe_load(File.read(file))
|
47
|
+
test_record = Dragnet::Validators::DataValidator.new(data, file).validate
|
48
|
+
Dragnet::Validators::FilesValidator.new(test_record, path).validate
|
49
|
+
Dragnet::Validators::ReposValidator.new(test_record, path).validate
|
50
|
+
|
51
|
+
logger.info "#{'✔ SUCCESS'.colorize(:light_green)} #{file} Successfully loaded"
|
52
|
+
test_record
|
53
|
+
rescue SystemCallError => e
|
54
|
+
push_error(file, 'IO Error: Cannot read the specified file', e)
|
55
|
+
rescue Psych::Exception => e
|
56
|
+
push_error(file, 'YAML Parsing Error', e)
|
57
|
+
rescue Dragnet::Errors::YAMLFormatError => e
|
58
|
+
push_error(file, 'YAML Formatting Error', e)
|
59
|
+
rescue Dragnet::Errors::FileNotFoundError => e
|
60
|
+
push_error(file, 'Referenced file not found in repository', e)
|
61
|
+
rescue Dragnet::Errors::RepoPathNotFoundError => e
|
62
|
+
push_error(file, 'Referenced repository not found', e)
|
63
|
+
end
|
64
|
+
# rubocop:enable Metrics/AbcSize
|
65
|
+
|
66
|
+
# Pushes an entry into the +errors+ array.
|
67
|
+
# @param [Pathname] file The file that contains the error.
|
68
|
+
# @param [String] message A general description of the message.
|
69
|
+
# @param [Exception] exception The raised exception (through which the file
|
70
|
+
# was branded invalid)
|
71
|
+
# @return [nil] Returns nil so that +validate_file+ can return nil for
|
72
|
+
# invalid files.
|
73
|
+
def push_error(file, message, exception)
|
74
|
+
errors << { file: file, message: message, exception: exception }
|
75
|
+
logger.error "#{'✘ FAILED'.colorize(:light_red)} #{file} Failed: #{message} - #{exception.message}"
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/core_ext/hash/keys'
|
5
|
+
|
6
|
+
require_relative '../test_record'
|
7
|
+
require_relative 'validator'
|
8
|
+
|
9
|
+
module Dragnet
|
10
|
+
module Validators
|
11
|
+
# Validates the data (key-value pairs) inside an MTR file. Verifies the
|
12
|
+
# structure, the required keys and their types.
|
13
|
+
class DataValidator < Dragnet::Validators::Validator
|
14
|
+
attr_reader :data, :source_file
|
15
|
+
|
16
|
+
# Creates a new instance of the class
|
17
|
+
# @param [Hash] data The data inside the YAML (after parsing)
|
18
|
+
# @param [Pathname] source_file The path to the file from which the MTR
|
19
|
+
# data was loaded.
|
20
|
+
def initialize(data, source_file)
|
21
|
+
@data = data
|
22
|
+
@source_file = source_file
|
23
|
+
end
|
24
|
+
|
25
|
+
# Validates the given data
|
26
|
+
# @return [Dragnet::TestRecord] A +TestRecord+ object created
|
27
|
+
# from the given data (if the data was valid).
|
28
|
+
# @raise [Dragnet::Errors::YAMLFormatError] If the data is invalid. The
|
29
|
+
# raised exceptions contains a message specifying why the data is
|
30
|
+
# invalid.
|
31
|
+
def validate
|
32
|
+
yaml_format_error("Incompatible data structure. Expecting a Hash, got a #{data.class}") unless data.is_a?(Hash)
|
33
|
+
data.deep_symbolize_keys!
|
34
|
+
|
35
|
+
# A call to chomp for strings is needed because the following YAML
|
36
|
+
# syntax:
|
37
|
+
#
|
38
|
+
# findings: >
|
39
|
+
# no findings
|
40
|
+
#
|
41
|
+
# causes the string values to end with a newline ("\n"):
|
42
|
+
data.transform_values! { |value| value.is_a?(String) ? value.chomp : value }
|
43
|
+
test_record = create_mtr(data)
|
44
|
+
validate_mtr(test_record)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# @param [Hash] data A hash with the data for the +TestRecord+
|
50
|
+
# @see Dragnet::TestRecord#initialize
|
51
|
+
def create_mtr(data)
|
52
|
+
Dragnet::TestRecord.new(data).tap do |test_record|
|
53
|
+
test_record.source_file = source_file
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Creates a +Dragnet::TestRecord+ with the given data and runs its
|
58
|
+
# validation. If the validation is successful the +TestRecord+
|
59
|
+
# object is returned, if the validation fails an error is raised.
|
60
|
+
# @param [Dragnet::TestRecord] test_record The +TestRecord+ object to
|
61
|
+
# validate.
|
62
|
+
# @return [Dragnet::TestRecord] The given +TestRecord+ object if the
|
63
|
+
# validation passed.
|
64
|
+
# @raise [Dragnet::Errors::YAMLFormatError] If the data is invalid. The
|
65
|
+
# raised exceptions contains a message specifying why the data is
|
66
|
+
# invalid.
|
67
|
+
def validate_mtr(test_record)
|
68
|
+
test_record.validate
|
69
|
+
test_record
|
70
|
+
rescue Dragnet::Errors::ValidationError => e
|
71
|
+
yaml_format_error(e.message)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../fields/sha1_validator'
|
4
|
+
require_relative '../fields/files_validator'
|
5
|
+
require_relative '../fields/path_validator'
|
6
|
+
|
7
|
+
module Dragnet
|
8
|
+
module Validators
|
9
|
+
module Entities
|
10
|
+
# Validates a +Dragnet::Repo+ object, by checking its attributes.
|
11
|
+
class RepoValidator
|
12
|
+
attr_reader :repo
|
13
|
+
|
14
|
+
# @param [Dragnet::Repo] repo An instance of +Dragnet::Repo+ to validate.
|
15
|
+
def initialize(repo)
|
16
|
+
@repo = repo
|
17
|
+
end
|
18
|
+
|
19
|
+
# Validates the instance of the +Dragnet::Repo+ object by checking each
|
20
|
+
# of its attributes.
|
21
|
+
# @raise [Dragnet::Errors::ValidationError] If any of the fields in the
|
22
|
+
# given +Dragnet::Repo+ object fails the validation.
|
23
|
+
def validate
|
24
|
+
Dragnet::Validators::Fields::SHA1Validator.new.validate('repos[sha1]', repo.sha1)
|
25
|
+
Dragnet::Validators::Fields::PathValidator.new.validate('repos[path]', repo.path)
|
26
|
+
repo.files = Dragnet::Validators::Fields::FilesValidator.new.validate('repos[files]', repo.files)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../fields/description_validator'
|
4
|
+
require_relative '../fields/files_validator'
|
5
|
+
require_relative '../fields/id_validator'
|
6
|
+
require_relative '../fields/meta_data_field_validator'
|
7
|
+
require_relative '../fields/repos_validator'
|
8
|
+
require_relative '../fields/result_validator'
|
9
|
+
require_relative '../fields/sha1_validator'
|
10
|
+
|
11
|
+
require_relative '../../errors/validation_error'
|
12
|
+
|
13
|
+
module Dragnet
|
14
|
+
module Validators
|
15
|
+
module Entities
|
16
|
+
# Validates a MTR object
|
17
|
+
class TestRecordValidator
|
18
|
+
attr_reader :test_record
|
19
|
+
|
20
|
+
# Creates a new instance of the class.
|
21
|
+
# @param [Dragnet::TestRecord] test_record The test record to validate.
|
22
|
+
def initialize(test_record)
|
23
|
+
@test_record = test_record
|
24
|
+
end
|
25
|
+
|
26
|
+
# Validates the given test record
|
27
|
+
# @raise [Dragnet::Errors::ValidationError] If the validation fails.
|
28
|
+
def validate
|
29
|
+
repos_xor_files
|
30
|
+
repos_xor_sha1
|
31
|
+
|
32
|
+
Dragnet::Validators::Fields::IDValidator.new.validate('id', test_record.id)
|
33
|
+
Dragnet::Validators::Fields::DescriptionValidator.new.validate('description', test_record.description)
|
34
|
+
validate_meta_data_fields
|
35
|
+
|
36
|
+
test_record.files = Dragnet::Validators::Fields::FilesValidator.new.validate('files', test_record.files)
|
37
|
+
test_record.result = Dragnet::Validators::Fields::ResultValidator.new.validate('result', test_record.result)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# @raise [Dragnet::Errors::ValidationError] If the MTR has both a
|
43
|
+
# +files+ and a +repos+ attribute.
|
44
|
+
def repos_xor_files
|
45
|
+
return unless test_record.files && test_record.repos
|
46
|
+
|
47
|
+
raise Dragnet::Errors::ValidationError,
|
48
|
+
"Invalid MTR: #{test_record.id}. Either 'files' or 'repos' should be provided, not both"
|
49
|
+
end
|
50
|
+
|
51
|
+
# Executes the validation over the +repos+ attribute and then verifies
|
52
|
+
# if the +sha1+ attribute was also given. If it was, an error is
|
53
|
+
# raised. If +repos+ is not present, then the +sha1+ attribute is
|
54
|
+
# validated.
|
55
|
+
#
|
56
|
+
# This happens in this order to leverage the fact that the
|
57
|
+
# +ReposValidator+ returns +nil+ for empty +Array+s. So if +repos+ is
|
58
|
+
# given as en empty +Array+ the MTR will still be considered valid
|
59
|
+
# (provided it has a SHA1).
|
60
|
+
#
|
61
|
+
# @raise [Dragnet::Errors::ValidationError] If the validation of the
|
62
|
+
# +repos+ attribute fails, if both +repos+ and +sha1+ are present or
|
63
|
+
# if the validation of the +sha1+ attribute fails.
|
64
|
+
def repos_xor_sha1
|
65
|
+
test_record.repos = Dragnet::Validators::Fields::ReposValidator.new.validate('repos', test_record.repos)
|
66
|
+
|
67
|
+
unless test_record.repos
|
68
|
+
Dragnet::Validators::Fields::SHA1Validator.new.validate('sha1', test_record.sha1)
|
69
|
+
return
|
70
|
+
end
|
71
|
+
|
72
|
+
return unless test_record.sha1
|
73
|
+
|
74
|
+
raise Dragnet::Errors::ValidationError,
|
75
|
+
"Invalid MTR: #{test_record.id}. Either 'repos' or 'sha1' should be provided, not both"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Validates the meta-data fields of the Test Record.
|
79
|
+
# @raise [Dragnet::Errors::ValidationError] If any of the meta-data
|
80
|
+
# fields fail the validation.
|
81
|
+
def validate_meta_data_fields
|
82
|
+
meta_data_validator = Dragnet::Validators::Fields::MetaDataFieldValidator.new
|
83
|
+
|
84
|
+
test_record.name = meta_data_validator.validate('name', test_record.name)
|
85
|
+
test_record.test_method = meta_data_validator.validate('test_method', test_record.test_method)
|
86
|
+
test_record.tc_derivation_method = meta_data_validator.validate(
|
87
|
+
'tc_derivation_method', test_record.tc_derivation_method
|
88
|
+
)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'entities/repo_validator'
|
4
|
+
require_relative 'entities/test_record_validator'
|
5
|
+
|
6
|
+
module Dragnet
|
7
|
+
module Validators
|
8
|
+
# Namespace for Dragnet's Entity Validators (validators that deal with data
|
9
|
+
# objects / models).
|
10
|
+
module Entities; end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'field_validator'
|
4
|
+
|
5
|
+
module Dragnet
|
6
|
+
module Validators
|
7
|
+
module Fields
|
8
|
+
# Validates the +description+ field for a MTR.
|
9
|
+
class DescriptionValidator < Dragnet::Validators::Fields::FieldValidator
|
10
|
+
# Validates a MTR's description
|
11
|
+
# @param [String] key The name of the key
|
12
|
+
# @param [Object] value The value of the key
|
13
|
+
# @raise [Dragnet::Errors::ValidationError] If the description contains
|
14
|
+
# anything but a +String+ or +nil+.
|
15
|
+
# :reek:NilCheck (Only +nil+ is allowed, +false+ should be considered invalid).
|
16
|
+
def validate(key, value)
|
17
|
+
return if value.nil?
|
18
|
+
|
19
|
+
validate_type(key, value, String)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../errors/validation_error'
|
4
|
+
|
5
|
+
module Dragnet
|
6
|
+
module Validators
|
7
|
+
module Fields
|
8
|
+
# Base class for all the validators used to validate individual fields
|
9
|
+
# inside entities.
|
10
|
+
class FieldValidator
|
11
|
+
def validate(_key, _value)
|
12
|
+
raise NotImplementedError, "#validate method not implemented in #{self.class}"
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# Validates the presence of a value
|
18
|
+
# @param [String, Symbol] key The key associated with the value.
|
19
|
+
# @param [Object] value The value to validate.
|
20
|
+
# @raise [Dragnet::Errors::ValidationError] If the given value is not
|
21
|
+
# present (i.e. is +nil+)
|
22
|
+
def validate_presence(key, value)
|
23
|
+
validation_error("Missing required key: #{key}") if value.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
# Validates the type of the given value.
|
27
|
+
# @param [String, Symbol] key The key associated with the value.
|
28
|
+
# @param [Object] value The value to validate.
|
29
|
+
# @param [Array<Class>] expected_types The allowed types for the given
|
30
|
+
# value.
|
31
|
+
# @raise [Dragnet::Errors::ValidationError] If the given value has a type
|
32
|
+
# which is not in the given array of expected types.
|
33
|
+
def validate_type(key, value, *expected_types)
|
34
|
+
return if expected_types.include?(value.class)
|
35
|
+
|
36
|
+
validation_error(
|
37
|
+
"Incompatible type for key #{key}: "\
|
38
|
+
"Expected #{expected_types.join(', ')} got #{value.class} instead"
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Raises a +Dragnet::Errors::ValidationError+ with the given message.
|
43
|
+
# @param [String] message The message for the error.
|
44
|
+
# @raise [Dragnet::Errors::ValidationError] Is always raised.
|
45
|
+
def validation_error(message)
|
46
|
+
raise Dragnet::Errors::ValidationError, message
|
47
|
+
end
|
48
|
+
|
49
|
+
# Validates that all elements inside the given array are of the
|
50
|
+
# expected type
|
51
|
+
# @param [String, Symbol] key The key associated with the array.
|
52
|
+
# @param [Array] array The array whose types should be checked.
|
53
|
+
# @param [Class] expected_type The type the elements inside the array
|
54
|
+
# should have.
|
55
|
+
# @raise [Dragnet::Errors::ValidationError] If any of the elements inside
|
56
|
+
# the given array is of a different type.
|
57
|
+
def validate_array_types(key, array, expected_type)
|
58
|
+
incompatible_value = array.find { |val| !val.is_a?(expected_type) }
|
59
|
+
return unless incompatible_value
|
60
|
+
|
61
|
+
validation_error(
|
62
|
+
"Incompatible type for key #{key}: Expected a Array<#{expected_type}>. "\
|
63
|
+
"Found a(n) #{incompatible_value.class} inside the array"
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'field_validator'
|
4
|
+
|
5
|
+
module Dragnet
|
6
|
+
module Validators
|
7
|
+
module Fields
|
8
|
+
# Validates the files field on a Manual Test Record
|
9
|
+
class FilesValidator < Dragnet::Validators::Fields::FieldValidator
|
10
|
+
# Validates the MTR's +files+ array.
|
11
|
+
# @param [String] key The name of the key
|
12
|
+
# @param [Object] value The value of the key
|
13
|
+
# @return [Array<String>, nil] If +files+ is an Array or a String then
|
14
|
+
# an array is returned, if +files+ is +nil+ then +nil+ is returned.
|
15
|
+
# @raise [Dragnet::Errors::ValidationError] If the +files+ key is not a
|
16
|
+
# +String+ or an +Array+ of +String+s.
|
17
|
+
def validate(key, value)
|
18
|
+
return unless value
|
19
|
+
|
20
|
+
validate_type(key, value, String, Array)
|
21
|
+
value = *value
|
22
|
+
validate_array_types(key, value, String)
|
23
|
+
|
24
|
+
value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'field_validator'
|
4
|
+
|
5
|
+
module Dragnet
|
6
|
+
module Validators
|
7
|
+
module Fields
|
8
|
+
# Validates the ID Field for Manual Test Records
|
9
|
+
class IDValidator < Dragnet::Validators::Fields::FieldValidator
|
10
|
+
# Validates the Requirement ID(s) of the MTR
|
11
|
+
# @param [String] key The name of the key
|
12
|
+
# @param [Object] value The value of the key
|
13
|
+
# @raise [Dragnet::Errors::ValidationError] If the Requirement ID(s) are
|
14
|
+
# missing, they are not a String or an Array of Strings if they contain
|
15
|
+
# a disallowed character or (in the case of an Array) any of its
|
16
|
+
# elements is not a String.
|
17
|
+
def validate(key, value)
|
18
|
+
validate_presence(key, value)
|
19
|
+
validate_type(key, value, String, Array)
|
20
|
+
|
21
|
+
if value.is_a?(String)
|
22
|
+
match = value.match(/,|\s/)
|
23
|
+
return unless match
|
24
|
+
|
25
|
+
validation_error(
|
26
|
+
"Disallowed character '#{match}' found in the value for key #{key}. "\
|
27
|
+
'To use multiple requirement IDs please put them into an array'
|
28
|
+
)
|
29
|
+
else
|
30
|
+
validate_array_types(key, value, String)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'field_validator'
|
4
|
+
require_relative '../../errors/validation_error'
|
5
|
+
|
6
|
+
module Dragnet
|
7
|
+
module Validators
|
8
|
+
module Fields
|
9
|
+
# Base class to validate the fields that are part of the meta-data group.
|
10
|
+
# This means: Either +String+ +Array<String>+ or +nil+ as value.
|
11
|
+
class MetaDataFieldValidator < Dragnet::Validators::Fields::FieldValidator
|
12
|
+
# Validates the specified attribute as a meta-data field.
|
13
|
+
# @param [String] key The name of the key
|
14
|
+
# @param [Object] value The value of the key
|
15
|
+
# @raise [Dragnet::Errors::ValidationError] If the attribute fails the
|
16
|
+
# validation.
|
17
|
+
# @return [nil] If +value+ is +nil+ or an empty array.
|
18
|
+
# @return [Array<String>] If +value+ is a +String+ or an +Arry<String>+
|
19
|
+
def validate(key, value)
|
20
|
+
return unless value
|
21
|
+
|
22
|
+
validate_type(key, value, String, Array)
|
23
|
+
|
24
|
+
if value.is_a?(Array)
|
25
|
+
return if value.empty?
|
26
|
+
|
27
|
+
validate_array_types(key, value, String)
|
28
|
+
value
|
29
|
+
else
|
30
|
+
[value]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'field_validator'
|
4
|
+
|
5
|
+
module Dragnet
|
6
|
+
module Validators
|
7
|
+
module Fields
|
8
|
+
# Validates the +path+ attribute of a +Repo+ object.
|
9
|
+
class PathValidator < Dragnet::Validators::Fields::FieldValidator
|
10
|
+
# Validates the Path of the repository.
|
11
|
+
# @param [String] key The name of the key
|
12
|
+
# @param [Object] value The value of the key
|
13
|
+
# @raise [Dragnet::Errors::ValidationError] If the path is missing, or
|
14
|
+
# it isn't a String.
|
15
|
+
def validate(key, value)
|
16
|
+
validate_presence(key, value)
|
17
|
+
validate_type(key, value, String)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|