diff --git a/.gitignore b/.gitignore index 8cc1a3f..e6d9610 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ coverage/ *.db .bundle/ _snippets/ -_cache/ \ No newline at end of file +_cache/ +.ruby-version \ No newline at end of file diff --git a/.rubocop.yml b/.rubocop.yml index 38f79a8..6966b53 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,7 +8,7 @@ AllCops: Exclude: - '_*/**/*' # - '_snippets/**/*' - - 'app/infrastructure/git/repostore/**/*' + # - 'app/infrastructure/git/repostore/**/*' # ignore block length in non-production code Metrics/BlockLength: diff --git a/Gemfile b/Gemfile index 2aa3a1b..0331ea8 100644 --- a/Gemfile +++ b/Gemfile @@ -37,9 +37,16 @@ gem 'aws-sdk-sqs', '~> 1' gem 'hirb', '~> 0.7' gem 'sequel', '~> 5.13' +# Ruby AST unparser +gem 'unparser' + +# Git Operation by using git object +gem 'git', '~> 1.5' + group :development, :test do gem 'database_cleaner' gem 'sqlite3' + gem 'factory_bot', '~> 5.0', '>= 5.0.2' end group :production do diff --git a/Gemfile.lock b/Gemfile.lock index 439ea16..a68c196 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,15 @@ GEM remote: https://rubygems.org/ specs: + abstract_type (0.0.7) + activesupport (5.2.2.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + adamantium (0.2.0) + ice_nine (~> 0.11.0) + memoizable (~> 0.4.0) addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) ast (2.4.0) @@ -27,6 +36,9 @@ GEM coderay (1.1.2) coercible (1.0.0) descendants_tracker (~> 0.0.1) + concord (0.1.5) + adamantium (~> 0.2.0) + equalizer (~> 0.0.9) concurrent-ruby (1.1.3) connection_pool (2.2.2) cookiejar (0.3.3) @@ -38,6 +50,7 @@ GEM declarative-option (0.1.0) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) + diff-lcs (1.3) docile (1.3.1) domain_name (0.5.20180417) unf (>= 0.0.5, < 1.0.0) @@ -100,6 +113,8 @@ GEM ethon (0.11.0) ffi (>= 1.3.0) eventmachine (1.2.7) + factory_bot (5.0.2) + activesupport (>= 4.2.0) faraday (0.15.4) multipart-post (>= 1.2, < 3) faraday_middleware (0.12.2) @@ -127,6 +142,7 @@ GEM multi_json (~> 1.0) net-http-persistent (>= 2.7) net-http-pipeline + git (1.5.0) hashdiff (0.3.7) highline (1.7.10) hirb (0.7.3) @@ -139,6 +155,8 @@ GEM domain_name (~> 0.5) http-form_data (2.1.1) http_parser.rb (0.6.0) + i18n (1.6.0) + concurrent-ruby (~> 1.0) ice_nine (0.11.2) interception (0.5) jaro_winkler (1.5.1) @@ -151,6 +169,8 @@ GEM rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) + memoizable (0.4.2) + thread_safe (~> 0.3, >= 0.3.1) method_source (0.9.2) minitest (5.11.3) minitest-rg (5.2.0) @@ -166,6 +186,7 @@ GEM path_expander (1.0.3) pg (0.21.0) powerpack (0.1.2) + procto (0.0.3) pry (0.12.2) coderay (~> 1.1.0) method_source (~> 0.9.0) @@ -249,11 +270,21 @@ GEM typhoeus (~> 0.6, >= 0.6.8) typhoeus (0.8.0) ethon (>= 0.8.0) + tzinfo (1.2.5) + thread_safe (~> 0.1) uber (0.1.0) unf (0.1.4) unf_ext unf_ext (0.0.7.5) unicode-display_width (1.4.0) + unparser (0.4.2) + abstract_type (~> 0.0.7) + adamantium (~> 0.2.0) + concord (~> 0.1.5) + diff-lcs (~> 1.3) + equalizer (~> 0.0.9) + parser (>= 2.3.1.2, < 2.6) + procto (~> 0.0.2) vcr (4.0.0) virtus (1.0.5) axiom-types (~> 0.1) @@ -281,8 +312,10 @@ DEPENDENCIES dry-types (~> 0.5) dry-validation econfig (~> 2.1) + factory_bot (~> 5.0, >= 5.0.2) faye (~> 1) flog + git (~> 1.5) hirb (~> 0.7) http (~> 3.0) minitest (~> 5.11) @@ -308,6 +341,7 @@ DEPENDENCIES simplecov (~> 0.16) sqlite3 travis + unparser vcr (~> 4.0) webmock (~> 3.4) diff --git a/app/application/services/appraise_project.rb b/app/application/services/appraise_project.rb index e9d2d0c..48cb7b9 100644 --- a/app/application/services/appraise_project.rb +++ b/app/application/services/appraise_project.rb @@ -26,7 +26,6 @@ def find_project_details(input) input[:project] = Repository::For.klass(Entity::Project).find_full_name( input[:requested].owner_name, input[:requested].project_name ) - if input[:project] Success(input) else @@ -59,10 +58,11 @@ def request_cloning_worker(input) end def appraise_contributions(input) - input[:folder] = Mapper::Contributions - .new(input[:gitrepo]).for_folder(input[:requested].folder_name) + contributions = Mapper::Contributions.new(input[:gitrepo]) + input[:folder] = contributions.for_folder(input[:requested].folder_name) + input[:commits] = contributions.commits - Value::ProjectFolderContributions.new(input[:project], input[:folder]) + Value::ProjectFolderContributions.new(input[:project], input[:folder], input[:commits]) .yield_self do |appraisal| Success(Value::Result.new(status: :ok, message: appraisal)) end diff --git a/app/application/values/project_folder_contributions.rb b/app/application/values/project_folder_contributions.rb index 1da288b..3c82aea 100644 --- a/app/application/values/project_folder_contributions.rb +++ b/app/application/values/project_folder_contributions.rb @@ -3,6 +3,6 @@ module CodePraise module Value # Contributions for a folder of a project - ProjectFolderContributions = Struct.new(:project, :folder) + ProjectFolderContributions = Struct.new(:project, :folder, :commits) end end diff --git a/app/domain/models/contributions/entities/children/complexity.rb b/app/domain/models/contributions/entities/children/complexity.rb new file mode 100644 index 0000000..cb21ccb --- /dev/null +++ b/app/domain/models/contributions/entities/children/complexity.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module CodePraise + module Entity + # Complexity for file and methods of this file + class Complexity + attr_reader :average, :methods + + def initialize(average:, methods:) + @average = average + @methods = methods + end + end + end +end diff --git a/app/domain/models/contributions/entities/children/file_change.rb b/app/domain/models/contributions/entities/children/file_change.rb new file mode 100644 index 0000000..fd9c381 --- /dev/null +++ b/app/domain/models/contributions/entities/children/file_change.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'dry-types' +require 'dry-struct' + + +module CodePraise + module Entity + # Entity for a single line of code contributed by a team-member + class FileChange < Dry::Struct + + include Dry::Types.module + + attribute :path, Strict::String + attribute :name, Strict::String + attribute :addition, Strict::Integer + attribute :deletion, Strict::Integer + + end + end +end diff --git a/app/domain/models/contributions/entities/children/file_contributions.rb b/app/domain/models/contributions/entities/children/file_contributions.rb index 2b6311b..a471ecd 100644 --- a/app/domain/models/contributions/entities/children/file_contributions.rb +++ b/app/domain/models/contributions/entities/children/file_contributions.rb @@ -5,24 +5,28 @@ module Entity # Entity for file contributions class FileContributions include Mixins::ContributionsCalculator + include Mixins::CommentCalculator DOT = '\.' LINE_END = '$' WANTED_EXTENSION = %w[rb js css html slim md].join('|') EXTENSION_REGEX = /#{DOT}#{WANTED_EXTENSION}#{LINE_END}/.freeze - attr_reader :file_path, :lines + attr_reader :file_path, :lines, :complexity, :idiomaticity, :methods - def initialize(file_path:, lines:) + def initialize(file_path:, lines:, complexity:, idiomaticity:, methods:) @file_path = Value::FilePath.new(file_path) @lines = lines + @complexity = complexity + @idiomaticity = idiomaticity + @methods = methods end - def total_credits - credit_share.total_credits + def total_line_credits + line_credit_share.total_credits end - def credit_share + def line_credit_share return Value::CreditShare.new if not_wanted @credit_share ||= lines @@ -32,7 +36,7 @@ def credit_share end def contributors - credit_share.contributors + line_credit_share.contributors end private diff --git a/app/domain/models/contributions/entities/children/idiomaticity.rb b/app/domain/models/contributions/entities/children/idiomaticity.rb new file mode 100644 index 0000000..42f8c28 --- /dev/null +++ b/app/domain/models/contributions/entities/children/idiomaticity.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'dry-types' +require 'dry-struct' +require_relative 'offense' + +module CodePraise + module Entity + class Idiomaticity < Dry::Struct + include Dry::Types.module + + attribute :offenses, Strict::Array.of(Entity::Offense).optional + end + end +end diff --git a/app/domain/models/contributions/entities/children/method_contribution.rb b/app/domain/models/contributions/entities/children/method_contribution.rb new file mode 100644 index 0000000..f0cfecf --- /dev/null +++ b/app/domain/models/contributions/entities/children/method_contribution.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require_relative 'line_contribution' +require 'dry-types' +require 'dry-struct' + +module CodePraise + module Entity + # Entity for a single method contributed by a team-member + class MethodContribution < Dry::Struct + include Dry::Types.module + + attribute :name, Coercible::String + attribute :lines, Array.of(LineContribution) + + def credit_share + @credit_share ||= lines.each_with_object(Value::CreditShare.new) do |line, credit| + credit.add_credit(line) + end + end + + def share + credit_share.share + end + + def contributors + credit_share.contributors + end + end + end +end diff --git a/app/domain/models/contributions/entities/children/offense.rb b/app/domain/models/contributions/entities/children/offense.rb new file mode 100644 index 0000000..00b2797 --- /dev/null +++ b/app/domain/models/contributions/entities/children/offense.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'dry-types' +require 'dry-struct' + +module CodePraise + module Entity + class Offense < Dry::Struct + include Dry::Types.module + + attribute :type, Strict::String + attribute :message, Strict::String + attribute :location, Strict::Integer + end + end +end diff --git a/app/domain/models/contributions/entities/init.rb b/app/domain/models/contributions/entities/init.rb index 7b02065..78f9159 100644 --- a/app/domain/models/contributions/entities/init.rb +++ b/app/domain/models/contributions/entities/init.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -folders = %w[children root] +folders = %w[metrics children root] folders.each do |folder| require_relative "#{folder}/init.rb" end diff --git a/app/domain/models/contributions/entities/metrics/init.rb b/app/domain/models/contributions/entities/metrics/init.rb new file mode 100644 index 0000000..0019f37 --- /dev/null +++ b/app/domain/models/contributions/entities/metrics/init.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +Dir.glob("#{__dir__}/*.rb").each do |file| + require file +end diff --git a/app/domain/models/contributions/entities/root/commit.rb b/app/domain/models/contributions/entities/root/commit.rb new file mode 100644 index 0000000..d184733 --- /dev/null +++ b/app/domain/models/contributions/entities/root/commit.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'dry-types' +require 'dry-struct' +require_relative '../children/contributor' + +module CodePraise + module Entity + # Entity for a single line of code contributed by a team-member + class Commit < Dry::Struct + include Dry::Types.module + + attribute :committer, Contributor + attribute :sha, Strict::String + attribute :date, Params::DateTime + attribute :size, Strict::Integer + attribute :message, Strict::String + attribute :file_changes, Strict::Array.of(FileChange) + + def total_additions + file_changes.map(&:addition).reduce(&:+) + end + + def total_deletions + file_changes.map(&:deletion).reduce(&:+) + end + + def total_files + file_changes.count + end + end + end +end diff --git a/app/domain/models/contributions/entities/root/folder_contributions.rb b/app/domain/models/contributions/entities/root/folder_contributions.rb index c7618f3..114d182 100644 --- a/app/domain/models/contributions/entities/root/folder_contributions.rb +++ b/app/domain/models/contributions/entities/root/folder_contributions.rb @@ -1,16 +1,19 @@ # frozen_string_literal: true + module CodePraise module Entity # Entity for folder contributions class FolderContributions < SimpleDelegator include Mixins::ContributionsCalculator + include Mixins::CodeOnwershipCalculator - attr_reader :path, :files + attr_reader :path, :files, :repo_path - def initialize(path:, files:) + def initialize(path:, files:, repo_path:) @path = path @files = files + @repo_path = repo_path super(Types::HashedArrays.new) base_files.each { |file| self[file.file_path.filename] = file } @@ -21,14 +24,22 @@ def line_count files.map(&:line_count).reduce(&:+) end - def total_credits - files.map(&:total_credits).sum + def total_line_credits + files.map(&:total_line_credits).sum end def lines files.map(&:lines).reduce(&:+) end + def average_complexity + files_complexity = files.map(&:complexity).reject(&:nil?) + + return 0 if files_complexity.count.zero? + + files_complexity.map(&:average).reduce(:+) / files_complexity.count + end + def base_files @base_files ||= files.select do |file| file.file_path.directory == comparitive_path @@ -45,7 +56,7 @@ def subfolders end @subfolders = folders.map do |folder_name, folder_files| - FolderContributions.new(path: folder_name, files: folder_files) + FolderContributions.new(path: folder_name, files: folder_files, repo_path: @repo_path) end end @@ -57,12 +68,12 @@ def any_base_files? base_files.count.positive? end - def credit_share - @credit_share ||= files.map(&:credit_share).reduce(&:+) + def line_credit_share + @credit_share ||= files.map(&:line_credit_share).reduce(&:+) end def contributors - credit_share.contributors + line_credit_share.contributors end private diff --git a/app/domain/models/contributions/lib/code_ownership_calculator.rb b/app/domain/models/contributions/lib/code_ownership_calculator.rb new file mode 100644 index 0000000..4c65af5 --- /dev/null +++ b/app/domain/models/contributions/lib/code_ownership_calculator.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module CodePraise + module Mixins + # Calculate coefficient variantion of subfolder contribution + module CodeOnwershipCalculator + def coefficient_variation + return nil unless any_subfolders? + + contributors_percentage_hash = contributros_subfolders_percentage + contributors_percentage_hash.each do |k, v| + nums = v.is_a?(Array) ? v : [v] + contributors_percentage_hash[k] = Math.coefficient_variation(nums) + end + contributors_percentage_hash + end + + private + + def contributros_subfolders_percentage + percentage_array = subfolders.map do |subfolder| + subfolder.line_credit_share.share_percentage + end + contributors_hash = contributors.each_with_object({}) {|contributor, hash| hash[contributor.username] = []; hash} + percentage_array.each do |subfolder_percentage| + contributors_hash.keys.each do |k| + contributors_hash[k].push(subfolder_percentage[k].nil? ? 0 : subfolder_percentage[k]) + end + end + contributors_hash + end + end + end +end diff --git a/app/domain/models/contributions/lib/comment_calculator.rb b/app/domain/models/contributions/lib/comment_calculator.rb new file mode 100644 index 0000000..2ad64dc --- /dev/null +++ b/app/domain/models/contributions/lib/comment_calculator.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module CodePraise + module Mixins + # Comment and multiline comment calculator + module CommentCalculator + MULTILINE = 2 + COMMENT = '#' + + def multiline_comment_count + count_comments(:multiline_comment?, lines_of_code) + end + + def singleline_comment_count + count_comments(:singleline_comment?, lines_of_code) + end + + def count_comments(condition, lines_of_code) + comment_index = not_comment_index = documentation = -1 + lines_of_code.each_with_index do |loc, i| + if comment?(loc) + comment_index = i + else + documentation += 1 if method(condition).call(comment_index, not_comment_index) + not_comment_index = i + end + end + documentation += 1 if method(condition).call(comment_index, not_comment_index) + documentation + end + + private + + def lines_of_code + lines.map(&:code) + end + + def singleline_comment?(comment_index, not_comment_index) + (comment_index - not_comment_index) == 1 + end + + def multiline_comment?(comment_index, not_comment_index) + (comment_index - not_comment_index) >= MULTILINE + end + + def comment?(loc) + loc.strip[0] == COMMENT + end + end + end +end diff --git a/app/domain/models/contributions/lib/contributions_calculator.rb b/app/domain/models/contributions/lib/contributions_calculator.rb index 1f43316..77b99f4 100644 --- a/app/domain/models/contributions/lib/contributions_calculator.rb +++ b/app/domain/models/contributions/lib/contributions_calculator.rb @@ -23,6 +23,10 @@ def credits_for(contributor) def percent_credit_of(contributor) ((credits_for(contributor).to_f / line_count) * 100).round end + + def total_methods + @methods.nil? ? 0 : @methods.count + end end end end diff --git a/app/domain/models/contributions/mappers/commit_diff.rb b/app/domain/models/contributions/mappers/commit_diff.rb new file mode 100644 index 0000000..9010645 --- /dev/null +++ b/app/domain/models/contributions/mappers/commit_diff.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module CodePraise + module Mapper + module CommitDiff + def self.parser(diff) + diff_files = diff.stats[:files] + diff_files.keys.map do |key| + { + path: key, + name: file_name(key), + addition: diff_files[key][:insertions], + deletion: diff_files[key][:deletions] + } + end + end + + def self.file_name(file_path) + file_path.split('/').last + end + end + end +end diff --git a/app/domain/models/contributions/mappers/commit_mapper.rb b/app/domain/models/contributions/mappers/commit_mapper.rb new file mode 100644 index 0000000..4a27cfa --- /dev/null +++ b/app/domain/models/contributions/mappers/commit_mapper.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require_relative 'commit_diff' + +module CodePraise + module Mapper + class Commit + + def initialize(commit, empty_commit) + @commit = commit + @empty_commit = empty_commit + end + + def build_entity + Entity::Commit.new( + committer: committer, + sha: @commit.sha, + date: @commit.date, + message: @commit.message, + size: @commit.size, + file_changes: file_changes + ) + end + + + private + + def committer + Entity::Contributor.new( + username: @commit.committer.name, + email: @commit.committer.email + ) + end + + def file_changes + file_change_array = CommitDiff.parser(get_diff) + file_change_array.each do |file_change| + Entity::FileChange.new( + path: file_change[:path], + name: file_change[:name], + addition: file_change[:addition], + deletion: file_change[:deletion], + modification: file_change[:modification] + ) + end + end + + def get_diff + if @commit.parent.nil? + @empty_commit.diff(@commit) + else + @commit.parent.diff(@commit) + end + end + + + end + end +end diff --git a/app/domain/models/contributions/mappers/complexity_mapper.rb b/app/domain/models/contributions/mappers/complexity_mapper.rb new file mode 100644 index 0000000..4977e3a --- /dev/null +++ b/app/domain/models/contributions/mappers/complexity_mapper.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module CodePraise + module Mapper + # Transform Flog raw data into Complexity Entity + class Complexity + def initialize(file_path) + @file_path = file_path + end + + def build_entity + complexity = abc_metric + + return nil if complexity.nil? + + Entity::Complexity.new( + average: complexity[:average], + methods: complexity[:methods] + ) + end + + private + + def abc_metric + flog = CodePraise::Complexity::FlogReporter.new(@file_path) + flog.report + end + end + end +end \ No newline at end of file diff --git a/app/domain/models/contributions/mappers/contributions_mapper.rb b/app/domain/models/contributions/mappers/contributions_mapper.rb index babcc61..9b07bc6 100644 --- a/app/domain/models/contributions/mappers/contributions_mapper.rb +++ b/app/domain/models/contributions/mappers/contributions_mapper.rb @@ -13,10 +13,21 @@ def for_folder(folder_name) Mapper::FolderContributions.new( folder_name, - parse_file_reports(blame) + parse_file_reports(blame), + @gitrepo.local.git_repo_path ).build_entity end + def commits(since=nil) + commit_report = GitCommit::CommitReporter.new(@gitrepo) + commits = commit_report.commits(since) + empty_commit = commit_report.empty_commit + + commits.map do |commit| + Mapper::Commit.new(commit, empty_commit).build_entity + end + end + def parse_file_reports(blame_output) blame_output.map do |file_blame| name = file_blame[0] diff --git a/app/domain/models/contributions/mappers/file_contributions_mapper.rb b/app/domain/models/contributions/mappers/file_contributions_mapper.rb index aa99136..502d3ae 100644 --- a/app/domain/models/contributions/mappers/file_contributions_mapper.rb +++ b/app/domain/models/contributions/mappers/file_contributions_mapper.rb @@ -4,14 +4,19 @@ module CodePraise module Mapper # Summarizes a single file's contributions by team members class FileContributions - def initialize(file_report) + def initialize(file_report, repo_path, idiomaticity_mapper) @file_report = file_report + @repo_path = repo_path + @idiomaticity_mapper = idiomaticity_mapper end def build_entity Entity::FileContributions.new( file_path: filename, - lines: contributions + lines: contributions, + complexity: complexity, + idiomaticity: idiomaticity, + methods: methods ) end @@ -25,6 +30,15 @@ def contributions summarize_line_reports(@file_report[1]) end + def complexity + Mapper::Complexity.new("#{@repo_path}/#{filename}").build_entity + end + + def idiomaticity + file_path = Value::FilePath.new(filename) + @idiomaticity_mapper.build_entity(file_path) + end + def summarize_line_reports(line_reports) line_reports.map.with_index do |report, line_index| Entity::LineContribution.new( @@ -36,6 +50,16 @@ def summarize_line_reports(line_reports) end end + def methods + return [] unless ruby_file? + + MethodContributions.new(contributions).build_entity + end + + def ruby_file? + File.extname(@file_report[0]) == '.rb' + end + def contributor_from(report) Entity::Contributor.new( username: report['author'], diff --git a/app/domain/models/contributions/mappers/folder_contributions_mapper.rb b/app/domain/models/contributions/mappers/folder_contributions_mapper.rb index 2153dd9..6582872 100644 --- a/app/domain/models/contributions/mappers/folder_contributions_mapper.rb +++ b/app/domain/models/contributions/mappers/folder_contributions_mapper.rb @@ -8,22 +8,27 @@ module Mapper class FolderContributions attr_reader :folder_name attr_reader :contributions_reports + attr_reader :repo_path - def initialize(folder_name, contributions_reports) + def initialize(folder_name, contributions_reports, repo_path) @folder_name = folder_name @contributions_reports = contributions_reports + @repo_path = repo_path + @idiomaticity_mapper = Mapper::Idiomaticity.new(repo_path) end def build_entity Entity::FolderContributions.new( path: @folder_name, - files: file_summaries + files: file_summaries, + repo_path: @repo_path ) end def file_summaries @contributions_reports.map do |file_report| - Mapper::FileContributions.new(file_report).build_entity + Mapper::FileContributions.new(file_report, @repo_path, + @idiomaticity_mapper).build_entity end end diff --git a/app/domain/models/contributions/mappers/idiomaticity_mapper.rb b/app/domain/models/contributions/mappers/idiomaticity_mapper.rb new file mode 100644 index 0000000..ce98883 --- /dev/null +++ b/app/domain/models/contributions/mappers/idiomaticity_mapper.rb @@ -0,0 +1,31 @@ +module CodePraise + module Mapper + class Idiomaticity + def initialize(git_repo_path) + @rubocop_reporter = Rubocop::Reporter.new(git_repo_path) + end + + def build_entity(file_path) + Entity::Idiomaticity.new( + offenses: offenses(file_path) + ) + end + + private + + def offenses(file_path) + idiomaticity_result = @rubocop_reporter.report[file_path] + + return nil if idiomaticity_result.nil? + + idiomaticity_result.map do |error_hash| + Entity::Offense.new( + type: error_hash['cop_name'], + message: error_hash['message'], + location: error_hash['location']['start_line'] + ) + end + end + end + end +end diff --git a/app/domain/models/contributions/mappers/method_contribution_mapper.rb b/app/domain/models/contributions/mappers/method_contribution_mapper.rb new file mode 100644 index 0000000..a59a549 --- /dev/null +++ b/app/domain/models/contributions/mappers/method_contribution_mapper.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module CodePraise + module Mapper + class MethodContributions + def initialize(file_contributions) + @file_contributions = file_contributions + end + + def build_entity + methods = all_methods + + return nil if methods.nil? + + methods.map do |method| + Entity::MethodContribution.new( + name: method[:name], + lines: method[:lines] + ) + end + end + + private + + def all_methods + MethodParser.parse_methods(@file_contributions) + end + end + end +end \ No newline at end of file diff --git a/app/domain/models/contributions/mappers/method_parser.rb b/app/domain/models/contributions/mappers/method_parser.rb new file mode 100644 index 0000000..acd1bd1 --- /dev/null +++ b/app/domain/models/contributions/mappers/method_parser.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +require 'parser/current' +require 'unparser' + +module CodePraise + module Mapper + # Find all method in a file + module MethodParser + def self.parse_methods(line_entities) + ast = Parser::CurrentRuby.parse(line_of_code(line_entities)) + all_methods_hash(ast, line_entities) + end + + def self.line_of_code(line_entities) + line_entities.map(&:code).join("\n") + end + + def self.all_methods_hash(ast, line_entities) + methods_ast = [] + find_methods_tree(ast, methods_ast) + + return nil if methods_ast.empty? + + methods_ast.inject([]) do |result, method_ast| + result.push(name: method_name(method_ast), + lines: select_entities(method_ast, line_entities)) + end + end + + def self.select_entities(method_ast, line_entities) + method_name = method_name(method_ast) + end_amount = count_end(method_ast) + + number = line_entities.select do |line_entity| + line_entity.code.include?("def #{method_name}") + end.first.number + + method_entities = [] + + while end_amount.positive? + line_entity = select_entity(line_entities, number) + + break if line_entity.nil? + + end_amount -= 1 if end_entity?(line_entity) + number += 1 + method_entities << line_entity + end + + method_entities + end + + private + + def self.select_entity(line_entities, number) + line_entities.select do |line_entity| + line_entity.number == number + end.first + end + + def self.end_entity?(line_entity) + line_entity.code.strip == 'end' + end + + def self.count_end(method_ast) + method_lines = Unparser.unparse(method_ast) + method_lines.scan(/end/).count + end + + def self.method_name(method_ast) + method_ast.children[0].to_s + end + + def self.find_methods_tree(ast, methods_ast) + return nil unless ast.is_a?(Parser::AST::Node) + + if ast.type == :def + methods_ast.append(ast) + else + ast.children.each do |child_ast| + find_methods_tree(child_ast, methods_ast) + end + end + end + + private_class_method :find_methods_tree, :count_end, + :method_name, :select_entity, :end_entity? + end + end +end diff --git a/app/domain/models/contributions/repositories/git_repo.rb b/app/domain/models/contributions/repositories/git_repo.rb index 84ad1a1..43ed79f 100644 --- a/app/domain/models/contributions/repositories/git_repo.rb +++ b/app/domain/models/contributions/repositories/git_repo.rb @@ -3,7 +3,7 @@ module CodePraise # Maps over local and remote git repo infrastructure class GitRepo - MAX_SIZE = 1000 # for cloning, analysis, summaries, etc. + MAX_SIZE = 100000 # for cloning, analysis, summaries, etc. class Errors NoGitRepoFound = Class.new(StandardError) diff --git a/app/domain/models/contributions/values/credit_share.rb b/app/domain/models/contributions/values/credit_share.rb index f76a1dc..f648fea 100644 --- a/app/domain/models/contributions/values/credit_share.rb +++ b/app/domain/models/contributions/values/credit_share.rb @@ -30,6 +30,18 @@ def ==(other) other.class == self.class && other.state == self.state end + def share_percentage + sum = share.values.reduce(&:+) + share.keys.inject({}) do |result, key| + if sum == 0 + result[key] = 0 + else + result[key] = ((share[key].to_f / sum) * 100).round + end + result + end + end + alias eql? == def hash diff --git a/app/domain/models/contributions/values/file_path.rb b/app/domain/models/contributions/values/file_path.rb index a6cd241..61d2bc9 100644 --- a/app/domain/models/contributions/values/file_path.rb +++ b/app/domain/models/contributions/values/file_path.rb @@ -16,7 +16,7 @@ def initialize(filepath) def folder_after(root) raise(ArgumentError, 'Path mismatch') unless - self.start_with?(root) || root.empty? + self.start_with?(root) || root.empty? matches = self.match(%r{(?^#{root}[^\/]+)[\/]?}) matches[:folder] diff --git a/app/infrastructure/database/orms/init.rb b/app/infrastructure/database/orms/init.rb index 50d81c0..035cc64 100644 --- a/app/infrastructure/database/orms/init.rb +++ b/app/infrastructure/database/orms/init.rb @@ -1,5 +1,11 @@ # frozen_string_literal: true +class Sequel::Model + def save! + self.save && true + end +end + Dir.glob("#{File.dirname(__FILE__)}/*.rb").each do |file| require file end diff --git a/app/infrastructure/flog/flog.rb b/app/infrastructure/flog/flog.rb new file mode 100644 index 0000000..fb664c8 --- /dev/null +++ b/app/infrastructure/flog/flog.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'flog' + +module CodePraise + module Complexity + # ABC Metric calculation class + class FlogReporter + def initialize(file_path) + @file_path = file_path + end + + def report + return nil unless ruby_file? + + flog_result = flog_process + { + average: flog_result.average, + methods: methods_complexity(flog_result.totals) + } + end + + private + + def flog_process + flog = Flog.new + flog.flog(*@file_path) + flog + end + + def ruby_file? + File.extname(@file_path) == '.rb' + end + + def methods_complexity(flog_totals) + flog_totals.keys.each_with_object({}) do |key, result| + result[only_method_name(key)] = flog_totals[key] + end + end + + def only_method_name(file_path) + file_path.split('#').last + end + end + end +end diff --git a/app/infrastructure/flog/init.rb b/app/infrastructure/flog/init.rb new file mode 100644 index 0000000..50d81c0 --- /dev/null +++ b/app/infrastructure/flog/init.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +Dir.glob("#{File.dirname(__FILE__)}/*.rb").each do |file| + require file +end diff --git a/app/infrastructure/git/commit_reporter.rb b/app/infrastructure/git/commit_reporter.rb new file mode 100644 index 0000000..8239fd6 --- /dev/null +++ b/app/infrastructure/git/commit_reporter.rb @@ -0,0 +1,39 @@ +require 'git' + + +module GitCommit + class CommitReporter + + EMPTY_SHA = '4b825dc642cb6eb9a060e54bf8d69288fbee4904' + + def initialize(gitrepo) + @local = gitrepo.local + @git = Git.open(@local.git_repo_path) + end + + def commit(sha) + @git.log(sha) + end + + def commits(since=nil) + commits = get_all_commits + commits = commits.since(since) unless since.nil? + commits + end + + def empty_commit + @git.gcommit(EMPTY_SHA) + end + + private + + def get_all_commits + n = 500 + until @git.log(n).size < n do + n += 500 + end + @git.log(n) + end + + end +end \ No newline at end of file diff --git a/app/infrastructure/git/local_repo.rb b/app/infrastructure/git/local_repo.rb index a06b4e1..853a786 100644 --- a/app/infrastructure/git/local_repo.rb +++ b/app/infrastructure/git/local_repo.rb @@ -45,6 +45,7 @@ def folder_structure parts = full_path.split('/') parent = parts.length.equal?(1) ? '/' : parts[0..-2].join('/') (structure[parent] ||= []).push(full_path) + structure end end end diff --git a/app/infrastructure/init.rb b/app/infrastructure/init.rb index 6ea203c..80bf989 100644 --- a/app/infrastructure/init.rb +++ b/app/infrastructure/init.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -folders = %w[github database git cache messaging] +folders = %w[github database git cache messaging flog rubocop] folders.each do |folder| require_relative "#{folder}/init.rb" end diff --git a/app/infrastructure/rubocop/command.rb b/app/infrastructure/rubocop/command.rb new file mode 100644 index 0000000..9885bd2 --- /dev/null +++ b/app/infrastructure/rubocop/command.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module CodePraise + module Rubocop + # Use object to operate rubocop command + class Command + FORMAT = { + 'json' => 'j' + }.freeze + + RUBOCOP = 'rubocop' + + def initialize + @format = '' + @except = [] + @redirects = [] + @target = '' + end + + def format(output_format) + @format = FORMAT[output_format] + self + end + + def target(file_path) + @target = file_path == '/' ? '' : file_path + self + end + + def except(cop) + @except << cop + self + end + + def with_stderr_output + @redirects << '2>&1' + self + end + + def full_command + [RUBOCOP, @target, options, @redirects].join(' ') + end + + private + + def options + "--except #{@except.join(',')} -f #{@format}" + end + end + end +end diff --git a/app/infrastructure/rubocop/init.rb b/app/infrastructure/rubocop/init.rb new file mode 100644 index 0000000..50d81c0 --- /dev/null +++ b/app/infrastructure/rubocop/init.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +Dir.glob("#{File.dirname(__FILE__)}/*.rb").each do |file| + require file +end diff --git a/app/infrastructure/rubocop/reporter.rb b/app/infrastructure/rubocop/reporter.rb new file mode 100644 index 0000000..b9016f6 --- /dev/null +++ b/app/infrastructure/rubocop/reporter.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require_relative 'command' + +module CodePraise + module Rubocop + class Reporter + def initialize(git_repo_path, target = '') + @git_repo_path = git_repo_path + @command = Command.new + .target(target) + .except('Metrics') + .format('json') + .with_stderr_output + end + + def report + @report ||= JSON.parse(call)['files'].each_with_object({}) do |file, hash| + hash[file['path']] = file['offenses'] + end + end + + private + + def call + in_repo do + `#{@command.full_command}` + end + end + + def in_repo(&block) + Dir.chdir(@git_repo_path) { yield block } + end + end + end +end diff --git a/app/presentation/representers/commit_representer.rb b/app/presentation/representers/commit_representer.rb new file mode 100644 index 0000000..ca48812 --- /dev/null +++ b/app/presentation/representers/commit_representer.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require_relative 'contributor_representer' +require_relative 'file_change_representer' + +module CodePraise + module Representer + class Commit < Roar::Decorator + include Roar::JSON + + property :committer, extend: Representer::Contributor, class: OpenStruct + property :sha + property :message + property :date + property :size + property :total_additions + property :total_deletions + property :total_files + collection :file_changes, extend: Representer::FileChange, class: OpenStruct + end + end +end diff --git a/app/presentation/representers/complexity_representer.rb b/app/presentation/representers/complexity_representer.rb new file mode 100644 index 0000000..be53eab --- /dev/null +++ b/app/presentation/representers/complexity_representer.rb @@ -0,0 +1,14 @@ +require 'roar/decorator' +require 'roar/json' + +module CodePraise + module Representer + # Represents folder summary about repo's folder + class Complexity < Roar::Decorator + include Roar::JSON + + property :average + property :methods + end + end +end diff --git a/app/presentation/representers/credit_share_representer.rb b/app/presentation/representers/credit_share_representer.rb index 36afb77..31e57da 100644 --- a/app/presentation/representers/credit_share_representer.rb +++ b/app/presentation/representers/credit_share_representer.rb @@ -12,6 +12,7 @@ class CreditShare < Roar::Decorator include Roar::JSON property :share + property :share_percentage collection :contributors, extend: Representer::Contributor, class: OpenStruct end diff --git a/app/presentation/representers/file_change_representer.rb b/app/presentation/representers/file_change_representer.rb new file mode 100644 index 0000000..e6e5546 --- /dev/null +++ b/app/presentation/representers/file_change_representer.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module CodePraise + module Representer + class FileChange < Roar::Decorator + include Roar::JSON + + property :path + property :name + property :addition + property :deletion + end + end +end diff --git a/app/presentation/representers/file_contributions_representer.rb b/app/presentation/representers/file_contributions_representer.rb index 6ca692d..a72183d 100644 --- a/app/presentation/representers/file_contributions_representer.rb +++ b/app/presentation/representers/file_contributions_representer.rb @@ -7,6 +7,9 @@ require_relative 'credit_share_representer' require_relative 'file_path_representer' require_relative 'line_contribution_representer' +require_relative 'complexity_representer' +require_relative 'idiomaticity_representer' +require_relative 'method_contributions_representer' module CodePraise module Representer @@ -15,9 +18,15 @@ class FileContributions < Roar::Decorator include Roar::JSON property :line_count - property :total_credits + property :total_line_credits + property :total_methods + property :multiline_comment_count + property :singleline_comment_count + collection :methods, extend: Representer::MethodContributions, class: OpenStruct property :file_path, extend: Representer::FilePath, class: OpenStruct - property :credit_share, extend: Representer::CreditShare, class: OpenStruct + property :line_credit_share, extend: Representer::CreditShare, class: OpenStruct + property :complexity, extend: Representer::Complexity, class: OpenStruct + property :idiomaticity, extend: Representer::Idiomaticity, class: OpenStruct collection :contributors, extend: Representer::Contributor, class: OpenStruct end end diff --git a/app/presentation/representers/folder_contributions_representer.rb b/app/presentation/representers/folder_contributions_representer.rb index ee31f69..5fbd333 100644 --- a/app/presentation/representers/folder_contributions_representer.rb +++ b/app/presentation/representers/folder_contributions_representer.rb @@ -16,10 +16,12 @@ class FolderContributions < Roar::Decorator property :path property :line_count - property :total_credits + property :total_line_credits property :any_subfolders? property :any_base_files? - property :credit_share, extend: Representer::CreditShare, class: OpenStruct + property :coefficient_variation + property :average_complexity + property :line_credit_share, extend: Representer::CreditShare, class: OpenStruct collection :base_files, extend: Representer::FileContributions, class: OpenStruct collection :subfolders, extend: Representer::FolderContributions, class: OpenStruct collection :contributors, extend: Representer::Contributor, class: OpenStruct diff --git a/app/presentation/representers/idiomaticity_representer.rb b/app/presentation/representers/idiomaticity_representer.rb new file mode 100644 index 0000000..0fad0c4 --- /dev/null +++ b/app/presentation/representers/idiomaticity_representer.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'roar/decorator' +require 'roar/json' + +require_relative 'offense_representer' + +module CodePraise + module Representer + # Represents Idiomaticity Errors + class Idiomaticity < Roar::Decorator + include Roar::JSON + + collection :offenses, extend: Representer::Offense, class: OpenStruct + end + end +end diff --git a/app/presentation/representers/method_contributions_representer.rb b/app/presentation/representers/method_contributions_representer.rb new file mode 100644 index 0000000..d58feae --- /dev/null +++ b/app/presentation/representers/method_contributions_representer.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'roar/decorator' +require 'roar/json' + +require_relative 'contributor_representer' +require_relative 'credit_share_representer' + +module CodePraise + module Representer + # Represents folder summary about repo's folder + class MethodContributions < Roar::Decorator + include Roar::JSON + + property :name + property :share + end + end +end diff --git a/app/presentation/representers/offense_representer.rb b/app/presentation/representers/offense_representer.rb new file mode 100644 index 0000000..41b33e5 --- /dev/null +++ b/app/presentation/representers/offense_representer.rb @@ -0,0 +1,17 @@ +# fronze_string_literal: true + +require 'roar/decorator' +require 'roar/json' + +module CodePraise + module Representer + # Represent idiomaticity offense information + class Offense < Roar::Decorator + include Roar::JSON + + property :type + property :message + property :location + end + end +end \ No newline at end of file diff --git a/app/presentation/representers/project_folder_contributions_representer.rb b/app/presentation/representers/project_folder_contributions_representer.rb index df5feaa..0c4731e 100644 --- a/app/presentation/representers/project_folder_contributions_representer.rb +++ b/app/presentation/representers/project_folder_contributions_representer.rb @@ -5,6 +5,7 @@ require_relative 'folder_contributions_representer' require_relative 'project_representer' +require_relative 'commit_representer' module CodePraise module Representer @@ -14,6 +15,7 @@ class ProjectFolderContributions < Roar::Decorator property :project, extend: Representer::Project, class: OpenStruct property :folder, extend: Representer::FolderContributions, class: OpenStruct + collection :commits, extend: Representer::Commit, class: OpenStruct end end end diff --git a/init.rb b/init.rb index a8a4f09..cbe4bc1 100644 --- a/init.rb +++ b/init.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -folders = %w[config app] +folders = %w[lib config app] folders.each do |folder| require_relative "#{folder}/init.rb" end diff --git a/lib/init.rb b/lib/init.rb new file mode 100644 index 0000000..0019f37 --- /dev/null +++ b/lib/init.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +Dir.glob("#{__dir__}/*.rb").each do |file| + require file +end diff --git a/lib/math_extension.rb b/lib/math_extension.rb new file mode 100644 index 0000000..853cd6c --- /dev/null +++ b/lib/math_extension.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +# Math +module Math + def self.coefficient_variation(nums) + return nil if mean(nums).zero? + + (sigma(nums) / mean(nums) * 100).round + end + + def self.sigma(nums) + Math.sqrt(variance(nums)) + end + + def self.variance(nums) + return nil if nums.count.zero? + + m = mean(nums) + sum = 0.0 + nums.each {|v| sum += (v-m)**2 } + (sum/nums.size).round(3) + end + + def self.mean(nums) + return nil if nums.count.zero? + + nums.reduce(:+).to_f / nums.count + end +end \ No newline at end of file diff --git a/spec/factories/init.rb b/spec/factories/init.rb new file mode 100644 index 0000000..58357b5 --- /dev/null +++ b/spec/factories/init.rb @@ -0,0 +1,6 @@ +require 'factory_bot' +require_relative '../../app/infrastructure/database/orms/init' + +Dir.glob("#{File.dirname(__FILE__)}/*_factory.rb").each do |file| + require file +end diff --git a/spec/factories/member_factory.rb b/spec/factories/member_factory.rb new file mode 100644 index 0000000..fe7b21e --- /dev/null +++ b/spec/factories/member_factory.rb @@ -0,0 +1,8 @@ +FactoryBot.define do + factory :member, class: "CodePraise::Database::MemberOrm" do + origin_id { 11512564 } + username { "XuVic" } + email { "xumingyo@gmail.com" } + initialize_with { CodePraise::Database::MemberOrm.find(origin_id: 11512564) || CodePraise::Database::MemberOrm.create(attributes) } + end +end \ No newline at end of file diff --git a/spec/factories/project_factory.rb b/spec/factories/project_factory.rb new file mode 100644 index 0000000..4110724 --- /dev/null +++ b/spec/factories/project_factory.rb @@ -0,0 +1,20 @@ +require_relative 'member_factory' + +FactoryBot.define do + factory :project, class: "CodePraise::Database::ProjectOrm" do + origin_id { 131723249 } + name {"talkup_api"} + size { 168 } + ssh_url { "git@github.com:PigAndChicken/talkup_api.git" } + http_url { "https://github.com/PigAndChicken/talkup_api.git" } + association :owner, factory: :member + initialize_with { CodePraise::Database::ProjectOrm.find(origin_id: 131723249) || CodePraise::Database::ProjectOrm.create(attributes) } + + factory :project_with_contributor do + after(:create) do |project| + contributor = FactoryBot.create(:member) + project.add_contributor(contributor) + end + end + end +end \ No newline at end of file diff --git a/spec/helpers/spec_helper.rb b/spec/helpers/spec_helper.rb index c1cd95d..e8b3703 100644 --- a/spec/helpers/spec_helper.rb +++ b/spec/helpers/spec_helper.rb @@ -12,7 +12,9 @@ require 'pry' # for debugging +require 'factory_bot' require_relative '../../init.rb' +require_relative '../factories/init' USERNAME = 'soumyaray' PROJECT_NAME = 'YPBT-app' @@ -20,3 +22,6 @@ GITHUB_TOKEN = CodePraise::Api.config.GITHUB_TOKEN CORRECT = YAML.safe_load(File.read('spec/fixtures/gh_results.yml')) +class Minitest::Spec + include FactoryBot::Syntax::Methods +end diff --git a/spec/tests_integration/measurement_integration/commit_level_metrics_spec.rb b/spec/tests_integration/measurement_integration/commit_level_metrics_spec.rb new file mode 100644 index 0000000..d742196 --- /dev/null +++ b/spec/tests_integration/measurement_integration/commit_level_metrics_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require_relative '../../helpers/spec_helper.rb' + +describe 'Test Commit-Level Measurement' do + before do + project = create(:project) + git_repo = CodePraise::GitRepo.new(project, CodePraise::Api.config) + contributions = CodePraise::Mapper::Contributions.new(git_repo) + @commits = contributions.commits + end + + describe 'Entity::Commit' do + it 'should return commit information' do + commit = @commits[0] + _(@commits.size).must_be :>, 0 + _(commit.total_additions).must_be :>, 0 + _(commit.total_deletions).must_be :>, 0 + _(commit.total_files).must_be :>, 0 + end + end + + describe 'Entity::FileChange' do + it 'should return addition, deletion and file information' do + file_change = @commits[0].file_changes[0] + _(file_change.addition).wont_be_nil + _(file_change.deletion).wont_be_nil + _(file_change.path).wont_be_nil + _(file_change.name).wont_be_nil + end + end + +end \ No newline at end of file diff --git a/spec/tests_integration/measurement_integration/file_level_metrics_spec.rb b/spec/tests_integration/measurement_integration/file_level_metrics_spec.rb new file mode 100644 index 0000000..d8681d9 --- /dev/null +++ b/spec/tests_integration/measurement_integration/file_level_metrics_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require_relative '../../helpers/spec_helper.rb' + +describe 'Test File-Level Measurement' do + before do + project = create(:project) + git_repo = CodePraise::GitRepo.new(project, CodePraise::Api.config) + contributions = CodePraise::Mapper::Contributions.new(git_repo) + @folder = contributions.for_folder('') + @file = @folder.files[35] + end + + describe CodePraise::Entity::Complexity do + it 'should return complexity score' do + _(@file.complexity.average).wont_be_nil + _(@file.complexity.methods.keys).wont_be_empty + end + end + + describe CodePraise::Entity::MethodContribution do + it 'should count number of method correctly' do + _(@file.methods.count).must_equal 2 + _(@file.methods.map { |method| method.lines.count }).must_equal [3, 3] + end + end + + describe CodePraise::Mixins::CommentCalculator do + it 'should count the multiline comment' do + _(@file.multiline_comment_count).must_equal 1 + end + + it 'should count the signle line comment' do + _(@file.singleline_comment_count).must_equal 1 + end + end +end diff --git a/spec/tests_unit/rubocop_spec.rb b/spec/tests_unit/rubocop_spec.rb new file mode 100644 index 0000000..1b1c823 --- /dev/null +++ b/spec/tests_unit/rubocop_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require_relative '../helpers/spec_helper.rb' + +JSON_FORMAT_COMMAND = 'rubocop --except Metrics -f j 2>&1' +REPO_PATH = 'app/infrastructure/git/repostore/znjWpkQzvSU8ZnQ82oXCbLVIO6X5L69XkZuDuN6aKaw=' +FILE_PATH = 'Gemfile' + +describe 'Rubucop Module Unit Test' do + describe CodePraise::Rubocop::Command do + it 'should make right rubocop command' do + command = CodePraise::Rubocop::Command.new + .target('/') + .except('Metrics') + .format('json') + .with_stderr_output + .full_command + + _(command).must_equal JSON_FORMAT_COMMAND + end + end + + describe CodePraise::Rubocop::Reporter do + it 'should return rubcop result with hash format' do + rubocop_reporter = CodePraise::Rubocop::Reporter.new(REPO_PATH) + _(rubocop_reporter.report).must_be_kind_of Hash + _(rubocop_reporter.report.keys).wont_be_empty + _(rubocop_reporter.report[FILE_PATH]).wont_be_nil + end + end +end diff --git a/workers/shoryuken_dev.yml b/workers/shoryuken_dev.yml index 9d7d62a..e3a5e5f 100644 --- a/workers/shoryuken_dev.yml +++ b/workers/shoryuken_dev.yml @@ -1,2 +1,2 @@ queues: - - https://sqs.ap-northeast-1.amazonaws.com/503315808870/codepraise-clone-development + - https://sqs.us-east-2.amazonaws.com/700371359912/codepraise-clone-development diff --git a/workers/shoryuken_test.yml b/workers/shoryuken_test.yml index ba59b11..e881152 100644 --- a/workers/shoryuken_test.yml +++ b/workers/shoryuken_test.yml @@ -1,2 +1,2 @@ queues: - - https://sqs.ap-northeast-1.amazonaws.com/503315808870/codepraise-clone-test + - https://sqs.us-east-2.amazonaws.com/700371359912/codepraise-clone-test