ticket_generic_time_spec.rb 17 KB

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