ticket_spec.rb 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe Gql::Queries::Ticket, current_user_id: 1, type: :graphql do
  4. context 'when fetching tickets' do
  5. let(:agent) { create(:agent) }
  6. let(:query) do
  7. <<~QUERY
  8. query ticket($ticketId: ID, $ticketInternalId: Int, $ticketNumber: String) {
  9. ticket(
  10. ticket: {
  11. ticketId: $ticketId
  12. ticketInternalId: $ticketInternalId
  13. ticketNumber: $ticketNumber
  14. }
  15. ) {
  16. id
  17. internalId
  18. number
  19. title
  20. owner {
  21. id
  22. firstname
  23. email
  24. createdBy {
  25. internalId
  26. }
  27. updatedBy {
  28. internalId
  29. }
  30. }
  31. customer {
  32. id
  33. firstname
  34. email
  35. }
  36. organization {
  37. name
  38. }
  39. tags
  40. subscribed
  41. mentions {
  42. edges {
  43. node {
  44. user {
  45. id
  46. }
  47. }
  48. cursor
  49. }
  50. }
  51. policy {
  52. update
  53. destroy
  54. followUp
  55. agentReadAccess
  56. agentUpdateAccess
  57. createMentions
  58. }
  59. timeUnitsPerType {
  60. name
  61. timeUnit
  62. }
  63. stateColorCode
  64. checklist {
  65. name
  66. }
  67. referencingChecklistTickets {
  68. id
  69. }
  70. }
  71. }
  72. QUERY
  73. end
  74. let(:variables) { { ticketId: gql.id(ticket) } }
  75. let(:ticket) do
  76. create(:ticket).tap do |t|
  77. t.tag_add('tag1', 1)
  78. t.tag_add('tag2', 1)
  79. end
  80. end
  81. let!(:checklist) do
  82. create(:checklist, ticket: ticket, item_count: 1, created_by: agent, updated_by: agent).tap do |checklist|
  83. checklist.items.last.update!(text: "Ticket##{another_ticket.number}", ticket_id: another_ticket.id)
  84. checklist.reload
  85. end
  86. end
  87. let!(:another_ticket) do
  88. create(:ticket, group: ticket.group, state: Ticket::State.find_by(name: 'new')).tap do |t|
  89. create(:checklist, ticket: t, item_count: 1, created_by: agent, updated_by: agent).tap do |checklist|
  90. checklist.items.last.update!(text: "Ticket##{ticket.number}", ticket_id: ticket.id)
  91. checklist.reload
  92. end
  93. end
  94. end
  95. before do
  96. setup if defined?(setup)
  97. gql.execute(query, variables: variables)
  98. end
  99. context 'with an agent', authenticated_as: :agent do
  100. context 'with permission' do
  101. let(:agent) { create(:agent, groups: [ticket.group]) }
  102. let(:base_expected_result) do
  103. {
  104. 'id' => gql.id(ticket),
  105. 'internalId' => ticket.id,
  106. 'number' => ticket.number,
  107. # Agent is allowed to see user data
  108. 'owner' => include(
  109. 'firstname' => ticket.owner.firstname,
  110. 'email' => ticket.owner.email,
  111. 'createdBy' => { 'internalId' => 1 },
  112. 'updatedBy' => { 'internalId' => 1 },
  113. ),
  114. 'tags' => %w[tag1 tag2],
  115. 'policy' => {
  116. 'agentReadAccess' => true,
  117. 'agentUpdateAccess' => true,
  118. 'createMentions' => true,
  119. 'destroy' => false,
  120. 'followUp' => true,
  121. 'update' => true
  122. },
  123. 'stateColorCode' => 'open',
  124. 'checklist' => {
  125. 'name' => checklist.name
  126. },
  127. 'referencingChecklistTickets' => [
  128. {
  129. 'id' => gql.id(another_ticket)
  130. }
  131. ]
  132. }
  133. end
  134. let(:expected_result) { base_expected_result }
  135. shared_examples 'finds the ticket' do
  136. it 'finds the ticket' do
  137. expect(gql.result.data).to include(expected_result)
  138. end
  139. end
  140. context 'when fetching a ticket by ticketId' do
  141. include_examples 'finds the ticket'
  142. end
  143. context 'when fetching a ticket by ticketInternalId' do
  144. let(:variables) { { ticketInternalId: ticket.id } }
  145. include_examples 'finds the ticket'
  146. end
  147. context 'when fetching a ticket by ticketNumber' do
  148. let(:variables) { { ticketNumber: ticket.number } }
  149. include_examples 'finds the ticket'
  150. end
  151. context 'when locator is missing' do
  152. let(:variables) { {} }
  153. it 'raises an exception' do
  154. expect(gql.result.error_type).to eq(GraphQL::Schema::Validator::ValidationFailedError)
  155. end
  156. end
  157. context 'with having checklist feature disabled' do
  158. let(:setup) do
  159. Setting.set('checklist', false)
  160. end
  161. let(:expected_result) { base_expected_result.merge({ 'checklist' => nil, 'referencingChecklistTickets' => nil }) }
  162. include_examples 'finds the ticket'
  163. end
  164. context 'with having time accounting enabled' do
  165. let(:ticket_time_accounting_types) { create_list(:ticket_time_accounting_type, 2) }
  166. let(:ticket_time_accounting) { create(:ticket_time_accounting, ticket: ticket, time_unit: 50) }
  167. let(:ticket_time_accounting_with_type) { create(:ticket_time_accounting, ticket: ticket, time_unit: 25, type: ticket_time_accounting_types[0]) }
  168. let(:ticket_time_accounting_with_type2) { create(:ticket_time_accounting, ticket: ticket, time_unit: 250, type: ticket_time_accounting_types[1]) }
  169. let(:setup) do
  170. Setting.set('time_accounting', true)
  171. Setting.set('time_accounting_types', true)
  172. ticket_time_accounting_with_type2 && ticket_time_accounting_with_type && ticket_time_accounting
  173. end
  174. it 'contains time unit entries grouped by type with a sum' do
  175. expect(gql.result.data[:timeUnitsPerType]).to eq([
  176. {
  177. 'name' => ticket_time_accounting_types[1].name,
  178. 'timeUnit' => 250.0,
  179. },
  180. {
  181. 'name' => 'None',
  182. 'timeUnit' => 50.0,
  183. },
  184. {
  185. 'name' => ticket_time_accounting_types[0].name,
  186. 'timeUnit' => 25.0,
  187. },
  188. ])
  189. end
  190. end
  191. context 'when subscribed' do
  192. before do
  193. Mention.subscribe! ticket, agent
  194. gql.execute(query, variables: variables)
  195. end
  196. it 'returns subscribed' do
  197. expect(gql.result.data).to include('subscribed' => true)
  198. end
  199. it 'returns user in subscribers list' do
  200. expect(gql.result.data.dig('mentions', 'edges'))
  201. .to include(include('node' => include('user' => include('id' => gql.id(agent)))))
  202. end
  203. end
  204. end
  205. context 'without permission' do
  206. it 'raises authorization error' do
  207. expect(gql.result.error_type).to eq(Exceptions::Forbidden)
  208. end
  209. end
  210. context 'without ticket' do
  211. let(:ticket) { create(:ticket).tap(&:destroy) }
  212. let(:checklist) { nil }
  213. let(:another_ticket) { nil }
  214. it 'fetches no ticket' do
  215. expect(gql.result.error_type).to eq(ActiveRecord::RecordNotFound)
  216. end
  217. end
  218. end
  219. context 'with a customer', authenticated_as: :customer do
  220. let(:customer) { create(:customer) }
  221. let(:ticket) { create(:ticket, customer: customer) }
  222. let(:expected_result) do
  223. {
  224. 'id' => gql.id(ticket),
  225. 'internalId' => ticket.id,
  226. 'number' => ticket.number,
  227. # Customer is not allowed to see data of other users
  228. 'owner' => include(
  229. 'firstname' => ticket.owner.firstname,
  230. 'email' => nil,
  231. 'createdBy' => nil,
  232. 'updatedBy' => nil,
  233. ),
  234. # Customer may see their own data
  235. 'customer' => include(
  236. 'firstname' => customer.firstname,
  237. 'email' => customer.email,
  238. ),
  239. 'policy' => {
  240. 'agentReadAccess' => false,
  241. 'agentUpdateAccess' => false,
  242. 'createMentions' => false,
  243. 'destroy' => false,
  244. 'followUp' => true,
  245. 'update' => true
  246. },
  247. 'checklist' => nil,
  248. 'referencingChecklistTickets' => nil,
  249. }
  250. end
  251. it 'finds the ticket, but without data of other users' do
  252. expect(gql.result.data).to include(expected_result)
  253. end
  254. end
  255. it_behaves_like 'graphql responds with error if unauthenticated'
  256. end
  257. end