ticket_generic_time_spec.rb 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. require 'lib/report_examples'
  4. RSpec.describe Report::TicketGenericTime, searchindex: true do
  5. include_examples 'with report examples'
  6. describe '.aggs' do
  7. it 'gets monthly aggregated results by created_at' do
  8. result = described_class.aggs(
  9. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  10. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  11. interval: 'month', # year, quarter, month, week, day, hour, minute, second
  12. selector: {}, # ticket selector to get only a collection of tickets
  13. params: { field: 'created_at' },
  14. )
  15. expect(result).to eq [0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 1, 0]
  16. end
  17. it 'gets monthly aggregated results by created_at not merged' do
  18. result = described_class.aggs(
  19. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  20. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  21. interval: 'month', # year, quarter, month, week, day, hour, minute, second
  22. selector: {
  23. 'state' => {
  24. 'operator' => 'is not',
  25. 'value' => 'merged'
  26. }
  27. },
  28. params: { field: 'created_at' },
  29. )
  30. expect(result).to eq [0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 1, 0]
  31. end
  32. context 'when report profile has a ticket tag condition' do
  33. shared_examples 'getting the correct aggregated results' do
  34. it { expect(result).to eq expected_result }
  35. end
  36. # With tag1
  37. let(:ticket_1) do
  38. travel_to DateTime.new 2015, 1, 28, 9, 30
  39. ticket = create(:ticket,
  40. group: group_2,
  41. customer: customer)
  42. ticket.tag_add('tag1', 1)
  43. travel_back
  44. ticket
  45. end
  46. # With tag2
  47. let(:ticket_2) do
  48. travel_to DateTime.new 2015, 2, 28, 9, 30
  49. ticket = create(:ticket,
  50. group: group_1,
  51. customer: customer)
  52. ticket.tag_add('tag2', 1)
  53. travel_back
  54. ticket
  55. end
  56. # Without tag1 or tag2, but article with tag1
  57. let(:ticket_3) do
  58. travel_to DateTime.new 2015, 3, 28, 9, 30
  59. ticket = create(:ticket,
  60. group: group_1,
  61. customer: customer)
  62. create(:ticket_article, ticket: ticket, body: 'tag1')
  63. travel_back
  64. ticket
  65. end
  66. # Without tag1 or tag2, but article with tag2
  67. let(:ticket_4) do
  68. travel_to DateTime.new 2015, 4, 28, 9, 30
  69. ticket = create(:ticket,
  70. group: group_1,
  71. customer: customer)
  72. create(:ticket_article, ticket: ticket, body: 'tag2')
  73. travel_back
  74. ticket
  75. end
  76. # With tag1 and tag2
  77. let(:ticket_5) do
  78. travel_to DateTime.new 2015, 5, 28, 9, 30
  79. ticket = create(:ticket,
  80. group: group_1,
  81. customer: customer)
  82. create(:ticket_article, ticket: ticket)
  83. ticket.tag_add('tag1', 1)
  84. ticket.tag_add('tag2', 1)
  85. travel_back
  86. ticket
  87. end
  88. # With tag1 and tag2, with article body tag1
  89. let(:ticket_6) do
  90. travel_to DateTime.new 2015, 6, 28, 9, 30
  91. ticket = create(:ticket,
  92. group: group_1,
  93. customer: customer)
  94. create(:ticket_article, ticket: ticket, body: 'tag1')
  95. ticket.tag_add('tag1', 1)
  96. ticket.tag_add('tag2', 1)
  97. travel_back
  98. ticket
  99. end
  100. # With tag1 and tag2, with article body tag2
  101. let(:ticket_7) do
  102. travel_to DateTime.new 2015, 7, 28, 9, 30
  103. ticket = create(:ticket,
  104. group: group_1,
  105. customer: customer)
  106. create(:ticket_article, ticket: ticket, body: 'tag1')
  107. ticket.tag_add('tag1', 1)
  108. ticket.tag_add('tag2', 1)
  109. travel_back
  110. ticket
  111. end
  112. # With tag1 and tag2, with article body tag1 and tag2
  113. let(:ticket_8) do
  114. travel_to DateTime.new 2015, 8, 28, 9, 30
  115. ticket = create(:ticket,
  116. group: group_1,
  117. customer: customer)
  118. create(:ticket_article, ticket: ticket, body: 'tag1')
  119. create(:ticket_article, ticket: ticket, body: 'tag2')
  120. ticket.tag_add('tag1', 1)
  121. ticket.tag_add('tag2', 1)
  122. travel_back
  123. ticket
  124. end
  125. # Without tag1 or tag2, and without article with tag1 or tag2
  126. let(:ticket_9) do
  127. travel_to DateTime.new 2015, 4, 28, 9, 30
  128. ticket = create(:ticket,
  129. group: group_1,
  130. customer: customer)
  131. travel_back
  132. ticket
  133. end
  134. let(:result) do
  135. described_class.aggs(
  136. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  137. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  138. interval: 'month',
  139. selector: selector,
  140. params: { field: 'created_at' },
  141. )
  142. end
  143. context 'with contains all' do
  144. let(:selector) do
  145. {
  146. 'ticket.tags' => {
  147. 'operator' => 'contains all',
  148. 'value' => 'tag1, tag2'
  149. }
  150. }
  151. end
  152. let(:expected_result) { [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0] }
  153. it_behaves_like 'getting the correct aggregated results'
  154. end
  155. context 'with contains one not' do
  156. let(:selector) do
  157. {
  158. 'ticket.tags' => {
  159. 'operator' => 'contains one not',
  160. 'value' => 'tag1, tag2'
  161. }
  162. }
  163. end
  164. let(:expected_result) { [0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0] }
  165. it_behaves_like 'getting the correct aggregated results'
  166. end
  167. context 'with contains one' do
  168. let(:selector) do
  169. {
  170. 'ticket.tags' => {
  171. 'operator' => 'contains one',
  172. 'value' => 'tag1, tag2'
  173. }
  174. }
  175. end
  176. let(:expected_result) { [1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0] }
  177. it_behaves_like 'getting the correct aggregated results'
  178. end
  179. context 'with contains all not' do
  180. let(:selector) do
  181. {
  182. 'ticket.tags' => {
  183. 'operator' => 'contains all not',
  184. 'value' => 'tag1, tag2'
  185. }
  186. }
  187. end
  188. let(:expected_result) { [1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0] }
  189. it_behaves_like 'getting the correct aggregated results'
  190. end
  191. end
  192. end
  193. describe '.items' do
  194. it 'gets items in year range by created_at' do
  195. result = described_class.items(
  196. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  197. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  198. selector: {}, # ticket selector to get only a collection of tickets
  199. params: { field: 'created_at' },
  200. )
  201. expect(result).to match_tickets ticket_7, ticket_6, ticket_5, ticket_4, ticket_3, ticket_2, ticket_1
  202. end
  203. it 'gets items in year range by created_at not merged' do
  204. result = described_class.items(
  205. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  206. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  207. selector: {
  208. 'state' => {
  209. 'operator' => 'is not',
  210. 'value' => 'merged'
  211. }
  212. },
  213. params: { field: 'created_at' },
  214. )
  215. expect(result).to match_tickets ticket_7, ticket_6, ticket_5, ticket_4, ticket_3, ticket_2, ticket_1
  216. end
  217. it 'gets items in year range by created_at before oct 31st' do
  218. result = described_class.items(
  219. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  220. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  221. selector: {
  222. 'created_at' => {
  223. 'operator' => 'before (absolute)',
  224. 'value' => '2015-10-31T00:00:00Z'
  225. }
  226. },
  227. params: { field: 'created_at' },
  228. )
  229. expect(result).to match_tickets ticket_5, ticket_4, ticket_3, ticket_2, ticket_1
  230. end
  231. it 'gets items in year range by created_at after oct 31st' do
  232. result = described_class.items(
  233. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  234. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  235. selector: {
  236. 'created_at' => {
  237. 'operator' => 'after (absolute)',
  238. 'value' => '2015-10-31T00:00:00Z'
  239. }
  240. },
  241. params: { field: 'created_at' },
  242. )
  243. expect(result).to match_tickets ticket_7, ticket_6
  244. end
  245. it 'gets items in 1 day from now' do
  246. result = described_class.items(
  247. range_start: 1.year.ago.beginning_of_year,
  248. range_end: 1.year.from_now.at_end_of_year,
  249. selector: {
  250. 'created_at' => {
  251. 'operator' => 'after (relative)',
  252. 'range' => 'day',
  253. 'value' => '1'
  254. }
  255. }, # ticket selector to get only a collection of tickets
  256. params: { field: 'created_at' },
  257. )
  258. expect(result).to match_tickets ticket_after_72h
  259. end
  260. it 'gets items in 1 month from now' do
  261. result = described_class.items(
  262. range_start: 1.year.ago.beginning_of_year,
  263. range_end: 1.year.from_now.at_end_of_year,
  264. selector: {
  265. 'created_at' => {
  266. 'operator' => 'after (relative)',
  267. 'range' => 'month',
  268. 'value' => '1'
  269. }
  270. }, # ticket selector to get only a collection of tickets
  271. params: { field: 'created_at' },
  272. )
  273. expect(result).to match_tickets []
  274. end
  275. it 'gets items in 1 month ago' do
  276. result = described_class.items(
  277. range_start: 1.year.ago.beginning_of_year,
  278. range_end: 1.year.from_now.at_end_of_year,
  279. selector: {
  280. 'created_at' => {
  281. 'operator' => 'before (relative)',
  282. 'range' => 'month',
  283. 'value' => '1'
  284. }
  285. }, # ticket selector to get only a collection of tickets
  286. params: { field: 'created_at' },
  287. )
  288. expect(result).to match_tickets ticket_before_40d
  289. end
  290. it 'gets items in 5 months ago' do
  291. result = described_class.items(
  292. range_start: 1.year.ago.beginning_of_year,
  293. range_end: 1.year.from_now.at_end_of_year,
  294. selector: {
  295. 'created_at' => {
  296. 'operator' => 'before (relative)',
  297. 'range' => 'month',
  298. 'value' => '5'
  299. }
  300. }, # ticket selector to get only a collection of tickets
  301. params: { field: 'created_at' },
  302. )
  303. expect(result).to match_tickets []
  304. end
  305. it 'gets items with aaa+bbb' do
  306. result = described_class.items(
  307. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  308. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  309. selector: {
  310. 'tags' => {
  311. 'operator' => 'contains all',
  312. 'value' => 'aaa, bbb'
  313. }
  314. },
  315. params: { field: 'created_at' },
  316. )
  317. expect(result).to match_tickets ticket_1
  318. end
  319. it 'gets items with not aaa+bbb' do
  320. result = described_class.items(
  321. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  322. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  323. selector: {
  324. 'tags' => {
  325. 'operator' => 'contains all not',
  326. 'value' => 'aaa, bbb'
  327. }
  328. },
  329. params: { field: 'created_at' },
  330. )
  331. expect(result).to match_tickets ticket_7, ticket_6, ticket_5, ticket_4, ticket_3, ticket_2
  332. end
  333. it 'gets items with aaa' do
  334. result = described_class.items(
  335. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  336. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  337. selector: {
  338. 'tags' => {
  339. 'operator' => 'contains all',
  340. 'value' => 'aaa'
  341. }
  342. },
  343. params: { field: 'created_at' },
  344. )
  345. expect(result).to match_tickets ticket_2, ticket_1
  346. end
  347. it 'gets items with not aaa' do
  348. result = described_class.items(
  349. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  350. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  351. selector: {
  352. 'tags' => {
  353. 'operator' => 'contains all not',
  354. 'value' => 'aaa'
  355. }
  356. },
  357. params: { field: 'created_at' },
  358. )
  359. expect(result).to match_tickets ticket_7, ticket_6, ticket_5, ticket_4, ticket_3
  360. end
  361. it 'gets items with one not aaa' do
  362. result = described_class.items(
  363. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  364. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  365. selector: {
  366. 'tags' => {
  367. 'operator' => 'contains one not',
  368. 'value' => 'aaa'
  369. }
  370. },
  371. params: { field: 'created_at' },
  372. )
  373. expect(result).to match_tickets ticket_7, ticket_6, ticket_5, ticket_4, ticket_3
  374. end
  375. it 'gets items with one not aaa+bbb' do
  376. result = described_class.items(
  377. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  378. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  379. selector: {
  380. 'tags' => {
  381. 'operator' => 'contains one not',
  382. 'value' => 'aaa, bbb'
  383. }
  384. },
  385. params: { field: 'created_at' },
  386. )
  387. expect(result).to match_tickets ticket_7, ticket_6, ticket_4, ticket_3
  388. end
  389. it 'gets items with one aaa' do
  390. result = described_class.items(
  391. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  392. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  393. selector: {
  394. 'tags' => {
  395. 'operator' => 'contains one',
  396. 'value' => 'aaa'
  397. }
  398. },
  399. params: { field: 'created_at' },
  400. )
  401. expect(result).to match_tickets ticket_2, ticket_1
  402. end
  403. it 'gets items with one aaa+bbb' do
  404. result = described_class.items(
  405. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  406. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  407. selector: {
  408. 'tags' => {
  409. 'operator' => 'contains one',
  410. 'value' => 'aaa, bbb'
  411. }
  412. },
  413. params: { field: 'created_at' },
  414. )
  415. expect(result).to match_tickets ticket_5, ticket_2, ticket_1
  416. end
  417. it 'gets items with test' do
  418. result = described_class.items(
  419. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  420. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  421. selector: {
  422. 'title' => {
  423. 'operator' => 'contains',
  424. 'value' => 'Test'
  425. }
  426. },
  427. params: { field: 'created_at' },
  428. )
  429. expect(result).to match_tickets ticket_7, ticket_6, ticket_5, ticket_4, ticket_3, ticket_2, ticket_1
  430. end
  431. it 'gets items with not test' do
  432. result = described_class.items(
  433. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  434. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  435. selector: {
  436. 'title' => {
  437. 'operator' => 'contains not',
  438. 'value' => 'Test'
  439. }
  440. },
  441. params: { field: 'created_at' },
  442. )
  443. expect(result).to match_tickets []
  444. end
  445. # Regression test for issue #2246 - Records in Reporting not updated when single ActiveRecord can not be found
  446. it 'correctly handles missing tickets', searchindex: false do
  447. class_double(SearchIndexBackend, selectors: { object_ids: [-1] }, drop_index: nil, drop_pipeline: nil).as_stubbed_const
  448. expect do
  449. described_class.items(
  450. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  451. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  452. selector: {}, # ticket selector to get only a collection of tickets
  453. params: { field: 'created_at' },
  454. )
  455. end.not_to raise_error
  456. end
  457. end
  458. context 'when additional attribute exists', db_strategy: :reset do
  459. before do
  460. ObjectManager::Attribute.add(
  461. object: 'Ticket',
  462. name: 'test_category',
  463. display: 'Test 1',
  464. data_type: 'tree_select',
  465. data_option: {
  466. maxlength: 200,
  467. null: false,
  468. default: '',
  469. options: [
  470. { 'name' => 'aa', 'value' => 'aa', 'children' => [{ 'name' => 'aa', 'value' => 'aa::aa' }, { 'name' => 'bb', 'value' => 'aa::bb' }, { 'name' => 'cc', 'value' => 'aa::cc' }] },
  471. { 'name' => 'bb', 'value' => 'bb', 'children' => [{ 'name' => 'aa', 'value' => 'bb::aa' }, { 'name' => 'bb', 'value' => 'bb::bb' }, { 'name' => 'cc', 'value' => 'bb::cc' }] },
  472. { 'name' => 'cc', 'value' => 'cc', 'children' => [{ 'name' => 'aa', 'value' => 'cc::aa' }, { 'name' => 'bb', 'value' => 'cc::bb' }, { 'name' => 'cc', 'value' => 'cc::cc' }] },
  473. ]
  474. },
  475. active: true,
  476. screens: {},
  477. position: 20,
  478. created_by_id: 1,
  479. updated_by_id: 1,
  480. editable: false,
  481. to_migrate: false,
  482. )
  483. ObjectManager::Attribute.migration_execute
  484. ticket_with_category
  485. searchindex_model_reload([Ticket])
  486. end
  487. let(:ticket_with_category) do
  488. travel_to DateTime.new 2015, 10, 28, 9, 30
  489. ticket = create(:ticket,
  490. group: group_2,
  491. customer: customer,
  492. test_category: 'cc::bb',
  493. state_name: 'new',
  494. priority_name: '2 normal')
  495. ticket.tag_add('aaa', 1)
  496. ticket.tag_add('bbb', 1)
  497. create(:ticket_article,
  498. :inbound_email,
  499. ticket: ticket)
  500. travel 5.hours
  501. ticket.update! group: group_1
  502. travel_back
  503. ticket
  504. end
  505. describe '.items' do
  506. it 'gets items with test_category cc:bb' do
  507. result = described_class.items(
  508. range_start: Time.zone.parse('2015-01-01T00:00:00Z'),
  509. range_end: Time.zone.parse('2015-12-31T23:59:59Z'),
  510. selector: {
  511. 'test_category' => {
  512. 'operator' => 'is',
  513. 'value' => 'cc::bb'
  514. },
  515. },
  516. params: { field: 'created_at' },
  517. )
  518. expect(result).to match_tickets ticket_with_category
  519. end
  520. end
  521. end
  522. end