@@ -1,93 +1,166 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2014 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2014 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program; if not, write to the Free Software |
|
15 | # along with this program; if not, write to the Free Software | |
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | require File.expand_path('../../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class Redmine::ApiTest::AuthenticationTest < Redmine::ApiTest::Base |
|
20 | class Redmine::ApiTest::AuthenticationTest < Redmine::ApiTest::Base | |
21 | fixtures :users |
|
21 | fixtures :users | |
22 |
|
22 | |||
23 | def setup |
|
23 | def setup | |
24 | Setting.rest_api_enabled = '1' |
|
24 | Setting.rest_api_enabled = '1' | |
25 | end |
|
25 | end | |
26 |
|
26 | |||
27 | def teardown |
|
27 | def teardown | |
28 | Setting.rest_api_enabled = '0' |
|
28 | Setting.rest_api_enabled = '0' | |
29 | end |
|
29 | end | |
30 |
|
30 | |||
|
31 | def test_api_should_deny_without_credentials | |||
|
32 | get '/users/current.xml', {} | |||
|
33 | assert_response 401 | |||
|
34 | assert_equal User.anonymous, User.current | |||
|
35 | assert response.headers.has_key?('WWW-Authenticate') | |||
|
36 | end | |||
|
37 | ||||
|
38 | def test_api_should_accept_http_basic_auth_using_username_and_password | |||
|
39 | user = User.generate! do |user| | |||
|
40 | user.password = 'my_password' | |||
|
41 | end | |||
|
42 | get '/users/current.xml', {}, credentials(user.login, 'my_password') | |||
|
43 | assert_response 200 | |||
|
44 | assert_equal user, User.current | |||
|
45 | end | |||
|
46 | ||||
|
47 | def test_api_should_deny_http_basic_auth_using_username_and_wrong_password | |||
|
48 | user = User.generate! do |user| | |||
|
49 | user.password = 'my_password' | |||
|
50 | end | |||
|
51 | get '/users/current.xml', {}, credentials(user.login, 'wrong_password') | |||
|
52 | assert_response 401 | |||
|
53 | assert_equal User.anonymous, User.current | |||
|
54 | end | |||
|
55 | ||||
|
56 | def test_api_should_accept_http_basic_auth_using_api_key | |||
|
57 | user = User.generate! | |||
|
58 | token = Token.create!(:user => user, :action => 'api') | |||
|
59 | get '/users/current.xml', {}, credentials(token.value, 'X') | |||
|
60 | assert_response 200 | |||
|
61 | assert_equal user, User.current | |||
|
62 | end | |||
|
63 | ||||
|
64 | def test_api_should_deny_http_basic_auth_using_wrong_api_key | |||
|
65 | user = User.generate! | |||
|
66 | token = Token.create!(:user => user, :action => 'feeds') # not the API key | |||
|
67 | get '/users/current.xml', {}, credentials(token.value, 'X') | |||
|
68 | assert_response 401 | |||
|
69 | assert_equal User.anonymous, User.current | |||
|
70 | end | |||
|
71 | ||||
|
72 | def test_api_should_accept_auth_using_api_key_as_parameter | |||
|
73 | user = User.generate! | |||
|
74 | token = Token.create!(:user => user, :action => 'api') | |||
|
75 | get "/users/current.xml?key=#{token.value}", {} | |||
|
76 | assert_response 200 | |||
|
77 | assert_equal user, User.current | |||
|
78 | end | |||
|
79 | ||||
|
80 | def test_api_should_deny_auth_using_wrong_api_key_as_parameter | |||
|
81 | user = User.generate! | |||
|
82 | token = Token.create!(:user => user, :action => 'feeds') # not the API key | |||
|
83 | get "/users/current.xml?key=#{token.value}", {} | |||
|
84 | assert_response 401 | |||
|
85 | assert_equal User.anonymous, User.current | |||
|
86 | end | |||
|
87 | ||||
|
88 | def test_api_should_accept_auth_using_api_key_as_request_header | |||
|
89 | user = User.generate! | |||
|
90 | token = Token.create!(:user => user, :action => 'api') | |||
|
91 | get "/users/current.xml", {}, {'X-Redmine-API-Key' => token.value.to_s} | |||
|
92 | assert_response 200 | |||
|
93 | assert_equal user, User.current | |||
|
94 | end | |||
|
95 | ||||
|
96 | def test_api_should_deny_auth_using_wrong_api_key_as_request_header | |||
|
97 | user = User.generate! | |||
|
98 | token = Token.create!(:user => user, :action => 'feeds') # not the API key | |||
|
99 | get "/users/current.xml", {}, {'X-Redmine-API-Key' => token.value.to_s} | |||
|
100 | assert_response 401 | |||
|
101 | assert_equal User.anonymous, User.current | |||
|
102 | end | |||
|
103 | ||||
31 | def test_api_should_trigger_basic_http_auth_with_basic_authorization_header |
|
104 | def test_api_should_trigger_basic_http_auth_with_basic_authorization_header | |
32 | ApplicationController.any_instance.expects(:authenticate_with_http_basic).once |
|
105 | ApplicationController.any_instance.expects(:authenticate_with_http_basic).once | |
33 | get '/users/current.xml', {}, credentials('jsmith') |
|
106 | get '/users/current.xml', {}, credentials('jsmith') | |
34 | assert_response 401 |
|
107 | assert_response 401 | |
35 | end |
|
108 | end | |
36 |
|
109 | |||
37 | def test_api_should_not_trigger_basic_http_auth_with_non_basic_authorization_header |
|
110 | def test_api_should_not_trigger_basic_http_auth_with_non_basic_authorization_header | |
38 | ApplicationController.any_instance.expects(:authenticate_with_http_basic).never |
|
111 | ApplicationController.any_instance.expects(:authenticate_with_http_basic).never | |
39 | get '/users/current.xml', {}, 'HTTP_AUTHORIZATION' => 'Digest foo bar' |
|
112 | get '/users/current.xml', {}, 'HTTP_AUTHORIZATION' => 'Digest foo bar' | |
40 | assert_response 401 |
|
113 | assert_response 401 | |
41 | end |
|
114 | end | |
42 |
|
115 | |||
43 | def test_invalid_utf8_credentials_should_not_trigger_an_error |
|
116 | def test_invalid_utf8_credentials_should_not_trigger_an_error | |
44 | invalid_utf8 = "\x82".force_encoding('UTF-8') |
|
117 | invalid_utf8 = "\x82".force_encoding('UTF-8') | |
45 | assert !invalid_utf8.valid_encoding? |
|
118 | assert !invalid_utf8.valid_encoding? | |
46 | assert_nothing_raised do |
|
119 | assert_nothing_raised do | |
47 | get '/users/current.xml', {}, credentials(invalid_utf8, "foo") |
|
120 | get '/users/current.xml', {}, credentials(invalid_utf8, "foo") | |
48 | end |
|
121 | end | |
49 | end |
|
122 | end | |
50 |
|
123 | |||
51 | def test_api_request_should_not_use_user_session |
|
124 | def test_api_request_should_not_use_user_session | |
52 | log_user('jsmith', 'jsmith') |
|
125 | log_user('jsmith', 'jsmith') | |
53 |
|
126 | |||
54 | get '/users/current' |
|
127 | get '/users/current' | |
55 | assert_response :success |
|
128 | assert_response :success | |
56 |
|
129 | |||
57 | get '/users/current.json' |
|
130 | get '/users/current.json' | |
58 | assert_response 401 |
|
131 | assert_response 401 | |
59 | end |
|
132 | end | |
60 |
|
133 | |||
61 | def test_api_should_accept_switch_user_header_for_admin_user |
|
134 | def test_api_should_accept_switch_user_header_for_admin_user | |
62 | user = User.find(1) |
|
135 | user = User.find(1) | |
63 | su = User.find(4) |
|
136 | su = User.find(4) | |
64 |
|
137 | |||
65 | get '/users/current', {}, {'X-Redmine-API-Key' => user.api_key, 'X-Redmine-Switch-User' => su.login} |
|
138 | get '/users/current', {}, {'X-Redmine-API-Key' => user.api_key, 'X-Redmine-Switch-User' => su.login} | |
66 | assert_response :success |
|
139 | assert_response :success | |
67 | assert_equal su, assigns(:user) |
|
140 | assert_equal su, assigns(:user) | |
68 | assert_equal su, User.current |
|
141 | assert_equal su, User.current | |
69 | end |
|
142 | end | |
70 |
|
143 | |||
71 | def test_api_should_respond_with_412_when_trying_to_switch_to_a_invalid_user |
|
144 | def test_api_should_respond_with_412_when_trying_to_switch_to_a_invalid_user | |
72 | get '/users/current', {}, {'X-Redmine-API-Key' => User.find(1).api_key, 'X-Redmine-Switch-User' => 'foobar'} |
|
145 | get '/users/current', {}, {'X-Redmine-API-Key' => User.find(1).api_key, 'X-Redmine-Switch-User' => 'foobar'} | |
73 | assert_response 412 |
|
146 | assert_response 412 | |
74 | end |
|
147 | end | |
75 |
|
148 | |||
76 | def test_api_should_respond_with_412_when_trying_to_switch_to_a_locked_user |
|
149 | def test_api_should_respond_with_412_when_trying_to_switch_to_a_locked_user | |
77 | user = User.find(5) |
|
150 | user = User.find(5) | |
78 | assert user.locked? |
|
151 | assert user.locked? | |
79 |
|
152 | |||
80 | get '/users/current', {}, {'X-Redmine-API-Key' => User.find(1).api_key, 'X-Redmine-Switch-User' => user.login} |
|
153 | get '/users/current', {}, {'X-Redmine-API-Key' => User.find(1).api_key, 'X-Redmine-Switch-User' => user.login} | |
81 | assert_response 412 |
|
154 | assert_response 412 | |
82 | end |
|
155 | end | |
83 |
|
156 | |||
84 | def test_api_should_not_accept_switch_user_header_for_non_admin_user |
|
157 | def test_api_should_not_accept_switch_user_header_for_non_admin_user | |
85 | user = User.find(2) |
|
158 | user = User.find(2) | |
86 | su = User.find(4) |
|
159 | su = User.find(4) | |
87 |
|
160 | |||
88 | get '/users/current', {}, {'X-Redmine-API-Key' => user.api_key, 'X-Redmine-Switch-User' => su.login} |
|
161 | get '/users/current', {}, {'X-Redmine-API-Key' => user.api_key, 'X-Redmine-Switch-User' => su.login} | |
89 | assert_response :success |
|
162 | assert_response :success | |
90 | assert_equal user, assigns(:user) |
|
163 | assert_equal user, assigns(:user) | |
91 | assert_equal user, User.current |
|
164 | assert_equal user, User.current | |
92 | end |
|
165 | end | |
93 | end |
|
166 | end |
@@ -1,632 +1,594 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2014 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2014 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program; if not, write to the Free Software |
|
15 | # along with this program; if not, write to the Free Software | |
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | require File.expand_path('../../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base |
|
20 | class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base | |
21 | fixtures :projects, |
|
21 | fixtures :projects, | |
22 | :users, |
|
22 | :users, | |
23 | :roles, |
|
23 | :roles, | |
24 | :members, |
|
24 | :members, | |
25 | :member_roles, |
|
25 | :member_roles, | |
26 | :issues, |
|
26 | :issues, | |
27 | :issue_statuses, |
|
27 | :issue_statuses, | |
28 | :issue_relations, |
|
28 | :issue_relations, | |
29 | :versions, |
|
29 | :versions, | |
30 | :trackers, |
|
30 | :trackers, | |
31 | :projects_trackers, |
|
31 | :projects_trackers, | |
32 | :issue_categories, |
|
32 | :issue_categories, | |
33 | :enabled_modules, |
|
33 | :enabled_modules, | |
34 | :enumerations, |
|
34 | :enumerations, | |
35 | :attachments, |
|
35 | :attachments, | |
36 | :workflows, |
|
36 | :workflows, | |
37 | :custom_fields, |
|
37 | :custom_fields, | |
38 | :custom_values, |
|
38 | :custom_values, | |
39 | :custom_fields_projects, |
|
39 | :custom_fields_projects, | |
40 | :custom_fields_trackers, |
|
40 | :custom_fields_trackers, | |
41 | :time_entries, |
|
41 | :time_entries, | |
42 | :journals, |
|
42 | :journals, | |
43 | :journal_details, |
|
43 | :journal_details, | |
44 | :queries, |
|
44 | :queries, | |
45 | :attachments |
|
45 | :attachments | |
46 |
|
46 | |||
47 | def setup |
|
47 | def setup | |
48 | Setting.rest_api_enabled = '1' |
|
48 | Setting.rest_api_enabled = '1' | |
49 | end |
|
49 | end | |
50 |
|
50 | |||
51 | # Use a private project to make sure auth is really working and not just |
|
|||
52 | # only showing public issues. |
|
|||
53 | should_allow_api_authentication(:get, "/projects/private-child/issues.xml") |
|
|||
54 | should_allow_api_authentication(:get, "/projects/private-child/issues.json") |
|
|||
55 |
|
||||
56 | should_allow_api_authentication(:get, "/issues/6.xml") |
|
|||
57 | should_allow_api_authentication(:get, "/issues/6.json") |
|
|||
58 |
|
||||
59 | should_allow_api_authentication( |
|
|||
60 | :post, |
|
|||
61 | '/issues.xml', |
|
|||
62 | {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}}, |
|
|||
63 | {:success_code => :created} |
|
|||
64 | ) |
|
|||
65 | should_allow_api_authentication(:post, |
|
|||
66 | '/issues.json', |
|
|||
67 | {:issue => {:project_id => 1, :subject => 'API test', |
|
|||
68 | :tracker_id => 2, :status_id => 3}}, |
|
|||
69 | {:success_code => :created}) |
|
|||
70 |
|
||||
71 | should_allow_api_authentication(:put, |
|
|||
72 | '/issues/6.xml', |
|
|||
73 | {:issue => {:subject => 'API update', :notes => 'A new note'}}, |
|
|||
74 | {:success_code => :ok}) |
|
|||
75 | should_allow_api_authentication(:put, |
|
|||
76 | '/issues/6.json', |
|
|||
77 | {:issue => {:subject => 'API update', :notes => 'A new note'}}, |
|
|||
78 | {:success_code => :ok}) |
|
|||
79 |
|
||||
80 | should_allow_api_authentication(:delete, |
|
|||
81 | '/issues/6.xml', |
|
|||
82 | {}, |
|
|||
83 | {:success_code => :ok}) |
|
|||
84 | should_allow_api_authentication(:delete, |
|
|||
85 | '/issues/6.json', |
|
|||
86 | {}, |
|
|||
87 | {:success_code => :ok}) |
|
|||
88 |
|
||||
89 | test "GET /issues.xml should contain metadata" do |
|
51 | test "GET /issues.xml should contain metadata" do | |
90 | get '/issues.xml' |
|
52 | get '/issues.xml' | |
91 | assert_select 'issues[type=array][total_count=?][limit="25"][offset="0"]', |
|
53 | assert_select 'issues[type=array][total_count=?][limit="25"][offset="0"]', | |
92 | assigns(:issue_count).to_s |
|
54 | assigns(:issue_count).to_s | |
93 | end |
|
55 | end | |
94 |
|
56 | |||
95 | test "GET /issues.xml with nometa param should not contain metadata" do |
|
57 | test "GET /issues.xml with nometa param should not contain metadata" do | |
96 | get '/issues.xml?nometa=1' |
|
58 | get '/issues.xml?nometa=1' | |
97 | assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])' |
|
59 | assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])' | |
98 | end |
|
60 | end | |
99 |
|
61 | |||
100 | test "GET /issues.xml with nometa header should not contain metadata" do |
|
62 | test "GET /issues.xml with nometa header should not contain metadata" do | |
101 | get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'} |
|
63 | get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'} | |
102 | assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])' |
|
64 | assert_select 'issues[type=array]:not([total_count]):not([limit]):not([offset])' | |
103 | end |
|
65 | end | |
104 |
|
66 | |||
105 | test "GET /issues.xml with offset and limit" do |
|
67 | test "GET /issues.xml with offset and limit" do | |
106 | get '/issues.xml?offset=2&limit=3' |
|
68 | get '/issues.xml?offset=2&limit=3' | |
107 |
|
69 | |||
108 | assert_equal 3, assigns(:limit) |
|
70 | assert_equal 3, assigns(:limit) | |
109 | assert_equal 2, assigns(:offset) |
|
71 | assert_equal 2, assigns(:offset) | |
110 | assert_select 'issues issue', 3 |
|
72 | assert_select 'issues issue', 3 | |
111 | end |
|
73 | end | |
112 |
|
74 | |||
113 | test "GET /issues.xml with relations" do |
|
75 | test "GET /issues.xml with relations" do | |
114 | get '/issues.xml?include=relations' |
|
76 | get '/issues.xml?include=relations' | |
115 |
|
77 | |||
116 | assert_response :success |
|
78 | assert_response :success | |
117 | assert_equal 'application/xml', @response.content_type |
|
79 | assert_equal 'application/xml', @response.content_type | |
118 |
|
80 | |||
119 | assert_select 'issue id:content(3)' do |
|
81 | assert_select 'issue id:content(3)' do | |
120 | assert_select '~ relations relation', 1 |
|
82 | assert_select '~ relations relation', 1 | |
121 | assert_select '~ relations relation[id="2"][issue_id="2"][issue_to_id="3"][relation_type=relates]' |
|
83 | assert_select '~ relations relation[id="2"][issue_id="2"][issue_to_id="3"][relation_type=relates]' | |
122 | end |
|
84 | end | |
123 |
|
85 | |||
124 | assert_select 'issue id:content(1)' do |
|
86 | assert_select 'issue id:content(1)' do | |
125 | assert_select '~ relations' |
|
87 | assert_select '~ relations' | |
126 | assert_select '~ relations relation', 0 |
|
88 | assert_select '~ relations relation', 0 | |
127 | end |
|
89 | end | |
128 | end |
|
90 | end | |
129 |
|
91 | |||
130 | test "GET /issues.xml with invalid query params" do |
|
92 | test "GET /issues.xml with invalid query params" do | |
131 | get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}} |
|
93 | get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}} | |
132 |
|
94 | |||
133 | assert_response :unprocessable_entity |
|
95 | assert_response :unprocessable_entity | |
134 | assert_equal 'application/xml', @response.content_type |
|
96 | assert_equal 'application/xml', @response.content_type | |
135 | assert_select 'errors error', :text => "Start date can't be blank" |
|
97 | assert_select 'errors error', :text => "Start date can't be blank" | |
136 | end |
|
98 | end | |
137 |
|
99 | |||
138 | test "GET /issues.xml with custom field filter" do |
|
100 | test "GET /issues.xml with custom field filter" do | |
139 | get '/issues.xml', |
|
101 | get '/issues.xml', | |
140 | {:set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='}, :v => {:cf_1 => ['MySQL']}} |
|
102 | {:set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='}, :v => {:cf_1 => ['MySQL']}} | |
141 |
|
103 | |||
142 | expected_ids = Issue.visible. |
|
104 | expected_ids = Issue.visible. | |
143 | joins(:custom_values). |
|
105 | joins(:custom_values). | |
144 | where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id) |
|
106 | where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id) | |
145 | assert expected_ids.any? |
|
107 | assert expected_ids.any? | |
146 |
|
108 | |||
147 | assert_select 'issues > issue > id', :count => expected_ids.count do |ids| |
|
109 | assert_select 'issues > issue > id', :count => expected_ids.count do |ids| | |
148 | ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) } |
|
110 | ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) } | |
149 | end |
|
111 | end | |
150 | end |
|
112 | end | |
151 |
|
113 | |||
152 | test "GET /issues.xml with custom field filter (shorthand method)" do |
|
114 | test "GET /issues.xml with custom field filter (shorthand method)" do | |
153 | get '/issues.xml', {:cf_1 => 'MySQL'} |
|
115 | get '/issues.xml', {:cf_1 => 'MySQL'} | |
154 |
|
116 | |||
155 | expected_ids = Issue.visible. |
|
117 | expected_ids = Issue.visible. | |
156 | joins(:custom_values). |
|
118 | joins(:custom_values). | |
157 | where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id) |
|
119 | where(:custom_values => {:custom_field_id => 1, :value => 'MySQL'}).map(&:id) | |
158 | assert expected_ids.any? |
|
120 | assert expected_ids.any? | |
159 |
|
121 | |||
160 | assert_select 'issues > issue > id', :count => expected_ids.count do |ids| |
|
122 | assert_select 'issues > issue > id', :count => expected_ids.count do |ids| | |
161 | ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) } |
|
123 | ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) } | |
162 | end |
|
124 | end | |
163 | end |
|
125 | end | |
164 |
|
126 | |||
165 | def test_index_should_include_issue_attributes |
|
127 | def test_index_should_include_issue_attributes | |
166 | get '/issues.xml' |
|
128 | get '/issues.xml' | |
167 | assert_select 'issues>issue>is_private', :text => 'false' |
|
129 | assert_select 'issues>issue>is_private', :text => 'false' | |
168 | end |
|
130 | end | |
169 |
|
131 | |||
170 | def test_index_should_allow_timestamp_filtering |
|
132 | def test_index_should_allow_timestamp_filtering | |
171 | Issue.delete_all |
|
133 | Issue.delete_all | |
172 | Issue.generate!(:subject => '1').update_column(:updated_on, Time.parse("2014-01-02T10:25:00Z")) |
|
134 | Issue.generate!(:subject => '1').update_column(:updated_on, Time.parse("2014-01-02T10:25:00Z")) | |
173 | Issue.generate!(:subject => '2').update_column(:updated_on, Time.parse("2014-01-02T12:13:00Z")) |
|
135 | Issue.generate!(:subject => '2').update_column(:updated_on, Time.parse("2014-01-02T12:13:00Z")) | |
174 |
|
136 | |||
175 | get '/issues.xml', |
|
137 | get '/issues.xml', | |
176 | {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '<='}, |
|
138 | {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '<='}, | |
177 | :v => {:updated_on => ['2014-01-02T12:00:00Z']}} |
|
139 | :v => {:updated_on => ['2014-01-02T12:00:00Z']}} | |
178 | assert_select 'issues>issue', :count => 1 |
|
140 | assert_select 'issues>issue', :count => 1 | |
179 | assert_select 'issues>issue>subject', :text => '1' |
|
141 | assert_select 'issues>issue>subject', :text => '1' | |
180 |
|
142 | |||
181 | get '/issues.xml', |
|
143 | get '/issues.xml', | |
182 | {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='}, |
|
144 | {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='}, | |
183 | :v => {:updated_on => ['2014-01-02T12:00:00Z']}} |
|
145 | :v => {:updated_on => ['2014-01-02T12:00:00Z']}} | |
184 | assert_select 'issues>issue', :count => 1 |
|
146 | assert_select 'issues>issue', :count => 1 | |
185 | assert_select 'issues>issue>subject', :text => '2' |
|
147 | assert_select 'issues>issue>subject', :text => '2' | |
186 |
|
148 | |||
187 | get '/issues.xml', |
|
149 | get '/issues.xml', | |
188 | {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='}, |
|
150 | {:set_filter => 1, :f => ['updated_on'], :op => {:updated_on => '>='}, | |
189 | :v => {:updated_on => ['2014-01-02T08:00:00Z']}} |
|
151 | :v => {:updated_on => ['2014-01-02T08:00:00Z']}} | |
190 | assert_select 'issues>issue', :count => 2 |
|
152 | assert_select 'issues>issue', :count => 2 | |
191 | end |
|
153 | end | |
192 |
|
154 | |||
193 | test "GET /issues.xml with filter" do |
|
155 | test "GET /issues.xml with filter" do | |
194 | get '/issues.xml?status_id=5' |
|
156 | get '/issues.xml?status_id=5' | |
195 |
|
157 | |||
196 | expected_ids = Issue.visible.where(:status_id => 5).map(&:id) |
|
158 | expected_ids = Issue.visible.where(:status_id => 5).map(&:id) | |
197 | assert expected_ids.any? |
|
159 | assert expected_ids.any? | |
198 |
|
160 | |||
199 | assert_select 'issues > issue > id', :count => expected_ids.count do |ids| |
|
161 | assert_select 'issues > issue > id', :count => expected_ids.count do |ids| | |
200 | ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) } |
|
162 | ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) } | |
201 | end |
|
163 | end | |
202 | end |
|
164 | end | |
203 |
|
165 | |||
204 | test "GET /issues.json with filter" do |
|
166 | test "GET /issues.json with filter" do | |
205 | get '/issues.json?status_id=5' |
|
167 | get '/issues.json?status_id=5' | |
206 |
|
168 | |||
207 | json = ActiveSupport::JSON.decode(response.body) |
|
169 | json = ActiveSupport::JSON.decode(response.body) | |
208 | status_ids_used = json['issues'].collect {|j| j['status']['id'] } |
|
170 | status_ids_used = json['issues'].collect {|j| j['status']['id'] } | |
209 | assert_equal 3, status_ids_used.length |
|
171 | assert_equal 3, status_ids_used.length | |
210 | assert status_ids_used.all? {|id| id == 5 } |
|
172 | assert status_ids_used.all? {|id| id == 5 } | |
211 | end |
|
173 | end | |
212 |
|
174 | |||
213 | test "GET /issues/:id.xml with journals" do |
|
175 | test "GET /issues/:id.xml with journals" do | |
214 | get '/issues/1.xml?include=journals' |
|
176 | get '/issues/1.xml?include=journals' | |
215 |
|
177 | |||
216 | assert_select 'issue journals[type=array]' do |
|
178 | assert_select 'issue journals[type=array]' do | |
217 | assert_select 'journal[id="1"]' do |
|
179 | assert_select 'journal[id="1"]' do | |
218 | assert_select 'details[type=array]' do |
|
180 | assert_select 'details[type=array]' do | |
219 | assert_select 'detail[name=status_id]' do |
|
181 | assert_select 'detail[name=status_id]' do | |
220 | assert_select 'old_value', :text => '1' |
|
182 | assert_select 'old_value', :text => '1' | |
221 | assert_select 'new_value', :text => '2' |
|
183 | assert_select 'new_value', :text => '2' | |
222 | end |
|
184 | end | |
223 | end |
|
185 | end | |
224 | end |
|
186 | end | |
225 | end |
|
187 | end | |
226 | end |
|
188 | end | |
227 |
|
189 | |||
228 | test "GET /issues/:id.xml with custom fields" do |
|
190 | test "GET /issues/:id.xml with custom fields" do | |
229 | get '/issues/3.xml' |
|
191 | get '/issues/3.xml' | |
230 |
|
192 | |||
231 | assert_select 'issue custom_fields[type=array]' do |
|
193 | assert_select 'issue custom_fields[type=array]' do | |
232 | assert_select 'custom_field[id="1"]' do |
|
194 | assert_select 'custom_field[id="1"]' do | |
233 | assert_select 'value', :text => 'MySQL' |
|
195 | assert_select 'value', :text => 'MySQL' | |
234 | end |
|
196 | end | |
235 | end |
|
197 | end | |
236 | assert_nothing_raised do |
|
198 | assert_nothing_raised do | |
237 | Hash.from_xml(response.body).to_xml |
|
199 | Hash.from_xml(response.body).to_xml | |
238 | end |
|
200 | end | |
239 | end |
|
201 | end | |
240 |
|
202 | |||
241 | test "GET /issues/:id.xml with multi custom fields" do |
|
203 | test "GET /issues/:id.xml with multi custom fields" do | |
242 | field = CustomField.find(1) |
|
204 | field = CustomField.find(1) | |
243 | field.update_attribute :multiple, true |
|
205 | field.update_attribute :multiple, true | |
244 | issue = Issue.find(3) |
|
206 | issue = Issue.find(3) | |
245 | issue.custom_field_values = {1 => ['MySQL', 'Oracle']} |
|
207 | issue.custom_field_values = {1 => ['MySQL', 'Oracle']} | |
246 | issue.save! |
|
208 | issue.save! | |
247 |
|
209 | |||
248 | get '/issues/3.xml' |
|
210 | get '/issues/3.xml' | |
249 | assert_response :success |
|
211 | assert_response :success | |
250 |
|
212 | |||
251 | assert_select 'issue custom_fields[type=array]' do |
|
213 | assert_select 'issue custom_fields[type=array]' do | |
252 | assert_select 'custom_field[id="1"]' do |
|
214 | assert_select 'custom_field[id="1"]' do | |
253 | assert_select 'value[type=array] value', 2 |
|
215 | assert_select 'value[type=array] value', 2 | |
254 | end |
|
216 | end | |
255 | end |
|
217 | end | |
256 | xml = Hash.from_xml(response.body) |
|
218 | xml = Hash.from_xml(response.body) | |
257 | custom_fields = xml['issue']['custom_fields'] |
|
219 | custom_fields = xml['issue']['custom_fields'] | |
258 | assert_kind_of Array, custom_fields |
|
220 | assert_kind_of Array, custom_fields | |
259 | field = custom_fields.detect {|f| f['id'] == '1'} |
|
221 | field = custom_fields.detect {|f| f['id'] == '1'} | |
260 | assert_kind_of Hash, field |
|
222 | assert_kind_of Hash, field | |
261 | assert_equal ['MySQL', 'Oracle'], field['value'].sort |
|
223 | assert_equal ['MySQL', 'Oracle'], field['value'].sort | |
262 | end |
|
224 | end | |
263 |
|
225 | |||
264 | test "GET /issues/:id.json with multi custom fields" do |
|
226 | test "GET /issues/:id.json with multi custom fields" do | |
265 | field = CustomField.find(1) |
|
227 | field = CustomField.find(1) | |
266 | field.update_attribute :multiple, true |
|
228 | field.update_attribute :multiple, true | |
267 | issue = Issue.find(3) |
|
229 | issue = Issue.find(3) | |
268 | issue.custom_field_values = {1 => ['MySQL', 'Oracle']} |
|
230 | issue.custom_field_values = {1 => ['MySQL', 'Oracle']} | |
269 | issue.save! |
|
231 | issue.save! | |
270 |
|
232 | |||
271 | get '/issues/3.json' |
|
233 | get '/issues/3.json' | |
272 | assert_response :success |
|
234 | assert_response :success | |
273 |
|
235 | |||
274 | json = ActiveSupport::JSON.decode(response.body) |
|
236 | json = ActiveSupport::JSON.decode(response.body) | |
275 | custom_fields = json['issue']['custom_fields'] |
|
237 | custom_fields = json['issue']['custom_fields'] | |
276 | assert_kind_of Array, custom_fields |
|
238 | assert_kind_of Array, custom_fields | |
277 | field = custom_fields.detect {|f| f['id'] == 1} |
|
239 | field = custom_fields.detect {|f| f['id'] == 1} | |
278 | assert_kind_of Hash, field |
|
240 | assert_kind_of Hash, field | |
279 | assert_equal ['MySQL', 'Oracle'], field['value'].sort |
|
241 | assert_equal ['MySQL', 'Oracle'], field['value'].sort | |
280 | end |
|
242 | end | |
281 |
|
243 | |||
282 | test "GET /issues/:id.xml with empty value for multi custom field" do |
|
244 | test "GET /issues/:id.xml with empty value for multi custom field" do | |
283 | field = CustomField.find(1) |
|
245 | field = CustomField.find(1) | |
284 | field.update_attribute :multiple, true |
|
246 | field.update_attribute :multiple, true | |
285 | issue = Issue.find(3) |
|
247 | issue = Issue.find(3) | |
286 | issue.custom_field_values = {1 => ['']} |
|
248 | issue.custom_field_values = {1 => ['']} | |
287 | issue.save! |
|
249 | issue.save! | |
288 |
|
250 | |||
289 | get '/issues/3.xml' |
|
251 | get '/issues/3.xml' | |
290 |
|
252 | |||
291 | assert_select 'issue custom_fields[type=array]' do |
|
253 | assert_select 'issue custom_fields[type=array]' do | |
292 | assert_select 'custom_field[id="1"]' do |
|
254 | assert_select 'custom_field[id="1"]' do | |
293 | assert_select 'value[type=array]:empty' |
|
255 | assert_select 'value[type=array]:empty' | |
294 | end |
|
256 | end | |
295 | end |
|
257 | end | |
296 | xml = Hash.from_xml(response.body) |
|
258 | xml = Hash.from_xml(response.body) | |
297 | custom_fields = xml['issue']['custom_fields'] |
|
259 | custom_fields = xml['issue']['custom_fields'] | |
298 | assert_kind_of Array, custom_fields |
|
260 | assert_kind_of Array, custom_fields | |
299 | field = custom_fields.detect {|f| f['id'] == '1'} |
|
261 | field = custom_fields.detect {|f| f['id'] == '1'} | |
300 | assert_kind_of Hash, field |
|
262 | assert_kind_of Hash, field | |
301 | assert_equal [], field['value'] |
|
263 | assert_equal [], field['value'] | |
302 | end |
|
264 | end | |
303 |
|
265 | |||
304 | test "GET /issues/:id.json with empty value for multi custom field" do |
|
266 | test "GET /issues/:id.json with empty value for multi custom field" do | |
305 | field = CustomField.find(1) |
|
267 | field = CustomField.find(1) | |
306 | field.update_attribute :multiple, true |
|
268 | field.update_attribute :multiple, true | |
307 | issue = Issue.find(3) |
|
269 | issue = Issue.find(3) | |
308 | issue.custom_field_values = {1 => ['']} |
|
270 | issue.custom_field_values = {1 => ['']} | |
309 | issue.save! |
|
271 | issue.save! | |
310 |
|
272 | |||
311 | get '/issues/3.json' |
|
273 | get '/issues/3.json' | |
312 | assert_response :success |
|
274 | assert_response :success | |
313 | json = ActiveSupport::JSON.decode(response.body) |
|
275 | json = ActiveSupport::JSON.decode(response.body) | |
314 | custom_fields = json['issue']['custom_fields'] |
|
276 | custom_fields = json['issue']['custom_fields'] | |
315 | assert_kind_of Array, custom_fields |
|
277 | assert_kind_of Array, custom_fields | |
316 | field = custom_fields.detect {|f| f['id'] == 1} |
|
278 | field = custom_fields.detect {|f| f['id'] == 1} | |
317 | assert_kind_of Hash, field |
|
279 | assert_kind_of Hash, field | |
318 | assert_equal [], field['value'].sort |
|
280 | assert_equal [], field['value'].sort | |
319 | end |
|
281 | end | |
320 |
|
282 | |||
321 | test "GET /issues/:id.xml with attachments" do |
|
283 | test "GET /issues/:id.xml with attachments" do | |
322 | get '/issues/3.xml?include=attachments' |
|
284 | get '/issues/3.xml?include=attachments' | |
323 |
|
285 | |||
324 | assert_select 'issue attachments[type=array]' do |
|
286 | assert_select 'issue attachments[type=array]' do | |
325 | assert_select 'attachment', 5 |
|
287 | assert_select 'attachment', 5 | |
326 | assert_select 'attachment id:content(4)' do |
|
288 | assert_select 'attachment id:content(4)' do | |
327 | assert_select '~ filename', :text => 'source.rb' |
|
289 | assert_select '~ filename', :text => 'source.rb' | |
328 | assert_select '~ content_url', :text => 'http://www.example.com/attachments/download/4/source.rb' |
|
290 | assert_select '~ content_url', :text => 'http://www.example.com/attachments/download/4/source.rb' | |
329 | end |
|
291 | end | |
330 | end |
|
292 | end | |
331 | end |
|
293 | end | |
332 |
|
294 | |||
333 | test "GET /issues/:id.xml with subtasks" do |
|
295 | test "GET /issues/:id.xml with subtasks" do | |
334 | issue = Issue.generate_with_descendants!(:project_id => 1) |
|
296 | issue = Issue.generate_with_descendants!(:project_id => 1) | |
335 | get "/issues/#{issue.id}.xml?include=children" |
|
297 | get "/issues/#{issue.id}.xml?include=children" | |
336 |
|
298 | |||
337 | assert_select 'issue children[type=array]' do |
|
299 | assert_select 'issue children[type=array]' do | |
338 | assert_select 'issue', 2 |
|
300 | assert_select 'issue', 2 | |
339 | assert_select 'issue children', 1 |
|
301 | assert_select 'issue children', 1 | |
340 | end |
|
302 | end | |
341 | end |
|
303 | end | |
342 |
|
304 | |||
343 | test "GET /issues/:id.json with subtasks" do |
|
305 | test "GET /issues/:id.json with subtasks" do | |
344 | issue = Issue.generate_with_descendants!(:project_id => 1) |
|
306 | issue = Issue.generate_with_descendants!(:project_id => 1) | |
345 | get "/issues/#{issue.id}.json?include=children" |
|
307 | get "/issues/#{issue.id}.json?include=children" | |
346 |
|
308 | |||
347 | json = ActiveSupport::JSON.decode(response.body) |
|
309 | json = ActiveSupport::JSON.decode(response.body) | |
348 | assert_equal 2, json['issue']['children'].size |
|
310 | assert_equal 2, json['issue']['children'].size | |
349 | assert_equal 1, json['issue']['children'].select {|child| child.key?('children')}.size |
|
311 | assert_equal 1, json['issue']['children'].select {|child| child.key?('children')}.size | |
350 | end |
|
312 | end | |
351 |
|
313 | |||
352 | def test_show_should_include_issue_attributes |
|
314 | def test_show_should_include_issue_attributes | |
353 | get '/issues/1.xml' |
|
315 | get '/issues/1.xml' | |
354 | assert_select 'issue>is_private', :text => 'false' |
|
316 | assert_select 'issue>is_private', :text => 'false' | |
355 | end |
|
317 | end | |
356 |
|
318 | |||
357 | test "GET /issues/:id.xml?include=watchers should include watchers" do |
|
319 | test "GET /issues/:id.xml?include=watchers should include watchers" do | |
358 | Watcher.create!(:user_id => 3, :watchable => Issue.find(1)) |
|
320 | Watcher.create!(:user_id => 3, :watchable => Issue.find(1)) | |
359 |
|
321 | |||
360 | get '/issues/1.xml?include=watchers', {}, credentials('jsmith') |
|
322 | get '/issues/1.xml?include=watchers', {}, credentials('jsmith') | |
361 |
|
323 | |||
362 | assert_response :ok |
|
324 | assert_response :ok | |
363 | assert_equal 'application/xml', response.content_type |
|
325 | assert_equal 'application/xml', response.content_type | |
364 | assert_select 'issue' do |
|
326 | assert_select 'issue' do | |
365 | assert_select 'watchers', Issue.find(1).watchers.count |
|
327 | assert_select 'watchers', Issue.find(1).watchers.count | |
366 | assert_select 'watchers' do |
|
328 | assert_select 'watchers' do | |
367 | assert_select 'user[id=3]' |
|
329 | assert_select 'user[id=3]' | |
368 | end |
|
330 | end | |
369 | end |
|
331 | end | |
370 | end |
|
332 | end | |
371 |
|
333 | |||
372 | test "POST /issues.xml should create an issue with the attributes" do |
|
334 | test "POST /issues.xml should create an issue with the attributes" do | |
373 | assert_difference('Issue.count') do |
|
335 | assert_difference('Issue.count') do | |
374 | post '/issues.xml', |
|
336 | post '/issues.xml', | |
375 | {:issue => {:project_id => 1, :subject => 'API test', |
|
337 | {:issue => {:project_id => 1, :subject => 'API test', | |
376 | :tracker_id => 2, :status_id => 3}}, credentials('jsmith') |
|
338 | :tracker_id => 2, :status_id => 3}}, credentials('jsmith') | |
377 | end |
|
339 | end | |
378 | issue = Issue.order('id DESC').first |
|
340 | issue = Issue.order('id DESC').first | |
379 | assert_equal 1, issue.project_id |
|
341 | assert_equal 1, issue.project_id | |
380 | assert_equal 2, issue.tracker_id |
|
342 | assert_equal 2, issue.tracker_id | |
381 | assert_equal 3, issue.status_id |
|
343 | assert_equal 3, issue.status_id | |
382 | assert_equal 'API test', issue.subject |
|
344 | assert_equal 'API test', issue.subject | |
383 |
|
345 | |||
384 | assert_response :created |
|
346 | assert_response :created | |
385 | assert_equal 'application/xml', @response.content_type |
|
347 | assert_equal 'application/xml', @response.content_type | |
386 | assert_select 'issue > id', :text => issue.id.to_s |
|
348 | assert_select 'issue > id', :text => issue.id.to_s | |
387 | end |
|
349 | end | |
388 |
|
350 | |||
389 | test "POST /issues.xml with watcher_user_ids should create issue with watchers" do |
|
351 | test "POST /issues.xml with watcher_user_ids should create issue with watchers" do | |
390 | assert_difference('Issue.count') do |
|
352 | assert_difference('Issue.count') do | |
391 | post '/issues.xml', |
|
353 | post '/issues.xml', | |
392 | {:issue => {:project_id => 1, :subject => 'Watchers', |
|
354 | {:issue => {:project_id => 1, :subject => 'Watchers', | |
393 | :tracker_id => 2, :status_id => 3, :watcher_user_ids => [3, 1]}}, credentials('jsmith') |
|
355 | :tracker_id => 2, :status_id => 3, :watcher_user_ids => [3, 1]}}, credentials('jsmith') | |
394 | assert_response :created |
|
356 | assert_response :created | |
395 | end |
|
357 | end | |
396 | issue = Issue.order('id desc').first |
|
358 | issue = Issue.order('id desc').first | |
397 | assert_equal 2, issue.watchers.size |
|
359 | assert_equal 2, issue.watchers.size | |
398 | assert_equal [1, 3], issue.watcher_user_ids.sort |
|
360 | assert_equal [1, 3], issue.watcher_user_ids.sort | |
399 | end |
|
361 | end | |
400 |
|
362 | |||
401 | test "POST /issues.xml with failure should return errors" do |
|
363 | test "POST /issues.xml with failure should return errors" do | |
402 | assert_no_difference('Issue.count') do |
|
364 | assert_no_difference('Issue.count') do | |
403 | post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith') |
|
365 | post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith') | |
404 | end |
|
366 | end | |
405 |
|
367 | |||
406 | assert_select 'errors error', :text => "Subject can't be blank" |
|
368 | assert_select 'errors error', :text => "Subject can't be blank" | |
407 | end |
|
369 | end | |
408 |
|
370 | |||
409 | test "POST /issues.json should create an issue with the attributes" do |
|
371 | test "POST /issues.json should create an issue with the attributes" do | |
410 | assert_difference('Issue.count') do |
|
372 | assert_difference('Issue.count') do | |
411 | post '/issues.json', |
|
373 | post '/issues.json', | |
412 | {:issue => {:project_id => 1, :subject => 'API test', |
|
374 | {:issue => {:project_id => 1, :subject => 'API test', | |
413 | :tracker_id => 2, :status_id => 3}}, |
|
375 | :tracker_id => 2, :status_id => 3}}, | |
414 | credentials('jsmith') |
|
376 | credentials('jsmith') | |
415 | end |
|
377 | end | |
416 |
|
378 | |||
417 | issue = Issue.order('id DESC').first |
|
379 | issue = Issue.order('id DESC').first | |
418 | assert_equal 1, issue.project_id |
|
380 | assert_equal 1, issue.project_id | |
419 | assert_equal 2, issue.tracker_id |
|
381 | assert_equal 2, issue.tracker_id | |
420 | assert_equal 3, issue.status_id |
|
382 | assert_equal 3, issue.status_id | |
421 | assert_equal 'API test', issue.subject |
|
383 | assert_equal 'API test', issue.subject | |
422 | end |
|
384 | end | |
423 |
|
385 | |||
424 | test "POST /issues.json with failure should return errors" do |
|
386 | test "POST /issues.json with failure should return errors" do | |
425 | assert_no_difference('Issue.count') do |
|
387 | assert_no_difference('Issue.count') do | |
426 | post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith') |
|
388 | post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith') | |
427 | end |
|
389 | end | |
428 |
|
390 | |||
429 | json = ActiveSupport::JSON.decode(response.body) |
|
391 | json = ActiveSupport::JSON.decode(response.body) | |
430 | assert json['errors'].include?("Subject can't be blank") |
|
392 | assert json['errors'].include?("Subject can't be blank") | |
431 | end |
|
393 | end | |
432 |
|
394 | |||
433 | test "PUT /issues/:id.xml" do |
|
395 | test "PUT /issues/:id.xml" do | |
434 | assert_difference('Journal.count') do |
|
396 | assert_difference('Journal.count') do | |
435 | put '/issues/6.xml', |
|
397 | put '/issues/6.xml', | |
436 | {:issue => {:subject => 'API update', :notes => 'A new note'}}, |
|
398 | {:issue => {:subject => 'API update', :notes => 'A new note'}}, | |
437 | credentials('jsmith') |
|
399 | credentials('jsmith') | |
438 | end |
|
400 | end | |
439 |
|
401 | |||
440 | issue = Issue.find(6) |
|
402 | issue = Issue.find(6) | |
441 | assert_equal "API update", issue.subject |
|
403 | assert_equal "API update", issue.subject | |
442 | journal = Journal.last |
|
404 | journal = Journal.last | |
443 | assert_equal "A new note", journal.notes |
|
405 | assert_equal "A new note", journal.notes | |
444 | end |
|
406 | end | |
445 |
|
407 | |||
446 | test "PUT /issues/:id.xml with custom fields" do |
|
408 | test "PUT /issues/:id.xml with custom fields" do | |
447 | put '/issues/3.xml', |
|
409 | put '/issues/3.xml', | |
448 | {:issue => {:custom_fields => [ |
|
410 | {:issue => {:custom_fields => [ | |
449 | {'id' => '1', 'value' => 'PostgreSQL' }, |
|
411 | {'id' => '1', 'value' => 'PostgreSQL' }, | |
450 | {'id' => '2', 'value' => '150'} |
|
412 | {'id' => '2', 'value' => '150'} | |
451 | ]}}, |
|
413 | ]}}, | |
452 | credentials('jsmith') |
|
414 | credentials('jsmith') | |
453 |
|
415 | |||
454 | issue = Issue.find(3) |
|
416 | issue = Issue.find(3) | |
455 | assert_equal '150', issue.custom_value_for(2).value |
|
417 | assert_equal '150', issue.custom_value_for(2).value | |
456 | assert_equal 'PostgreSQL', issue.custom_value_for(1).value |
|
418 | assert_equal 'PostgreSQL', issue.custom_value_for(1).value | |
457 | end |
|
419 | end | |
458 |
|
420 | |||
459 | test "PUT /issues/:id.xml with multi custom fields" do |
|
421 | test "PUT /issues/:id.xml with multi custom fields" do | |
460 | field = CustomField.find(1) |
|
422 | field = CustomField.find(1) | |
461 | field.update_attribute :multiple, true |
|
423 | field.update_attribute :multiple, true | |
462 |
|
424 | |||
463 | put '/issues/3.xml', |
|
425 | put '/issues/3.xml', | |
464 | {:issue => {:custom_fields => [ |
|
426 | {:issue => {:custom_fields => [ | |
465 | {'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] }, |
|
427 | {'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] }, | |
466 | {'id' => '2', 'value' => '150'} |
|
428 | {'id' => '2', 'value' => '150'} | |
467 | ]}}, |
|
429 | ]}}, | |
468 | credentials('jsmith') |
|
430 | credentials('jsmith') | |
469 |
|
431 | |||
470 | issue = Issue.find(3) |
|
432 | issue = Issue.find(3) | |
471 | assert_equal '150', issue.custom_value_for(2).value |
|
433 | assert_equal '150', issue.custom_value_for(2).value | |
472 | assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort |
|
434 | assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort | |
473 | end |
|
435 | end | |
474 |
|
436 | |||
475 | test "PUT /issues/:id.xml with project change" do |
|
437 | test "PUT /issues/:id.xml with project change" do | |
476 | put '/issues/3.xml', |
|
438 | put '/issues/3.xml', | |
477 | {:issue => {:project_id => 2, :subject => 'Project changed'}}, |
|
439 | {:issue => {:project_id => 2, :subject => 'Project changed'}}, | |
478 | credentials('jsmith') |
|
440 | credentials('jsmith') | |
479 |
|
441 | |||
480 | issue = Issue.find(3) |
|
442 | issue = Issue.find(3) | |
481 | assert_equal 2, issue.project_id |
|
443 | assert_equal 2, issue.project_id | |
482 | assert_equal 'Project changed', issue.subject |
|
444 | assert_equal 'Project changed', issue.subject | |
483 | end |
|
445 | end | |
484 |
|
446 | |||
485 | test "PUT /issues/:id.xml with failed update" do |
|
447 | test "PUT /issues/:id.xml with failed update" do | |
486 | put '/issues/6.xml', {:issue => {:subject => ''}}, credentials('jsmith') |
|
448 | put '/issues/6.xml', {:issue => {:subject => ''}}, credentials('jsmith') | |
487 |
|
449 | |||
488 | assert_response :unprocessable_entity |
|
450 | assert_response :unprocessable_entity | |
489 | assert_select 'errors error', :text => "Subject can't be blank" |
|
451 | assert_select 'errors error', :text => "Subject can't be blank" | |
490 | end |
|
452 | end | |
491 |
|
453 | |||
492 | test "PUT /issues/:id.json" do |
|
454 | test "PUT /issues/:id.json" do | |
493 | assert_difference('Journal.count') do |
|
455 | assert_difference('Journal.count') do | |
494 | put '/issues/6.json', |
|
456 | put '/issues/6.json', | |
495 | {:issue => {:subject => 'API update', :notes => 'A new note'}}, |
|
457 | {:issue => {:subject => 'API update', :notes => 'A new note'}}, | |
496 | credentials('jsmith') |
|
458 | credentials('jsmith') | |
497 |
|
459 | |||
498 | assert_response :ok |
|
460 | assert_response :ok | |
499 | assert_equal '', response.body |
|
461 | assert_equal '', response.body | |
500 | end |
|
462 | end | |
501 |
|
463 | |||
502 | issue = Issue.find(6) |
|
464 | issue = Issue.find(6) | |
503 | assert_equal "API update", issue.subject |
|
465 | assert_equal "API update", issue.subject | |
504 | journal = Journal.last |
|
466 | journal = Journal.last | |
505 | assert_equal "A new note", journal.notes |
|
467 | assert_equal "A new note", journal.notes | |
506 | end |
|
468 | end | |
507 |
|
469 | |||
508 | test "PUT /issues/:id.json with failed update" do |
|
470 | test "PUT /issues/:id.json with failed update" do | |
509 | put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith') |
|
471 | put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith') | |
510 |
|
472 | |||
511 | assert_response :unprocessable_entity |
|
473 | assert_response :unprocessable_entity | |
512 | json = ActiveSupport::JSON.decode(response.body) |
|
474 | json = ActiveSupport::JSON.decode(response.body) | |
513 | assert json['errors'].include?("Subject can't be blank") |
|
475 | assert json['errors'].include?("Subject can't be blank") | |
514 | end |
|
476 | end | |
515 |
|
477 | |||
516 | test "DELETE /issues/:id.xml" do |
|
478 | test "DELETE /issues/:id.xml" do | |
517 | assert_difference('Issue.count', -1) do |
|
479 | assert_difference('Issue.count', -1) do | |
518 | delete '/issues/6.xml', {}, credentials('jsmith') |
|
480 | delete '/issues/6.xml', {}, credentials('jsmith') | |
519 |
|
481 | |||
520 | assert_response :ok |
|
482 | assert_response :ok | |
521 | assert_equal '', response.body |
|
483 | assert_equal '', response.body | |
522 | end |
|
484 | end | |
523 | assert_nil Issue.find_by_id(6) |
|
485 | assert_nil Issue.find_by_id(6) | |
524 | end |
|
486 | end | |
525 |
|
487 | |||
526 | test "DELETE /issues/:id.json" do |
|
488 | test "DELETE /issues/:id.json" do | |
527 | assert_difference('Issue.count', -1) do |
|
489 | assert_difference('Issue.count', -1) do | |
528 | delete '/issues/6.json', {}, credentials('jsmith') |
|
490 | delete '/issues/6.json', {}, credentials('jsmith') | |
529 |
|
491 | |||
530 | assert_response :ok |
|
492 | assert_response :ok | |
531 | assert_equal '', response.body |
|
493 | assert_equal '', response.body | |
532 | end |
|
494 | end | |
533 | assert_nil Issue.find_by_id(6) |
|
495 | assert_nil Issue.find_by_id(6) | |
534 | end |
|
496 | end | |
535 |
|
497 | |||
536 | test "POST /issues/:id/watchers.xml should add watcher" do |
|
498 | test "POST /issues/:id/watchers.xml should add watcher" do | |
537 | assert_difference 'Watcher.count' do |
|
499 | assert_difference 'Watcher.count' do | |
538 | post '/issues/1/watchers.xml', {:user_id => 3}, credentials('jsmith') |
|
500 | post '/issues/1/watchers.xml', {:user_id => 3}, credentials('jsmith') | |
539 |
|
501 | |||
540 | assert_response :ok |
|
502 | assert_response :ok | |
541 | assert_equal '', response.body |
|
503 | assert_equal '', response.body | |
542 | end |
|
504 | end | |
543 | watcher = Watcher.order('id desc').first |
|
505 | watcher = Watcher.order('id desc').first | |
544 | assert_equal Issue.find(1), watcher.watchable |
|
506 | assert_equal Issue.find(1), watcher.watchable | |
545 | assert_equal User.find(3), watcher.user |
|
507 | assert_equal User.find(3), watcher.user | |
546 | end |
|
508 | end | |
547 |
|
509 | |||
548 | test "DELETE /issues/:id/watchers/:user_id.xml should remove watcher" do |
|
510 | test "DELETE /issues/:id/watchers/:user_id.xml should remove watcher" do | |
549 | Watcher.create!(:user_id => 3, :watchable => Issue.find(1)) |
|
511 | Watcher.create!(:user_id => 3, :watchable => Issue.find(1)) | |
550 |
|
512 | |||
551 | assert_difference 'Watcher.count', -1 do |
|
513 | assert_difference 'Watcher.count', -1 do | |
552 | delete '/issues/1/watchers/3.xml', {}, credentials('jsmith') |
|
514 | delete '/issues/1/watchers/3.xml', {}, credentials('jsmith') | |
553 |
|
515 | |||
554 | assert_response :ok |
|
516 | assert_response :ok | |
555 | assert_equal '', response.body |
|
517 | assert_equal '', response.body | |
556 | end |
|
518 | end | |
557 | assert_equal false, Issue.find(1).watched_by?(User.find(3)) |
|
519 | assert_equal false, Issue.find(1).watched_by?(User.find(3)) | |
558 | end |
|
520 | end | |
559 |
|
521 | |||
560 | def test_create_issue_with_uploaded_file |
|
522 | def test_create_issue_with_uploaded_file | |
561 | set_tmp_attachments_directory |
|
523 | set_tmp_attachments_directory | |
562 | # upload the file |
|
524 | # upload the file | |
563 | assert_difference 'Attachment.count' do |
|
525 | assert_difference 'Attachment.count' do | |
564 | post '/uploads.xml', 'test_create_with_upload', |
|
526 | post '/uploads.xml', 'test_create_with_upload', | |
565 | {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith')) |
|
527 | {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith')) | |
566 | assert_response :created |
|
528 | assert_response :created | |
567 | end |
|
529 | end | |
568 | xml = Hash.from_xml(response.body) |
|
530 | xml = Hash.from_xml(response.body) | |
569 | token = xml['upload']['token'] |
|
531 | token = xml['upload']['token'] | |
570 | attachment = Attachment.order('id DESC').first |
|
532 | attachment = Attachment.order('id DESC').first | |
571 |
|
533 | |||
572 | # create the issue with the upload's token |
|
534 | # create the issue with the upload's token | |
573 | assert_difference 'Issue.count' do |
|
535 | assert_difference 'Issue.count' do | |
574 | post '/issues.xml', |
|
536 | post '/issues.xml', | |
575 | {:issue => {:project_id => 1, :subject => 'Uploaded file', |
|
537 | {:issue => {:project_id => 1, :subject => 'Uploaded file', | |
576 | :uploads => [{:token => token, :filename => 'test.txt', |
|
538 | :uploads => [{:token => token, :filename => 'test.txt', | |
577 | :content_type => 'text/plain'}]}}, |
|
539 | :content_type => 'text/plain'}]}}, | |
578 | credentials('jsmith') |
|
540 | credentials('jsmith') | |
579 | assert_response :created |
|
541 | assert_response :created | |
580 | end |
|
542 | end | |
581 | issue = Issue.order('id DESC').first |
|
543 | issue = Issue.order('id DESC').first | |
582 | assert_equal 1, issue.attachments.count |
|
544 | assert_equal 1, issue.attachments.count | |
583 | assert_equal attachment, issue.attachments.first |
|
545 | assert_equal attachment, issue.attachments.first | |
584 |
|
546 | |||
585 | attachment.reload |
|
547 | attachment.reload | |
586 | assert_equal 'test.txt', attachment.filename |
|
548 | assert_equal 'test.txt', attachment.filename | |
587 | assert_equal 'text/plain', attachment.content_type |
|
549 | assert_equal 'text/plain', attachment.content_type | |
588 | assert_equal 'test_create_with_upload'.size, attachment.filesize |
|
550 | assert_equal 'test_create_with_upload'.size, attachment.filesize | |
589 | assert_equal 2, attachment.author_id |
|
551 | assert_equal 2, attachment.author_id | |
590 |
|
552 | |||
591 | # get the issue with its attachments |
|
553 | # get the issue with its attachments | |
592 | get "/issues/#{issue.id}.xml", :include => 'attachments' |
|
554 | get "/issues/#{issue.id}.xml", :include => 'attachments' | |
593 | assert_response :success |
|
555 | assert_response :success | |
594 | xml = Hash.from_xml(response.body) |
|
556 | xml = Hash.from_xml(response.body) | |
595 | attachments = xml['issue']['attachments'] |
|
557 | attachments = xml['issue']['attachments'] | |
596 | assert_kind_of Array, attachments |
|
558 | assert_kind_of Array, attachments | |
597 | assert_equal 1, attachments.size |
|
559 | assert_equal 1, attachments.size | |
598 | url = attachments.first['content_url'] |
|
560 | url = attachments.first['content_url'] | |
599 | assert_not_nil url |
|
561 | assert_not_nil url | |
600 |
|
562 | |||
601 | # download the attachment |
|
563 | # download the attachment | |
602 | get url |
|
564 | get url | |
603 | assert_response :success |
|
565 | assert_response :success | |
604 | end |
|
566 | end | |
605 |
|
567 | |||
606 | def test_update_issue_with_uploaded_file |
|
568 | def test_update_issue_with_uploaded_file | |
607 | set_tmp_attachments_directory |
|
569 | set_tmp_attachments_directory | |
608 | # upload the file |
|
570 | # upload the file | |
609 | assert_difference 'Attachment.count' do |
|
571 | assert_difference 'Attachment.count' do | |
610 | post '/uploads.xml', 'test_upload_with_upload', |
|
572 | post '/uploads.xml', 'test_upload_with_upload', | |
611 | {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith')) |
|
573 | {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith')) | |
612 | assert_response :created |
|
574 | assert_response :created | |
613 | end |
|
575 | end | |
614 | xml = Hash.from_xml(response.body) |
|
576 | xml = Hash.from_xml(response.body) | |
615 | token = xml['upload']['token'] |
|
577 | token = xml['upload']['token'] | |
616 | attachment = Attachment.order('id DESC').first |
|
578 | attachment = Attachment.order('id DESC').first | |
617 |
|
579 | |||
618 | # update the issue with the upload's token |
|
580 | # update the issue with the upload's token | |
619 | assert_difference 'Journal.count' do |
|
581 | assert_difference 'Journal.count' do | |
620 | put '/issues/1.xml', |
|
582 | put '/issues/1.xml', | |
621 | {:issue => {:notes => 'Attachment added', |
|
583 | {:issue => {:notes => 'Attachment added', | |
622 | :uploads => [{:token => token, :filename => 'test.txt', |
|
584 | :uploads => [{:token => token, :filename => 'test.txt', | |
623 | :content_type => 'text/plain'}]}}, |
|
585 | :content_type => 'text/plain'}]}}, | |
624 | credentials('jsmith') |
|
586 | credentials('jsmith') | |
625 | assert_response :ok |
|
587 | assert_response :ok | |
626 | assert_equal '', @response.body |
|
588 | assert_equal '', @response.body | |
627 | end |
|
589 | end | |
628 |
|
590 | |||
629 | issue = Issue.find(1) |
|
591 | issue = Issue.find(1) | |
630 | assert_include attachment, issue.attachments |
|
592 | assert_include attachment, issue.attachments | |
631 | end |
|
593 | end | |
632 | end |
|
594 | end |
@@ -1,68 +1,65 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2014 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2014 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program; if not, write to the Free Software |
|
15 | # along with this program; if not, write to the Free Software | |
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | require File.expand_path('../../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class Redmine::ApiTest::NewsTest < Redmine::ApiTest::Base |
|
20 | class Redmine::ApiTest::NewsTest < Redmine::ApiTest::Base | |
21 | fixtures :projects, :trackers, :issue_statuses, :issues, |
|
21 | fixtures :projects, :trackers, :issue_statuses, :issues, | |
22 | :enumerations, :users, :issue_categories, |
|
22 | :enumerations, :users, :issue_categories, | |
23 | :projects_trackers, |
|
23 | :projects_trackers, | |
24 | :roles, |
|
24 | :roles, | |
25 | :member_roles, |
|
25 | :member_roles, | |
26 | :members, |
|
26 | :members, | |
27 | :enabled_modules, |
|
27 | :enabled_modules, | |
28 | :news |
|
28 | :news | |
29 |
|
29 | |||
30 | def setup |
|
30 | def setup | |
31 | Setting.rest_api_enabled = '1' |
|
31 | Setting.rest_api_enabled = '1' | |
32 | end |
|
32 | end | |
33 |
|
33 | |||
34 | should_allow_api_authentication(:get, "/projects/onlinestore/news.xml") |
|
|||
35 | should_allow_api_authentication(:get, "/projects/onlinestore/news.json") |
|
|||
36 |
|
||||
37 | test "GET /news.xml should return news" do |
|
34 | test "GET /news.xml should return news" do | |
38 | get '/news.xml' |
|
35 | get '/news.xml' | |
39 |
|
36 | |||
40 | assert_select 'news[type=array] news id', :text => '2' |
|
37 | assert_select 'news[type=array] news id', :text => '2' | |
41 | end |
|
38 | end | |
42 |
|
39 | |||
43 | test "GET /news.json should return news" do |
|
40 | test "GET /news.json should return news" do | |
44 | get '/news.json' |
|
41 | get '/news.json' | |
45 |
|
42 | |||
46 | json = ActiveSupport::JSON.decode(response.body) |
|
43 | json = ActiveSupport::JSON.decode(response.body) | |
47 | assert_kind_of Hash, json |
|
44 | assert_kind_of Hash, json | |
48 | assert_kind_of Array, json['news'] |
|
45 | assert_kind_of Array, json['news'] | |
49 | assert_kind_of Hash, json['news'].first |
|
46 | assert_kind_of Hash, json['news'].first | |
50 | assert_equal 2, json['news'].first['id'] |
|
47 | assert_equal 2, json['news'].first['id'] | |
51 | end |
|
48 | end | |
52 |
|
49 | |||
53 | test "GET /projects/:project_id/news.xml should return news" do |
|
50 | test "GET /projects/:project_id/news.xml should return news" do | |
54 | get '/projects/ecookbook/news.xml' |
|
51 | get '/projects/ecookbook/news.xml' | |
55 |
|
52 | |||
56 | assert_select 'news[type=array] news id', :text => '2' |
|
53 | assert_select 'news[type=array] news id', :text => '2' | |
57 | end |
|
54 | end | |
58 |
|
55 | |||
59 | test "GET /projects/:project_id/news.json should return news" do |
|
56 | test "GET /projects/:project_id/news.json should return news" do | |
60 | get '/projects/ecookbook/news.json' |
|
57 | get '/projects/ecookbook/news.json' | |
61 |
|
58 | |||
62 | json = ActiveSupport::JSON.decode(response.body) |
|
59 | json = ActiveSupport::JSON.decode(response.body) | |
63 | assert_kind_of Hash, json |
|
60 | assert_kind_of Hash, json | |
64 | assert_kind_of Array, json['news'] |
|
61 | assert_kind_of Array, json['news'] | |
65 | assert_kind_of Hash, json['news'].first |
|
62 | assert_kind_of Hash, json['news'].first | |
66 | assert_equal 2, json['news'].first['id'] |
|
63 | assert_equal 2, json['news'].first['id'] | |
67 | end |
|
64 | end | |
68 | end |
|
65 | end |
@@ -1,251 +1,234 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2014 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2014 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program; if not, write to the Free Software |
|
15 | # along with this program; if not, write to the Free Software | |
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | require File.expand_path('../../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class Redmine::ApiTest::ProjectsTest < Redmine::ApiTest::Base |
|
20 | class Redmine::ApiTest::ProjectsTest < Redmine::ApiTest::Base | |
21 | fixtures :projects, :versions, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details, |
|
21 | fixtures :projects, :versions, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details, | |
22 | :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages, |
|
22 | :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages, | |
23 | :attachments, :custom_fields, :custom_values, :time_entries, :issue_categories |
|
23 | :attachments, :custom_fields, :custom_values, :time_entries, :issue_categories | |
24 |
|
24 | |||
25 | def setup |
|
25 | def setup | |
26 | Setting.rest_api_enabled = '1' |
|
26 | Setting.rest_api_enabled = '1' | |
27 | set_tmp_attachments_directory |
|
27 | set_tmp_attachments_directory | |
28 | end |
|
28 | end | |
29 |
|
29 | |||
30 | # TODO: A private project is needed because should_allow_api_authentication |
|
|||
31 | # actually tests that authentication is *required*, not just allowed |
|
|||
32 | should_allow_api_authentication(:get, "/projects/2.xml") |
|
|||
33 | should_allow_api_authentication(:get, "/projects/2.json") |
|
|||
34 | should_allow_api_authentication(:post, |
|
|||
35 | '/projects.xml', |
|
|||
36 | {:project => {:name => 'API test', :identifier => 'api-test'}}, |
|
|||
37 | {:success_code => :created}) |
|
|||
38 | should_allow_api_authentication(:put, |
|
|||
39 | '/projects/2.xml', |
|
|||
40 | {:project => {:name => 'API update'}}, |
|
|||
41 | {:success_code => :ok}) |
|
|||
42 | should_allow_api_authentication(:delete, |
|
|||
43 | '/projects/2.xml', |
|
|||
44 | {}, |
|
|||
45 | {:success_code => :ok}) |
|
|||
46 |
|
||||
47 | test "GET /projects.xml should return projects" do |
|
30 | test "GET /projects.xml should return projects" do | |
48 | get '/projects.xml' |
|
31 | get '/projects.xml' | |
49 | assert_response :success |
|
32 | assert_response :success | |
50 | assert_equal 'application/xml', @response.content_type |
|
33 | assert_equal 'application/xml', @response.content_type | |
51 |
|
34 | |||
52 | assert_select 'projects>project>id', :text => '1' |
|
35 | assert_select 'projects>project>id', :text => '1' | |
53 | assert_select 'projects>project>status', :text => '1' |
|
36 | assert_select 'projects>project>status', :text => '1' | |
54 | assert_select 'projects>project>is_public', :text => 'true' |
|
37 | assert_select 'projects>project>is_public', :text => 'true' | |
55 | end |
|
38 | end | |
56 |
|
39 | |||
57 | test "GET /projects.json should return projects" do |
|
40 | test "GET /projects.json should return projects" do | |
58 | get '/projects.json' |
|
41 | get '/projects.json' | |
59 | assert_response :success |
|
42 | assert_response :success | |
60 | assert_equal 'application/json', @response.content_type |
|
43 | assert_equal 'application/json', @response.content_type | |
61 |
|
44 | |||
62 | json = ActiveSupport::JSON.decode(response.body) |
|
45 | json = ActiveSupport::JSON.decode(response.body) | |
63 | assert_kind_of Hash, json |
|
46 | assert_kind_of Hash, json | |
64 | assert_kind_of Array, json['projects'] |
|
47 | assert_kind_of Array, json['projects'] | |
65 | assert_kind_of Hash, json['projects'].first |
|
48 | assert_kind_of Hash, json['projects'].first | |
66 | assert json['projects'].first.has_key?('id') |
|
49 | assert json['projects'].first.has_key?('id') | |
67 | end |
|
50 | end | |
68 |
|
51 | |||
69 | test "GET /projects.xml with include=issue_categories should return categories" do |
|
52 | test "GET /projects.xml with include=issue_categories should return categories" do | |
70 | get '/projects.xml?include=issue_categories' |
|
53 | get '/projects.xml?include=issue_categories' | |
71 | assert_response :success |
|
54 | assert_response :success | |
72 | assert_equal 'application/xml', @response.content_type |
|
55 | assert_equal 'application/xml', @response.content_type | |
73 |
|
56 | |||
74 | assert_select 'issue_categories[type=array] issue_category[id=2][name=Recipes]' |
|
57 | assert_select 'issue_categories[type=array] issue_category[id=2][name=Recipes]' | |
75 | end |
|
58 | end | |
76 |
|
59 | |||
77 | test "GET /projects.xml with include=trackers should return trackers" do |
|
60 | test "GET /projects.xml with include=trackers should return trackers" do | |
78 | get '/projects.xml?include=trackers' |
|
61 | get '/projects.xml?include=trackers' | |
79 | assert_response :success |
|
62 | assert_response :success | |
80 | assert_equal 'application/xml', @response.content_type |
|
63 | assert_equal 'application/xml', @response.content_type | |
81 |
|
64 | |||
82 | assert_select 'trackers[type=array] tracker[id=2][name=Feature request]' |
|
65 | assert_select 'trackers[type=array] tracker[id=2][name=Feature request]' | |
83 | end |
|
66 | end | |
84 |
|
67 | |||
85 | test "GET /projects.xml with include=enabled_modules should return enabled modules" do |
|
68 | test "GET /projects.xml with include=enabled_modules should return enabled modules" do | |
86 | get '/projects.xml?include=enabled_modules' |
|
69 | get '/projects.xml?include=enabled_modules' | |
87 | assert_response :success |
|
70 | assert_response :success | |
88 | assert_equal 'application/xml', @response.content_type |
|
71 | assert_equal 'application/xml', @response.content_type | |
89 |
|
72 | |||
90 | assert_select 'enabled_modules[type=array] enabled_module[name=issue_tracking]' |
|
73 | assert_select 'enabled_modules[type=array] enabled_module[name=issue_tracking]' | |
91 | end |
|
74 | end | |
92 |
|
75 | |||
93 | test "GET /projects/:id.xml should return the project" do |
|
76 | test "GET /projects/:id.xml should return the project" do | |
94 | get '/projects/1.xml' |
|
77 | get '/projects/1.xml' | |
95 | assert_response :success |
|
78 | assert_response :success | |
96 | assert_equal 'application/xml', @response.content_type |
|
79 | assert_equal 'application/xml', @response.content_type | |
97 |
|
80 | |||
98 | assert_select 'project>id', :text => '1' |
|
81 | assert_select 'project>id', :text => '1' | |
99 | assert_select 'project>status', :text => '1' |
|
82 | assert_select 'project>status', :text => '1' | |
100 | assert_select 'project>is_public', :text => 'true' |
|
83 | assert_select 'project>is_public', :text => 'true' | |
101 | assert_select 'custom_field[name=Development status]', :text => 'Stable' |
|
84 | assert_select 'custom_field[name=Development status]', :text => 'Stable' | |
102 |
|
85 | |||
103 | assert_select 'trackers', 0 |
|
86 | assert_select 'trackers', 0 | |
104 | assert_select 'issue_categories', 0 |
|
87 | assert_select 'issue_categories', 0 | |
105 | end |
|
88 | end | |
106 |
|
89 | |||
107 | test "GET /projects/:id.json should return the project" do |
|
90 | test "GET /projects/:id.json should return the project" do | |
108 | get '/projects/1.json' |
|
91 | get '/projects/1.json' | |
109 |
|
92 | |||
110 | json = ActiveSupport::JSON.decode(response.body) |
|
93 | json = ActiveSupport::JSON.decode(response.body) | |
111 | assert_kind_of Hash, json |
|
94 | assert_kind_of Hash, json | |
112 | assert_kind_of Hash, json['project'] |
|
95 | assert_kind_of Hash, json['project'] | |
113 | assert_equal 1, json['project']['id'] |
|
96 | assert_equal 1, json['project']['id'] | |
114 | end |
|
97 | end | |
115 |
|
98 | |||
116 | test "GET /projects/:id.xml with hidden custom fields should not display hidden custom fields" do |
|
99 | test "GET /projects/:id.xml with hidden custom fields should not display hidden custom fields" do | |
117 | ProjectCustomField.find_by_name('Development status').update_attribute :visible, false |
|
100 | ProjectCustomField.find_by_name('Development status').update_attribute :visible, false | |
118 |
|
101 | |||
119 | get '/projects/1.xml' |
|
102 | get '/projects/1.xml' | |
120 | assert_response :success |
|
103 | assert_response :success | |
121 | assert_equal 'application/xml', @response.content_type |
|
104 | assert_equal 'application/xml', @response.content_type | |
122 |
|
105 | |||
123 | assert_select 'custom_field[name=?]', 'Development status', 0 |
|
106 | assert_select 'custom_field[name=?]', 'Development status', 0 | |
124 | end |
|
107 | end | |
125 |
|
108 | |||
126 | test "GET /projects/:id.xml with include=issue_categories should return categories" do |
|
109 | test "GET /projects/:id.xml with include=issue_categories should return categories" do | |
127 | get '/projects/1.xml?include=issue_categories' |
|
110 | get '/projects/1.xml?include=issue_categories' | |
128 | assert_response :success |
|
111 | assert_response :success | |
129 | assert_equal 'application/xml', @response.content_type |
|
112 | assert_equal 'application/xml', @response.content_type | |
130 |
|
113 | |||
131 | assert_select 'issue_categories[type=array] issue_category[id=2][name=Recipes]' |
|
114 | assert_select 'issue_categories[type=array] issue_category[id=2][name=Recipes]' | |
132 | end |
|
115 | end | |
133 |
|
116 | |||
134 | test "GET /projects/:id.xml with include=trackers should return trackers" do |
|
117 | test "GET /projects/:id.xml with include=trackers should return trackers" do | |
135 | get '/projects/1.xml?include=trackers' |
|
118 | get '/projects/1.xml?include=trackers' | |
136 | assert_response :success |
|
119 | assert_response :success | |
137 | assert_equal 'application/xml', @response.content_type |
|
120 | assert_equal 'application/xml', @response.content_type | |
138 |
|
121 | |||
139 | assert_select 'trackers[type=array] tracker[id=2][name=Feature request]' |
|
122 | assert_select 'trackers[type=array] tracker[id=2][name=Feature request]' | |
140 | end |
|
123 | end | |
141 |
|
124 | |||
142 | test "GET /projects/:id.xml with include=enabled_modules should return enabled modules" do |
|
125 | test "GET /projects/:id.xml with include=enabled_modules should return enabled modules" do | |
143 | get '/projects/1.xml?include=enabled_modules' |
|
126 | get '/projects/1.xml?include=enabled_modules' | |
144 | assert_response :success |
|
127 | assert_response :success | |
145 | assert_equal 'application/xml', @response.content_type |
|
128 | assert_equal 'application/xml', @response.content_type | |
146 |
|
129 | |||
147 | assert_select 'enabled_modules[type=array] enabled_module[name=issue_tracking]' |
|
130 | assert_select 'enabled_modules[type=array] enabled_module[name=issue_tracking]' | |
148 | end |
|
131 | end | |
149 |
|
132 | |||
150 | test "POST /projects.xml with valid parameters should create the project" do |
|
133 | test "POST /projects.xml with valid parameters should create the project" do | |
151 | with_settings :default_projects_modules => ['issue_tracking', 'repository'] do |
|
134 | with_settings :default_projects_modules => ['issue_tracking', 'repository'] do | |
152 | assert_difference('Project.count') do |
|
135 | assert_difference('Project.count') do | |
153 | post '/projects.xml', |
|
136 | post '/projects.xml', | |
154 | {:project => {:name => 'API test', :identifier => 'api-test'}}, |
|
137 | {:project => {:name => 'API test', :identifier => 'api-test'}}, | |
155 | credentials('admin') |
|
138 | credentials('admin') | |
156 | end |
|
139 | end | |
157 | end |
|
140 | end | |
158 |
|
141 | |||
159 | project = Project.order('id DESC').first |
|
142 | project = Project.order('id DESC').first | |
160 | assert_equal 'API test', project.name |
|
143 | assert_equal 'API test', project.name | |
161 | assert_equal 'api-test', project.identifier |
|
144 | assert_equal 'api-test', project.identifier | |
162 | assert_equal ['issue_tracking', 'repository'], project.enabled_module_names.sort |
|
145 | assert_equal ['issue_tracking', 'repository'], project.enabled_module_names.sort | |
163 | assert_equal Tracker.all.size, project.trackers.size |
|
146 | assert_equal Tracker.all.size, project.trackers.size | |
164 |
|
147 | |||
165 | assert_response :created |
|
148 | assert_response :created | |
166 | assert_equal 'application/xml', @response.content_type |
|
149 | assert_equal 'application/xml', @response.content_type | |
167 | assert_select 'project id', :text => project.id.to_s |
|
150 | assert_select 'project id', :text => project.id.to_s | |
168 | end |
|
151 | end | |
169 |
|
152 | |||
170 | test "POST /projects.xml should accept enabled_module_names attribute" do |
|
153 | test "POST /projects.xml should accept enabled_module_names attribute" do | |
171 | assert_difference('Project.count') do |
|
154 | assert_difference('Project.count') do | |
172 | post '/projects.xml', |
|
155 | post '/projects.xml', | |
173 | {:project => {:name => 'API test', :identifier => 'api-test', :enabled_module_names => ['issue_tracking', 'news', 'time_tracking']}}, |
|
156 | {:project => {:name => 'API test', :identifier => 'api-test', :enabled_module_names => ['issue_tracking', 'news', 'time_tracking']}}, | |
174 | credentials('admin') |
|
157 | credentials('admin') | |
175 | end |
|
158 | end | |
176 |
|
159 | |||
177 | project = Project.order('id DESC').first |
|
160 | project = Project.order('id DESC').first | |
178 | assert_equal ['issue_tracking', 'news', 'time_tracking'], project.enabled_module_names.sort |
|
161 | assert_equal ['issue_tracking', 'news', 'time_tracking'], project.enabled_module_names.sort | |
179 | end |
|
162 | end | |
180 |
|
163 | |||
181 | test "POST /projects.xml should accept tracker_ids attribute" do |
|
164 | test "POST /projects.xml should accept tracker_ids attribute" do | |
182 | assert_difference('Project.count') do |
|
165 | assert_difference('Project.count') do | |
183 | post '/projects.xml', |
|
166 | post '/projects.xml', | |
184 | {:project => {:name => 'API test', :identifier => 'api-test', :tracker_ids => [1, 3]}}, |
|
167 | {:project => {:name => 'API test', :identifier => 'api-test', :tracker_ids => [1, 3]}}, | |
185 | credentials('admin') |
|
168 | credentials('admin') | |
186 | end |
|
169 | end | |
187 |
|
170 | |||
188 | project = Project.order('id DESC').first |
|
171 | project = Project.order('id DESC').first | |
189 | assert_equal [1, 3], project.trackers.map(&:id).sort |
|
172 | assert_equal [1, 3], project.trackers.map(&:id).sort | |
190 | end |
|
173 | end | |
191 |
|
174 | |||
192 | test "POST /projects.xml with invalid parameters should return errors" do |
|
175 | test "POST /projects.xml with invalid parameters should return errors" do | |
193 | assert_no_difference('Project.count') do |
|
176 | assert_no_difference('Project.count') do | |
194 | post '/projects.xml', {:project => {:name => 'API test'}}, credentials('admin') |
|
177 | post '/projects.xml', {:project => {:name => 'API test'}}, credentials('admin') | |
195 | end |
|
178 | end | |
196 |
|
179 | |||
197 | assert_response :unprocessable_entity |
|
180 | assert_response :unprocessable_entity | |
198 | assert_equal 'application/xml', @response.content_type |
|
181 | assert_equal 'application/xml', @response.content_type | |
199 | assert_select 'errors error', :text => "Identifier can't be blank" |
|
182 | assert_select 'errors error', :text => "Identifier can't be blank" | |
200 | end |
|
183 | end | |
201 |
|
184 | |||
202 | test "PUT /projects/:id.xml with valid parameters should update the project" do |
|
185 | test "PUT /projects/:id.xml with valid parameters should update the project" do | |
203 | assert_no_difference 'Project.count' do |
|
186 | assert_no_difference 'Project.count' do | |
204 | put '/projects/2.xml', {:project => {:name => 'API update'}}, credentials('jsmith') |
|
187 | put '/projects/2.xml', {:project => {:name => 'API update'}}, credentials('jsmith') | |
205 | end |
|
188 | end | |
206 | assert_response :ok |
|
189 | assert_response :ok | |
207 | assert_equal '', @response.body |
|
190 | assert_equal '', @response.body | |
208 | assert_equal 'application/xml', @response.content_type |
|
191 | assert_equal 'application/xml', @response.content_type | |
209 | project = Project.find(2) |
|
192 | project = Project.find(2) | |
210 | assert_equal 'API update', project.name |
|
193 | assert_equal 'API update', project.name | |
211 | end |
|
194 | end | |
212 |
|
195 | |||
213 | test "PUT /projects/:id.xml should accept enabled_module_names attribute" do |
|
196 | test "PUT /projects/:id.xml should accept enabled_module_names attribute" do | |
214 | assert_no_difference 'Project.count' do |
|
197 | assert_no_difference 'Project.count' do | |
215 | put '/projects/2.xml', {:project => {:name => 'API update', :enabled_module_names => ['issue_tracking', 'news', 'time_tracking']}}, credentials('admin') |
|
198 | put '/projects/2.xml', {:project => {:name => 'API update', :enabled_module_names => ['issue_tracking', 'news', 'time_tracking']}}, credentials('admin') | |
216 | end |
|
199 | end | |
217 | assert_response :ok |
|
200 | assert_response :ok | |
218 | assert_equal '', @response.body |
|
201 | assert_equal '', @response.body | |
219 | project = Project.find(2) |
|
202 | project = Project.find(2) | |
220 | assert_equal ['issue_tracking', 'news', 'time_tracking'], project.enabled_module_names.sort |
|
203 | assert_equal ['issue_tracking', 'news', 'time_tracking'], project.enabled_module_names.sort | |
221 | end |
|
204 | end | |
222 |
|
205 | |||
223 | test "PUT /projects/:id.xml should accept tracker_ids attribute" do |
|
206 | test "PUT /projects/:id.xml should accept tracker_ids attribute" do | |
224 | assert_no_difference 'Project.count' do |
|
207 | assert_no_difference 'Project.count' do | |
225 | put '/projects/2.xml', {:project => {:name => 'API update', :tracker_ids => [1, 3]}}, credentials('admin') |
|
208 | put '/projects/2.xml', {:project => {:name => 'API update', :tracker_ids => [1, 3]}}, credentials('admin') | |
226 | end |
|
209 | end | |
227 | assert_response :ok |
|
210 | assert_response :ok | |
228 | assert_equal '', @response.body |
|
211 | assert_equal '', @response.body | |
229 | project = Project.find(2) |
|
212 | project = Project.find(2) | |
230 | assert_equal [1, 3], project.trackers.map(&:id).sort |
|
213 | assert_equal [1, 3], project.trackers.map(&:id).sort | |
231 | end |
|
214 | end | |
232 |
|
215 | |||
233 | test "PUT /projects/:id.xml with invalid parameters should return errors" do |
|
216 | test "PUT /projects/:id.xml with invalid parameters should return errors" do | |
234 | assert_no_difference('Project.count') do |
|
217 | assert_no_difference('Project.count') do | |
235 | put '/projects/2.xml', {:project => {:name => ''}}, credentials('admin') |
|
218 | put '/projects/2.xml', {:project => {:name => ''}}, credentials('admin') | |
236 | end |
|
219 | end | |
237 |
|
220 | |||
238 | assert_response :unprocessable_entity |
|
221 | assert_response :unprocessable_entity | |
239 | assert_equal 'application/xml', @response.content_type |
|
222 | assert_equal 'application/xml', @response.content_type | |
240 | assert_select 'errors error', :text => "Name can't be blank" |
|
223 | assert_select 'errors error', :text => "Name can't be blank" | |
241 | end |
|
224 | end | |
242 |
|
225 | |||
243 | test "DELETE /projects/:id.xml should delete the project" do |
|
226 | test "DELETE /projects/:id.xml should delete the project" do | |
244 | assert_difference('Project.count',-1) do |
|
227 | assert_difference('Project.count',-1) do | |
245 | delete '/projects/2.xml', {}, credentials('admin') |
|
228 | delete '/projects/2.xml', {}, credentials('admin') | |
246 | end |
|
229 | end | |
247 | assert_response :ok |
|
230 | assert_response :ok | |
248 | assert_equal '', @response.body |
|
231 | assert_equal '', @response.body | |
249 | assert_nil Project.find_by_id(2) |
|
232 | assert_nil Project.find_by_id(2) | |
250 | end |
|
233 | end | |
251 | end |
|
234 | end |
@@ -1,317 +1,278 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2014 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2014 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program; if not, write to the Free Software |
|
15 | # along with this program; if not, write to the Free Software | |
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | require File.expand_path('../../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class Redmine::ApiTest::UsersTest < Redmine::ApiTest::Base |
|
20 | class Redmine::ApiTest::UsersTest < Redmine::ApiTest::Base | |
21 | fixtures :users, :members, :member_roles, :roles, :projects |
|
21 | fixtures :users, :members, :member_roles, :roles, :projects | |
22 |
|
22 | |||
23 | def setup |
|
23 | def setup | |
24 | Setting.rest_api_enabled = '1' |
|
24 | Setting.rest_api_enabled = '1' | |
25 | end |
|
25 | end | |
26 |
|
26 | |||
27 | should_allow_api_authentication(:get, "/users.xml") |
|
|||
28 | should_allow_api_authentication(:get, "/users.json") |
|
|||
29 | should_allow_api_authentication(:post, |
|
|||
30 | '/users.xml', |
|
|||
31 | {:user => { |
|
|||
32 | :login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname', |
|
|||
33 | :mail => 'foo@example.net', :password => 'secret123' |
|
|||
34 | }}, |
|
|||
35 | {:success_code => :created}) |
|
|||
36 | should_allow_api_authentication(:post, |
|
|||
37 | '/users.json', |
|
|||
38 | {:user => { |
|
|||
39 | :login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname', |
|
|||
40 | :mail => 'foo@example.net' |
|
|||
41 | }}, |
|
|||
42 | {:success_code => :created}) |
|
|||
43 | should_allow_api_authentication(:put, |
|
|||
44 | '/users/2.xml', |
|
|||
45 | {:user => { |
|
|||
46 | :login => 'jsmith', :firstname => 'John', :lastname => 'Renamed', |
|
|||
47 | :mail => 'jsmith@somenet.foo' |
|
|||
48 | }}, |
|
|||
49 | {:success_code => :ok}) |
|
|||
50 | should_allow_api_authentication(:put, |
|
|||
51 | '/users/2.json', |
|
|||
52 | {:user => { |
|
|||
53 | :login => 'jsmith', :firstname => 'John', :lastname => 'Renamed', |
|
|||
54 | :mail => 'jsmith@somenet.foo' |
|
|||
55 | }}, |
|
|||
56 | {:success_code => :ok}) |
|
|||
57 | should_allow_api_authentication(:delete, |
|
|||
58 | '/users/2.xml', |
|
|||
59 | {}, |
|
|||
60 | {:success_code => :ok}) |
|
|||
61 | should_allow_api_authentication(:delete, |
|
|||
62 | '/users/2.xml', |
|
|||
63 | {}, |
|
|||
64 | {:success_code => :ok}) |
|
|||
65 |
|
||||
66 | test "GET /users/:id.xml should return the user" do |
|
27 | test "GET /users/:id.xml should return the user" do | |
67 | get '/users/2.xml' |
|
28 | get '/users/2.xml' | |
68 |
|
29 | |||
69 | assert_response :success |
|
30 | assert_response :success | |
70 | assert_select 'user id', :text => '2' |
|
31 | assert_select 'user id', :text => '2' | |
71 | end |
|
32 | end | |
72 |
|
33 | |||
73 | test "GET /users/:id.json should return the user" do |
|
34 | test "GET /users/:id.json should return the user" do | |
74 | get '/users/2.json' |
|
35 | get '/users/2.json' | |
75 |
|
36 | |||
76 | assert_response :success |
|
37 | assert_response :success | |
77 | json = ActiveSupport::JSON.decode(response.body) |
|
38 | json = ActiveSupport::JSON.decode(response.body) | |
78 | assert_kind_of Hash, json |
|
39 | assert_kind_of Hash, json | |
79 | assert_kind_of Hash, json['user'] |
|
40 | assert_kind_of Hash, json['user'] | |
80 | assert_equal 2, json['user']['id'] |
|
41 | assert_equal 2, json['user']['id'] | |
81 | end |
|
42 | end | |
82 |
|
43 | |||
83 | test "GET /users/:id.xml with include=memberships should include memberships" do |
|
44 | test "GET /users/:id.xml with include=memberships should include memberships" do | |
84 | get '/users/2.xml?include=memberships' |
|
45 | get '/users/2.xml?include=memberships' | |
85 |
|
46 | |||
86 | assert_response :success |
|
47 | assert_response :success | |
87 | assert_select 'user memberships', 1 |
|
48 | assert_select 'user memberships', 1 | |
88 | end |
|
49 | end | |
89 |
|
50 | |||
90 | test "GET /users/:id.json with include=memberships should include memberships" do |
|
51 | test "GET /users/:id.json with include=memberships should include memberships" do | |
91 | get '/users/2.json?include=memberships' |
|
52 | get '/users/2.json?include=memberships' | |
92 |
|
53 | |||
93 | assert_response :success |
|
54 | assert_response :success | |
94 | json = ActiveSupport::JSON.decode(response.body) |
|
55 | json = ActiveSupport::JSON.decode(response.body) | |
95 | assert_kind_of Array, json['user']['memberships'] |
|
56 | assert_kind_of Array, json['user']['memberships'] | |
96 | assert_equal [{ |
|
57 | assert_equal [{ | |
97 | "id"=>1, |
|
58 | "id"=>1, | |
98 | "project"=>{"name"=>"eCookbook", "id"=>1}, |
|
59 | "project"=>{"name"=>"eCookbook", "id"=>1}, | |
99 | "roles"=>[{"name"=>"Manager", "id"=>1}] |
|
60 | "roles"=>[{"name"=>"Manager", "id"=>1}] | |
100 | }], json['user']['memberships'] |
|
61 | }], json['user']['memberships'] | |
101 | end |
|
62 | end | |
102 |
|
63 | |||
103 | test "GET /users/current.xml should require authentication" do |
|
64 | test "GET /users/current.xml should require authentication" do | |
104 | get '/users/current.xml' |
|
65 | get '/users/current.xml' | |
105 |
|
66 | |||
106 | assert_response 401 |
|
67 | assert_response 401 | |
107 | end |
|
68 | end | |
108 |
|
69 | |||
109 | test "GET /users/current.xml should return current user" do |
|
70 | test "GET /users/current.xml should return current user" do | |
110 | get '/users/current.xml', {}, credentials('jsmith') |
|
71 | get '/users/current.xml', {}, credentials('jsmith') | |
111 |
|
72 | |||
112 | assert_select 'user id', :text => '2' |
|
73 | assert_select 'user id', :text => '2' | |
113 | end |
|
74 | end | |
114 |
|
75 | |||
115 | test "GET /users/:id should not return login for other user" do |
|
76 | test "GET /users/:id should not return login for other user" do | |
116 | get '/users/3.xml', {}, credentials('jsmith') |
|
77 | get '/users/3.xml', {}, credentials('jsmith') | |
117 | assert_response :success |
|
78 | assert_response :success | |
118 | assert_select 'user login', 0 |
|
79 | assert_select 'user login', 0 | |
119 | end |
|
80 | end | |
120 |
|
81 | |||
121 | test "GET /users/:id should return login for current user" do |
|
82 | test "GET /users/:id should return login for current user" do | |
122 | get '/users/2.xml', {}, credentials('jsmith') |
|
83 | get '/users/2.xml', {}, credentials('jsmith') | |
123 | assert_response :success |
|
84 | assert_response :success | |
124 | assert_select 'user login', :text => 'jsmith' |
|
85 | assert_select 'user login', :text => 'jsmith' | |
125 | end |
|
86 | end | |
126 |
|
87 | |||
127 | test "GET /users/:id should not return api_key for other user" do |
|
88 | test "GET /users/:id should not return api_key for other user" do | |
128 | get '/users/3.xml', {}, credentials('jsmith') |
|
89 | get '/users/3.xml', {}, credentials('jsmith') | |
129 | assert_response :success |
|
90 | assert_response :success | |
130 | assert_select 'user api_key', 0 |
|
91 | assert_select 'user api_key', 0 | |
131 | end |
|
92 | end | |
132 |
|
93 | |||
133 | test "GET /users/:id should return api_key for current user" do |
|
94 | test "GET /users/:id should return api_key for current user" do | |
134 | get '/users/2.xml', {}, credentials('jsmith') |
|
95 | get '/users/2.xml', {}, credentials('jsmith') | |
135 | assert_response :success |
|
96 | assert_response :success | |
136 | assert_select 'user api_key', :text => User.find(2).api_key |
|
97 | assert_select 'user api_key', :text => User.find(2).api_key | |
137 | end |
|
98 | end | |
138 |
|
99 | |||
139 | test "GET /users/:id should not return status for standard user" do |
|
100 | test "GET /users/:id should not return status for standard user" do | |
140 | get '/users/3.xml', {}, credentials('jsmith') |
|
101 | get '/users/3.xml', {}, credentials('jsmith') | |
141 | assert_response :success |
|
102 | assert_response :success | |
142 | assert_select 'user status', 0 |
|
103 | assert_select 'user status', 0 | |
143 | end |
|
104 | end | |
144 |
|
105 | |||
145 | test "GET /users/:id should return status for administrators" do |
|
106 | test "GET /users/:id should return status for administrators" do | |
146 | get '/users/2.xml', {}, credentials('admin') |
|
107 | get '/users/2.xml', {}, credentials('admin') | |
147 | assert_response :success |
|
108 | assert_response :success | |
148 | assert_select 'user status', :text => User.find(1).status.to_s |
|
109 | assert_select 'user status', :text => User.find(1).status.to_s | |
149 | end |
|
110 | end | |
150 |
|
111 | |||
151 | test "POST /users.xml with valid parameters should create the user" do |
|
112 | test "POST /users.xml with valid parameters should create the user" do | |
152 | assert_difference('User.count') do |
|
113 | assert_difference('User.count') do | |
153 | post '/users.xml', { |
|
114 | post '/users.xml', { | |
154 | :user => { |
|
115 | :user => { | |
155 | :login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname', |
|
116 | :login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname', | |
156 | :mail => 'foo@example.net', :password => 'secret123', |
|
117 | :mail => 'foo@example.net', :password => 'secret123', | |
157 | :mail_notification => 'only_assigned'} |
|
118 | :mail_notification => 'only_assigned'} | |
158 | }, |
|
119 | }, | |
159 | credentials('admin') |
|
120 | credentials('admin') | |
160 | end |
|
121 | end | |
161 |
|
122 | |||
162 | user = User.order('id DESC').first |
|
123 | user = User.order('id DESC').first | |
163 | assert_equal 'foo', user.login |
|
124 | assert_equal 'foo', user.login | |
164 | assert_equal 'Firstname', user.firstname |
|
125 | assert_equal 'Firstname', user.firstname | |
165 | assert_equal 'Lastname', user.lastname |
|
126 | assert_equal 'Lastname', user.lastname | |
166 | assert_equal 'foo@example.net', user.mail |
|
127 | assert_equal 'foo@example.net', user.mail | |
167 | assert_equal 'only_assigned', user.mail_notification |
|
128 | assert_equal 'only_assigned', user.mail_notification | |
168 | assert !user.admin? |
|
129 | assert !user.admin? | |
169 | assert user.check_password?('secret123') |
|
130 | assert user.check_password?('secret123') | |
170 |
|
131 | |||
171 | assert_response :created |
|
132 | assert_response :created | |
172 | assert_equal 'application/xml', @response.content_type |
|
133 | assert_equal 'application/xml', @response.content_type | |
173 | assert_select 'user id', :text => user.id.to_s |
|
134 | assert_select 'user id', :text => user.id.to_s | |
174 | end |
|
135 | end | |
175 |
|
136 | |||
176 | test "POST /users.json with valid parameters should create the user" do |
|
137 | test "POST /users.json with valid parameters should create the user" do | |
177 | assert_difference('User.count') do |
|
138 | assert_difference('User.count') do | |
178 | post '/users.json', { |
|
139 | post '/users.json', { | |
179 | :user => { |
|
140 | :user => { | |
180 | :login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname', |
|
141 | :login => 'foo', :firstname => 'Firstname', :lastname => 'Lastname', | |
181 | :mail => 'foo@example.net', :password => 'secret123', |
|
142 | :mail => 'foo@example.net', :password => 'secret123', | |
182 | :mail_notification => 'only_assigned'} |
|
143 | :mail_notification => 'only_assigned'} | |
183 | }, |
|
144 | }, | |
184 | credentials('admin') |
|
145 | credentials('admin') | |
185 | end |
|
146 | end | |
186 |
|
147 | |||
187 | user = User.order('id DESC').first |
|
148 | user = User.order('id DESC').first | |
188 | assert_equal 'foo', user.login |
|
149 | assert_equal 'foo', user.login | |
189 | assert_equal 'Firstname', user.firstname |
|
150 | assert_equal 'Firstname', user.firstname | |
190 | assert_equal 'Lastname', user.lastname |
|
151 | assert_equal 'Lastname', user.lastname | |
191 | assert_equal 'foo@example.net', user.mail |
|
152 | assert_equal 'foo@example.net', user.mail | |
192 | assert !user.admin? |
|
153 | assert !user.admin? | |
193 |
|
154 | |||
194 | assert_response :created |
|
155 | assert_response :created | |
195 | assert_equal 'application/json', @response.content_type |
|
156 | assert_equal 'application/json', @response.content_type | |
196 | json = ActiveSupport::JSON.decode(response.body) |
|
157 | json = ActiveSupport::JSON.decode(response.body) | |
197 | assert_kind_of Hash, json |
|
158 | assert_kind_of Hash, json | |
198 | assert_kind_of Hash, json['user'] |
|
159 | assert_kind_of Hash, json['user'] | |
199 | assert_equal user.id, json['user']['id'] |
|
160 | assert_equal user.id, json['user']['id'] | |
200 | end |
|
161 | end | |
201 |
|
162 | |||
202 | test "POST /users.xml with with invalid parameters should return errors" do |
|
163 | test "POST /users.xml with with invalid parameters should return errors" do | |
203 | assert_no_difference('User.count') do |
|
164 | assert_no_difference('User.count') do | |
204 | post '/users.xml', {:user => {:login => 'foo', :lastname => 'Lastname', :mail => 'foo'}}, credentials('admin') |
|
165 | post '/users.xml', {:user => {:login => 'foo', :lastname => 'Lastname', :mail => 'foo'}}, credentials('admin') | |
205 | end |
|
166 | end | |
206 |
|
167 | |||
207 | assert_response :unprocessable_entity |
|
168 | assert_response :unprocessable_entity | |
208 | assert_equal 'application/xml', @response.content_type |
|
169 | assert_equal 'application/xml', @response.content_type | |
209 | assert_select 'errors error', :text => "First name can't be blank" |
|
170 | assert_select 'errors error', :text => "First name can't be blank" | |
210 | end |
|
171 | end | |
211 |
|
172 | |||
212 | test "POST /users.json with with invalid parameters should return errors" do |
|
173 | test "POST /users.json with with invalid parameters should return errors" do | |
213 | assert_no_difference('User.count') do |
|
174 | assert_no_difference('User.count') do | |
214 | post '/users.json', {:user => {:login => 'foo', :lastname => 'Lastname', :mail => 'foo'}}, credentials('admin') |
|
175 | post '/users.json', {:user => {:login => 'foo', :lastname => 'Lastname', :mail => 'foo'}}, credentials('admin') | |
215 | end |
|
176 | end | |
216 |
|
177 | |||
217 | assert_response :unprocessable_entity |
|
178 | assert_response :unprocessable_entity | |
218 | assert_equal 'application/json', @response.content_type |
|
179 | assert_equal 'application/json', @response.content_type | |
219 | json = ActiveSupport::JSON.decode(response.body) |
|
180 | json = ActiveSupport::JSON.decode(response.body) | |
220 | assert_kind_of Hash, json |
|
181 | assert_kind_of Hash, json | |
221 | assert json.has_key?('errors') |
|
182 | assert json.has_key?('errors') | |
222 | assert_kind_of Array, json['errors'] |
|
183 | assert_kind_of Array, json['errors'] | |
223 | end |
|
184 | end | |
224 |
|
185 | |||
225 | test "PUT /users/:id.xml with valid parameters should update the user" do |
|
186 | test "PUT /users/:id.xml with valid parameters should update the user" do | |
226 | assert_no_difference('User.count') do |
|
187 | assert_no_difference('User.count') do | |
227 | put '/users/2.xml', { |
|
188 | put '/users/2.xml', { | |
228 | :user => { |
|
189 | :user => { | |
229 | :login => 'jsmith', :firstname => 'John', :lastname => 'Renamed', |
|
190 | :login => 'jsmith', :firstname => 'John', :lastname => 'Renamed', | |
230 | :mail => 'jsmith@somenet.foo'} |
|
191 | :mail => 'jsmith@somenet.foo'} | |
231 | }, |
|
192 | }, | |
232 | credentials('admin') |
|
193 | credentials('admin') | |
233 | end |
|
194 | end | |
234 |
|
195 | |||
235 | user = User.find(2) |
|
196 | user = User.find(2) | |
236 | assert_equal 'jsmith', user.login |
|
197 | assert_equal 'jsmith', user.login | |
237 | assert_equal 'John', user.firstname |
|
198 | assert_equal 'John', user.firstname | |
238 | assert_equal 'Renamed', user.lastname |
|
199 | assert_equal 'Renamed', user.lastname | |
239 | assert_equal 'jsmith@somenet.foo', user.mail |
|
200 | assert_equal 'jsmith@somenet.foo', user.mail | |
240 | assert !user.admin? |
|
201 | assert !user.admin? | |
241 |
|
202 | |||
242 | assert_response :ok |
|
203 | assert_response :ok | |
243 | assert_equal '', @response.body |
|
204 | assert_equal '', @response.body | |
244 | end |
|
205 | end | |
245 |
|
206 | |||
246 | test "PUT /users/:id.json with valid parameters should update the user" do |
|
207 | test "PUT /users/:id.json with valid parameters should update the user" do | |
247 | assert_no_difference('User.count') do |
|
208 | assert_no_difference('User.count') do | |
248 | put '/users/2.json', { |
|
209 | put '/users/2.json', { | |
249 | :user => { |
|
210 | :user => { | |
250 | :login => 'jsmith', :firstname => 'John', :lastname => 'Renamed', |
|
211 | :login => 'jsmith', :firstname => 'John', :lastname => 'Renamed', | |
251 | :mail => 'jsmith@somenet.foo'} |
|
212 | :mail => 'jsmith@somenet.foo'} | |
252 | }, |
|
213 | }, | |
253 | credentials('admin') |
|
214 | credentials('admin') | |
254 | end |
|
215 | end | |
255 |
|
216 | |||
256 | user = User.find(2) |
|
217 | user = User.find(2) | |
257 | assert_equal 'jsmith', user.login |
|
218 | assert_equal 'jsmith', user.login | |
258 | assert_equal 'John', user.firstname |
|
219 | assert_equal 'John', user.firstname | |
259 | assert_equal 'Renamed', user.lastname |
|
220 | assert_equal 'Renamed', user.lastname | |
260 | assert_equal 'jsmith@somenet.foo', user.mail |
|
221 | assert_equal 'jsmith@somenet.foo', user.mail | |
261 | assert !user.admin? |
|
222 | assert !user.admin? | |
262 |
|
223 | |||
263 | assert_response :ok |
|
224 | assert_response :ok | |
264 | assert_equal '', @response.body |
|
225 | assert_equal '', @response.body | |
265 | end |
|
226 | end | |
266 |
|
227 | |||
267 | test "PUT /users/:id.xml with invalid parameters" do |
|
228 | test "PUT /users/:id.xml with invalid parameters" do | |
268 | assert_no_difference('User.count') do |
|
229 | assert_no_difference('User.count') do | |
269 | put '/users/2.xml', { |
|
230 | put '/users/2.xml', { | |
270 | :user => { |
|
231 | :user => { | |
271 | :login => 'jsmith', :firstname => '', :lastname => 'Lastname', |
|
232 | :login => 'jsmith', :firstname => '', :lastname => 'Lastname', | |
272 | :mail => 'foo'} |
|
233 | :mail => 'foo'} | |
273 | }, |
|
234 | }, | |
274 | credentials('admin') |
|
235 | credentials('admin') | |
275 | end |
|
236 | end | |
276 |
|
237 | |||
277 | assert_response :unprocessable_entity |
|
238 | assert_response :unprocessable_entity | |
278 | assert_equal 'application/xml', @response.content_type |
|
239 | assert_equal 'application/xml', @response.content_type | |
279 | assert_select 'errors error', :text => "First name can't be blank" |
|
240 | assert_select 'errors error', :text => "First name can't be blank" | |
280 | end |
|
241 | end | |
281 |
|
242 | |||
282 | test "PUT /users/:id.json with invalid parameters" do |
|
243 | test "PUT /users/:id.json with invalid parameters" do | |
283 | assert_no_difference('User.count') do |
|
244 | assert_no_difference('User.count') do | |
284 | put '/users/2.json', { |
|
245 | put '/users/2.json', { | |
285 | :user => { |
|
246 | :user => { | |
286 | :login => 'jsmith', :firstname => '', :lastname => 'Lastname', |
|
247 | :login => 'jsmith', :firstname => '', :lastname => 'Lastname', | |
287 | :mail => 'foo'} |
|
248 | :mail => 'foo'} | |
288 | }, |
|
249 | }, | |
289 | credentials('admin') |
|
250 | credentials('admin') | |
290 | end |
|
251 | end | |
291 |
|
252 | |||
292 | assert_response :unprocessable_entity |
|
253 | assert_response :unprocessable_entity | |
293 | assert_equal 'application/json', @response.content_type |
|
254 | assert_equal 'application/json', @response.content_type | |
294 | json = ActiveSupport::JSON.decode(response.body) |
|
255 | json = ActiveSupport::JSON.decode(response.body) | |
295 | assert_kind_of Hash, json |
|
256 | assert_kind_of Hash, json | |
296 | assert json.has_key?('errors') |
|
257 | assert json.has_key?('errors') | |
297 | assert_kind_of Array, json['errors'] |
|
258 | assert_kind_of Array, json['errors'] | |
298 | end |
|
259 | end | |
299 |
|
260 | |||
300 | test "DELETE /users/:id.xml should delete the user" do |
|
261 | test "DELETE /users/:id.xml should delete the user" do | |
301 | assert_difference('User.count', -1) do |
|
262 | assert_difference('User.count', -1) do | |
302 | delete '/users/2.xml', {}, credentials('admin') |
|
263 | delete '/users/2.xml', {}, credentials('admin') | |
303 | end |
|
264 | end | |
304 |
|
265 | |||
305 | assert_response :ok |
|
266 | assert_response :ok | |
306 | assert_equal '', @response.body |
|
267 | assert_equal '', @response.body | |
307 | end |
|
268 | end | |
308 |
|
269 | |||
309 | test "DELETE /users/:id.json should delete the user" do |
|
270 | test "DELETE /users/:id.json should delete the user" do | |
310 | assert_difference('User.count', -1) do |
|
271 | assert_difference('User.count', -1) do | |
311 | delete '/users/2.json', {}, credentials('admin') |
|
272 | delete '/users/2.json', {}, credentials('admin') | |
312 | end |
|
273 | end | |
313 |
|
274 | |||
314 | assert_response :ok |
|
275 | assert_response :ok | |
315 | assert_equal '', @response.body |
|
276 | assert_equal '', @response.body | |
316 | end |
|
277 | end | |
317 | end |
|
278 | end |
@@ -1,542 +1,293 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2014 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2014 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program; if not, write to the Free Software |
|
15 | # along with this program; if not, write to the Free Software | |
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | #require 'shoulda' |
|
18 | #require 'shoulda' | |
19 | ENV["RAILS_ENV"] = "test" |
|
19 | ENV["RAILS_ENV"] = "test" | |
20 | require File.expand_path(File.dirname(__FILE__) + "/../config/environment") |
|
20 | require File.expand_path(File.dirname(__FILE__) + "/../config/environment") | |
21 | require 'rails/test_help' |
|
21 | require 'rails/test_help' | |
22 | require Rails.root.join('test', 'mocks', 'open_id_authentication_mock.rb').to_s |
|
22 | require Rails.root.join('test', 'mocks', 'open_id_authentication_mock.rb').to_s | |
23 |
|
23 | |||
24 | require File.expand_path(File.dirname(__FILE__) + '/object_helpers') |
|
24 | require File.expand_path(File.dirname(__FILE__) + '/object_helpers') | |
25 | include ObjectHelpers |
|
25 | include ObjectHelpers | |
26 |
|
26 | |||
27 | require 'awesome_nested_set/version' |
|
27 | require 'awesome_nested_set/version' | |
28 | require 'net/ldap' |
|
28 | require 'net/ldap' | |
29 |
|
29 | |||
30 | class ActionView::TestCase |
|
30 | class ActionView::TestCase | |
31 | helper :application |
|
31 | helper :application | |
32 | include ApplicationHelper |
|
32 | include ApplicationHelper | |
33 | end |
|
33 | end | |
34 |
|
34 | |||
35 | class ActiveSupport::TestCase |
|
35 | class ActiveSupport::TestCase | |
36 | include ActionDispatch::TestProcess |
|
36 | include ActionDispatch::TestProcess | |
37 | include Shoulda::Context::Assertions |
|
37 | include Shoulda::Context::Assertions | |
38 | include Shoulda::Context::InstanceMethods |
|
38 | include Shoulda::Context::InstanceMethods | |
39 | extend Shoulda::Context::ClassMethods |
|
39 | extend Shoulda::Context::ClassMethods | |
40 |
|
40 | |||
41 | self.use_transactional_fixtures = true |
|
41 | self.use_transactional_fixtures = true | |
42 | self.use_instantiated_fixtures = false |
|
42 | self.use_instantiated_fixtures = false | |
43 |
|
43 | |||
44 | #ESCAPED_CANT = 'can't' |
|
44 | #ESCAPED_CANT = 'can't' | |
45 | #ESCAPED_UCANT = 'Can't' |
|
45 | #ESCAPED_UCANT = 'Can't' | |
46 | # Rails 4.0.2 |
|
46 | # Rails 4.0.2 | |
47 | ESCAPED_CANT = 'can't' |
|
47 | ESCAPED_CANT = 'can't' | |
48 | ESCAPED_UCANT = 'Can't' |
|
48 | ESCAPED_UCANT = 'Can't' | |
49 |
|
49 | |||
50 | def log_user(login, password) |
|
50 | def log_user(login, password) | |
51 | User.anonymous |
|
51 | User.anonymous | |
52 | get "/login" |
|
52 | get "/login" | |
53 | assert_equal nil, session[:user_id] |
|
53 | assert_equal nil, session[:user_id] | |
54 | assert_response :success |
|
54 | assert_response :success | |
55 | assert_template "account/login" |
|
55 | assert_template "account/login" | |
56 | post "/login", :username => login, :password => password |
|
56 | post "/login", :username => login, :password => password | |
57 | assert_equal login, User.find(session[:user_id]).login |
|
57 | assert_equal login, User.find(session[:user_id]).login | |
58 | end |
|
58 | end | |
59 |
|
59 | |||
60 | def uploaded_test_file(name, mime) |
|
60 | def uploaded_test_file(name, mime) | |
61 | fixture_file_upload("files/#{name}", mime, true) |
|
61 | fixture_file_upload("files/#{name}", mime, true) | |
62 | end |
|
62 | end | |
63 |
|
63 | |||
64 | def credentials(user, password=nil) |
|
64 | def credentials(user, password=nil) | |
65 | {'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)} |
|
65 | {'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)} | |
66 | end |
|
66 | end | |
67 |
|
67 | |||
68 | # Mock out a file |
|
68 | # Mock out a file | |
69 | def self.mock_file |
|
69 | def self.mock_file | |
70 | file = 'a_file.png' |
|
70 | file = 'a_file.png' | |
71 | file.stubs(:size).returns(32) |
|
71 | file.stubs(:size).returns(32) | |
72 | file.stubs(:original_filename).returns('a_file.png') |
|
72 | file.stubs(:original_filename).returns('a_file.png') | |
73 | file.stubs(:content_type).returns('image/png') |
|
73 | file.stubs(:content_type).returns('image/png') | |
74 | file.stubs(:read).returns(false) |
|
74 | file.stubs(:read).returns(false) | |
75 | file |
|
75 | file | |
76 | end |
|
76 | end | |
77 |
|
77 | |||
78 | def mock_file |
|
78 | def mock_file | |
79 | self.class.mock_file |
|
79 | self.class.mock_file | |
80 | end |
|
80 | end | |
81 |
|
81 | |||
82 | def mock_file_with_options(options={}) |
|
82 | def mock_file_with_options(options={}) | |
83 | file = '' |
|
83 | file = '' | |
84 | file.stubs(:size).returns(32) |
|
84 | file.stubs(:size).returns(32) | |
85 | original_filename = options[:original_filename] || nil |
|
85 | original_filename = options[:original_filename] || nil | |
86 | file.stubs(:original_filename).returns(original_filename) |
|
86 | file.stubs(:original_filename).returns(original_filename) | |
87 | content_type = options[:content_type] || nil |
|
87 | content_type = options[:content_type] || nil | |
88 | file.stubs(:content_type).returns(content_type) |
|
88 | file.stubs(:content_type).returns(content_type) | |
89 | file.stubs(:read).returns(false) |
|
89 | file.stubs(:read).returns(false) | |
90 | file |
|
90 | file | |
91 | end |
|
91 | end | |
92 |
|
92 | |||
93 | # Use a temporary directory for attachment related tests |
|
93 | # Use a temporary directory for attachment related tests | |
94 | def set_tmp_attachments_directory |
|
94 | def set_tmp_attachments_directory | |
95 | Dir.mkdir "#{Rails.root}/tmp/test" unless File.directory?("#{Rails.root}/tmp/test") |
|
95 | Dir.mkdir "#{Rails.root}/tmp/test" unless File.directory?("#{Rails.root}/tmp/test") | |
96 | unless File.directory?("#{Rails.root}/tmp/test/attachments") |
|
96 | unless File.directory?("#{Rails.root}/tmp/test/attachments") | |
97 | Dir.mkdir "#{Rails.root}/tmp/test/attachments" |
|
97 | Dir.mkdir "#{Rails.root}/tmp/test/attachments" | |
98 | end |
|
98 | end | |
99 | Attachment.storage_path = "#{Rails.root}/tmp/test/attachments" |
|
99 | Attachment.storage_path = "#{Rails.root}/tmp/test/attachments" | |
100 | end |
|
100 | end | |
101 |
|
101 | |||
102 | def set_fixtures_attachments_directory |
|
102 | def set_fixtures_attachments_directory | |
103 | Attachment.storage_path = "#{Rails.root}/test/fixtures/files" |
|
103 | Attachment.storage_path = "#{Rails.root}/test/fixtures/files" | |
104 | end |
|
104 | end | |
105 |
|
105 | |||
106 | def with_settings(options, &block) |
|
106 | def with_settings(options, &block) | |
107 | saved_settings = options.keys.inject({}) do |h, k| |
|
107 | saved_settings = options.keys.inject({}) do |h, k| | |
108 | h[k] = case Setting[k] |
|
108 | h[k] = case Setting[k] | |
109 | when Symbol, false, true, nil |
|
109 | when Symbol, false, true, nil | |
110 | Setting[k] |
|
110 | Setting[k] | |
111 | else |
|
111 | else | |
112 | Setting[k].dup |
|
112 | Setting[k].dup | |
113 | end |
|
113 | end | |
114 | h |
|
114 | h | |
115 | end |
|
115 | end | |
116 | options.each {|k, v| Setting[k] = v} |
|
116 | options.each {|k, v| Setting[k] = v} | |
117 | yield |
|
117 | yield | |
118 | ensure |
|
118 | ensure | |
119 | saved_settings.each {|k, v| Setting[k] = v} if saved_settings |
|
119 | saved_settings.each {|k, v| Setting[k] = v} if saved_settings | |
120 | end |
|
120 | end | |
121 |
|
121 | |||
122 | # Yields the block with user as the current user |
|
122 | # Yields the block with user as the current user | |
123 | def with_current_user(user, &block) |
|
123 | def with_current_user(user, &block) | |
124 | saved_user = User.current |
|
124 | saved_user = User.current | |
125 | User.current = user |
|
125 | User.current = user | |
126 | yield |
|
126 | yield | |
127 | ensure |
|
127 | ensure | |
128 | User.current = saved_user |
|
128 | User.current = saved_user | |
129 | end |
|
129 | end | |
130 |
|
130 | |||
131 | def with_locale(locale, &block) |
|
131 | def with_locale(locale, &block) | |
132 | saved_localed = ::I18n.locale |
|
132 | saved_localed = ::I18n.locale | |
133 | ::I18n.locale = locale |
|
133 | ::I18n.locale = locale | |
134 | yield |
|
134 | yield | |
135 | ensure |
|
135 | ensure | |
136 | ::I18n.locale = saved_localed |
|
136 | ::I18n.locale = saved_localed | |
137 | end |
|
137 | end | |
138 |
|
138 | |||
139 | def change_user_password(login, new_password) |
|
139 | def change_user_password(login, new_password) | |
140 | user = User.where(:login => login).first |
|
140 | user = User.where(:login => login).first | |
141 | user.password, user.password_confirmation = new_password, new_password |
|
141 | user.password, user.password_confirmation = new_password, new_password | |
142 | user.save! |
|
142 | user.save! | |
143 | end |
|
143 | end | |
144 |
|
144 | |||
145 | def self.ldap_configured? |
|
145 | def self.ldap_configured? | |
146 | @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389) |
|
146 | @test_ldap = Net::LDAP.new(:host => '127.0.0.1', :port => 389) | |
147 | return @test_ldap.bind |
|
147 | return @test_ldap.bind | |
148 | rescue Exception => e |
|
148 | rescue Exception => e | |
149 | # LDAP is not listening |
|
149 | # LDAP is not listening | |
150 | return nil |
|
150 | return nil | |
151 | end |
|
151 | end | |
152 |
|
152 | |||
153 | def self.convert_installed? |
|
153 | def self.convert_installed? | |
154 | Redmine::Thumbnail.convert_available? |
|
154 | Redmine::Thumbnail.convert_available? | |
155 | end |
|
155 | end | |
156 |
|
156 | |||
157 | # Returns the path to the test +vendor+ repository |
|
157 | # Returns the path to the test +vendor+ repository | |
158 | def self.repository_path(vendor) |
|
158 | def self.repository_path(vendor) | |
159 | path = Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s |
|
159 | path = Rails.root.join("tmp/test/#{vendor.downcase}_repository").to_s | |
160 | # Unlike ruby, JRuby returns Rails.root with backslashes under Windows |
|
160 | # Unlike ruby, JRuby returns Rails.root with backslashes under Windows | |
161 | path.tr("\\", "/") |
|
161 | path.tr("\\", "/") | |
162 | end |
|
162 | end | |
163 |
|
163 | |||
164 | # Returns the url of the subversion test repository |
|
164 | # Returns the url of the subversion test repository | |
165 | def self.subversion_repository_url |
|
165 | def self.subversion_repository_url | |
166 | path = repository_path('subversion') |
|
166 | path = repository_path('subversion') | |
167 | path = '/' + path unless path.starts_with?('/') |
|
167 | path = '/' + path unless path.starts_with?('/') | |
168 | "file://#{path}" |
|
168 | "file://#{path}" | |
169 | end |
|
169 | end | |
170 |
|
170 | |||
171 | # Returns true if the +vendor+ test repository is configured |
|
171 | # Returns true if the +vendor+ test repository is configured | |
172 | def self.repository_configured?(vendor) |
|
172 | def self.repository_configured?(vendor) | |
173 | File.directory?(repository_path(vendor)) |
|
173 | File.directory?(repository_path(vendor)) | |
174 | end |
|
174 | end | |
175 |
|
175 | |||
176 | def repository_path_hash(arr) |
|
176 | def repository_path_hash(arr) | |
177 | hs = {} |
|
177 | hs = {} | |
178 | hs[:path] = arr.join("/") |
|
178 | hs[:path] = arr.join("/") | |
179 | hs[:param] = arr.join("/") |
|
179 | hs[:param] = arr.join("/") | |
180 | hs |
|
180 | hs | |
181 | end |
|
181 | end | |
182 |
|
182 | |||
183 | def assert_save(object) |
|
183 | def assert_save(object) | |
184 | saved = object.save |
|
184 | saved = object.save | |
185 | message = "#{object.class} could not be saved" |
|
185 | message = "#{object.class} could not be saved" | |
186 | errors = object.errors.full_messages.map {|m| "- #{m}"} |
|
186 | errors = object.errors.full_messages.map {|m| "- #{m}"} | |
187 | message << ":\n#{errors.join("\n")}" if errors.any? |
|
187 | message << ":\n#{errors.join("\n")}" if errors.any? | |
188 | assert_equal true, saved, message |
|
188 | assert_equal true, saved, message | |
189 | end |
|
189 | end | |
190 |
|
190 | |||
191 | def assert_select_error(arg) |
|
191 | def assert_select_error(arg) | |
192 | assert_select '#errorExplanation', :text => arg |
|
192 | assert_select '#errorExplanation', :text => arg | |
193 | end |
|
193 | end | |
194 |
|
194 | |||
195 | def assert_include(expected, s, message=nil) |
|
195 | def assert_include(expected, s, message=nil) | |
196 | assert s.include?(expected), (message || "\"#{expected}\" not found in \"#{s}\"") |
|
196 | assert s.include?(expected), (message || "\"#{expected}\" not found in \"#{s}\"") | |
197 | end |
|
197 | end | |
198 |
|
198 | |||
199 | def assert_not_include(expected, s, message=nil) |
|
199 | def assert_not_include(expected, s, message=nil) | |
200 | assert !s.include?(expected), (message || "\"#{expected}\" found in \"#{s}\"") |
|
200 | assert !s.include?(expected), (message || "\"#{expected}\" found in \"#{s}\"") | |
201 | end |
|
201 | end | |
202 |
|
202 | |||
203 | def assert_select_in(text, *args, &block) |
|
203 | def assert_select_in(text, *args, &block) | |
204 | d = HTML::Document.new(CGI::unescapeHTML(String.new(text))).root |
|
204 | d = HTML::Document.new(CGI::unescapeHTML(String.new(text))).root | |
205 | assert_select(d, *args, &block) |
|
205 | assert_select(d, *args, &block) | |
206 | end |
|
206 | end | |
207 |
|
207 | |||
208 | def assert_mail_body_match(expected, mail, message=nil) |
|
208 | def assert_mail_body_match(expected, mail, message=nil) | |
209 | if expected.is_a?(String) |
|
209 | if expected.is_a?(String) | |
210 | assert_include expected, mail_body(mail), message |
|
210 | assert_include expected, mail_body(mail), message | |
211 | else |
|
211 | else | |
212 | assert_match expected, mail_body(mail), message |
|
212 | assert_match expected, mail_body(mail), message | |
213 | end |
|
213 | end | |
214 | end |
|
214 | end | |
215 |
|
215 | |||
216 | def assert_mail_body_no_match(expected, mail, message=nil) |
|
216 | def assert_mail_body_no_match(expected, mail, message=nil) | |
217 | if expected.is_a?(String) |
|
217 | if expected.is_a?(String) | |
218 | assert_not_include expected, mail_body(mail), message |
|
218 | assert_not_include expected, mail_body(mail), message | |
219 | else |
|
219 | else | |
220 | assert_no_match expected, mail_body(mail), message |
|
220 | assert_no_match expected, mail_body(mail), message | |
221 | end |
|
221 | end | |
222 | end |
|
222 | end | |
223 |
|
223 | |||
224 | def mail_body(mail) |
|
224 | def mail_body(mail) | |
225 | mail.parts.first.body.encoded |
|
225 | mail.parts.first.body.encoded | |
226 | end |
|
226 | end | |
227 |
|
227 | |||
228 | # awesome_nested_set new node lft and rgt value changed this refactor revision. |
|
228 | # awesome_nested_set new node lft and rgt value changed this refactor revision. | |
229 | # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb938e40200cd90714dc69247ef017c61 |
|
229 | # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb938e40200cd90714dc69247ef017c61 | |
230 | # The reason of behavior change is that "self.class.base_class.unscoped" was added to this line. |
|
230 | # The reason of behavior change is that "self.class.base_class.unscoped" was added to this line. | |
231 | # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb9#diff-f61b59a5e6319024e211b0ffdd0e4ef1R273 |
|
231 | # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb9#diff-f61b59a5e6319024e211b0ffdd0e4ef1R273 | |
232 | # It seems correct behavior because of this line comment. |
|
232 | # It seems correct behavior because of this line comment. | |
233 | # https://github.com/collectiveidea/awesome_nested_set/blame/199fca9bb9/lib/awesome_nested_set/model.rb#L278 |
|
233 | # https://github.com/collectiveidea/awesome_nested_set/blame/199fca9bb9/lib/awesome_nested_set/model.rb#L278 | |
234 | def new_issue_lft |
|
234 | def new_issue_lft | |
235 | # ::AwesomeNestedSet::VERSION > "2.1.6" ? Issue.maximum(:rgt) + 1 : 1 |
|
235 | # ::AwesomeNestedSet::VERSION > "2.1.6" ? Issue.maximum(:rgt) + 1 : 1 | |
236 | Issue.maximum(:rgt) + 1 |
|
236 | Issue.maximum(:rgt) + 1 | |
237 | end |
|
237 | end | |
238 | end |
|
238 | end | |
239 |
|
239 | |||
240 | module Redmine |
|
240 | module Redmine | |
241 | class RoutingTest < ActionDispatch::IntegrationTest |
|
241 | class RoutingTest < ActionDispatch::IntegrationTest | |
242 | def should_route(arg) |
|
242 | def should_route(arg) | |
243 | arg = arg.dup |
|
243 | arg = arg.dup | |
244 | request = arg.keys.detect {|key| key.is_a?(String)} |
|
244 | request = arg.keys.detect {|key| key.is_a?(String)} | |
245 | raise ArgumentError unless request |
|
245 | raise ArgumentError unless request | |
246 | options = arg.slice!(request) |
|
246 | options = arg.slice!(request) | |
247 |
|
247 | |||
248 | raise ArgumentError unless request =~ /\A(GET|POST|PUT|PATCH|DELETE)\s+(.+)\z/ |
|
248 | raise ArgumentError unless request =~ /\A(GET|POST|PUT|PATCH|DELETE)\s+(.+)\z/ | |
249 | method, path = $1.downcase.to_sym, $2 |
|
249 | method, path = $1.downcase.to_sym, $2 | |
250 |
|
250 | |||
251 | raise ArgumentError unless arg.values.first =~ /\A(.+)#(.+)\z/ |
|
251 | raise ArgumentError unless arg.values.first =~ /\A(.+)#(.+)\z/ | |
252 | controller, action = $1, $2 |
|
252 | controller, action = $1, $2 | |
253 |
|
253 | |||
254 | assert_routing( |
|
254 | assert_routing( | |
255 | {:method => method, :path => path}, |
|
255 | {:method => method, :path => path}, | |
256 | options.merge(:controller => controller, :action => action) |
|
256 | options.merge(:controller => controller, :action => action) | |
257 | ) |
|
257 | ) | |
258 | end |
|
258 | end | |
259 | end |
|
259 | end | |
260 |
|
260 | |||
261 | module ApiTest |
|
261 | module ApiTest | |
262 | API_FORMATS = %w(json xml).freeze |
|
262 | API_FORMATS = %w(json xml).freeze | |
263 |
|
263 | |||
264 | # Base class for API tests |
|
264 | # Base class for API tests | |
265 | class Base < ActionDispatch::IntegrationTest |
|
265 | class Base < ActionDispatch::IntegrationTest | |
266 | # Test that a request allows the three types of API authentication |
|
|||
267 | # |
|
|||
268 | # * HTTP Basic with username and password |
|
|||
269 | # * HTTP Basic with an api key for the username |
|
|||
270 | # * Key based with the key=X parameter |
|
|||
271 | # |
|
|||
272 | # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete) |
|
|||
273 | # @param [String] url the request url |
|
|||
274 | # @param [optional, Hash] parameters additional request parameters |
|
|||
275 | # @param [optional, Hash] options additional options |
|
|||
276 | # @option options [Symbol] :success_code Successful response code (:success) |
|
|||
277 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) |
|
|||
278 | def self.should_allow_api_authentication(http_method, url, parameters={}, options={}) |
|
|||
279 | should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters, options) |
|
|||
280 | should_allow_http_basic_auth_with_key(http_method, url, parameters, options) |
|
|||
281 | should_allow_key_based_auth(http_method, url, parameters, options) |
|
|||
282 | end |
|
|||
283 |
|
||||
284 | # Test that a request allows the username and password for HTTP BASIC |
|
|||
285 | # |
|
|||
286 | # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete) |
|
|||
287 | # @param [String] url the request url |
|
|||
288 | # @param [optional, Hash] parameters additional request parameters |
|
|||
289 | # @param [optional, Hash] options additional options |
|
|||
290 | # @option options [Symbol] :success_code Successful response code (:success) |
|
|||
291 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) |
|
|||
292 | def self.should_allow_http_basic_auth_with_username_and_password(http_method, url, parameters={}, options={}) |
|
|||
293 | success_code = options[:success_code] || :success |
|
|||
294 | failure_code = options[:failure_code] || :unauthorized |
|
|||
295 |
|
||||
296 | context "should allow http basic auth using a username and password for #{http_method} #{url}" do |
|
|||
297 | context "with a valid HTTP authentication" do |
|
|||
298 | setup do |
|
|||
299 | @user = User.generate! do |user| |
|
|||
300 | user.admin = true |
|
|||
301 | user.password = 'my_password' |
|
|||
302 | end |
|
|||
303 | send(http_method, url, parameters, credentials(@user.login, 'my_password')) |
|
|||
304 | end |
|
|||
305 |
|
||||
306 | should_respond_with success_code |
|
|||
307 | should_respond_with_content_type_based_on_url(url) |
|
|||
308 | should "login as the user" do |
|
|||
309 | assert_equal @user, User.current |
|
|||
310 | end |
|
|||
311 | end |
|
|||
312 |
|
||||
313 | context "with an invalid HTTP authentication" do |
|
|||
314 | setup do |
|
|||
315 | @user = User.generate! |
|
|||
316 | send(http_method, url, parameters, credentials(@user.login, 'wrong_password')) |
|
|||
317 | end |
|
|||
318 |
|
||||
319 | should_respond_with failure_code |
|
|||
320 | should_respond_with_content_type_based_on_url(url) |
|
|||
321 | should "not login as the user" do |
|
|||
322 | assert_equal User.anonymous, User.current |
|
|||
323 | end |
|
|||
324 | end |
|
|||
325 |
|
||||
326 | context "without credentials" do |
|
|||
327 | setup do |
|
|||
328 | send(http_method, url, parameters) |
|
|||
329 | end |
|
|||
330 |
|
||||
331 | should_respond_with failure_code |
|
|||
332 | should_respond_with_content_type_based_on_url(url) |
|
|||
333 | should "include_www_authenticate_header" do |
|
|||
334 | assert @controller.response.headers.has_key?('WWW-Authenticate') |
|
|||
335 | end |
|
|||
336 | end |
|
|||
337 | end |
|
|||
338 | end |
|
|||
339 |
|
||||
340 | # Test that a request allows the API key with HTTP BASIC |
|
|||
341 | # |
|
|||
342 | # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete) |
|
|||
343 | # @param [String] url the request url |
|
|||
344 | # @param [optional, Hash] parameters additional request parameters |
|
|||
345 | # @param [optional, Hash] options additional options |
|
|||
346 | # @option options [Symbol] :success_code Successful response code (:success) |
|
|||
347 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) |
|
|||
348 | def self.should_allow_http_basic_auth_with_key(http_method, url, parameters={}, options={}) |
|
|||
349 | success_code = options[:success_code] || :success |
|
|||
350 | failure_code = options[:failure_code] || :unauthorized |
|
|||
351 |
|
||||
352 | context "should allow http basic auth with a key for #{http_method} #{url}" do |
|
|||
353 | context "with a valid HTTP authentication using the API token" do |
|
|||
354 | setup do |
|
|||
355 | @user = User.generate! do |user| |
|
|||
356 | user.admin = true |
|
|||
357 | end |
|
|||
358 | @token = Token.create!(:user => @user, :action => 'api') |
|
|||
359 | send(http_method, url, parameters, credentials(@token.value, 'X')) |
|
|||
360 | end |
|
|||
361 | should_respond_with success_code |
|
|||
362 | should_respond_with_content_type_based_on_url(url) |
|
|||
363 | should_be_a_valid_response_string_based_on_url(url) |
|
|||
364 | should "login as the user" do |
|
|||
365 | assert_equal @user, User.current |
|
|||
366 | end |
|
|||
367 | end |
|
|||
368 |
|
||||
369 | context "with an invalid HTTP authentication" do |
|
|||
370 | setup do |
|
|||
371 | @user = User.generate! |
|
|||
372 | @token = Token.create!(:user => @user, :action => 'feeds') |
|
|||
373 | send(http_method, url, parameters, credentials(@token.value, 'X')) |
|
|||
374 | end |
|
|||
375 | should_respond_with failure_code |
|
|||
376 | should_respond_with_content_type_based_on_url(url) |
|
|||
377 | should "not login as the user" do |
|
|||
378 | assert_equal User.anonymous, User.current |
|
|||
379 | end |
|
|||
380 | end |
|
|||
381 | end |
|
|||
382 | end |
|
|||
383 |
|
||||
384 | # Test that a request allows full key authentication |
|
|||
385 | # |
|
|||
386 | # @param [Symbol] http_method the HTTP method for request (:get, :post, :put, :delete) |
|
|||
387 | # @param [String] url the request url, without the key=ZXY parameter |
|
|||
388 | # @param [optional, Hash] parameters additional request parameters |
|
|||
389 | # @param [optional, Hash] options additional options |
|
|||
390 | # @option options [Symbol] :success_code Successful response code (:success) |
|
|||
391 | # @option options [Symbol] :failure_code Failure response code (:unauthorized) |
|
|||
392 | def self.should_allow_key_based_auth(http_method, url, parameters={}, options={}) |
|
|||
393 | success_code = options[:success_code] || :success |
|
|||
394 | failure_code = options[:failure_code] || :unauthorized |
|
|||
395 |
|
||||
396 | context "should allow key based auth using key=X for #{http_method} #{url}" do |
|
|||
397 | context "with a valid api token" do |
|
|||
398 | setup do |
|
|||
399 | @user = User.generate! do |user| |
|
|||
400 | user.admin = true |
|
|||
401 | end |
|
|||
402 | @token = Token.create!(:user => @user, :action => 'api') |
|
|||
403 | # Simple url parse to add on ?key= or &key= |
|
|||
404 | request_url = if url.match(/\?/) |
|
|||
405 | url + "&key=#{@token.value}" |
|
|||
406 | else |
|
|||
407 | url + "?key=#{@token.value}" |
|
|||
408 | end |
|
|||
409 | send(http_method, request_url, parameters) |
|
|||
410 | end |
|
|||
411 | should_respond_with success_code |
|
|||
412 | should_respond_with_content_type_based_on_url(url) |
|
|||
413 | should_be_a_valid_response_string_based_on_url(url) |
|
|||
414 | should "login as the user" do |
|
|||
415 | assert_equal @user, User.current |
|
|||
416 | end |
|
|||
417 | end |
|
|||
418 |
|
||||
419 | context "with an invalid api token" do |
|
|||
420 | setup do |
|
|||
421 | @user = User.generate! do |user| |
|
|||
422 | user.admin = true |
|
|||
423 | end |
|
|||
424 | @token = Token.create!(:user => @user, :action => 'feeds') |
|
|||
425 | # Simple url parse to add on ?key= or &key= |
|
|||
426 | request_url = if url.match(/\?/) |
|
|||
427 | url + "&key=#{@token.value}" |
|
|||
428 | else |
|
|||
429 | url + "?key=#{@token.value}" |
|
|||
430 | end |
|
|||
431 | send(http_method, request_url, parameters) |
|
|||
432 | end |
|
|||
433 | should_respond_with failure_code |
|
|||
434 | should_respond_with_content_type_based_on_url(url) |
|
|||
435 | should "not login as the user" do |
|
|||
436 | assert_equal User.anonymous, User.current |
|
|||
437 | end |
|
|||
438 | end |
|
|||
439 | end |
|
|||
440 |
|
||||
441 | context "should allow key based auth using X-Redmine-API-Key header for #{http_method} #{url}" do |
|
|||
442 | setup do |
|
|||
443 | @user = User.generate! do |user| |
|
|||
444 | user.admin = true |
|
|||
445 | end |
|
|||
446 | @token = Token.create!(:user => @user, :action => 'api') |
|
|||
447 | send(http_method, url, parameters, {'X-Redmine-API-Key' => @token.value.to_s}) |
|
|||
448 | end |
|
|||
449 | should_respond_with success_code |
|
|||
450 | should_respond_with_content_type_based_on_url(url) |
|
|||
451 | should_be_a_valid_response_string_based_on_url(url) |
|
|||
452 | should "login as the user" do |
|
|||
453 | assert_equal @user, User.current |
|
|||
454 | end |
|
|||
455 | end |
|
|||
456 | end |
|
|||
457 |
|
||||
458 | # Uses should_respond_with_content_type based on what's in the url: |
|
|||
459 | # |
|
|||
460 | # '/project/issues.xml' => should_respond_with_content_type :xml |
|
|||
461 | # '/project/issues.json' => should_respond_with_content_type :json |
|
|||
462 | # |
|
|||
463 | # @param [String] url Request |
|
|||
464 | def self.should_respond_with_content_type_based_on_url(url) |
|
|||
465 | case |
|
|||
466 | when url.match(/xml/i) |
|
|||
467 | should "respond with XML" do |
|
|||
468 | assert_equal 'application/xml', @response.content_type |
|
|||
469 | end |
|
|||
470 | when url.match(/json/i) |
|
|||
471 | should "respond with JSON" do |
|
|||
472 | assert_equal 'application/json', @response.content_type |
|
|||
473 | end |
|
|||
474 | else |
|
|||
475 | raise "Unknown content type for should_respond_with_content_type_based_on_url: #{url}" |
|
|||
476 | end |
|
|||
477 | end |
|
|||
478 |
|
||||
479 | # Uses the url to assert which format the response should be in |
|
|||
480 | # |
|
|||
481 | # '/project/issues.xml' => should_be_a_valid_xml_string |
|
|||
482 | # '/project/issues.json' => should_be_a_valid_json_string |
|
|||
483 | # |
|
|||
484 | # @param [String] url Request |
|
|||
485 | def self.should_be_a_valid_response_string_based_on_url(url) |
|
|||
486 | case |
|
|||
487 | when url.match(/xml/i) |
|
|||
488 | should_be_a_valid_xml_string |
|
|||
489 | when url.match(/json/i) |
|
|||
490 | should_be_a_valid_json_string |
|
|||
491 | else |
|
|||
492 | raise "Unknown content type for should_be_a_valid_response_based_on_url: #{url}" |
|
|||
493 | end |
|
|||
494 | end |
|
|||
495 |
|
||||
496 | # Checks that the response is a valid JSON string |
|
|||
497 | def self.should_be_a_valid_json_string |
|
|||
498 | should "be a valid JSON string (or empty)" do |
|
|||
499 | assert(response.body.blank? || ActiveSupport::JSON.decode(response.body)) |
|
|||
500 | end |
|
|||
501 | end |
|
|||
502 |
|
||||
503 | # Checks that the response is a valid XML string |
|
|||
504 | def self.should_be_a_valid_xml_string |
|
|||
505 | should "be a valid XML string" do |
|
|||
506 | assert REXML::Document.new(response.body) |
|
|||
507 | end |
|
|||
508 | end |
|
|||
509 |
|
||||
510 | def self.should_respond_with(status) |
|
|||
511 | should "respond with #{status}" do |
|
|||
512 | assert_response status |
|
|||
513 | end |
|
|||
514 | end |
|
|||
515 | end |
|
266 | end | |
516 |
|
267 | |||
517 | class Routing < Redmine::RoutingTest |
|
268 | class Routing < Redmine::RoutingTest | |
518 | def should_route(arg) |
|
269 | def should_route(arg) | |
519 | arg = arg.dup |
|
270 | arg = arg.dup | |
520 | request = arg.keys.detect {|key| key.is_a?(String)} |
|
271 | request = arg.keys.detect {|key| key.is_a?(String)} | |
521 | raise ArgumentError unless request |
|
272 | raise ArgumentError unless request | |
522 | options = arg.slice!(request) |
|
273 | options = arg.slice!(request) | |
523 |
|
274 | |||
524 | API_FORMATS.each do |format| |
|
275 | API_FORMATS.each do |format| | |
525 | format_request = request.sub /$/, ".#{format}" |
|
276 | format_request = request.sub /$/, ".#{format}" | |
526 | super options.merge(format_request => arg[request], :format => format) |
|
277 | super options.merge(format_request => arg[request], :format => format) | |
527 | end |
|
278 | end | |
528 | end |
|
279 | end | |
529 | end |
|
280 | end | |
530 | end |
|
281 | end | |
531 | end |
|
282 | end | |
532 |
|
283 | |||
533 | # URL helpers do not work with config.threadsafe! |
|
284 | # URL helpers do not work with config.threadsafe! | |
534 | # https://github.com/rspec/rspec-rails/issues/476#issuecomment-4705454 |
|
285 | # https://github.com/rspec/rspec-rails/issues/476#issuecomment-4705454 | |
535 | ActionView::TestCase::TestController.instance_eval do |
|
286 | ActionView::TestCase::TestController.instance_eval do | |
536 | helper Rails.application.routes.url_helpers |
|
287 | helper Rails.application.routes.url_helpers | |
537 | end |
|
288 | end | |
538 | ActionView::TestCase::TestController.class_eval do |
|
289 | ActionView::TestCase::TestController.class_eval do | |
539 | def _routes |
|
290 | def _routes | |
540 | Rails.application.routes |
|
291 | Rails.application.routes | |
541 | end |
|
292 | end | |
542 | end |
|
293 | end |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now