Project has now correct owner and creator. Increased test coverage

This commit is contained in:
Dmitriy Zaporozhets 2013-01-02 19:00:00 +02:00
parent 91995909d9
commit 00a1f5bc2c
15 changed files with 241 additions and 113 deletions

View file

@ -20,7 +20,7 @@ class DashboardController < ApplicationController
@projects = @projects.page(params[:page]).per(30) @projects = @projects.page(params[:page]).per(30)
@events = Event.in_projects(current_user.project_ids) @events = Event.in_projects(current_user.authorized_projects.pluck(:id))
@events = @event_filter.apply_filter(@events) @events = @event_filter.apply_filter(@events)
@events = @events.limit(20).offset(params[:offset] || 0) @events = @events.limit(20).offset(params[:offset] || 0)

View file

@ -70,7 +70,7 @@ class GroupsController < ApplicationController
end end
def projects def projects
@projects ||= group.projects.authorized_for(current_user).sorted_by_activity @projects ||= current_user.authorized_projects.where(namespace_id: group.id).sorted_by_activity
end end
def project_ids def project_ids

View file

@ -32,7 +32,7 @@ class Project < ActiveRecord::Base
attr_accessible :name, :path, :description, :default_branch, :issues_enabled, attr_accessible :name, :path, :description, :default_branch, :issues_enabled,
:wall_enabled, :merge_requests_enabled, :wiki_enabled, as: [:default, :admin] :wall_enabled, :merge_requests_enabled, :wiki_enabled, as: [:default, :admin]
attr_accessible :namespace_id, :owner_id, as: :admin attr_accessible :namespace_id, :creator_id, as: :admin
attr_accessor :error_code attr_accessor :error_code
@ -40,10 +40,10 @@ class Project < ActiveRecord::Base
belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'" belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
belongs_to :namespace belongs_to :namespace
# TODO: replace owner with creator. belongs_to :creator,
# With namespaces a project owner will be a namespace owner class_name: "User",
# so this field makes sense only for global projects foreign_key: "creator_id"
belongs_to :owner, class_name: "User"
has_many :users, through: :users_projects has_many :users, through: :users_projects
has_many :events, dependent: :destroy has_many :events, dependent: :destroy
has_many :merge_requests, dependent: :destroy has_many :merge_requests, dependent: :destroy
@ -62,7 +62,7 @@ class Project < ActiveRecord::Base
delegate :name, to: :owner, allow_nil: true, prefix: true delegate :name, to: :owner, allow_nil: true, prefix: true
# Validations # Validations
validates :owner, presence: true validates :creator, presence: true
validates :description, length: { within: 0..2000 } validates :description, length: { within: 0..2000 }
validates :name, presence: true, length: { within: 0..255 }, validates :name, presence: true, length: { within: 0..255 },
format: { with: Gitlab::Regex.project_name_regex, format: { with: Gitlab::Regex.project_name_regex,
@ -89,8 +89,7 @@ class Project < ActiveRecord::Base
class << self class << self
def authorized_for user def authorized_for user
projects = includes(:users_projects, :namespace) raise "DERECATED"
projects = projects.where("users_projects.user_id = :user_id or projects.owner_id = :user_id or namespaces.owner_id = :user_id", user_id: user.id)
end end
def active def active
@ -104,8 +103,10 @@ class Project < ActiveRecord::Base
def find_with_namespace(id) def find_with_namespace(id)
if id.include?("/") if id.include?("/")
id = id.split("/") id = id.split("/")
namespace_id = Namespace.find_by_path(id.first).id namespace = Namespace.find_by_path(id.first)
where(namespace_id: namespace_id).find_by_path(id.second) return nil unless namespace
where(namespace_id: namespace.id).find_by_path(id.second)
else else
where(path: id, namespace_id: nil).last where(path: id, namespace_id: nil).last
end end
@ -125,7 +126,7 @@ class Project < ActiveRecord::Base
# #
project.path = project.name.dup.parameterize project.path = project.name.dup.parameterize
project.owner = user project.creator = user
# Apply namespace if user has access to it # Apply namespace if user has access to it
# else fallback to user namespace # else fallback to user namespace
@ -174,8 +175,8 @@ class Project < ActiveRecord::Base
end end
def check_limit def check_limit
unless owner.can_create_project? unless creator.can_create_project?
errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it") errors[:base] << ("Your own projects limit is #{creator.projects_limit}! Please contact administrator to increase it")
end end
rescue rescue
errors[:base] << ("Can't check your ability to create project") errors[:base] << ("Can't check your ability to create project")
@ -268,4 +269,12 @@ class Project < ActiveRecord::Base
Notify.project_was_moved_email(member.id).deliver Notify.project_was_moved_email(member.id).deliver
end end
end end
def owner
if namespace
namespace_owner
else
creator
end
end
end end

View file

@ -51,7 +51,6 @@ class User < ActiveRecord::Base
has_many :groups, class_name: "Group", foreign_key: :owner_id has_many :groups, class_name: "Group", foreign_key: :owner_id
has_many :keys, dependent: :destroy has_many :keys, dependent: :destroy
has_many :projects, through: :users_projects
has_many :users_projects, dependent: :destroy has_many :users_projects, dependent: :destroy
has_many :issues, foreign_key: :author_id, dependent: :destroy has_many :issues, foreign_key: :author_id, dependent: :destroy
has_many :notes, foreign_key: :author_id, dependent: :destroy has_many :notes, foreign_key: :author_id, dependent: :destroy
@ -82,6 +81,9 @@ class User < ActiveRecord::Base
scope :active, where(blocked: false) scope :active, where(blocked: false)
scope :alphabetically, order('name ASC') scope :alphabetically, order('name ASC')
#
# Class methods
#
class << self class << self
def filter filter_name def filter filter_name
case filter_name case filter_name
@ -126,9 +128,63 @@ class User < ActiveRecord::Base
end end
end end
#
# Instance methods
#
def generate_password def generate_password
if self.force_random_password if self.force_random_password
self.password = self.password_confirmation = Devise.friendly_token.first(8) self.password = self.password_confirmation = Devise.friendly_token.first(8)
end end
end end
# Namespaces user has access to
def namespaces
namespaces = []
# Add user account namespace
namespaces << self.namespace if self.namespace
# Add groups you can manage
namespaces += if admin
Group.all
else
groups.all
end
namespaces
end
# Groups where user is an owner
def owned_groups
groups
end
# Groups user has access to
def authorized_groups
@authorized_groups ||= begin
groups = Group.where(id: self.authorized_projects.pluck(:namespace_id)).all
groups = groups + self.groups
groups.uniq
end
end
# Projects user has access to
def authorized_projects
project_ids = users_projects.pluck(:project_id)
project_ids = project_ids | owned_projects.pluck(:id)
Project.where(id: project_ids)
end
# Projects in user namespace
def personal_projects
Project.personal(self)
end
# Projects where user is an owner
def owned_projects
Project.where("(projects.namespace_id IN (:namespaces)) OR
(projects.namespace_id IS NULL AND projects.creator_id = :user_id)",
namespaces: namespaces.map(&:id), user_id: self.id)
end
end end

View file

@ -25,7 +25,7 @@ module Account
end end
def can_create_project? def can_create_project?
projects_limit > my_own_projects.count projects_limit > personal_projects.count
end end
def can_create_group? def can_create_group?
@ -56,10 +56,6 @@ module Account
MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id) MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id)
end end
def project_ids
projects.map(&:id)
end
# Remove user from all projects and # Remove user from all projects and
# set blocked attribute to true # set blocked attribute to true
def block def block
@ -86,22 +82,7 @@ module Account
end end
def projects_sorted_by_activity def projects_sorted_by_activity
projects.sorted_by_activity authorized_projects.sorted_by_activity
end
def namespaces
namespaces = []
# Add user account namespace
namespaces << self.namespace if self.namespace
# Add groups you can manage
namespaces += if admin
Group.all
else
groups.all
end
namespaces
end end
def several_namespaces? def several_namespaces?
@ -111,20 +92,4 @@ module Account
def namespace_id def namespace_id
namespace.try :id namespace.try :id
end end
def authorized_groups
@authorized_groups ||= begin
groups = Group.where(id: self.projects.pluck(:namespace_id)).all
groups = groups + self.groups
groups.uniq
end
end
def authorized_projects
Project.authorized_for(self)
end
def my_own_projects
Project.personal(self)
end
end end

View file

@ -50,14 +50,6 @@ module NamespacedProject
namespace.try(:owner) namespace.try(:owner)
end end
def chief
if namespace
namespace_owner
else
owner
end
end
def path_with_namespace def path_with_namespace
if namespace if namespace
namespace.path + '/' + path namespace.path + '/' + path

View file

@ -49,8 +49,8 @@
%b %b
Owned by: Owned by:
%td %td
- if @project.chief - if @project.owner
= link_to @project.chief.name, admin_user_path(@project.chief) = link_to @project.owner_name, admin_user_path(@project.owner)
- else - else
(deleted) (deleted)
%tr %tr
@ -58,7 +58,7 @@
%b %b
Created by: Created by:
%td %td
= @project.owner_name || '(deleted)' = @project.creator.try(:name) || '(deleted)'
%tr %tr
%td %td
%b %b

View file

@ -17,4 +17,4 @@
&rarr; &rarr;
%span.last_activity %span.last_activity
%strong Projects: %strong Projects:
%span= group.projects.authorized_for(current_user).count %span= current_user.authorized_projects.where(namespace_id: group.id).count

View file

@ -0,0 +1,5 @@
class RenameOwnerToCreatorForProject < ActiveRecord::Migration
def change
rename_column :projects, :owner_id, :creator_id
end
end

View file

@ -11,7 +11,7 @@
# #
# It's strongly recommended to check this file into your version control system. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20121219095402) do ActiveRecord::Schema.define(:version => 20130102143055) do
create_table "events", :force => true do |t| create_table "events", :force => true do |t|
t.string "target_type" t.string "target_type"
@ -148,7 +148,7 @@ ActiveRecord::Schema.define(:version => 20121219095402) do
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
t.boolean "private_flag", :default => true, :null => false t.boolean "private_flag", :default => true, :null => false
t.integer "owner_id" t.integer "creator_id"
t.string "default_branch" t.string "default_branch"
t.boolean "issues_enabled", :default => true, :null => false t.boolean "issues_enabled", :default => true, :null => false
t.boolean "wall_enabled", :default => true, :null => false t.boolean "wall_enabled", :default => true, :null => false
@ -157,8 +157,8 @@ ActiveRecord::Schema.define(:version => 20121219095402) do
t.integer "namespace_id" t.integer "namespace_id"
end end
add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
add_index "projects", ["namespace_id"], :name => "index_projects_on_namespace_id" add_index "projects", ["namespace_id"], :name => "index_projects_on_namespace_id"
add_index "projects", ["owner_id"], :name => "index_projects_on_owner_id"
create_table "protected_branches", :force => true do |t| create_table "protected_branches", :force => true do |t|
t.integer "project_id", :null => false t.integer "project_id", :null => false

View file

@ -9,7 +9,7 @@ FactoryGirl.define do
sequence(:url) { Faker::Internet.uri('http') } sequence(:url) { Faker::Internet.uri('http') }
factory :user, aliases: [:author, :assignee, :owner] do factory :user, aliases: [:author, :assignee, :owner, :creator] do
email { Faker::Internet.email } email { Faker::Internet.email }
name name
username { Faker::Internet.user_name } username { Faker::Internet.user_name }
@ -26,7 +26,7 @@ FactoryGirl.define do
factory :project do factory :project do
sequence(:name) { |n| "project#{n}" } sequence(:name) { |n| "project#{n}" }
path { name.downcase.gsub(/\s/, '_') } path { name.downcase.gsub(/\s/, '_') }
owner creator
end end
factory :group do factory :group do

View file

@ -8,7 +8,7 @@ describe Project do
@u1 = create(:user) @u1 = create(:user)
@u2 = create(:user) @u2 = create(:user)
@u3 = create(:user) @u3 = create(:user)
@u4 = @p1.chief @u4 = @p1.owner
@abilities = Six.new @abilities = Six.new
@abilities << Ability @abilities << Ability

View file

@ -24,7 +24,7 @@ describe Project do
describe "Associations" do describe "Associations" do
it { should belong_to(:group) } it { should belong_to(:group) }
it { should belong_to(:namespace) } it { should belong_to(:namespace) }
it { should belong_to(:owner).class_name('User') } it { should belong_to(:creator).class_name('User') }
it { should have_many(:users) } it { should have_many(:users) }
it { should have_many(:events).dependent(:destroy) } it { should have_many(:events).dependent(:destroy) }
it { should have_many(:merge_requests).dependent(:destroy) } it { should have_many(:merge_requests).dependent(:destroy) }
@ -41,7 +41,7 @@ describe Project do
describe "Mass assignment" do describe "Mass assignment" do
it { should_not allow_mass_assignment_of(:namespace_id) } it { should_not allow_mass_assignment_of(:namespace_id) }
it { should_not allow_mass_assignment_of(:owner_id) } it { should_not allow_mass_assignment_of(:creator_id) }
it { should_not allow_mass_assignment_of(:private_flag) } it { should_not allow_mass_assignment_of(:private_flag) }
end end
@ -55,20 +55,15 @@ describe Project do
it { should validate_presence_of(:path) } it { should validate_presence_of(:path) }
it { should validate_uniqueness_of(:path) } it { should validate_uniqueness_of(:path) }
it { should ensure_length_of(:path).is_within(0..255) } it { should ensure_length_of(:path).is_within(0..255) }
# TODO: Formats
it { should ensure_length_of(:description).is_within(0..2000) } it { should ensure_length_of(:description).is_within(0..2000) }
it { should validate_presence_of(:creator) }
# TODO: Formats
it { should validate_presence_of(:owner) }
it { should ensure_inclusion_of(:issues_enabled).in_array([true, false]) } it { should ensure_inclusion_of(:issues_enabled).in_array([true, false]) }
it { should ensure_inclusion_of(:wall_enabled).in_array([true, false]) } it { should ensure_inclusion_of(:wall_enabled).in_array([true, false]) }
it { should ensure_inclusion_of(:merge_requests_enabled).in_array([true, false]) } it { should ensure_inclusion_of(:merge_requests_enabled).in_array([true, false]) }
it { should ensure_inclusion_of(:wiki_enabled).in_array([true, false]) } it { should ensure_inclusion_of(:wiki_enabled).in_array([true, false]) }
it "should not allow new projects beyond user limits" do it "should not allow new projects beyond user limits" do
project.stub(:owner).and_return(double(can_create_project?: false, projects_limit: 1)) project.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 1))
project.should_not be_valid project.should_not be_valid
project.errors[:base].first.should match(/Your own projects limit is 1/) project.errors[:base].first.should match(/Your own projects limit is 1/)
end end
@ -134,7 +129,7 @@ describe Project do
it { should respond_to(:transfer) } it { should respond_to(:transfer) }
it { should respond_to(:name_with_namespace) } it { should respond_to(:name_with_namespace) }
it { should respond_to(:namespace_owner) } it { should respond_to(:namespace_owner) }
it { should respond_to(:chief) } it { should respond_to(:owner) }
it { should respond_to(:path_with_namespace) } it { should respond_to(:path_with_namespace) }
end end
@ -211,4 +206,75 @@ describe Project do
@merge_request.last_commit.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" @merge_request.last_commit.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a"
end end
end end
describe :create_by_user do
before do
@user = create :user
@opts = {
name: "GitLab"
}
end
context 'user namespace' do
before do
@project = Project.create_by_user(@opts, @user)
end
it { @project.should be_valid }
it { @project.owner.should == @user }
it { @project.namespace.should == @user.namespace }
end
context 'user namespace' do
before do
@group = create :group, owner: @user
@opts.merge!(namespace_id: @group.id)
@project = Project.create_by_user(@opts, @user)
end
it { @project.should be_valid }
it { @project.owner.should == @user }
it { @project.namespace.should == @group }
end
end
describe :find_with_namespace do
context 'with namespace' do
before do
@group = create :group, name: 'gitlab'
@project = create(:project, name: 'gitlab-ci', namespace: @group)
end
it { Project.find_with_namespace('gitlab/gitlab-ci').should == @project }
it { Project.find_with_namespace('gitlab-ci').should be_nil }
end
context 'w/o namespace' do
before do
@project = create(:project, name: 'gitlab-ci')
end
it { Project.find_with_namespace('gitlab-ci').should == @project }
it { Project.find_with_namespace('gitlab/gitlab-ci').should be_nil }
end
end
describe :to_param do
context 'with namespace' do
before do
@group = create :group, name: 'gitlab'
@project = create(:project, name: 'gitlab-ci', namespace: @group)
end
it { @project.to_param.should == "gitlab/gitlab-ci" }
end
context 'w/o namespace' do
before do
@project = create(:project, name: 'gitlab-ci')
end
it { @project.to_param.should == "gitlab-ci" }
end
end
end end

View file

@ -39,7 +39,6 @@ describe User do
describe "Associations" do describe "Associations" do
it { should have_one(:namespace) } it { should have_one(:namespace) }
it { should have_many(:users_projects).dependent(:destroy) } it { should have_many(:users_projects).dependent(:destroy) }
it { should have_many(:projects) }
it { should have_many(:groups) } it { should have_many(:groups) }
it { should have_many(:keys).dependent(:destroy) } it { should have_many(:keys).dependent(:destroy) }
it { should have_many(:events).class_name('Event').dependent(:destroy) } it { should have_many(:events).class_name('Event').dependent(:destroy) }
@ -119,4 +118,71 @@ describe User do
user.authentication_token.should_not be_blank user.authentication_token.should_not be_blank
end end
end end
describe 'projects' do
before do
ActiveRecord::Base.observers.enable(:user_observer)
@user = create :user
@project = create :project, namespace: @user.namespace
end
it { @user.authorized_projects.should include(@project) }
it { @user.owned_projects.should include(@project) }
it { @user.personal_projects.should include(@project) }
end
describe 'groups' do
before do
ActiveRecord::Base.observers.enable(:user_observer)
@user = create :user
@group = create :group, owner: @user
end
it { @user.several_namespaces?.should be_true }
it { @user.namespaces.should == [@user.namespace, @group] }
it { @user.authorized_groups.should == [@group] }
it { @user.owned_groups.should == [@group] }
end
describe 'namespaced' do
before do
ActiveRecord::Base.observers.enable(:user_observer)
@user = create :user
@project = create :project, namespace: @user.namespace
end
it { @user.several_namespaces?.should be_false }
it { @user.namespaces.should == [@user.namespace] }
end
describe 'blocking user' do
let(:user) { create(:user, name: 'John Smith') }
it "should block user" do
user.block
user.blocked.should be_true
end
end
describe 'filter' do
before do
@user = create :user
@admin = create :user, admin: true
@blocked = create :user, blocked: true
end
it { User.filter("admins").should == [@admin] }
it { User.filter("blocked").should == [@blocked] }
it { User.filter("wop").should == [@user, @admin, @blocked] }
it { User.filter(nil).should == [@user, @admin] }
end
describe :not_in_project do
before do
@user = create :user
@project = create :project
end
it { User.not_in_project(@project).should == [@user, @project.owner] }
end
end end

View file

@ -10,35 +10,4 @@ describe User, "Account" do
it { user.can_create_project?.should be_true } it { user.can_create_project?.should be_true }
it { user.first_name.should == 'John' } it { user.first_name.should == 'John' }
end end
describe 'blocking user' do
let(:user) { create(:user, name: 'John Smith') }
it "should block user" do
user.block
user.blocked.should be_true
end
end
describe 'projects' do
before do
ActiveRecord::Base.observers.enable(:user_observer)
@user = create :user
@project = create :project, namespace: @user.namespace
end
it { @user.authorized_projects.should include(@project) }
it { @user.my_own_projects.should include(@project) }
end
describe 'namespaced' do
before do
ActiveRecord::Base.observers.enable(:user_observer)
@user = create :user
@project = create :project, namespace: @user.namespace
end
it { @user.several_namespaces?.should be_false }
it { @user.namespaces.should == [@user.namespace] }
end
end end