sql_spec.rb 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe Selector::Sql do
  4. context 'when relative time range is selected in ticket selector' do
  5. def get_condition(operator, range)
  6. {
  7. 'ticket.created_at' => {
  8. operator: operator,
  9. range: range, # minute|hour|day|month|
  10. value: '10',
  11. },
  12. }
  13. end
  14. before do
  15. freeze_time
  16. end
  17. it 'calculates proper time interval, when operator is within last relative' do
  18. condition = get_condition('within last (relative)', 'minute')
  19. _, bind_params = Ticket.selector2sql(condition)
  20. expect(bind_params).to eq([10.minutes.ago, Time.zone.now])
  21. end
  22. it 'calculates proper time interval, when operator is within next relative' do
  23. condition = get_condition('within next (relative)', 'hour')
  24. _, bind_params = Ticket.selector2sql(condition)
  25. expect(bind_params).to eq([Time.zone.now, 10.hours.from_now])
  26. end
  27. it 'calculates proper time interval, when operator is before (relative)' do
  28. condition = get_condition('before (relative)', 'day')
  29. _, bind_params = Ticket.selector2sql(condition)
  30. expect(bind_params).to eq([10.days.ago])
  31. end
  32. it 'calculates proper time interval, when operator is after (relative)' do
  33. condition = get_condition('after (relative)', 'week')
  34. _, bind_params = Ticket.selector2sql(condition)
  35. expect(bind_params).to eq([10.weeks.from_now])
  36. end
  37. it 'calculates proper time interval, when operator is till (relative)' do
  38. condition = get_condition('till (relative)', 'month')
  39. _, bind_params = Ticket.selector2sql(condition)
  40. expect(bind_params).to eq([10.months.from_now])
  41. end
  42. it 'calculates proper time interval, when operator is from (relative)' do
  43. condition = get_condition('from (relative)', 'year')
  44. _, bind_params = Ticket.selector2sql(condition)
  45. expect(bind_params).to eq([10.years.ago])
  46. end
  47. context 'when today operator is used' do
  48. before do
  49. travel_to '2022-10-11 14:40:00'
  50. Setting.set('timezone_default', 'Europe/Berlin')
  51. end
  52. it 'calculates proper time interval when today operator is used', :aggregate_failures do
  53. _, bind_params = Ticket.selector2sql({ 'ticket.created_at' => { 'operator' => 'today' } })
  54. Time.use_zone(Setting.get('timezone_default')) do
  55. expect(bind_params[0].to_s).to eq('2022-10-10 22:00:00 UTC')
  56. expect(bind_params[1].to_s).to eq('2022-10-11 21:59:59 UTC')
  57. end
  58. end
  59. end
  60. end
  61. describe 'Expert mode overview not working when using "owner is me" OR "subscribe is me #4547' do
  62. let(:agent) { create(:agent, groups: [Group.first]) }
  63. let(:ticket_1) { create(:ticket, owner: agent, group: Group.first) }
  64. let(:ticket_2) { create(:ticket, group: Group.first) }
  65. let(:ticket_3) { create(:ticket, owner: agent, group: Group.first) }
  66. before do
  67. Ticket.destroy_all
  68. ticket_1 && ticket_2 && ticket_3
  69. create(:mention, mentionable: ticket_2, user: agent)
  70. create(:mention, mentionable: ticket_3, user: agent)
  71. end
  72. it 'does return 1 mentioned ticket' do
  73. condition = {
  74. operator: 'AND',
  75. conditions: [
  76. {
  77. name: 'ticket.mention_user_ids',
  78. operator: 'is',
  79. pre_condition: 'specific',
  80. value: agent.id,
  81. }
  82. ]
  83. }
  84. count, = Ticket.selectors(condition, { current_user: agent })
  85. expect(count).to eq(2)
  86. end
  87. it 'does return 1 owned ticket' do
  88. condition = {
  89. operator: 'AND',
  90. conditions: [
  91. {
  92. name: 'ticket.owner_id',
  93. operator: 'is',
  94. pre_condition: 'specific',
  95. value: agent.id,
  96. }
  97. ]
  98. }
  99. count, = Ticket.selectors(condition, { current_user: agent })
  100. expect(count).to eq(2)
  101. end
  102. it 'does return 1 owned & subscribed ticket' do
  103. condition = {
  104. operator: 'AND',
  105. conditions: [
  106. {
  107. name: 'ticket.mention_user_ids',
  108. operator: 'is',
  109. pre_condition: 'specific',
  110. value: agent.id,
  111. },
  112. {
  113. name: 'ticket.owner_id',
  114. operator: 'is',
  115. pre_condition: 'specific',
  116. value: agent.id,
  117. }
  118. ]
  119. }
  120. count, = Ticket.selectors(condition, { current_user: agent })
  121. expect(count).to eq(1)
  122. end
  123. it 'does return 3 owned or subscribed tickets' do
  124. condition = {
  125. operator: 'OR',
  126. conditions: [
  127. {
  128. name: 'ticket.mention_user_ids',
  129. operator: 'is',
  130. pre_condition: 'specific',
  131. value: agent.id,
  132. },
  133. {
  134. name: 'ticket.owner_id',
  135. operator: 'is',
  136. pre_condition: 'specific',
  137. value: agent.id,
  138. }
  139. ]
  140. }
  141. count, = Ticket.selectors(condition, { current_user: agent })
  142. expect(count).to eq(3)
  143. end
  144. end
  145. describe 'Overviews: "Organization" does not work as a pre-condition in the expert mode #4557' do
  146. let(:agent) { create(:agent, groups: [Group.first]) }
  147. let(:organization) { create(:organization) }
  148. let(:customer_1) { create(:customer) }
  149. let(:customer_2) { create(:customer, organization: organization) }
  150. let(:ticket_1) { create(:ticket, customer: customer_1, group: Group.first) }
  151. let(:ticket_2) { create(:ticket, customer: customer_2, group: Group.first) }
  152. before do
  153. Ticket.destroy_all
  154. ticket_1 && ticket_2
  155. end
  156. it 'does return 1 customer ticket without organization' do
  157. condition = {
  158. operator: 'AND',
  159. conditions: [
  160. {
  161. name: 'ticket.organization_id',
  162. operator: 'is',
  163. pre_condition: 'not_set',
  164. }
  165. ]
  166. }
  167. count, = Ticket.selectors(condition, { current_user: agent })
  168. expect(count).to eq(1)
  169. end
  170. it 'does return 1 ticket with organization title' do
  171. condition = {
  172. operator: 'AND',
  173. conditions: [
  174. {
  175. name: 'organization.name',
  176. operator: 'is',
  177. value: organization.name,
  178. }
  179. ]
  180. }
  181. count, = Ticket.selectors(condition, { current_user: agent })
  182. expect(count).to eq(1)
  183. end
  184. it 'does return 1 ticket with organization and name' do
  185. condition = {
  186. operator: 'AND',
  187. conditions: [
  188. {
  189. name: 'ticket.organization_id',
  190. operator: 'is not',
  191. pre_condition: 'not_set',
  192. },
  193. {
  194. name: 'organization.name',
  195. operator: 'is',
  196. value: organization.name,
  197. }
  198. ]
  199. }
  200. count, = Ticket.selectors(condition, { current_user: agent })
  201. expect(count).to eq(1)
  202. end
  203. it 'does return 1 ticket without organization OR NO name' do
  204. condition = {
  205. operator: 'OR',
  206. conditions: [
  207. {
  208. name: 'ticket.organization_id',
  209. operator: 'is',
  210. pre_condition: 'not_set',
  211. },
  212. {
  213. name: 'organization.name',
  214. operator: 'is not',
  215. value: organization.name,
  216. }
  217. ]
  218. }
  219. count, = Ticket.selectors(condition, { current_user: agent })
  220. expect(count).to eq(1)
  221. end
  222. end
  223. describe '.condition_sql' do
  224. # We test this monstrous method indirectly though ".selectors" :(
  225. shared_examples 'finds the ticket' do
  226. it 'finds the ticket' do
  227. expect(Ticket.selectors(condition, { current_user: agent }).first).to eq 1
  228. end
  229. end
  230. shared_examples 'does not find the ticket' do
  231. it 'does not find the ticket' do
  232. expect(Ticket.selectors(condition, { current_user: agent }).first).to eq 0
  233. end
  234. end
  235. before do
  236. Ticket.destroy_all
  237. end
  238. let(:agent) { create(:agent, groups: [Group.first]) }
  239. let(:ticket_attributes) do
  240. {
  241. title: 'Some really nice title',
  242. owner: agent,
  243. group: Group.first
  244. }
  245. end
  246. let(:additional_ticket_attributes) { {} }
  247. let(:ticket) { create(:ticket, ticket_attributes.merge(additional_ticket_attributes)) }
  248. let(:condition) do
  249. { operator: 'AND', conditions: [ {
  250. name: name,
  251. operator: operator,
  252. value: value,
  253. } ] }
  254. end
  255. describe 'input fields' do
  256. let(:name) { 'ticket.title' }
  257. before do
  258. ticket
  259. end
  260. describe "operator 'contains'" do
  261. let(:operator) { 'contains' }
  262. context 'with matching string' do
  263. let(:value) { 'Some' }
  264. include_examples 'finds the ticket'
  265. end
  266. context 'with matching upcased string' do
  267. let(:value) { 'SOME' }
  268. include_examples 'finds the ticket'
  269. end
  270. context 'with non-matching string' do
  271. let(:value) { 'Other' }
  272. include_examples 'does not find the ticket'
  273. end
  274. end
  275. describe "operator 'contains not'" do
  276. let(:operator) { 'contains not' }
  277. context 'with matching string' do
  278. let(:value) { 'Some' }
  279. include_examples 'does not find the ticket'
  280. end
  281. context 'with matching upcased string' do
  282. let(:value) { 'SOME' }
  283. include_examples 'does not find the ticket'
  284. end
  285. context 'with non-matching string' do
  286. let(:value) { 'Other' }
  287. include_examples 'finds the ticket'
  288. end
  289. context 'with empty-looking values in DB' do
  290. let(:value) { 'Some' }
  291. let(:name) { 'ticket.note' }
  292. before { ticket.update! note: database_value }
  293. context 'when value is empty string' do
  294. let(:database_value) { '' }
  295. include_examples 'finds the ticket'
  296. end
  297. context 'when value is NULL' do
  298. let(:database_value) { nil }
  299. include_examples 'finds the ticket'
  300. end
  301. end
  302. end
  303. describe "operator 'is'" do
  304. let(:operator) { 'is' }
  305. context 'with matching string' do
  306. let(:value) { 'Some really nice title' }
  307. include_examples 'finds the ticket'
  308. end
  309. # Skip for MySQL as it handles IN case insensitive.
  310. context 'with matching upcased string', db_adapter: :postgresql do
  311. let(:value) { 'SOME really nice title' }
  312. include_examples 'does not find the ticket'
  313. end
  314. context 'with non-matching string' do
  315. let(:value) { 'Another title' }
  316. include_examples 'does not find the ticket'
  317. end
  318. context 'with empty value' do
  319. let(:ticket_attributes) do
  320. {
  321. title: '',
  322. owner: agent,
  323. group: Group.first
  324. }
  325. end
  326. context 'with non-matching filter value' do
  327. let(:value) { 'Another title' }
  328. include_examples 'does not find the ticket'
  329. end
  330. context 'with empty filter value' do
  331. let(:value) { '' }
  332. include_examples 'finds the ticket'
  333. end
  334. end
  335. end
  336. describe "operator 'is any of'" do
  337. let(:operator) { 'is any of' }
  338. context 'with matching string' do
  339. let(:value) { ['Some really nice title', 'another example'] }
  340. include_examples 'finds the ticket'
  341. end
  342. # Skip for MySQL as it handles IN case insensitive.
  343. context 'with matching upcased string', db_adapter: :postgresql do
  344. let(:value) { ['SOME really nice title', 'another example'] }
  345. include_examples 'does not find the ticket'
  346. end
  347. context 'with non-matching string' do
  348. let(:value) { ['Another title', 'Example'] }
  349. include_examples 'does not find the ticket'
  350. end
  351. context 'with empty value' do
  352. let(:ticket_attributes) do
  353. {
  354. title: '',
  355. owner: agent,
  356. group: Group.first
  357. }
  358. end
  359. context 'with non-matching filter value' do
  360. let(:value) { ['Another title', 'Example'] }
  361. include_examples 'does not find the ticket'
  362. end
  363. context 'with empty filter value' do
  364. let(:value) { [] }
  365. include_examples 'finds the ticket'
  366. end
  367. end
  368. end
  369. describe "operator 'is not'" do
  370. let(:operator) { 'is not' }
  371. context 'with matching string' do
  372. let(:value) { 'Some really nice title' }
  373. include_examples 'does not find the ticket'
  374. end
  375. # Skip for MySQL as it handles IN case insensitive.
  376. context 'with matching upcased string', db_adapter: :postgresql do
  377. let(:value) { 'SOME really nice title' }
  378. include_examples 'finds the ticket'
  379. end
  380. context 'with non-matching string' do
  381. let(:value) { 'Another title' }
  382. include_examples 'finds the ticket'
  383. end
  384. context 'with empty value' do
  385. let(:ticket_attributes) do
  386. {
  387. title: '',
  388. owner: agent,
  389. group: Group.first
  390. }
  391. end
  392. context 'with non-matching filter value' do
  393. let(:value) { 'Another title' }
  394. include_examples 'finds the ticket'
  395. end
  396. context 'with empty filter value' do
  397. let(:value) { '' }
  398. include_examples 'does not find the ticket'
  399. end
  400. end
  401. end
  402. describe "operator 'is none of'" do
  403. let(:operator) { 'is none of' }
  404. context 'with matching string' do
  405. let(:value) { ['Some really nice title', 'another example'] }
  406. include_examples 'does not find the ticket'
  407. end
  408. # Skip for MySQL as it handles IN case insensitive.
  409. context 'with matching upcased string', db_adapter: :postgresql do
  410. let(:value) { %w[SO SOME] }
  411. include_examples 'finds the ticket'
  412. end
  413. context 'with non-matching string' do
  414. let(:value) { %w[A B] }
  415. include_examples 'finds the ticket'
  416. end
  417. context 'with empty value' do
  418. let(:ticket_attributes) do
  419. {
  420. title: '',
  421. owner: agent,
  422. group: Group.first
  423. }
  424. end
  425. context 'with non-matching filter value' do
  426. let(:value) { %w[A B] }
  427. include_examples 'finds the ticket'
  428. end
  429. context 'with empty filter value' do
  430. let(:value) { [] }
  431. include_examples 'does not find the ticket'
  432. end
  433. end
  434. end
  435. describe "operator 'starts with'" do
  436. let(:operator) { 'starts with' }
  437. context 'with matching string' do
  438. let(:value) { 'Some really' }
  439. include_examples 'finds the ticket'
  440. end
  441. context 'with matching upcased string' do
  442. let(:value) { 'SOME really' }
  443. include_examples 'finds the ticket'
  444. end
  445. context 'with special characters' do
  446. let(:ticket_attributes) do
  447. {
  448. title: '\\ [ ]',
  449. owner: agent,
  450. group: Group.first
  451. }
  452. end
  453. let(:value) { '\\ [ ]' }
  454. include_examples 'finds the ticket'
  455. end
  456. context 'with non-matching string' do
  457. let(:value) { 'Another' }
  458. include_examples 'does not find the ticket'
  459. end
  460. end
  461. describe "operator 'starts with one of'" do
  462. let(:operator) { 'starts with one of' }
  463. context 'with matching string' do
  464. let(:value) { ['Some really', 'Some'] }
  465. include_examples 'finds the ticket'
  466. end
  467. context 'with matching upcased string' do
  468. let(:value) { ['SOME', 'Some really',] }
  469. include_examples 'finds the ticket'
  470. end
  471. context 'with non-matching string' do
  472. let(:value) { %w[Another Example] }
  473. include_examples 'does not find the ticket'
  474. end
  475. end
  476. describe "operator 'ends with'" do
  477. let(:operator) { 'ends with' }
  478. context 'with matching string' do
  479. let(:value) { 'nice title' }
  480. include_examples 'finds the ticket'
  481. end
  482. context 'with matching upcased string' do
  483. let(:value) { 'NICE title' }
  484. include_examples 'finds the ticket'
  485. end
  486. context 'with special characters' do
  487. let(:ticket_attributes) do
  488. {
  489. title: '[ ] \\',
  490. owner: agent,
  491. group: Group.first
  492. }
  493. end
  494. let(:value) { '[ ] \\' }
  495. include_examples 'finds the ticket'
  496. end
  497. context 'with non-matching string' do
  498. let(:value) { 'Another title' }
  499. include_examples 'does not find the ticket'
  500. end
  501. end
  502. describe "operator 'ends with one of'" do
  503. let(:operator) { 'ends with one of' }
  504. context 'with matching string' do
  505. let(:value) { ['title', 'nice title'] }
  506. include_examples 'finds the ticket'
  507. end
  508. context 'with matching upcased string' do
  509. let(:value) { ['TITLE', 'NICE title'] }
  510. include_examples 'finds the ticket'
  511. end
  512. context 'with non-matching string' do
  513. let(:value) { ['Another title', 'Example'] }
  514. include_examples 'does not find the ticket'
  515. end
  516. end
  517. describe "operator 'matches regex'", mariadb: true do
  518. let(:operator) { 'matches regex' }
  519. context 'with matching string' do
  520. let(:value) { '^[a-s]' }
  521. include_examples 'finds the ticket'
  522. end
  523. context 'with matching upcased string' do
  524. let(:value) { '^[A-S]' }
  525. include_examples 'finds the ticket'
  526. end
  527. context 'with non-matching string' do
  528. let(:value) { '^[t-z]' }
  529. include_examples 'does not find the ticket'
  530. end
  531. end
  532. describe "operator 'does not match regex'", mariadb: true do
  533. let(:operator) { 'does not match regex' }
  534. context 'with matching string' do
  535. let(:value) { '^[a-s]' }
  536. include_examples 'does not find the ticket'
  537. end
  538. context 'with matching upcased string' do
  539. let(:value) { '^[A-S]' }
  540. include_examples 'does not find the ticket'
  541. end
  542. context 'with non-matching string' do
  543. let(:value) { '^[t-z]' }
  544. include_examples 'finds the ticket'
  545. end
  546. end
  547. end
  548. describe 'complex conditions' do
  549. context "when 'contains not' operator is after negative operator" do
  550. let(:condition) do
  551. { operator: 'AND', conditions: [
  552. {
  553. name: 'ticket.title',
  554. operator: 'is not',
  555. value: 'title',
  556. }, {
  557. name: 'ticket.note',
  558. operator: 'contains not',
  559. value: 'some',
  560. },
  561. ] }
  562. end
  563. let(:additional_ticket_attributes) { { title: 'title' } }
  564. before do
  565. ticket
  566. end
  567. include_examples 'does not find the ticket'
  568. end
  569. context "when 'contains not' operator is before negative operator" do
  570. let(:condition) do
  571. { operator: 'AND', conditions: [
  572. {
  573. name: 'ticket.note',
  574. operator: 'contains not',
  575. value: 'some',
  576. }, {
  577. name: 'ticket.title',
  578. operator: 'is not',
  579. value: 'title',
  580. }
  581. ] }
  582. end
  583. let(:additional_ticket_attributes) { { title: 'title' } }
  584. before do
  585. ticket
  586. end
  587. include_examples 'does not find the ticket'
  588. end
  589. context "when 'contains not' operator on a related table is after negative operator" do
  590. let(:condition) do
  591. { operator: 'AND', conditions: [
  592. {
  593. name: 'ticket.title',
  594. operator: 'is not',
  595. value: 'title',
  596. }, {
  597. name: 'customer.email',
  598. operator: 'contains not',
  599. value: 'some',
  600. },
  601. ] }
  602. end
  603. let(:additional_ticket_attributes) { { title: 'title' } }
  604. before do
  605. ticket
  606. end
  607. include_examples 'does not find the ticket'
  608. end
  609. context "when 'contains not' operator on a related table is before negative operator" do
  610. let(:condition) do
  611. { operator: 'AND', conditions: [
  612. {
  613. name: 'customer.email',
  614. operator: 'contains not',
  615. value: 'some',
  616. }, {
  617. name: 'ticket.title',
  618. operator: 'is not',
  619. value: 'title',
  620. }
  621. ] }
  622. end
  623. let(:additional_ticket_attributes) { { title: 'title' } }
  624. before do
  625. ticket
  626. end
  627. include_examples 'does not find the ticket'
  628. end
  629. end
  630. describe 'external data source field', db_adapter: :postgresql, db_strategy: :reset do
  631. let(:external_data_source_attribute) do
  632. create(:object_manager_attribute_autocompletion_ajax_external_data_source,
  633. name: 'external_data_source_attribute')
  634. end
  635. let(:name) { "ticket.#{external_data_source_attribute.name}" }
  636. let(:external_data_source_attribute_value) { 123 }
  637. let(:additional_ticket_attributes) do
  638. {
  639. external_data_source_attribute.name => {
  640. value: external_data_source_attribute_value,
  641. label: 'Example'
  642. }
  643. }
  644. end
  645. before do
  646. external_data_source_attribute
  647. ObjectManager::Attribute.migration_execute
  648. ticket
  649. end
  650. describe "operator 'is'" do
  651. let(:operator) { 'is' }
  652. context 'with matching integer as value' do
  653. let(:value) do
  654. {
  655. value: 123,
  656. label: 'Example'
  657. }
  658. end
  659. include_examples 'finds the ticket'
  660. end
  661. context 'with multiple values for matching' do
  662. let(:value) do
  663. [
  664. {
  665. value: 123,
  666. label: 'Example'
  667. },
  668. {
  669. value: '987',
  670. label: 'Example'
  671. }
  672. ]
  673. end
  674. include_examples 'finds the ticket'
  675. end
  676. context 'with string' do
  677. context 'with matching string as value' do
  678. let(:external_data_source_attribute_value) { 'Example' }
  679. let(:value) do
  680. {
  681. value: 'Example',
  682. label: 'Example'
  683. }
  684. end
  685. include_examples 'finds the ticket'
  686. end
  687. context 'with non-matching string' do
  688. let(:value) do
  689. {
  690. value: 'Wrong',
  691. label: 'Wrong'
  692. }
  693. end
  694. include_examples 'does not find the ticket'
  695. end
  696. end
  697. context 'with matching boolean as value' do
  698. let(:external_data_source_attribute_value) { true }
  699. let(:value) do
  700. {
  701. value: true,
  702. label: 'Yes'
  703. }
  704. end
  705. include_examples 'finds the ticket'
  706. end
  707. end
  708. describe "operator 'is not'" do
  709. let(:operator) { 'is not' }
  710. context 'with matching integer as value' do
  711. let(:value) do
  712. {
  713. value: 986,
  714. label: 'Example'
  715. }
  716. end
  717. include_examples 'finds the ticket'
  718. end
  719. context 'with matching integer' do
  720. let(:value) do
  721. {
  722. value: 123,
  723. label: 'Example'
  724. }
  725. end
  726. include_examples 'does not find the ticket'
  727. end
  728. end
  729. end
  730. end
  731. describe '.valid?' do
  732. let(:instance) { described_class.new(selector: { operator: 'AND', conditions: [ condition ] }, options: {}) }
  733. context 'with valid conditions' do
  734. let(:condition) do
  735. {
  736. name: 'ticket.organization_id',
  737. operator: 'is',
  738. pre_condition: 'not_set',
  739. }
  740. end
  741. it 'validates' do
  742. expect(instance.valid?).to be true
  743. end
  744. end
  745. context 'with wrong ticket attribute' do
  746. let(:condition) do
  747. {
  748. name: 'ticket.unknown_field',
  749. operator: 'is',
  750. pre_condition: 'not_set',
  751. }
  752. end
  753. it 'does not validate' do
  754. expect(instance.valid?).to be false
  755. end
  756. end
  757. context 'with unknown operator' do
  758. let(:condition) do
  759. {
  760. name: 'ticket.title',
  761. operator: 'looks nice',
  762. }
  763. end
  764. it 'does not validate' do
  765. expect(instance.valid?).to be false
  766. end
  767. end
  768. context 'with invalid regular expression', mariadb: true do
  769. let(:condition) do
  770. {
  771. name: 'ticket.title',
  772. operator: 'matches regex',
  773. value: '(',
  774. }
  775. end
  776. it 'does not validate' do
  777. expect(instance.valid?).to be false
  778. end
  779. end
  780. context 'with external data source field', db_adapter: :postgresql, db_strategy: :reset do
  781. let(:external_data_source_attribute) do
  782. create(:object_manager_attribute_autocompletion_ajax_external_data_source,
  783. name: 'external_data_source_attribute')
  784. end
  785. let(:condition) do
  786. {
  787. name: "ticket.#{external_data_source_attribute.name}",
  788. operator: 'is',
  789. value: {
  790. value: 123,
  791. label: 'Example'
  792. }
  793. }
  794. end
  795. before do
  796. external_data_source_attribute
  797. ObjectManager::Attribute.migration_execute
  798. end
  799. it 'validates' do
  800. expect(instance.valid?).to be true
  801. end
  802. end
  803. end
  804. describe 'Error 500 if overview with "out of office replacement" filter is set to "specific user" #4599' do
  805. let(:agent) { create(:agent) }
  806. let(:agent_ooo) { create(:agent, :ooo, ooo_agent: agent_ooo_replacement) }
  807. let(:agent_ooo_replacement) { create(:agent) }
  808. let(:condition) do
  809. {
  810. 'ticket.out_of_office_replacement_id': {
  811. operator: 'is',
  812. pre_condition: 'specific',
  813. value: [
  814. agent_ooo_replacement.id.to_s,
  815. ],
  816. value_completion: ''
  817. }
  818. }
  819. end
  820. before do
  821. agent_ooo
  822. end
  823. it 'calculates the out of office user ids for the out of office replacement agent' do
  824. _, bind_params = Ticket.selector2sql(condition)
  825. expect(bind_params.flatten).to include(agent_ooo.id)
  826. end
  827. end
  828. describe 'Performance: Improve tags performance when only one tag is used' do
  829. it 'does optimize the sql when one element is set' do
  830. sql, = Ticket.selector2sql({
  831. 'ticket.tags' => {
  832. operator: 'contains all',
  833. value: 'blub',
  834. },
  835. })
  836. expect(sql).not_to include('SELECT')
  837. end
  838. it 'does not optimize the sql when multiple elements are set' do
  839. sql, = Ticket.selector2sql({
  840. 'ticket.tags' => {
  841. operator: 'contains all',
  842. value: 't1,t2',
  843. },
  844. })
  845. expect(sql).to include('SELECT')
  846. end
  847. end
  848. end