user_agent_spec.rb 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  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. describe '#get' do
  94. context 'without http basic auth' do
  95. subject(:response) { described_class.get(request_url) }
  96. context 'with code 200' do
  97. let(:code) { '200' }
  98. let(:content_type) { 'application/json; charset=utf-8' }
  99. let(:request_url) { "#{host}/test/get/1?submitted=123" }
  100. let(:expected_body) do
  101. {
  102. 'method' => 'get',
  103. 'submitted' => '123',
  104. 'content_type_requested' => nil,
  105. }
  106. end
  107. include_examples 'successful get request'
  108. end
  109. context 'with code 202' do
  110. let(:code) { '202' }
  111. let(:content_type) { 'application/json; charset=utf-8' }
  112. let(:request_url) { "#{host}/test/get_accepted/1?submitted=123" }
  113. let(:expected_body) do
  114. {
  115. 'method' => 'get',
  116. 'submitted' => '123',
  117. 'content_type_requested' => nil,
  118. }
  119. end
  120. include_examples 'successful get request'
  121. end
  122. context 'with code 404' do
  123. let(:code) { '404' }
  124. let(:request_url) { "#{host}/test/not_existing" }
  125. include_examples 'unsuccessful request with body'
  126. end
  127. end
  128. context 'with http basic auth' do
  129. subject(:response) do
  130. described_class.get(request_url, {}, {
  131. user: 'basic_auth_user',
  132. password: password,
  133. })
  134. end
  135. context 'with code 200' do
  136. let(:code) { '200' }
  137. let(:content_type) { 'application/json; charset=utf-8' }
  138. let(:request_url) { "#{host}/test_basic_auth/get/1?submitted=123" }
  139. let(:password) { 'test123' }
  140. let(:expected_body) do
  141. {
  142. 'method' => 'get',
  143. 'submitted' => '123',
  144. 'content_type_requested' => nil,
  145. }
  146. end
  147. include_examples 'successful get request'
  148. end
  149. context 'with code 401' do
  150. let(:code) { '401' }
  151. let(:request_url) { "#{host}/test_basic_auth/get/1?submitted=123" }
  152. let(:password) { 'test<>123' }
  153. let(:expected_body) { "HTTP Basic: Access denied.\n" }
  154. include_examples 'unsuccessful get/post/put/delete request'
  155. end
  156. end
  157. context 'with bearer token auth' do
  158. subject(:response) do
  159. described_class.get(request_url, {}, {
  160. bearer_token: bearer_token,
  161. })
  162. end
  163. context 'with code 200' do
  164. let(:code) { '200' }
  165. let(:content_type) { 'application/json; charset=utf-8' }
  166. let(:request_url) { "#{host}/test_bearer_auth/get/1?submitted=123" }
  167. let(:bearer_token) { 'test_bearer_123' }
  168. let(:expected_body) do
  169. {
  170. 'method' => 'get',
  171. 'submitted' => '123',
  172. 'content_type_requested' => nil,
  173. }
  174. end
  175. include_examples 'successful get request'
  176. end
  177. context 'with code 401' do
  178. let(:code) { '401' }
  179. let(:request_url) { "#{host}/test_bearer_auth/get/1?submitted=123" }
  180. let(:bearer_token) { 'wrong_test_bearer' }
  181. let(:expected_body) { "HTTP Token: Access denied.\n" }
  182. include_examples 'unsuccessful get/post/put/delete request'
  183. end
  184. end
  185. context 'when timeouts are raised' do
  186. subject(:response) do
  187. described_class.get(request_url, {}, {
  188. open_timeout: 0,
  189. read_timeout: 0,
  190. })
  191. end
  192. let(:request_url) { "#{host}/test/get/1?submitted=123" }
  193. let(:code) { 0 }
  194. include_examples 'unsuccessful request without body'
  195. end
  196. context 'with content type set to json' do
  197. subject(:response) { described_class.get(request_url, request_params, request_options) }
  198. context 'with code 200' do
  199. let(:code) { '200' }
  200. let(:content_type) { 'application/json; charset=utf-8' }
  201. let(:request_url) { "#{host}/test/get/1" }
  202. let(:request_params) { { submitted: 'some value' } }
  203. let(:request_options) { { json: true } }
  204. let(:expected_body) do
  205. {
  206. 'method' => 'get',
  207. 'content_type_requested' => nil,
  208. 'submitted' => 'some value',
  209. }
  210. end
  211. include_examples 'successful get request'
  212. end
  213. context 'with code 404' do
  214. let(:code) { '404' }
  215. let(:request_url) { "#{host}/test/not_existing" }
  216. let(:request_params) { { submitted: { key: 'some value' } } }
  217. let(:request_options) { { json: true } }
  218. include_examples 'unsuccessful request with body'
  219. end
  220. end
  221. end
  222. describe '#post' do
  223. context 'without http basic auth' do
  224. subject(:response) { described_class.post(request_url, request_params, request_options) }
  225. let(:request_options) { {} }
  226. context 'with code 201' do
  227. let(:code) { '201' }
  228. let(:request_url) { "#{host}/test/post/1" }
  229. let(:request_params) { { submitted: 'some value' } }
  230. let(:expected_body) do
  231. {
  232. 'method' => 'post',
  233. 'submitted' => 'some value',
  234. 'body' => ['submitted=some+value'],
  235. 'content_type_requested' => 'application/x-www-form-urlencoded',
  236. }
  237. end
  238. include_examples 'successful post/put/patch request'
  239. end
  240. context 'with raw body' do
  241. let(:code) { '201' }
  242. let(:request_url) { "#{host}/test/post/1" }
  243. let(:request_params) { {} }
  244. let(:request_options) { { send_as_raw_body: 'raw body' } }
  245. let(:expected_body) do
  246. {
  247. 'method' => 'post',
  248. 'submitted' => nil,
  249. 'body' => ['raw body'],
  250. 'content_type_requested' => 'application/x-www-form-urlencoded',
  251. }
  252. end
  253. include_examples 'successful post/put/patch request'
  254. end
  255. context 'with code 404' do
  256. let(:code) { '404' }
  257. let(:request_url) { "#{host}/test/not_existing" }
  258. let(:request_params) { { submitted: 'some value' } }
  259. include_examples 'unsuccessful request with body'
  260. end
  261. end
  262. context 'with http basic auth' do
  263. subject(:response) do
  264. described_class.post(request_url, request_params, {
  265. user: 'basic_auth_user',
  266. password: password,
  267. })
  268. end
  269. context 'with code 201' do
  270. let(:code) { '201' }
  271. let(:request_url) { "#{host}/test_basic_auth/post/1" }
  272. let(:request_params) { { submitted: 'some value' } }
  273. let(:password) { 'test123' }
  274. let(:expected_body) do
  275. {
  276. 'method' => 'post',
  277. 'submitted' => 'some value',
  278. 'content_type_requested' => 'application/x-www-form-urlencoded',
  279. }
  280. end
  281. include_examples 'successful post/put/patch request'
  282. end
  283. context 'with code 401' do
  284. let(:code) { '401' }
  285. let(:request_url) { "#{host}/test_basic_auth/post/1" }
  286. let(:request_params) { { submitted: 'some value' } }
  287. let(:password) { 'test<>123' }
  288. let(:expected_body) { "HTTP Basic: Access denied.\n" }
  289. include_examples 'unsuccessful get/post/put/delete request'
  290. end
  291. end
  292. context 'with bearer token auth' do
  293. subject(:response) do
  294. described_class.post(request_url, request_params, {
  295. bearer_token: bearer_token,
  296. })
  297. end
  298. context 'with code 201' do
  299. let(:code) { '201' }
  300. let(:request_url) { "#{host}/test_bearer_auth/post/1" }
  301. let(:request_params) { { submitted: 'some value' } }
  302. let(:bearer_token) { 'test_bearer_123' }
  303. let(:expected_body) do
  304. {
  305. 'method' => 'post',
  306. 'submitted' => 'some value',
  307. 'content_type_requested' => 'application/x-www-form-urlencoded',
  308. }
  309. end
  310. include_examples 'successful post/put/patch request'
  311. end
  312. context 'with code 401' do
  313. let(:code) { '401' }
  314. let(:request_url) { "#{host}/test_bearer_auth/post/1" }
  315. let(:request_params) { { submitted: 'some value' } }
  316. let(:bearer_token) { 'wrong_test_bearer' }
  317. let(:expected_body) { "HTTP Token: Access denied.\n" }
  318. include_examples 'unsuccessful get/post/put/delete request'
  319. end
  320. end
  321. context 'when timeouts are raised' do
  322. subject(:response) do
  323. described_class.post(request_url, request_params, {
  324. open_timeout: 0,
  325. read_timeout: 0,
  326. })
  327. end
  328. let(:request_url) { "#{host}/test/post/1" }
  329. let(:request_params) { { submitted: 'timeout' } }
  330. let(:code) { 0 }
  331. include_examples 'unsuccessful request without body'
  332. end
  333. context 'with content type set to json' do
  334. subject(:response) { described_class.post(request_url, request_params, request_options) }
  335. context 'with code 201' do
  336. let(:code) { '201' }
  337. let(:content_type) { 'application/json; charset=utf-8' }
  338. let(:request_url) { "#{host}/test/post/1" }
  339. let(:request_params) { { submitted: { key: 'some value' } } }
  340. let(:request_options) { { json: true } }
  341. let(:expected_body) do
  342. {
  343. 'method' => 'post',
  344. 'content_type_requested' => 'application/json',
  345. 'submitted' => {
  346. 'key' => 'some value',
  347. },
  348. }
  349. end
  350. include_examples 'successful post/put/patch request'
  351. end
  352. end
  353. end
  354. describe '#put' do
  355. subject(:response) { described_class.put(request_url, request_params) }
  356. context 'without http basic auth' do
  357. context 'with code 200' do
  358. let(:code) { '200' }
  359. let(:request_url) { "#{host}/test/put/1" }
  360. let(:request_params) { { submitted: 'some value' } }
  361. let(:expected_body) do
  362. {
  363. 'method' => 'put',
  364. 'submitted' => 'some value',
  365. 'content_type_requested' => 'application/x-www-form-urlencoded',
  366. }
  367. end
  368. include_examples 'successful post/put/patch request'
  369. end
  370. context 'with code 404' do
  371. let(:code) { '404' }
  372. let(:request_url) { "#{host}/test/not_existing" }
  373. let(:request_params) { { submitted: 'some value' } }
  374. include_examples 'unsuccessful request with body'
  375. end
  376. end
  377. context 'with http basic auth' do
  378. subject(:response) do
  379. described_class.put(request_url, request_params, {
  380. user: 'basic_auth_user',
  381. password: password,
  382. })
  383. end
  384. let(:password) { 'test123' }
  385. let(:submit_value) { 'some value' }
  386. context 'with code 200' do
  387. let(:code) { '200' }
  388. let(:request_url) { "#{host}/test_basic_auth/put/1" }
  389. let(:request_params) { { submitted: 'some value' } }
  390. let(:expected_body) do
  391. {
  392. 'method' => 'put',
  393. 'submitted' => 'some value',
  394. 'content_type_requested' => 'application/x-www-form-urlencoded',
  395. }
  396. end
  397. include_examples 'successful post/put/patch request'
  398. end
  399. context 'with code 401' do
  400. let(:code) { '401' }
  401. let(:request_url) { "#{host}/test_basic_auth/put/1" }
  402. let(:request_params) { { submitted: 'some value' } }
  403. let(:password) { 'test<>123' }
  404. let(:expected_body) { "HTTP Basic: Access denied.\n" }
  405. include_examples 'unsuccessful get/post/put/delete request'
  406. end
  407. end
  408. context 'with bearer token auth' do
  409. subject(:response) do
  410. described_class.put(request_url, request_params, {
  411. bearer_token: bearer_token,
  412. })
  413. end
  414. context 'with code 200' do
  415. let(:code) { '200' }
  416. let(:request_url) { "#{host}/test_bearer_auth/put/1" }
  417. let(:request_params) { { submitted: 'some value' } }
  418. let(:bearer_token) { 'test_bearer_123' }
  419. let(:expected_body) do
  420. {
  421. 'method' => 'put',
  422. 'submitted' => 'some value',
  423. 'content_type_requested' => 'application/x-www-form-urlencoded',
  424. }
  425. end
  426. include_examples 'successful post/put/patch request'
  427. end
  428. context 'with code 401' do
  429. let(:code) { '401' }
  430. let(:request_url) { "#{host}/test_bearer_auth/put/1" }
  431. let(:request_params) { { submitted: 'some value' } }
  432. let(:bearer_token) { 'wrong_test_bearer' }
  433. let(:expected_body) { "HTTP Token: Access denied.\n" }
  434. include_examples 'unsuccessful get/post/put/delete request'
  435. end
  436. end
  437. end
  438. describe '#patch' do
  439. subject(:response) { described_class.patch(request_url, request_params) }
  440. context 'with code 200' do
  441. let(:code) { '200' }
  442. let(:request_url) { "#{host}/test/patch/1" }
  443. let(:request_params) { { submitted: 'some value' } }
  444. let(:expected_body) do
  445. {
  446. 'method' => 'patch',
  447. 'submitted' => 'some value',
  448. 'content_type_requested' => 'application/x-www-form-urlencoded',
  449. }
  450. end
  451. include_examples 'successful post/put/patch request'
  452. end
  453. context 'with code 404' do
  454. let(:code) { '404' }
  455. let(:request_url) { "#{host}/test/not_existing" }
  456. let(:request_params) { { submitted: 'some value' } }
  457. include_examples 'unsuccessful request with body'
  458. end
  459. end
  460. describe '#delete' do
  461. context 'without http basic auth' do
  462. subject(:response) { described_class.delete(request_url) }
  463. context 'with code 200' do
  464. let(:code) { '200' }
  465. let(:request_url) { "#{host}/test/delete/1" }
  466. let(:expected_body) do
  467. {
  468. 'method' => 'delete',
  469. 'content_type_requested' => nil,
  470. }
  471. end
  472. include_examples 'successful delete request'
  473. end
  474. context 'with code 404' do
  475. let(:code) { '404' }
  476. let(:request_url) { "#{host}/test/not_existing" }
  477. include_examples 'unsuccessful request with body'
  478. end
  479. end
  480. context 'with http basic auth' do
  481. subject(:response) do
  482. described_class.delete(request_url, {}, {
  483. user: 'basic_auth_user',
  484. password: password,
  485. })
  486. end
  487. context 'with code 200' do
  488. let(:code) { '200' }
  489. let(:content_type) { 'application/json; charset=utf-8' }
  490. let(:request_url) { "#{host}/test_basic_auth/delete/1" }
  491. let(:password) { 'test123' }
  492. let(:expected_body) do
  493. {
  494. 'method' => 'delete',
  495. 'content_type_requested' => nil,
  496. }
  497. end
  498. include_examples 'successful delete request'
  499. end
  500. context 'with code 401' do
  501. let(:code) { '401' }
  502. let(:request_url) { "#{host}/test_basic_auth/delete/1" }
  503. let(:password) { 'test<>123' }
  504. let(:expected_body) { "HTTP Basic: Access denied.\n" }
  505. include_examples 'unsuccessful get/post/put/delete request'
  506. end
  507. end
  508. context 'with bearer token auth' do
  509. subject(:response) do
  510. described_class.delete(request_url, {}, {
  511. bearer_token: bearer_token,
  512. })
  513. end
  514. context 'with code 200' do
  515. let(:code) { '200' }
  516. let(:content_type) { 'application/json; charset=utf-8' }
  517. let(:request_url) { "#{host}/test_bearer_auth/delete/1" }
  518. let(:request_params) { { submitted: 'some value' } }
  519. let(:bearer_token) { 'test_bearer_123' }
  520. let(:expected_body) do
  521. {
  522. 'method' => 'delete',
  523. 'content_type_requested' => nil,
  524. }
  525. end
  526. include_examples 'successful delete request'
  527. end
  528. context 'with code 401' do
  529. let(:code) { '401' }
  530. let(:request_url) { "#{host}/test_bearer_auth/delete/1" }
  531. let(:request_params) { { submitted: 'some value' } }
  532. let(:bearer_token) { 'wrong_test_bearer' }
  533. let(:expected_body) { "HTTP Token: Access denied.\n" }
  534. include_examples 'unsuccessful get/post/put/delete request'
  535. end
  536. end
  537. end
  538. end
  539. describe 'testing without proxy' do
  540. include_context 'when doing user agent tests'
  541. end
  542. describe 'testing with proxy', required_envs: %w[CI_PROXY_URL CI_PROXY_USER CI_PROXY_PASSWORD] do
  543. before do
  544. Setting.set('proxy', ENV['CI_PROXY_URL'])
  545. Setting.set('proxy_username', ENV['CI_PROXY_USER'])
  546. Setting.set('proxy_password', ENV['CI_PROXY_PASSWORD'])
  547. end
  548. include_context 'when doing user agent tests'
  549. end
  550. end