history_spec.rb 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. # Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. require 'models/concerns/can_be_imported_examples'
  4. RSpec.describe History, type: :model do
  5. it_behaves_like 'CanBeImported'
  6. describe '.list' do
  7. context 'when given an object with no histories' do
  8. let!(:object) { create(:'cti/log') }
  9. it 'returns an empty array' do
  10. expect(described_class.list(object.class.name, object.id))
  11. .to be_an(Array).and be_empty
  12. end
  13. end
  14. context 'when given an object with histories' do
  15. context 'and called without "related_history_object" argument' do
  16. let!(:object) { create(:user) }
  17. before { object.update(email: 'foo@example.com') }
  18. context 'or "assets" flag' do
  19. let(:list) { described_class.list(object.class.name, object.id) }
  20. it 'returns an array of attribute hashes for those histories' do
  21. expect(list).to match_array(
  22. [
  23. hash_including(
  24. 'o_id' => object.id,
  25. ),
  26. hash_including(
  27. 'o_id' => object.id,
  28. 'value_to' => 'foo@example.com',
  29. )
  30. ]
  31. )
  32. end
  33. it 'replaces *_id attributes with the corresponding association #name' do
  34. expect(list.first)
  35. .to not_include('history_object_id', 'history_type_id')
  36. .and include(
  37. 'object' => object.class.name,
  38. 'type' => 'created',
  39. )
  40. expect(list.second)
  41. .to not_include('history_object_id', 'history_type_id', 'history_attribute_id')
  42. .and include(
  43. 'object' => object.class.name,
  44. 'type' => 'updated',
  45. 'attribute' => 'email',
  46. )
  47. end
  48. end
  49. context 'but with "assets" flag' do
  50. let(:list) { described_class.list(object.class.name, object.id, nil, true) }
  51. let(:matching_histories) do
  52. described_class.where(
  53. o_id: object.id,
  54. history_object_id: History::Object.lookup(name: object.class.name).id
  55. )
  56. end
  57. it 'returns a hash including an array of history attribute hashes' do
  58. expect(list).to include(
  59. list: [
  60. hash_including(
  61. 'o_id' => object.id,
  62. 'object' => object.class.name,
  63. 'type' => 'created',
  64. ),
  65. hash_including(
  66. 'o_id' => object.id,
  67. 'object' => object.class.name,
  68. 'type' => 'updated',
  69. 'attribute' => 'email',
  70. 'value_to' => 'foo@example.com',
  71. )
  72. ]
  73. )
  74. end
  75. it 'returns a hash including each history record’s FE assets' do
  76. expect(list).to include(
  77. assets: matching_histories.reduce({}) { |assets, h| h.assets(assets) }
  78. )
  79. end
  80. end
  81. end
  82. context 'with "related_history_object" argument' do
  83. let!(:object) { related_object.ticket }
  84. let!(:related_object) { create(:ticket_article, internal: true) } # MUST be internal, or else callbacks will create additional histories
  85. before { object.update(title: 'Lorem ipsum dolor') }
  86. context 'but no "assets" flag' do
  87. let(:list) { described_class.list(object.class.name, object.id, 'Ticket::Article') }
  88. it 'returns an array of attribute hashes for those histories' do
  89. expect(list).to match_array(
  90. [
  91. hash_including(
  92. 'o_id' => object.id,
  93. ),
  94. hash_including(
  95. 'o_id' => related_object.id,
  96. ),
  97. hash_including(
  98. 'o_id' => object.id,
  99. 'value_to' => 'Lorem ipsum dolor',
  100. )
  101. ]
  102. )
  103. end
  104. it 'replaces *_id attributes with the corresponding association #name' do
  105. expect(list.first)
  106. .to not_include('history_object_id', 'history_type_id')
  107. .and include(
  108. 'object' => object.class.name,
  109. 'type' => 'created',
  110. )
  111. expect(list.second)
  112. .to not_include('history_object_id', 'history_type_id')
  113. .and include(
  114. 'object' => related_object.class.name,
  115. 'type' => 'created',
  116. )
  117. expect(list.third)
  118. .to not_include('history_object_id', 'history_type_id', 'history_attribute_id')
  119. .and include(
  120. 'object' => object.class.name,
  121. 'type' => 'updated',
  122. 'attribute' => 'title',
  123. )
  124. end
  125. end
  126. context 'and "assets" flag' do
  127. let(:list) { described_class.list(object.class.name, object.id, 'Ticket::Article', true) }
  128. let(:matching_histories) do
  129. described_class.where(
  130. o_id: object.id,
  131. history_object_id: History::Object.lookup(name: object.class.name).id
  132. ) + described_class.where(
  133. o_id: related_object.id,
  134. history_object_id: History::Object.lookup(name: related_object.class.name).id
  135. )
  136. end
  137. it 'returns a hash including an array of history attribute hashes' do
  138. expect(list).to include(
  139. list: [
  140. hash_including(
  141. 'o_id' => object.id,
  142. 'object' => object.class.name,
  143. 'type' => 'created',
  144. ),
  145. hash_including(
  146. 'o_id' => related_object.id,
  147. 'object' => related_object.class.name,
  148. 'type' => 'created',
  149. ),
  150. hash_including(
  151. 'o_id' => object.id,
  152. 'object' => object.class.name,
  153. 'type' => 'updated',
  154. 'attribute' => 'title',
  155. 'value_to' => 'Lorem ipsum dolor',
  156. )
  157. ]
  158. )
  159. end
  160. it 'returns a hash including each history record’s FE assets' do
  161. expect(list).to include(
  162. assets: matching_histories.reduce({}) { |assets, h| h.assets(assets) }
  163. )
  164. end
  165. end
  166. end
  167. end
  168. end
  169. end