external_sync_spec.rb 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. require 'rails_helper'
  2. RSpec.describe ExternalSync do
  3. describe '#changed?' do
  4. it 'keeps ActiveRecord instance unchanged on local but no remote changes' do
  5. object = create(:group)
  6. previous_changes = { name: 'Changed' }
  7. current_changes = previous_changes.dup
  8. result = described_class.changed?(
  9. object: object,
  10. previous_changes: previous_changes,
  11. current_changes: current_changes,
  12. )
  13. expect(result).to be false
  14. expect(object.has_changes_to_save?).to be false
  15. end
  16. it 'keeps ActiveRecord instance unchanged on local and remote changes' do
  17. object = create(:group)
  18. previous_changes = { name: 'Initial' }
  19. current_changes = { name: 'Changed' }
  20. result = described_class.changed?(
  21. object: object,
  22. previous_changes: previous_changes,
  23. current_changes: current_changes,
  24. )
  25. expect(result).to be false
  26. expect(object.has_changes_to_save?).to be false
  27. end
  28. it 'changes ActiveRecord instance attribute(s) for remote changes' do
  29. object = create(:group)
  30. previous_changes = { name: object.name }
  31. current_changes = { name: 'Changed' }
  32. result = described_class.changed?(
  33. object: object,
  34. previous_changes: previous_changes,
  35. current_changes: current_changes,
  36. )
  37. expect(result).to be true
  38. expect(object.has_changes_to_save?).to be true
  39. end
  40. it 'prevents ActiveRecord method calls' do
  41. object = create(:group)
  42. previous_changes = { name: object.name }
  43. current_changes = { destroy: 'Changed' }
  44. result = described_class.changed?(
  45. object: object,
  46. previous_changes: previous_changes,
  47. current_changes: current_changes,
  48. )
  49. expect(result).to be false
  50. expect(object.has_changes_to_save?).to be false
  51. expect(object.destroyed?).to be false
  52. end
  53. end
  54. describe '#map' do
  55. it 'maps to symbol keys' do
  56. mapping = {
  57. 'key' => 'key'
  58. }
  59. source = {
  60. 'key' => 'value'
  61. }
  62. result = {
  63. key: 'value'
  64. }
  65. expect(described_class.map(mapping: mapping, source: source)).to eq(result)
  66. end
  67. it 'resolves deep structures' do
  68. mapping = {
  69. 'sub.structure.key' => 'key',
  70. }
  71. source = {
  72. 'sub' => {
  73. 'structure' => {
  74. 'key' => 'value'
  75. }
  76. }
  77. }
  78. result = {
  79. key: 'value'
  80. }
  81. expect(described_class.map(mapping: mapping, source: source)).to eq(result)
  82. # check if sub structure is untouched
  83. expect(source['sub'].key?('structure')).to be true
  84. end
  85. it 'skips irrelevant keys' do
  86. mapping = {
  87. 'key' => 'key'
  88. }
  89. source = {
  90. 'key' => 'value',
  91. 'skipped' => 'skipped'
  92. }
  93. result = {
  94. key: 'value'
  95. }
  96. expect(described_class.map(mapping: mapping, source: source)).to eq(result)
  97. end
  98. it 'can handle object instances' do
  99. mapping = {
  100. 'name' => 'key'
  101. }
  102. source = double(name: 'value')
  103. result = {
  104. key: 'value'
  105. }
  106. expect(described_class.map(mapping: mapping, source: source)).to eq(result)
  107. end
  108. it 'can handle ActiveRecord instances' do
  109. mapping = {
  110. 'name' => 'key'
  111. }
  112. source = create(:group, name: 'value')
  113. result = {
  114. key: 'value'
  115. }
  116. expect(described_class.map(mapping: mapping, source: source)).to eq(result)
  117. end
  118. it 'prevents ActiveRecord method calls' do
  119. mapping = {
  120. 'name' => 'key',
  121. 'destroy' => 'evil'
  122. }
  123. source = create(:group, name: 'value')
  124. result = {
  125. key: 'value'
  126. }
  127. expect(described_class.map(mapping: mapping, source: source)).to eq(result)
  128. expect(source.destroyed?).to be false
  129. end
  130. end
  131. describe '.migrate' do
  132. let(:model) { 'Ticket' }
  133. let(:factory_name) { model.downcase.to_sym }
  134. let(:source) { create(factory_name) }
  135. let(:target) { create(factory_name) }
  136. let(:entries) do
  137. create_list(:external_sync, 2,
  138. object: model,
  139. o_id: source.id,)
  140. end
  141. it 'migrates entries' do
  142. expect do
  143. described_class.migrate('Ticket', source.id, target.id)
  144. end.to change {
  145. entries.each(&:reload).map(&:o_id).uniq
  146. }.from([source.id]).to([target.id])
  147. end
  148. end
  149. end