##// END OF EJS Templates
Fixed error on commits per month graph (#10806)....
Jean-Philippe Lang -
r9427:38011c0fb11b
parent child
Show More
@@ -1,426 +1,426
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'SVG/Graph/Bar'
18 require 'SVG/Graph/Bar'
19 require 'SVG/Graph/BarHorizontal'
19 require 'SVG/Graph/BarHorizontal'
20 require 'digest/sha1'
20 require 'digest/sha1'
21 require 'redmine/scm/adapters/abstract_adapter'
21 require 'redmine/scm/adapters/abstract_adapter'
22
22
23 class ChangesetNotFound < Exception; end
23 class ChangesetNotFound < Exception; end
24 class InvalidRevisionParam < Exception; end
24 class InvalidRevisionParam < Exception; end
25
25
26 class RepositoriesController < ApplicationController
26 class RepositoriesController < ApplicationController
27 menu_item :repository
27 menu_item :repository
28 menu_item :settings, :only => [:new, :create, :edit, :update, :destroy, :committers]
28 menu_item :settings, :only => [:new, :create, :edit, :update, :destroy, :committers]
29 default_search_scope :changesets
29 default_search_scope :changesets
30
30
31 before_filter :find_project_by_project_id, :only => [:new, :create]
31 before_filter :find_project_by_project_id, :only => [:new, :create]
32 before_filter :find_repository, :only => [:edit, :update, :destroy, :committers]
32 before_filter :find_repository, :only => [:edit, :update, :destroy, :committers]
33 before_filter :find_project_repository, :except => [:new, :create, :edit, :update, :destroy, :committers]
33 before_filter :find_project_repository, :except => [:new, :create, :edit, :update, :destroy, :committers]
34 before_filter :find_changeset, :only => [:revision, :add_related_issue, :remove_related_issue]
34 before_filter :find_changeset, :only => [:revision, :add_related_issue, :remove_related_issue]
35 before_filter :authorize
35 before_filter :authorize
36 accept_rss_auth :revisions
36 accept_rss_auth :revisions
37
37
38 rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
38 rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
39
39
40 def new
40 def new
41 scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first
41 scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first
42 @repository = Repository.factory(scm)
42 @repository = Repository.factory(scm)
43 @repository.is_default = @project.repository.nil?
43 @repository.is_default = @project.repository.nil?
44 @repository.project = @project
44 @repository.project = @project
45 render :layout => !request.xhr?
45 render :layout => !request.xhr?
46 end
46 end
47
47
48 def create
48 def create
49 @repository = Repository.factory(params[:repository_scm], params[:repository])
49 @repository = Repository.factory(params[:repository_scm], params[:repository])
50 @repository.project = @project
50 @repository.project = @project
51 if request.post? && @repository.save
51 if request.post? && @repository.save
52 redirect_to settings_project_path(@project, :tab => 'repositories')
52 redirect_to settings_project_path(@project, :tab => 'repositories')
53 else
53 else
54 render :action => 'new'
54 render :action => 'new'
55 end
55 end
56 end
56 end
57
57
58 def edit
58 def edit
59 end
59 end
60
60
61 def update
61 def update
62 @repository.attributes = params[:repository]
62 @repository.attributes = params[:repository]
63 @repository.project = @project
63 @repository.project = @project
64 if request.put? && @repository.save
64 if request.put? && @repository.save
65 redirect_to settings_project_path(@project, :tab => 'repositories')
65 redirect_to settings_project_path(@project, :tab => 'repositories')
66 else
66 else
67 render :action => 'edit'
67 render :action => 'edit'
68 end
68 end
69 end
69 end
70
70
71 def committers
71 def committers
72 @committers = @repository.committers
72 @committers = @repository.committers
73 @users = @project.users
73 @users = @project.users
74 additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
74 additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
75 @users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
75 @users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
76 @users.compact!
76 @users.compact!
77 @users.sort!
77 @users.sort!
78 if request.post? && params[:committers].is_a?(Hash)
78 if request.post? && params[:committers].is_a?(Hash)
79 # Build a hash with repository usernames as keys and corresponding user ids as values
79 # Build a hash with repository usernames as keys and corresponding user ids as values
80 @repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
80 @repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
81 flash[:notice] = l(:notice_successful_update)
81 flash[:notice] = l(:notice_successful_update)
82 redirect_to settings_project_path(@project, :tab => 'repositories')
82 redirect_to settings_project_path(@project, :tab => 'repositories')
83 end
83 end
84 end
84 end
85
85
86 def destroy
86 def destroy
87 @repository.destroy if request.delete?
87 @repository.destroy if request.delete?
88 redirect_to settings_project_path(@project, :tab => 'repositories')
88 redirect_to settings_project_path(@project, :tab => 'repositories')
89 end
89 end
90
90
91 def show
91 def show
92 @repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
92 @repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
93
93
94 @entries = @repository.entries(@path, @rev)
94 @entries = @repository.entries(@path, @rev)
95 @changeset = @repository.find_changeset_by_name(@rev)
95 @changeset = @repository.find_changeset_by_name(@rev)
96 if request.xhr?
96 if request.xhr?
97 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
97 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
98 else
98 else
99 (show_error_not_found; return) unless @entries
99 (show_error_not_found; return) unless @entries
100 @changesets = @repository.latest_changesets(@path, @rev)
100 @changesets = @repository.latest_changesets(@path, @rev)
101 @properties = @repository.properties(@path, @rev)
101 @properties = @repository.properties(@path, @rev)
102 @repositories = @project.repositories
102 @repositories = @project.repositories
103 render :action => 'show'
103 render :action => 'show'
104 end
104 end
105 end
105 end
106
106
107 alias_method :browse, :show
107 alias_method :browse, :show
108
108
109 def changes
109 def changes
110 @entry = @repository.entry(@path, @rev)
110 @entry = @repository.entry(@path, @rev)
111 (show_error_not_found; return) unless @entry
111 (show_error_not_found; return) unless @entry
112 @changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
112 @changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
113 @properties = @repository.properties(@path, @rev)
113 @properties = @repository.properties(@path, @rev)
114 @changeset = @repository.find_changeset_by_name(@rev)
114 @changeset = @repository.find_changeset_by_name(@rev)
115 end
115 end
116
116
117 def revisions
117 def revisions
118 @changeset_count = @repository.changesets.count
118 @changeset_count = @repository.changesets.count
119 @changeset_pages = Paginator.new self, @changeset_count,
119 @changeset_pages = Paginator.new self, @changeset_count,
120 per_page_option,
120 per_page_option,
121 params['page']
121 params['page']
122 @changesets = @repository.changesets.find(:all,
122 @changesets = @repository.changesets.find(:all,
123 :limit => @changeset_pages.items_per_page,
123 :limit => @changeset_pages.items_per_page,
124 :offset => @changeset_pages.current.offset,
124 :offset => @changeset_pages.current.offset,
125 :include => [:user, :repository, :parents])
125 :include => [:user, :repository, :parents])
126
126
127 respond_to do |format|
127 respond_to do |format|
128 format.html { render :layout => false if request.xhr? }
128 format.html { render :layout => false if request.xhr? }
129 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
129 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
130 end
130 end
131 end
131 end
132
132
133 def entry
133 def entry
134 @entry = @repository.entry(@path, @rev)
134 @entry = @repository.entry(@path, @rev)
135 (show_error_not_found; return) unless @entry
135 (show_error_not_found; return) unless @entry
136
136
137 # If the entry is a dir, show the browser
137 # If the entry is a dir, show the browser
138 (show; return) if @entry.is_dir?
138 (show; return) if @entry.is_dir?
139
139
140 @content = @repository.cat(@path, @rev)
140 @content = @repository.cat(@path, @rev)
141 (show_error_not_found; return) unless @content
141 (show_error_not_found; return) unless @content
142 if 'raw' == params[:format] ||
142 if 'raw' == params[:format] ||
143 (@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) ||
143 (@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) ||
144 ! is_entry_text_data?(@content, @path)
144 ! is_entry_text_data?(@content, @path)
145 # Force the download
145 # Force the download
146 send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
146 send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
147 send_type = Redmine::MimeType.of(@path)
147 send_type = Redmine::MimeType.of(@path)
148 send_opt[:type] = send_type.to_s if send_type
148 send_opt[:type] = send_type.to_s if send_type
149 send_data @content, send_opt
149 send_data @content, send_opt
150 else
150 else
151 # Prevent empty lines when displaying a file with Windows style eol
151 # Prevent empty lines when displaying a file with Windows style eol
152 # TODO: UTF-16
152 # TODO: UTF-16
153 # Is this needs? AttachmentsController reads file simply.
153 # Is this needs? AttachmentsController reads file simply.
154 @content.gsub!("\r\n", "\n")
154 @content.gsub!("\r\n", "\n")
155 @changeset = @repository.find_changeset_by_name(@rev)
155 @changeset = @repository.find_changeset_by_name(@rev)
156 end
156 end
157 end
157 end
158
158
159 def is_entry_text_data?(ent, path)
159 def is_entry_text_data?(ent, path)
160 # UTF-16 contains "\x00".
160 # UTF-16 contains "\x00".
161 # It is very strict that file contains less than 30% of ascii symbols
161 # It is very strict that file contains less than 30% of ascii symbols
162 # in non Western Europe.
162 # in non Western Europe.
163 return true if Redmine::MimeType.is_type?('text', path)
163 return true if Redmine::MimeType.is_type?('text', path)
164 # Ruby 1.8.6 has a bug of integer divisions.
164 # Ruby 1.8.6 has a bug of integer divisions.
165 # http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F
165 # http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F
166 return false if ent.is_binary_data?
166 return false if ent.is_binary_data?
167 true
167 true
168 end
168 end
169 private :is_entry_text_data?
169 private :is_entry_text_data?
170
170
171 def annotate
171 def annotate
172 @entry = @repository.entry(@path, @rev)
172 @entry = @repository.entry(@path, @rev)
173 (show_error_not_found; return) unless @entry
173 (show_error_not_found; return) unless @entry
174
174
175 @annotate = @repository.scm.annotate(@path, @rev)
175 @annotate = @repository.scm.annotate(@path, @rev)
176 if @annotate.nil? || @annotate.empty?
176 if @annotate.nil? || @annotate.empty?
177 (render_error l(:error_scm_annotate); return)
177 (render_error l(:error_scm_annotate); return)
178 end
178 end
179 ann_buf_size = 0
179 ann_buf_size = 0
180 @annotate.lines.each do |buf|
180 @annotate.lines.each do |buf|
181 ann_buf_size += buf.size
181 ann_buf_size += buf.size
182 end
182 end
183 if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte
183 if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte
184 (render_error l(:error_scm_annotate_big_text_file); return)
184 (render_error l(:error_scm_annotate_big_text_file); return)
185 end
185 end
186 @changeset = @repository.find_changeset_by_name(@rev)
186 @changeset = @repository.find_changeset_by_name(@rev)
187 end
187 end
188
188
189 def revision
189 def revision
190 respond_to do |format|
190 respond_to do |format|
191 format.html
191 format.html
192 format.js {render :layout => false}
192 format.js {render :layout => false}
193 end
193 end
194 end
194 end
195
195
196 # Adds a related issue to a changeset
196 # Adds a related issue to a changeset
197 # POST /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues
197 # POST /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues
198 def add_related_issue
198 def add_related_issue
199 @issue = @changeset.find_referenced_issue_by_id(params[:issue_id])
199 @issue = @changeset.find_referenced_issue_by_id(params[:issue_id])
200 if @issue && (!@issue.visible? || @changeset.issues.include?(@issue))
200 if @issue && (!@issue.visible? || @changeset.issues.include?(@issue))
201 @issue = nil
201 @issue = nil
202 end
202 end
203
203
204 if @issue
204 if @issue
205 @changeset.issues << @issue
205 @changeset.issues << @issue
206 respond_to do |format|
206 respond_to do |format|
207 format.js {
207 format.js {
208 render :update do |page|
208 render :update do |page|
209 page.replace_html "related-issues", :partial => "related_issues"
209 page.replace_html "related-issues", :partial => "related_issues"
210 page.visual_effect :highlight, "related-issue-#{@issue.id}"
210 page.visual_effect :highlight, "related-issue-#{@issue.id}"
211 end
211 end
212 }
212 }
213 end
213 end
214 else
214 else
215 respond_to do |format|
215 respond_to do |format|
216 format.js {
216 format.js {
217 render :update do |page|
217 render :update do |page|
218 page.alert(l(:label_issue) + ' ' + l('activerecord.errors.messages.invalid'))
218 page.alert(l(:label_issue) + ' ' + l('activerecord.errors.messages.invalid'))
219 end
219 end
220 }
220 }
221 end
221 end
222 end
222 end
223 end
223 end
224
224
225 # Removes a related issue from a changeset
225 # Removes a related issue from a changeset
226 # DELETE /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues/:issue_id
226 # DELETE /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues/:issue_id
227 def remove_related_issue
227 def remove_related_issue
228 @issue = Issue.visible.find_by_id(params[:issue_id])
228 @issue = Issue.visible.find_by_id(params[:issue_id])
229 if @issue
229 if @issue
230 @changeset.issues.delete(@issue)
230 @changeset.issues.delete(@issue)
231 end
231 end
232
232
233 respond_to do |format|
233 respond_to do |format|
234 format.js {
234 format.js {
235 render :update do |page|
235 render :update do |page|
236 page.remove "related-issue-#{@issue.id}"
236 page.remove "related-issue-#{@issue.id}"
237 end if @issue
237 end if @issue
238 }
238 }
239 end
239 end
240 end
240 end
241
241
242 def diff
242 def diff
243 if params[:format] == 'diff'
243 if params[:format] == 'diff'
244 @diff = @repository.diff(@path, @rev, @rev_to)
244 @diff = @repository.diff(@path, @rev, @rev_to)
245 (show_error_not_found; return) unless @diff
245 (show_error_not_found; return) unless @diff
246 filename = "changeset_r#{@rev}"
246 filename = "changeset_r#{@rev}"
247 filename << "_r#{@rev_to}" if @rev_to
247 filename << "_r#{@rev_to}" if @rev_to
248 send_data @diff.join, :filename => "#{filename}.diff",
248 send_data @diff.join, :filename => "#{filename}.diff",
249 :type => 'text/x-patch',
249 :type => 'text/x-patch',
250 :disposition => 'attachment'
250 :disposition => 'attachment'
251 else
251 else
252 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
252 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
253 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
253 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
254
254
255 # Save diff type as user preference
255 # Save diff type as user preference
256 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
256 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
257 User.current.pref[:diff_type] = @diff_type
257 User.current.pref[:diff_type] = @diff_type
258 User.current.preference.save
258 User.current.preference.save
259 end
259 end
260 @cache_key = "repositories/diff/#{@repository.id}/" +
260 @cache_key = "repositories/diff/#{@repository.id}/" +
261 Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}")
261 Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}")
262 unless read_fragment(@cache_key)
262 unless read_fragment(@cache_key)
263 @diff = @repository.diff(@path, @rev, @rev_to)
263 @diff = @repository.diff(@path, @rev, @rev_to)
264 show_error_not_found unless @diff
264 show_error_not_found unless @diff
265 end
265 end
266
266
267 @changeset = @repository.find_changeset_by_name(@rev)
267 @changeset = @repository.find_changeset_by_name(@rev)
268 @changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
268 @changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
269 @diff_format_revisions = @repository.diff_format_revisions(@changeset, @changeset_to)
269 @diff_format_revisions = @repository.diff_format_revisions(@changeset, @changeset_to)
270 end
270 end
271 end
271 end
272
272
273 def stats
273 def stats
274 end
274 end
275
275
276 def graph
276 def graph
277 data = nil
277 data = nil
278 case params[:graph]
278 case params[:graph]
279 when "commits_per_month"
279 when "commits_per_month"
280 data = graph_commits_per_month(@repository)
280 data = graph_commits_per_month(@repository)
281 when "commits_per_author"
281 when "commits_per_author"
282 data = graph_commits_per_author(@repository)
282 data = graph_commits_per_author(@repository)
283 end
283 end
284 if data
284 if data
285 headers["Content-Type"] = "image/svg+xml"
285 headers["Content-Type"] = "image/svg+xml"
286 send_data(data, :type => "image/svg+xml", :disposition => "inline")
286 send_data(data, :type => "image/svg+xml", :disposition => "inline")
287 else
287 else
288 render_404
288 render_404
289 end
289 end
290 end
290 end
291
291
292 private
292 private
293
293
294 def find_repository
294 def find_repository
295 @repository = Repository.find(params[:id])
295 @repository = Repository.find(params[:id])
296 @project = @repository.project
296 @project = @repository.project
297 rescue ActiveRecord::RecordNotFound
297 rescue ActiveRecord::RecordNotFound
298 render_404
298 render_404
299 end
299 end
300
300
301 REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
301 REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
302
302
303 def find_project_repository
303 def find_project_repository
304 @project = Project.find(params[:id])
304 @project = Project.find(params[:id])
305 if params[:repository_id].present?
305 if params[:repository_id].present?
306 @repository = @project.repositories.find_by_identifier_param(params[:repository_id])
306 @repository = @project.repositories.find_by_identifier_param(params[:repository_id])
307 else
307 else
308 @repository = @project.repository
308 @repository = @project.repository
309 end
309 end
310 (render_404; return false) unless @repository
310 (render_404; return false) unless @repository
311 @path = params[:path].is_a?(Array) ? params[:path].join('/') : params[:path].to_s
311 @path = params[:path].is_a?(Array) ? params[:path].join('/') : params[:path].to_s
312 @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip
312 @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip
313 @rev_to = params[:rev_to]
313 @rev_to = params[:rev_to]
314
314
315 unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE)
315 unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE)
316 if @repository.branches.blank?
316 if @repository.branches.blank?
317 raise InvalidRevisionParam
317 raise InvalidRevisionParam
318 end
318 end
319 end
319 end
320 rescue ActiveRecord::RecordNotFound
320 rescue ActiveRecord::RecordNotFound
321 render_404
321 render_404
322 rescue InvalidRevisionParam
322 rescue InvalidRevisionParam
323 show_error_not_found
323 show_error_not_found
324 end
324 end
325
325
326 def find_changeset
326 def find_changeset
327 if @rev.present?
327 if @rev.present?
328 @changeset = @repository.find_changeset_by_name(@rev)
328 @changeset = @repository.find_changeset_by_name(@rev)
329 end
329 end
330 show_error_not_found unless @changeset
330 show_error_not_found unless @changeset
331 end
331 end
332
332
333 def show_error_not_found
333 def show_error_not_found
334 render_error :message => l(:error_scm_not_found), :status => 404
334 render_error :message => l(:error_scm_not_found), :status => 404
335 end
335 end
336
336
337 # Handler for Redmine::Scm::Adapters::CommandFailed exception
337 # Handler for Redmine::Scm::Adapters::CommandFailed exception
338 def show_error_command_failed(exception)
338 def show_error_command_failed(exception)
339 render_error l(:error_scm_command_failed, exception.message)
339 render_error l(:error_scm_command_failed, exception.message)
340 end
340 end
341
341
342 def graph_commits_per_month(repository)
342 def graph_commits_per_month(repository)
343 @date_to = Date.today
343 @date_to = Date.today
344 @date_from = @date_to << 11
344 @date_from = @date_to << 11
345 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
345 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
346 commits_by_day = Changeset.count(
346 commits_by_day = Changeset.count(
347 :all, :group => :commit_date,
347 :all, :group => :commit_date,
348 :conditions => ["repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
348 :conditions => ["repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
349 commits_by_month = [0] * 12
349 commits_by_month = [0] * 12
350 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
350 commits_by_day.each {|c| commits_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
351
351
352 changes_by_day = Change.count(
352 changes_by_day = Change.count(
353 :all, :group => :commit_date, :include => :changeset,
353 :all, :group => :commit_date, :include => :changeset,
354 :conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
354 :conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
355 changes_by_month = [0] * 12
355 changes_by_month = [0] * 12
356 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
356 changes_by_day.each {|c| changes_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
357
357
358 fields = []
358 fields = []
359 12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
359 12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
360
360
361 graph = SVG::Graph::Bar.new(
361 graph = SVG::Graph::Bar.new(
362 :height => 300,
362 :height => 300,
363 :width => 800,
363 :width => 800,
364 :fields => fields.reverse,
364 :fields => fields.reverse,
365 :stack => :side,
365 :stack => :side,
366 :scale_integers => true,
366 :scale_integers => true,
367 :step_x_labels => 2,
367 :step_x_labels => 2,
368 :show_data_values => false,
368 :show_data_values => false,
369 :graph_title => l(:label_commits_per_month),
369 :graph_title => l(:label_commits_per_month),
370 :show_graph_title => true
370 :show_graph_title => true
371 )
371 )
372
372
373 graph.add_data(
373 graph.add_data(
374 :data => commits_by_month[0..11].reverse,
374 :data => commits_by_month[0..11].reverse,
375 :title => l(:label_revision_plural)
375 :title => l(:label_revision_plural)
376 )
376 )
377
377
378 graph.add_data(
378 graph.add_data(
379 :data => changes_by_month[0..11].reverse,
379 :data => changes_by_month[0..11].reverse,
380 :title => l(:label_change_plural)
380 :title => l(:label_change_plural)
381 )
381 )
382
382
383 graph.burn
383 graph.burn
384 end
384 end
385
385
386 def graph_commits_per_author(repository)
386 def graph_commits_per_author(repository)
387 commits_by_author = Changeset.count(:all, :group => :committer, :conditions => ["repository_id = ?", repository.id])
387 commits_by_author = Changeset.count(:all, :group => :committer, :conditions => ["repository_id = ?", repository.id])
388 commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
388 commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
389
389
390 changes_by_author = Change.count(:all, :group => :committer, :include => :changeset, :conditions => ["#{Changeset.table_name}.repository_id = ?", repository.id])
390 changes_by_author = Change.count(:all, :group => :committer, :include => :changeset, :conditions => ["#{Changeset.table_name}.repository_id = ?", repository.id])
391 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
391 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
392
392
393 fields = commits_by_author.collect {|r| r.first}
393 fields = commits_by_author.collect {|r| r.first}
394 commits_data = commits_by_author.collect {|r| r.last}
394 commits_data = commits_by_author.collect {|r| r.last}
395 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
395 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
396
396
397 fields = fields + [""]*(10 - fields.length) if fields.length<10
397 fields = fields + [""]*(10 - fields.length) if fields.length<10
398 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
398 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
399 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
399 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
400
400
401 # Remove email adress in usernames
401 # Remove email adress in usernames
402 fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
402 fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
403
403
404 graph = SVG::Graph::BarHorizontal.new(
404 graph = SVG::Graph::BarHorizontal.new(
405 :height => 400,
405 :height => 400,
406 :width => 800,
406 :width => 800,
407 :fields => fields,
407 :fields => fields,
408 :stack => :side,
408 :stack => :side,
409 :scale_integers => true,
409 :scale_integers => true,
410 :show_data_values => false,
410 :show_data_values => false,
411 :rotate_y_labels => false,
411 :rotate_y_labels => false,
412 :graph_title => l(:label_commits_per_author),
412 :graph_title => l(:label_commits_per_author),
413 :show_graph_title => true
413 :show_graph_title => true
414 )
414 )
415 graph.add_data(
415 graph.add_data(
416 :data => commits_data,
416 :data => commits_data,
417 :title => l(:label_revision_plural)
417 :title => l(:label_revision_plural)
418 )
418 )
419 graph.add_data(
419 graph.add_data(
420 :data => changes_data,
420 :data => changes_data,
421 :title => l(:label_change_plural)
421 :title => l(:label_change_plural)
422 )
422 )
423 graph.burn
423 graph.burn
424 end
424 end
425 end
425 end
426
426
@@ -1,259 +1,264
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19 require 'repositories_controller'
19 require 'repositories_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class RepositoriesController; def rescue_action(e) raise e end; end
22 class RepositoriesController; def rescue_action(e) raise e end; end
23
23
24 class RepositoriesControllerTest < ActionController::TestCase
24 class RepositoriesControllerTest < ActionController::TestCase
25 fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules,
25 fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules,
26 :repositories, :issues, :issue_statuses, :changesets, :changes,
26 :repositories, :issues, :issue_statuses, :changesets, :changes,
27 :issue_categories, :enumerations, :custom_fields, :custom_values, :trackers
27 :issue_categories, :enumerations, :custom_fields, :custom_values, :trackers
28
28
29 def setup
29 def setup
30 @controller = RepositoriesController.new
30 @controller = RepositoriesController.new
31 @request = ActionController::TestRequest.new
31 @request = ActionController::TestRequest.new
32 @response = ActionController::TestResponse.new
32 @response = ActionController::TestResponse.new
33 User.current = nil
33 User.current = nil
34 end
34 end
35
35
36 def test_new
36 def test_new
37 @request.session[:user_id] = 1
37 @request.session[:user_id] = 1
38 get :new, :project_id => 'subproject1'
38 get :new, :project_id => 'subproject1'
39 assert_response :success
39 assert_response :success
40 assert_template 'new'
40 assert_template 'new'
41 assert_kind_of Repository::Subversion, assigns(:repository)
41 assert_kind_of Repository::Subversion, assigns(:repository)
42 assert assigns(:repository).new_record?
42 assert assigns(:repository).new_record?
43 assert_tag 'input', :attributes => {:name => 'repository[url]'}
43 assert_tag 'input', :attributes => {:name => 'repository[url]'}
44 end
44 end
45
45
46 def test_new_should_propose_enabled_scm_only
46 def test_new_should_propose_enabled_scm_only
47 @request.session[:user_id] = 1
47 @request.session[:user_id] = 1
48 with_settings :enabled_scm => ['Mercurial', 'Git'] do
48 with_settings :enabled_scm => ['Mercurial', 'Git'] do
49 get :new, :project_id => 'subproject1'
49 get :new, :project_id => 'subproject1'
50 end
50 end
51 assert_response :success
51 assert_response :success
52 assert_template 'new'
52 assert_template 'new'
53 assert_kind_of Repository::Mercurial, assigns(:repository)
53 assert_kind_of Repository::Mercurial, assigns(:repository)
54 assert_tag 'select', :attributes => {:name => 'repository_scm'},
54 assert_tag 'select', :attributes => {:name => 'repository_scm'},
55 :children => {:count => 3}
55 :children => {:count => 3}
56 assert_tag 'select', :attributes => {:name => 'repository_scm'},
56 assert_tag 'select', :attributes => {:name => 'repository_scm'},
57 :child => {:tag => 'option', :attributes => {:value => 'Mercurial', :selected => 'selected'}}
57 :child => {:tag => 'option', :attributes => {:value => 'Mercurial', :selected => 'selected'}}
58 assert_tag 'select', :attributes => {:name => 'repository_scm'},
58 assert_tag 'select', :attributes => {:name => 'repository_scm'},
59 :child => {:tag => 'option', :attributes => {:value => 'Git', :selected => nil}}
59 :child => {:tag => 'option', :attributes => {:value => 'Git', :selected => nil}}
60 end
60 end
61
61
62 def test_create
62 def test_create
63 @request.session[:user_id] = 1
63 @request.session[:user_id] = 1
64 assert_difference 'Repository.count' do
64 assert_difference 'Repository.count' do
65 post :create, :project_id => 'subproject1',
65 post :create, :project_id => 'subproject1',
66 :repository_scm => 'Subversion',
66 :repository_scm => 'Subversion',
67 :repository => {:url => 'file:///test', :is_default => '1', :identifier => ''}
67 :repository => {:url => 'file:///test', :is_default => '1', :identifier => ''}
68 end
68 end
69 assert_response 302
69 assert_response 302
70 repository = Repository.first(:order => 'id DESC')
70 repository = Repository.first(:order => 'id DESC')
71 assert_kind_of Repository::Subversion, repository
71 assert_kind_of Repository::Subversion, repository
72 assert_equal 'file:///test', repository.url
72 assert_equal 'file:///test', repository.url
73 end
73 end
74
74
75 def test_create_with_failure
75 def test_create_with_failure
76 @request.session[:user_id] = 1
76 @request.session[:user_id] = 1
77 assert_no_difference 'Repository.count' do
77 assert_no_difference 'Repository.count' do
78 post :create, :project_id => 'subproject1',
78 post :create, :project_id => 'subproject1',
79 :repository_scm => 'Subversion',
79 :repository_scm => 'Subversion',
80 :repository => {:url => 'invalid'}
80 :repository => {:url => 'invalid'}
81 end
81 end
82 assert_response :success
82 assert_response :success
83 assert_template 'new'
83 assert_template 'new'
84 assert_kind_of Repository::Subversion, assigns(:repository)
84 assert_kind_of Repository::Subversion, assigns(:repository)
85 assert assigns(:repository).new_record?
85 assert assigns(:repository).new_record?
86 end
86 end
87
87
88 def test_edit
88 def test_edit
89 @request.session[:user_id] = 1
89 @request.session[:user_id] = 1
90 get :edit, :id => 11
90 get :edit, :id => 11
91 assert_response :success
91 assert_response :success
92 assert_template 'edit'
92 assert_template 'edit'
93 assert_equal Repository.find(11), assigns(:repository)
93 assert_equal Repository.find(11), assigns(:repository)
94 assert_tag 'input', :attributes => {:name => 'repository[url]', :value => 'svn://localhost/test'}
94 assert_tag 'input', :attributes => {:name => 'repository[url]', :value => 'svn://localhost/test'}
95 end
95 end
96
96
97 def test_update
97 def test_update
98 @request.session[:user_id] = 1
98 @request.session[:user_id] = 1
99 put :update, :id => 11, :repository => {:password => 'test_update'}
99 put :update, :id => 11, :repository => {:password => 'test_update'}
100 assert_response 302
100 assert_response 302
101 assert_equal 'test_update', Repository.find(11).password
101 assert_equal 'test_update', Repository.find(11).password
102 end
102 end
103
103
104 def test_update_with_failure
104 def test_update_with_failure
105 @request.session[:user_id] = 1
105 @request.session[:user_id] = 1
106 put :update, :id => 11, :repository => {:password => 'x'*260}
106 put :update, :id => 11, :repository => {:password => 'x'*260}
107 assert_response :success
107 assert_response :success
108 assert_template 'edit'
108 assert_template 'edit'
109 assert_equal Repository.find(11), assigns(:repository)
109 assert_equal Repository.find(11), assigns(:repository)
110 end
110 end
111
111
112 def test_destroy
112 def test_destroy
113 @request.session[:user_id] = 1
113 @request.session[:user_id] = 1
114 assert_difference 'Repository.count', -1 do
114 assert_difference 'Repository.count', -1 do
115 delete :destroy, :id => 11
115 delete :destroy, :id => 11
116 end
116 end
117 assert_response 302
117 assert_response 302
118 assert_nil Repository.find_by_id(11)
118 assert_nil Repository.find_by_id(11)
119 end
119 end
120
120
121 def test_revisions
121 def test_revisions
122 get :revisions, :id => 1
122 get :revisions, :id => 1
123 assert_response :success
123 assert_response :success
124 assert_template 'revisions'
124 assert_template 'revisions'
125 assert_equal Repository.find(10), assigns(:repository)
125 assert_equal Repository.find(10), assigns(:repository)
126 assert_not_nil assigns(:changesets)
126 assert_not_nil assigns(:changesets)
127 end
127 end
128
128
129 def test_revisions_for_other_repository
129 def test_revisions_for_other_repository
130 repository = Repository::Subversion.create!(:project_id => 1, :identifier => 'foo', :url => 'file:///foo')
130 repository = Repository::Subversion.create!(:project_id => 1, :identifier => 'foo', :url => 'file:///foo')
131
131
132 get :revisions, :id => 1, :repository_id => 'foo'
132 get :revisions, :id => 1, :repository_id => 'foo'
133 assert_response :success
133 assert_response :success
134 assert_template 'revisions'
134 assert_template 'revisions'
135 assert_equal repository, assigns(:repository)
135 assert_equal repository, assigns(:repository)
136 assert_not_nil assigns(:changesets)
136 assert_not_nil assigns(:changesets)
137 end
137 end
138
138
139 def test_revisions_for_invalid_repository
139 def test_revisions_for_invalid_repository
140 get :revisions, :id => 1, :repository_id => 'foo'
140 get :revisions, :id => 1, :repository_id => 'foo'
141 assert_response 404
141 assert_response 404
142 end
142 end
143
143
144 def test_revision
144 def test_revision
145 get :revision, :id => 1, :rev => 1
145 get :revision, :id => 1, :rev => 1
146 assert_response :success
146 assert_response :success
147 assert_not_nil assigns(:changeset)
147 assert_not_nil assigns(:changeset)
148 assert_equal "1", assigns(:changeset).revision
148 assert_equal "1", assigns(:changeset).revision
149 end
149 end
150
150
151 def test_revision_should_not_change_the_project_menu_link
151 def test_revision_should_not_change_the_project_menu_link
152 get :revision, :id => 1, :rev => 1
152 get :revision, :id => 1, :rev => 1
153 assert_response :success
153 assert_response :success
154
154
155 assert_tag 'a', :attributes => {:href => '/projects/ecookbook/repository', :class => /repository/},
155 assert_tag 'a', :attributes => {:href => '/projects/ecookbook/repository', :class => /repository/},
156 :ancestor => {:attributes => {:id => 'main-menu'}}
156 :ancestor => {:attributes => {:id => 'main-menu'}}
157 end
157 end
158
158
159 def test_revision_with_before_nil_and_afer_normal
159 def test_revision_with_before_nil_and_afer_normal
160 get :revision, {:id => 1, :rev => 1}
160 get :revision, {:id => 1, :rev => 1}
161 assert_response :success
161 assert_response :success
162 assert_template 'revision'
162 assert_template 'revision'
163 assert_no_tag :tag => "div", :attributes => { :class => "contextual" },
163 assert_no_tag :tag => "div", :attributes => { :class => "contextual" },
164 :child => { :tag => "a", :attributes => { :href => '/projects/ecookbook/repository/revisions/0'}
164 :child => { :tag => "a", :attributes => { :href => '/projects/ecookbook/repository/revisions/0'}
165 }
165 }
166 assert_tag :tag => "div", :attributes => { :class => "contextual" },
166 assert_tag :tag => "div", :attributes => { :class => "contextual" },
167 :child => { :tag => "a", :attributes => { :href => '/projects/ecookbook/repository/revisions/2'}
167 :child => { :tag => "a", :attributes => { :href => '/projects/ecookbook/repository/revisions/2'}
168 }
168 }
169 end
169 end
170
170
171 def test_add_related_issue
171 def test_add_related_issue
172 @request.session[:user_id] = 2
172 @request.session[:user_id] = 2
173 assert_difference 'Changeset.find(103).issues.size' do
173 assert_difference 'Changeset.find(103).issues.size' do
174 post :add_related_issue, :id => 1, :rev => 4, :issue_id => 2, :format => 'js'
174 post :add_related_issue, :id => 1, :rev => 4, :issue_id => 2, :format => 'js'
175 assert_response :success
175 assert_response :success
176 end
176 end
177 assert_select_rjs :replace_html, 'related-issues'
177 assert_select_rjs :replace_html, 'related-issues'
178 assert_equal [2], Changeset.find(103).issue_ids
178 assert_equal [2], Changeset.find(103).issue_ids
179 end
179 end
180
180
181 def test_add_related_issue_with_invalid_issue_id
181 def test_add_related_issue_with_invalid_issue_id
182 @request.session[:user_id] = 2
182 @request.session[:user_id] = 2
183 assert_no_difference 'Changeset.find(103).issues.size' do
183 assert_no_difference 'Changeset.find(103).issues.size' do
184 post :add_related_issue, :id => 1, :rev => 4, :issue_id => 9999, :format => 'js'
184 post :add_related_issue, :id => 1, :rev => 4, :issue_id => 9999, :format => 'js'
185 assert_response :success
185 assert_response :success
186 end
186 end
187 assert_include 'alert("Issue is invalid")', @response.body
187 assert_include 'alert("Issue is invalid")', @response.body
188 end
188 end
189
189
190 def test_remove_related_issue
190 def test_remove_related_issue
191 Changeset.find(103).issues << Issue.find(1)
191 Changeset.find(103).issues << Issue.find(1)
192 Changeset.find(103).issues << Issue.find(2)
192 Changeset.find(103).issues << Issue.find(2)
193
193
194 @request.session[:user_id] = 2
194 @request.session[:user_id] = 2
195 assert_difference 'Changeset.find(103).issues.size', -1 do
195 assert_difference 'Changeset.find(103).issues.size', -1 do
196 delete :remove_related_issue, :id => 1, :rev => 4, :issue_id => 2, :format => 'js'
196 delete :remove_related_issue, :id => 1, :rev => 4, :issue_id => 2, :format => 'js'
197 assert_response :success
197 assert_response :success
198 end
198 end
199 assert_select_rjs :remove, 'related-issue-2'
199 assert_select_rjs :remove, 'related-issue-2'
200 assert_equal [1], Changeset.find(103).issue_ids
200 assert_equal [1], Changeset.find(103).issue_ids
201 end
201 end
202
202
203 def test_graph_commits_per_month
203 def test_graph_commits_per_month
204 # Make sure there's some data to display
205 latest = Project.find(1).repository.changesets.maximum(:commit_date)
206 assert_not_nil latest
207 Date.stubs(:today).returns(latest.to_date + 10)
208
204 get :graph, :id => 1, :graph => 'commits_per_month'
209 get :graph, :id => 1, :graph => 'commits_per_month'
205 assert_response :success
210 assert_response :success
206 assert_equal 'image/svg+xml', @response.content_type
211 assert_equal 'image/svg+xml', @response.content_type
207 end
212 end
208
213
209 def test_graph_commits_per_author
214 def test_graph_commits_per_author
210 get :graph, :id => 1, :graph => 'commits_per_author'
215 get :graph, :id => 1, :graph => 'commits_per_author'
211 assert_response :success
216 assert_response :success
212 assert_equal 'image/svg+xml', @response.content_type
217 assert_equal 'image/svg+xml', @response.content_type
213 end
218 end
214
219
215 def test_get_committers
220 def test_get_committers
216 @request.session[:user_id] = 2
221 @request.session[:user_id] = 2
217 # add a commit with an unknown user
222 # add a commit with an unknown user
218 Changeset.create!(
223 Changeset.create!(
219 :repository => Project.find(1).repository,
224 :repository => Project.find(1).repository,
220 :committer => 'foo',
225 :committer => 'foo',
221 :committed_on => Time.now,
226 :committed_on => Time.now,
222 :revision => 100,
227 :revision => 100,
223 :comments => 'Committed by foo.'
228 :comments => 'Committed by foo.'
224 )
229 )
225
230
226 get :committers, :id => 10
231 get :committers, :id => 10
227 assert_response :success
232 assert_response :success
228 assert_template 'committers'
233 assert_template 'committers'
229
234
230 assert_tag :td, :content => 'dlopper',
235 assert_tag :td, :content => 'dlopper',
231 :sibling => { :tag => 'td',
236 :sibling => { :tag => 'td',
232 :child => { :tag => 'select', :attributes => { :name => %r{^committers\[\d+\]\[\]$} },
237 :child => { :tag => 'select', :attributes => { :name => %r{^committers\[\d+\]\[\]$} },
233 :child => { :tag => 'option', :content => 'Dave Lopper',
238 :child => { :tag => 'option', :content => 'Dave Lopper',
234 :attributes => { :value => '3', :selected => 'selected' }}}}
239 :attributes => { :value => '3', :selected => 'selected' }}}}
235 assert_tag :td, :content => 'foo',
240 assert_tag :td, :content => 'foo',
236 :sibling => { :tag => 'td',
241 :sibling => { :tag => 'td',
237 :child => { :tag => 'select', :attributes => { :name => %r{^committers\[\d+\]\[\]$} }}}
242 :child => { :tag => 'select', :attributes => { :name => %r{^committers\[\d+\]\[\]$} }}}
238 assert_no_tag :td, :content => 'foo',
243 assert_no_tag :td, :content => 'foo',
239 :sibling => { :tag => 'td',
244 :sibling => { :tag => 'td',
240 :descendant => { :tag => 'option', :attributes => { :selected => 'selected' }}}
245 :descendant => { :tag => 'option', :attributes => { :selected => 'selected' }}}
241 end
246 end
242
247
243 def test_post_committers
248 def test_post_committers
244 @request.session[:user_id] = 2
249 @request.session[:user_id] = 2
245 # add a commit with an unknown user
250 # add a commit with an unknown user
246 c = Changeset.create!(
251 c = Changeset.create!(
247 :repository => Project.find(1).repository,
252 :repository => Project.find(1).repository,
248 :committer => 'foo',
253 :committer => 'foo',
249 :committed_on => Time.now,
254 :committed_on => Time.now,
250 :revision => 100,
255 :revision => 100,
251 :comments => 'Committed by foo.'
256 :comments => 'Committed by foo.'
252 )
257 )
253 assert_no_difference "Changeset.count(:conditions => 'user_id = 3')" do
258 assert_no_difference "Changeset.count(:conditions => 'user_id = 3')" do
254 post :committers, :id => 10, :committers => { '0' => ['foo', '2'], '1' => ['dlopper', '3']}
259 post :committers, :id => 10, :committers => { '0' => ['foo', '2'], '1' => ['dlopper', '3']}
255 assert_response 302
260 assert_response 302
256 assert_equal User.find(2), c.reload.user
261 assert_equal User.find(2), c.reload.user
257 end
262 end
258 end
263 end
259 end
264 end
General Comments 0
You need to be logged in to leave comments. Login now