Redmine 3.4.4

This commit is contained in:
Manuel Cillero 2018-02-02 22:19:29 +01:00
commit 64924a6376
2112 changed files with 259028 additions and 0 deletions

View file

@ -0,0 +1,165 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::ApiRoutingTest < Redmine::ApiTest::Routing
def test_attachments
should_route 'GET /attachments/1' => 'attachments#show', :id => '1'
should_route 'PATCH /attachments/1' => 'attachments#update', :id => '1'
should_route 'DELETE /attachments/1' => 'attachments#destroy', :id => '1'
should_route 'POST /uploads' => 'attachments#upload'
end
def test_custom_fields
should_route 'GET /custom_fields' => 'custom_fields#index'
end
def test_enumerations
should_route 'GET /enumerations/issue_priorities' => 'enumerations#index', :type => 'issue_priorities'
end
def test_files
should_route 'GET /projects/foo/files' => 'files#index', :project_id => 'foo'
should_route 'POST /projects/foo/files' => 'files#create', :project_id => 'foo'
end
def test_groups
should_route 'GET /groups' => 'groups#index'
should_route 'POST /groups' => 'groups#create'
should_route 'GET /groups/1' => 'groups#show', :id => '1'
should_route 'PUT /groups/1' => 'groups#update', :id => '1'
should_route 'DELETE /groups/1' => 'groups#destroy', :id => '1'
end
def test_group_users
should_route 'POST /groups/567/users' => 'groups#add_users', :id => '567'
should_route 'DELETE /groups/567/users/12' => 'groups#remove_user', :id => '567', :user_id => '12'
end
def test_issue_categories
should_route 'GET /projects/foo/issue_categories' => 'issue_categories#index', :project_id => 'foo'
should_route 'POST /projects/foo/issue_categories' => 'issue_categories#create', :project_id => 'foo'
should_route 'GET /issue_categories/1' => 'issue_categories#show', :id => '1'
should_route 'PUT /issue_categories/1' => 'issue_categories#update', :id => '1'
should_route 'DELETE /issue_categories/1' => 'issue_categories#destroy', :id => '1'
end
def test_issue_relations
should_route 'GET /issues/1/relations' => 'issue_relations#index', :issue_id => '1'
should_route 'POST /issues/1/relations' => 'issue_relations#create', :issue_id => '1'
should_route 'GET /relations/23' => 'issue_relations#show', :id => '23'
should_route 'DELETE /relations/23' => 'issue_relations#destroy', :id => '23'
end
def test_issue_statuses
should_route 'GET /issue_statuses' => 'issue_statuses#index'
end
def test_issues
should_route 'GET /issues' => 'issues#index'
should_route 'POST /issues' => 'issues#create'
should_route 'GET /issues/64' => 'issues#show', :id => '64'
should_route 'PUT /issues/64' => 'issues#update', :id => '64'
should_route 'DELETE /issues/64' => 'issues#destroy', :id => '64'
end
def test_issue_watchers
should_route 'POST /issues/12/watchers' => 'watchers#create', :object_type => 'issue', :object_id => '12'
should_route 'DELETE /issues/12/watchers/3' => 'watchers#destroy', :object_type => 'issue', :object_id => '12', :user_id => '3'
end
def test_memberships
should_route 'GET /projects/5234/memberships' => 'members#index', :project_id => '5234'
should_route 'POST /projects/5234/memberships' => 'members#create', :project_id => '5234'
should_route 'GET /memberships/5234' => 'members#show', :id => '5234'
should_route 'PUT /memberships/5234' => 'members#update', :id => '5234'
should_route 'DELETE /memberships/5234' => 'members#destroy', :id => '5234'
end
def test_news
should_route 'GET /news' => 'news#index'
should_route 'GET /projects/567/news' => 'news#index', :project_id => '567'
end
def test_projects
should_route 'GET /projects' => 'projects#index'
should_route 'POST /projects' => 'projects#create'
should_route 'GET /projects/1' => 'projects#show', :id => '1'
should_route 'PUT /projects/1' => 'projects#update', :id => '1'
should_route 'DELETE /projects/1' => 'projects#destroy', :id => '1'
end
def test_queries
should_route 'GET /queries' => 'queries#index'
end
def test_roles
should_route 'GET /roles' => 'roles#index'
should_route 'GET /roles/2' => 'roles#show', :id => '2'
end
def test_time_entries
should_route 'GET /time_entries' => 'timelog#index'
should_route 'POST /time_entries' => 'timelog#create'
should_route 'GET /time_entries/1' => 'timelog#show', :id => '1'
should_route 'PUT /time_entries/1' => 'timelog#update', :id => '1'
should_route 'DELETE /time_entries/1' => 'timelog#destroy', :id => '1'
end
def test_trackers
should_route 'GET /trackers' => 'trackers#index'
end
def test_users
should_route 'GET /users' => 'users#index'
should_route 'POST /users' => 'users#create'
should_route 'GET /users/44' => 'users#show', :id => '44'
should_route 'GET /users/current' => 'users#show', :id => 'current'
should_route 'PUT /users/44' => 'users#update', :id => '44'
should_route 'DELETE /users/44' => 'users#destroy', :id => '44'
end
def test_versions
should_route 'GET /projects/foo/versions' => 'versions#index', :project_id => 'foo'
should_route 'POST /projects/foo/versions' => 'versions#create', :project_id => 'foo'
should_route 'GET /versions/1' => 'versions#show', :id => '1'
should_route 'PUT /versions/1' => 'versions#update', :id => '1'
should_route 'DELETE /versions/1' => 'versions#destroy', :id => '1'
end
def test_wiki
should_route 'GET /projects/567/wiki/index' => 'wiki#index', :project_id => '567'
should_route 'GET /projects/567/wiki/my_page' => 'wiki#show', :project_id => '567', :id => 'my_page'
should_route 'GET /projects/567/wiki/my_page' => 'wiki#show', :project_id => '567', :id => 'my_page'
should_route 'GET /projects/1/wiki/my_page/2' => 'wiki#show', :project_id => '1', :id => 'my_page', :version => '2'
should_route 'PUT /projects/567/wiki/my_page' => 'wiki#update', :project_id => '567', :id => 'my_page'
should_route 'DELETE /projects/567/wiki/my_page' => 'wiki#destroy', :project_id => '567', :id => 'my_page'
end
end

View file

@ -0,0 +1,55 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::ApiTest < Redmine::ApiTest::Base
fixtures :users, :email_addresses, :members, :member_roles, :roles, :projects
def test_api_should_work_with_protect_from_forgery
ActionController::Base.allow_forgery_protection = true
assert_difference('User.count') do
post '/users.xml',
:params => {
:user => {
:login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname',
:mail => 'foo@example.net', :password => 'secret123'}
},
:headers => credentials('admin')
assert_response 201
end
ensure
ActionController::Base.allow_forgery_protection = false
end
def test_json_datetime_format
get '/users/1.json', :headers => credentials('admin')
assert_include '"created_on":"2006-07-19T17:12:21Z"', response.body
end
def test_xml_datetime_format
get '/users/1.xml', :headers => credentials('admin')
assert_include '<created_on>2006-07-19T17:12:21Z</created_on>', response.body
end
def test_head_response_should_have_empty_body
put '/users/7.xml', :params => {:user => {:login => 'foo'}}, :headers => credentials('admin')
assert_response :ok
assert_equal '', response.body
end
end

View file

@ -0,0 +1,236 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::AttachmentsTest < Redmine::ApiTest::Base
fixtures :projects, :trackers, :issue_statuses, :issues,
:enumerations, :users, :issue_categories,
:projects_trackers,
:roles,
:member_roles,
:members,
:enabled_modules,
:attachments
def setup
super
set_fixtures_attachments_directory
end
def teardown
super
set_tmp_attachments_directory
end
test "GET /attachments/:id.xml should return the attachment" do
get '/attachments/7.xml', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'attachment id', :text => '7' do
assert_select '~ filename', :text => 'archive.zip'
assert_select '~ content_url', :text => 'http://www.example.com/attachments/download/7/archive.zip'
end
end
test "GET /attachments/:id.xml for image should include thumbnail_url" do
get '/attachments/16.xml', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'attachment id:contains(16)' do
assert_select '~ thumbnail_url', :text => 'http://www.example.com/attachments/thumbnail/16'
end
end
test "GET /attachments/:id.xml should deny access without credentials" do
get '/attachments/7.xml'
assert_response 401
set_tmp_attachments_directory
end
test "GET /attachments/download/:id/:filename should return the attachment content" do
get '/attachments/download/7/archive.zip', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/zip', @response.content_type
set_tmp_attachments_directory
end
test "GET /attachments/download/:id/:filename should deny access without credentials" do
get '/attachments/download/7/archive.zip'
assert_response 302
set_tmp_attachments_directory
end
test "GET /attachments/thumbnail/:id should return the thumbnail" do
skip unless convert_installed?
get '/attachments/thumbnail/16', :headers => credentials('jsmith')
assert_response :success
end
test "DELETE /attachments/:id.xml should return ok and delete Attachment" do
assert_difference 'Attachment.count', -1 do
delete '/attachments/7.xml', :headers => credentials('jsmith')
assert_response :ok
assert_equal '', response.body
end
assert_nil Attachment.find_by_id(7)
end
test "DELETE /attachments/:id.json should return ok and delete Attachment" do
assert_difference 'Attachment.count', -1 do
delete '/attachments/7.json', :headers => credentials('jsmith')
assert_response :ok
assert_equal '', response.body
end
assert_nil Attachment.find_by_id(7)
end
test "PATCH /attachments/:id.json should update the attachment" do
patch '/attachments/7.json',
:params => {:attachment => {:filename => 'renamed.zip', :description => 'updated'}},
:headers => credentials('jsmith')
assert_response :ok
assert_equal 'application/json', response.content_type
attachment = Attachment.find(7)
assert_equal 'renamed.zip', attachment.filename
assert_equal 'updated', attachment.description
end
test "PATCH /attachments/:id.json with failure should return the errors" do
patch '/attachments/7.json',
:params => {:attachment => {:filename => '', :description => 'updated'}},
:headers => credentials('jsmith')
assert_response 422
assert_equal 'application/json', response.content_type
json = ActiveSupport::JSON.decode(response.body)
assert_include "File cannot be blank", json['errors']
end
test "POST /uploads.xml should return the token" do
set_tmp_attachments_directory
assert_difference 'Attachment.count' do
post '/uploads.xml', :headers => {
"RAW_POST_DATA" => 'File content',
"CONTENT_TYPE" => 'application/octet-stream'
}.merge(credentials('jsmith'))
assert_response :created
assert_equal 'application/xml', response.content_type
end
xml = Hash.from_xml(response.body)
assert_kind_of Hash, xml['upload']
token = xml['upload']['token']
assert_not_nil token
attachment_id = xml['upload']['id']
assert_not_nil attachment_id
attachment = Attachment.order('id DESC').first
assert_equal token, attachment.token
assert_equal attachment_id, attachment.id.to_s
assert_nil attachment.container
assert_equal 2, attachment.author_id
assert_equal 'File content'.size, attachment.filesize
assert attachment.content_type.blank?
assert attachment.filename.present?
assert_match %r{\d+_[0-9a-z]+}, attachment.diskfile
assert File.exist?(attachment.diskfile)
assert_equal 'File content', File.read(attachment.diskfile)
end
test "POST /uploads.json should return the token" do
set_tmp_attachments_directory
assert_difference 'Attachment.count' do
post '/uploads.json', :headers => {
"RAW_POST_DATA" => 'File content',
"CONTENT_TYPE" => 'application/octet-stream'
}.merge(credentials('jsmith'))
assert_response :created
assert_equal 'application/json', response.content_type
end
json = ActiveSupport::JSON.decode(response.body)
assert_kind_of Hash, json['upload']
token = json['upload']['token']
assert_not_nil token
attachment = Attachment.order('id DESC').first
assert_equal token, attachment.token
end
test "POST /uploads.xml should accept :filename param as the attachment filename" do
set_tmp_attachments_directory
assert_difference 'Attachment.count' do
post '/uploads.xml?filename=test.txt', :headers => {
"RAW_POST_DATA" => 'File content',
"CONTENT_TYPE" => 'application/octet-stream'
}.merge(credentials('jsmith'))
assert_response :created
end
attachment = Attachment.order('id DESC').first
assert_equal 'test.txt', attachment.filename
assert_match /_test\.txt$/, attachment.diskfile
end
test "POST /uploads.xml should not accept other content types" do
set_tmp_attachments_directory
assert_no_difference 'Attachment.count' do
post '/uploads.xml', :headers => {
"RAW_POST_DATA" => 'PNG DATA',
"CONTENT_TYPE" => 'image/png'
}.merge(credentials('jsmith'))
assert_response 406
end
end
test "POST /uploads.xml should return errors if file is too big" do
set_tmp_attachments_directory
with_settings :attachment_max_size => 1 do
assert_no_difference 'Attachment.count' do
post '/uploads.xml', :headers => {
"RAW_POST_DATA" => ('x' * 2048),
"CONTENT_TYPE" => 'application/octet-stream'
}.merge(credentials('jsmith'))
assert_response 422
assert_select 'error', :text => /exceeds the maximum allowed file size/
end
end
end
test "POST /uploads.json should create an empty file and return a valid token" do
set_tmp_attachments_directory
assert_difference 'Attachment.count' do
post '/uploads.json', :headers => {
"CONTENT_TYPE" => 'application/octet-stream'
}.merge(credentials('jsmith'))
assert_response :created
end
json = ActiveSupport::JSON.decode(response.body)
assert_kind_of Hash, json['upload']
token = json['upload']['token']
assert token.present?
assert attachment = Attachment.find_by_token(token)
assert_equal 0, attachment.filesize
assert attachment.digest.present?
assert File.exist? attachment.diskfile
end
end

View file

@ -0,0 +1,158 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::AuthenticationTest < Redmine::ApiTest::Base
fixtures :users
def test_api_should_deny_without_credentials
get '/users/current.xml'
assert_response 401
assert_equal User.anonymous, User.current
assert response.headers.has_key?('WWW-Authenticate')
end
def test_api_should_accept_http_basic_auth_using_username_and_password
user = User.generate! do |user|
user.password = 'my_password'
end
get '/users/current.xml', :headers => credentials(user.login, 'my_password')
assert_response 200
assert_equal user, User.current
end
def test_api_should_deny_http_basic_auth_using_username_and_wrong_password
user = User.generate! do |user|
user.password = 'my_password'
end
get '/users/current.xml', :headers => credentials(user.login, 'wrong_password')
assert_response 401
assert_equal User.anonymous, User.current
end
def test_api_should_accept_http_basic_auth_using_api_key
user = User.generate!
token = Token.create!(:user => user, :action => 'api')
get '/users/current.xml', :headers => credentials(token.value, 'X')
assert_response 200
assert_equal user, User.current
end
def test_api_should_deny_http_basic_auth_using_wrong_api_key
user = User.generate!
token = Token.create!(:user => user, :action => 'feeds') # not the API key
get '/users/current.xml', :headers => credentials(token.value, 'X')
assert_response 401
assert_equal User.anonymous, User.current
end
def test_api_should_accept_auth_using_api_key_as_parameter
user = User.generate!
token = Token.create!(:user => user, :action => 'api')
get "/users/current.xml?key=#{token.value}"
assert_response 200
assert_equal user, User.current
end
def test_api_should_deny_auth_using_wrong_api_key_as_parameter
user = User.generate!
token = Token.create!(:user => user, :action => 'feeds') # not the API key
get "/users/current.xml?key=#{token.value}"
assert_response 401
assert_equal User.anonymous, User.current
end
def test_api_should_accept_auth_using_api_key_as_request_header
user = User.generate!
token = Token.create!(:user => user, :action => 'api')
get "/users/current.xml", :headers => {'X-Redmine-API-Key' => token.value.to_s}
assert_response 200
assert_equal user, User.current
end
def test_api_should_deny_auth_using_wrong_api_key_as_request_header
user = User.generate!
token = Token.create!(:user => user, :action => 'feeds') # not the API key
get "/users/current.xml", :headers => {'X-Redmine-API-Key' => token.value.to_s}
assert_response 401
assert_equal User.anonymous, User.current
end
def test_api_should_trigger_basic_http_auth_with_basic_authorization_header
ApplicationController.any_instance.expects(:authenticate_with_http_basic).once
get '/users/current.xml', :headers => credentials('jsmith')
assert_response 401
end
def test_api_should_not_trigger_basic_http_auth_with_non_basic_authorization_header
ApplicationController.any_instance.expects(:authenticate_with_http_basic).never
get '/users/current.xml', :headers => {'HTTP_AUTHORIZATION' => 'Digest foo bar'}
assert_response 401
end
def test_invalid_utf8_credentials_should_not_trigger_an_error
invalid_utf8 = "\x82".force_encoding('UTF-8')
assert !invalid_utf8.valid_encoding?
assert_nothing_raised do
get '/users/current.xml', :headers => credentials(invalid_utf8, "foo")
end
end
def test_api_request_should_not_use_user_session
log_user('jsmith', 'jsmith')
get '/users/current'
assert_response :success
get '/users/current.json'
assert_response 401
end
def test_api_should_accept_switch_user_header_for_admin_user
user = User.find(1)
su = User.find(4)
get '/users/current', :headers => {'X-Redmine-API-Key' => user.api_key, 'X-Redmine-Switch-User' => su.login}
assert_response :success
assert_select 'h2', :text => su.name
assert_equal su, User.current
end
def test_api_should_respond_with_412_when_trying_to_switch_to_a_invalid_user
get '/users/current', :headers => {'X-Redmine-API-Key' => User.find(1).api_key, 'X-Redmine-Switch-User' => 'foobar'}
assert_response 412
end
def test_api_should_respond_with_412_when_trying_to_switch_to_a_locked_user
user = User.find(5)
assert user.locked?
get '/users/current', :headers => {'X-Redmine-API-Key' => User.find(1).api_key, 'X-Redmine-Switch-User' => user.login}
assert_response 412
end
def test_api_should_not_accept_switch_user_header_for_non_admin_user
user = User.find(2)
su = User.find(4)
get '/users/current', :headers => {'X-Redmine-API-Key' => user.api_key, 'X-Redmine-Switch-User' => su.login}
assert_response :success
assert_select 'h2', :text => user.name
assert_equal user, User.current
end
end

View file

@ -0,0 +1,99 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::CustomFieldsAttributeTest < Redmine::ApiTest::Base
fixtures :users
def test_integer_custom_fields_should_accept_strings
field = GroupCustomField.generate!(:field_format => 'int')
post '/groups.json',
:params => %({"group":{"name":"Foo","custom_field_values":{"#{field.id}":"52"}}}),
:headers => {
'CONTENT_TYPE' => 'application/json'
}.merge(credentials('admin'))
assert_response :created
group = Group.order('id DESC').first
assert_equal "52", group.custom_field_value(field)
end
def test_integer_custom_fields_should_accept_integers
field = GroupCustomField.generate!(:field_format => 'int')
post '/groups.json',
:params => %({"group":{"name":"Foo","custom_field_values":{"#{field.id}":52}}}),
:headers => {
'CONTENT_TYPE' => 'application/json'
}.merge(credentials('admin'))
assert_response :created
group = Group.order('id DESC').first
assert_equal "52", group.custom_field_value(field)
end
def test_boolean_custom_fields_should_accept_strings
field = GroupCustomField.generate!(:field_format => 'bool')
post '/groups.json',
:params => %({"group":{"name":"Foo","custom_field_values":{"#{field.id}": "1"}}}),
:headers => {
'CONTENT_TYPE' => 'application/json'
}.merge(credentials('admin'))
assert_response :created
group = Group.order('id DESC').first
assert_equal "1", group.custom_field_value(field)
end
def test_boolean_custom_fields_should_accept_integers
field = GroupCustomField.generate!(:field_format => 'bool')
post '/groups.json',
:params => %({"group":{"name":"Foo","custom_field_values":{"#{field.id}": 1}}}),
:headers => {
'CONTENT_TYPE' => 'application/json'
}.merge(credentials('admin'))
assert_response :created
group = Group.order('id DESC').first
assert_equal "1", group.custom_field_value(field)
end
def test_multivalued_custom_fields_should_accept_an_array
field = GroupCustomField.generate!(
:field_format => 'list',
:multiple => true,
:possible_values => ["V1", "V2", "V3"],
:default_value => "V2"
)
payload = <<-JSON
{"group": {"name":"Foooo",
"custom_field_values":{"#{field.id}":["V1","V3"]}
}
}
JSON
post '/groups.json',
:params => payload,
:headers => {
'CONTENT_TYPE' => 'application/json'
}.merge(credentials('admin'))
assert_response :created
group = Group.order('id DESC').first
assert_equal ["V1", "V3"], group.custom_field_value(field).sort
end
end

View file

@ -0,0 +1,56 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::CustomFieldsTest < Redmine::ApiTest::Base
fixtures :users, :custom_fields
test "GET /custom_fields.xml should return custom fields" do
get '/custom_fields.xml', :headers => credentials('admin')
assert_response :success
assert_equal 'application/xml', response.content_type
assert_select 'custom_fields' do
assert_select 'custom_field' do
assert_select 'name', :text => 'Database'
assert_select 'id', :text => '2'
assert_select 'customized_type', :text => 'issue'
assert_select 'possible_values[type=array]' do
assert_select 'possible_value>value', :text => 'PostgreSQL'
assert_select 'possible_value>label', :text => 'PostgreSQL'
end
assert_select 'trackers[type=array]'
assert_select 'roles[type=array]'
end
end
end
test "GET /custom_fields.xml should include value and label for enumeration custom fields" do
field = IssueCustomField.generate!(:field_format => 'enumeration')
foo = field.enumerations.create!(:name => 'Foo')
bar = field.enumerations.create!(:name => 'Bar')
get '/custom_fields.xml', :headers => credentials('admin')
assert_response :success
assert_select 'possible_value' do
assert_select "value:contains(?) + label:contains(?)", foo.id.to_s, 'Foo'
assert_select "value:contains(?) + label:contains(?)", bar.id.to_s, 'Bar'
end
end
end

View file

@ -0,0 +1,78 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::DisabledRestApiTest < Redmine::ApiTest::Base
fixtures :projects, :trackers, :issue_statuses, :issues,
:enumerations, :users, :issue_categories,
:projects_trackers,
:roles,
:member_roles,
:members,
:enabled_modules
def setup
Setting.rest_api_enabled = '0'
Setting.login_required = '1'
end
def teardown
Setting.rest_api_enabled = '1'
Setting.login_required = '0'
end
def test_with_a_valid_api_token
@user = User.generate!
@token = Token.create!(:user => @user, :action => 'api')
get "/news.xml?key=#{@token.value}"
assert_response :unauthorized
assert_equal User.anonymous, User.current
get "/news.json?key=#{@token.value}"
assert_response :unauthorized
assert_equal User.anonymous, User.current
end
def test_with_valid_username_password_http_authentication
@user = User.generate! do |user|
user.password = 'my_password'
end
get "/news.xml", :headers => credentials(@user.login, 'my_password')
assert_response :unauthorized
assert_equal User.anonymous, User.current
get "/news.json", :headers => credentials(@user.login, 'my_password')
assert_response :unauthorized
assert_equal User.anonymous, User.current
end
def test_with_valid_token_http_authentication
@user = User.generate!
@token = Token.create!(:user => @user, :action => 'api')
get "/news.xml", :headers => credentials(@token.value, 'X')
assert_response :unauthorized
assert_equal User.anonymous, User.current
get "/news.json", :headers => credentials(@token.value, 'X')
assert_response :unauthorized
assert_equal User.anonymous, User.current
end
end

View file

@ -0,0 +1,40 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::EnumerationsTest < Redmine::ApiTest::Base
fixtures :enumerations
test "GET /enumerations/issue_priorities.xml should return priorities" do
get '/enumerations/issue_priorities.xml'
assert_response :success
assert_equal 'application/xml', response.content_type
assert_select 'issue_priorities[type=array]' do
assert_select 'issue_priority' do
assert_select 'id', :text => '6'
assert_select 'name', :text => 'High'
end
end
end
test "GET /enumerations/invalid_subclass.xml should return 404" do
get '/enumerations/invalid_subclass.xml'
assert_response 404
assert_equal 'application/xml', response.content_type
end
end

View file

@ -0,0 +1,133 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::FilesTest < Redmine::ApiTest::Base
fixtures :projects,
:users,
:members,
:roles,
:member_roles,
:enabled_modules,
:attachments,
:versions
test "GET /projects/:project_id/files.xml should return the list of uploaded files" do
get '/projects/1/files.xml', :headers => credentials('jsmith')
assert_response :success
assert_select 'files>file>id', :text => '8'
end
test "POST /projects/:project_id/files.json should create a file" do
set_tmp_attachments_directory
post '/uploads.xml',
:params => 'File content',
:headers => {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
token = Attachment.last.token
payload = <<-JSON
{ "file": {
"token": "#{token}"
}
}
JSON
post '/projects/1/files.json',
:params => payload,
:headers => {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
assert_response :success
assert_equal 1, Attachment.last.container_id
assert_equal "Project", Attachment.last.container_type
end
test "POST /projects/:project_id/files.xml should create a file" do
set_tmp_attachments_directory
post '/uploads.xml',
:params => 'File content',
:headers => {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
token = Attachment.last.token
payload = <<-XML
<file>
<token>#{token}</token>
</file>
XML
post '/projects/1/files.xml',
:params => payload,
:headers => {"CONTENT_TYPE" => 'application/xml'}.merge(credentials('jsmith'))
assert_response :success
assert_equal 1, Attachment.last.container_id
assert_equal "Project", Attachment.last.container_type
end
test "POST /projects/:project_id/files.json should refuse requests without the :token parameter" do
payload = <<-JSON
{ "file": {
"filename": "project_file.zip",
}
}
JSON
post '/projects/1/files.json',
:params => payload,
:headers => {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
assert_response :bad_request
end
test "POST /projects/:project_id/files.json should accept :filename, :description, :content_type as optional parameters" do
set_tmp_attachments_directory
post '/uploads.xml',
:params => 'File content',
:headers => {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
token = Attachment.last.token
payload = <<-JSON
{ "file": {
"filename": "New filename",
"description": "New description",
"content_type": "application/txt",
"token": "#{token}"
}
}
JSON
post '/projects/1/files.json',
:params => payload,
:headers => {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
assert_response :success
assert_equal "New filename", Attachment.last.filename
assert_equal "New description", Attachment.last.description
assert_equal "application/txt", Attachment.last.content_type
end
test "POST /projects/:project_id/files.json should accept :version_id to attach the files to a version" do
set_tmp_attachments_directory
post '/uploads.xml',
:params => 'File content',
:headers => {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
token = Attachment.last.token
payload = <<-JSON
{ "file": {
"version_id": 3,
"filename": "New filename",
"description": "New description",
"token": "#{token}"
}
}
JSON
post '/projects/1/files.json',
:params => payload,
:headers => {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
assert_equal 3, Attachment.last.container_id
assert_equal "Version", Attachment.last.container_type
end
end

View file

@ -0,0 +1,228 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::GroupsTest < Redmine::ApiTest::Base
fixtures :users, :groups_users, :email_addresses
test "GET /groups.xml should require authentication" do
get '/groups.xml'
assert_response 401
end
test "GET /groups.xml should return givable groups" do
get '/groups.xml', :headers => credentials('admin')
assert_response :success
assert_equal 'application/xml', response.content_type
assert_select 'groups' do
assert_select 'group', Group.givable.count
assert_select 'group' do
assert_select 'name', :text => 'A Team'
assert_select 'id', :text => '10'
end
end
end
test "GET /groups.xml?builtin=1 should return all groups" do
get '/groups.xml?builtin=1', :headers => credentials('admin')
assert_response :success
assert_equal 'application/xml', response.content_type
assert_select 'groups' do
assert_select 'group', Group.givable.count + 2
assert_select 'group' do
assert_select 'builtin', :text => 'non_member'
assert_select 'id', :text => '12'
end
assert_select 'group' do
assert_select 'builtin', :text => 'anonymous'
assert_select 'id', :text => '13'
end
end
end
test "GET /groups.json should require authentication" do
get '/groups.json'
assert_response 401
end
test "GET /groups.json should return groups" do
get '/groups.json', :headers => credentials('admin')
assert_response :success
assert_equal 'application/json', response.content_type
json = ActiveSupport::JSON.decode(response.body)
groups = json['groups']
assert_kind_of Array, groups
group = groups.detect {|g| g['name'] == 'A Team'}
assert_not_nil group
assert_equal({'id' => 10, 'name' => 'A Team'}, group)
end
test "GET /groups/:id.xml should return the group" do
get '/groups/10.xml', :headers => credentials('admin')
assert_response :success
assert_equal 'application/xml', response.content_type
assert_select 'group' do
assert_select 'name', :text => 'A Team'
assert_select 'id', :text => '10'
end
end
test "GET /groups/:id.xml should return the builtin group" do
get '/groups/12.xml', :headers => credentials('admin')
assert_response :success
assert_equal 'application/xml', response.content_type
assert_select 'group' do
assert_select 'builtin', :text => 'non_member'
assert_select 'id', :text => '12'
end
end
test "GET /groups/:id.xml should include users if requested" do
get '/groups/10.xml?include=users', :headers => credentials('admin')
assert_response :success
assert_equal 'application/xml', response.content_type
assert_select 'group' do
assert_select 'users' do
assert_select 'user', Group.find(10).users.count
assert_select 'user[id="8"]'
end
end
end
test "GET /groups/:id.xml include memberships if requested" do
get '/groups/10.xml?include=memberships', :headers => credentials('admin')
assert_response :success
assert_equal 'application/xml', response.content_type
assert_select 'group' do
assert_select 'memberships'
end
end
test "POST /groups.xml with valid parameters should create the group" do
assert_difference('Group.count') do
post '/groups.xml',
:params => {:group => {:name => 'Test', :user_ids => [2, 3]}},
:headers => credentials('admin')
assert_response :created
assert_equal 'application/xml', response.content_type
end
group = Group.order('id DESC').first
assert_equal 'Test', group.name
assert_equal [2, 3], group.users.map(&:id).sort
assert_select 'group' do
assert_select 'name', :text => 'Test'
end
end
test "POST /groups.xml with invalid parameters should return errors" do
assert_no_difference('Group.count') do
post '/groups.xml',
:params => {:group => {:name => ''}},
:headers => credentials('admin')
end
assert_response :unprocessable_entity
assert_equal 'application/xml', response.content_type
assert_select 'errors' do
assert_select 'error', :text => /Name cannot be blank/
end
end
test "PUT /groups/:id.xml with valid parameters should update the group" do
group = Group.generate!
put "/groups/#{group.id}.xml",
:params => {:group => {:name => 'New name', :user_ids => [2, 3]}},
:headers => credentials('admin')
assert_response :ok
assert_equal '', @response.body
assert_equal 'New name', group.reload.name
assert_equal [2, 3], group.users.map(&:id).sort
end
test "PUT /groups/:id.xml with invalid parameters should return errors" do
group = Group.generate!
put "/groups/#{group.id}.xml",
:params => {:group => {:name => ''}},
:headers => credentials('admin')
assert_response :unprocessable_entity
assert_equal 'application/xml', response.content_type
assert_select 'errors' do
assert_select 'error', :text => /Name cannot be blank/
end
end
test "DELETE /groups/:id.xml should delete the group" do
group = Group.generate!
assert_difference 'Group.count', -1 do
delete "/groups/#{group.id}.xml", :headers => credentials('admin')
assert_response :ok
assert_equal '', @response.body
end
end
test "POST /groups/:id/users.xml should add user to the group" do
group = Group.generate!
assert_difference 'group.reload.users.count' do
post "/groups/#{group.id}/users.xml",
:params => {:user_id => 5},
:headers => credentials('admin')
assert_response :ok
assert_equal '', @response.body
end
assert_include User.find(5), group.reload.users
end
test "POST /groups/:id/users.xml should not add the user if already added" do
group = Group.generate!
group.users << User.find(5)
assert_no_difference 'group.reload.users.count' do
post "/groups/#{group.id}/users.xml",
:params => {:user_id => 5},
:headers => credentials('admin')
assert_response :unprocessable_entity
end
assert_select 'errors' do
assert_select 'error', :text => /User is invalid/
end
end
test "DELETE /groups/:id/users/:user_id.xml should remove user from the group" do
group = Group.generate!
group.users << User.find(8)
assert_difference 'group.reload.users.count', -1 do
delete "/groups/#{group.id}/users/8.xml", :headers => credentials('admin')
assert_response :ok
assert_equal '', @response.body
end
assert_not_include User.find(8), group.reload.users
end
end

View file

@ -0,0 +1,114 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::IssueCategoriesTest < Redmine::ApiTest::Base
fixtures :projects, :users, :issue_categories, :issues,
:roles,
:member_roles,
:members,
:enabled_modules
test "GET /projects/:project_id/issue_categories.xml should return the issue categories" do
get '/projects/1/issue_categories.xml', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'issue_categories issue_category id', :text => '2'
end
test "GET /issue_categories/:id.xml should return the issue category" do
get '/issue_categories/2.xml', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'issue_category id', :text => '2'
end
test "POST /projects/:project_id/issue_categories.xml should return create issue category" do
assert_difference 'IssueCategory.count' do
post '/projects/1/issue_categories.xml',
:params => {:issue_category => {:name => 'API'}},
:headers => credentials('jsmith')
end
assert_response :created
assert_equal 'application/xml', @response.content_type
category = IssueCategory.order('id DESC').first
assert_equal 'API', category.name
assert_equal 1, category.project_id
end
test "POST /projects/:project_id/issue_categories.xml with invalid parameters should return errors" do
assert_no_difference 'IssueCategory.count' do
post '/projects/1/issue_categories.xml',
:params => {:issue_category => {:name => ''}},
:headers => credentials('jsmith')
end
assert_response :unprocessable_entity
assert_equal 'application/xml', @response.content_type
assert_select 'errors error', :text => "Name cannot be blank"
end
test "PUT /issue_categories/:id.xml with valid parameters should update the issue category" do
assert_no_difference 'IssueCategory.count' do
put '/issue_categories/2.xml',
:params => {:issue_category => {:name => 'API Update'}},
:headers => credentials('jsmith')
end
assert_response :ok
assert_equal '', @response.body
assert_equal 'API Update', IssueCategory.find(2).name
end
test "PUT /issue_categories/:id.xml with invalid parameters should return errors" do
assert_no_difference 'IssueCategory.count' do
put '/issue_categories/2.xml',
:params => {:issue_category => {:name => ''}},
:headers => credentials('jsmith')
end
assert_response :unprocessable_entity
assert_equal 'application/xml', @response.content_type
assert_select 'errors error', :text => "Name cannot be blank"
end
test "DELETE /issue_categories/:id.xml should destroy the issue category" do
assert_difference 'IssueCategory.count', -1 do
delete '/issue_categories/1.xml', :headers => credentials('jsmith')
end
assert_response :ok
assert_equal '', @response.body
assert_nil IssueCategory.find_by_id(1)
end
test "DELETE /issue_categories/:id.xml should reassign issues with :reassign_to_id param" do
issue_count = Issue.where(:category_id => 1).count
assert issue_count > 0
assert_difference 'IssueCategory.count', -1 do
assert_difference 'Issue.where(:category_id => 2).count', 3 do
delete '/issue_categories/1.xml',
:params => {:reassign_to_id => 2},
:headers => credentials('jsmith')
end
end
assert_response :ok
assert_equal '', @response.body
assert_nil IssueCategory.find_by_id(1)
end
end

View file

@ -0,0 +1,84 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::IssueRelationsTest < Redmine::ApiTest::Base
fixtures :projects, :trackers, :issue_statuses, :issues,
:enumerations, :users, :issue_categories,
:projects_trackers,
:roles,
:member_roles,
:members,
:enabled_modules,
:issue_relations
test "GET /issues/:issue_id/relations.xml should return issue relations" do
get '/issues/9/relations.xml', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'relations[type=array] relation id', :text => '1'
end
test "POST /issues/:issue_id/relations.xml should create the relation" do
assert_difference('IssueRelation.count') do
post '/issues/2/relations.xml',
:params => {:relation => {:issue_to_id => 7, :relation_type => 'relates'}},
:headers => credentials('jsmith')
end
relation = IssueRelation.order('id DESC').first
assert_equal 2, relation.issue_from_id
assert_equal 7, relation.issue_to_id
assert_equal 'relates', relation.relation_type
assert_response :created
assert_equal 'application/xml', @response.content_type
assert_select 'relation id', :text => relation.id.to_s
end
test "POST /issues/:issue_id/relations.xml with failure should return errors" do
assert_no_difference('IssueRelation.count') do
post '/issues/2/relations.xml',
:params => {:relation => {:issue_to_id => 7, :relation_type => 'foo'}},
:headers => credentials('jsmith')
end
assert_response :unprocessable_entity
assert_select 'errors error', :text => /Relation type is not included in the list/
end
test "GET /relations/:id.xml should return the relation" do
get '/relations/2.xml', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'relation id', :text => '2'
end
test "DELETE /relations/:id.xml should delete the relation" do
assert_difference('IssueRelation.count', -1) do
delete '/relations/2.xml', :headers => credentials('jsmith')
end
assert_response :ok
assert_equal '', @response.body
assert_nil IssueRelation.find_by_id(2)
end
end

View file

@ -0,0 +1,32 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::IssueStatusesTest < Redmine::ApiTest::Base
fixtures :issue_statuses
test "GET /issue_statuses.xml should return issue statuses" do
get '/issue_statuses.xml'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'issue_statuses[type=array] issue_status id', :text => '2' do
assert_select '~ name', :text => 'Assigned'
end
end
end

View file

@ -0,0 +1,978 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base
fixtures :projects,
:users,
:roles,
:members,
:member_roles,
:issues,
:issue_statuses,
:issue_relations,
:versions,
:trackers,
:projects_trackers,
:issue_categories,
:enabled_modules,
:enumerations,
:attachments,
:workflows,
:custom_fields,
:custom_values,
:custom_fields_projects,
:custom_fields_trackers,
:time_entries,
:journals,
:journal_details,
:queries,
:attachments
test "GET /issues.xml should contain metadata" do
get '/issues.xml'
assert_select 'issues[type=array][total_count][limit="25"][offset="0"]'
end
test "GET /issues.xml with nometa param should not contain metadata" do
get '/issues.xml?nometa=1'
assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])'
end
test "GET /issues.xml with nometa header should not contain metadata" do
get '/issues.xml', :headers => {'X-Redmine-Nometa' => '1'}
assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])'
end
test "GET /issues.xml with offset and limit" do
get '/issues.xml?offset=2&limit=3'
assert_select 'issues[type=array][total_count][limit="3"][offset="2"]'
assert_select 'issues issue', 3
end
test "GET /issues.xml with relations" do
get '/issues.xml?include=relations'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'issue id', :text => '3' do
assert_select '~ relations relation', 1
assert_select '~ relations relation[id="2"][issue_id="2"][issue_to_id="3"][relation_type=relates]'
end
assert_select 'issue id', :text => '1' do
assert_select '~ relations'
assert_select '~ relations relation', 0
end
end
test "GET /issues.xml with attachments" do
get '/issues.xml?include=attachments'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'issue id', :text => '3' do
assert_select '~ attachments attachment', 4
end
assert_select 'issue id', :text => '1' do
assert_select '~ attachments'
assert_select '~ attachments attachment', 0
end
end
test "GET /issues.xml with invalid query params" do
get '/issues.xml', :params => {:f => ['start_date'], :op => {:start_date => '='}}
assert_response :unprocessable_entity
assert_equal 'application/xml', @response.content_type
assert_select 'errors error', :text => "Start date cannot be blank"
end
test "GET /issues.xml with custom field filter" do
get '/issues.xml',
:params => {:set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='}, :v => {:cf_1 => ['MySQL']}}
expected_ids = Issue.visible.
joins(:custom_values).
where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
assert expected_ids.any?
assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
end
end
test "GET /issues.xml with custom field filter (shorthand method)" do
get '/issues.xml', :params => {:cf_1 => 'MySQL'}
expected_ids = Issue.visible.
joins(:custom_values).
where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id)
assert expected_ids.any?
assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
end
end
def test_index_should_include_issue_attributes
get '/issues.xml'
assert_select 'issues>issue>is_private', :text => 'false'
end
def test_index_should_allow_timestamp_filtering
Issue.delete_all
Issue.generate!(:subject => '1').update_column(:updated_on, Time.parse("2014-01-02T10:25:00Z"))
Issue.generate!(:subject => '2').update_column(:updated_on, Time.parse("2014-01-02T12:13:00Z"))
get '/issues.xml', :params => {
:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '<='},
:v => {:updated_on => ['2014-01-02T12:00:00Z']}
}
assert_select 'issues>issue', :count => 1
assert_select 'issues>issue>subject', :text => '1'
get '/issues.xml', :params => {
:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
:v => {:updated_on => ['2014-01-02T12:00:00Z']}
}
assert_select 'issues>issue', :count => 1
assert_select 'issues>issue>subject', :text => '2'
get '/issues.xml', :params => {
:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='},
:v => {:updated_on => ['2014-01-02T08:00:00Z']}
}
assert_select 'issues>issue', :count => 2
end
test "GET /issues.xml with filter" do
get '/issues.xml?status_id=5'
expected_ids = Issue.visible.where(:status_id => 5).map(&:id)
assert expected_ids.any?
assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
end
end
test "GET /issues.json with filter" do
get '/issues.json?status_id=5'
json = ActiveSupport::JSON.decode(response.body)
status_ids_used = json['issues'].collect {|j| j['status']['id'] }
assert_equal 3, status_ids_used.length
assert status_ids_used.all? {|id| id == 5 }
end
test "GET /issues/:id.xml with journals" do
Journal.find(2).update_attribute(:private_notes, true)
get '/issues/1.xml?include=journals', :headers => credentials('jsmith')
assert_select 'issue journals[type=array]' do
assert_select 'journal[id="1"]' do
assert_select 'private_notes', :text => 'false'
assert_select 'details[type=array]' do
assert_select 'detail[name=status_id]' do
assert_select 'old_value', :text => '1'
assert_select 'new_value', :text => '2'
end
end
end
assert_select 'journal[id="2"]' do
assert_select 'private_notes', :text => 'true'
assert_select 'details[type=array]'
end
end
end
test "GET /issues/:id.xml with journals should format timestamps in ISO 8601" do
get '/issues/1.xml?include=journals'
iso_date = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/
assert_select 'issue>created_on', :text => iso_date
assert_select 'issue>updated_on', :text => iso_date
assert_select 'issue journal>created_on', :text => iso_date
end
test "GET /issues/:id.xml with custom fields" do
get '/issues/3.xml'
assert_select 'issue custom_fields[type=array]' do
assert_select 'custom_field[id="1"]' do
assert_select 'value', :text => 'MySQL'
end
end
assert_nothing_raised do
Hash.from_xml(response.body).to_xml
end
end
test "GET /issues/:id.xml with multi custom fields" do
field = CustomField.find(1)
field.update_attribute :multiple, true
issue = Issue.find(3)
issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
issue.save!
get '/issues/3.xml'
assert_response :success
assert_select 'issue custom_fields[type=array]' do
assert_select 'custom_field[id="1"]' do
assert_select 'value[type=array] value', 2
end
end
xml = Hash.from_xml(response.body)
custom_fields = xml['issue']['custom_fields']
assert_kind_of Array, custom_fields
field = custom_fields.detect {|f| f['id'] == '1'}
assert_kind_of Hash, field
assert_equal ['MySQL', 'Oracle'], field['value'].sort
end
test "GET /issues/:id.json with multi custom fields" do
field = CustomField.find(1)
field.update_attribute :multiple, true
issue = Issue.find(3)
issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
issue.save!
get '/issues/3.json'
assert_response :success
json = ActiveSupport::JSON.decode(response.body)
custom_fields = json['issue']['custom_fields']
assert_kind_of Array, custom_fields
field = custom_fields.detect {|f| f['id'] == 1}
assert_kind_of Hash, field
assert_equal ['MySQL', 'Oracle'], field['value'].sort
end
test "GET /issues/:id.xml with empty value for multi custom field" do
field = CustomField.find(1)
field.update_attribute :multiple, true
issue = Issue.find(3)
issue.custom_field_values = {1 => ['']}
issue.save!
get '/issues/3.xml'
assert_select 'issue custom_fields[type=array]' do
assert_select 'custom_field[id="1"]' do
assert_select 'value[type=array]:empty'
end
end
xml = Hash.from_xml(response.body)
custom_fields = xml['issue']['custom_fields']
assert_kind_of Array, custom_fields
field = custom_fields.detect {|f| f['id'] == '1'}
assert_kind_of Hash, field
assert_equal [], field['value']
end
test "GET /issues/:id.json with empty value for multi custom field" do
field = CustomField.find(1)
field.update_attribute :multiple, true
issue = Issue.find(3)
issue.custom_field_values = {1 => ['']}
issue.save!
get '/issues/3.json'
assert_response :success
json = ActiveSupport::JSON.decode(response.body)
custom_fields = json['issue']['custom_fields']
assert_kind_of Array, custom_fields
field = custom_fields.detect {|f| f['id'] == 1}
assert_kind_of Hash, field
assert_equal [], field['value'].sort
end
test "GET /issues/:id.xml with attachments" do
get '/issues/3.xml?include=attachments'
assert_select 'issue attachments[type=array]' do
assert_select 'attachment', 4
assert_select 'attachment id', :text => '1' do
assert_select '~ filename', :text => 'error281.txt'
assert_select '~ content_url', :text => 'http://www.example.com/attachments/download/1/error281.txt'
end
end
end
test "GET /issues/:id.xml with subtasks" do
issue = Issue.generate_with_descendants!(:project_id => 1)
get "/issues/#{issue.id}.xml?include=children"
assert_select 'issue id', :text => issue.id.to_s do
assert_select '~ children[type=array] > issue', 2
assert_select '~ children[type=array] > issue > children', 1
end
end
test "GET /issues/:id.json with subtasks" do
issue = Issue.generate_with_descendants!(:project_id => 1)
get "/issues/#{issue.id}.json?include=children"
json = ActiveSupport::JSON.decode(response.body)
assert_equal 2, json['issue']['children'].size
assert_equal 1, json['issue']['children'].select {|child| child.key?('children')}.size
end
test "GET /issues/:id.json with no spent time should return floats" do
issue = Issue.generate!
get "/issues/#{issue.id}.json"
json = ActiveSupport::JSON.decode(response.body)
assert_kind_of Float, json['issue']['spent_hours']
assert_kind_of Float, json['issue']['total_spent_hours']
end
def test_show_should_include_issue_attributes
get '/issues/1.xml'
assert_select 'issue>is_private', :text => 'false'
end
test "GET /issues/:id.xml?include=watchers should include watchers" do
Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
get '/issues/1.xml?include=watchers', :headers => credentials('jsmith')
assert_response :ok
assert_equal 'application/xml', response.content_type
assert_select 'issue' do
assert_select 'watchers', Issue.find(1).watchers.count
assert_select 'watchers' do
assert_select 'user[id="3"]'
end
end
end
test "GET /issues/:id.xml should not disclose associated changesets from projects the user has no access to" do
project = Project.generate!(:is_public => false)
repository = Repository::Subversion.create!(:project => project, :url => "svn://localhost")
Issue.find(1).changesets << Changeset.generate!(:repository => repository)
assert Issue.find(1).changesets.any?
get '/issues/1.xml?include=changesets', :headers => credentials('jsmith')
# the user jsmith has no permission to view the associated changeset
assert_select 'issue changesets[type=array]' do
assert_select 'changeset', 0
end
end
test "GET /issues/:id.xml should contains total_estimated_hours and total_spent_hours" do
parent = Issue.find(3)
parent.update_columns :estimated_hours => 2.0
child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
TimeEntry.create!(:project => child.project, :issue => child, :user => child.author, :spent_on => child.author.today,
:hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first.id)
get '/issues/3.xml'
assert_equal 'application/xml', response.content_type
assert_select 'issue' do
assert_select 'estimated_hours', parent.estimated_hours.to_s
assert_select 'total_estimated_hours', (parent.estimated_hours.to_f + 3.0).to_s
assert_select 'spent_hours', parent.spent_hours.to_s
assert_select 'total_spent_hours', (parent.spent_hours.to_f + 2.5).to_s
end
end
test "GET /issues/:id.xml should contains total_estimated_hours, and should not contains spent_hours and total_spent_hours when permission does not exists" do
parent = Issue.find(3)
parent.update_columns :estimated_hours => 2.0
child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
# remove permission!
Role.anonymous.remove_permission! :view_time_entries
#Role.all.each { |role| role.remove_permission! :view_time_entries }
get '/issues/3.xml'
assert_equal 'application/xml', response.content_type
assert_select 'issue' do
assert_select 'estimated_hours', parent.estimated_hours.to_s
assert_select 'total_estimated_hours', (parent.estimated_hours.to_f + 3.0).to_s
assert_select 'spent_hours', false
assert_select 'total_spent_hours', false
end
end
test "GET /issues/:id.xml should contains visible spent_hours only" do
user = User.find_by_login('jsmith')
Role.find(1).update(:time_entries_visibility => 'own')
parent = Issue.find(3)
child = Issue.generate!(:parent_issue_id => parent.id)
TimeEntry.generate!(:user => user, :hours => 5.5, :issue_id => parent.id)
TimeEntry.generate!(:user => user, :hours => 2, :issue_id => child.id)
TimeEntry.generate!(:user => User.find(1), :hours => 100, :issue_id => child.id)
get '/issues/3.xml', :headers => credentials(user.login)
assert_equal 'application/xml', response.content_type
assert_select 'issue' do
assert_select 'spent_hours', '5.5'
assert_select 'total_spent_hours', '7.5'
end
end
test "GET /issues/:id.json should contains total_estimated_hours and total_spent_hours" do
parent = Issue.find(3)
parent.update_columns :estimated_hours => 2.0
child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
TimeEntry.create!(:project => child.project, :issue => child, :user => child.author, :spent_on => child.author.today,
:hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first.id)
get '/issues/3.json'
assert_equal 'application/json', response.content_type
json = ActiveSupport::JSON.decode(response.body)
assert_equal parent.estimated_hours, json['issue']['estimated_hours']
assert_equal (parent.estimated_hours.to_f + 3.0), json['issue']['total_estimated_hours']
assert_equal parent.spent_hours, json['issue']['spent_hours']
assert_equal (parent.spent_hours.to_f + 2.5), json['issue']['total_spent_hours']
end
test "GET /issues/:id.json should contains total_estimated_hours, and should not contains spent_hours and total_spent_hours when permission does not exists" do
parent = Issue.find(3)
parent.update_columns :estimated_hours => 2.0
child = Issue.generate!(:parent_issue_id => parent.id, :estimated_hours => 3.0)
# remove permission!
Role.anonymous.remove_permission! :view_time_entries
#Role.all.each { |role| role.remove_permission! :view_time_entries }
get '/issues/3.json'
assert_equal 'application/json', response.content_type
json = ActiveSupport::JSON.decode(response.body)
assert_equal parent.estimated_hours, json['issue']['estimated_hours']
assert_equal (parent.estimated_hours.to_f + 3.0), json['issue']['total_estimated_hours']
assert_nil json['issue']['spent_hours']
assert_nil json['issue']['total_spent_hours']
end
test "GET /issues/:id.json should contains visible spent_hours only" do
user = User.find_by_login('jsmith')
Role.find(1).update(:time_entries_visibility => 'own')
parent = Issue.find(3)
child = Issue.generate!(:parent_issue_id => parent.id)
TimeEntry.generate!(:user => user, :hours => 5.5, :issue_id => parent.id)
TimeEntry.generate!(:user => user, :hours => 2, :issue_id => child.id)
TimeEntry.generate!(:user => User.find(1), :hours => 100, :issue_id => child.id)
get '/issues/3.json', :headers => credentials(user.login)
assert_equal 'application/json', response.content_type
json = ActiveSupport::JSON.decode(response.body)
assert_equal 5.5, json['issue']['spent_hours']
assert_equal 7.5, json['issue']['total_spent_hours']
end
test "POST /issues.xml should create an issue with the attributes" do
payload = <<-XML
<?xml version="1.0" encoding="UTF-8" ?>
<issue>
<project_id>1</project_id>
<tracker_id>2</tracker_id>
<status_id>3</status_id>
<subject>API test</subject>
</issue>
XML
assert_difference('Issue.count') do
post '/issues.xml',
:params => payload,
:headers => {"CONTENT_TYPE" => 'application/xml'}.merge(credentials('jsmith'))
end
issue = Issue.order('id DESC').first
assert_equal 1, issue.project_id
assert_equal 2, issue.tracker_id
assert_equal 3, issue.status_id
assert_equal 'API test', issue.subject
assert_response :created
assert_equal 'application/xml', @response.content_type
assert_select 'issue > id', :text => issue.id.to_s
end
test "POST /issues.xml with watcher_user_ids should create issue with watchers" do
assert_difference('Issue.count') do
post '/issues.xml',
:params => {
:issue => {
:project_id => 1, :subject => 'Watchers',
:tracker_id => 2, :status_id => 3, :watcher_user_ids => [3, 1]
}
},
:headers => credentials('jsmith')
assert_response :created
end
issue = Issue.order('id desc').first
assert_equal 2, issue.watchers.size
assert_equal [1, 3], issue.watcher_user_ids.sort
end
test "POST /issues.xml with failure should return errors" do
assert_no_difference('Issue.count') do
post '/issues.xml',
:params => {:issue => {:project_id => 1}},
:headers => credentials('jsmith')
end
assert_select 'errors error', :text => "Subject cannot be blank"
end
test "POST /issues.json should create an issue with the attributes" do
payload = <<-JSON
{
"issue": {
"project_id": "1",
"tracker_id": "2",
"status_id": "3",
"subject": "API test"
}
}
JSON
assert_difference('Issue.count') do
post '/issues.json',
:params => payload,
:headers => {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
end
issue = Issue.order('id DESC').first
assert_equal 1, issue.project_id
assert_equal 2, issue.tracker_id
assert_equal 3, issue.status_id
assert_equal 'API test', issue.subject
end
test "POST /issues.json should accept project identifier as project_id" do
assert_difference('Issue.count') do
post '/issues.json',
:params => {:issue => {:project_id => 'subproject1', :tracker_id => 2, :subject => 'Foo'}},
:headers => credentials('jsmith')
assert_response :created
end
end
test "POST /issues.json without tracker_id should accept custom fields" do
field = IssueCustomField.generate!(
:field_format => 'list',
:multiple => true,
:possible_values => ["V1", "V2", "V3"],
:default_value => "V2",
:is_for_all => true,
:trackers => Tracker.all.to_a
)
payload = <<-JSON
{
"issue": {
"project_id": "1",
"subject": "Multivalued custom field",
"custom_field_values":{"#{field.id}":["V1","V3"]}
}
}
JSON
assert_difference('Issue.count') do
post '/issues.json',
:params => payload,
:headers => {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
end
assert_response :created
issue = Issue.order('id DESC').first
assert_equal ["V1", "V3"], issue.custom_field_value(field).sort
end
test "POST /issues.json with omitted custom field should set default value" do
field = IssueCustomField.generate!(:default_value => "Default")
issue = new_record(Issue) do
post '/issues.json',
:params => {:issue => {:project_id => 1, :subject => 'API', :custom_field_values => {}}},
:headers => credentials('jsmith')
end
assert_equal "Default", issue.custom_field_value(field)
end
test "POST /issues.json with custom field set to blank should not set default value" do
field = IssueCustomField.generate!(:default_value => "Default")
issue = new_record(Issue) do
post '/issues.json',
:params => {:issue => {:project_id => 1, :subject => 'API', :custom_field_values => {field.id.to_s => ""}}},
:headers => credentials('jsmith')
end
assert_equal "", issue.custom_field_value(field)
end
test "POST /issues.json with failure should return errors" do
assert_no_difference('Issue.count') do
post '/issues.json',
:params => {:issue => {:project_id => 1}},
:headers => credentials('jsmith')
end
json = ActiveSupport::JSON.decode(response.body)
assert json['errors'].include?("Subject cannot be blank")
end
test "POST /issues.json with invalid project_id should respond with 422" do
post '/issues.json',
:params => {:issue => {:project_id => 999, :subject => "API"}},
:headers => credentials('jsmith')
assert_response 422
end
test "PUT /issues/:id.xml" do
assert_difference('Journal.count') do
put '/issues/6.xml',
:params => {:issue => {:subject => 'API update', :notes => 'A new note'}},
:headers => credentials('jsmith')
end
issue = Issue.find(6)
assert_equal "API update", issue.subject
journal = Journal.last
assert_equal "A new note", journal.notes
end
test "PUT /issues/:id.xml with custom fields" do
put '/issues/3.xml',
:params => {:issue => {:custom_fields => [
{'id' => '1', 'value' => 'PostgreSQL' },
{'id' => '2', 'value' => '150'}
]}
},
:headers => credentials('jsmith')
issue = Issue.find(3)
assert_equal '150', issue.custom_value_for(2).value
assert_equal 'PostgreSQL', issue.custom_value_for(1).value
end
test "PUT /issues/:id.xml with multi custom fields" do
field = CustomField.find(1)
field.update_attribute :multiple, true
put '/issues/3.xml',
:params => {:issue => {:custom_fields => [
{'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] },
{'id' => '2', 'value' => '150'}
]}
},
:headers => credentials('jsmith')
issue = Issue.find(3)
assert_equal '150', issue.custom_value_for(2).value
assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort
end
test "PUT /issues/:id.xml with project change" do
put '/issues/3.xml',
:params => {:issue => {:project_id => 2, :subject => 'Project changed'}},
:headers => credentials('jsmith')
issue = Issue.find(3)
assert_equal 2, issue.project_id
assert_equal 'Project changed', issue.subject
end
test "PUT /issues/:id.xml with notes only" do
assert_difference('Journal.count') do
put '/issues/6.xml',
:params => {:issue => {:notes => 'Notes only'}},
:headers => credentials('jsmith')
end
journal = Journal.last
assert_equal "Notes only", journal.notes
end
test "PUT /issues/:id.json with omitted custom field should not change blank value to default value" do
field = IssueCustomField.generate!(:default_value => "Default")
issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id.to_s => ""})
assert_equal "", issue.reload.custom_field_value(field)
assert_difference('Journal.count') do
put "/issues/#{issue.id}.json",
:params => {:issue => {:custom_field_values => {}, :notes => 'API'}},
:headers => credentials('jsmith')
end
assert_equal "", issue.reload.custom_field_value(field)
end
test "PUT /issues/:id.json with custom field set to blank should not change blank value to default value" do
field = IssueCustomField.generate!(:default_value => "Default")
issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id.to_s => ""})
assert_equal "", issue.reload.custom_field_value(field)
assert_difference('Journal.count') do
put "/issues/#{issue.id}.json",
:params => {:issue => {:custom_field_values => {field.id.to_s => ""}, :notes => 'API'}},
:headers => credentials('jsmith')
end
assert_equal "", issue.reload.custom_field_value(field)
end
test "PUT /issues/:id.json with tracker change and omitted custom field specific to that tracker should set default value" do
field = IssueCustomField.generate!(:default_value => "Default", :tracker_ids => [2])
issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
assert_difference('Journal.count') do
put "/issues/#{issue.id}.json",
:params => {:issue => {:tracker_id => 2, :custom_field_values => {}, :notes => 'API'}},
:headers => credentials('jsmith')
end
assert_equal 2, issue.reload.tracker_id
assert_equal "Default", issue.reload.custom_field_value(field)
end
test "PUT /issues/:id.json with tracker change and custom field specific to that tracker set to blank should not set default value" do
field = IssueCustomField.generate!(:default_value => "Default", :tracker_ids => [2])
issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
assert_difference('Journal.count') do
put "/issues/#{issue.id}.json",
:params => {:issue => {:tracker_id => 2, :custom_field_values => {field.id.to_s => ""}, :notes => 'API'}},
:headers => credentials('jsmith')
end
assert_equal 2, issue.reload.tracker_id
assert_equal "", issue.reload.custom_field_value(field)
end
test "PUT /issues/:id.xml with failed update" do
put '/issues/6.xml',
:params => {:issue => {:subject => ''}},
:headers => credentials('jsmith')
assert_response :unprocessable_entity
assert_select 'errors error', :text => "Subject cannot be blank"
end
test "PUT /issues/:id.xml with invalid assignee should return error" do
user = User.generate!
put '/issues/6.xml',
:params => {:issue => {:assigned_to_id => user.id}},
:headers => credentials('jsmith')
assert_response :unprocessable_entity
assert_select 'errors error', :text => "Assignee is invalid"
end
test "PUT /issues/:id.json" do
assert_difference('Journal.count') do
put '/issues/6.json',
:params => {:issue => {:subject => 'API update', :notes => 'A new note'}},
:headers => credentials('jsmith')
assert_response :ok
assert_equal '', response.body
end
issue = Issue.find(6)
assert_equal "API update", issue.subject
journal = Journal.last
assert_equal "A new note", journal.notes
end
test "PUT /issues/:id.json with failed update" do
put '/issues/6.json',
:params => {:issue => {:subject => ''}},
:headers => credentials('jsmith')
assert_response :unprocessable_entity
json = ActiveSupport::JSON.decode(response.body)
assert json['errors'].include?("Subject cannot be blank")
end
test "DELETE /issues/:id.xml" do
assert_difference('Issue.count', -1) do
delete '/issues/6.xml', :headers => credentials('jsmith')
assert_response :ok
assert_equal '', response.body
end
assert_nil Issue.find_by_id(6)
end
test "DELETE /issues/:id.json" do
assert_difference('Issue.count', -1) do
delete '/issues/6.json', :headers => credentials('jsmith')
assert_response :ok
assert_equal '', response.body
end
assert_nil Issue.find_by_id(6)
end
test "POST /issues/:id/watchers.xml should add watcher" do
assert_difference 'Watcher.count' do
post '/issues/1/watchers.xml',
:params => {:user_id => 3},
:headers => credentials('jsmith')
assert_response :ok
assert_equal '', response.body
end
watcher = Watcher.order('id desc').first
assert_equal Issue.find(1), watcher.watchable
assert_equal User.find(3), watcher.user
end
test "DELETE /issues/:id/watchers/:user_id.xml should remove watcher" do
Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
assert_difference 'Watcher.count', -1 do
delete '/issues/1/watchers/3.xml', :headers => credentials('jsmith')
assert_response :ok
assert_equal '', response.body
end
assert_equal false, Issue.find(1).watched_by?(User.find(3))
end
def test_create_issue_with_uploaded_file
token = xml_upload('test_create_with_upload', credentials('jsmith'))
attachment = Attachment.find_by_token(token)
# create the issue with the upload's token
assert_difference 'Issue.count' do
post '/issues.xml',
:params => {:issue => {:project_id => 1, :subject => 'Uploaded file',
:uploads => [{:token => token, :filename => 'test.txt',
:content_type => 'text/plain'}]}},
:headers => credentials('jsmith')
assert_response :created
end
issue = Issue.order('id DESC').first
assert_equal 1, issue.attachments.count
assert_equal attachment, issue.attachments.first
attachment.reload
assert_equal 'test.txt', attachment.filename
assert_equal 'text/plain', attachment.content_type
assert_equal 'test_create_with_upload'.size, attachment.filesize
assert_equal 2, attachment.author_id
# get the issue with its attachments
get "/issues/#{issue.id}.xml?include=attachments"
assert_response :success
xml = Hash.from_xml(response.body)
attachments = xml['issue']['attachments']
assert_kind_of Array, attachments
assert_equal 1, attachments.size
url = attachments.first['content_url']
assert_not_nil url
# download the attachment
get url
assert_response :success
assert_equal 'test_create_with_upload', response.body
end
def test_create_issue_with_multiple_uploaded_files_as_xml
token1 = xml_upload('File content 1', credentials('jsmith'))
token2 = xml_upload('File content 2', credentials('jsmith'))
payload = <<-XML
<?xml version="1.0" encoding="UTF-8" ?>
<issue>
<project_id>1</project_id>
<tracker_id>1</tracker_id>
<subject>Issue with multiple attachments</subject>
<uploads type="array">
<upload>
<token>#{token1}</token>
<filename>test1.txt</filename>
</upload>
<upload>
<token>#{token2}</token>
<filename>test1.txt</filename>
</upload>
</uploads>
</issue>
XML
assert_difference 'Issue.count' do
post '/issues.xml',
:params => payload,
:headers => {"CONTENT_TYPE" => 'application/xml'}.merge(credentials('jsmith'))
assert_response :created
end
issue = Issue.order('id DESC').first
assert_equal 2, issue.attachments.count
end
def test_create_issue_with_multiple_uploaded_files_as_json
token1 = json_upload('File content 1', credentials('jsmith'))
token2 = json_upload('File content 2', credentials('jsmith'))
payload = <<-JSON
{
"issue": {
"project_id": "1",
"tracker_id": "1",
"subject": "Issue with multiple attachments",
"uploads": [
{"token": "#{token1}", "filename": "test1.txt"},
{"token": "#{token2}", "filename": "test2.txt"}
]
}
}
JSON
assert_difference 'Issue.count' do
post '/issues.json',
:params => payload,
:headers => {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
assert_response :created
end
issue = Issue.order('id DESC').first
assert_equal 2, issue.attachments.count
end
def test_update_issue_with_uploaded_file
token = xml_upload('test_upload_with_upload', credentials('jsmith'))
attachment = Attachment.find_by_token(token)
# update the issue with the upload's token
assert_difference 'Journal.count' do
put '/issues/1.xml',
:params => {:issue => {:notes => 'Attachment added',
:uploads => [{:token => token, :filename => 'test.txt',
:content_type => 'text/plain'}]}},
:headers => credentials('jsmith')
assert_response :ok
assert_equal '', @response.body
end
issue = Issue.find(1)
assert_include attachment, issue.attachments
end
end

View file

@ -0,0 +1,72 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::JsonpTest < Redmine::ApiTest::Base
fixtures :trackers
def test_should_ignore_jsonp_callback_with_jsonp_disabled
with_settings :jsonp_enabled => '0' do
get '/trackers.json?jsonp=handler'
end
assert_response :success
assert_match %r{^\{"trackers":.+\}$}, response.body
assert_equal 'application/json; charset=utf-8', response.headers['Content-Type']
end
def test_jsonp_should_accept_callback_param
with_settings :jsonp_enabled => '1' do
get '/trackers.json?callback=handler'
end
assert_response :success
assert_match %r{^handler\(\{"trackers":.+\}\)$}, response.body
assert_equal 'application/javascript; charset=utf-8', response.headers['Content-Type']
end
def test_jsonp_should_accept_jsonp_param
with_settings :jsonp_enabled => '1' do
get '/trackers.json?jsonp=handler'
end
assert_response :success
assert_match %r{^handler\(\{"trackers":.+\}\)$}, response.body
assert_equal 'application/javascript; charset=utf-8', response.headers['Content-Type']
end
def test_jsonp_should_strip_invalid_characters_from_callback
with_settings :jsonp_enabled => '1' do
get '/trackers.json?callback=+-aA$1_.'
end
assert_response :success
assert_match %r{^aA1_.\(\{"trackers":.+\}\)$}, response.body
assert_equal 'application/javascript; charset=utf-8', response.headers['Content-Type']
end
def test_jsonp_without_callback_should_return_json
with_settings :jsonp_enabled => '1' do
get '/trackers.json?callback='
end
assert_response :success
assert_match %r{^\{"trackers":.+\}$}, response.body
assert_equal 'application/json; charset=utf-8', response.headers['Content-Type']
end
end

View file

@ -0,0 +1,174 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::MembershipsTest < Redmine::ApiTest::Base
fixtures :projects, :users, :roles, :members, :member_roles
test "GET /projects/:project_id/memberships.xml should return memberships" do
get '/projects/1/memberships.xml', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'memberships[type=array] membership id', :text => '2' do
assert_select '~ user[id="3"][name="Dave Lopper"]'
assert_select '~ roles role[id="2"][name=Developer]'
end
end
test "GET /projects/:project_id/memberships.json should return memberships" do
get '/projects/1/memberships.json', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/json', @response.content_type
json = ActiveSupport::JSON.decode(response.body)
assert_equal 3, json["total_count"]
assert_equal 25, json["limit"]
assert_equal 0, json["offset"]
assert_include({
"id"=>1,
"project" => {"name"=>"eCookbook", "id"=>1},
"roles" => [{"name"=>"Manager", "id"=>1}],
"user" => {"name"=>"John Smith", "id"=>2}
},
json["memberships"]
)
end
test "GET /projects/:project_id/memberships.xml should succeed for closed project" do
project = Project.find(1)
project.close
assert !project.reload.active?
get '/projects/1/memberships.json', :headers => credentials('jsmith')
assert_response :success
end
test "GET /projects/:project_id/memberships.xml should include locked users" do
assert User.find(3).lock!
get '/projects/ecookbook/memberships.xml', :headers => credentials('jsmith')
assert_response :success
assert_select 'memberships[type=array] membership id', :text => '2' do
assert_select '~ user[id="3"][name="Dave Lopper"]'
end
end
test "POST /projects/:project_id/memberships.xml should create the membership" do
assert_difference 'Member.count' do
post '/projects/1/memberships.xml',
:params => {:membership => {:user_id => 7, :role_ids => [2,3]}},
:headers => credentials('jsmith')
assert_response :created
end
end
test "POST /projects/:project_id/memberships.xml should create the group membership" do
group = Group.find(11)
assert_difference 'Member.count', 1 + group.users.count do
post '/projects/1/memberships.xml',
:params => {:membership => {:user_id => 11, :role_ids => [2,3]}},
:headers => credentials('jsmith')
assert_response :created
end
end
test "POST /projects/:project_id/memberships.xml with invalid parameters should return errors" do
assert_no_difference 'Member.count' do
post '/projects/1/memberships.xml',
:params => {:membership => {:role_ids => [2,3]}},
:headers => credentials('jsmith')
assert_response :unprocessable_entity
assert_equal 'application/xml', @response.content_type
assert_select 'errors error', :text => "Principal cannot be blank"
end
end
test "GET /memberships/:id.xml should return the membership" do
get '/memberships/2.xml', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'membership id', :text => '2' do
assert_select '~ user[id="3"][name="Dave Lopper"]'
assert_select '~ roles role[id="2"][name=Developer]'
end
end
test "GET /memberships/:id.json should return the membership" do
get '/memberships/2.json', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/json', @response.content_type
json = ActiveSupport::JSON.decode(response.body)
assert_equal(
{"membership" => {
"id" => 2,
"project" => {"name"=>"eCookbook", "id"=>1},
"roles" => [{"name"=>"Developer", "id"=>2}],
"user" => {"name"=>"Dave Lopper", "id"=>3}}
},
json)
end
test "PUT /memberships/:id.xml should update the membership" do
assert_not_equal [1,2], Member.find(2).role_ids.sort
assert_no_difference 'Member.count' do
put '/memberships/2.xml',
:params => {:membership => {:user_id => 3, :role_ids => [1,2]}},
:headers => credentials('jsmith')
assert_response :ok
assert_equal '', @response.body
end
member = Member.find(2)
assert_equal [1,2], member.role_ids.sort
end
test "PUT /memberships/:id.xml with invalid parameters should return errors" do
put '/memberships/2.xml',
:params => {:membership => {:user_id => 3, :role_ids => [99]}},
:headers => credentials('jsmith')
assert_response :unprocessable_entity
assert_equal 'application/xml', @response.content_type
assert_select 'errors error', :text => "Role cannot be empty"
end
test "DELETE /memberships/:id.xml should destroy the membership" do
assert_difference 'Member.count', -1 do
delete '/memberships/2.xml', :headers => credentials('jsmith')
assert_response :ok
assert_equal '', @response.body
end
assert_nil Member.find_by_id(2)
end
test "DELETE /memberships/:id.xml should respond with 422 on failure" do
assert_no_difference 'Member.count' do
# A membership with an inherited role cannot be deleted
Member.find(2).member_roles.first.update_attribute :inherited_from, 99
delete '/memberships/2.xml', :headers => credentials('jsmith')
assert_response :unprocessable_entity
end
end
end

View file

@ -0,0 +1,61 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::NewsTest < Redmine::ApiTest::Base
fixtures :projects, :trackers, :issue_statuses, :issues,
:enumerations, :users, :issue_categories,
:projects_trackers,
:roles,
:member_roles,
:members,
:enabled_modules,
:news
test "GET /news.xml should return news" do
get '/news.xml'
assert_select 'news[type=array] news id', :text => '2'
end
test "GET /news.json should return news" do
get '/news.json'
json = ActiveSupport::JSON.decode(response.body)
assert_kind_of Hash, json
assert_kind_of Array, json['news']
assert_kind_of Hash, json['news'].first
assert_equal 2, json['news'].first['id']
end
test "GET /projects/:project_id/news.xml should return news" do
get '/projects/ecookbook/news.xml'
assert_select 'news[type=array] news id', :text => '2'
end
test "GET /projects/:project_id/news.json should return news" do
get '/projects/ecookbook/news.json'
json = ActiveSupport::JSON.decode(response.body)
assert_kind_of Hash, json
assert_kind_of Array, json['news']
assert_kind_of Hash, json['news'].first
assert_equal 2, json['news'].first['id']
end
end

View file

@ -0,0 +1,252 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::ProjectsTest < Redmine::ApiTest::Base
fixtures :projects, :versions, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details,
:trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages,
:attachments, :custom_fields, :custom_values, :time_entries, :issue_categories
def setup
super
set_tmp_attachments_directory
end
test "GET /projects.xml should return projects" do
get '/projects.xml'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'projects>project>id', :text => '1'
assert_select 'projects>project>status', :text => '1'
assert_select 'projects>project>is_public', :text => 'true'
end
test "GET /projects.json should return projects" do
get '/projects.json'
assert_response :success
assert_equal 'application/json', @response.content_type
json = ActiveSupport::JSON.decode(response.body)
assert_kind_of Hash, json
assert_kind_of Array, json['projects']
assert_kind_of Hash, json['projects'].first
assert json['projects'].first.has_key?('id')
end
test "GET /projects.xml with include=issue_categories should return categories" do
get '/projects.xml?include=issue_categories'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'issue_categories[type=array] issue_category[id="2"][name=Recipes]'
end
test "GET /projects.xml with include=trackers should return trackers" do
get '/projects.xml?include=trackers'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'trackers[type=array] tracker[id="2"][name="Feature request"]'
end
test "GET /projects.xml with include=enabled_modules should return enabled modules" do
get '/projects.xml?include=enabled_modules'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'enabled_modules[type=array] enabled_module[name=issue_tracking]'
end
test "GET /projects/:id.xml should return the project" do
get '/projects/1.xml'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'project>id', :text => '1'
assert_select 'project>status', :text => '1'
assert_select 'project>is_public', :text => 'true'
assert_select 'custom_field[name="Development status"]', :text => 'Stable'
assert_select 'trackers', 0
assert_select 'issue_categories', 0
end
test "GET /projects/:id.json should return the project" do
get '/projects/1.json'
json = ActiveSupport::JSON.decode(response.body)
assert_kind_of Hash, json
assert_kind_of Hash, json['project']
assert_equal 1, json['project']['id']
end
test "GET /projects/:id.xml with hidden custom fields should not display hidden custom fields" do
ProjectCustomField.find_by_name('Development status').update_attribute :visible, false
get '/projects/1.xml'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'custom_field[name=?]', 'Development status', 0
end
test "GET /projects/:id.xml with include=issue_categories should return categories" do
get '/projects/1.xml?include=issue_categories'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'issue_categories[type=array] issue_category[id="2"][name=Recipes]'
end
test "GET /projects/:id.xml with include=time_entry_activities should return activities" do
get '/projects/1.xml?include=time_entry_activities'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'time_entry_activities[type=array] time_entry_activity[id="10"][name=Development]'
end
test "GET /projects/:id.xml with include=trackers should return trackers" do
get '/projects/1.xml?include=trackers'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'trackers[type=array] tracker[id="2"][name="Feature request"]'
end
test "GET /projects/:id.xml with include=enabled_modules should return enabled modules" do
get '/projects/1.xml?include=enabled_modules'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'enabled_modules[type=array] enabled_module[name=issue_tracking]'
end
test "POST /projects.xml with valid parameters should create the project" do
with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
assert_difference('Project.count') do
post '/projects.xml',
:params => {:project => {:name => 'API test', :identifier => 'api-test'}},
:headers => credentials('admin')
end
end
project = Project.order('id DESC').first
assert_equal 'API test', project.name
assert_equal 'api-test', project.identifier
assert_equal ['issue_tracking', 'repository'], project.enabled_module_names.sort
assert_equal Tracker.all.size, project.trackers.size
assert_response :created
assert_equal 'application/xml', @response.content_type
assert_select 'project id', :text => project.id.to_s
end
test "POST /projects.xml should accept enabled_module_names attribute" do
assert_difference('Project.count') do
post '/projects.xml',
:params => {:project => {:name => 'API test', :identifier => 'api-test', :enabled_module_names => ['issue_tracking', 'news', 'time_tracking']}},
:headers => credentials('admin')
end
project = Project.order('id DESC').first
assert_equal ['issue_tracking', 'news', 'time_tracking'], project.enabled_module_names.sort
end
test "POST /projects.xml should accept tracker_ids attribute" do
assert_difference('Project.count') do
post '/projects.xml',
:params => {:project => {:name => 'API test', :identifier => 'api-test', :tracker_ids => [1, 3]}},
:headers => credentials('admin')
end
project = Project.order('id DESC').first
assert_equal [1, 3], project.trackers.map(&:id).sort
end
test "POST /projects.xml with invalid parameters should return errors" do
assert_no_difference('Project.count') do
post '/projects.xml',
:params => {:project => {:name => 'API test'}},
:headers => credentials('admin')
end
assert_response :unprocessable_entity
assert_equal 'application/xml', @response.content_type
assert_select 'errors error', :text => "Identifier cannot be blank"
end
test "PUT /projects/:id.xml with valid parameters should update the project" do
assert_no_difference 'Project.count' do
put '/projects/2.xml',
:params => {:project => {:name => 'API update'}},
:headers => credentials('jsmith')
end
assert_response :ok
assert_equal '', @response.body
assert_equal 'application/xml', @response.content_type
project = Project.find(2)
assert_equal 'API update', project.name
end
test "PUT /projects/:id.xml should accept enabled_module_names attribute" do
assert_no_difference 'Project.count' do
put '/projects/2.xml',
:params => {:project => {:name => 'API update', :enabled_module_names => ['issue_tracking', 'news', 'time_tracking']}},
:headers => credentials('admin')
end
assert_response :ok
assert_equal '', @response.body
project = Project.find(2)
assert_equal ['issue_tracking', 'news', 'time_tracking'], project.enabled_module_names.sort
end
test "PUT /projects/:id.xml should accept tracker_ids attribute" do
assert_no_difference 'Project.count' do
put '/projects/2.xml',
:params => {:project => {:name => 'API update', :tracker_ids => [1, 3]}},
:headers => credentials('admin')
end
assert_response :ok
assert_equal '', @response.body
project = Project.find(2)
assert_equal [1, 3], project.trackers.map(&:id).sort
end
test "PUT /projects/:id.xml with invalid parameters should return errors" do
assert_no_difference('Project.count') do
put '/projects/2.xml',
:params => {:project => {:name => ''}},
:headers => credentials('admin')
end
assert_response :unprocessable_entity
assert_equal 'application/xml', @response.content_type
assert_select 'errors error', :text => "Name cannot be blank"
end
test "DELETE /projects/:id.xml should delete the project" do
assert_difference('Project.count',-1) do
delete '/projects/2.xml', :headers => credentials('admin')
end
assert_response :ok
assert_equal '', @response.body
assert_nil Project.find_by_id(2)
end
end

View file

@ -0,0 +1,39 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::QueriesTest < Redmine::ApiTest::Base
fixtures :projects, :trackers, :issue_statuses, :issues,
:enumerations, :users, :issue_categories,
:projects_trackers,
:roles,
:member_roles,
:members,
:enabled_modules,
:queries
test "GET /queries.xml should return queries" do
get '/queries.xml'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'queries[type=array] query id', :text => '4' do
assert_select '~ name', :text => 'Public query for all projects'
end
end
end

View file

@ -0,0 +1,62 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::RolesTest < Redmine::ApiTest::Base
fixtures :roles
test "GET /roles.xml should return the roles" do
get '/roles.xml'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'roles role', 3
assert_select 'roles[type=array] role id', :text => '2' do
assert_select '~ name', :text => 'Developer'
end
end
test "GET /roles.json should return the roles" do
get '/roles.json'
assert_response :success
assert_equal 'application/json', @response.content_type
json = ActiveSupport::JSON.decode(response.body)
assert_kind_of Hash, json
assert_kind_of Array, json['roles']
assert_equal 3, json['roles'].size
assert_include({'id' => 2, 'name' => 'Developer'}, json['roles'])
end
test "GET /roles/:id.xml should return the role" do
get '/roles/1.xml'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'role' do
assert_select 'name', :text => 'Manager'
assert_select 'role permissions[type=array]' do
assert_select 'permission', Role.find(1).permissions.size
assert_select 'permission', :text => 'view_issues'
end
end
end
end

View file

@ -0,0 +1,92 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::SearchTest < Redmine::ApiTest::Base
fixtures :projects, :projects_trackers,
:enabled_modules, :roles, :users, :members, :member_roles,
:issues, :trackers, :issue_statuses, :enumerations,
:workflows,
:custom_fields, :custom_values,
:custom_fields_projects, :custom_fields_trackers,
:repositories, :changesets
test "GET /search.xml should return xml content" do
get '/search.xml'
assert_response :success
assert_equal 'application/xml', @response.content_type
end
test "GET /search.json should return json content" do
get '/search.json'
assert_response :success
assert_equal 'application/json', @response.content_type
json = ActiveSupport::JSON.decode(response.body)
assert_kind_of Hash, json
assert_kind_of Array, json['results']
end
test "GET /search.xml without query strings should return empty results" do
get '/search.xml', :params => {:q => '', :all_words => ''}
assert_response :success
assert_select 'result', 0
end
test "GET /search.xml with query strings should return results" do
issue = Issue.generate!(:subject => 'searchapi')
get '/search.xml', :params => {:q => 'searchapi', :all_words => ''}
assert_response :success
assert_select 'results[type=array]' do
assert_select 'result', 1
assert_select 'result' do
assert_select 'id', :text => issue.id.to_s
assert_select 'title', :text => "Bug ##{issue.id} (New): searchapi"
assert_select 'type', :text => 'issue'
assert_select 'url', :text => "http://www.example.com/issues/#{issue.id}"
assert_select 'description', :text => ''
assert_select 'datetime'
end
end
end
test "GET /search.xml should paginate" do
issue = (0..10).map {Issue.generate! :subject => 'search_with_limited_results'}.reverse.map(&:id)
get '/search.json', :params => {:q => 'search_with_limited_results', :limit => 4}
json = ActiveSupport::JSON.decode(response.body)
assert_equal 11, json['total_count']
assert_equal 0, json['offset']
assert_equal 4, json['limit']
assert_equal issue[0..3], json['results'].map {|r| r['id']}
get '/search.json', :params => {:q => 'search_with_limited_results', :offset => 8, :limit => 4}
json = ActiveSupport::JSON.decode(response.body)
assert_equal 11, json['total_count']
assert_equal 8, json['offset']
assert_equal 4, json['limit']
assert_equal issue[8..10], json['results'].map {|r| r['id']}
end
end

View file

@ -0,0 +1,158 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::TimeEntriesTest < Redmine::ApiTest::Base
fixtures :projects, :trackers, :issue_statuses, :issues,
:enumerations, :users, :issue_categories,
:projects_trackers,
:roles,
:member_roles,
:members,
:enabled_modules,
:time_entries
test "GET /time_entries.xml should return time entries" do
get '/time_entries.xml', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'time_entries[type=array] time_entry id', :text => '2'
end
test "GET /time_entries.xml with limit should return limited results" do
get '/time_entries.xml?limit=2', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'time_entries[type=array] time_entry', 2
end
test "GET /time_entries/:id.xml should return the time entry" do
get '/time_entries/2.xml', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'time_entry id', :text => '2'
end
test "GET /time_entries/:id.xml on closed project should return the time entry" do
project = TimeEntry.find(2).project
project.close
project.save!
get '/time_entries/2.xml', :headers => credentials('jsmith')
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'time_entry id', :text => '2'
end
test "POST /time_entries.xml with issue_id should create time entry" do
assert_difference 'TimeEntry.count' do
post '/time_entries.xml',
:params => {:time_entry => {:issue_id => '1', :spent_on => '2010-12-02', :hours => '3.5', :activity_id => '11'}},
:headers => credentials('jsmith')
end
assert_response :created
assert_equal 'application/xml', @response.content_type
entry = TimeEntry.order('id DESC').first
assert_equal 'jsmith', entry.user.login
assert_equal Issue.find(1), entry.issue
assert_equal Project.find(1), entry.project
assert_equal Date.parse('2010-12-02'), entry.spent_on
assert_equal 3.5, entry.hours
assert_equal TimeEntryActivity.find(11), entry.activity
end
test "POST /time_entries.xml with issue_id should accept custom fields" do
field = TimeEntryCustomField.create!(:name => 'Test', :field_format => 'string')
assert_difference 'TimeEntry.count' do
post '/time_entries.xml',
:params => {:time_entry => {
:issue_id => '1', :spent_on => '2010-12-02', :hours => '3.5', :activity_id => '11', :custom_fields => [{:id => field.id.to_s, :value => 'accepted'}]
}},
:headers => credentials('jsmith')
end
assert_response :created
assert_equal 'application/xml', @response.content_type
entry = TimeEntry.order('id DESC').first
assert_equal 'accepted', entry.custom_field_value(field)
end
test "POST /time_entries.xml with project_id should create time entry" do
assert_difference 'TimeEntry.count' do
post '/time_entries.xml',
:params => {:time_entry => {:project_id => '1', :spent_on => '2010-12-02', :hours => '3.5', :activity_id => '11'}},
:headers => credentials('jsmith')
end
assert_response :created
assert_equal 'application/xml', @response.content_type
entry = TimeEntry.order('id DESC').first
assert_equal 'jsmith', entry.user.login
assert_nil entry.issue
assert_equal Project.find(1), entry.project
assert_equal Date.parse('2010-12-02'), entry.spent_on
assert_equal 3.5, entry.hours
assert_equal TimeEntryActivity.find(11), entry.activity
end
test "POST /time_entries.xml with invalid parameters should return errors" do
assert_no_difference 'TimeEntry.count' do
post '/time_entries.xml',
:params => {:time_entry => {:project_id => '1', :spent_on => '2010-12-02', :activity_id => '11'}},
:headers => credentials('jsmith')
end
assert_response :unprocessable_entity
assert_equal 'application/xml', @response.content_type
assert_select 'errors error', :text => "Hours cannot be blank"
end
test "PUT /time_entries/:id.xml with valid parameters should update time entry" do
assert_no_difference 'TimeEntry.count' do
put '/time_entries/2.xml',
:params => {:time_entry => {:comments => 'API Update'}},
:headers => credentials('jsmith')
end
assert_response :ok
assert_equal '', @response.body
assert_equal 'API Update', TimeEntry.find(2).comments
end
test "PUT /time_entries/:id.xml with invalid parameters should return errors" do
assert_no_difference 'TimeEntry.count' do
put '/time_entries/2.xml',
:params => {:time_entry => {:hours => '', :comments => 'API Update'}},
:headers => credentials('jsmith')
end
assert_response :unprocessable_entity
assert_equal 'application/xml', @response.content_type
assert_select 'errors error', :text => "Hours cannot be blank"
end
test "DELETE /time_entries/:id.xml should destroy time entry" do
assert_difference 'TimeEntry.count', -1 do
delete '/time_entries/2.xml', :headers => credentials('jsmith')
end
assert_response :ok
assert_equal '', @response.body
assert_nil TimeEntry.find_by_id(2)
end
end

View file

@ -0,0 +1,33 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::TrackersTest < Redmine::ApiTest::Base
fixtures :trackers
test "GET /trackers.xml should return trackers" do
get '/trackers.xml'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'trackers[type=array] tracker id', :text => '2' do
assert_select '~ name', :text => 'Feature request'
end
end
end

View file

@ -0,0 +1,318 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::UsersTest < Redmine::ApiTest::Base
fixtures :users, :email_addresses, :members, :member_roles, :roles, :projects
test "GET /users.xml should return users" do
get '/users.xml', :headers => credentials('admin')
assert_response :success
assert_equal 'application/xml', response.content_type
assert_select 'users' do
assert_select 'user', User.active.count
end
end
test "GET /users.json should return users" do
get '/users.json', :headers => credentials('admin')
assert_response :success
assert_equal 'application/json', response.content_type
json = ActiveSupport::JSON.decode(response.body)
assert json.key?('users')
assert_equal User.active.count, json['users'].size
end
test "GET /users/:id.xml should return the user" do
get '/users/2.xml'
assert_response :success
assert_select 'user id', :text => '2'
end
test "GET /users/:id.json should return the user" do
get '/users/2.json'
assert_response :success
json = ActiveSupport::JSON.decode(response.body)
assert_kind_of Hash, json
assert_kind_of Hash, json['user']
assert_equal 2, json['user']['id']
end
test "GET /users/:id.xml with include=memberships should include memberships" do
get '/users/2.xml?include=memberships'
assert_response :success
assert_select 'user memberships', 1
end
test "GET /users/:id.json with include=memberships should include memberships" do
get '/users/2.json?include=memberships'
assert_response :success
json = ActiveSupport::JSON.decode(response.body)
assert_kind_of Array, json['user']['memberships']
assert_equal [{
"id"=>1,
"project"=>{"name"=>"eCookbook", "id"=>1},
"roles"=>[{"name"=>"Manager", "id"=>1}]
}], json['user']['memberships']
end
test "GET /users/current.xml should require authentication" do
get '/users/current.xml'
assert_response 401
end
test "GET /users/current.xml should return current user" do
get '/users/current.xml', :headers => credentials('jsmith')
assert_select 'user id', :text => '2'
end
test "GET /users/:id should not return login for other user" do
get '/users/3.xml', :headers => credentials('jsmith')
assert_response :success
assert_select 'user login', 0
end
test "GET /users/:id should return login for current user" do
get '/users/2.xml', :headers => credentials('jsmith')
assert_response :success
assert_select 'user login', :text => 'jsmith'
end
test "GET /users/:id should not return api_key for other user" do
get '/users/3.xml', :headers => credentials('jsmith')
assert_response :success
assert_select 'user api_key', 0
end
test "GET /users/:id should return api_key for current user" do
get '/users/2.xml', :headers => credentials('jsmith')
assert_response :success
assert_select 'user api_key', :text => User.find(2).api_key
end
test "GET /users/:id should not return status for standard user" do
get '/users/3.xml', :headers => credentials('jsmith')
assert_response :success
assert_select 'user status', 0
end
test "GET /users/:id should return status for administrators" do
get '/users/2.xml', :headers => credentials('admin')
assert_response :success
assert_select 'user status', :text => User.find(1).status.to_s
end
test "POST /users.xml with valid parameters should create the user" do
assert_difference('User.count') do
post '/users.xml',
:params => {
:user => {
:login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname',
:mail => 'foo@example.net', :password => 'secret123',
:mail_notification => 'only_assigned'
}
},
:headers => credentials('admin')
end
user = User.order('id DESC').first
assert_equal 'foo', user.login
assert_equal 'Firstname', user.firstname
assert_equal 'Lastname', user.lastname
assert_equal 'foo@example.net', user.mail
assert_equal 'only_assigned', user.mail_notification
assert !user.admin?
assert user.check_password?('secret123')
assert_response :created
assert_equal 'application/xml', @response.content_type
assert_select 'user id', :text => user.id.to_s
end
test "POST /users.json with valid parameters should create the user" do
assert_difference('User.count') do
post '/users.json',
:params => {
:user => {
:login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname',
:mail => 'foo@example.net', :password => 'secret123',
:mail_notification => 'only_assigned'
}
},
:headers => credentials('admin')
end
user = User.order('id DESC').first
assert_equal 'foo', user.login
assert_equal 'Firstname', user.firstname
assert_equal 'Lastname', user.lastname
assert_equal 'foo@example.net', user.mail
assert !user.admin?
assert_response :created
assert_equal 'application/json', @response.content_type
json = ActiveSupport::JSON.decode(response.body)
assert_kind_of Hash, json
assert_kind_of Hash, json['user']
assert_equal user.id, json['user']['id']
end
test "POST /users.xml with with invalid parameters should return errors" do
assert_no_difference('User.count') do
post '/users.xml',
:params => {
:user =>{
:login => 'foo', :lastname => 'Lastname', :mail => 'foo'
}
},
:headers => credentials('admin')
end
assert_response :unprocessable_entity
assert_equal 'application/xml', @response.content_type
assert_select 'errors error', :text => "First name cannot be blank"
end
test "POST /users.json with with invalid parameters should return errors" do
assert_no_difference('User.count') do
post '/users.json',
:params => {
:user => {
:login => 'foo', :lastname => 'Lastname', :mail => 'foo'
}
},
:headers => credentials('admin')
end
assert_response :unprocessable_entity
assert_equal 'application/json', @response.content_type
json = ActiveSupport::JSON.decode(response.body)
assert_kind_of Hash, json
assert json.has_key?('errors')
assert_kind_of Array, json['errors']
end
test "PUT /users/:id.xml with valid parameters should update the user" do
assert_no_difference('User.count') do
put '/users/2.xml',
:params => {
:user => {
:login => 'jsmith', :firstname => 'John', :lastname => 'Renamed',
:mail => 'jsmith@somenet.foo'
}
},
:headers => credentials('admin')
end
user = User.find(2)
assert_equal 'jsmith', user.login
assert_equal 'John', user.firstname
assert_equal 'Renamed', user.lastname
assert_equal 'jsmith@somenet.foo', user.mail
assert !user.admin?
assert_response :ok
assert_equal '', @response.body
end
test "PUT /users/:id.json with valid parameters should update the user" do
assert_no_difference('User.count') do
put '/users/2.json',
:params => {
:user => {
:login => 'jsmith', :firstname => 'John', :lastname => 'Renamed',
:mail => 'jsmith@somenet.foo'
}
},
:headers => credentials('admin')
end
user = User.find(2)
assert_equal 'jsmith', user.login
assert_equal 'John', user.firstname
assert_equal 'Renamed', user.lastname
assert_equal 'jsmith@somenet.foo', user.mail
assert !user.admin?
assert_response :ok
assert_equal '', @response.body
end
test "PUT /users/:id.xml with invalid parameters" do
assert_no_difference('User.count') do
put '/users/2.xml',
:params => {
:user => {
:login => 'jsmith', :firstname => '', :lastname => 'Lastname',
:mail => 'foo'
}
},
:headers => credentials('admin')
end
assert_response :unprocessable_entity
assert_equal 'application/xml', @response.content_type
assert_select 'errors error', :text => "First name cannot be blank"
end
test "PUT /users/:id.json with invalid parameters" do
assert_no_difference('User.count') do
put '/users/2.json',
:params => {
:user => {
:login => 'jsmith', :firstname => '', :lastname => 'Lastname',
:mail => 'foo'
}
},
:headers => credentials('admin')
end
assert_response :unprocessable_entity
assert_equal 'application/json', @response.content_type
json = ActiveSupport::JSON.decode(response.body)
assert_kind_of Hash, json
assert json.has_key?('errors')
assert_kind_of Array, json['errors']
end
test "DELETE /users/:id.xml should delete the user" do
assert_difference('User.count', -1) do
delete '/users/2.xml', :headers => credentials('admin')
end
assert_response :ok
assert_equal '', @response.body
end
test "DELETE /users/:id.json should delete the user" do
assert_difference('User.count', -1) do
delete '/users/2.json', :headers => credentials('admin')
end
assert_response :ok
assert_equal '', @response.body
end
end

View file

@ -0,0 +1,139 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::VersionsTest < Redmine::ApiTest::Base
fixtures :projects, :trackers, :issue_statuses, :issues,
:enumerations, :users, :issue_categories,
:projects_trackers,
:roles,
:member_roles,
:members,
:enabled_modules,
:versions
test "GET /projects/:project_id/versions.xml should return project versions" do
get '/projects/1/versions.xml'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'versions[type=array] version id', :text => '2' do
assert_select '~ name', :text => '1.0'
end
end
test "POST /projects/:project_id/versions.xml should create the version" do
assert_difference 'Version.count' do
post '/projects/1/versions.xml',
:params => {:version => {:name => 'API test'}},
:headers => credentials('jsmith')
end
version = Version.order('id DESC').first
assert_equal 'API test', version.name
assert_response :created
assert_equal 'application/xml', @response.content_type
assert_select 'version id', :text => version.id.to_s
end
test "POST /projects/:project_id/versions.xml should create the version with due date" do
assert_difference 'Version.count' do
post '/projects/1/versions.xml',
:params => {:version => {:name => 'API test', :due_date => '2012-01-24'}},
:headers => credentials('jsmith')
end
version = Version.order('id DESC').first
assert_equal 'API test', version.name
assert_equal Date.parse('2012-01-24'), version.due_date
assert_response :created
assert_equal 'application/xml', @response.content_type
assert_select 'version id', :text => version.id.to_s
end
test "POST /projects/:project_id/versions.xml should create the version with custom fields" do
field = VersionCustomField.generate!
assert_difference 'Version.count' do
post '/projects/1/versions.xml',
:params => {
:version => {
:name => 'API test',
:custom_fields => [
{'id' => field.id.to_s, 'value' => 'Some value'}
]
}
},
:headers => credentials('jsmith')
end
version = Version.order('id DESC').first
assert_equal 'API test', version.name
assert_equal 'Some value', version.custom_field_value(field)
assert_response :created
assert_equal 'application/xml', @response.content_type
assert_select 'version>custom_fields>custom_field[id=?]>value', field.id.to_s, 'Some value'
end
test "POST /projects/:project_id/versions.xml with failure should return the errors" do
assert_no_difference('Version.count') do
post '/projects/1/versions.xml',
:params => {:version => {:name => ''}},
:headers => credentials('jsmith')
end
assert_response :unprocessable_entity
assert_select 'errors error', :text => "Name cannot be blank"
end
test "GET /versions/:id.xml should return the version" do
get '/versions/2.xml'
assert_response :success
assert_equal 'application/xml', @response.content_type
assert_select 'version' do
assert_select 'id', :text => '2'
assert_select 'name', :text => '1.0'
assert_select 'sharing', :text => 'none'
end
end
test "PUT /versions/:id.xml should update the version" do
put '/versions/2.xml',
:params => {:version => {:name => 'API update'}},
:headers => credentials('jsmith')
assert_response :ok
assert_equal '', @response.body
assert_equal 'API update', Version.find(2).name
end
test "DELETE /versions/:id.xml should destroy the version" do
assert_difference 'Version.count', -1 do
delete '/versions/3.xml', :headers => credentials('jsmith')
end
assert_response :ok
assert_equal '', @response.body
assert_nil Version.find_by_id(3)
end
end

View file

@ -0,0 +1,209 @@
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require File.expand_path('../../../test_helper', __FILE__)
class Redmine::ApiTest::WikiPagesTest < Redmine::ApiTest::Base
fixtures :projects, :users, :roles, :members, :member_roles,
:enabled_modules, :wikis, :wiki_pages, :wiki_contents,
:wiki_content_versions, :attachments
test "GET /projects/:project_id/wiki/index.xml should return wiki pages" do
get '/projects/ecookbook/wiki/index.xml'
assert_response 200
assert_equal 'application/xml', response.content_type
assert_select 'wiki_pages[type=array]' do
assert_select 'wiki_page', :count => Wiki.find(1).pages.count
assert_select 'wiki_page' do
assert_select 'title', :text => 'CookBook_documentation'
assert_select 'version', :text => '3'
assert_select 'created_on'
assert_select 'updated_on'
end
assert_select 'wiki_page' do
assert_select 'title', :text => 'Page_with_an_inline_image'
assert_select 'parent[title=?]', 'CookBook_documentation'
end
end
end
test "GET /projects/:project_id/wiki/:title.xml should return wiki page" do
get '/projects/ecookbook/wiki/CookBook_documentation.xml'
assert_response 200
assert_equal 'application/xml', response.content_type
assert_select 'wiki_page' do
assert_select 'title', :text => 'CookBook_documentation'
assert_select 'version', :text => '3'
assert_select 'text'
assert_select 'author'
assert_select 'comments'
assert_select 'created_on'
assert_select 'updated_on'
end
end
test "GET /projects/:project_id/wiki/:title.xml?include=attachments should include attachments" do
get '/projects/ecookbook/wiki/Page_with_an_inline_image.xml?include=attachments'
assert_response 200
assert_equal 'application/xml', response.content_type
assert_select 'wiki_page' do
assert_select 'title', :text => 'Page_with_an_inline_image'
assert_select 'attachments[type=array]' do
assert_select 'attachment' do
assert_select 'id', :text => '3'
assert_select 'filename', :text => 'logo.gif'
end
end
end
end
test "GET /projects/:project_id/wiki/:title.xml with unknown title and edit permission should respond with 404" do
get '/projects/ecookbook/wiki/Invalid_Page.xml', :headers => credentials('jsmith')
assert_response 404
assert_equal 'application/xml', response.content_type
end
test "GET /projects/:project_id/wiki/:title/:version.xml should return wiki page version" do
get '/projects/ecookbook/wiki/CookBook_documentation/2.xml'
assert_response 200
assert_equal 'application/xml', response.content_type
assert_select 'wiki_page' do
assert_select 'title', :text => 'CookBook_documentation'
assert_select 'version', :text => '2'
assert_select 'text'
assert_select 'author'
assert_select 'comments', :text => 'Small update'
assert_select 'created_on'
assert_select 'updated_on'
end
end
test "GET /projects/:project_id/wiki/:title/:version.xml without permission should be denied" do
Role.anonymous.remove_permission! :view_wiki_edits
get '/projects/ecookbook/wiki/CookBook_documentation/2.xml'
assert_response 401
assert_equal 'application/xml', response.content_type
end
test "PUT /projects/:project_id/wiki/:title.xml should update wiki page" do
assert_no_difference 'WikiPage.count' do
assert_difference 'WikiContent::Version.count' do
put '/projects/ecookbook/wiki/CookBook_documentation.xml',
:params => {:wiki_page => {:text => 'New content from API', :comments => 'API update'}},
:headers => credentials('jsmith')
assert_response 200
end
end
page = WikiPage.find(1)
assert_equal 'New content from API', page.content.text
assert_equal 4, page.content.version
assert_equal 'API update', page.content.comments
assert_equal 'jsmith', page.content.author.login
end
test "PUT /projects/:project_id/wiki/:title.xml with current versino should update wiki page" do
assert_no_difference 'WikiPage.count' do
assert_difference 'WikiContent::Version.count' do
put '/projects/ecookbook/wiki/CookBook_documentation.xml',
:params => {:wiki_page => {:text => 'New content from API', :comments => 'API update', :version => '3'}},
:headers => credentials('jsmith')
assert_response 200
end
end
page = WikiPage.find(1)
assert_equal 'New content from API', page.content.text
assert_equal 4, page.content.version
assert_equal 'API update', page.content.comments
assert_equal 'jsmith', page.content.author.login
end
test "PUT /projects/:project_id/wiki/:title.xml with stale version should respond with 409" do
assert_no_difference 'WikiPage.count' do
assert_no_difference 'WikiContent::Version.count' do
put '/projects/ecookbook/wiki/CookBook_documentation.xml',
:params => {:wiki_page => {:text => 'New content from API', :comments => 'API update', :version => '2'}},
:headers => credentials('jsmith')
assert_response 409
end
end
end
test "PUT /projects/:project_id/wiki/:title.xml should create the page if it does not exist" do
assert_difference 'WikiPage.count' do
assert_difference 'WikiContent::Version.count' do
put '/projects/ecookbook/wiki/New_page_from_API.xml',
:params => {:wiki_page => {:text => 'New content from API', :comments => 'API create'}},
:headers => credentials('jsmith')
assert_response 201
end
end
page = WikiPage.order('id DESC').first
assert_equal 'New_page_from_API', page.title
assert_equal 'New content from API', page.content.text
assert_equal 1, page.content.version
assert_equal 'API create', page.content.comments
assert_equal 'jsmith', page.content.author.login
assert_nil page.parent
end
test "PUT /projects/:project_id/wiki/:title.xml with attachment" do
set_tmp_attachments_directory
attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
assert_difference 'WikiPage.count' do
assert_difference 'WikiContent::Version.count' do
put '/projects/ecookbook/wiki/New_page_from_API.xml',
:params => {:wiki_page => {:text => 'New content from API with Attachments', :comments => 'API create with Attachments',
:uploads => [:token => attachment.token, :filename => 'testfile.txt', :content_type => "text/plain"]}},
:headers => credentials('jsmith')
assert_response 201
end
end
page = WikiPage.order('id DESC').first
assert_equal 'New_page_from_API', page.title
assert_include attachment, page.attachments
assert_equal attachment.filename, page.attachments.first.filename
end
test "PUT /projects/:project_id/wiki/:title.xml with parent" do
assert_difference 'WikiPage.count' do
assert_difference 'WikiContent::Version.count' do
put '/projects/ecookbook/wiki/New_subpage_from_API.xml',
:params => {:wiki_page => {:parent_title => 'CookBook_documentation', :text => 'New content from API', :comments => 'API create'}},
:headers => credentials('jsmith')
assert_response 201
end
end
page = WikiPage.order('id DESC').first
assert_equal 'New_subpage_from_API', page.title
assert_equal WikiPage.find(1), page.parent
end
test "DELETE /projects/:project_id/wiki/:title.xml should destroy the page" do
assert_difference 'WikiPage.count', -1 do
delete '/projects/ecookbook/wiki/CookBook_documentation.xml', :headers => credentials('jsmith')
assert_response 200
end
assert_nil WikiPage.find_by_id(1)
end
end