Skip to content

自動テストの追加 #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions app/models/repository/github.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def branches
scm.branches
end

# リモートリポジトリの最新状況を取得し、changesetsに反映する
def fetch_changesets(options = {})
opts = options.merge({
last_committed_date: extra_info&.send(:[], "last_committed_date"),
Expand All @@ -56,6 +57,8 @@ def fetch_changesets(options = {})
save(validate: false)
end

# revisionオブジェクトの配列を引数に受け取る
# changesetsテーブルに保存する(すでに保存済みのrevisionはスキップ)
def save_revisions!(revisions, revisions_copy)
limit = 100
offset = 0
Expand Down Expand Up @@ -96,13 +99,18 @@ def save_revisions!(revisions, revisions_copy)
end
private :save_revisions!

# nameにコミットのSHA1ハッシュを受け取る
# nameにリビジョン名が一致する、もしくはscmidに先頭一致するchangeset一件を返す
def find_changeset_by_name(name)
if name.present?
changesets.find_by(revision: name.to_s) ||
changesets.where('scmid LIKE ?', "#{name}%").first
end
end

# pathにファイルパス、identifierにコミットのshaを受け取る
# scmから引数に該当するエントリを取得し配列で返す
# pathが空(ルート)でidentifierがdefault_branchの場合は、ファイルセットの参照時キャッシュを使用する
def scm_entries(path=nil, identifier=nil)
is_using_cache = using_root_fileset_cache?(path, identifier)

Expand All @@ -120,9 +128,9 @@ def scm_entries(path=nil, identifier=nil)
path: fileset.path,
kind: fileset.size.blank? ? 'dir': 'file',
size: fileset.size,
author: latest_changeset.committer,
lastrev: Redmine::Scm::Adapters::Revision.new(
identifier: latest_changeset.identifier,
author: latest_changeset.committer,
time: latest_changeset.committed_on
),
)
Expand All @@ -132,7 +140,6 @@ def scm_entries(path=nil, identifier=nil)
# Not found in cache, get entries from SCM
if entries.blank?
entries = scm.entries(path, identifier, :report_last_commit => report_last_commit)

# Save as cache
if changeset.present?
GithubAdapterRootFileset.where(repository_id: self.id, revision: identifier).delete_all
Expand All @@ -152,6 +159,9 @@ def scm_entries(path=nil, identifier=nil)
entries
end

# pathにファイルパス、revにコミットのshaもしくはブランチ名を受け取る
# rev時点のpath以下のファイルに該当するchengesetsを取得し配列で返す
# revがデフォルトブランチ以外の場合、未反映のrevisionを保存しchangesetsテーブルに保存する
def latest_changesets(path, rev, limit = 10)
revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)

Expand All @@ -165,6 +175,8 @@ def latest_changesets(path, rev, limit = 10)
changesets.where(:scmid => revisions.map {|c| c.scmid}).to_a
end

# このリポジトリが削除された場合のクリーンアップ処理
# 紐づくGithubAdapterRootFilesetを削除する
def clear_changesets
super
GithubAdapterRootFileset.where(repository_id: self.id).delete_all
Expand All @@ -177,6 +189,8 @@ def default_branch
def properties(path, rev)
end

# scm_entries内でキャッシュを使用するかどうかの判定
# pathがルートで、identifierがデフォルトブランチ(またはデフォルトブランチのSHA1ハッシュ)の場合、true を返す
def using_root_fileset_cache?(path, identifier)
return false if path.present?
return false if identifier.blank?
Expand Down
35 changes: 35 additions & 0 deletions dot.github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby

name: Run plugin Test

on: push

jobs:
test:
runs-on: ubuntu-latest
container:
image: redmine:4.2.10-bullseye
env:
RAILS_ENV: test
steps:
- name: Setup Env
run: |
ln -s /usr/src/redmine ./redmine
git config --global --add safe.directory /usr/src/redmine/plugins/redmine_github_adapter
- uses: actions/checkout@v3
with:
path: ./redmine/plugins/redmine_github_adapter
- name: Run tests
run: |
cd redmine
/docker-entrypoint.sh rails s --help
bundle config unset without --local
bundle install
bundle exec rake redmine:plugins:migrate
bundle exec rake redmine:plugins:test RAILS_ENV=test

38 changes: 33 additions & 5 deletions lib/redmine/scm/adapters/github_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ def initialize(url, root_url=nil, login=nil, password=nil, path_encoding=nil)
end
end

# レシーバに紐づくGithubBranchオブジェクトを配列にし、ブランチ名でソートして返す
# 対象となるGithubBranchオブジェクトが存在しない場合、空の配列を返す
def branches
return @branches if @branches
@branches = []
Expand All @@ -41,6 +43,11 @@ def branches
raise CommandFailed, handle_octokit_error(e)
end

# pathにファイル・ディレクトリのパス, identifierにコミットのSHAを受け取る
# identifierに該当するコミットの、path以下に含まれる各ファイルのエントリを配列にして返す
# pathが空の場合rootディレクトリを、identifierが空の場合HEADコミットを対象とする
# 対象となるファイルが存在しない場合、空の配列を返す
# report_last_commitオプションにtrueが与えられた場合、各エントリにファイルの最新コミット情報を含める
def entries(path=nil, identifier=nil, options={})
identifier = 'HEAD' if identifier.nil?

Expand All @@ -67,12 +74,17 @@ def entries(path=nil, identifier=nil, options={})
raise CommandFailed, handle_octokit_error(e)
end

# revにコミットのSHAもしくはブランチ名を受け取る
# 引数に該当するコミットをすべて取得し、その中で最新コミットのSHAを返す
def revision_to_sha(rev)
Octokit.commits(@repos, rev, { per_page: 1 }).map(&:sha).first
rescue Octokit::Error => e
raise CommandFailed, handle_octokit_error(e)
end

# pathにファイル・ディレクトリのパス、revにコミットのSHAもしくはブランチ名を受け取る
# revに該当するコミット以前でpath以下に変更があった最新のコミットを取得し、Revisionオブジェクトとして返す
# 引数pathが与えられなかった場合、もしくは該当するコミットが存在しない場合nilを返す
def lastrev(path, rev)
return if path.nil?

Expand All @@ -92,16 +104,20 @@ def lastrev(path, rev)
raise CommandFailed, handle_octokit_error(e)
end

# pathにtreeオブジェクトのshaを受け取る
# treeの最上位に位置するファイル・ディレクトリ名を返す
def get_path_name(path)

Octokit.commits(@repos).map {|c|
Octokit.tree(@repos, c.commit.tree.sha).tree.map{|b| [b.sha, b.path] }
}.flatten.each_slice(2).to_h[path]

rescue Octokit::Error => e
raise CommandFailed, handle_octokit_error(e)
end

# pathにファイル・ディレクトリのパス, identifier_from/identifier_toにコミットのshaを受け取る
# 引数で与えられた条件に合致するコミットをRevisionオブジェクトの配列として返す
# allオプションがtrueの場合、リポジトリの全てのrevisionを取得する
# 配列はコミット日時の降順でソートされる
def revisions(path, identifier_from, identifier_to, options={})
path ||= ''
revs = Revisions.new
Expand All @@ -116,7 +132,6 @@ def revisions(path, identifier_from, identifier_to, options={})
0.step do |i|
start_page = i * MAX_PAGES + 1
github_commits = Octokit.commits(@repos, api_opts.merge(page: start_page))

# if fetched latest commit, github_commits.length is 1, and github_commits[0][:sha] == latest_committed_id
return [] if i == 0 && github_commits.none?{ |commit| commit.sha != options[:last_committed_id] }

Expand Down Expand Up @@ -165,6 +180,8 @@ def revisions(path, identifier_from, identifier_to, options={})
raise CommandFailed, handle_octokit_error(e)
end

# 複数のrevisionオブジェクトを配列として受け取り、該当コミットの変更状況をhashにする
# 作成したhashを各revisionオブジェクトのpathsパラメータに追記する
def get_filechanges_and_append_to(revisions)
revisions.each do |revision|
commit_diff = Octokit.commit(@repos, revision.identifier)
Expand All @@ -191,6 +208,9 @@ def get_filechanges_and_append_to(revisions)
raise CommandFailed, handle_octokit_error(e)
end

# pathにファイルのパス, identifier_from/identifier_toにコミットのSHAを受け取る
# pathで指定されたファイル内容について、identifier~で指定されたコミット間の差分箇所を特定する
# 特定した差分箇所に追記を行い、文字列の配列として返す
def diff(path, identifier_from, identifier_to=nil)
path ||= ''
diff = []
Expand Down Expand Up @@ -245,6 +265,8 @@ def annotate(path, identifier=nil)
nil
end

# デフォルトブランチ名を文字列にして返す
# ブランチが1つも無い場合nilを返す
def default_branch
return if branches.blank?

Expand All @@ -255,13 +277,17 @@ def default_branch
).to_s
end

# pathにファイル・ディレクトリのパス, identifierにコミットのSHAを受け取る
# identifierに該当するコミットの、pathに指定されたファイル・ディレクトリのエントリを返す
# identifierが空の場合、HEADコミットを対象にする
# pathが空の場合、トップレベルのエントリを返す
def entry(path=nil, identifier=nil)
identifier ||= 'HEAD'
if path.blank?
# Root entry
Entry.new(:path => '', :kind => 'dir')
else
es = entries(path, identifier, {report_last_commit: true })
es = entries(path, identifier, {report_last_commit: false })
content = es&.find {|e| e.name == path} || es&.first

Entry.new({
Expand All @@ -273,12 +299,15 @@ def entry(path=nil, identifier=nil)
end
end

# identifierにSHAが該当するコミットの、pathに指定されたファイルの内容を文字列で返す
# identifierが空の場合、HEADコミットを対象にする
def cat(path, identifier=nil)
identifier = 'HEAD' if identifier.nil?

begin
blob = Octokit.contents(@repos, path: path, ref: identifier)
url = blob.download_url

rescue Octokit::NotFound
commit = Octokit.commit(@repos, identifier).files.select{|c| c.filename == path }.first
blob = Octokit.blob(@repos, commit.sha)
Expand All @@ -287,7 +316,6 @@ def cat(path, identifier=nil)
Octokit.get(url)
content_type = Octokit.last_response.headers['content-type'].slice(/charset=.+$/)&.gsub("charset=", "")
return '' if content_type == "binary" || content_type.nil?

content = blob.encoding == "base64" ? Base64.decode64(blob.content) : blob.content
content.force_encoding 'utf-8'

Expand Down
10 changes: 10 additions & 0 deletions test/fixtures/changesets.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
default:
id: 1
repository_id: 1
revision: 'shashasha'
committer: 'author_name'
committed_on: 2023-01-01
comments: 'message'
commit_date: 2023-01-01
scmid: 'shashasha'
user_id:
14 changes: 14 additions & 0 deletions test/fixtures/repositories.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
default:
id: 1
project_id: 1
url: 'https://github.com/farend/redmine_github_repo.git'
login: ''
password: ''
identifier: 'test_project'
type: 'Repository::Github'
path_encoding: nil
log_encoding: nil
is_default: true
extra_info:
'last_committed_date': ''
'last_committed_id': ''
17 changes: 17 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
# Load the Redmine helper
require 'minitest/mock'
require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper')

module Redmine
module PluginFixturesLoader
def self.included(base)
base.class_eval do
def self.plugin_fixtures(*symbols)
ActiveRecord::FixtureSet.create_fixtures(File.dirname(__FILE__) + '/fixtures/', symbols)
end
end
end
end
end

## ユニットテスト
unless ActiveSupport::TestCase.included_modules.include?(Redmine::PluginFixturesLoader)
ActiveSupport::TestCase.send :include, Redmine::PluginFixturesLoader
end
Loading