history_spec.rb 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. # Copyright (C) 2012-2024 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 contain_exactly(hash_including(
  22. 'o_id' => object.id,
  23. ), hash_including(
  24. 'o_id' => object.id,
  25. 'value_to' => 'foo@example.com',
  26. ))
  27. end
  28. it 'replaces *_id attributes with the corresponding association #name' do
  29. expect(list.first)
  30. .to not_include('history_object_id', 'history_type_id')
  31. .and include(
  32. 'object' => object.class.name,
  33. 'type' => 'created',
  34. )
  35. expect(list.second)
  36. .to not_include('history_object_id', 'history_type_id', 'history_attribute_id')
  37. .and include(
  38. 'object' => object.class.name,
  39. 'type' => 'updated',
  40. 'attribute' => 'email',
  41. )
  42. end
  43. end
  44. context 'but with "assets" flag' do
  45. let(:list) { described_class.list(object.class.name, object.id, nil, true) }
  46. let(:matching_histories) do
  47. described_class.where(
  48. o_id: object.id,
  49. history_object_id: History::Object.lookup(name: object.class.name).id
  50. )
  51. end
  52. it 'returns a hash including an array of history attribute hashes' do
  53. expect(list).to include(
  54. list: [
  55. hash_including(
  56. 'o_id' => object.id,
  57. 'object' => object.class.name,
  58. 'type' => 'created',
  59. ),
  60. hash_including(
  61. 'o_id' => object.id,
  62. 'object' => object.class.name,
  63. 'type' => 'updated',
  64. 'attribute' => 'email',
  65. 'value_to' => 'foo@example.com',
  66. )
  67. ]
  68. )
  69. end
  70. it 'returns a hash including each history record’s FE assets' do
  71. expect(list).to include(
  72. assets: matching_histories.reduce({}) { |assets, h| h.assets(assets) }
  73. )
  74. end
  75. end
  76. end
  77. context 'with "related_history_object" argument' do
  78. let!(:object) { related_object.ticket }
  79. let!(:related_object) { create(:ticket_article, internal: true) } # MUST be internal, or else callbacks will create additional histories
  80. before { object.update(title: 'Lorem ipsum dolor') }
  81. context 'but no "assets" flag' do
  82. let(:list) { described_class.list(object.class.name, object.id, 'Ticket::Article') }
  83. it 'returns an array of attribute hashes for those histories' do
  84. expect(list).to contain_exactly(hash_including(
  85. 'o_id' => object.id,
  86. ), hash_including(
  87. 'o_id' => related_object.id,
  88. ), hash_including(
  89. 'o_id' => object.id,
  90. 'value_to' => 'Lorem ipsum dolor',
  91. ))
  92. end
  93. it 'replaces *_id attributes with the corresponding association #name' do
  94. expect(list.first)
  95. .to not_include('history_object_id', 'history_type_id')
  96. .and include(
  97. 'object' => object.class.name,
  98. 'type' => 'created',
  99. )
  100. expect(list.second)
  101. .to not_include('history_object_id', 'history_type_id')
  102. .and include(
  103. 'object' => related_object.class.name,
  104. 'type' => 'created',
  105. )
  106. expect(list.third)
  107. .to not_include('history_object_id', 'history_type_id', 'history_attribute_id')
  108. .and include(
  109. 'object' => object.class.name,
  110. 'type' => 'updated',
  111. 'attribute' => 'title',
  112. )
  113. end
  114. end
  115. context 'and "assets" flag' do
  116. let(:list) { described_class.list(object.class.name, object.id, 'Ticket::Article', true) }
  117. let(:matching_histories) do
  118. described_class.where(
  119. o_id: object.id,
  120. history_object_id: History::Object.lookup(name: object.class.name).id
  121. ) + described_class.where(
  122. o_id: related_object.id,
  123. history_object_id: History::Object.lookup(name: related_object.class.name).id
  124. )
  125. end
  126. it 'returns a hash including an array of history attribute hashes' do
  127. expect(list).to include(
  128. list: [
  129. hash_including(
  130. 'o_id' => object.id,
  131. 'object' => object.class.name,
  132. 'type' => 'created',
  133. ),
  134. hash_including(
  135. 'o_id' => related_object.id,
  136. 'object' => related_object.class.name,
  137. 'type' => 'created',
  138. ),
  139. hash_including(
  140. 'o_id' => object.id,
  141. 'object' => object.class.name,
  142. 'type' => 'updated',
  143. 'attribute' => 'title',
  144. 'value_to' => 'Lorem ipsum dolor',
  145. )
  146. ]
  147. )
  148. end
  149. it 'returns a hash including each history record’s FE assets' do
  150. expect(list).to include(
  151. assets: matching_histories.reduce({}) { |assets, h| h.assets(assets) }
  152. )
  153. end
  154. end
  155. end
  156. end
  157. end
  158. end