user_agent_spec.rb 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. # Copyright (C) 2012-2025 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. ENV['CI_BEARER_TOKEN'] = 'test_bearer_123'
  20. puma_thread = Thread.new do
  21. app = Rack::Builder.new do
  22. map '/' do
  23. run Rails.application
  24. end
  25. end.to_app
  26. Rack::Handler::Puma.run app, Port: 3000
  27. end
  28. sleep 0.25
  29. # wait for server to start
  30. server_started = false
  31. 10.times do
  32. next if server_started
  33. server_started = system("curl -sSf #{host_with_port} > /dev/null")
  34. sleep 0.2 if !server_started
  35. end
  36. end
  37. after :all do # rubocop:disable RSpec/BeforeAfterAll
  38. puma_thread.kill
  39. end
  40. shared_context 'when doing user agent tests' do
  41. let(:host) { host_with_port }
  42. shared_examples 'successful request' do
  43. it 'returns a response' do
  44. expect(response).to be_success
  45. expect(response.code).to eq(code)
  46. end
  47. end
  48. shared_examples 'successful request with json body' do
  49. it 'returns a response' do
  50. expect(response).to be_success
  51. expect(response.code).to eq(code)
  52. expect(json_response).to include(expected_body)
  53. end
  54. end
  55. shared_examples 'successful get request' do
  56. it 'returns a response' do
  57. expect(response).to be_success
  58. expect(response.code).to eq(code)
  59. expect(response.header).to include('content-type' => content_type)
  60. expect(json_response).to include(expected_body)
  61. end
  62. end
  63. shared_examples 'successful post/put/patch request' do
  64. include_examples 'successful request with json body'
  65. end
  66. shared_examples 'successful delete request' do
  67. include_examples 'successful request with json body'
  68. end
  69. shared_examples 'successful redirect request' do
  70. include_examples 'successful request with json body'
  71. end
  72. shared_examples 'unsuccessful request with body' do
  73. it 'returns a response' do
  74. expect(response).not_to be_success
  75. expect(response.code).to eq(code)
  76. expect(response.body).to be_present
  77. end
  78. end
  79. shared_examples 'unsuccessful request without body' do
  80. it 'returns a response' do
  81. expect(response).not_to be_success
  82. expect(response.code).to eq(code)
  83. expect(response.body).to be_nil
  84. end
  85. end
  86. shared_examples 'unsuccessful get/post/put/delete request' do
  87. it 'returns a response' do
  88. expect(response).not_to be_success
  89. expect(response.code).to eq(code)
  90. expect(response.body).to eq(expected_body)
  91. end
  92. end
  93. shared_examples 'ftp requests' do
  94. it 'returns a response' do
  95. expect(response).to be_success
  96. expect(response.code).to eq(code)
  97. expect(response.body).to match(expected_body)
  98. end
  99. end
  100. describe '#get' do
  101. context 'without http basic auth' do
  102. subject(:response) { described_class.get(request_url) }
  103. context 'with code 200' do
  104. let(:code) { '200' }
  105. let(:content_type) { 'application/json; charset=utf-8' }
  106. let(:request_url) { "#{host}/test/get/1?submitted=123" }
  107. let(:expected_body) do
  108. {
  109. 'method' => 'get',
  110. 'submitted' => '123',
  111. 'content_type_requested' => nil,
  112. }
  113. end
  114. include_examples 'successful get request'
  115. end
  116. context 'with code 202' do
  117. let(:code) { '202' }
  118. let(:content_type) { 'application/json; charset=utf-8' }
  119. let(:request_url) { "#{host}/test/get_accepted/1?submitted=123" }
  120. let(:expected_body) do
  121. {
  122. 'method' => 'get',
  123. 'submitted' => '123',
  124. 'content_type_requested' => nil,
  125. }
  126. end
  127. include_examples 'successful get request'
  128. end
  129. context 'with code 404' do
  130. let(:code) { '404' }
  131. let(:request_url) { "#{host}/test/not_existing" }
  132. include_examples 'unsuccessful request with body'
  133. end
  134. end
  135. context 'with http basic auth' do
  136. subject(:response) do
  137. described_class.get(request_url, {}, {
  138. user: 'basic_auth_user',
  139. password: password,
  140. })
  141. end
  142. context 'with code 200' do
  143. let(:code) { '200' }
  144. let(:content_type) { 'application/json; charset=utf-8' }
  145. let(:request_url) { "#{host}/test_basic_auth/get/1?submitted=123" }
  146. let(:password) { 'test123' }
  147. let(:expected_body) do
  148. {
  149. 'method' => 'get',
  150. 'submitted' => '123',
  151. 'content_type_requested' => nil,
  152. }
  153. end
  154. include_examples 'successful get request'
  155. end
  156. context 'with code 401' do
  157. let(:code) { '401' }
  158. let(:request_url) { "#{host}/test_basic_auth/get/1?submitted=123" }
  159. let(:password) { 'test<>123' }
  160. let(:expected_body) { "HTTP Basic: Access denied.\n" }
  161. include_examples 'unsuccessful get/post/put/delete request'
  162. end
  163. end
  164. context 'with bearer token auth' do
  165. subject(:response) do
  166. described_class.get(request_url, {}, {
  167. bearer_token: bearer_token,
  168. })
  169. end
  170. context 'with code 200' do
  171. let(:code) { '200' }
  172. let(:content_type) { 'application/json; charset=utf-8' }
  173. let(:request_url) { "#{host}/test_bearer_auth/get/1?submitted=123" }
  174. let(:bearer_token) { 'test_bearer_123' }
  175. let(:expected_body) do
  176. {
  177. 'method' => 'get',
  178. 'submitted' => '123',
  179. 'content_type_requested' => nil,
  180. }
  181. end
  182. include_examples 'successful get request'
  183. end
  184. context 'with code 401' do
  185. let(:code) { '401' }
  186. let(:request_url) { "#{host}/test_bearer_auth/get/1?submitted=123" }
  187. let(:bearer_token) { 'wrong_test_bearer' }
  188. let(:expected_body) { "HTTP Token: Access denied.\n" }
  189. include_examples 'unsuccessful get/post/put/delete request'
  190. end
  191. end
  192. context 'when timeouts are raised' do
  193. subject(:response) do
  194. described_class.get(request_url, {}, {
  195. open_timeout: 0,
  196. read_timeout: 0,
  197. })
  198. end
  199. let(:request_url) { "#{host}/test/get/1?submitted=123" }
  200. let(:code) { 0 }
  201. include_examples 'unsuccessful request without body'
  202. end
  203. context 'with content type set to json' do
  204. subject(:response) { described_class.get(request_url, request_params, request_options) }
  205. context 'with code 200' do
  206. let(:code) { '200' }
  207. let(:content_type) { 'application/json; charset=utf-8' }
  208. let(:request_url) { "#{host}/test/get/1" }
  209. let(:request_params) { { submitted: 'some value' } }
  210. let(:request_options) { { json: true } }
  211. let(:expected_body) do
  212. {
  213. 'method' => 'get',
  214. 'content_type_requested' => nil,
  215. 'submitted' => 'some value',
  216. }
  217. end
  218. include_examples 'successful get request'
  219. end
  220. context 'with code 404' do
  221. let(:code) { '404' }
  222. let(:request_url) { "#{host}/test/not_existing" }
  223. let(:request_params) { { submitted: { key: 'some value' } } }
  224. let(:request_options) { { json: true } }
  225. include_examples 'unsuccessful request with body'
  226. end
  227. end
  228. end
  229. describe '#post' do
  230. context 'without http basic auth' do
  231. subject(:response) { described_class.post(request_url, request_params, request_options) }
  232. let(:request_options) { {} }
  233. context 'with code 201' do
  234. let(:code) { '201' }
  235. let(:request_url) { "#{host}/test/post/1" }
  236. let(:request_params) { { submitted: 'some value' } }
  237. let(:expected_body) do
  238. {
  239. 'method' => 'post',
  240. 'submitted' => 'some value',
  241. 'body' => ['submitted=some+value'],
  242. 'content_type_requested' => 'application/x-www-form-urlencoded',
  243. }
  244. end
  245. include_examples 'successful post/put/patch request'
  246. end
  247. context 'with raw body' do
  248. let(:code) { '201' }
  249. let(:request_url) { "#{host}/test/post/1" }
  250. let(:request_params) { {} }
  251. let(:request_options) { { send_as_raw_body: 'raw body' } }
  252. let(:expected_body) do
  253. {
  254. 'method' => 'post',
  255. 'submitted' => nil,
  256. 'body' => ['raw body'],
  257. 'content_type_requested' => 'application/x-www-form-urlencoded',
  258. }
  259. end
  260. include_examples 'successful post/put/patch request'
  261. end
  262. context 'with code 404' do
  263. let(:code) { '404' }
  264. let(:request_url) { "#{host}/test/not_existing" }
  265. let(:request_params) { { submitted: 'some value' } }
  266. include_examples 'unsuccessful request with body'
  267. end
  268. end
  269. context 'with http basic auth' do
  270. subject(:response) do
  271. described_class.post(request_url, request_params, {
  272. user: 'basic_auth_user',
  273. password: password,
  274. })
  275. end
  276. context 'with code 201' do
  277. let(:code) { '201' }
  278. let(:request_url) { "#{host}/test_basic_auth/post/1" }
  279. let(:request_params) { { submitted: 'some value' } }
  280. let(:password) { 'test123' }
  281. let(:expected_body) do
  282. {
  283. 'method' => 'post',
  284. 'submitted' => 'some value',
  285. 'content_type_requested' => 'application/x-www-form-urlencoded',
  286. }
  287. end
  288. include_examples 'successful post/put/patch request'
  289. end
  290. context 'with code 401' do
  291. let(:code) { '401' }
  292. let(:request_url) { "#{host}/test_basic_auth/post/1" }
  293. let(:request_params) { { submitted: 'some value' } }
  294. let(:password) { 'test<>123' }
  295. let(:expected_body) { "HTTP Basic: Access denied.\n" }
  296. include_examples 'unsuccessful get/post/put/delete request'
  297. end
  298. end
  299. context 'with bearer token auth' do
  300. subject(:response) do
  301. described_class.post(request_url, request_params, {
  302. bearer_token: bearer_token,
  303. })
  304. end
  305. context 'with code 201' do
  306. let(:code) { '201' }
  307. let(:request_url) { "#{host}/test_bearer_auth/post/1" }
  308. let(:request_params) { { submitted: 'some value' } }
  309. let(:bearer_token) { 'test_bearer_123' }
  310. let(:expected_body) do
  311. {
  312. 'method' => 'post',
  313. 'submitted' => 'some value',
  314. 'content_type_requested' => 'application/x-www-form-urlencoded',
  315. }
  316. end
  317. include_examples 'successful post/put/patch request'
  318. end
  319. context 'with code 401' do
  320. let(:code) { '401' }
  321. let(:request_url) { "#{host}/test_bearer_auth/post/1" }
  322. let(:request_params) { { submitted: 'some value' } }
  323. let(:bearer_token) { 'wrong_test_bearer' }
  324. let(:expected_body) { "HTTP Token: Access denied.\n" }
  325. include_examples 'unsuccessful get/post/put/delete request'
  326. end
  327. end
  328. context 'when timeouts are raised' do
  329. subject(:response) do
  330. described_class.post(request_url, request_params, {
  331. open_timeout: 0,
  332. read_timeout: 0,
  333. })
  334. end
  335. let(:request_url) { "#{host}/test/post/1" }
  336. let(:request_params) { { submitted: 'timeout' } }
  337. let(:code) { 0 }
  338. include_examples 'unsuccessful request without body'
  339. end
  340. context 'with content type set to json' do
  341. subject(:response) { described_class.post(request_url, request_params, request_options) }
  342. context 'with code 201' do
  343. let(:code) { '201' }
  344. let(:content_type) { 'application/json; charset=utf-8' }
  345. let(:request_url) { "#{host}/test/post/1" }
  346. let(:request_params) { { submitted: { key: 'some value' } } }
  347. let(:request_options) { { json: true } }
  348. let(:expected_body) do
  349. {
  350. 'method' => 'post',
  351. 'content_type_requested' => 'application/json',
  352. 'submitted' => {
  353. 'key' => 'some value',
  354. },
  355. }
  356. end
  357. include_examples 'successful post/put/patch request'
  358. end
  359. end
  360. end
  361. describe '#put' do
  362. subject(:response) { described_class.put(request_url, request_params) }
  363. context 'without http basic auth' do
  364. context 'with code 200' do
  365. let(:code) { '200' }
  366. let(:request_url) { "#{host}/test/put/1" }
  367. let(:request_params) { { submitted: 'some value' } }
  368. let(:expected_body) do
  369. {
  370. 'method' => 'put',
  371. 'submitted' => 'some value',
  372. 'content_type_requested' => 'application/x-www-form-urlencoded',
  373. }
  374. end
  375. include_examples 'successful post/put/patch request'
  376. end
  377. context 'with code 404' do
  378. let(:code) { '404' }
  379. let(:request_url) { "#{host}/test/not_existing" }
  380. let(:request_params) { { submitted: 'some value' } }
  381. include_examples 'unsuccessful request with body'
  382. end
  383. end
  384. context 'with http basic auth' do
  385. subject(:response) do
  386. described_class.put(request_url, request_params, {
  387. user: 'basic_auth_user',
  388. password: password,
  389. })
  390. end
  391. let(:password) { 'test123' }
  392. let(:submit_value) { 'some value' }
  393. context 'with code 200' do
  394. let(:code) { '200' }
  395. let(:request_url) { "#{host}/test_basic_auth/put/1" }
  396. let(:request_params) { { submitted: 'some value' } }
  397. let(:expected_body) do
  398. {
  399. 'method' => 'put',
  400. 'submitted' => 'some value',
  401. 'content_type_requested' => 'application/x-www-form-urlencoded',
  402. }
  403. end
  404. include_examples 'successful post/put/patch request'
  405. end
  406. context 'with code 401' do
  407. let(:code) { '401' }
  408. let(:request_url) { "#{host}/test_basic_auth/put/1" }
  409. let(:request_params) { { submitted: 'some value' } }
  410. let(:password) { 'test<>123' }
  411. let(:expected_body) { "HTTP Basic: Access denied.\n" }
  412. include_examples 'unsuccessful get/post/put/delete request'
  413. end
  414. end
  415. context 'with bearer token auth' do
  416. subject(:response) do
  417. described_class.put(request_url, request_params, {
  418. bearer_token: bearer_token,
  419. })
  420. end
  421. context 'with code 200' do
  422. let(:code) { '200' }
  423. let(:request_url) { "#{host}/test_bearer_auth/put/1" }
  424. let(:request_params) { { submitted: 'some value' } }
  425. let(:bearer_token) { 'test_bearer_123' }
  426. let(:expected_body) do
  427. {
  428. 'method' => 'put',
  429. 'submitted' => 'some value',
  430. 'content_type_requested' => 'application/x-www-form-urlencoded',
  431. }
  432. end
  433. include_examples 'successful post/put/patch request'
  434. end
  435. context 'with code 401' do
  436. let(:code) { '401' }
  437. let(:request_url) { "#{host}/test_bearer_auth/put/1" }
  438. let(:request_params) { { submitted: 'some value' } }
  439. let(:bearer_token) { 'wrong_test_bearer' }
  440. let(:expected_body) { "HTTP Token: Access denied.\n" }
  441. include_examples 'unsuccessful get/post/put/delete request'
  442. end
  443. end
  444. end
  445. describe '#patch' do
  446. subject(:response) { described_class.patch(request_url, request_params) }
  447. context 'with code 200' do
  448. let(:code) { '200' }
  449. let(:request_url) { "#{host}/test/patch/1" }
  450. let(:request_params) { { submitted: 'some value' } }
  451. let(:expected_body) do
  452. {
  453. 'method' => 'patch',
  454. 'submitted' => 'some value',
  455. 'content_type_requested' => 'application/x-www-form-urlencoded',
  456. }
  457. end
  458. include_examples 'successful post/put/patch request'
  459. end
  460. context 'with code 404' do
  461. let(:code) { '404' }
  462. let(:request_url) { "#{host}/test/not_existing" }
  463. let(:request_params) { { submitted: 'some value' } }
  464. include_examples 'unsuccessful request with body'
  465. end
  466. end
  467. describe '#delete' do
  468. context 'without http basic auth' do
  469. subject(:response) { described_class.delete(request_url) }
  470. context 'with code 200' do
  471. let(:code) { '200' }
  472. let(:request_url) { "#{host}/test/delete/1" }
  473. let(:expected_body) do
  474. {
  475. 'method' => 'delete',
  476. 'content_type_requested' => nil,
  477. }
  478. end
  479. include_examples 'successful delete request'
  480. end
  481. context 'with code 404' do
  482. let(:code) { '404' }
  483. let(:request_url) { "#{host}/test/not_existing" }
  484. include_examples 'unsuccessful request with body'
  485. end
  486. end
  487. context 'with http basic auth' do
  488. subject(:response) do
  489. described_class.delete(request_url, {}, {
  490. user: 'basic_auth_user',
  491. password: password,
  492. })
  493. end
  494. context 'with code 200' do
  495. let(:code) { '200' }
  496. let(:content_type) { 'application/json; charset=utf-8' }
  497. let(:request_url) { "#{host}/test_basic_auth/delete/1" }
  498. let(:password) { 'test123' }
  499. let(:expected_body) do
  500. {
  501. 'method' => 'delete',
  502. 'content_type_requested' => nil,
  503. }
  504. end
  505. include_examples 'successful delete request'
  506. end
  507. context 'with code 401' do
  508. let(:code) { '401' }
  509. let(:request_url) { "#{host}/test_basic_auth/delete/1" }
  510. let(:password) { 'test<>123' }
  511. let(:expected_body) { "HTTP Basic: Access denied.\n" }
  512. include_examples 'unsuccessful get/post/put/delete request'
  513. end
  514. end
  515. context 'with bearer token auth' do
  516. subject(:response) do
  517. described_class.delete(request_url, {}, {
  518. bearer_token: bearer_token,
  519. })
  520. end
  521. context 'with code 200' do
  522. let(:code) { '200' }
  523. let(:content_type) { 'application/json; charset=utf-8' }
  524. let(:request_url) { "#{host}/test_bearer_auth/delete/1" }
  525. let(:request_params) { { submitted: 'some value' } }
  526. let(:bearer_token) { 'test_bearer_123' }
  527. let(:expected_body) do
  528. {
  529. 'method' => 'delete',
  530. 'content_type_requested' => nil,
  531. }
  532. end
  533. include_examples 'successful delete request'
  534. end
  535. context 'with code 401' do
  536. let(:code) { '401' }
  537. let(:request_url) { "#{host}/test_bearer_auth/delete/1" }
  538. let(:request_params) { { submitted: 'some value' } }
  539. let(:bearer_token) { 'wrong_test_bearer' }
  540. let(:expected_body) { "HTTP Token: Access denied.\n" }
  541. include_examples 'unsuccessful get/post/put/delete request'
  542. end
  543. end
  544. end
  545. describe '#request' do
  546. context 'without http basic auth' do
  547. subject(:response) { described_class.request(request_url) }
  548. context 'with code 200' do
  549. let(:code) { '200' }
  550. let(:content_type) { 'application/json; charset=utf-8' }
  551. let(:request_url) { "#{host}/test/redirect" }
  552. let(:expected_body) do
  553. {
  554. 'method' => 'get',
  555. 'submitted' => 'abc',
  556. 'content_type_requested' => nil,
  557. }
  558. end
  559. include_examples 'successful redirect request'
  560. end
  561. end
  562. context 'with http basic auth' do
  563. subject(:response) do
  564. described_class.request(request_url, {
  565. user: 'basic_auth_user',
  566. password: password,
  567. })
  568. end
  569. context 'with code 200' do
  570. let(:code) { '200' }
  571. let(:request_url) { "#{host}/test_basic_auth/redirect" }
  572. let(:password) { 'test123' }
  573. let(:expected_body) do
  574. {
  575. 'method' => 'get',
  576. 'submitted' => 'abc',
  577. 'content_type_requested' => nil,
  578. }
  579. end
  580. include_examples 'successful redirect request'
  581. end
  582. context 'with code 401' do
  583. let(:code) { '401' }
  584. let(:request_url) { "#{host}/test_basic_auth/redirect" }
  585. let(:password) { 'test<>123' }
  586. let(:expected_body) { "HTTP Basic: Access denied.\n" }
  587. include_examples 'unsuccessful get/post/put/delete request'
  588. end
  589. end
  590. context 'when ftp', integration: true, required_envs: ['FTP_URL'] do
  591. subject(:response) do
  592. described_class.request(request_url)
  593. end
  594. context 'with code 200' do
  595. let(:code) { '200' }
  596. let(:request_url) { "#{ENV['FTP_URL']}/zammad.txt" }
  597. let(:expected_body) { %r{zammad}i }
  598. include_examples 'ftp requests'
  599. end
  600. context 'with code 550' do
  601. let(:code) { '550' }
  602. let(:request_url) { "#{ENV['FTP_URL']}/nonexisting.txt" }
  603. include_examples 'unsuccessful request without body'
  604. end
  605. context 'with a not existing URL' do
  606. let(:code) { 0 }
  607. let(:request_url) { 'http://not.existing.host.tld/test.php' }
  608. include_examples 'unsuccessful request without body'
  609. end
  610. end
  611. end
  612. end
  613. describe 'testing without proxy' do
  614. include_context 'when doing user agent tests'
  615. end
  616. describe 'testing with proxy', required_envs: %w[CI_PROXY_URL CI_PROXY_USER CI_PROXY_PASSWORD] do
  617. before do
  618. Setting.set('proxy', ENV['CI_PROXY_URL'])
  619. Setting.set('proxy_username', ENV['CI_PROXY_USER'])
  620. Setting.set('proxy_password', ENV['CI_PROXY_PASSWORD'])
  621. end
  622. include_context 'when doing user agent tests'
  623. end
  624. end