Feature #34981 » identifiers.diff
app/controllers/application_controller.rb | ||
---|---|---|
325 | 325 |
def find_project(project_id=params[:id]) |
326 | 326 |
@project = Project.find(project_id) |
327 | 327 |
rescue ActiveRecord::RecordNotFound |
328 |
render_404 |
|
328 |
project_alias = Project.find_alias(project_id) |
|
329 |
if @project = project_alias&.project |
|
330 |
flash[:warning] = l(:warning_identifier_renamed, alias: project_alias.identifier, identifier: @project.identifier) |
|
331 |
else |
|
332 |
render_404 |
|
333 |
end |
|
329 | 334 |
end |
330 | 335 | |
331 | 336 |
# Find project of id params[:project_id] |
app/controllers/repositories_controller.rb | ||
---|---|---|
333 | 333 |
REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i |
334 | 334 | |
335 | 335 |
def find_project_repository |
336 |
@project = Project.find(params[:id])
|
|
336 |
return unless find_project
|
|
337 | 337 |
if params[:repository_id].present? |
338 | 338 |
@repository = @project.repositories.find_by_identifier_param(params[:repository_id]) |
339 |
unless @repository |
|
340 |
repository_alias = @project.repositories.find_alias(params[:repository_id]) |
|
341 |
@repository = repository_alias&.repository |
|
342 |
if @repository |
|
343 |
flash[:warning] = l(:warning_identifier_renamed, alias: repository_alias.identifier, identifier: @repository.identifier) |
|
344 |
end |
|
345 |
end |
|
339 | 346 |
else |
340 | 347 |
@repository = @project.repository |
341 | 348 |
end |
app/models/project.rb | ||
---|---|---|
52 | 52 |
has_many :repositories, :dependent => :destroy |
53 | 53 |
has_many :changesets, :through => :repository |
54 | 54 |
has_one :wiki, :dependent => :destroy |
55 |
has_one :project_identifier, :primary_key => 'identifier', :foreign_key => 'identifier' |
|
56 |
has_many :project_identifiers, :dependent => :destroy |
|
55 | 57 |
# Custom field for the project issues |
56 | 58 |
has_and_belongs_to_many :issue_custom_fields, |
57 | 59 |
lambda {order(:position)}, |
... | ... | |
72 | 74 |
:author => nil |
73 | 75 | |
74 | 76 |
validates_presence_of :name, :identifier |
75 |
validates_uniqueness_of :identifier, :if => proc {|p| p.identifier_changed?}, :case_sensitive => true
|
|
77 |
validates :identifier, :if => proc {|p| p.identifier_changed? && p.identifier.present?}, :project_identifier_uniqueness => true
|
|
76 | 78 |
validates_length_of :name, :maximum => 255 |
77 | 79 |
validates_length_of :homepage, :maximum => 255 |
78 | 80 |
validates_length_of :identifier, :maximum => IDENTIFIER_MAX_LENGTH |
79 | 81 |
# downcase letters, digits, dashes but not digits only |
80 | 82 |
validates_format_of :identifier, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/, |
81 | 83 |
:if => proc {|p| p.identifier_changed?} |
84 |
after_validation :reset_invalid_identifier |
|
85 | ||
82 | 86 |
# reserved words |
83 | 87 |
validates_exclusion_of :identifier, :in => %w(new) |
84 | 88 |
validate :validate_parent |
85 | 89 | |
90 |
after_save :update_identifier, |
|
91 |
:if => proc {|project| project.saved_change_to_identifier? && project.identifier.present? && !project.new_record?} |
|
86 | 92 |
after_save :update_inherited_members, |
87 | 93 |
:if => proc {|project| project.saved_change_to_inherit_members?} |
88 | 94 |
after_save :remove_inherited_member_roles, :add_inherited_member_roles, |
... | ... | |
137 | 143 |
end |
138 | 144 |
end |
139 | 145 | |
140 |
def identifier=(identifier) |
|
141 |
super unless identifier_frozen? |
|
142 |
end |
|
143 | ||
144 |
def identifier_frozen? |
|
145 |
errors[:identifier].blank? && !(new_record? || identifier.blank?) |
|
146 |
end |
|
147 | 146 | |
148 | 147 |
# returns latest created projects |
149 | 148 |
# non public projects will be returned only if user is a member of those |
... | ... | |
350 | 349 |
end |
351 | 350 |
end |
352 | 351 | |
352 |
def self.find_alias(*args) |
|
353 |
if args.first && args.first.is_a?(String) && !/^\d*$/.match?(args.first) |
|
354 |
scope = ProjectIdentifier |
|
355 |
scope = scope.where(project_id: current_scope) if scope_attributes? |
|
356 |
scope.find_by_identifier(*args) |
|
357 |
end |
|
358 |
end |
|
359 | ||
353 | 360 |
def self.find_by_param(*args) |
354 | 361 |
self.find(*args) |
355 | 362 |
end |
... | ... | |
993 | 1000 |
end |
994 | 1001 |
end |
995 | 1002 | |
1003 |
def update_identifier |
|
1004 |
self.project_identifiers << project_identifiers.build(:identifier => identifier, :project_id => self.id) |
|
1005 |
end |
|
1006 | ||
996 | 1007 |
def remove_inherited_member_roles |
997 | 1008 |
member_roles = MemberRole.where(:member_id => membership_ids).to_a |
998 | 1009 |
member_role_ids = member_roles.map(&:id) |
... | ... | |
1032 | 1043 |
end |
1033 | 1044 |
end |
1034 | 1045 | |
1046 |
def reset_invalid_identifier |
|
1047 |
if errors[:identifier].present? |
|
1048 |
self.identifier = identifier_was |
|
1049 |
end |
|
1050 |
end |
|
1051 | ||
1035 | 1052 |
# Copies wiki from +project+ |
1036 | 1053 |
def copy_wiki(project) |
1037 | 1054 |
# Check that the source project has a wiki first |
app/models/project_identifier.rb | ||
---|---|---|
1 |
# frozen_string_literal: true |
|
2 | ||
3 |
# Redmine - project management software |
|
4 |
# Copyright (C) 2006-2021 Jean-Philippe Lang |
|
5 |
# |
|
6 |
# This program is free software; you can redistribute it and/or |
|
7 |
# modify it under the terms of the GNU General Public License |
|
8 |
# as published by the Free Software Foundation; either version 2 |
|
9 |
# of the License, or (at your option) any later version. |
|
10 |
# |
|
11 |
# This program is distributed in the hope that it will be useful, |
|
12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
# GNU General Public License for more details. |
|
15 |
# |
|
16 |
# You should have received a copy of the GNU General Public License |
|
17 |
# along with this program; if not, write to the Free Software |
|
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
19 | ||
20 |
class ProjectIdentifier < ActiveRecord::Base |
|
21 |
belongs_to :project |
|
22 | ||
23 |
validates :identifier, :uniqueness => { :case_sensitive => true }, :presence => true |
|
24 |
validates :project_id, :presence => true |
|
25 |
end |
app/models/repository.rb | ||
---|---|---|
29 | 29 |
belongs_to :project |
30 | 30 |
has_many :changesets, lambda{order("#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC")} |
31 | 31 |
has_many :filechanges, :class_name => 'Change', :through => :changesets |
32 |
has_one :repository_identifier, :primary_key => 'identifier', :foreign_key => 'identifier' |
|
33 |
has_many :repository_identifiers, :dependent => :destroy |
|
32 | 34 | |
33 | 35 |
serialize :extra_info |
34 | 36 | |
35 | 37 |
before_validation :normalize_identifier |
36 | 38 |
before_save :check_default |
39 |
after_save :update_identifier, |
|
40 |
:if => proc {|repository| repository.saved_change_to_identifier? && !repository.new_record? && repository.project} |
|
37 | 41 | |
38 | 42 |
# Raw SQL to delete changesets and changes in the database |
39 | 43 |
# has_many :changesets, :dependent => :destroy is too slow for big repositories |
... | ... | |
43 | 47 |
validates_length_of :password, :maximum => 255, :allow_nil => true |
44 | 48 |
validates_length_of :root_url, :url, maximum: 255 |
45 | 49 |
validates_length_of :identifier, :maximum => IDENTIFIER_MAX_LENGTH, :allow_blank => true |
46 |
validates_uniqueness_of :identifier, :scope => :project_id, :case_sensitive => true
|
|
50 |
validates :identifier, :if => proc { |r| r.identifier_changed? }, :repository_identifier_uniqueness => true
|
|
47 | 51 |
validates_exclusion_of :identifier, :in => %w(browse show entry raw changes annotate diff statistics graph revisions revision) |
48 | 52 |
# donwcase letters, digits, dashes, underscores but not digits only |
49 | 53 |
validates_format_of :identifier, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/, :allow_blank => true |
50 | 54 |
# Checks if the SCM is enabled when creating a repository |
51 | 55 |
validate :repo_create_validation, :on => :create |
52 | 56 |
validate :validate_repository_path |
57 |
after_validation :reset_invalid_identifier |
|
53 | 58 | |
54 | 59 |
safe_attributes( |
55 | 60 |
'identifier', |
... | ... | |
124 | 129 |
end |
125 | 130 |
end |
126 | 131 | |
127 |
def identifier=(identifier) |
|
128 |
super unless identifier_frozen? |
|
129 |
end |
|
130 | ||
131 |
def identifier_frozen? |
|
132 |
errors[:identifier].blank? && !(new_record? || identifier.blank?) |
|
133 |
end |
|
134 | ||
135 | 132 |
def identifier_param |
136 | 133 |
if identifier.present? |
137 | 134 |
identifier |
... | ... | |
150 | 147 |
end |
151 | 148 |
end |
152 | 149 | |
150 |
def reset_invalid_identifier |
|
151 |
if errors[:identifier].present? |
|
152 |
self.identifier = identifier_was |
|
153 |
end |
|
154 |
end |
|
155 | ||
153 | 156 |
def self.find_by_identifier_param(param) |
154 | 157 |
if /^\d+$/.match?(param.to_s) |
155 | 158 |
find_by_id(param) |
... | ... | |
158 | 161 |
end |
159 | 162 |
end |
160 | 163 | |
164 |
def self.find_alias(param) |
|
165 |
if !/^\d+$/.match?(param.to_s) |
|
166 |
scope = RepositoryIdentifier |
|
167 |
scope = scope.where(repository_id: current_scope) if scope_attributes? |
|
168 |
scope.find_by_identifier(param) |
|
169 |
end |
|
170 |
end |
|
171 | ||
161 | 172 |
# TODO: should return an empty hash instead of nil to avoid many ||{} |
162 | 173 |
def extra_info |
163 | 174 |
h = read_attribute(:extra_info) |
... | ... | |
481 | 492 |
self.identifier = identifier.to_s.strip |
482 | 493 |
end |
483 | 494 | |
495 |
def update_identifier |
|
496 |
self.repository_identifiers << repository_identifiers.build(:project_id => project.id, :identifier => identifier, :repository_id => self.id) |
|
497 |
end |
|
498 | ||
484 | 499 |
def check_default |
485 | 500 |
if !is_default? && set_as_default? |
486 | 501 |
self.is_default = true |
app/models/repository_identifier.rb | ||
---|---|---|
1 |
# frozen_string_literal: true |
|
2 | ||
3 |
# Redmine - project management software |
|
4 |
# Copyright (C) 2006-2021 Jean-Philippe Lang |
|
5 |
# |
|
6 |
# This program is free software; you can redistribute it and/or |
|
7 |
# modify it under the terms of the GNU General Public License |
|
8 |
# as published by the Free Software Foundation; either version 2 |
|
9 |
# of the License, or (at your option) any later version. |
|
10 |
# |
|
11 |
# This program is distributed in the hope that it will be useful, |
|
12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
# GNU General Public License for more details. |
|
15 |
# |
|
16 |
# You should have received a copy of the GNU General Public License |
|
17 |
# along with this program; if not, write to the Free Software |
|
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
19 | ||
20 |
class RepositoryIdentifier < ActiveRecord::Base |
|
21 |
belongs_to :repository |
|
22 |
belongs_to :project |
|
23 | ||
24 |
validates :identifier, :uniqueness => { :scope => :project_id, :case_sensitive => true } |
|
25 |
validates :repository_id, :presence => true |
|
26 |
end |
app/views/projects/_form.html.erb | ||
---|---|---|
5 | 5 |
<p><%= f.text_field :name, :required => true, :size => 60 %></p> |
6 | 6 | |
7 | 7 |
<p><%= f.text_area :description, :rows => 8, :class => 'wiki-edit' %></p> |
8 |
<p><%= f.text_field :identifier, :required => true, :size => 60, :disabled => @project.identifier_frozen?, :maxlength => Project::IDENTIFIER_MAX_LENGTH %>
|
|
9 |
<% unless @project.identifier_frozen? %>
|
|
8 |
<p> |
|
9 |
<%= f.text_field :identifier, :required => true, :size => 60, :maxlength => Project::IDENTIFIER_MAX_LENGTH %>
|
|
10 | 10 |
<em class="info"><%= l(:text_length_between, :min => 1, :max => Project::IDENTIFIER_MAX_LENGTH) %> <%= l(:text_project_identifier_info).html_safe %></em> |
11 |
<% end %></p>
|
|
11 |
</p> |
|
12 | 12 |
<p><%= f.text_field :homepage, :size => 60 %></p> |
13 | 13 |
<p> |
14 | 14 |
<%= f.check_box :is_public %> |
... | ... | |
44 | 44 |
<% end %> |
45 | 45 |
<!--[eoform:project]--> |
46 | 46 | |
47 |
<% unless @project.identifier_frozen? %> |
|
48 |
<% content_for :header_tags do %> |
|
49 |
<%= javascript_include_tag 'project_identifier' %> |
|
50 |
<% end %> |
|
47 |
<% content_for :header_tags do %> |
|
48 |
<%= javascript_include_tag 'project_identifier' %> |
|
51 | 49 |
<% end %> |
52 | 50 | |
53 | 51 |
<% if !User.current.admin? && @project.inherit_members? && @project.parent && User.current.member_of?(@project.parent) %> |
app/views/repositories/_form.html.erb | ||
---|---|---|
10 | 10 | |
11 | 11 |
<p><%= f.check_box :is_default, :label => :field_repository_is_default %></p> |
12 | 12 |
<p> |
13 |
<%= f.text_field :identifier, :disabled => @repository.identifier_frozen? %> |
|
14 |
<% unless @repository.identifier_frozen? %> |
|
13 |
<%= f.text_field :identifier %> |
|
15 | 14 |
<em class="info"> |
16 | 15 |
<%= l(:text_length_between, :min => 1, :max => Repository::IDENTIFIER_MAX_LENGTH) %> <%= l(:text_repository_identifier_info).html_safe %> |
17 | 16 |
</em> |
18 |
<% end %> |
|
19 | 17 |
</p> |
20 | 18 | |
21 | 19 |
<% button_disabled = true %> |
config/locales/en.yml | ||
---|---|---|
116 | 116 |
too_short: "is too short (minimum is %{count} characters)" |
117 | 117 |
wrong_length: "is the wrong length (should be %{count} characters)" |
118 | 118 |
taken: "has already been taken" |
119 |
used: "has already been used once" |
|
119 | 120 |
not_a_number: "is not a number" |
120 | 121 |
not_a_date: "is not a valid date" |
121 | 122 |
greater_than: "must be greater than %{count}" |
... | ... | |
1199 | 1200 |
text_tip_issue_begin_day: issue beginning this day |
1200 | 1201 |
text_tip_issue_end_day: issue ending this day |
1201 | 1202 |
text_tip_issue_begin_end_day: issue beginning and ending this day |
1202 |
text_project_identifier_info: 'Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.'
|
|
1203 |
text_project_identifier_info: 'Only lower case letters (a-z), numbers, dashes and underscores are allowed.' |
|
1203 | 1204 |
text_caracters_maximum: "%{count} characters maximum." |
1204 | 1205 |
text_caracters_minimum: "Must be at least %{count} characters long." |
1205 | 1206 |
text_characters_must_contain: "Must contain %{character_classes}." |
... | ... | |
1312 | 1313 |
description_all_columns: All Columns |
1313 | 1314 |
description_issue_category_reassign: Choose issue category |
1314 | 1315 |
description_wiki_subpages_reassign: Choose new parent page |
1315 |
text_repository_identifier_info: 'Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.'
|
|
1316 |
text_repository_identifier_info: 'Only lower case letters (a-z), numbers, dashes and underscores are allowed.' |
|
1316 | 1317 |
text_login_required_html: When not requiring authentication, public projects and their contents are openly available on the network. You can <a href="%{anonymous_role_path}">edit the applicable permissions</a>. |
1317 | 1318 |
label_login_required_yes: "Yes" |
1318 | 1319 |
label_login_required_no: "No, allow anonymous access to public projects" |
... | ... | |
1356 | 1357 | |
1357 | 1358 |
text_user_destroy_confirmation: "Are you sure you want to delete this user and remove all references to them? This cannot be undone. Often, locking a user instead of deleting them is the better solution. To confirm, please enter their login (%{login}) below." |
1358 | 1359 |
text_project_destroy_enter_identifier: "To confirm, please enter the project's identifier (%{identifier}) below." |
1360 | ||
1361 |
warning_identifier_renamed: 'The identifier has been renamed from "%{alias}" to "%{identifier}", please change your url' |
db/migrate/20210402143502_create_identifiers.rb | ||
---|---|---|
1 |
class CreateIdentifiers < ActiveRecord::Migration[5.2] |
|
2 |
def change |
|
3 |
create_table :project_identifiers do |t| |
|
4 |
t.references :project, :null => false, :index => true |
|
5 |
t.string :identifier, :null => false, :index => { :unique => true } |
|
6 |
t.timestamps |
|
7 |
end |
|
8 | ||
9 |
create_table :repository_identifiers do |t| |
|
10 |
t.references :project, :null => false |
|
11 |
t.references :repository, :null => false, :index => true |
|
12 |
t.string :identifier, :null => false |
|
13 |
t.timestamps |
|
14 |
end |
|
15 |
add_index :repository_identifiers, [:project_id, :identifier], :unique => true |
|
16 |
end |
|
17 |
end |
db/migrate/20210402153405_migrate_identifiers.rb | ||
---|---|---|
1 |
class MigrateIdentifiers < ActiveRecord::Migration[5.2] |
|
2 |
def up |
|
3 |
Project.where.not(identifier: nil).find_each do |project| |
|
4 |
ProjectIdentifier.create!(project: project, identifier: project.identifier) |
|
5 |
end |
|
6 | ||
7 |
Repository.where.not(identifier: nil).find_each do |repository| |
|
8 |
RepositoryIdentifier.create!(project: repository.project, respository: repository, identifier: repository.identifier) |
|
9 |
end |
|
10 |
end |
|
11 |
end |
lib/redmine/core_ext/active_record.rb | ||
---|---|---|
27 | 27 |
end |
28 | 28 |
end |
29 | 29 |
end |
30 | ||
31 |
class IdentifierUniquenessValidator < ActiveRecord::Validations::UniquenessValidator |
|
32 |
include Redmine::I18n |
|
33 | ||
34 |
def initialize(options) |
|
35 |
options[:case_sensitive] = true |
|
36 |
super |
|
37 |
end |
|
38 |
end |
|
39 | ||
40 |
class ProjectIdentifierUniquenessValidator < IdentifierUniquenessValidator |
|
41 |
def validate_each(record, attribute, value) |
|
42 |
super |
|
43 |
if record.errors[attribute].empty? |
|
44 |
identifier = ProjectIdentifier.new |
|
45 |
ProjectIdentifier.validators_on(:identifier).each do |validator| |
|
46 |
validator.validate_each(identifier, :identifier, value) |
|
47 |
end |
|
48 |
if identifier.errors.any? |
|
49 |
record.errors.add attribute, l(:used, :scope => [:activerecord, :errors, :messages]) |
|
50 |
end |
|
51 |
end |
|
52 |
end |
|
53 |
end |
|
54 | ||
55 |
class RepositoryIdentifierUniquenessValidator < IdentifierUniquenessValidator |
|
56 |
def initialize(options) |
|
57 |
options[:scope] = :project_id |
|
58 |
super |
|
59 |
end |
|
60 | ||
61 |
def validate_each(record, attribute, value) |
|
62 |
super |
|
63 |
if record.errors[attribute].empty? |
|
64 |
identifier = RepositoryIdentifier.new(project: record.project) |
|
65 |
RepositoryIdentifier.validators_on(:identifier).each do |validator| |
|
66 |
validator.validate_each(identifier, :identifier, value) |
|
67 |
end |
|
68 |
if identifier.errors.any? |
|
69 |
record.errors.add attribute, l(:used, :scope => [:activerecord, :errors, :messages]) |
|
70 |
end |
|
71 |
end |
|
72 |
end |
|
73 |
end |
test/functional/issues_controller_test.rb | ||
---|---|---|
119 | 119 |
end |
120 | 120 |
end |
121 | 121 | |
122 |
def test_index_find_previous_project_identifier |
|
123 |
Project.where(:id => 1).update_all(:identifier => 'new_identifier') |
|
124 |
ProjectIdentifier.create(project_id: 1, identifier: 'previous_identifier') |
|
125 | ||
126 |
get(:index, :params => {:project_id => 'new_identifier'}) |
|
127 |
assert_response :success |
|
128 |
assert_select '.flash.warning', :count => 0 |
|
129 | ||
130 |
get(:index, :params => {:project_id => 'previous_identifier'}) |
|
131 |
assert_response :success |
|
132 |
assert_select '.flash.warning', :text => I18n.t(:warning_identifier_renamed, alias: 'previous_identifier', identifier: 'new_identifier') |
|
133 | ||
134 |
get(:index, :params => {:project_id => 'unknown_identifier'}) |
|
135 |
assert_response 404 |
|
136 |
end |
|
137 | ||
122 | 138 |
def test_index_should_list_issues_of_closed_subprojects |
123 | 139 |
@request.session[:user_id] = 1 |
124 | 140 |
project = Project.find(1) |
test/functional/repositories_controller_test.rb | ||
---|---|---|
236 | 236 |
assert_select 'table.changesets' |
237 | 237 |
end |
238 | 238 | |
239 |
def test_revisions_with_previous_repository_identifier |
|
240 |
repository = Repository::Subversion.create!(:project_id => 1, :identifier => 'new_identifier', :url => 'file:///foo') |
|
241 |
RepositoryIdentifier.create(project_id: 1, identifier: 'previous_identifier', repository_id: repository.id) |
|
242 |
get( |
|
243 |
:revisions, |
|
244 |
:params => { |
|
245 |
:id => 1, |
|
246 |
:repository_id => 'new_identifier' |
|
247 |
} |
|
248 |
) |
|
249 |
assert_response :success |
|
250 |
assert_select '.flash.warning', :count => 0 |
|
251 | ||
252 |
get( |
|
253 |
:revisions, |
|
254 |
:params => { |
|
255 |
:id => 1, |
|
256 |
:repository_id => 'previous_identifier' |
|
257 |
} |
|
258 |
) |
|
259 |
assert_response :success |
|
260 |
assert_select '.flash.warning', :text => I18n.t(:warning_identifier_renamed, alias: 'previous_identifier', identifier: 'new_identifier') |
|
261 | ||
262 |
get( |
|
263 |
:revisions, |
|
264 |
:params => { |
|
265 |
:id => 1, |
|
266 |
:repository_id => 'unknown_identifier' |
|
267 |
} |
|
268 |
) |
|
269 |
assert_response 404 |
|
270 |
end |
|
271 | ||
239 | 272 |
def test_revisions_for_other_repository |
240 | 273 |
repository = Repository::Subversion.create!(:project_id => 1, :identifier => 'foo', :url => 'file:///foo') |
241 | 274 |
get( |
test/unit/project_test.rb | ||
---|---|---|
132 | 132 |
end |
133 | 133 |
end |
134 | 134 | |
135 |
def test_identifier_should_not_be_frozen_for_a_new_project |
|
136 |
assert_equal false, Project.new.identifier_frozen? |
|
137 |
end |
|
138 | ||
139 |
def test_identifier_should_not_be_frozen_for_a_saved_project_with_blank_identifier |
|
140 |
Project.where(:id => 1).update_all(["identifier = ''"]) |
|
141 |
assert_equal false, Project.find(1).identifier_frozen? |
|
135 |
def test_identifier_should_validate_used_identifiers |
|
136 |
p = Project.find(1) |
|
137 |
p.identifier = 'test' |
|
138 |
assert p.save |
|
139 |
p.identifier = 'test2' |
|
140 |
assert p.save |
|
141 |
p.identifier = 'test' |
|
142 |
assert !p.save |
|
143 |
assert p.errors['identifier'].present? |
|
142 | 144 |
end |
143 | 145 | |
144 |
def test_identifier_should_be_frozen_for_a_saved_project_with_valid_identifier |
|
145 |
assert_equal true, Project.find(1).identifier_frozen? |
|
146 |
end |
|
147 | 146 | |
148 | 147 |
def test_to_param_should_be_nil_for_new_records |
149 | 148 |
project = Project.new |
test/unit/repository_git_test.rb | ||
---|---|---|
46 | 46 |
end |
47 | 47 | |
48 | 48 |
def test_nondefault_repo_with_blank_identifier_destruction |
49 |
Repository.delete_all
|
|
49 |
Repository.destroy_all
|
|
50 | 50 | |
51 | 51 |
repo1 = |
52 | 52 |
Repository::Git.new( |
test/unit/repository_test.rb | ||
---|---|---|
114 | 114 |
assert !r.save |
115 | 115 |
end |
116 | 116 | |
117 |
def test_identifier_should_validate_used_identifiers |
|
118 |
r = Repository::Subversion.new(:project_id => 3, :identifier => 'test', :url => 'file:///bar') |
|
119 |
assert r.save |
|
120 |
r.identifier = 'test2' |
|
121 |
assert r.save |
|
122 |
r.identifier = 'test' |
|
123 |
assert !r.save |
|
124 |
assert r.errors['identifier'].present? |
|
125 |
end |
|
126 | ||
127 |
def test_identifier_should_allow_same_identifier_on_multiple_projects |
|
128 |
r = Repository::Subversion.new(:project_id => 3, :identifier => 'test', :url => 'file:///bar') |
|
129 |
assert r.save |
|
130 |
r = Repository::Subversion.new(:project_id => 4, :identifier => 'test', :url => 'file:///bar') |
|
131 |
assert r.save |
|
132 |
end |
|
133 | ||
117 | 134 |
def test_first_repository_should_be_set_as_default |
118 | 135 |
repository1 = |
119 | 136 |
Repository::Subversion. |
... | ... | |
179 | 196 |
assert r.save |
180 | 197 |
end |
181 | 198 | |
182 |
def test_identifier_should_not_be_frozen_for_a_new_repository |
|
183 |
assert_equal false, Repository.new.identifier_frozen? |
|
184 |
end |
|
185 | ||
186 |
def test_identifier_should_not_be_frozen_for_a_saved_repository_with_blank_identifier |
|
187 |
Repository.where(:id => 10).update_all(["identifier = ''"]) |
|
188 |
assert_equal false, Repository.find(10).identifier_frozen? |
|
189 |
end |
|
190 | ||
191 |
def test_identifier_should_be_frozen_for_a_saved_repository_with_valid_identifier |
|
192 |
Repository.where(:id => 10).update_all(["identifier = 'abc123'"]) |
|
193 |
assert_equal true, Repository.find(10).identifier_frozen? |
|
194 |
end |
|
195 | ||
196 |
def test_identifier_should_not_accept_change_if_frozen |
|
197 |
r = Repository.new(:identifier => 'foo') |
|
198 |
r.stubs(:identifier_frozen?).returns(true) |
|
199 | ||
200 |
r.identifier = 'bar' |
|
201 |
assert_equal 'foo', r.identifier |
|
202 |
end |
|
203 | ||
204 |
def test_identifier_should_accept_change_if_not_frozen |
|
205 |
r = Repository.new(:identifier => 'foo') |
|
206 |
r.stubs(:identifier_frozen?).returns(false) |
|
207 | ||
208 |
r.identifier = 'bar' |
|
209 |
assert_equal 'bar', r.identifier |
|
210 |
end |
|
211 | 199 | |
212 | 200 |
def test_destroy |
213 | 201 |
repository = Repository.find(10) |