# encoding: utf-8 #-- # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . #++ require File.dirname(__FILE__) + '/../test_helper' require "ostruct" class RepositoryTest < ActiveSupport::TestCase def new_repos(opts={}) Repository.new({ :name => "foo", :project => projects(:johans), :user => users(:johan), :owner => users(:johan) }.merge(opts)) end def setup @repository = new_repos FileUtils.mkdir_p(@repository.full_repository_path, :mode => 0755) end should_validate_presence_of :user_id, :name, :owner_id should_validate_uniqueness_of :hashed_path should_validate_uniqueness_of :name, :scoped_to => :project_id, :case_sensitive => false should_have_many :hooks, :dependent => :destroy should " only accept names with alphanum characters in it" do @repository.name = "foo bar" assert !@repository.valid?, 'valid? should be false' @repository.name = "foo!bar" assert !@repository.valid?, 'valid? should be false' @repository.name = "foobar" assert @repository.valid? @repository.name = "foo42" assert @repository.valid? end should "has a unique name within a project" do @repository.save repos = new_repos(:name => "FOO") assert !repos.valid?, 'valid? should be false' assert_not_nil repos.errors.on(:name) assert new_repos(:project => projects(:moes)).valid? end should "not have a reserved name" do repo = new_repos(:name => Gitorious::Reservations.repository_names.first.dup) repo.valid? assert_not_nil repo.errors.on(:name) RepositoriesController.action_methods.each do |action| repo.name = action.dup repo.valid? assert_not_nil repo.errors.on(:name), "fail on #{action}" end end context "git urls" do setup do @host_with_user = "#{GitoriousConfig['gitorious_user']}@#{GitoriousConfig['gitorious_host']}" @host = "#{GitoriousConfig['gitorious_host']}" end should "has a gitdir name" do assert_equal "#{@repository.project.slug}/foo.git", @repository.gitdir end should "has a push url" do assert_equal "#{@host_with_user}:#{@repository.project.slug}/foo.git", @repository.push_url end should "has a clone url" do assert_equal "git://#{@host}/#{@repository.project.slug}/foo.git", @repository.clone_url end should "has a http url" do assert_equal "http://git.#{@host}/#{@repository.project.slug}/foo.git", @repository.http_clone_url end should "has a clone url with the project name, if it's a mainline" do @repository.owner = groups(:team_thunderbird) @repository.kind = Repository::KIND_PROJECT_REPO assert_equal "git://#{@host}/#{@repository.project.slug}/foo.git", @repository.clone_url end should "have a clone url with the team/user, if it's not a mainline" do @repository.kind = Repository::KIND_TEAM_REPO url = "git://#{@host}/#{@repository.owner.to_param_with_prefix}/#{@repository.project.slug}/foo.git" assert_equal url, @repository.clone_url @repository.kind = Repository::KIND_USER_REPO @repository.owner = users(:johan) url = "git://#{@host}/#{users(:johan).to_param_with_prefix}/#{@repository.project.slug}/foo.git" assert_equal url, @repository.clone_url end should "has a push url with the project name, if it's a mainline" do @repository.owner = groups(:team_thunderbird) @repository.kind = Repository::KIND_PROJECT_REPO assert_equal "#{@host_with_user}:#{@repository.project.slug}/foo.git", @repository.push_url end should "have a push url with the team/user, if it's not a mainline" do @repository.owner = groups(:team_thunderbird) @repository.kind = Repository::KIND_TEAM_REPO url = "#{@host_with_user}:#{groups(:team_thunderbird).to_param_with_prefix}/#{@repository.project.slug}/foo.git" assert_equal url, @repository.push_url @repository.kind = Repository::KIND_USER_REPO @repository.owner = users(:johan) url = "#{@host_with_user}:#{users(:johan).to_param_with_prefix}/#{@repository.project.slug}/foo.git" assert_equal url, @repository.push_url end should "has a http clone url with the project name, if it's a mainline" do @repository.owner = groups(:team_thunderbird) @repository.kind = Repository::KIND_PROJECT_REPO assert_equal "http://git.#{@host}/#{@repository.project.slug}/foo.git", @repository.http_clone_url end should "have a http clone url with the team/user, if it's not a mainline" do @repository.owner = groups(:team_thunderbird) @repository.kind = Repository::KIND_TEAM_REPO url = "http://git.#{@host}/#{groups(:team_thunderbird).to_param_with_prefix}/#{@repository.project.slug}/foo.git" assert_equal url, @repository.http_clone_url @repository.owner = users(:johan) @repository.kind = Repository::KIND_USER_REPO url = "http://git.#{@host}/#{users(:johan).to_param_with_prefix}/#{@repository.project.slug}/foo.git" assert_equal url, @repository.http_clone_url end should "has a full repository_path" do expected_dir = File.expand_path(File.join(GitoriousConfig["repository_base_path"], "#{@repository.full_hashed_path}.git")) assert_equal expected_dir, @repository.full_repository_path end end should "inits the git repository" do path = @repository.full_repository_path Repository.git_backend.expects(:create).with(path).returns(true) Repository.create_git_repository(@repository.real_gitdir) assert File.exist?(path), 'File.exist?(path) should be true' Dir.chdir(path) do hooks = File.join(path, "hooks") assert File.exist?(hooks), 'File.exist?(hooks) should be true' assert File.symlink?(hooks), 'File.symlink?(hooks) should be true' assert File.symlink?(File.expand_path(File.readlink(hooks))), 'File.symlink?(File.expand_path(File.readlink(hooks))) should be true' end end should "clones a git repository" do source = repositories(:johans) target = @repository target_path = @repository.full_repository_path git_backend = mock("Git backend") Repository.expects(:git_backend).returns(git_backend) git_backend.expects(:clone).with(target.full_repository_path, source.full_repository_path).returns(true) Repository.expects(:create_hooks).returns(true) assert Repository.clone_git_repository(target.real_gitdir, source.real_gitdir) end should "not create hooks if the :skip_hooks option is set to true" do source = repositories(:johans) target = @repository target_path = @repository.full_repository_path git_backend = mock("Git backend") Repository.expects(:git_backend).returns(git_backend) git_backend.expects(:clone).with(target.full_repository_path, source.full_repository_path).returns(true) Repository.expects(:create_hooks).never Repository.clone_git_repository(target.real_gitdir, source.real_gitdir, :skip_hooks => true) end should " create the hooks" do hooks = "/path/to/hooks" path = "/path/to/repository" base_path = "#{RAILS_ROOT}/data/hooks" File.expects(:join).in_sequence.with(GitoriousConfig["repository_base_path"], ".hooks").returns(hooks) Dir.expects(:chdir).in_sequence.with(path).yields(nil) File.expects(:symlink?).in_sequence.with(hooks).returns(false) File.expects(:exist?).in_sequence.with(hooks).returns(false) FileUtils.expects(:ln_s).in_sequence.with(base_path, hooks) local_hooks = "/path/to/local/hooks" File.expects(:join).in_sequence.with(path, "hooks").returns(local_hooks) File.expects(:exist?).in_sequence.with(local_hooks).returns(true) File.expects(:join).with(path, "description").in_sequence File.expects(:open).in_sequence.returns(true) assert Repository.create_hooks(path) end should "deletes a repository" do Repository.git_backend.expects(:delete!).with(@repository.full_repository_path).returns(true) Repository.delete_git_repository(@repository.real_gitdir) end should "knows if has commits" do @repository.stubs(:new_record?).returns(false) @repository.stubs(:ready?).returns(true) git_mock = mock("Grit::Git") @repository.stubs(:git).returns(git_mock) head = mock("head") head.stubs(:name).returns("master") @repository.git.expects(:heads).returns([head]) assert @repository.has_commits?, '@repository.has_commits? should be true' end should "knows if has commits, unless its a new record" do @repository.stubs(:new_record?).returns(false) assert !@repository.has_commits?, '@repository.has_commits? should be false' end should "knows if has commits, unless its not ready" do @repository.stubs(:ready?).returns(false) assert !@repository.has_commits?, '@repository.has_commits? should be false' end should " build a new repository by cloning another one" do repos = Repository.new_by_cloning(@repository) assert_equal @repository, repos.parent assert_equal @repository.project, repos.project assert repos.new_record?, 'new_record? should be true' end should "inherit merge request inclusion from its parent" do @repository.update_attribute(:merge_requests_enabled, true) clone = Repository.new_by_cloning(@repository) assert clone.merge_requests_enabled? @repository.update_attribute(:merge_requests_enabled, false) clone = Repository.new_by_cloning(@repository) assert !clone.merge_requests_enabled? end should "suggests a decent name for a cloned repository bsed on username" do repos = Repository.new_by_cloning(@repository, username="johan") assert_equal "johans-foo", repos.name repos = Repository.new_by_cloning(@repository, username=nil) assert_equal nil, repos.name end should "has it's name as its to_param value" do @repository.save assert_equal @repository.name, @repository.to_param end should "finds a repository by name or raises" do assert_equal repositories(:johans), Repository.find_by_name!(repositories(:johans).name) assert_raises(ActiveRecord::RecordNotFound) do Repository.find_by_name!("asdasdasd") end end context "find_by_path" do should "finds a repository by its path" do repo = repositories(:johans) path = File.join(GitoriousConfig['repository_base_path'], projects(:johans).slug, "#{repo.name}.git") assert_equal repo, Repository.find_by_path(path) end should_eventually "finds a repository by its path, regardless of repository kind" do repo = projects(:johans).wiki_repository path = File.join(GitoriousConfig['repository_base_path'].chomp("/"), projects(:johans).slug, "#{repo.name}.git") assert_equal repo, Repository.find_by_path(path) end should "finds a group repository by its path" do repo = repositories(:johans) repo.owner = groups(:team_thunderbird) repo.kind = Repository::KIND_TEAM_REPO repo.save! path = File.join(GitoriousConfig['repository_base_path'], repo.gitdir) assert_equal repo, Repository.find_by_path(path) end should "finds a user repository by its path" do repo = repositories(:johans) repo.owner = users(:johan) repo.kind = Repository::KIND_USER_REPO repo.save! path = File.join(GitoriousConfig['repository_base_path'], repo.gitdir) assert_equal repo, Repository.find_by_path(path) end should "scope the find by project id" do repo = repositories(:johans) repo.owner = groups(:team_thunderbird) repo.kind = Repository::KIND_TEAM_REPO repo.save! same_name_repo = new_repos(:name => repo.name) same_name_repo same_name_repo.project = projects(:moes) same_name_repo.owner = groups(:team_thunderbird) same_name_repo.kind = Repository::KIND_TEAM_REPO same_name_repo.save! path = File.join(GitoriousConfig['repository_base_path'], same_name_repo.gitdir) assert_equal same_name_repo, Repository.find_by_path(path) end end context "#to_xml" do should "xmlilizes git paths as well" do assert @repository.to_xml.include?("") assert @repository.to_xml.include?("") end should "include a description of the kind" do assert_match(/mainline<\/kind>/, @repository.to_xml) @repository.kind = Repository::KIND_TEAM_REPO assert_match(/team<\/kind>/, @repository.to_xml) end should "include the project name" do assert_match(/#{@repository.project.slug}<\/project>/, @repository.to_xml) end should "include the owner" do assert_match(/johan<\/owner>/, @repository.to_xml) end end context "#writable_by?" do should "knows if a user can write to self" do @repository.owner = users(:johan) @repository.save! @repository.reload assert @repository.writable_by?(users(:johan)) assert !@repository.writable_by?(users(:mike)) @repository.change_owner_to!(groups(:team_thunderbird)) @repository.save! assert !@repository.writable_by?(users(:johan)) @repository.owner.add_member(users(:moe), Role.member) @repository.committerships.reload assert @repository.writable_by?(users(:moe)) end context "a wiki repository" do setup do @repository.kind = Repository::KIND_WIKI end should "be writable by everyone" do @repository.wiki_permissions = Repository::WIKI_WRITABLE_EVERYONE [:johan, :mike, :moe].each do |login| assert @repository.writable_by?(users(login)), "not writable_by #{login}" end end should "only be writable by project members" do @repository.wiki_permissions = Repository::WIKI_WRITABLE_PROJECT_MEMBERS assert @repository.project.member?(users(:johan)) assert @repository.writable_by?(users(:johan)) assert !@repository.project.member?(users(:moe)) assert !@repository.writable_by?(users(:moe)) end end end should "publishes a message on create and update" do p = proc{ @repository.save! } message = find_message_with_queue_and_regexp('/queue/GitoriousRepositoryCreation', /create_git_repository/) {p.call} assert_equal 'create_git_repository', message['command'] assert_equal 1, message['arguments'].size assert_match(/#{@repository.real_gitdir}$/, message['arguments'].first) assert_equal @repository.id, message['target_id'].to_i end should "publishes a message on clone" do p = proc{ @repository.parent = repositories(:johans) @repository.save! } message = find_message_with_queue_and_regexp('/queue/GitoriousRepositoryCreation', /clone_git_repository/) {p.call} assert_equal "clone_git_repository", message['command'] assert_equal 2, message['arguments'].size assert_match(/#{@repository.real_gitdir}$/, message['arguments'].first) assert_equal @repository.id, message['target_id'] end should "creates a notification on destroy" do @repository.save! message = find_message_with_queue_and_regexp( '/queue/GitoriousRepositoryDeletion', /delete_git_repository/) { @repository.destroy } assert_equal 'delete_git_repository', message['command'] assert_equal 1, message['arguments'].size assert_match(/#{@repository.real_gitdir}$/, message['arguments'].first) end should "has one recent commit" do @repository.save! repos_mock = mock("Git mock") commit_mock = stub_everything("Git::Commit mock") repos_mock.expects(:commits).with("master", 1).returns([commit_mock]) @repository.stubs(:git).returns(repos_mock) @repository.stubs(:has_commits?).returns(true) heads_stub = mock("head") heads_stub.stubs(:name).returns("master") @repository.stubs(:head_candidate).returns(heads_stub) assert_equal commit_mock, @repository.last_commit end should "has one recent commit within a given ref" do @repository.save! repos_mock = mock("Git mock") commit_mock = stub_everything("Git::Commit mock") repos_mock.expects(:commits).with("foo", 1).returns([commit_mock]) @repository.stubs(:git).returns(repos_mock) @repository.stubs(:has_commits?).returns(true) @repository.expects(:head_candidate).never assert_equal commit_mock, @repository.last_commit("foo") end context "deletion" do setup do @repository.kind = Repository::KIND_PROJECT_REPO @repository.project.repositories << new_repos(:name => "another") @repository.save! @repository.committerships.create!({ :committer => users(:moe), :permissions => (Committership::CAN_REVIEW | Committership::CAN_COMMIT) }) end should "be deletable by admins" do assert @repository.admin?(users(:johan)) assert !@repository.admin?(users(:moe)) assert @repository.can_be_deleted_by?(users(:johan)) assert !@repository.can_be_deleted_by?(users(:moe)) end should "always be deletable if admin and non-project repo" do @repository.kind = Repository::KIND_TEAM_REPO assert @repository.can_be_deleted_by?(users(:johan)) assert !@repository.can_be_deleted_by?(users(:moe)) end should "also be deletable by users with admin privs" do @repository.kind = Repository::KIND_TEAM_REPO cs = @repository.committerships.create_with_permissions!({ :committer => users(:mike) }, Committership::CAN_ADMIN) assert @repository.can_be_deleted_by?(users(:johan)) assert @repository.can_be_deleted_by?(users(:mike)) end end should "have a git method that accesses the repository" do # FIXME: meh for stubbing internals, need to refactor that part in Grit File.expects(:exist?).at_least(1).with("#{@repository.full_repository_path}/.git").returns(false) File.expects(:exist?).at_least(1).with(@repository.full_repository_path).returns(true) assert_instance_of Grit::Repo, @repository.git assert_equal @repository.full_repository_path, @repository.git.path end should "have a head_candidate" do head_stub = mock("head") head_stub.stubs(:name).returns("master") git = mock("git backend") @repository.stubs(:git).returns(git) git.expects(:head).returns(head_stub) @repository.expects(:has_commits?).returns(true) assert_equal head_stub, @repository.head_candidate end should "have a head_candidate, unless it doesn't have commits" do @repository.expects(:has_commits?).returns(false) assert_equal nil, @repository.head_candidate end should "has paginated_commits" do git = mock("git") commits = [mock("commit"), mock("commit")] @repository.expects(:git).times(2).returns(git) git.expects(:commit_count).returns(120) git.expects(:commits).with("foo", 30, 30).returns(commits) commits = @repository.paginated_commits("foo", 2, 30) assert_instance_of WillPaginate::Collection, commits end should "has a count_commits_from_last_week_by_user of 0 if no commits" do @repository.expects(:has_commits?).returns(false) assert_equal 0, @repository.count_commits_from_last_week_by_user(users(:johan)) end should "returns a set of users from a list of commits" do commits = [] users(:johan, :moe).map do |u| committer = OpenStruct.new(:email => u.email) commits << OpenStruct.new(:committer => committer, :author => committer) end users = @repository.users_by_commits(commits) assert_equal users(:johan, :moe).map(&:email).sort, users.keys.sort assert_equal users(:johan, :moe).map(&:login).sort, users.values.map(&:login).sort end should "know if it's a normal project repository" do assert @repository.project_repo?, '@repository.project_repo? should be true' end should "know if it's a wiki repo" do @repository.kind = Repository::KIND_WIKI assert @repository.wiki?, '@repository.wiki? should be true' end should "has a parent, which is the owner" do @repository.kind = Repository::KIND_TEAM_REPO @repository.owner = groups(:team_thunderbird) assert_equal groups(:team_thunderbird), @repository.breadcrumb_parent @repository.kind = Repository::KIND_USER_REPO @repository.owner = users(:johan) assert_equal users(:johan), @repository.breadcrumb_parent end should "has a parent, which is the project for mainlines" do @repository.kind = Repository::KIND_PROJECT_REPO @repository.owner = groups(:team_thunderbird) assert_equal projects(:johans), @repository.breadcrumb_parent @repository.owner = users(:johan) assert_equal projects(:johans), @repository.breadcrumb_parent end should " return its name as title" do assert_equal @repository.title, @repository.name end should "return the project title as owner_title if it's a mainline" do @repository.kind = Repository::KIND_PROJECT_REPO assert_equal @repository.project.title, @repository.owner_title end should "return the owner title as owner_title if it's not a mainline" do @repository.kind = Repository::KIND_TEAM_REPO assert_equal @repository.owner.title, @repository.owner_title end should "returns a list of committers depending on owner type" do repo = repositories(:johans2) repo.committerships.each(&:delete) repo.reload assert !repo.committers.include?(users(:moe)) repo.committerships.create_with_permissions!({ :committer => users(:johan) }, Committership::CAN_COMMIT) assert_equal [users(:johan).login], repo.committers.map(&:login) repo.committerships.create_with_permissions!({ :committer => groups(:team_thunderbird) }, Committership::CAN_COMMIT) exp_users = groups(:team_thunderbird).members.unshift(users(:johan)) assert_equal exp_users.map(&:login), repo.committers.map(&:login) groups(:team_thunderbird).add_member(users(:moe), Role.admin) repo.reload assert repo.committers.include?(users(:moe)) end should "know you can request merges from it" do repo = repositories(:johans2) assert !repo.mainline? assert repo.committer?(users(:mike)) assert repo.can_request_merge?(users(:mike)) repo.kind = Repository::KIND_PROJECT_REPO assert repo.mainline? assert !repo.can_request_merge?(users(:mike)), "mainlines shouldn't request merges" end should "sets a hash on create" do assert @repository.new_record?, '@repository.new_record? should be true' @repository.save! assert_not_nil @repository.hashed_path assert_equal 3, @repository.hashed_path.split("/").length assert_match(/[a-z0-9\/]{42}/, @repository.hashed_path) end should "create the initial committership on create for owner" do group_repo = new_repos(:owner => groups(:team_thunderbird)) assert_difference("Committership.count") do group_repo.save! assert_equal 1, group_repo.committerships.count assert_equal groups(:team_thunderbird), group_repo.committerships.first.committer end user_repo = new_repos(:owner => users(:johan), :name => "foo2") assert_difference("Committership.count") do user_repo.save! assert_equal 1, user_repo.committerships.count cs = user_repo.committerships.first assert_equal users(:johan), cs.committer [:reviewer?, :committer?, :admin?].each do |m| assert cs.send(m), "should have #{m} permissions" end end end should "know the full hashed path" do assert_equal @repository.hashed_path, @repository.full_hashed_path end should "allow changing ownership from a user to a group" do repo = repositories(:johans) repo.change_owner_to!(groups(:team_thunderbird)) assert_equal groups(:team_thunderbird), repo.owner repo.change_owner_to!(users(:johan)) assert_equal groups(:team_thunderbird), repo.owner end should "not change kind when it's a project repo and changing owner" do repo = repositories(:johans) repo.change_owner_to!(groups(:team_thunderbird)) assert_equal groups(:team_thunderbird), repo.owner assert_equal Repository::KIND_PROJECT_REPO, repo.kind end should "change kind when changing owner" do repo = repositories(:johans) repo.update_attribute(:kind, Repository::KIND_USER_REPO) assert repo.user_repo? repo.change_owner_to!(groups(:team_thunderbird)) assert_equal groups(:team_thunderbird), repo.owner assert repo.team_repo? end should "changing ownership adds the new owner to the committerships" do repo = repositories(:johans) old_committer = repo.owner repo.change_owner_to!(groups(:team_thunderbird)) assert !repo.committers.include?(old_committer) assert repo.committers.include?(groups(:team_thunderbird).members.first) [:reviewer?, :committer?, :admin?].each do |m| assert repo.committerships.last.send(m), "cannot #{m}" end end should "downcases the name before validation" do repo = new_repos(:name => "FOOBAR") repo.save! assert_equal "foobar", repo.reload.name end should "have a project_or_owner" do repo = repositories(:johans) assert repo.project_repo? assert_equal repo.project, repo.project_or_owner repo.kind = Repository::KIND_TEAM_REPO repo.owner = groups(:team_thunderbird) assert_equal repo.owner, repo.project_or_owner repo.kind = Repository::KIND_TEAM_REPO repo.owner = groups(:team_thunderbird) assert_equal repo.owner, repo.project_or_owner end context "participant groups" do setup do @repo = repositories(:moes) end should "includes the groups' members in #committers" do assert @repo.committers.include?(groups(:team_thunderbird).members.first) end should "only include unique users in #committers" do groups(:team_thunderbird).add_member(users(:moe), Role.member) assert_equal 1, @repo.committers.select{|u| u == users(:moe)}.size end should "not include committerships without a commit permission bit" do assert_equal 1, @repo.committerships.count cs = @repo.committerships.first cs.build_permissions(:review) cs.save! assert_equal [], @repo.committers.map(&:login) end should "return a list of reviewers" do assert !@repo.reviewers.map(&:login).include?(users(:moe).login) @repo.committerships.create_with_permissions!({ :committer => users(:moe) }, Committership::CAN_REVIEW) assert @repo.reviewers.map(&:login).include?(users(:moe).login) end context "permission helpers" do setup do @cs = @repo.committerships.first @cs.permissions = 0 @cs.save! end should "know if a user is a committer" do assert !@repo.committer?(@cs.committer) @cs.build_permissions(:commit); @cs.save assert !@repo.committer?(:false) assert !@repo.committer?(@cs.committer) end should "know if a user is a reviewer" do assert !@repo.reviewer?(@cs.committer) @cs.build_permissions(:review); @cs.save assert !@repo.reviewer?(:false) assert !@repo.reviewer?(@cs.committer) end should "know if a user is a admin" do assert !@repo.admin?(@cs.committer) @cs.build_permissions(:commit, :admin); @cs.save assert !@repo.admin?(:false) assert !@repo.admin?(@cs.committer) end end end context 'owners as User or Group' do setup do @repo = repositories(:moes) end should "return a list of the users who are admin for the repository if owned_by_group?" do @repo.change_owner_to!(groups(:a_team)) assert_equal([users(:johan)], @repo.owners) end should 'not throw an error if transferring ownership to a group if the group is already a committer' do @repo.change_owner_to!(groups(:team_thunderbird)) assert_equal([users(:mike)], @repo.owners) end should 'return the owner if owned by user' do @repo.change_owner_to!(users(:moe)) assert_equal([users(:moe)], @repo.owners) end end should "create an event on create if it's a project repo" do repo = new_repos repo.kind = Repository::KIND_PROJECT_REPO assert_difference("repo.project.events.count") do repo.save! end assert_equal repo, Event.last.target assert_equal Action::ADD_PROJECT_REPOSITORY, Event.last.action end context "find_by_name_in_project" do should "find with a project" do Repository.expects(:find_by_name_and_project_id!).with(repositories(:johans).name, projects(:johans).id).once Repository.find_by_name_in_project!(repositories(:johans).name, projects(:johans)) end should "find without a project" do Repository.expects(:find_by_name!).with(repositories(:johans).name).once Repository.find_by_name_in_project!(repositories(:johans).name) end end context 'Signoff of merge requests' do setup do @project = projects(:johans) @mainline_repository = repositories(:johans) @other_repository = repositories(:johans2) end should 'know that the mainline repository requires signoff of merge requests' do assert @mainline_repository.mainline? assert @mainline_repository.requires_signoff_on_merge_requests? end should 'not require signoff of merge requests in other repositories' do assert !@other_repository.mainline? assert !@other_repository.requires_signoff_on_merge_requests? end end context "Merge request status tags" do setup {@repo = repositories(:johans)} should "have a list of used status tags" do @repo.merge_requests.last.update_attribute(:status_tag, "worksforme") assert_equal %w[open worksforme], @repo.merge_request_status_tags end end context "Thottling" do setup{ Repository.destroy_all } should "throttle on create" do assert_nothing_raised do 5.times{|i| new_repos(:name => "wifebeater#{i}").save! } end assert_no_difference("Repository.count") do assert_raises(RecordThrottling::LimitReachedError) do new_repos(:name => "wtf-are-you-doing-bro").save! end end end end context 'Logging updates' do setup {@repository = repositories(:johans)} should 'generate events for each value that is changed' do assert_incremented_by(@repository.events, :size, 1) do @repository.log_changes_with_user(users(:johan)) do @repository.replace_value(:name, "new_name") end assert @repository.save end assert_equal 'new_name', @repository.reload.name end should 'not generate events when blank values are provided' do assert_incremented_by(@repository.events, :size, 0) do @repository.log_changes_with_user(users(:johan)) do @repository.replace_value(:name, "") end end end should "allow blank updates if we say it's ok" do @repository.update_attribute(:description, "asdf") @repository.log_changes_with_user(users(:johan)) do @repository.replace_value(:description, "", true) end @repository.save! assert @repository.reload.description.blank?, "desc: #{@repository.description.inspect}" end should 'not generate events when invalid values are provided' do assert_incremented_by(@repository.events, :size, 0) do @repository.log_changes_with_user(users(:johan)) do @repository.replace_value(:name, "Some illegal value") end end end should 'not generate events when a value is unchanged' do assert_incremented_by(@repository.events, :size, 0) do @repository.log_changes_with_user(users(:johan)) do @repository.replace_value(:name, @repository.name) end end end end context "changing the HEAD" do setup do @grit = Grit::Repo.new(grit_test_repo("dot_git"), :is_bare => true) @repository.stubs(:git).returns(@grit) end should "change the head" do assert the_head = @grit.get_head("test/master") @grit.expects(:update_head).with(the_head).returns(true) @repository.head = the_head.name end should "not change the head if given a nonexistant ref" do @grit.expects(:update_head).never @repository.head = "non-existant" @repository.head = nil @repository.head = "" end should "change the head, even if the current head is nil" do assert the_head = @grit.get_head("test/master") @grit.expects(:head).returns(nil) @grit.expects(:update_head).with(the_head).returns(true) @repository.head = the_head.name end end context 'Merge request repositories' do setup do @project = Factory.create(:user_project) @main_repo = Factory.create(:repository, :project => @project, :owner => @project.owner, :user => @project.user) end should 'initially not have a merge request repository' do assert !@main_repo.has_tracking_repository? end should 'generate a tracking repository' do @merge_repo = @main_repo.create_tracking_repository assert @main_repo.project_repo? assert @merge_repo.tracking_repo? assert_equal @main_repo, @merge_repo.parent assert_equal @main_repo.owner, @merge_repo.owner assert_equal @main_repo.user, @merge_repo.user assert @main_repo.has_tracking_repository? assert_equal @merge_repo, @main_repo.tracking_repository end should 'not post a repository creation message for merge request repositories' do @merge_repo = @main_repo.build_tracking_repository @merge_repo.expects(:publish).never assert @merge_repo.save end end context "Merge requests enabling" do setup do @repository = Repository.new end should "by default allow merge requests" do assert @repository.merge_requests_enabled? end end context "garbage collection" do setup do @repository = repositories(:johans) @now = Time.now Time.stubs(:now).returns(@now) @repository.stubs(:git).returns(stub()) @repository.git.expects(:gc_auto).returns(true) end should "have a gc! method that updates last_gc_at" do assert_nil @repository.last_gc_at assert @repository.gc! assert_not_nil @repository.last_gc_at assert_equal @now, @repository.last_gc_at end should "set push_count_since_gc to 0 when doing gc" do @repository.push_count_since_gc = 10 @repository.gc! assert_equal 0, @repository.push_count_since_gc end end context "Fresh repositories" do setup do @me = Factory.create(:user, :login => "johnnie") @project = Factory.create(:project, :user => @me, :owner => @me) @repo = Factory.create(:repository, :project => @project, :owner => @project, :user => @me) @users = %w(bill steve jack nellie).map { | login | Factory.create(:user, :login => login) } @user_repos = @users.map do |u| new_repo = Repository.new_by_cloning(@repo) new_repo.name = "#{u.login}s-clone" new_repo.user = u new_repo.owner = u new_repo.kind = Repository::KIND_USER_REPO new_repo.last_pushed_at = 1.hour.ago assert new_repo.save new_repo end end should "include repositories recently pushed to" do assert @project.repositories.by_users.fresh(2).include?(@user_repos.first) end should "not include repositories last pushed to in the middle ages" do older_repo = @user_repos.pop older_repo.last_pushed_at = 500.years.ago older_repo.save assert !@project.repositories.by_users.fresh(2).include?(older_repo) end end context "Searching clones" do setup do @repo = repositories(:johans) @clone = repositories(:johans2) end should "find clones matching an owning group's name" do assert @repo.clones.include?(@clone) assert @repo.search_clones("sproject").include?(@clone) end context "by user name" do setup do @repo = repositories(:moes) @clone = repositories(:johans_moe_clone) users(:johan).update_attribute(:login, "rohan") @clone.update_attribute(:name, "rohans-clone-of-moes") end should "match users with a matching name" do assert_includes(@repo.search_clones("rohan"), @clone) end should "not match user with diverging name" do assert_not_includes(@repo.search_clones("johan"), @clone) end end context "by group name" do setup do @repo = repositories(:johans) @clone = repositories(:johans2) end should "match groups with a matching name" do assert_includes(@repo.search_clones("thunderbird"), @clone) end should "not match groups with diverging name" do assert_not_includes(@repo.search_clones("A-team"), @clone) end end context "by repo name and description" do setup do @repo = repositories(:johans) @clone = repositories(:johans2) end should "match repos with a matching name" do assert_includes(@repo.search_clones("projectrepos"), @clone) end should "not match repos with a different parent" do assert_not_includes(@repo.search_clones("projectrepos"), repositories(:moes)) end end end context "Sequences" do setup do @repository = repositories(:johans) end should "calculate the highest existing sequence number" do assert_equal(@repository.merge_requests.max_by(&:sequence_number).sequence_number, @repository.calculate_highest_merge_request_sequence_number) end should "calculate the number of merge requests" do assert_equal(3, @repository.merge_request_count) end should "be the number of merge requests for a given repo" do assert_equal 3, @repository.merge_requests.size assert_equal 4, @repository.next_merge_request_sequence_number end # 3 merge requests, update one to have seq 4 should "handle taken sequence numbers gracefully" do last_merge_request = @repository.merge_requests.last last_merge_request.update_attribute(:sequence_number, 4) @repository.expects(:calculate_highest_merge_request_sequence_number).returns(99) assert_equal(100, @repository.next_merge_request_sequence_number) end end context "default favoriting" do should "add the owner as a watcher when creating a clone" do user = users(:mike) repo = Repository.new_by_cloning(repositories(:johans), "mike") repo.user = repo.owner = user assert_difference("user.favorites.reload.count") do repo.save! end assert repo.reload.watched_by?(user) end should "not add as watcher if it's an internal repository" do repo = new_repos(:user => users(:moe)) repo.kind = Repository::KIND_TRACKING_REPO assert_no_difference("users(:moe).favorites.count") do repo.save! end end end context "Calculation of disk usage" do setup do @repository = repositories(:johans) @bytes = 90129 end should "save the bytes used" do @repository.expects(:calculate_disk_usage).returns(@bytes) @repository.update_disk_usage assert_equal @bytes, @repository.disk_usage end end context "Pushing" do setup do @repository = repositories(:johans) end should "update last_pushed_at" do @repository.last_pushed_at = 1.hour.ago.utc @repository.register_push assert @repository.last_pushed_at > 1.hour.ago.utc end should "increment the number of pushes" do @repository.push_count_since_gc = 2 @repository.register_push assert_equal 3, @repository.push_count_since_gc end end end