user_agent_spec.rb 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. # Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. require 'rack/handler/puma'
  4. # this cop is disabled to speed up testing by avoiding the overhead of multiple requests
  5. RSpec.describe UserAgent, :aggregate_failures, integration: true do
  6. include ZammadSpecSupportRequest
  7. def host_with_port
  8. host = 'http://localhost:3000'
  9. if ENV['CI'].present?
  10. host = 'http://build:3000'
  11. end
  12. host
  13. end
  14. puma_thread = nil
  15. # we need a running web server, otherwise the requests will fail
  16. before :all do # rubocop:disable RSpec/BeforeAfterAll
  17. ENV['CI_BASIC_AUTH_USER'] = 'basic_auth_user'
  18. ENV['CI_BASIC_AUTH_PASSWORD'] = 'test123'
  19. puma_thread = Thread.new do
  20. app = Rack::Builder.new do
  21. map '/' do
  22. run Rails.application
  23. end
  24. end.to_app
  25. Rack::Handler::Puma.run app, Port: 3000
  26. end
  27. sleep 0.25
  28. # wait for server to start
  29. server_started = false
  30. 10.times do
  31. next if server_started
  32. server_started = system("curl -sSf #{host_with_port} > /dev/null")
  33. sleep 0.2 if !server_started
  34. end
  35. end
  36. after :all do # rubocop:disable RSpec/BeforeAfterAll
  37. puma_thread.kill
  38. end
  39. shared_context 'when doing user agent tests' do
  40. let(:host) { host_with_port }
  41. shared_examples 'successful request' do
  42. it 'returns a response' do
  43. expect(response).to be_success
  44. expect(response.code).to eq(code)
  45. end
  46. end
  47. shared_examples 'successful request with json body' do
  48. it 'returns a response' do
  49. expect(response).to be_success
  50. expect(response.code).to eq(code)
  51. expect(json_response).to include(expected_body)
  52. end
  53. end
  54. shared_examples 'successful get request' do
  55. it 'returns a response' do
  56. expect(response).to be_success
  57. expect(response.code).to eq(code)
  58. expect(response.header).to include('content-type' => content_type)
  59. expect(json_response).to include(expected_body)
  60. end
  61. end
  62. shared_examples 'successful post/put request' do
  63. include_examples 'successful request with json body'
  64. end
  65. shared_examples 'successful delete request' do
  66. include_examples 'successful request with json body'
  67. end
  68. shared_examples 'successful redirect request' do
  69. include_examples 'successful request with json body'
  70. end
  71. shared_examples 'unsuccessful request' do
  72. it 'returns a response' do
  73. expect(response).not_to be_success
  74. expect(response.code).to eq(code)
  75. end
  76. end
  77. shared_examples 'unsuccessful request without body' do
  78. it 'returns a response' do
  79. expect(response).not_to be_success
  80. expect(response.code).to eq(code)
  81. expect(response.body).to be_nil
  82. end
  83. end
  84. shared_examples 'unsuccessful get/post/put/delete request' do
  85. it 'returns a response' do
  86. expect(response).not_to be_success
  87. expect(response.code).to eq(code)
  88. expect(response.body).to eq(expected_body)
  89. end
  90. end
  91. shared_examples 'ftp requests' do
  92. it 'returns a response' do
  93. expect(response).to be_success
  94. expect(response.code).to eq(code)
  95. expect(response.body).to match(expected_body)
  96. end
  97. end
  98. describe '#get' do
  99. context 'without http basic auth' do
  100. subject(:response) { described_class.get(request_url) }
  101. context 'with code 200' do
  102. let(:code) { '200' }
  103. let(:content_type) { 'application/json; charset=utf-8' }
  104. let(:request_url) { "#{host}/test/get/1?submitted=123" }
  105. let(:expected_body) do
  106. {
  107. 'method' => 'get',
  108. 'submitted' => '123',
  109. 'content_type_requested' => nil,
  110. }
  111. end
  112. include_examples 'successful get request'
  113. end
  114. context 'with code 202' do
  115. let(:code) { '202' }
  116. let(:content_type) { 'application/json; charset=utf-8' }
  117. let(:request_url) { "#{host}/test/get_accepted/1?submitted=123" }
  118. let(:expected_body) do
  119. {
  120. 'method' => 'get',
  121. 'submitted' => '123',
  122. 'content_type_requested' => nil,
  123. }
  124. end
  125. include_examples 'successful get request'
  126. end
  127. context 'with code 404' do
  128. let(:code) { '404' }
  129. let(:request_url) { "#{host}/test/not_existing" }
  130. include_examples 'unsuccessful request'
  131. end
  132. end
  133. context 'with http basic auth' do
  134. subject(:response) do
  135. described_class.get(request_url, {}, {
  136. user: 'basic_auth_user',
  137. password: password,
  138. })
  139. end
  140. context 'with code 200' do
  141. let(:code) { '200' }
  142. let(:content_type) { 'application/json; charset=utf-8' }
  143. let(:request_url) { "#{host}/test_basic_auth/get/1?submitted=123" }
  144. let(:password) { 'test123' }
  145. let(:expected_body) do
  146. {
  147. 'method' => 'get',
  148. 'submitted' => '123',
  149. 'content_type_requested' => nil,
  150. }
  151. end
  152. include_examples 'successful get request'
  153. end
  154. context 'with code 401' do
  155. let(:code) { '401' }
  156. let(:request_url) { "#{host}/test_basic_auth/get/1?submitted=123" }
  157. let(:password) { 'test<>123' }
  158. let(:expected_body) { "HTTP Basic: Access denied.\n" }
  159. include_examples 'unsuccessful get/post/put/delete request'
  160. end
  161. end
  162. context 'when timeouts are raised' do
  163. subject(:response) do
  164. described_class.get(request_url, {}, {
  165. open_timeout: 0,
  166. read_timeout: 0,
  167. })
  168. end
  169. let(:request_url) { "#{host}/test/get/1?submitted=123" }
  170. let(:code) { 0 }
  171. include_examples 'unsuccessful request without body'
  172. end
  173. context 'with content type set to json' do
  174. subject(:response) { described_class.get(request_url, request_params, request_options) }
  175. context 'with code 200' do
  176. let(:code) { '200' }
  177. let(:content_type) { 'application/json; charset=utf-8' }
  178. let(:request_url) { "#{host}/test/get/1" }
  179. let(:request_params) { { submitted: 'some value' } }
  180. let(:request_options) { { json: true } }
  181. let(:expected_body) do
  182. {
  183. 'method' => 'get',
  184. 'content_type_requested' => nil,
  185. 'submitted' => 'some value',
  186. }
  187. end
  188. include_examples 'successful get request'
  189. end
  190. context 'with code 404' do
  191. let(:code) { '404' }
  192. let(:request_url) { "#{host}/test/not_existing" }
  193. let(:request_params) { { submitted: { key: 'some value' } } }
  194. let(:request_options) { { json: true } }
  195. include_examples 'unsuccessful request'
  196. end
  197. end
  198. end
  199. describe '#post' do
  200. context 'without http basic auth' do
  201. subject(:response) { described_class.post(request_url, request_params) }
  202. context 'with code 201' do
  203. let(:code) { '201' }
  204. let(:request_url) { "#{host}/test/post/1" }
  205. let(:request_params) { { submitted: 'some value' } }
  206. let(:expected_body) do
  207. {
  208. 'method' => 'post',
  209. 'submitted' => 'some value',
  210. 'content_type_requested' => 'application/x-www-form-urlencoded',
  211. }
  212. end
  213. include_examples 'successful post/put request'
  214. end
  215. context 'with code 404' do
  216. let(:code) { '404' }
  217. let(:request_url) { "#{host}/test/not_existing" }
  218. let(:request_params) { { submitted: 'some value' } }
  219. include_examples 'unsuccessful request without body'
  220. end
  221. end
  222. context 'with http basic auth' do
  223. subject(:response) do
  224. described_class.post(request_url, request_params, {
  225. user: 'basic_auth_user',
  226. password: password,
  227. })
  228. end
  229. context 'with code 201' do
  230. let(:code) { '201' }
  231. let(:request_url) { "#{host}/test_basic_auth/post/1" }
  232. let(:request_params) { { submitted: 'some value' } }
  233. let(:password) { 'test123' }
  234. let(:expected_body) do
  235. {
  236. 'method' => 'post',
  237. 'submitted' => 'some value',
  238. 'content_type_requested' => 'application/x-www-form-urlencoded',
  239. }
  240. end
  241. include_examples 'successful post/put request'
  242. end
  243. context 'with code 401' do
  244. let(:code) { '401' }
  245. let(:request_url) { "#{host}/test_basic_auth/post/1" }
  246. let(:request_params) { { submitted: 'some value' } }
  247. let(:password) { 'test<>123' }
  248. let(:expected_body) { "HTTP Basic: Access denied.\n" }
  249. include_examples 'unsuccessful get/post/put/delete request'
  250. end
  251. end
  252. context 'when timeouts are raised' do
  253. subject(:response) do
  254. described_class.post(request_url, request_params, {
  255. open_timeout: 0,
  256. read_timeout: 0,
  257. })
  258. end
  259. let(:request_url) { "#{host}/test/post/1" }
  260. let(:request_params) { { submitted: 'timeout' } }
  261. let(:code) { 0 }
  262. include_examples 'unsuccessful request without body'
  263. end
  264. context 'with content type set to json' do
  265. subject(:response) { described_class.post(request_url, request_params, request_options) }
  266. context 'with code 201' do
  267. let(:code) { '201' }
  268. let(:content_type) { 'application/json; charset=utf-8' }
  269. let(:request_url) { "#{host}/test/post/1" }
  270. let(:request_params) { { submitted: { key: 'some value' } } }
  271. let(:request_options) { { json: true } }
  272. let(:expected_body) do
  273. {
  274. 'method' => 'post',
  275. 'content_type_requested' => 'application/json',
  276. 'submitted' => {
  277. 'key' => 'some value',
  278. },
  279. }
  280. end
  281. include_examples 'successful post/put request'
  282. end
  283. end
  284. end
  285. describe '#put' do
  286. subject(:response) { described_class.put(request_url, request_params) }
  287. context 'without http basic auth' do
  288. context 'with code 200' do
  289. let(:code) { '200' }
  290. let(:request_url) { "#{host}/test/put/1" }
  291. let(:request_params) { { submitted: 'some value' } }
  292. let(:expected_body) do
  293. {
  294. 'method' => 'put',
  295. 'submitted' => 'some value',
  296. 'content_type_requested' => 'application/x-www-form-urlencoded',
  297. }
  298. end
  299. include_examples 'successful post/put request'
  300. end
  301. context 'with code 404' do
  302. let(:code) { '404' }
  303. let(:request_url) { "#{host}/test/not_existing" }
  304. let(:request_params) { { submitted: 'some value' } }
  305. include_examples 'unsuccessful request without body'
  306. end
  307. end
  308. context 'with http basic auth' do
  309. subject(:response) do
  310. described_class.put(request_url, request_params, {
  311. user: 'basic_auth_user',
  312. password: password,
  313. })
  314. end
  315. let(:password) { 'test123' }
  316. let(:submit_value) { 'some value' }
  317. context 'with code 200' do
  318. let(:code) { '200' }
  319. let(:request_url) { "#{host}/test_basic_auth/put/1" }
  320. let(:request_params) { { submitted: 'some value' } }
  321. let(:expected_body) do
  322. {
  323. 'method' => 'put',
  324. 'submitted' => 'some value',
  325. 'content_type_requested' => 'application/x-www-form-urlencoded',
  326. }
  327. end
  328. include_examples 'successful post/put request'
  329. end
  330. context 'with code 401' do
  331. let(:code) { '401' }
  332. let(:request_url) { "#{host}/test_basic_auth/put/1" }
  333. let(:request_params) { { submitted: 'some value' } }
  334. let(:password) { 'test<>123' }
  335. let(:expected_body) { "HTTP Basic: Access denied.\n" }
  336. include_examples 'unsuccessful get/post/put/delete request'
  337. end
  338. end
  339. end
  340. describe '#delete' do
  341. context 'without http basic auth' do
  342. subject(:response) { described_class.delete(request_url) }
  343. context 'with code 200' do
  344. let(:code) { '200' }
  345. let(:request_url) { "#{host}/test/delete/1" }
  346. let(:expected_body) do
  347. {
  348. 'method' => 'delete',
  349. 'content_type_requested' => nil,
  350. }
  351. end
  352. include_examples 'successful delete request'
  353. end
  354. context 'with code 404' do
  355. let(:code) { '404' }
  356. let(:request_url) { "#{host}/test/not_existing" }
  357. include_examples 'unsuccessful request without body'
  358. end
  359. end
  360. context 'with http basic auth' do
  361. subject(:response) do
  362. described_class.delete(request_url, {}, {
  363. user: 'basic_auth_user',
  364. password: password,
  365. })
  366. end
  367. context 'with code 200' do
  368. let(:code) { '200' }
  369. let(:content_type) { 'application/json; charset=utf-8' }
  370. let(:request_url) { "#{host}/test_basic_auth/delete/1" }
  371. let(:password) { 'test123' }
  372. let(:expected_body) do
  373. {
  374. 'method' => 'delete',
  375. 'content_type_requested' => nil,
  376. }
  377. end
  378. include_examples 'successful delete request'
  379. end
  380. context 'with code 401' do
  381. let(:code) { '401' }
  382. let(:request_url) { "#{host}/test_basic_auth/delete/1" }
  383. let(:password) { 'test<>123' }
  384. let(:expected_body) { "HTTP Basic: Access denied.\n" }
  385. include_examples 'unsuccessful get/post/put/delete request'
  386. end
  387. end
  388. end
  389. describe '#request' do
  390. context 'without http basic auth' do
  391. subject(:response) { described_class.request(request_url) }
  392. context 'with code 200' do
  393. let(:code) { '200' }
  394. let(:content_type) { 'application/json; charset=utf-8' }
  395. let(:request_url) { "#{host}/test/redirect" }
  396. let(:expected_body) do
  397. {
  398. 'method' => 'get',
  399. 'submitted' => 'abc',
  400. 'content_type_requested' => nil,
  401. }
  402. end
  403. include_examples 'successful redirect request'
  404. end
  405. end
  406. context 'with http basic auth' do
  407. subject(:response) do
  408. described_class.request(request_url, {
  409. user: 'basic_auth_user',
  410. password: password,
  411. })
  412. end
  413. context 'with code 200' do
  414. let(:code) { '200' }
  415. let(:request_url) { "#{host}/test_basic_auth/redirect" }
  416. let(:password) { 'test123' }
  417. let(:expected_body) do
  418. {
  419. 'method' => 'get',
  420. 'submitted' => 'abc',
  421. 'content_type_requested' => nil,
  422. }
  423. end
  424. include_examples 'successful redirect request'
  425. end
  426. context 'with code 401' do
  427. let(:code) { '401' }
  428. let(:request_url) { "#{host}/test_basic_auth/redirect" }
  429. let(:password) { 'test<>123' }
  430. let(:expected_body) { "HTTP Basic: Access denied.\n" }
  431. include_examples 'unsuccessful get/post/put/delete request'
  432. end
  433. end
  434. context 'when ftp', integration: true, required_envs: ['FTP_URL'] do
  435. subject(:response) do
  436. described_class.request(request_url)
  437. end
  438. context 'with code 200' do
  439. let(:code) { '200' }
  440. let(:request_url) { "#{ENV['FTP_URL']}/zammad.txt" }
  441. let(:expected_body) { %r{zammad}i }
  442. include_examples 'ftp requests'
  443. end
  444. context 'with code 550' do
  445. let(:code) { '550' }
  446. let(:request_url) { "#{ENV['FTP_URL']}/nonexisting.txt" }
  447. include_examples 'unsuccessful request without body'
  448. end
  449. context 'with a not existing URL' do
  450. let(:code) { 0 }
  451. let(:request_url) { 'http://not.existing.host.tld/test.php' }
  452. include_examples 'unsuccessful request without body'
  453. end
  454. end
  455. end
  456. end
  457. describe 'testing without proxy' do
  458. include_context 'when doing user agent tests'
  459. end
  460. describe 'testing with proxy', required_envs: %w[CI_PROXY_URL CI_PROXY_USER CI_PROXY_PASSWORD] do
  461. before do
  462. Setting.set('proxy', ENV['CI_PROXY_URL'])
  463. Setting.set('proxy_username', ENV['CI_PROXY_USER'])
  464. Setting.set('proxy_password', ENV['CI_PROXY_PASSWORD'])
  465. end
  466. include_context 'when doing user agent tests'
  467. end
  468. end